dmenu-navhistory+search-20200709.diff (6371B)
1 diff --git a/config.def.h b/config.def.h 2 index 1edb647..e3e1b53 100644 3 --- a/config.def.h 4 +++ b/config.def.h 5 @@ -15,6 +15,8 @@ static const char *colors[SchemeLast][2] = { 6 }; 7 /* -l option; if nonzero, dmenu uses vertical list with given number of lines */ 8 static unsigned int lines = 0; 9 +static unsigned int maxhist = 64; 10 +static int histnodup = 1; /* if 0, record repeated histories */ 11 12 /* 13 * Characters not considered part of a word while deleting words 14 diff --git a/dmenu.1 b/dmenu.1 15 index 323f93c..ff496dd 100644 16 --- a/dmenu.1 17 +++ b/dmenu.1 18 @@ -22,6 +22,8 @@ dmenu \- dynamic menu 19 .IR color ] 20 .RB [ \-w 21 .IR windowid ] 22 +.RB [ \-H 23 +.IR histfile ] 24 .P 25 .BR dmenu_run " ..." 26 .SH DESCRIPTION 27 @@ -80,6 +82,9 @@ prints version information to stdout, then exits. 28 .TP 29 .BI \-w " windowid" 30 embed into windowid. 31 +.TP 32 +.BI \-H " histfile" 33 +save input in histfile and use it for history navigation. 34 .SH USAGE 35 dmenu is completely controlled by the keyboard. Items are selected using the 36 arrow keys, page up, page down, home, and end. 37 diff --git a/dmenu.c b/dmenu.c 38 index 65f25ce..4242eb4 100644 39 --- a/dmenu.c 40 +++ b/dmenu.c 41 @@ -40,7 +40,7 @@ static int bh, mw, mh; 42 static int inputw = 0, promptw; 43 static int lrpad; /* sum of left and right padding */ 44 static size_t cursor; 45 -static struct item *items = NULL; 46 +static struct item *items = NULL, *backup_items; 47 static struct item *matches, *matchend; 48 static struct item *prev, *curr, *next, *sel; 49 static int mon = -1, screen; 50 @@ -53,6 +53,10 @@ static XIC xic; 51 static Drw *drw; 52 static Clr *scheme[SchemeLast]; 53 54 +static char *histfile; 55 +static char **history; 56 +static size_t histsz, histpos; 57 + 58 #include "config.h" 59 60 static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; 61 @@ -304,11 +308,134 @@ movewordedge(int dir) 62 } 63 } 64 65 +static void 66 +loadhistory(void) 67 +{ 68 + FILE *fp = NULL; 69 + static size_t cap = 0; 70 + size_t llen; 71 + char *line; 72 + 73 + if (!histfile) { 74 + return; 75 + } 76 + 77 + fp = fopen(histfile, "r"); 78 + if (!fp) { 79 + return; 80 + } 81 + 82 + for (;;) { 83 + line = NULL; 84 + llen = 0; 85 + if (-1 == getline(&line, &llen, fp)) { 86 + if (ferror(fp)) { 87 + die("failed to read history"); 88 + } 89 + free(line); 90 + break; 91 + } 92 + 93 + if (cap == histsz) { 94 + cap += 64 * sizeof(char*); 95 + history = realloc(history, cap); 96 + if (!history) { 97 + die("failed to realloc memory"); 98 + } 99 + } 100 + strtok(line, "\n"); 101 + history[histsz] = line; 102 + histsz++; 103 + } 104 + histpos = histsz; 105 + 106 + if (fclose(fp)) { 107 + die("failed to close file %s", histfile); 108 + } 109 +} 110 + 111 +static void 112 +navhistory(int dir) 113 +{ 114 + static char def[BUFSIZ]; 115 + char *p = NULL; 116 + size_t len = 0; 117 + 118 + if (!history || histpos + 1 == 0) 119 + return; 120 + 121 + if (histsz == histpos) { 122 + strncpy(def, text, sizeof(def)); 123 + } 124 + 125 + switch(dir) { 126 + case 1: 127 + if (histpos < histsz - 1) { 128 + p = history[++histpos]; 129 + } else if (histpos == histsz - 1) { 130 + p = def; 131 + histpos++; 132 + } 133 + break; 134 + case -1: 135 + if (histpos > 0) { 136 + p = history[--histpos]; 137 + } 138 + break; 139 + } 140 + if (p == NULL) { 141 + return; 142 + } 143 + 144 + len = MIN(strlen(p), BUFSIZ - 1); 145 + strncpy(text, p, len); 146 + text[len] = '\0'; 147 + cursor = len; 148 + match(); 149 +} 150 + 151 +static void 152 +savehistory(char *input) 153 +{ 154 + unsigned int i; 155 + FILE *fp; 156 + 157 + if (!histfile || 158 + 0 == maxhist || 159 + 0 == strlen(input)) { 160 + goto out; 161 + } 162 + 163 + fp = fopen(histfile, "w"); 164 + if (!fp) { 165 + die("failed to open %s", histfile); 166 + } 167 + for (i = histsz < maxhist ? 0 : histsz - maxhist; i < histsz; i++) { 168 + if (0 >= fprintf(fp, "%s\n", history[i])) { 169 + die("failed to write to %s", histfile); 170 + } 171 + } 172 + if (!histnodup || (histsz > 0 && strcmp(input, history[histsz-1]) != 0)) { /* TODO */ 173 + if (0 >= fputs(input, fp)) { 174 + die("failed to write to %s", histfile); 175 + } 176 + } 177 + if (fclose(fp)) { 178 + die("failed to close file %s", histfile); 179 + } 180 + 181 +out: 182 + for (i = 0; i < histsz; i++) { 183 + free(history[i]); 184 + } 185 + free(history); 186 +} 187 + 188 static void 189 keypress(XKeyEvent *ev) 190 { 191 char buf[32]; 192 - int len; 193 + int len, i; 194 KeySym ksym; 195 Status status; 196 197 @@ -359,6 +486,26 @@ keypress(XKeyEvent *ev) 198 XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, 199 utf8, utf8, win, CurrentTime); 200 return; 201 + case XK_r: 202 + if (histfile) { 203 + if (!backup_items) { 204 + backup_items = items; 205 + items = calloc(histsz + 1, sizeof(struct item)); 206 + if (!items) { 207 + die("cannot allocate memory"); 208 + } 209 + 210 + for (i = 0; i < histsz; i++) { 211 + items[i].text = history[i]; 212 + } 213 + } else { 214 + free(items); 215 + items = backup_items; 216 + backup_items = NULL; 217 + } 218 + } 219 + match(); 220 + goto draw; 221 case XK_Left: 222 movewordedge(-1); 223 goto draw; 224 @@ -388,6 +535,14 @@ keypress(XKeyEvent *ev) 225 case XK_j: ksym = XK_Next; break; 226 case XK_k: ksym = XK_Prior; break; 227 case XK_l: ksym = XK_Down; break; 228 + case XK_p: 229 + navhistory(-1); 230 + buf[0]=0; 231 + break; 232 + case XK_n: 233 + navhistory(1); 234 + buf[0]=0; 235 + break; 236 default: 237 return; 238 } 239 @@ -466,6 +621,8 @@ insert: 240 case XK_KP_Enter: 241 puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); 242 if (!(ev->state & ControlMask)) { 243 + savehistory((sel && !(ev->state & ShiftMask)) 244 + ? sel->text : text); 245 cleanup(); 246 exit(0); 247 } 248 @@ -690,7 +847,8 @@ static void 249 usage(void) 250 { 251 fputs("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" 252 - " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n", stderr); 253 + " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n" 254 + " [-H histfile]", stderr); 255 exit(1); 256 } 257 258 @@ -715,6 +873,8 @@ main(int argc, char *argv[]) 259 } else if (i + 1 == argc) 260 usage(); 261 /* these options take one argument */ 262 + else if (!strcmp(argv[i], "-H")) 263 + histfile = argv[++i]; 264 else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */ 265 lines = atoi(argv[++i]); 266 else if (!strcmp(argv[i], "-m")) 267 @@ -757,6 +917,8 @@ main(int argc, char *argv[]) 268 die("pledge"); 269 #endif 270 271 + loadhistory(); 272 + 273 if (fast && !isatty(0)) { 274 grabkeyboard(); 275 readstdin(); 276 diff --git a/dmenu_run b/dmenu_run 277 index 834ede5..59ec622 100755 278 --- a/dmenu_run 279 +++ b/dmenu_run 280 @@ -1,2 +1,2 @@ 281 #!/bin/sh 282 -dmenu_path | dmenu "$@" | ${SHELL:-"/bin/sh"} & 283 +dmenu_path | dmenu -H "${XDG_CACHE_HOME:-$HOME/.cache/}/dmenu_run.hist" "$@" | ${SHELL:-"/bin/sh"} &