xaizek / hstr (License: Apachev2) (since 2018-12-07)
Bash and Zsh shell history suggest box - easily view, navigate, search and manage your command history.
Commit a06d94d0a4b93e6855396a8f7531cd815a7a5800

Initial regexp version that doesn't crash, but still needs a few improvements (in rendering).
Author: Martin Dvorak
Author date (UTC): 2014-04-25 20:59
Committer name: Martin Dvorak
Committer date (UTC): 2014-04-25 20:59
Parent(s): 9984ad7376cb2682e09ba22f39526089a927717a
Signing key:
Tree: 02dffaa2ea3069f035d130e068c269c10c73fff4
File Lines added Lines deleted
src/hstr.c 117 61
src/hstr_regexp.c 1 1
src/include/hstr_regexp.h 1 1
tests/src/test_keyb.c 1 1
tests/test_keyb.sh 1 0
File src/hstr.c changed (mode: 100644) (index fd8c5fb..51dedbf)
83 83 #define HH_VIEW_HISTORY 1 #define HH_VIEW_HISTORY 1
84 84 #define HH_VIEW_FAVORITES 2 #define HH_VIEW_FAVORITES 2
85 85
86 #define HH_MATCH_CASE_INSENSITIVE 0
87 #define HH_MATCH_REGEXP 1
88 #define HH_MATCH_CASE_SENSITIVE 2
86 #define HH_MATCH_EXACT 0
87 #define HH_MATCH_REGEXP 1
89 88
90 89 #define HH_CASE_INSENSITIVE 0 #define HH_CASE_INSENSITIVE 0
91 90 #define HH_CASE_SENSITIVE 1 #define HH_CASE_SENSITIVE 1
 
... ... static const char *HH_VIEW_LABELS[]={
111 110 }; };
112 111
113 112 static const char *HH_MATCH_LABELS[]={ static const char *HH_MATCH_LABELS[]={
114 "sensitive",
115 "insensitive"
113 "exact",
114 "regexp"
116 115 }; };
117 116
118 117 static const char *HH_CASE_LABELS[]={ static const char *HH_CASE_LABELS[]={
119 "exact",
120 "regexp"
118 "sensitive",
119 "insensitive"
121 120 }; };
122 121
123 122 static const char *INSTALL_STRING= static const char *INSTALL_STRING=
 
... ... static const char *HELP_STRING=
138 137 "\n" "\n"
139 138 "\n --favorites -f ... show command favorites" "\n --favorites -f ... show command favorites"
140 139 "\n --show-configuration ... show configuration to be added to ~/.bashrc" "\n --show-configuration ... show configuration to be added to ~/.bashrc"
140 "\n --version ... show version details"
141 141 "\n --help ... display this help and exit" "\n --help ... display this help and exit"
142 142 "\n" "\n"
143 143 "\nReport bugs to martin.dvorak@mindforger.com" "\nReport bugs to martin.dvorak@mindforger.com"
 
... ... static const char *HELP_STRING=
145 145 "\n"; "\n";
146 146
147 147 static const char *VERSION_STRING= static const char *VERSION_STRING=
148 "hh version \"1.10\""
148 "hh version \"1.11\""
149 149 "\n build \""__DATE__" " __TIME__"\"" "\n build \""__DATE__" " __TIME__"\""
150 150 "\n"; "\n";
151 151
 
... ... typedef struct {
161 161 FavoriteItems *favorites; FavoriteItems *favorites;
162 162
163 163 char **selection; char **selection;
164 regmatch_t *selectionRegexpMatch;
164 165 unsigned selectionSize; unsigned selectionSize;
165 166
166 167 int historyMatch; // TODO patternMatching: exact, regexp int historyMatch; // TODO patternMatching: exact, regexp
 
... ... static Hstr *hstr;
180 181 void hstr_init(Hstr *hstr) void hstr_init(Hstr *hstr)
181 182 { {
182 183 hstr->selection=NULL; hstr->selection=NULL;
184 hstr->selectionRegexpMatch=NULL;
183 185 hstr->selectionSize=0; hstr->selectionSize=0;
184 186
185 hstr->historyMatch=HH_MATCH_CASE_INSENSITIVE;
187 hstr->historyMatch=HH_MATCH_REGEXP;
186 188 hstr->historyView=HH_VIEW_RANKING; hstr->historyView=HH_VIEW_RANKING;
187 189 hstr->caseSensitive=HH_CASE_INSENSITIVE; hstr->caseSensitive=HH_CASE_INSENSITIVE;
188 190
 
... ... void hstr_init(Hstr *hstr)
190 192
191 193 hstr->debugLevel=HH_DEBUG_LEVEL_NONE; hstr->debugLevel=HH_DEBUG_LEVEL_NONE;
192 194
195 hstr->cmdline[0]=0;
196
193 197 hstr_regexp_init(&hstr->regexp); hstr_regexp_init(&hstr->regexp);
194 198 } }
195 199
 
... ... void hstr_get_env_configuration(Hstr *hstr)
201 205 hstr->hicolor=TRUE; hstr->hicolor=TRUE;
202 206 } }
203 207 if(strstr(hstr_config,HH_CONFIG_CASE)) { if(strstr(hstr_config,HH_CONFIG_CASE)) {
204 hstr->historyMatch=HH_MATCH_CASE_SENSITIVE;
208 hstr->caseSensitive=HH_CASE_SENSITIVE;
205 209 } }
206 210 if(strstr(hstr_config,HH_CONFIG_SORTING)) { if(strstr(hstr_config,HH_CONFIG_SORTING)) {
207 211 hstr->historyView=HH_VIEW_HISTORY; hstr->historyView=HH_VIEW_HISTORY;
 
... ... void print_history_label(Hstr *hstr)
287 291 { {
288 292 int width=getmaxx(stdscr); int width=getmaxx(stdscr);
289 293
290 snprintf(screenLine, width, "- HISTORY - view:%s (C-/) - match:%s (C-t) - case:%s (C-.) - %d/%d ",
294 snprintf(screenLine, width, "- HISTORY - view:%s (C-/) - match:%s (C-e) - case:%s (C-t) - %d/%d ",
291 295 HH_VIEW_LABELS[hstr->historyView], HH_VIEW_LABELS[hstr->historyView],
292 296 HH_MATCH_LABELS[hstr->historyMatch], HH_MATCH_LABELS[hstr->historyMatch],
293 297 HH_CASE_LABELS[hstr->caseSensitive], HH_CASE_LABELS[hstr->caseSensitive],
 
... ... unsigned get_max_history_items()
323 327 return (getmaxy(stdscr)-Y_OFFSET_ITEMS); return (getmaxy(stdscr)-Y_OFFSET_ITEMS);
324 328 } }
325 329
326
330 // TODO don't realloc if size doesn't change
327 331 void hstr_realloc_selection(unsigned size, Hstr *hstr) void hstr_realloc_selection(unsigned size, Hstr *hstr)
328 332 { {
329 333 if(hstr->selection) { if(hstr->selection) {
330 334 if(size) { if(size) {
331 hstr->selection=realloc(hstr->selection, size);
335 hstr->selection
336 =realloc(hstr->selection, sizeof(char*) * size);
337 hstr->selectionRegexpMatch
338 =realloc(hstr->selectionRegexpMatch, sizeof(regmatch_t) * size);
332 339 } else { } else {
333 340 free(hstr->selection); free(hstr->selection);
341 free(hstr->selectionRegexpMatch);
334 342 hstr->selection=NULL; hstr->selection=NULL;
343 hstr->selectionRegexpMatch=NULL;
335 344 } }
336 345 } else { } else {
337 346 if(size) { if(size) {
338 hstr->selection = malloc(size);
347 hstr->selection = malloc(sizeof(char*) * size);
348 hstr->selectionRegexpMatch = malloc(sizeof(regmatch_t) * size);
339 349 } }
340 350 } }
341 351 } }
342 352
343 353 unsigned hstr_make_selection(char *prefix, HistoryItems *history, int maxSelectionCount, Hstr *hstr) unsigned hstr_make_selection(char *prefix, HistoryItems *history, int maxSelectionCount, Hstr *hstr)
344 354 { {
345 hstr_realloc_selection(sizeof(char*) * maxSelectionCount, hstr);
355 hstr_realloc_selection(maxSelectionCount, hstr);
346 356
347 357 unsigned i, selectionCount=0; unsigned i, selectionCount=0;
348 358 char **source; char **source;
 
... ... unsigned hstr_make_selection(char *prefix, HistoryItems *history, int maxSelecti
364 374 break; break;
365 375 } }
366 376
377 regmatch_t regexpMatch;
367 378 for(i=0; i<count && selectionCount<maxSelectionCount; i++) { for(i=0; i<count && selectionCount<maxSelectionCount; i++) {
368 379 if(source[i]) { if(source[i]) {
369 380 if(!prefix || !strlen(prefix)) { if(!prefix || !strlen(prefix)) {
370 381 hstr->selection[selectionCount++]=source[i]; hstr->selection[selectionCount++]=source[i];
371 382 } else { } else {
372 383 switch(hstr->historyMatch) { switch(hstr->historyMatch) {
373 case HH_MATCH_CASE_INSENSITIVE:
374 if(source[i]==strcasestr(source[i], prefix)) {
375 hstr->selection[selectionCount++]=source[i];
384 case HH_MATCH_EXACT:
385 switch(hstr->caseSensitive) {
386 case HH_CASE_SENSITIVE:
387 if(source[i]==strstr(source[i], prefix)) {
388 hstr->selection[selectionCount++]=source[i];
389 }
390 break;
391 case HH_CASE_INSENSITIVE:
392 if(source[i]==strcasestr(source[i], prefix)) {
393 hstr->selection[selectionCount++]=source[i];
394 }
395 break;
376 396 } }
377 397 break; break;
378 398 case HH_MATCH_REGEXP: case HH_MATCH_REGEXP:
379 // TODO TODO TODO REGEXP: call regexp implementation from here regexp_match(&regexp, match)
380 break;
381 case HH_MATCH_CASE_SENSITIVE:
382 default:
383 if(source[i]==strstr(source[i], prefix)) {
384 hstr->selection[selectionCount++]=source[i];
399 if(hstr_regexp_match(&(hstr->regexp), prefix, source[i], &regexpMatch)) {
400 hstr->selection[selectionCount]=source[i];
401 hstr->selectionRegexpMatch[selectionCount].rm_so=regexpMatch.rm_so;
402 hstr->selectionRegexpMatch[selectionCount].rm_eo=regexpMatch.rm_eo;
403 selectionCount++;
385 404 } }
386 405 break; break;
387 406 } }
 
... ... unsigned hstr_make_selection(char *prefix, HistoryItems *history, int maxSelecti
393 412 char *substring; char *substring;
394 413 for(i=0; i<count && selectionCount<maxSelectionCount; i++) { for(i=0; i<count && selectionCount<maxSelectionCount; i++) {
395 414 switch(hstr->historyMatch) { switch(hstr->historyMatch) {
396 case HH_MATCH_CASE_SENSITIVE:
397 substring = strstr(source[i], prefix);
398 if (substring != NULL && substring!=source[i]) {
399 hstr->selection[selectionCount++]=source[i];
415 case HH_MATCH_EXACT:
416 switch(hstr->caseSensitive) {
417 case HH_CASE_SENSITIVE:
418 substring = strstr(source[i], prefix);
419 if (substring != NULL && substring!=source[i]) {
420 hstr->selection[selectionCount++]=source[i];
421 }
422 break;
423 case HH_CASE_INSENSITIVE:
424 substring = strcasestr(source[i], prefix);
425 if (substring != NULL && substring!=source[i]) {
426 hstr->selection[selectionCount++]=source[i];
427 }
428 break;
400 429 } }
401 430 break; break;
402 431 case HH_MATCH_REGEXP: case HH_MATCH_REGEXP:
403 // TODO TODO regexp_match(&regexp)
404 break;
405 case HH_MATCH_CASE_INSENSITIVE:
406 default:
407 substring = strcasestr(source[i], prefix);
408 if (substring != NULL && substring!=source[i]) {
409 hstr->selection[selectionCount++]=source[i];
410 }
411 break;
432 // all regexps matched previously - user decides whether match ^ or infix
433 break;
412 434 } }
413 435 } }
414 436 } }
 
... ... unsigned hstr_make_selection(char *prefix, HistoryItems *history, int maxSelecti
417 439 return selectionCount; return selectionCount;
418 440 } }
419 441
420 void print_selection_row(char *text, int y, int width, char *prefix)
442 void print_selection_row(char *text, int y, int width, char *pattern)
421 443 { {
422 444 snprintf(screenLine, width, " %s", text); snprintf(screenLine, width, " %s", text);
423 445 mvprintw(y, 0, "%s", screenLine); clrtoeol(); mvprintw(y, 0, "%s", screenLine); clrtoeol();
424 if(prefix && strlen(prefix)>0) {
446
447 if(pattern && strlen(pattern)) {
425 448 color_attr_on(A_BOLD); color_attr_on(A_BOLD);
426 449 char *p; char *p;
427 450
428 451 switch(hstr->historyMatch) { switch(hstr->historyMatch) {
429 case HH_MATCH_CASE_SENSITIVE:
430 p=strstr(text, prefix);
431 mvprintw(y, 1+(p-text), "%s", prefix);
452 case HH_MATCH_EXACT:
453 switch(hstr->caseSensitive) {
454 case HH_CASE_INSENSITIVE:
455 p=strcasestr(text, pattern);
456 snprintf(screenLine, strlen(pattern)+1, "%s", p);
457 mvprintw(y, 1+(p-text), "%s", screenLine);
458 break;
459 case HH_CASE_SENSITIVE:
460 p=strstr(text, pattern);
461 mvprintw(y, 1+(p-text), "%s", pattern);
462 break;
463 }
432 464 break; break;
433 465 case HH_MATCH_REGEXP: case HH_MATCH_REGEXP:
434 // TODO regexp_(&regex)
435 break;
436 case HH_MATCH_CASE_INSENSITIVE:
437 default:
438 p=strcasestr(text, prefix);
439 snprintf(screenLine, strlen(prefix)+1, "%s", p);
440 mvprintw(y, 1+(p-text), "%s", screenLine);
466 p=strstr(text, pattern);
467 mvprintw(y, 1+(p-text), "%s", pattern);
441 468 break; break;
442 469 } }
443 470 color_attr_off(A_BOLD); color_attr_off(A_BOLD);
 
... ... void hstr_print_highlighted_selection_row(char *text, int y, int width, Hstr *hs
464 491 color_attr_off(A_BOLD); color_attr_off(A_BOLD);
465 492 } }
466 493
467 char *hstr_print_selection(unsigned maxHistoryItems, char *prefix, Hstr *hstr)
494 char *hstr_print_selection(unsigned maxHistoryItems, char *pattern, Hstr *hstr)
468 495 { {
469 496 char *result=NULL; char *result=NULL;
470 unsigned selectionCount=hstr_make_selection(prefix, hstr->history, maxHistoryItems, hstr);
497 unsigned selectionCount=hstr_make_selection(pattern, hstr->history, maxHistoryItems, hstr);
471 498 if (selectionCount > 0) { if (selectionCount > 0) {
472 499 result=hstr->selection[0]; result=hstr->selection[0];
473 500 } }
 
... ... char *hstr_print_selection(unsigned maxHistoryItems, char *prefix, Hstr *hstr)
480 507 move(Y_OFFSET_ITEMS, 0); move(Y_OFFSET_ITEMS, 0);
481 508 clrtobot(); clrtobot();
482 509
510 char buffer[CMDLINE_LNG];
511 int start, end;
512
483 513 for (i = 0; i<height; ++i) { for (i = 0; i<height; ++i) {
484 514 if(i<hstr->selectionSize) { if(i<hstr->selectionSize) {
485 print_selection_row(hstr->selection[i], y++, width, prefix);
515 if(pattern && strlen(pattern)) {
516 if(hstr->historyMatch==HH_MATCH_REGEXP) {
517 start=hstr->selectionRegexpMatch[i].rm_so;
518 end=hstr->selectionRegexpMatch[i].rm_eo-start;
519 strncpy(buffer,
520 hstr->selection[i]+start,
521 end);
522 buffer[end]=0;
523 } else {
524 strcpy(buffer, pattern);
525 }
526 print_selection_row(hstr->selection[i], y++, width, buffer);
527 } else {
528 print_selection_row(hstr->selection[i], y++, width, pattern);
529 }
486 530 } else { } else {
487 531 mvprintw(y++, 0, " "); mvprintw(y++, 0, " ");
488 532 } }
 
... ... char *hstr_print_selection(unsigned maxHistoryItems, char *prefix, Hstr *hstr)
495 539 void highlight_selection(int selectionCursorPosition, int previousSelectionCursorPosition, char *prefix, Hstr *hstr) void highlight_selection(int selectionCursorPosition, int previousSelectionCursorPosition, char *prefix, Hstr *hstr)
496 540 { {
497 541 if(previousSelectionCursorPosition!=SELECTION_CURSOR_IN_PROMPT) { if(previousSelectionCursorPosition!=SELECTION_CURSOR_IN_PROMPT) {
542 // TODO regexp match instead of prefix one level up
498 543 print_selection_row( print_selection_row(
499 544 hstr->selection[previousSelectionCursorPosition], hstr->selection[previousSelectionCursorPosition],
500 545 Y_OFFSET_ITEMS+previousSelectionCursorPosition, Y_OFFSET_ITEMS+previousSelectionCursorPosition,
 
... ... void loop_to_select(Hstr *hstr)
628 673 } }
629 674 print_history_label(hstr); print_history_label(hstr);
630 675 break; break;
676 case K_CTRL_E:
677 hstr->historyMatch++;
678 hstr->historyMatch=hstr->historyMatch%2;
679 result=hstr_print_selection(maxHistoryItems, pattern, hstr);
680 print_history_label(hstr);
681 selectionCursorPosition=0;
682 break;
631 683 case K_CTRL_T: case K_CTRL_T:
632 // TODO TODO TODO TODO TODO MOD to rotate regexp views goes here
633 // TODO TODO TODO hstr->caseSensitive=!hstr->caseSensitive;
634
684 hstr->caseSensitive=!hstr->caseSensitive;
685 hstr->regexp.caseSensitive=hstr->caseSensitive;
635 686 result=hstr_print_selection(maxHistoryItems, pattern, hstr); result=hstr_print_selection(maxHistoryItems, pattern, hstr);
636 687 print_history_label(hstr); print_history_label(hstr);
637 688 selectionCursorPosition=0; selectionCursorPosition=0;
 
... ... void loop_to_select(Hstr *hstr)
684 735 print_prefix(pattern, y, basex); print_prefix(pattern, y, basex);
685 736 } }
686 737
738 // TODO why I make selection if it's done in print_selection?
687 739 if(strlen(pattern)>0) { if(strlen(pattern)>0) {
688 740 hstr_make_selection(pattern, hstr->history, maxHistoryItems, hstr); hstr_make_selection(pattern, hstr->history, maxHistoryItems, hstr);
689 741 } else { } else {
 
... ... void loop_to_select(Hstr *hstr)
775 827 // TODO support BASH substitutions: !!, !!ps, !$, !* // TODO support BASH substitutions: !!, !!ps, !$, !*
776 828 void hstr_assemble_cmdline_pattern(int argc, char* argv[], Hstr* hstr) void hstr_assemble_cmdline_pattern(int argc, char* argv[], Hstr* hstr)
777 829 { {
778 hstr->cmdline[0]=0;
779 830 if(argc>0) { if(argc>0) {
780 831 int i; int i;
781 832 for(i=1; i<argc; i++) { for(i=1; i<argc; i++) {
 
... ... void hstr_assemble_cmdline_pattern(int argc, char* argv[], Hstr* hstr)
795 846 } }
796 847
797 848 // TODO to be rewritten to getopt // TODO to be rewritten to getopt
849
850 // TODO on unknown option make it filter
851 // TODO on favorites - skip -f otherwise it becomes filter
798 852 void hstr_get_cmdline_options(int argc, char *argv[], Hstr *hstr) void hstr_get_cmdline_options(int argc, char *argv[], Hstr *hstr)
799 853 { {
800 854 if(argc>0) { if(argc>0) {
 
... ... void hstr_get_cmdline_options(int argc, char *argv[], Hstr *hstr)
811 865 printf("%s", VERSION_STRING); printf("%s", VERSION_STRING);
812 866 exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
813 867 } else { } else {
814 // if(strstr(argv[1], "--help")) {
815 printf("Unknown option: %s\n", argv[1]);
816 printf("%s", HELP_STRING);
817 exit(EXIT_SUCCESS);
868 if(strstr(argv[1], "--help")) {
869 printf("Unknown option: %s\n", argv[1]);
870 printf("%s", HELP_STRING);
871 exit(EXIT_SUCCESS);
872 } else {
873 hstr_assemble_cmdline_pattern(argc, argv, hstr);
874 }
818 875 } }
819 876 } }
820 877 } }
 
... ... int main(int argc, char *argv[])
850 907 hstr_init(hstr); hstr_init(hstr);
851 908 hstr_get_env_configuration(hstr); hstr_get_env_configuration(hstr);
852 909 hstr_get_cmdline_options(argc, argv, hstr); hstr_get_cmdline_options(argc, argv, hstr);
853 hstr_assemble_cmdline_pattern(argc, argv, hstr);
854 910 hstr_init_favorites(hstr); hstr_init_favorites(hstr);
855 911 hstr_main(hstr); hstr_main(hstr);
856 912
File src/hstr_regexp.c changed (mode: 100644) (index eda1a8a..61cea9d)
... ... void hstr_regexp_init(HstrRegexp *hstrRegexp)
16 16 hashset_init(&hstrRegexp->cache); hashset_init(&hstrRegexp->cache);
17 17 } }
18 18
19 bool hstr_match(HstrRegexp *hstrRegexp, char *regexp, char *text, regmatch_t *match)
19 bool hstr_regexp_match(HstrRegexp *hstrRegexp, char *regexp, char *text, regmatch_t *match)
20 20 { {
21 21 regex_t* compiled=malloc(sizeof(regex_t)); regex_t* compiled=malloc(sizeof(regex_t));
22 22 if(hashset_contains(&hstrRegexp->cache,regexp)) { if(hashset_contains(&hstrRegexp->cache,regexp)) {
File src/include/hstr_regexp.h changed (mode: 100644) (index 3ae8238..fc7643f)
... ... typedef struct {
23 23 } HstrRegexp; } HstrRegexp;
24 24
25 25 void hstr_regexp_init(HstrRegexp *hstrRegexp); void hstr_regexp_init(HstrRegexp *hstrRegexp);
26 bool hstr_match(HstrRegexp *hstrRegexp, char *regexp, char *text, regmatch_t *match);
26 bool hstr_regexp_match(HstrRegexp *hstrRegexp, char *regexp, char *text, regmatch_t *match);
27 27 void hstr_regexp_destroy(HstrRegexp *hstrRegexp); void hstr_regexp_destroy(HstrRegexp *hstrRegexp);
28 28
29 29 #endif #endif
File tests/src/test_keyb.c changed (mode: 100644) (index 50e594e..c49f7e5)
... ... void echo_keyb_characters() {
32 32
33 33 int main(int argc, char *argv[]) int main(int argc, char *argv[])
34 34 { {
35
35 echo_keyb_characters();
36 36 } }
File tests/test_keyb.sh changed (mode: 100755) (index 828e87b..cab80a0)
1 1 #!/bin/bash #!/bin/bash
2 2
3 rm -vf _keyb
3 4 gcc ./src/test_keyb.c -o _keyb gcc ./src/test_keyb.c -o _keyb
4 5
5 6 # eof # eof
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/hstr

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/hstr

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master