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 53643e2b6f6c5a5d09b4ab6b923834ece9f279e4

Fixed #24 by making the search case insensitive with possibility to switch sensitivity using Ctrl-i.
Author: Martin Dvorak
Author date (UTC): 2014-01-04 07:39
Committer name: Martin Dvorak
Committer date (UTC): 2014-01-04 07:39
Parent(s): 3230917c2a538297fc769ad95cabac8cc962c856
Signing key:
Tree: 89219c5bac252da6083df15c1d993aaf3038e7f9
File Lines added Lines deleted
man/hh.1 3 0
src/hstr.c 55 32
File man/hh.1 changed (mode: 100644) (index 48dc705..a22898f)
... ... hh allows removal of commands from history - for instance with a typo or with a
15 15 .TP .TP
16 16 \fBpattern\fR \fBpattern\fR
17 17 Type to filter shell history. Type to filter shell history.
18 .TP
19 \fBCtrl\-i\fR
20 Toggle case sensitive search.
18 21 .TP .TP
19 22 \fBUP\fR arrow, \fBDOWN\fR arrow \fBUP\fR arrow, \fBDOWN\fR arrow
20 23 Navigate in the history list. Navigate in the history list.
File src/hstr.c changed (mode: 100644) (index 483b418..dc75181)
30 30 #define Y_OFFSET_HISTORY 3 #define Y_OFFSET_HISTORY 3
31 31 #define Y_OFFSET_ITEMS 4 #define Y_OFFSET_ITEMS 4
32 32
33 #define KEY_TERMINAL_RESIZE 410
34 #define KEY_CTRL_A 1
35 #define KEY_CTRL_E 5
36 #define KEY_CTRL_R 18
37 #define KEY_CTRL_X 24
33 #define K_CTRL_A 1
34 #define K_CTRL_E 5
35 #define K_CTRL_R 18
36 #define K_CTRL_X 24
37 #define K_CTRL_I 9
38 #define K_ENTER 10
39 #define K_ARROW_LEFT 68
40 #define K_ARROW_RIGHT 67
41 #define K_BACKSPACE 127
42 #define K_UP 65
43 #define K_DOWN 66
38 44
39 45 #ifdef DEBUG_KEYS #ifdef DEBUG_KEYS
40 46 #define LOGKEYS(Y,KEY) mvprintw(Y, 0, "Key number: '%3d' / Char: '%c'", KEY, KEY) #define LOGKEYS(Y,KEY) mvprintw(Y, 0, "Key number: '%3d' / Char: '%c'", KEY, KEY)
 
... ... static const char *INSTALL_STRING=
54 60 "\nbind '\"\\C-r\": \"\\C-k\\C-uhh\\C-j\"'" "\nbind '\"\\C-r\": \"\\C-k\\C-uhh\\C-j\"'"
55 61 "\n\n"; "\n\n";
56 62
57 static char *LABEL_HELP=
63 static const char *LABEL_HELP=
58 64 "Type to filter, UP/DOWN to move, Ctrl-r to remove, ENTER to select, Ctrl-x to exit"; "Type to filter, UP/DOWN to move, Ctrl-r to remove, ENTER to select, Ctrl-x to exit";
59 65
60 66 static char **selection=NULL; static char **selection=NULL;
 
... ... void alloc_selection(unsigned size)
127 133 } }
128 134 } }
129 135
130 unsigned make_selection(char *prefix, HistoryItems *history, int maxSelectionCount)
136 unsigned make_selection(char *prefix, HistoryItems *history, int maxSelectionCount, bool caseSensitive)
131 137 { {
132 138 alloc_selection(sizeof(char*) * maxSelectionCount); // TODO realloc alloc_selection(sizeof(char*) * maxSelectionCount); // TODO realloc
133 139 unsigned i, selectionCount=0; unsigned i, selectionCount=0;
 
... ... unsigned make_selection(char *prefix, HistoryItems *history, int maxSelectionCou
145 151 } }
146 152
147 153 if(prefix && selectionCount<maxSelectionCount) { if(prefix && selectionCount<maxSelectionCount) {
154 char *substring;
148 155 for(i=0; i<history->count && selectionCount<maxSelectionCount; i++) { for(i=0; i<history->count && selectionCount<maxSelectionCount; i++) {
149 char *substring = strstr(history->items[i], prefix);
150 if (substring != NULL && substring!=history->items[i]) {
151 selection[selectionCount++]=history->items[i];
156 if(caseSensitive) {
157 substring = strstr(history->items[i], prefix);
158 if (substring != NULL && substring!=history->items[i]) {
159 selection[selectionCount++]=history->items[i];
160 }
161 } else {
162 substring = strcasestr(history->items[i], prefix);
163 if (substring != NULL && substring!=history->items[i]) {
164 selection[selectionCount++]=history->items[i];
165 }
152 166 } }
153 167 } }
154 168 } }
 
... ... unsigned make_selection(char *prefix, HistoryItems *history, int maxSelectionCou
157 171 return selectionCount; return selectionCount;
158 172 } }
159 173
160 char *print_selection(WINDOW *win, unsigned maxHistoryItems, char *prefix, HistoryItems *history)
174 char *print_selection(WINDOW *win, unsigned maxHistoryItems, char *prefix, HistoryItems *history, bool caseSensitive)
161 175 { {
162 176 char *result=""; char *result="";
163 unsigned selectionCount=make_selection(prefix, history, maxHistoryItems);
177 unsigned selectionCount=make_selection(prefix, history, maxHistoryItems, caseSensitive);
164 178 if (selectionCount > 0) { if (selectionCount > 0) {
165 179 result = selection[0]; result = selection[0];
166 180 } }
 
... ... char *print_selection(WINDOW *win, unsigned maxHistoryItems, char *prefix, Histo
173 187 move(Y_OFFSET_ITEMS, 0); move(Y_OFFSET_ITEMS, 0);
174 188 wclrtobot(win); wclrtobot(win);
175 189
190 char *p;
176 191 for (i = 0; i<height; ++i) { for (i = 0; i<height; ++i) {
177 192 if(i<selectionSize) { if(i<selectionSize) {
178 193 snprintf(screenLine, width, " %s", selection[i]); snprintf(screenLine, width, " %s", selection[i]);
179 194 mvwprintw(win, y++, 0, screenLine); mvwprintw(win, y++, 0, screenLine);
180 195 if(prefix!=NULL) { if(prefix!=NULL) {
181 196 wattron(win,A_BOLD); wattron(win,A_BOLD);
182 char *p=strstr(selection[i], prefix);
197 if(caseSensitive) {
198 p=strstr(selection[i], prefix);
199 } else {
200 p=strcasestr(selection[i], prefix);
201 }
183 202 mvwprintw(win, (y-1), 1+(p-selection[i]), "%s", prefix); mvwprintw(win, (y-1), 1+(p-selection[i]), "%s", prefix);
184 203 wattroff(win,A_BOLD); wattroff(win,A_BOLD);
185 204 } }
 
... ... char *selection_loop(HistoryItems *history)
254 273 color_attr_on(COLOR_PAIR(1)); color_attr_on(COLOR_PAIR(1));
255 274 print_history_label(stdscr); print_history_label(stdscr);
256 275 print_help_label(stdscr); print_help_label(stdscr);
257 print_selection(stdscr, get_max_history_items(stdscr), NULL, history);
276 bool caseSensitive=FALSE;
277 print_selection(stdscr, get_max_history_items(stdscr), NULL, history, caseSensitive);
258 278 int basex = print_prompt(stdscr); int basex = print_prompt(stdscr);
259 279 int x = basex; int x = basex;
260 280 int width=getmaxx(stdscr); int width=getmaxx(stdscr);
 
... ... char *selection_loop(HistoryItems *history)
275 295 echo(); echo();
276 296
277 297 switch (c) { switch (c) {
278 case KEY_TERMINAL_RESIZE:
279 case KEY_CTRL_A:
280 case KEY_CTRL_E:
298 case KEY_RESIZE:
299 case K_CTRL_A:
300 case K_CTRL_E:
301 case K_ARROW_LEFT:
302 case K_ARROW_RIGHT:
303 case 91: // TODO 91 killed > debug to determine how to distinguish \e and [
281 304 break; break;
282 case KEY_CTRL_R:
305 case K_CTRL_R:
283 306 if(selectionCursorPosition!=SELECTION_CURSOR_IN_PROMPT) { if(selectionCursorPosition!=SELECTION_CURSOR_IN_PROMPT) {
284 307 delete=selection[selectionCursorPosition]; delete=selection[selectionCursorPosition];
285 308 msg=malloc(strlen(delete)+1); msg=malloc(strlen(delete)+1);
286 309 strcpy(msg,delete); strcpy(msg,delete);
287 310 selection_remove(delete, history); selection_remove(delete, history);
288 311 deleteOccurences=history_mgmt_remove(delete); deleteOccurences=history_mgmt_remove(delete);
289 result = print_selection(stdscr, maxHistoryItems, prefix, history);
312 result = print_selection(stdscr, maxHistoryItems, prefix, history, caseSensitive);
290 313 print_cmd_deleted_label(stdscr, msg, deleteOccurences); print_cmd_deleted_label(stdscr, msg, deleteOccurences);
291 314 move(y, basex+strlen(prefix)); move(y, basex+strlen(prefix));
292 315 } }
293 316 break; break;
294 case 91: // TODO 91 killed > debug to determine how to distinguish \e and [
295 case 68: // left arrow
296 case 67: // rigtht arrow
297 break;
298 317 case KEY_BACKSPACE: case KEY_BACKSPACE:
299 case 127:
318 case K_BACKSPACE:
300 319 if(strlen(prefix)>0) { if(strlen(prefix)>0) {
301 320 prefix[strlen(prefix)-1]=0; prefix[strlen(prefix)-1]=0;
302 321 x--; x--;
 
... ... char *selection_loop(HistoryItems *history)
307 326 } }
308 327
309 328 if(strlen(prefix)>0) { if(strlen(prefix)>0) {
310 make_selection(prefix, history, maxHistoryItems);
329 make_selection(prefix, history, maxHistoryItems, caseSensitive);
311 330 } else { } else {
312 make_selection(NULL, history, maxHistoryItems);
331 make_selection(NULL, history, maxHistoryItems, caseSensitive);
313 332 } }
314 result = print_selection(stdscr, maxHistoryItems, prefix, history);
333 result = print_selection(stdscr, maxHistoryItems, prefix, history, caseSensitive);
315 334
316 335 move(y, basex+strlen(prefix)); move(y, basex+strlen(prefix));
317 336 break; break;
318 337 case KEY_UP: case KEY_UP:
319 case 65:
338 case K_UP:
320 339 previousSelectionCursorPosition=selectionCursorPosition; previousSelectionCursorPosition=selectionCursorPosition;
321 340 if(selectionCursorPosition>0) { if(selectionCursorPosition>0) {
322 341 selectionCursorPosition--; selectionCursorPosition--;
 
... ... char *selection_loop(HistoryItems *history)
326 345 highlight_selection(selectionCursorPosition, previousSelectionCursorPosition); highlight_selection(selectionCursorPosition, previousSelectionCursorPosition);
327 346 break; break;
328 347 case KEY_DOWN: case KEY_DOWN:
329 case 66:
348 case K_DOWN:
330 349 if(selectionCursorPosition==SELECTION_CURSOR_IN_PROMPT) { if(selectionCursorPosition==SELECTION_CURSOR_IN_PROMPT) {
331 350 selectionCursorPosition=previousSelectionCursorPosition=0; selectionCursorPosition=previousSelectionCursorPosition=0;
332 351 } else { } else {
 
... ... char *selection_loop(HistoryItems *history)
339 358 } }
340 359 highlight_selection(selectionCursorPosition, previousSelectionCursorPosition); highlight_selection(selectionCursorPosition, previousSelectionCursorPosition);
341 360 break; break;
342 case 10:
361 case KEY_ENTER:
362 case K_ENTER:
343 363 if(selectionCursorPosition!=SELECTION_CURSOR_IN_PROMPT) { if(selectionCursorPosition!=SELECTION_CURSOR_IN_PROMPT) {
344 364 result=selection[selectionCursorPosition]; result=selection[selectionCursorPosition];
345 365 alloc_selection(0); alloc_selection(0);
346 366 } }
347 367 done = TRUE; done = TRUE;
348 368 break; break;
349 case KEY_CTRL_X:
369 case K_CTRL_I:
370 caseSensitive=!caseSensitive;
371 break;
372 case K_CTRL_X:
350 373 result = NULL; result = NULL;
351 374 done = TRUE; done = TRUE;
352 375 break; break;
 
... ... char *selection_loop(HistoryItems *history)
367 390 clrtoeol(); clrtoeol();
368 391 } }
369 392
370 result = print_selection(stdscr, maxHistoryItems, prefix, history);
393 result = print_selection(stdscr, maxHistoryItems, prefix, history, caseSensitive);
371 394 move(cursorY, cursorX); move(cursorY, cursorX);
372 395 refresh(); refresh();
373 396 } }
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