dmenu-navhistory-20200709.diff (5285B)
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..9d15f78 100644 39 --- a/dmenu.c 40 +++ b/dmenu.c 41 @@ -53,6 +53,10 @@ static XIC xic; 42 static Drw *drw; 43 static Clr *scheme[SchemeLast]; 44 45 +static char *histfile; 46 +static char **history; 47 +static size_t histsz, histpos; 48 + 49 #include "config.h" 50 51 static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; 52 @@ -304,6 +308,129 @@ movewordedge(int dir) 53 } 54 } 55 56 +static void 57 +loadhistory(void) 58 +{ 59 + FILE *fp = NULL; 60 + static size_t cap = 0; 61 + size_t llen; 62 + char *line; 63 + 64 + if (!histfile) { 65 + return; 66 + } 67 + 68 + fp = fopen(histfile, "r"); 69 + if (!fp) { 70 + return; 71 + } 72 + 73 + for (;;) { 74 + line = NULL; 75 + llen = 0; 76 + if (-1 == getline(&line, &llen, fp)) { 77 + if (ferror(fp)) { 78 + die("failed to read history"); 79 + } 80 + free(line); 81 + break; 82 + } 83 + 84 + if (cap == histsz) { 85 + cap += 64 * sizeof(char*); 86 + history = realloc(history, cap); 87 + if (!history) { 88 + die("failed to realloc memory"); 89 + } 90 + } 91 + strtok(line, "\n"); 92 + history[histsz] = line; 93 + histsz++; 94 + } 95 + histpos = histsz; 96 + 97 + if (fclose(fp)) { 98 + die("failed to close file %s", histfile); 99 + } 100 +} 101 + 102 +static void 103 +navhistory(int dir) 104 +{ 105 + static char def[BUFSIZ]; 106 + char *p = NULL; 107 + size_t len = 0; 108 + 109 + if (!history || histpos + 1 == 0) 110 + return; 111 + 112 + if (histsz == histpos) { 113 + strncpy(def, text, sizeof(def)); 114 + } 115 + 116 + switch(dir) { 117 + case 1: 118 + if (histpos < histsz - 1) { 119 + p = history[++histpos]; 120 + } else if (histpos == histsz - 1) { 121 + p = def; 122 + histpos++; 123 + } 124 + break; 125 + case -1: 126 + if (histpos > 0) { 127 + p = history[--histpos]; 128 + } 129 + break; 130 + } 131 + if (p == NULL) { 132 + return; 133 + } 134 + 135 + len = MIN(strlen(p), BUFSIZ - 1); 136 + strncpy(text, p, len); 137 + text[len] = '\0'; 138 + cursor = len; 139 + match(); 140 +} 141 + 142 +static void 143 +savehistory(char *input) 144 +{ 145 + unsigned int i; 146 + FILE *fp; 147 + 148 + if (!histfile || 149 + 0 == maxhist || 150 + 0 == strlen(input)) { 151 + goto out; 152 + } 153 + 154 + fp = fopen(histfile, "w"); 155 + if (!fp) { 156 + die("failed to open %s", histfile); 157 + } 158 + for (i = histsz < maxhist ? 0 : histsz - maxhist; i < histsz; i++) { 159 + if (0 >= fprintf(fp, "%s\n", history[i])) { 160 + die("failed to write to %s", histfile); 161 + } 162 + } 163 + if (!histnodup || (histsz > 0 && strcmp(input, history[histsz-1]) != 0)) { /* TODO */ 164 + if (0 >= fputs(input, fp)) { 165 + die("failed to write to %s", histfile); 166 + } 167 + } 168 + if (fclose(fp)) { 169 + die("failed to close file %s", histfile); 170 + } 171 + 172 +out: 173 + for (i = 0; i < histsz; i++) { 174 + free(history[i]); 175 + } 176 + free(history); 177 +} 178 + 179 static void 180 keypress(XKeyEvent *ev) 181 { 182 @@ -388,6 +515,14 @@ keypress(XKeyEvent *ev) 183 case XK_j: ksym = XK_Next; break; 184 case XK_k: ksym = XK_Prior; break; 185 case XK_l: ksym = XK_Down; break; 186 + case XK_p: 187 + navhistory(-1); 188 + buf[0]=0; 189 + break; 190 + case XK_n: 191 + navhistory(1); 192 + buf[0]=0; 193 + break; 194 default: 195 return; 196 } 197 @@ -466,6 +601,8 @@ insert: 198 case XK_KP_Enter: 199 puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); 200 if (!(ev->state & ControlMask)) { 201 + savehistory((sel && !(ev->state & ShiftMask)) 202 + ? sel->text : text); 203 cleanup(); 204 exit(0); 205 } 206 @@ -690,7 +827,8 @@ static void 207 usage(void) 208 { 209 fputs("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" 210 - " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n", stderr); 211 + " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n" 212 + " [-H histfile]", stderr); 213 exit(1); 214 } 215 216 @@ -715,6 +853,8 @@ main(int argc, char *argv[]) 217 } else if (i + 1 == argc) 218 usage(); 219 /* these options take one argument */ 220 + else if (!strcmp(argv[i], "-H")) 221 + histfile = argv[++i]; 222 else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */ 223 lines = atoi(argv[++i]); 224 else if (!strcmp(argv[i], "-m")) 225 @@ -757,6 +897,8 @@ main(int argc, char *argv[]) 226 die("pledge"); 227 #endif 228 229 + loadhistory(); 230 + 231 if (fast && !isatty(0)) { 232 grabkeyboard(); 233 readstdin(); 234 diff --git a/dmenu_run b/dmenu_run 235 index 834ede5..59ec622 100755 236 --- a/dmenu_run 237 +++ b/dmenu_run 238 @@ -1,2 +1,2 @@ 239 #!/bin/sh 240 -dmenu_path | dmenu "$@" | ${SHELL:-"/bin/sh"} & 241 +dmenu_path | dmenu -H "${XDG_CACHE_HOME:-$HOME/.cache/}/dmenu_run.hist" "$@" | ${SHELL:-"/bin/sh"} &