Browse code

fix acceptance tests

Ondrej Martinek authored on 21/03/2019 13:47:58 • John Hawthorn committed on 28/12/2019 07:17:18
Showing 1 changed files
... ...
@@ -109,8 +109,13 @@ static void draw(tty_interface_t *state) {
109 109
 		}
110 110
 	}
111 111
 
112
-	tty_moveup(tty, num_lines + (options->show_info ? 1 : 0));
113
-	tty_setcol(tty, strlen(options->prompt) + state->cursor);
112
+	if (num_lines + options->show_info)
113
+		tty_moveup(tty, num_lines + options->show_info);
114
+
115
+	tty_setcol(tty, 0);
116
+	fputs(options->prompt, tty->fout);
117
+	for (size_t i = 0; i < state->cursor; i++)
118
+		fputc(state->search[i], tty->fout);
114 119
 	tty_flush(tty);
115 120
 }
116 121
 
Browse code

show selection info (with -i option)

Ondrej Martinek authored on 16/03/2019 04:54:08 • John Hawthorn committed on 28/12/2019 07:17:18
Showing 1 changed files
... ...
@@ -20,7 +20,7 @@ static void clear(tty_interface_t *state) {
20 20
 
21 21
 	tty_setcol(tty, 0);
22 22
 	size_t line = 0;
23
-	while (line++ < state->options->num_lines) {
23
+	while (line++ < state->options->num_lines + (state->options->show_info ? 1 : 0)) {
24 24
 		tty_newline(tty);
25 25
 	}
26 26
 	tty_clearline(tty);
... ...
@@ -90,9 +90,16 @@ static void draw(tty_interface_t *state) {
90 90
 			start = available - num_lines;
91 91
 		}
92 92
 	}
93
+
93 94
 	tty_setcol(tty, 0);
94 95
 	tty_printf(tty, "%s%s", options->prompt, state->search);
95 96
 	tty_clearline(tty);
97
+
98
+	if (options->show_info) {
99
+		tty_printf(tty, "\n[%lu/%lu]", choices->available, choices->size);
100
+		tty_clearline(tty);
101
+	}
102
+
96 103
 	for (size_t i = start; i < start + num_lines; i++) {
97 104
 		tty_printf(tty, "\n");
98 105
 		tty_clearline(tty);
... ...
@@ -101,14 +108,9 @@ static void draw(tty_interface_t *state) {
101 108
 			draw_match(state, choice, i == choices->selection);
102 109
 		}
103 110
 	}
104
-	if (num_lines > 0) {
105
-		tty_moveup(tty, num_lines);
106
-	}
107 111
 
108
-	tty_setcol(tty, 0);
109
-	fputs(options->prompt, tty->fout);
110
-	for (size_t i = 0; i < state->cursor; i++)
111
-		fputc(state->search[i], tty->fout);
112
+	tty_moveup(tty, num_lines + (options->show_info ? 1 : 0));
113
+	tty_setcol(tty, strlen(options->prompt) + state->cursor);
112 114
 	tty_flush(tty);
113 115
 }
114 116
 
Browse code

Merge pull request #125 from gpanders/ctrl-g-exit

Cancel with Ctrl-G

John Hawthorn authored on 28/12/2019 07:01:53 • GitHub committed on 28/12/2019 07:01:53
Showing 0 changed files
Browse code

Avoid VLA in tty_interface

John Hawthorn authored on 28/12/2019 03:39:50
Showing 1 changed files
... ...
@@ -36,8 +36,8 @@ static void draw_match(tty_interface_t *state, const char *choice, int selected)
36 36
 	char *search = state->last_search;
37 37
 
38 38
 	int n = strlen(search);
39
-	size_t positions[n + 1];
40
-	for (int i = 0; i < n + 1; i++)
39
+	size_t positions[MATCH_MAX_LEN];
40
+	for (int i = 0; i < n + 1 && i < MATCH_MAX_LEN; i++)
41 41
 		positions[i] = -1;
42 42
 
43 43
 	score_t score = match_positions(search, choice, &positions[0]);
Browse code

Cancel with Ctrl-G

Greg Anders authored on 13/10/2019 22:10:34
Showing 1 changed files
... ...
@@ -291,6 +291,7 @@ static const keybinding_t keybindings[] = {{"\x1b", action_exit},       /* ESC *
291 291
 					   {KEY_CTRL('I'), action_autocomplete}, /* TAB (C-I ) */
292 292
 					   {KEY_CTRL('C'), action_exit},	 /* C-C */
293 293
 					   {KEY_CTRL('D'), action_exit},	 /* C-D */
294
+					   {KEY_CTRL('G'), action_exit},	 /* C-G */
294 295
 					   {KEY_CTRL('M'), action_emit},	 /* CR */
295 296
 					   {KEY_CTRL('P'), action_prev},	 /* C-P */
296 297
 					   {KEY_CTRL('N'), action_next},	 /* C-N */
Browse code

Add ability to use null as input delimiter.

Update tty to print newline as space
Add tty_putc

Ashkan Kiani authored on 03/05/2019 10:30:13 • John Hawthorn committed on 16/08/2019 08:05:01
Showing 1 changed files
... ...
@@ -65,7 +65,11 @@ static void draw_match(tty_interface_t *state, const char *choice, int selected)
65 65
 		} else {
66 66
 			tty_setfg(tty, TTY_COLOR_NORMAL);
67 67
 		}
68
-		tty_printf(tty, "%c", choice[i]);
68
+		if (choice[i] == '\n') {
69
+			tty_putc(tty, ' ');
70
+		} else {
71
+			tty_printf(tty, "%c", choice[i]);
72
+		}
69 73
 	}
70 74
 	tty_setwrap(tty);
71 75
 	tty_setnormal(tty);
Browse code

Use do {} while in action_del_char

John Hawthorn authored on 23/09/2018 18:52:58
Showing 1 changed files
... ...
@@ -148,9 +148,9 @@ static void action_del_char(tty_interface_t *state) {
148 148
 	}
149 149
 	size_t original_cursor = state->cursor;
150 150
 
151
-	state->cursor--;
152
-	while (!is_boundary(state->search[state->cursor]) && state->cursor)
151
+	do {
153 152
 		state->cursor--;
153
+	} while (!is_boundary(state->search[state->cursor]) && state->cursor);
154 154
 
155 155
 	memmove(&state->search[state->cursor], &state->search[original_cursor], length - original_cursor + 1);
156 156
 }
Browse code

Remove unnecessary check from action_del_char

John Hawthorn authored on 16/09/2018 03:59:46
Showing 1 changed files
... ...
@@ -142,19 +142,17 @@ static void action_emit(tty_interface_t *state) {
142 142
 }
143 143
 
144 144
 static void action_del_char(tty_interface_t *state) {
145
-	if (*state->search) {
146
-		size_t length = strlen(state->search);
147
-		if (state->cursor == 0) {
148
-			return;
149
-		}
150
-		size_t original_cursor = state->cursor;
145
+	size_t length = strlen(state->search);
146
+	if (state->cursor == 0) {
147
+		return;
148
+	}
149
+	size_t original_cursor = state->cursor;
151 150
 
151
+	state->cursor--;
152
+	while (!is_boundary(state->search[state->cursor]) && state->cursor)
152 153
 		state->cursor--;
153
-		while (!is_boundary(state->search[state->cursor]) && state->cursor)
154
-			state->cursor--;
155 154
 
156
-		memmove(&state->search[state->cursor], &state->search[original_cursor], length - original_cursor + 1);
157
-	}
155
+	memmove(&state->search[state->cursor], &state->search[original_cursor], length - original_cursor + 1);
158 156
 }
159 157
 
160 158
 static void action_del_word(tty_interface_t *state) {
Browse code

Redraw on signals

John Hawthorn authored on 15/09/2018 21:20:29
Showing 1 changed files
... ...
@@ -366,6 +366,11 @@ int tty_interface_run(tty_interface_t *state) {
366 366
 
367 367
 	for (;;) {
368 368
 		do {
369
+			while(!tty_input_ready(state->tty, -1, 1)) {
370
+				/* We received a signal (probably WINCH) */
371
+				draw(state);
372
+			}
373
+
369 374
 			char s[2] = {tty_getchar(state->tty), '\0'};
370 375
 			handle_input(state, s, 0);
371 376
 
Browse code

Allow masking signals in tty_input_ready

John Hawthorn authored on 15/09/2018 21:15:28
Showing 1 changed files
... ...
@@ -373,7 +373,7 @@ int tty_interface_run(tty_interface_t *state) {
373 373
 				return state->exit;
374 374
 
375 375
 			draw(state);
376
-		} while (tty_input_ready(state->tty, state->ambiguous_key_pending ? KEYTIMEOUT : 0));
376
+		} while (tty_input_ready(state->tty, state->ambiguous_key_pending ? KEYTIMEOUT : 0, 0));
377 377
 
378 378
 		if (state->ambiguous_key_pending) {
379 379
 			char s[1] = "";
Browse code

Pass a timeout to tty_input_ready

John Hawthorn authored on 15/09/2018 20:55:10
Showing 1 changed files
... ...
@@ -373,7 +373,7 @@ int tty_interface_run(tty_interface_t *state) {
373 373
 				return state->exit;
374 374
 
375 375
 			draw(state);
376
-		} while (tty_input_ready(state->tty, state->ambiguous_key_pending));
376
+		} while (tty_input_ready(state->tty, state->ambiguous_key_pending ? KEYTIMEOUT : 0));
377 377
 
378 378
 		if (state->ambiguous_key_pending) {
379 379
 			char s[1] = "";
Browse code

Merge branch 'abort_on_escape'

John Hawthorn authored on 10/09/2018 04:02:45
Showing 0 changed files
Browse code

Fix formatting

John Hawthorn authored on 09/09/2018 21:12:00
Showing 1 changed files
... ...
@@ -7,12 +7,12 @@
7 7
 #include "tty_interface.h"
8 8
 #include "../config.h"
9 9
 
10
-static int isprint_unicode(char c){
11
-	return isprint(c) || c & (1<<7);
10
+static int isprint_unicode(char c) {
11
+	return isprint(c) || c & (1 << 7);
12 12
 }
13 13
 
14 14
 static int is_boundary(char c) {
15
-	return ~c & (1<<7) || c & (1<<6);
15
+	return ~c & (1 << 7) || c & (1 << 6);
16 16
 }
17 17
 
18 18
 static void clear(tty_interface_t *state) {
... ...
@@ -103,7 +103,7 @@ static void draw(tty_interface_t *state) {
103 103
 
104 104
 	tty_setcol(tty, 0);
105 105
 	fputs(options->prompt, tty->fout);
106
-	for(size_t i=0; i<state->cursor; i++)
106
+	for (size_t i = 0; i < state->cursor; i++)
107 107
 		fputc(state->search[i], tty->fout);
108 108
 	tty_flush(tty);
109 109
 }
... ...
@@ -144,13 +144,13 @@ static void action_emit(tty_interface_t *state) {
144 144
 static void action_del_char(tty_interface_t *state) {
145 145
 	if (*state->search) {
146 146
 		size_t length = strlen(state->search);
147
-		if(state->cursor == 0) {
147
+		if (state->cursor == 0) {
148 148
 			return;
149 149
 		}
150 150
 		size_t original_cursor = state->cursor;
151 151
 
152 152
 		state->cursor--;
153
-		while(!is_boundary(state->search[state->cursor]) && state->cursor)
153
+		while (!is_boundary(state->search[state->cursor]) && state->cursor)
154 154
 			state->cursor--;
155 155
 
156 156
 		memmove(&state->search[state->cursor], &state->search[original_cursor], length - original_cursor + 1);
... ...
@@ -182,7 +182,7 @@ static void action_prev(tty_interface_t *state) {
182 182
 }
183 183
 
184 184
 static void action_ignore(tty_interface_t *state) {
185
-	(void) state;
185
+	(void)state;
186 186
 }
187 187
 
188 188
 static void action_next(tty_interface_t *state) {
... ...
@@ -191,17 +191,17 @@ static void action_next(tty_interface_t *state) {
191 191
 }
192 192
 
193 193
 static void action_left(tty_interface_t *state) {
194
-	if (state->cursor > 0){
194
+	if (state->cursor > 0) {
195 195
 		state->cursor--;
196
-		while(!is_boundary(state->search[state->cursor]) && state->cursor)
196
+		while (!is_boundary(state->search[state->cursor]) && state->cursor)
197 197
 			state->cursor--;
198 198
 	}
199 199
 }
200 200
 
201 201
 static void action_right(tty_interface_t *state) {
202
-	if (state->cursor < strlen(state->search)){
202
+	if (state->cursor < strlen(state->search)) {
203 203
 		state->cursor++;
204
-		while(!is_boundary(state->search[state->cursor]))
204
+		while (!is_boundary(state->search[state->cursor]))
205 205
 			state->cursor++;
206 206
 	}
207 207
 }
... ...
@@ -216,13 +216,13 @@ static void action_end(tty_interface_t *state) {
216 216
 
217 217
 static void action_pageup(tty_interface_t *state) {
218 218
 	update_state(state);
219
-	for(size_t i = 0; i < state->options->num_lines && state->choices->selection > 0; i++)
219
+	for (size_t i = 0; i < state->options->num_lines && state->choices->selection > 0; i++)
220 220
 		choices_prev(state->choices);
221 221
 }
222 222
 
223 223
 static void action_pagedown(tty_interface_t *state) {
224 224
 	update_state(state);
225
-	for(size_t i = 0; i < state->options->num_lines && state->choices->selection < state->choices->available-1; i++)
225
+	for (size_t i = 0; i < state->options->num_lines && state->choices->selection < state->choices->available - 1; i++)
226 226
 		choices_next(state->choices);
227 227
 }
228 228
 
Browse code

Disable line wrap when printing candidates

This solves the line wrapping issue with much simpler code, which also
works better with Unicode characters and when the terminal is resized.

John Hawthorn authored on 09/09/2018 20:05:12
Showing 1 changed files
... ...
@@ -42,15 +42,12 @@ static void draw_match(tty_interface_t *state, const char *choice, int selected)
42 42
 
43 43
 	score_t score = match_positions(search, choice, &positions[0]);
44 44
 
45
-	size_t maxwidth = tty_getwidth(tty);
46
-
47
-	if (options->show_scores && maxwidth >= 9) {
45
+	if (options->show_scores) {
48 46
 		if (score == SCORE_MIN) {
49 47
 			tty_printf(tty, "(     ) ");
50 48
 		} else {
51 49
 			tty_printf(tty, "(%5.2f) ", score);
52 50
 		}
53
-		maxwidth -= 8;
54 51
 	}
55 52
 
56 53
 	if (selected)
... ...
@@ -60,20 +57,17 @@ static void draw_match(tty_interface_t *state, const char *choice, int selected)
60 57
 		tty_setinvert(tty);
61 58
 #endif
62 59
 
60
+	tty_setnowrap(tty);
63 61
 	for (size_t i = 0, p = 0; choice[i] != '\0'; i++) {
64
-		if (i + 1 < maxwidth) {
65
-			if (positions[p] == i) {
66
-				tty_setfg(tty, TTY_COLOR_HIGHLIGHT);
67
-				p++;
68
-			} else {
69
-				tty_setfg(tty, TTY_COLOR_NORMAL);
70
-			}
71
-			tty_printf(tty, "%c", choice[i]);
62
+		if (positions[p] == i) {
63
+			tty_setfg(tty, TTY_COLOR_HIGHLIGHT);
64
+			p++;
72 65
 		} else {
73
-			tty_printf(tty, "$");
74
-			break;
66
+			tty_setfg(tty, TTY_COLOR_NORMAL);
75 67
 		}
68
+		tty_printf(tty, "%c", choice[i]);
76 69
 	}
70
+	tty_setwrap(tty);
77 71
 	tty_setnormal(tty);
78 72
 }
79 73
 
Browse code

Merge pull request #77 from syrrim/uni

Add utf-8 support to input, fixes #21

John Hawthorn authored on 09/09/2018 20:07:59 • GitHub committed on 09/09/2018 20:07:59
Showing 0 changed files
Browse code

Add support for underlining selected item

Michael Mackus authored on 18/07/2018 21:54:22
Showing 1 changed files
... ...
@@ -46,7 +46,11 @@ static void draw_match(tty_interface_t *state, const char *choice, int selected)
46 46
 	}
47 47
 
48 48
 	if (selected)
49
+#ifdef TTY_SELECTION_UNDERLINE
50
+		tty_setunderline(tty);
51
+#else
49 52
 		tty_setinvert(tty);
53
+#endif
50 54
 
51 55
 	for (size_t i = 0, p = 0; choice[i] != '\0'; i++) {
52 56
 		if (i + 1 < maxwidth) {
Browse code

tty_interface: Fix comments about CTRL-J and CTRL-K

Fixes: 5a3ce8b ("Support movement with CTRL-J/CTRL-K")

Jonathan Neuschäfer authored on 16/06/2018 20:29:09
Showing 1 changed files
... ...
@@ -270,8 +270,8 @@ static const keybinding_t keybindings[] = {{"\x7f", action_del_char},	/* DEL */
270 270
 					   {KEY_CTRL('M'), action_emit},	 /* CR */
271 271
 					   {KEY_CTRL('P'), action_prev},	 /* C-P */
272 272
 					   {KEY_CTRL('N'), action_next},	 /* C-N */
273
-					   {KEY_CTRL('K'), action_prev},	 /* C-J */
274
-					   {KEY_CTRL('J'), action_next},	 /* C-K */
273
+					   {KEY_CTRL('K'), action_prev},	 /* C-K */
274
+					   {KEY_CTRL('J'), action_next},	 /* C-J */
275 275
 					   {KEY_CTRL('A'), action_beginning},    /* C-A */
276 276
 					   {KEY_CTRL('E'), action_end},		 /* C-E */
277 277
 
Browse code

Abort if Esc is pressed

Jason Felice authored on 04/05/2018 16:32:40
Showing 1 changed files
... ...
@@ -238,6 +238,7 @@ void tty_interface_init(tty_interface_t *state, tty_t *tty, choices_t *choices,
238 238
 	state->tty = tty;
239 239
 	state->choices = choices;
240 240
 	state->options = options;
241
+	state->ambiguous_key_pending = 0;
241 242
 
242 243
 	strcpy(state->input, "");
243 244
 	strcpy(state->search, "");
... ...
@@ -260,7 +261,9 @@ typedef struct {
260 261
 
261 262
 #define KEY_CTRL(key) ((const char[]){((key) - ('@')), '\0'})
262 263
 
263
-static const keybinding_t keybindings[] = {{"\x7f", action_del_char},	/* DEL */
264
+static const keybinding_t keybindings[] = {{"\x1b", action_exit},       /* ESC */
265
+					   {"\x7f", action_del_char},	/* DEL */
266
+
264 267
 					   {KEY_CTRL('H'), action_del_char}, /* Backspace (C-H) */
265 268
 					   {KEY_CTRL('W'), action_del_word}, /* C-W */
266 269
 					   {KEY_CTRL('U'), action_del_all},  /* C-U */
... ...
@@ -295,23 +298,40 @@ static const keybinding_t keybindings[] = {{"\x7f", action_del_char},	/* DEL */
295 298
 
296 299
 #undef KEY_CTRL
297 300
 
298
-static void handle_input(tty_interface_t *state, const char *s) {
301
+static void handle_input(tty_interface_t *state, const char *s, int handle_ambiguous_key) {
302
+	state->ambiguous_key_pending = 0;
303
+
299 304
 	char *input = state->input;
300 305
 	strcat(state->input, s);
301 306
 
302
-	/* See if we have matched a keybinding */
307
+	/* Figure out if we have completed a keybinding and whether we're in the
308
+	 * middle of one (both can happen, because of Esc). */
309
+	int found_keybinding = -1;
310
+	int in_middle = 0;
303 311
 	for (int i = 0; keybindings[i].key; i++) {
304
-		if (!strcmp(input, keybindings[i].key)) {
305
-			keybindings[i].action(state);
306
-			strcpy(input, "");
307
-			return;
308
-		}
312
+		if (!strcmp(input, keybindings[i].key))
313
+			found_keybinding = i;
314
+		else if (!strncmp(input, keybindings[i].key, strlen(state->input)))
315
+			in_middle = 1;
309 316
 	}
310 317
 
311
-	/* Check if we are in the middle of a keybinding */
312
-	for (int i = 0; keybindings[i].key; i++)
313
-		if (!strncmp(input, keybindings[i].key, strlen(input)))
314
-			return;
318
+	/* If we have an unambiguous keybinding, run it.  */
319
+	if (found_keybinding != -1 && (!in_middle || handle_ambiguous_key)) {
320
+		keybindings[found_keybinding].action(state);
321
+		strcpy(input, "");
322
+		return;
323
+	}
324
+
325
+	/* We could have a complete keybinding, or could be in the middle of one.
326
+	 * We'll need to wait a few milliseconds to find out. */
327
+	if (found_keybinding != -1 && in_middle) {
328
+		state->ambiguous_key_pending = 1;
329
+		return;
330
+	}
331
+
332
+	/* Wait for more if we are in the middle of a keybinding */
333
+	if (in_middle)
334
+		return;
315 335
 
316 336
 	/* No matching keybinding, add to search */
317 337
 	for (int i = 0; input[i]; i++)
... ...
@@ -328,13 +348,21 @@ int tty_interface_run(tty_interface_t *state) {
328 348
 	for (;;) {
329 349
 		do {
330 350
 			char s[2] = {tty_getchar(state->tty), '\0'};
331
-			handle_input(state, s);
351
+			handle_input(state, s, 0);
332 352
 
333 353
 			if (state->exit >= 0)
334 354
 				return state->exit;
335 355
 
336 356
 			draw(state);
337
-		} while (tty_input_ready(state->tty));
357
+		} while (tty_input_ready(state->tty, state->ambiguous_key_pending));
358
+
359
+		if (state->ambiguous_key_pending) {
360
+			char s[1] = "";
361
+			handle_input(state, s, 1);
362
+
363
+			if (state->exit >= 0)
364
+				return state->exit;
365
+		}
338 366
 
339 367
 		update_state(state);
340 368
 	}
Browse code

add utf-8 support to input, fixes #21

- non ascii bytes won't be ignored
- one can seek over and delete whole utf-8 codepoints at a time
- the cursor will be positioned properly around double width chars

syrrim authored on 23/04/2018 05:25:48
Showing 1 changed files
... ...
@@ -7,6 +7,14 @@
7 7
 #include "tty_interface.h"
8 8
 #include "../config.h"
9 9
 
10
+static int isprint_unicode(char c){
11
+	return isprint(c) || c & (1<<7);
12
+}
13
+
14
+static int is_boundary(char c) {
15
+	return ~c & (1<<7) || c & (1<<6);
16
+}
17
+
10 18
 static void clear(tty_interface_t *state) {
11 19
 	tty_t *tty = state->tty;
12 20
 
... ...
@@ -95,7 +103,10 @@ static void draw(tty_interface_t *state) {
95 103
 		tty_moveup(tty, num_lines);
96 104
 	}
97 105
 
98
-	tty_setcol(tty, strlen(options->prompt) + state->cursor);
106
+	tty_setcol(tty, 0);
107
+	fputs(options->prompt, tty->fout);
108
+	for(size_t i=0; i<state->cursor; i++)
109
+		fputc(state->search[i], tty->fout);
99 110
 	tty_flush(tty);
100 111
 }
101 112
 
... ...
@@ -138,9 +149,13 @@ static void action_del_char(tty_interface_t *state) {
138 149
 		if(state->cursor == 0) {
139 150
 			return;
140 151
 		}
152
+		size_t original_cursor = state->cursor;
141 153
 
142 154
 		state->cursor--;
143
-		memmove(&state->search[state->cursor], &state->search[state->cursor + 1], length - state->cursor);
155
+		while(!is_boundary(state->search[state->cursor]) && state->cursor)
156
+			state->cursor--;
157
+
158
+		memmove(&state->search[state->cursor], &state->search[original_cursor], length - original_cursor + 1);
144 159
 	}
145 160
 }
146 161
 
... ...
@@ -178,13 +193,19 @@ static void action_next(tty_interface_t *state) {
178 193
 }
179 194
 
180 195
 static void action_left(tty_interface_t *state) {
181
-	if (state->cursor > 0)
196
+	if (state->cursor > 0){
182 197
 		state->cursor--;
198
+		while(!is_boundary(state->search[state->cursor]) && state->cursor)
199
+			state->cursor--;
200
+	}
183 201
 }
184 202
 
185 203
 static void action_right(tty_interface_t *state) {
186
-	if (state->cursor < strlen(state->search))
204
+	if (state->cursor < strlen(state->search)){
187 205
 		state->cursor++;
206
+		while(!is_boundary(state->search[state->cursor]))
207
+			state->cursor++;
208
+	}
188 209
 }
189 210
 
190 211
 static void action_beginning(tty_interface_t *state) {
... ...
@@ -315,7 +336,7 @@ static void handle_input(tty_interface_t *state, const char *s) {
315 336
 
316 337
 	/* No matching keybinding, add to search */
317 338
 	for (int i = 0; input[i]; i++)
318
-		if (isprint(input[i]))
339
+		if (isprint_unicode(input[i]))
319 340
 			append_search(state, input[i]);
320 341
 
321 342
 	/* We have processed the input, so clear it */
Browse code

Merge pull request #71 from tgeng/master

Support cursor jumping with Home and End keys

John Hawthorn authored on 21/04/2018 18:43:30 • GitHub committed on 21/04/2018 18:43:30
Showing 0 changed files
Browse code

Fix home/end support on Mac

Tianyu Geng authored on 20/03/2018 03:53:28
Showing 1 changed files
... ...
@@ -280,7 +280,9 @@ static const keybinding_t keybindings[] = {{"\x7f", action_del_char},	/* DEL */
280 280
 					   {"\x1bOC", action_right}, /* RIGHT */
281 281
 					   {"\x1b[C", action_right}, /* RIGHT */
282 282
 					   {"\x1b[1~", action_beginning}, /* HOME */
283
+					   {"\x1b[H", action_beginning}, /* HOME */
283 284
 					   {"\x1b[4~", action_end}, /* END */
285
+					   {"\x1b[F", action_end}, /* END */
284 286
 					   {"\x1b[A", action_prev}, /* UP */
285 287
 					   {"\x1bOA", action_prev}, /* UP */
286 288
 					   {"\x1b[B", action_next}, /* DOWN */
Browse code

Support cursor jumping with Home and End keys

Tianyu Geng authored on 05/02/2018 22:20:50
Showing 1 changed files
... ...
@@ -279,6 +279,8 @@ static const keybinding_t keybindings[] = {{"\x7f", action_del_char},	/* DEL */
279 279
 					   {"\x1b[D", action_left}, /* LEFT */
280 280
 					   {"\x1bOC", action_right}, /* RIGHT */
281 281
 					   {"\x1b[C", action_right}, /* RIGHT */
282
+					   {"\x1b[1~", action_beginning}, /* HOME */
283
+					   {"\x1b[4~", action_end}, /* END */
282 284
 					   {"\x1b[A", action_prev}, /* UP */
283 285
 					   {"\x1bOA", action_prev}, /* UP */
284 286
 					   {"\x1b[B", action_next}, /* DOWN */
Browse code

tty_interface: Don't show score if it doesn't fit

Jonathan Neuschäfer authored on 24/01/2018 13:44:09
Showing 1 changed files
... ...
@@ -36,7 +36,7 @@ static void draw_match(tty_interface_t *state, const char *choice, int selected)
36 36
 
37 37
 	size_t maxwidth = tty_getwidth(tty);
38 38
 
39
-	if (options->show_scores) {
39
+	if (options->show_scores && maxwidth >= 9) {
40 40
 		if (score == SCORE_MIN) {
41 41
 			tty_printf(tty, "(     ) ");
42 42
 		} else {
Browse code

Rewrite cursor implementation

Index the cursor from the beginning instead of the end, which is easier
(at least for me) to think about.

This also fixes issues with Ctrl-W in the previous implementation.

John Hawthorn authored on 16/10/2017 06:32:41
Showing 1 changed files
... ...
@@ -95,8 +95,7 @@ static void draw(tty_interface_t *state) {
95 95
 		tty_moveup(tty, num_lines);
96 96
 	}
97 97
 
98
-	int length = strlen(options->prompt) + strlen(state->search);
99
-	tty_setcol(tty, length + state->offset);
98
+	tty_setcol(tty, strlen(options->prompt) + state->cursor);
100 99
 	tty_flush(tty);
101 100
 }
102 101
 
... ...
@@ -135,26 +134,33 @@ static void action_emit(tty_interface_t *state) {
135 134
 
136 135
 static void action_del_char(tty_interface_t *state) {
137 136
 	if (*state->search) {
138
-		int length = strlen(state->search);
139
-		int index = length + state->offset - 1;
140
-		if (index < 0) {
137
+		size_t length = strlen(state->search);
138
+		if(state->cursor == 0) {
141 139
 			return;
142 140
 		}
143 141
 
144
-		memmove(&state->search[index], &state->search[index + 1], length - index);
142
+		state->cursor--;
143
+		memmove(&state->search[state->cursor], &state->search[state->cursor + 1], length - state->cursor);
145 144
 	}
146 145
 }
147 146
 
148 147
 static void action_del_word(tty_interface_t *state) {
149
-	size_t search_size = strlen(state->search);
150
-	if (search_size)
151
-		state->search[--search_size] = '\0';
152
-	while (search_size && !isspace(state->search[--search_size]))
153
-		state->search[search_size] = '\0';
148
+	size_t original_cursor = state->cursor;
149
+	size_t cursor = state->cursor;
150
+
151
+	while (cursor && isspace(state->search[cursor - 1]))
152
+		cursor--;
153
+
154
+	while (cursor && !isspace(state->search[cursor - 1]))
155
+		cursor--;
156
+
157
+	memmove(&state->search[cursor], &state->search[original_cursor], strlen(state->search) - original_cursor + 1);
158
+	state->cursor = cursor;
154 159
 }
155 160
 
156 161
 static void action_del_all(tty_interface_t *state) {
157
-	strcpy(state->search, "");
162
+	memmove(state->search, &state->search[state->cursor], strlen(state->search) - state->cursor + 1);
163
+	state->cursor = 0;
158 164
 }
159 165
 
160 166
 static void action_prev(tty_interface_t *state) {
... ...
@@ -172,21 +178,21 @@ static void action_next(tty_interface_t *state) {
172 178
 }
173 179
 
174 180
 static void action_left(tty_interface_t *state) {
175
-	if ((unsigned long)abs(state->offset) < strlen(state->search))
176
-		state->offset--;
181
+	if (state->cursor > 0)
182
+		state->cursor--;
177 183
 }
178 184
 
179 185
 static void action_right(tty_interface_t *state) {
180
-	if (state->offset < 0)
181
-		state->offset++;
186
+	if (state->cursor < strlen(state->search))
187
+		state->cursor++;
182 188
 }
183 189
 
184 190
 static void action_beginning(tty_interface_t *state) {
185
-	state->offset = -strlen(state->search);
191
+	state->cursor = 0;
186 192
 }
187 193
 
188 194
 static void action_end(tty_interface_t *state) {
189
-	state->offset = 0;
195
+	state->cursor = strlen(state->search);
190 196
 }
191 197
 
192 198
 static void action_pageup(tty_interface_t *state) {
... ...
@@ -206,6 +212,7 @@ static void action_autocomplete(tty_interface_t *state) {
206 212
 	const char *current_selection = choices_get(state->choices, state->choices->selection);
207 213
 	if (current_selection) {
208 214
 		strncpy(state->search, choices_get(state->choices, state->choices->selection), SEARCH_SIZE_MAX);
215
+		state->cursor = strlen(state->search);
209 216
 	}
210 217
 }
211 218
 
... ...
@@ -220,14 +227,10 @@ static void append_search(tty_interface_t *state, char ch) {
220 227
 	char *search = state->search;
221 228
 	size_t search_size = strlen(search);
222 229
 	if (search_size < SEARCH_SIZE_MAX) {
223
-		int location = state->offset + search_size;
224
-		for (int i = search_size; i >= 0; i--) {
225
-			if (i >= location) {
226
-				search[i + 1] = search[i];
227
-			}
228
-		}
230
+		memmove(&search[state->cursor+1], &search[state->cursor], search_size - state->cursor + 1);
231
+		search[state->cursor] = ch;
229 232
 
230
-		search[location] = ch;
233
+		state->cursor++;
231 234
 	}
232 235
 }
233 236
 
... ...
@@ -235,7 +238,6 @@ void tty_interface_init(tty_interface_t *state, tty_t *tty, choices_t *choices,
235 238
 	state->tty = tty;
236 239
 	state->choices = choices;
237 240
 	state->options = options;
238
-	state->offset = 0;
239 241
 
240 242
 	strcpy(state->input, "");
241 243
 	strcpy(state->search, "");
... ...
@@ -246,6 +248,8 @@ void tty_interface_init(tty_interface_t *state, tty_t *tty, choices_t *choices,
246 248
 	if (options->init_search)
247 249
 		strncpy(state->search, options->init_search, SEARCH_SIZE_MAX);
248 250
 
251
+	state->cursor = strlen(state->search);
252
+
249 253
 	update_search(state);
250 254
 }
251 255
 
Browse code

Merge pull request #46 from keith/ks/arrow-keys

Support arrow key movements

John Hawthorn authored on 08/10/2017 04:33:40 • GitHub committed on 08/10/2017 04:33:40
Showing 0 changed files
Browse code

Fix line maximum line length when scores are shown

Jonathan Neuschäfer authored on 23/09/2017 16:11:13
Showing 1 changed files
... ...
@@ -42,6 +42,7 @@ static void draw_match(tty_interface_t *state, const char *choice, int selected)
42 42
 		} else {
43 43
 			tty_printf(tty, "(%5.2f) ", score);
44 44
 		}
45
+		maxwidth -= 8;
45 46
 	}
46 47
 
47 48
 	if (selected)
Browse code

Support arrow key movements

Keith Smiley authored on 22/07/2017 03:58:04
Showing 1 changed files
... ...
@@ -93,7 +93,9 @@ static void draw(tty_interface_t *state) {
93 93
 	if (num_lines > 0) {
94 94
 		tty_moveup(tty, num_lines);
95 95
 	}
96
-	tty_setcol(tty, strlen(options->prompt) + strlen(state->search));
96
+
97
+	int length = strlen(options->prompt) + strlen(state->search);
98
+	tty_setcol(tty, length + state->offset);
97 99
 	tty_flush(tty);
98 100
 }
99 101
 
... ...
@@ -131,8 +133,15 @@ static void action_emit(tty_interface_t *state) {
131 133
 }
132 134
 
133 135
 static void action_del_char(tty_interface_t *state) {
134
-	if (*state->search)
135
-		state->search[strlen(state->search) - 1] = '\0';
136
+	if (*state->search) {
137
+		int length = strlen(state->search);
138
+		int index = length + state->offset - 1;
139
+		if (index < 0) {
140
+			return;
141
+		}
142
+
143
+		memmove(&state->search[index], &state->search[index + 1], length - index);
144
+	}
136 145
 }
137 146
 
138 147
 static void action_del_word(tty_interface_t *state) {
... ...
@@ -161,6 +170,24 @@ static void action_next(tty_interface_t *state) {
161 170
 	choices_next(state->choices);
162 171
 }
163 172
 
173
+static void action_left(tty_interface_t *state) {
174
+	if ((unsigned long)abs(state->offset) < strlen(state->search))
175
+		state->offset--;
176
+}
177
+
178
+static void action_right(tty_interface_t *state) {
179
+	if (state->offset < 0)
180
+		state->offset++;
181
+}
182
+
183
+static void action_beginning(tty_interface_t *state) {
184
+	state->offset = -strlen(state->search);
185
+}
186
+
187
+static void action_end(tty_interface_t *state) {
188
+	state->offset = 0;
189
+}
190
+
164 191
 static void action_pageup(tty_interface_t *state) {
165 192
 	update_state(state);
166 193
 	for(size_t i = 0; i < state->options->num_lines && state->choices->selection > 0; i++)
... ...
@@ -192,8 +219,14 @@ static void append_search(tty_interface_t *state, char ch) {
192 219
 	char *search = state->search;
193 220
 	size_t search_size = strlen(search);
194 221
 	if (search_size < SEARCH_SIZE_MAX) {
195
-		search[search_size++] = ch;
196
-		search[search_size] = '\0';
222
+		int location = state->offset + search_size;
223
+		for (int i = search_size; i >= 0; i--) {
224
+			if (i >= location) {
225
+				search[i + 1] = search[i];
226
+			}
227
+		}
228
+
229
+		search[location] = ch;
197 230
 	}
198 231
 }
199 232
 
... ...
@@ -201,6 +234,7 @@ void tty_interface_init(tty_interface_t *state, tty_t *tty, choices_t *choices,
201 234
 	state->tty = tty;
202 235
 	state->choices = choices;
203 236
 	state->options = options;
237
+	state->offset = 0;
204 238
 
205 239
 	strcpy(state->input, "");
206 240
 	strcpy(state->search, "");
... ...
@@ -233,7 +267,13 @@ static const keybinding_t keybindings[] = {{"\x7f", action_del_char},	/* DEL */
233 267
 					   {KEY_CTRL('N'), action_next},	 /* C-N */
234 268
 					   {KEY_CTRL('K'), action_prev},	 /* C-J */
235 269
 					   {KEY_CTRL('J'), action_next},	 /* C-K */
270
+					   {KEY_CTRL('A'), action_beginning},    /* C-A */
271
+					   {KEY_CTRL('E'), action_end},		 /* C-E */
236 272
 
273
+					   {"\x1bOD", action_left}, /* LEFT */
274
+					   {"\x1b[D", action_left}, /* LEFT */
275
+					   {"\x1bOC", action_right}, /* RIGHT */
276
+					   {"\x1b[C", action_right}, /* RIGHT */
237 277
 					   {"\x1b[A", action_prev}, /* UP */
238 278
 					   {"\x1bOA", action_prev}, /* UP */
239 279
 					   {"\x1b[B", action_next}, /* DOWN */
Browse code

Ignore bracketed paste characters

Keith Smiley authored on 21/07/2017 18:46:40 • John Hawthorn committed on 13/08/2017 14:22:57
Showing 1 changed files
... ...
@@ -152,6 +152,10 @@ static void action_prev(tty_interface_t *state) {
152 152
 	choices_prev(state->choices);
153 153
 }
154 154
 
155
+static void action_ignore(tty_interface_t *state) {
156
+	(void) state;
157
+}
158
+
155 159
 static void action_next(tty_interface_t *state) {
156 160
 	update_state(state);
157 161
 	choices_next(state->choices);
... ...
@@ -236,6 +240,8 @@ static const keybinding_t keybindings[] = {{"\x7f", action_del_char},	/* DEL */
236 240
 					   {"\x1bOB", action_next}, /* DOWN */
237 241
 					   {"\x1b[5~", action_pageup},
238 242
 					   {"\x1b[6~", action_pagedown},
243
+					   {"\x1b[200~", action_ignore},
244
+					   {"\x1b[201~", action_ignore},
239 245
 					   {NULL, NULL}};
240 246
 
241 247
 #undef KEY_CTRL
Browse code

Support movement with CTRL-J/CTRL-K

Michael Stock authored on 18/01/2017 05:41:15
Showing 1 changed files
... ...
@@ -227,6 +227,8 @@ static const keybinding_t keybindings[] = {{"\x7f", action_del_char},	/* DEL */
227 227
 					   {KEY_CTRL('M'), action_emit},	 /* CR */
228 228
 					   {KEY_CTRL('P'), action_prev},	 /* C-P */
229 229
 					   {KEY_CTRL('N'), action_next},	 /* C-N */
230
+					   {KEY_CTRL('K'), action_prev},	 /* C-J */
231
+					   {KEY_CTRL('J'), action_next},	 /* C-K */
230 232
 
231 233
 					   {"\x1b[A", action_prev}, /* UP */
232 234
 					   {"\x1bOA", action_prev}, /* UP */
Browse code

Fix tty_interface clear for num_lines=0

John Hawthorn authored on 28/12/2016 09:09:01
Showing 1 changed files
... ...
@@ -16,7 +16,9 @@ static void clear(tty_interface_t *state) {
16 16
 		tty_newline(tty);
17 17
 	}
18 18
 	tty_clearline(tty);
19
-	tty_moveup(tty, line - 1);
19
+	if (state->options->num_lines > 0) {
20
+		tty_moveup(tty, line - 1);
21
+	}
20 22
 	tty_flush(tty);
21 23
 }
22 24
 
Browse code

Fix a problem that the cursor position shifted upward

momotaro authored on 20/12/2016 19:18:05
Showing 1 changed files
... ...
@@ -72,8 +72,9 @@ static void draw(tty_interface_t *state) {
72 72
 	size_t current_selection = choices->selection;
73 73
 	if (current_selection + options->scrolloff >= num_lines) {
74 74
 		start = current_selection + options->scrolloff - num_lines + 1;
75
-		if (start + num_lines >= choices_available(choices)) {
76
-			start = choices_available(choices) - num_lines;
75
+		size_t available = choices_available(choices);
76
+		if (start + num_lines >= available && available > 0) {
77
+			start = available - num_lines;
77 78
 		}
78 79
 	}
79 80
 	tty_setcol(tty, 0);
... ...
@@ -87,7 +88,9 @@ static void draw(tty_interface_t *state) {
87 88
 			draw_match(state, choice, i == choices->selection);
88 89
 		}
89 90
 	}
90
-	tty_moveup(tty, num_lines);
91
+	if (num_lines > 0) {
92
+		tty_moveup(tty, num_lines);
93
+	}
91 94
 	tty_setcol(tty, strlen(options->prompt) + strlen(state->search));
92 95
 	tty_flush(tty);
93 96
 }
Browse code

Add a few missing static declarations

John Hawthorn authored on 04/07/2016 05:53:26
Showing 1 changed files
... ...
@@ -97,7 +97,7 @@ static void update_search(tty_interface_t *state) {
97 97
 	strcpy(state->last_search, state->search);
98 98
 }
99 99
 
100
-void update_state(tty_interface_t *state) {
100
+static void update_state(tty_interface_t *state) {
101 101
 	if (strcmp(state->last_search, state->search)) {
102 102
 		update_search(state);
103 103
 		draw(state);
... ...
@@ -233,7 +233,7 @@ static const keybinding_t keybindings[] = {{"\x7f", action_del_char},	/* DEL */
233 233
 
234 234
 #undef KEY_CTRL
235 235
 
236
-void handle_input(tty_interface_t *state, const char *s) {
236
+static void handle_input(tty_interface_t *state, const char *s) {
237 237
 	char *input = state->input;
238 238
 	strcat(state->input, s);
239 239
 
Browse code

Fix segfault when autocompleting on no matches

John Hawthorn authored on 04/07/2016 05:51:35
Showing 1 changed files
... ...
@@ -166,7 +166,10 @@ static void action_pagedown(tty_interface_t *state) {
166 166
 
167 167
 static void action_autocomplete(tty_interface_t *state) {
168 168
 	update_state(state);
169
-	strncpy(state->search, choices_get(state->choices, state->choices->selection), SEARCH_SIZE_MAX);
169
+	const char *current_selection = choices_get(state->choices, state->choices->selection);
170
+	if (current_selection) {
171
+		strncpy(state->search, choices_get(state->choices, state->choices->selection), SEARCH_SIZE_MAX);
172
+	}
170 173
 }
171 174
 
172 175
 static void action_exit(tty_interface_t *state) {
Browse code

Use score_t instead of double

John Hawthorn authored on 27/06/2016 08:14:44
Showing 1 changed files
... ...
@@ -30,12 +30,17 @@ static void draw_match(tty_interface_t *state, const char *choice, int selected)
30 30
 	for (int i = 0; i < n + 1; i++)
31 31
 		positions[i] = -1;
32 32
 
33
-	double score = match_positions(search, choice, &positions[0]);
33
+	score_t score = match_positions(search, choice, &positions[0]);
34 34
 
35 35
 	size_t maxwidth = tty_getwidth(tty);
36 36
 
37
-	if (options->show_scores)
38
-		tty_printf(tty, "(%5.2f) ", score);
37
+	if (options->show_scores) {
38
+		if (score == SCORE_MIN) {
39
+			tty_printf(tty, "(     ) ");
40
+		} else {
41
+			tty_printf(tty, "(%5.2f) ", score);
42
+		}
43
+	}
39 44
 
40 45
 	if (selected)
41 46
 		tty_setinvert(tty);
Browse code

Batch together input for searches

John Hawthorn authored on 21/06/2016 06:29:15
Showing 1 changed files
... ...
@@ -23,7 +23,7 @@ static void clear(tty_interface_t *state) {
23 23
 static void draw_match(tty_interface_t *state, const char *choice, int selected) {
24 24
 	tty_t *tty = state->tty;
25 25
 	options_t *options = state->options;
26
-	char *search = state->search;
26
+	char *search = state->last_search;
27 27
 
28 28
 	int n = strlen(search);
29 29
 	size_t positions[n + 1];
... ...
@@ -87,7 +87,21 @@ static void draw(tty_interface_t *state) {
87 87
 	tty_flush(tty);
88 88
 }
89 89
 
90
+static void update_search(tty_interface_t *state) {
91
+	choices_search(state->choices, state->search);
92
+	strcpy(state->last_search, state->search);
93
+}
94
+
95
+void update_state(tty_interface_t *state) {
96
+	if (strcmp(state->last_search, state->search)) {
97
+		update_search(state);
98
+		draw(state);
99
+	}
100
+}
101
+
90 102
 static void action_emit(tty_interface_t *state) {
103
+	update_state(state);
104
+
91 105
 	/* Reset the tty as close as possible to the previous state */
92 106
 	clear(state);
93 107
 
... ...
@@ -124,24 +138,29 @@ static void action_del_all(tty_interface_t *state) {
124 138
 }
125 139
 
126 140
 static void action_prev(tty_interface_t *state) {
141
+	update_state(state);
127 142
 	choices_prev(state->choices);
128 143
 }
129 144
 
130 145
 static void action_next(tty_interface_t *state) {
146
+	update_state(state);
131 147
 	choices_next(state->choices);
132 148
 }
133 149
 
134 150
 static void action_pageup(tty_interface_t *state) {
151
+	update_state(state);
135 152
 	for(size_t i = 0; i < state->options->num_lines && state->choices->selection > 0; i++)
136 153
 		choices_prev(state->choices);
137 154
 }
138 155
 
139 156
 static void action_pagedown(tty_interface_t *state) {
157
+	update_state(state);
140 158
 	for(size_t i = 0; i < state->options->num_lines && state->choices->selection < state->choices->available-1; i++)
141 159
 		choices_next(state->choices);
142 160
 }
143 161
 
144 162
 static void action_autocomplete(tty_interface_t *state) {
163
+	update_state(state);
145 164
 	strncpy(state->search, choices_get(state->choices, state->choices->selection), SEARCH_SIZE_MAX);
146 165
 }
147 166
 
... ...
@@ -161,16 +180,6 @@ static void append_search(tty_interface_t *state, char ch) {
161 180
 	}
162 181
 }
163 182
 
164
-static void update_search(tty_interface_t *state) {
165
-	choices_search(state->choices, state->search);
166
-	strcpy(state->last_search, state->search);
167
-}
168
-
169
-void update_state(tty_interface_t *state) {
170
-	if (strcmp(state->last_search, state->search))
171
-		update_search(state);
172
-}
173
-
174 183
 void tty_interface_init(tty_interface_t *state, tty_t *tty, choices_t *choices, options_t *options) {
175 184
 	state->tty = tty;
176 185
 	state->choices = choices;
... ...
@@ -216,8 +225,9 @@ static const keybinding_t keybindings[] = {{"\x7f", action_del_char},	/* DEL */
216 225
 
217 226
 #undef KEY_CTRL
218 227
 
219
-void handle_input(tty_interface_t *state) {
228
+void handle_input(tty_interface_t *state, const char *s) {
220 229
 	char *input = state->input;
230
+	strcat(state->input, s);
221 231
 
222 232
 	/* See if we have matched a keybinding */
223 233
 	for (int i = 0; keybindings[i].key; i++) {
... ...
@@ -243,13 +253,18 @@ void handle_input(tty_interface_t *state) {
243 253
 }
244 254
 
245 255
 int tty_interface_run(tty_interface_t *state) {
246
-	while (state->exit < 0) {
247
-		draw(state);
256
+	draw(state);
257
+
258
+	for (;;) {
259
+		do {
260
+			char s[2] = {tty_getchar(state->tty), '\0'};
261
+			handle_input(state, s);
248 262
 
249
-		char s[2] = {tty_getchar(state->tty), '\0'};
250
-		strcat(state->input, s);
263
+			if (state->exit >= 0)
264
+				return state->exit;
251 265
 
252
-		handle_input(state);
266
+			draw(state);
267
+		} while (tty_input_ready(state->tty));
253 268
 
254 269
 		update_state(state);
255 270
 	}
Browse code

Add pageup and pagedown

John Hawthorn authored on 20/06/2016 08:57:07
Showing 1 changed files
... ...
@@ -131,6 +131,16 @@ static void action_next(tty_interface_t *state) {
131 131
 	choices_next(state->choices);
132 132
 }
133 133
 
134
+static void action_pageup(tty_interface_t *state) {
135
+	for(size_t i = 0; i < state->options->num_lines && state->choices->selection > 0; i++)
136
+		choices_prev(state->choices);
137
+}
138
+
139
+static void action_pagedown(tty_interface_t *state) {
140
+	for(size_t i = 0; i < state->options->num_lines && state->choices->selection < state->choices->available-1; i++)
141
+		choices_next(state->choices);
142
+}
143
+
134 144
 static void action_autocomplete(tty_interface_t *state) {
135 145
 	strncpy(state->search, choices_get(state->choices, state->choices->selection), SEARCH_SIZE_MAX);
136 146
 }
... ...
@@ -200,6 +210,8 @@ static const keybinding_t keybindings[] = {{"\x7f", action_del_char},	/* DEL */
200 210
 					   {"\x1bOA", action_prev}, /* UP */
201 211
 					   {"\x1b[B", action_next}, /* DOWN */
202 212
 					   {"\x1bOB", action_next}, /* DOWN */
213
+					   {"\x1b[5~", action_pageup},
214
+					   {"\x1b[6~", action_pagedown},
203 215
 					   {NULL, NULL}};
204 216
 
205 217
 #undef KEY_CTRL
Browse code

Use a struct to store keybindings

John Hawthorn authored on 20/06/2016 08:45:09
Showing 1 changed files
... ...
@@ -151,10 +151,6 @@ static void append_search(tty_interface_t *state, char ch) {
151 151
 	}
152 152
 }
153 153
 
154
-#define KEY_CTRL(key) ((key) - ('@'))
155
-#define KEY_DEL 127
156
-#define KEY_ESC 27
157
-
158 154
 static void update_search(tty_interface_t *state) {
159 155
 	choices_search(state->choices, state->search);
160 156
 	strcpy(state->last_search, state->search);
... ...
@@ -170,6 +166,7 @@ void tty_interface_init(tty_interface_t *state, tty_t *tty, choices_t *choices,
170 166
 	state->choices = choices;
171 167
 	state->options = options;
172 168
 
169
+	strcpy(state->input, "");
173 170
 	strcpy(state->search, "");
174 171
 	strcpy(state->last_search, "");
175 172
 
... ...
@@ -181,42 +178,67 @@ void tty_interface_init(tty_interface_t *state, tty_t *tty, choices_t *choices,
181 178
 	update_search(state);
182 179
 }
183 180
 
184
-int tty_interface_run(tty_interface_t *state) {
185
-	tty_t *tty = state->tty;
181
+typedef struct {
182
+	const char *key;
183
+	void (*action)(tty_interface_t *);
184
+} keybinding_t;
185
+
186
+#define KEY_CTRL(key) ((const char[]){((key) - ('@')), '\0'})
187
+
188
+static const keybinding_t keybindings[] = {{"\x7f", action_del_char},	/* DEL */
189
+					   {KEY_CTRL('H'), action_del_char}, /* Backspace (C-H) */
190
+					   {KEY_CTRL('W'), action_del_word}, /* C-W */
191
+					   {KEY_CTRL('U'), action_del_all},  /* C-U */
192
+					   {KEY_CTRL('I'), action_autocomplete}, /* TAB (C-I ) */
193
+					   {KEY_CTRL('C'), action_exit},	 /* C-C */
194
+					   {KEY_CTRL('D'), action_exit},	 /* C-D */
195
+					   {KEY_CTRL('M'), action_emit},	 /* CR */
196
+					   {KEY_CTRL('P'), action_prev},	 /* C-P */
197
+					   {KEY_CTRL('N'), action_next},	 /* C-N */
198
+
199
+					   {"\x1b[A", action_prev}, /* UP */
200
+					   {"\x1bOA", action_prev}, /* UP */
201
+					   {"\x1b[B", action_next}, /* DOWN */
202
+					   {"\x1bOB", action_next}, /* DOWN */
203
+					   {NULL, NULL}};
204
+
205
+#undef KEY_CTRL
206
+
207
+void handle_input(tty_interface_t *state) {
208
+	char *input = state->input;
209
+
210
+	/* See if we have matched a keybinding */
211
+	for (int i = 0; keybindings[i].key; i++) {
212
+		if (!strcmp(input, keybindings[i].key)) {
213
+			keybindings[i].action(state);
214
+			strcpy(input, "");
215
+			return;
216
+		}
217
+	}
218
+
219
+	/* Check if we are in the middle of a keybinding */
220
+	for (int i = 0; keybindings[i].key; i++)
221
+		if (!strncmp(input, keybindings[i].key, strlen(input)))
222
+			return;
186 223
 
187
-	char ch;
224
+	/* No matching keybinding, add to search */
225
+	for (int i = 0; input[i]; i++)
226
+		if (isprint(input[i]))
227
+			append_search(state, input[i]);
228
+
229
+	/* We have processed the input, so clear it */
230
+	strcpy(input, "");
231
+}
232
+
233
+int tty_interface_run(tty_interface_t *state) {
188 234
 	while (state->exit < 0) {
189 235
 		draw(state);
190
-		ch = tty_getchar(tty);
191
-		if (isprint(ch)) {
192
-			append_search(state, ch);
193
-		} else if (ch == KEY_DEL || ch == KEY_CTRL('H')) { /* DEL || Backspace (C-H) */
194
-			action_del_char(state);
195
-		} else if (ch == KEY_CTRL('U')) { /* C-U */
196
-			action_del_all(state);
197
-		} else if (ch == KEY_CTRL('W')) { /* C-W */
198
-			action_del_word(state);
199
-		} else if (ch == KEY_CTRL('N')) { /* C-N */
200
-			action_next(state);
201
-		} else if (ch == KEY_CTRL('P')) { /* C-P */
202
-			action_prev(state);
203
-		} else if (ch == KEY_CTRL('I')) { /* TAB (C-I) */
204
-			action_autocomplete(state);
205
-		} else if (ch == KEY_CTRL('C') || ch == KEY_CTRL('D')) { /* ^C || ^D */
206
-			action_exit(state);
207
-		} else if (ch == KEY_CTRL('M')) { /* CR */
208
-			action_emit(state);
209
-		} else if (ch == KEY_ESC) { /* ESC */
210
-			ch = tty_getchar(tty);
211
-			if (ch == '[' || ch == 'O') {
212
-				ch = tty_getchar(tty);
213
-				if (ch == 'A') { /* UP ARROW */
214
-					action_prev(state);
215
-				} else if (ch == 'B') { /* DOWN ARROW */
216
-					action_next(state);
217
-				}
218
-			}
219
-		}
236
+
237
+		char s[2] = {tty_getchar(state->tty), '\0'};
238
+		strcat(state->input, s);
239
+
240
+		handle_input(state);
241
+
220 242
 		update_state(state);
221 243
 	}
222 244
 
Browse code

Extract append_search method

John Hawthorn authored on 20/06/2016 07:23:43
Showing 1 changed files
... ...
@@ -142,6 +142,15 @@ static void action_exit(tty_interface_t *state) {
142 142
 	state->exit = EXIT_FAILURE;
143 143
 }
144 144
 
145
+static void append_search(tty_interface_t *state, char ch) {
146
+	char *search = state->search;
147
+	size_t search_size = strlen(search);
148
+	if (search_size < SEARCH_SIZE_MAX) {
149
+		search[search_size++] = ch;
150
+		search[search_size] = '\0';
151
+	}
152
+}
153
+
145 154
 #define KEY_CTRL(key) ((key) - ('@'))
146 155
 #define KEY_DEL 127
147 156
 #define KEY_ESC 27
... ...
@@ -174,18 +183,13 @@ void tty_interface_init(tty_interface_t *state, tty_t *tty, choices_t *choices,
174 183
 
175 184
 int tty_interface_run(tty_interface_t *state) {
176 185
 	tty_t *tty = state->tty;
177
-	char *search = state->search;
178 186
 
179 187
 	char ch;
180 188
 	while (state->exit < 0) {
181 189
 		draw(state);
182 190
 		ch = tty_getchar(tty);
183
-		size_t search_size = strlen(search);
184 191
 		if (isprint(ch)) {
185
-			if (search_size < SEARCH_SIZE_MAX) {
186
-				search[search_size++] = ch;
187
-				search[search_size] = '\0';
188
-			}
192
+			append_search(state, ch);
189 193
 		} else if (ch == KEY_DEL || ch == KEY_CTRL('H')) { /* DEL || Backspace (C-H) */
190 194
 			action_del_char(state);
191 195
 		} else if (ch == KEY_CTRL('U')) { /* C-U */
Browse code

Move all action into their own functions

John Hawthorn authored on 20/06/2016 07:08:50
Showing 1 changed files
... ...
@@ -106,6 +106,42 @@ static void action_emit(tty_interface_t *state) {
106 106
 	state->exit = EXIT_SUCCESS;
107 107
 }
108 108
 
109
+static void action_del_char(tty_interface_t *state) {
110
+	if (*state->search)
111
+		state->search[strlen(state->search) - 1] = '\0';
112
+}
113
+
114
+static void action_del_word(tty_interface_t *state) {
115
+	size_t search_size = strlen(state->search);
116
+	if (search_size)
117
+		state->search[--search_size] = '\0';
118
+	while (search_size && !isspace(state->search[--search_size]))
119
+		state->search[search_size] = '\0';
120
+}
121
+
122
+static void action_del_all(tty_interface_t *state) {
123
+	strcpy(state->search, "");
124
+}
125
+
126
+static void action_prev(tty_interface_t *state) {
127
+	choices_prev(state->choices);
128
+}
129
+
130
+static void action_next(tty_interface_t *state) {
131
+	choices_next(state->choices);
132
+}
133
+
134
+static void action_autocomplete(tty_interface_t *state) {
135
+	strncpy(state->search, choices_get(state->choices, state->choices->selection), SEARCH_SIZE_MAX);
136
+}
137
+
138
+static void action_exit(tty_interface_t *state) {
139
+	clear(state);
140
+	tty_close(state->tty);
141
+
142
+	state->exit = EXIT_FAILURE;
143
+}
144
+
109 145
 #define KEY_CTRL(key) ((key) - ('@'))
110 146
 #define KEY_DEL 127
111 147
 #define KEY_ESC 27
... ...
@@ -138,7 +174,6 @@ void tty_interface_init(tty_interface_t *state, tty_t *tty, choices_t *choices,
138 174
 
139 175
 int tty_interface_run(tty_interface_t *state) {
140 176
 	tty_t *tty = state->tty;
141
-	choices_t *choices = state->choices;
142 177
 	char *search = state->search;
143 178
 
144 179
 	char ch;
... ...
@@ -152,26 +187,19 @@ int tty_interface_run(tty_interface_t *state) {
152 187
 				search[search_size] = '\0';
153 188
 			}
154 189
 		} else if (ch == KEY_DEL || ch == KEY_CTRL('H')) { /* DEL || Backspace (C-H) */
155
-			if (search_size)
156
-				search[--search_size] = '\0';
190
+			action_del_char(state);
157 191
 		} else if (ch == KEY_CTRL('U')) { /* C-U */
158
-			search_size = 0;
159
-			search[0] = '\0';
192
+			action_del_all(state);
160 193
 		} else if (ch == KEY_CTRL('W')) { /* C-W */
161
-			if (search_size)
162
-				search[--search_size] = '\0';
163
-			while (search_size && !isspace(search[--search_size]))
164
-				search[search_size] = '\0';
194
+			action_del_word(state);
165 195
 		} else if (ch == KEY_CTRL('N')) { /* C-N */
166
-			choices_next(choices);
196
+			action_next(state);
167 197
 		} else if (ch == KEY_CTRL('P')) { /* C-P */
168
-			choices_prev(choices);
198
+			action_prev(state);
169 199
 		} else if (ch == KEY_CTRL('I')) { /* TAB (C-I) */
170
-			strncpy(search, choices_get(choices, choices->selection), SEARCH_SIZE_MAX);
200
+			action_autocomplete(state);
171 201
 		} else if (ch == KEY_CTRL('C') || ch == KEY_CTRL('D')) { /* ^C || ^D */
172
-			clear(state);
173
-			tty_close(tty);
174
-			state->exit = EXIT_FAILURE;
202
+			action_exit(state);
175 203
 		} else if (ch == KEY_CTRL('M')) { /* CR */
176 204
 			action_emit(state);
177 205
 		} else if (ch == KEY_ESC) { /* ESC */
... ...
@@ -179,9 +207,9 @@ int tty_interface_run(tty_interface_t *state) {
179 207
 			if (ch == '[' || ch == 'O') {
180 208
 				ch = tty_getchar(tty);
181 209
 				if (ch == 'A') { /* UP ARROW */
182
-					choices_prev(choices);
210
+					action_prev(state);
183 211
 				} else if (ch == 'B') { /* DOWN ARROW */
184
-					choices_next(choices);
212
+					action_next(state);
185 213
 				}
186 214
 			}
187 215
 		}
Browse code

Track last search

This allows us to avoid explicitly calling out to choices_search

John Hawthorn authored on 20/06/2016 06:58:44
Showing 1 changed files
... ...
@@ -110,15 +110,30 @@ static void action_emit(tty_interface_t *state) {
110 110
 #define KEY_DEL 127
111 111
 #define KEY_ESC 27
112 112
 
113
+static void update_search(tty_interface_t *state) {
114
+	choices_search(state->choices, state->search);
115
+	strcpy(state->last_search, state->search);
116
+}
117
+
118
+void update_state(tty_interface_t *state) {
119
+	if (strcmp(state->last_search, state->search))
120
+		update_search(state);
121
+}
122
+
113 123
 void tty_interface_init(tty_interface_t *state, tty_t *tty, choices_t *choices, options_t *options) {
114 124
 	state->tty = tty;
115 125
 	state->choices = choices;
116 126
 	state->options = options;
117 127
 
128
+	strcpy(state->search, "");
129
+	strcpy(state->last_search, "");
130
+
118 131
 	state->exit = -1;
119 132
 
120 133
 	if (options->init_search)
121 134
 		strncpy(state->search, options->init_search, SEARCH_SIZE_MAX);
135
+
136
+	update_search(state);
122 137
 }
123 138
 
124 139
 int tty_interface_run(tty_interface_t *state) {
... ...
@@ -126,7 +141,6 @@ int tty_interface_run(tty_interface_t *state) {
126 141
 	choices_t *choices = state->choices;
127 142
 	char *search = state->search;
128 143
 
129
-	choices_search(choices, search);
130 144
 	char ch;
131 145
 	while (state->exit < 0) {
132 146
 		draw(state);
... ...
@@ -136,29 +150,24 @@ int tty_interface_run(tty_interface_t *state) {
136 150
 			if (search_size < SEARCH_SIZE_MAX) {
137 151
 				search[search_size++] = ch;
138 152
 				search[search_size] = '\0';
139
-				choices_search(choices, search);
140 153
 			}
141 154
 		} else if (ch == KEY_DEL || ch == KEY_CTRL('H')) { /* DEL || Backspace (C-H) */
142 155
 			if (search_size)
143 156
 				search[--search_size] = '\0';
144
-			choices_search(choices, search);
145 157
 		} else if (ch == KEY_CTRL('U')) { /* C-U */
146 158
 			search_size = 0;
147 159
 			search[0] = '\0';
148
-			choices_search(choices, search);
149 160
 		} else if (ch == KEY_CTRL('W')) { /* C-W */
150 161
 			if (search_size)
151 162
 				search[--search_size] = '\0';
152 163
 			while (search_size && !isspace(search[--search_size]))
153 164
 				search[search_size] = '\0';
154
-			choices_search(choices, search);
155 165
 		} else if (ch == KEY_CTRL('N')) { /* C-N */
156 166
 			choices_next(choices);
157 167
 		} else if (ch == KEY_CTRL('P')) { /* C-P */
158 168
 			choices_prev(choices);
159 169
 		} else if (ch == KEY_CTRL('I')) { /* TAB (C-I) */
160 170
 			strncpy(search, choices_get(choices, choices->selection), SEARCH_SIZE_MAX);
161
-			choices_search(choices, search);
162 171
 		} else if (ch == KEY_CTRL('C') || ch == KEY_CTRL('D')) { /* ^C || ^D */
163 172
 			clear(state);
164 173
 			tty_close(tty);
... ...
@@ -176,6 +185,7 @@ int tty_interface_run(tty_interface_t *state) {
176 185
 				}
177 186
 			}
178 187
 		}
188
+		update_state(state);
179 189
 	}
180 190
 
181 191
 	return state->exit;
Browse code

Use tty_interface_t to communicate exit

John Hawthorn authored on 20/06/2016 06:40:44
Showing 1 changed files
... ...
@@ -87,10 +87,14 @@ static void draw(tty_interface_t *state) {
87 87
 	tty_flush(tty);
88 88
 }
89 89
 
90
-static void emit(tty_interface_t *state) {
91
-	choices_t *choices = state->choices;
90
+static void action_emit(tty_interface_t *state) {
91
+	/* Reset the tty as close as possible to the previous state */
92
+	clear(state);
93
+
94
+	/* ttyout should be flushed before outputting on stdout */
95
+	tty_close(state->tty);
92 96
 
93
-	const char *selection = choices_get(choices, choices->selection);
97
+	const char *selection = choices_get(state->choices, state->choices->selection);
94 98
 	if (selection) {
95 99
 		/* output the selected result */
96 100
 		printf("%s\n", selection);
... ...
@@ -98,6 +102,8 @@ static void emit(tty_interface_t *state) {
98 102
 		/* No match, output the query instead */
99 103
 		printf("%s\n", state->search);
100 104
 	}
105
+
106
+	state->exit = EXIT_SUCCESS;
101 107
 }
102 108
 
103 109
 #define KEY_CTRL(key) ((key) - ('@'))
... ...
@@ -109,6 +115,8 @@ void tty_interface_init(tty_interface_t *state, tty_t *tty, choices_t *choices,
109 115
 	state->choices = choices;
110 116
 	state->options = options;
111 117
 
118
+	state->exit = -1;
119
+
112 120
 	if (options->init_search)
113 121
 		strncpy(state->search, options->init_search, SEARCH_SIZE_MAX);
114 122
 }
... ...
@@ -120,7 +128,7 @@ int tty_interface_run(tty_interface_t *state) {
120 128
 
121 129
 	choices_search(choices, search);
122 130
 	char ch;
123
-	do {
131
+	while (state->exit < 0) {
124 132
 		draw(state);
125 133
 		ch = tty_getchar(tty);
126 134
 		size_t search_size = strlen(search);
... ...
@@ -154,17 +162,9 @@ int tty_interface_run(tty_interface_t *state) {
154 162
 		} else if (ch == KEY_CTRL('C') || ch == KEY_CTRL('D')) { /* ^C || ^D */
155 163
 			clear(state);
156 164
 			tty_close(tty);
157
-			exit(EXIT_FAILURE);
165
+			state->exit = EXIT_FAILURE;
158 166
 		} else if (ch == KEY_CTRL('M')) { /* CR */
159
-			clear(state);
160
-
161
-			/* ttyout should be flushed before outputting on stdout */
162
-			tty_close(tty);
163
-
164
-			emit(state);
165
-
166
-			/* Return to eventually exit successfully */
167
-			return 0;
167
+			action_emit(state);
168 168
 		} else if (ch == KEY_ESC) { /* ESC */
169 169
 			ch = tty_getchar(tty);
170 170
 			if (ch == '[' || ch == 'O') {
... ...
@@ -176,7 +176,7 @@ int tty_interface_run(tty_interface_t *state) {
176 176
 				}
177 177
 			}
178 178
 		}
179
-	} while (1);
179
+	}
180 180
 
181
-	return 0;
181
+	return state->exit;
182 182
 }
Browse code

Return exit code from run

John Hawthorn authored on 20/06/2016 06:36:39
Showing 1 changed files
... ...
@@ -113,7 +113,7 @@ void tty_interface_init(tty_interface_t *state, tty_t *tty, choices_t *choices,
113 113
 		strncpy(state->search, options->init_search, SEARCH_SIZE_MAX);
114 114
 }
115 115
 
116
-void tty_interface_run(tty_interface_t *state) {
116
+int tty_interface_run(tty_interface_t *state) {
117 117
 	tty_t *tty = state->tty;
118 118
 	choices_t *choices = state->choices;
119 119
 	char *search = state->search;
... ...
@@ -164,7 +164,7 @@ void tty_interface_run(tty_interface_t *state) {
164 164
 			emit(state);
165 165
 
166 166
 			/* Return to eventually exit successfully */
167
-			return;
167
+			return 0;
168 168
 		} else if (ch == KEY_ESC) { /* ESC */
169 169
 			ch = tty_getchar(tty);
170 170
 			if (ch == '[' || ch == 'O') {
... ...
@@ -177,4 +177,6 @@ void tty_interface_run(tty_interface_t *state) {
177 177
 			}
178 178
 		}
179 179
 	} while (1);
180
+
181
+	return 0;
180 182
 }
Browse code

Use tty_interface_t throughout

John Hawthorn authored on 20/06/2016 06:28:51
Showing 1 changed files
... ...
@@ -7,12 +7,12 @@
7 7
 #include "tty_interface.h"
8 8
 #include "../config.h"
9 9
 
10
-static char search[SEARCH_SIZE_MAX + 1];
10
+static void clear(tty_interface_t *state) {
11
+	tty_t *tty = state->tty;
11 12
 
12
-static void clear(tty_t *tty, options_t *options) {
13 13
 	tty_setcol(tty, 0);
14 14
 	size_t line = 0;
15
-	while (line++ < options->num_lines) {
15
+	while (line++ < state->options->num_lines) {
16 16
 		tty_newline(tty);
17 17
 	}
18 18
 	tty_clearline(tty);
... ...
@@ -20,7 +20,11 @@ static void clear(tty_t *tty, options_t *options) {
20 20
 	tty_flush(tty);
21 21
 }
22 22
 
23
-static void draw_match(tty_t *tty, const char *choice, int selected, options_t *options) {
23
+static void draw_match(tty_interface_t *state, const char *choice, int selected) {
24
+	tty_t *tty = state->tty;
25
+	options_t *options = state->options;
26
+	char *search = state->search;
27
+
24 28
 	int n = strlen(search);
25 29
 	size_t positions[n + 1];
26 30
 	for (int i = 0; i < n + 1; i++)
... ...
@@ -53,7 +57,11 @@ static void draw_match(tty_t *tty, const char *choice, int selected, options_t *
53 57
 	tty_setnormal(tty);
54 58
 }
55 59
 
56
-static void draw(tty_t *tty, choices_t *choices, options_t *options) {
60
+static void draw(tty_interface_t *state) {
61
+	tty_t *tty = state->tty;
62
+	choices_t *choices = state->choices;
63
+	options_t *options = state->options;
64
+
57 65
 	unsigned int num_lines = options->num_lines;
58 66
 	size_t start = 0;
59 67
 	size_t current_selection = choices->selection;
... ...
@@ -64,29 +72,31 @@ static void draw(tty_t *tty, choices_t *choices, options_t *options) {
64 72
 		}
65 73
 	}
66 74
 	tty_setcol(tty, 0);
67
-	tty_printf(tty, "%s%s", options->prompt, search);
75
+	tty_printf(tty, "%s%s", options->prompt, state->search);
68 76
 	tty_clearline(tty);
69 77
 	for (size_t i = start; i < start + num_lines; i++) {
70 78
 		tty_printf(tty, "\n");
71 79
 		tty_clearline(tty);
72 80
 		const char *choice = choices_get(choices, i);
73 81
 		if (choice) {
74
-			draw_match(tty, choice, i == choices->selection, options);
82
+			draw_match(state, choice, i == choices->selection);
75 83
 		}
76 84
 	}
77 85
 	tty_moveup(tty, num_lines);
78
-	tty_setcol(tty, strlen(options->prompt) + strlen(search));
86
+	tty_setcol(tty, strlen(options->prompt) + strlen(state->search));
79 87
 	tty_flush(tty);
80 88
 }
81 89
 
82
-static void emit(choices_t *choices) {
90
+static void emit(tty_interface_t *state) {
91
+	choices_t *choices = state->choices;
92
+
83 93
 	const char *selection = choices_get(choices, choices->selection);
84 94
 	if (selection) {
85 95
 		/* output the selected result */
86 96
 		printf("%s\n", selection);
87 97
 	} else {
88 98
 		/* No match, output the query instead */
89
-		printf("%s\n", search);
99
+		printf("%s\n", state->search);
90 100
 	}
91 101
 }
92 102
 
... ...
@@ -106,13 +116,12 @@ void tty_interface_init(tty_interface_t *state, tty_t *tty, choices_t *choices,
106 116
 void tty_interface_run(tty_interface_t *state) {
107 117
 	tty_t *tty = state->tty;
108 118
 	choices_t *choices = state->choices;
109
-	options_t *options = state->options;
110 119
 	char *search = state->search;
111 120
 
112 121
 	choices_search(choices, search);
113 122
 	char ch;
114 123
 	do {
115
-		draw(tty, choices, options);
124
+		draw(state);
116 125
 		ch = tty_getchar(tty);
117 126
 		size_t search_size = strlen(search);
118 127
 		if (isprint(ch)) {
... ...
@@ -143,16 +152,16 @@ void tty_interface_run(tty_interface_t *state) {
143 152
 			strncpy(search, choices_get(choices, choices->selection), SEARCH_SIZE_MAX);
144 153
 			choices_search(choices, search);
145 154
 		} else if (ch == KEY_CTRL('C') || ch == KEY_CTRL('D')) { /* ^C || ^D */
146
-			clear(tty, options);
155
+			clear(state);
147 156
 			tty_close(tty);
148 157
 			exit(EXIT_FAILURE);
149 158
 		} else if (ch == KEY_CTRL('M')) { /* CR */
150
-			clear(tty, options);
159
+			clear(state);
151 160
 
152 161
 			/* ttyout should be flushed before outputting on stdout */
153 162
 			tty_close(tty);
154 163
 
155
-			emit(choices);
164
+			emit(state);
156 165
 
157 166
 			/* Return to eventually exit successfully */
158 167
 			return;
Browse code

Store state in tty_interface_t

John Hawthorn authored on 20/06/2016 06:17:53
Showing 1 changed files
... ...
@@ -94,9 +94,20 @@ static void emit(choices_t *choices) {
94 94
 #define KEY_DEL 127
95 95
 #define KEY_ESC 27
96 96
 
97
-void tty_interface_run(tty_t *tty, choices_t *choices, options_t *options) {
97
+void tty_interface_init(tty_interface_t *state, tty_t *tty, choices_t *choices, options_t *options) {
98
+	state->tty = tty;
99
+	state->choices = choices;
100
+	state->options = options;
101
+
98 102
 	if (options->init_search)
99
-		strncpy(search, options->init_search, SEARCH_SIZE_MAX);
103
+		strncpy(state->search, options->init_search, SEARCH_SIZE_MAX);
104
+}
105
+
106
+void tty_interface_run(tty_interface_t *state) {
107
+	tty_t *tty = state->tty;
108
+	choices_t *choices = state->choices;
109
+	options_t *options = state->options;
110
+	char *search = state->search;
100 111
 
101 112
 	choices_search(choices, search);
102 113
 	char ch;
Browse code

Extract tty interface to own file

John Hawthorn authored on 20/06/2016 06:08:00
Showing 1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,160 @@
1
+#include <ctype.h>
2
+#include <stdio.h>
3
+#include <stdlib.h>
4
+#include <string.h>
5
+
6
+#include "match.h"
7
+#include "tty_interface.h"
8
+#include "../config.h"
9
+
10
+static char search[SEARCH_SIZE_MAX + 1];
11
+
12
+static void clear(tty_t *tty, options_t *options) {
13
+	tty_setcol(tty, 0);
14
+	size_t line = 0;
15
+	while (line++ < options->num_lines) {
16
+		tty_newline(tty);
17
+	}
18
+	tty_clearline(tty);
19
+	tty_moveup(tty, line - 1);
20
+	tty_flush(tty);
21
+}
22
+
23
+static void draw_match(tty_t *tty, const char *choice, int selected, options_t *options) {
24
+	int n = strlen(search);
25
+	size_t positions[n + 1];
26
+	for (int i = 0; i < n + 1; i++)
27
+		positions[i] = -1;
28
+
29
+	double score = match_positions(search, choice, &positions[0]);
30
+
31
+	size_t maxwidth = tty_getwidth(tty);
32
+
33
+	if (options->show_scores)
34
+		tty_printf(tty, "(%5.2f) ", score);
35
+
36
+	if (selected)
37
+		tty_setinvert(tty);
38
+
39
+	for (size_t i = 0, p = 0; choice[i] != '\0'; i++) {
40
+		if (i + 1 < maxwidth) {
41
+			if (positions[p] == i) {
42
+				tty_setfg(tty, TTY_COLOR_HIGHLIGHT);
43
+				p++;
44
+			} else {
45
+				tty_setfg(tty, TTY_COLOR_NORMAL);
46
+			}
47
+			tty_printf(tty, "%c", choice[i]);
48
+		} else {
49
+			tty_printf(tty, "$");
50
+			break;
51
+		}
52
+	}
53
+	tty_setnormal(tty);
54
+}
55
+
56
+static void draw(tty_t *tty, choices_t *choices, options_t *options) {
57
+	unsigned int num_lines = options->num_lines;
58
+	size_t start = 0;
59
+	size_t current_selection = choices->selection;
60
+	if (current_selection + options->scrolloff >= num_lines) {
61
+		start = current_selection + options->scrolloff - num_lines + 1;
62
+		if (start + num_lines >= choices_available(choices)) {
63
+			start = choices_available(choices) - num_lines;
64
+		}
65
+	}
66
+	tty_setcol(tty, 0);
67
+	tty_printf(tty, "%s%s", options->prompt, search);
68
+	tty_clearline(tty);
69
+	for (size_t i = start; i < start + num_lines; i++) {
70
+		tty_printf(tty, "\n");
71
+		tty_clearline(tty);
72
+		const char *choice = choices_get(choices, i);
73
+		if (choice) {
74
+			draw_match(tty, choice, i == choices->selection, options);
75
+		}
76
+	}
77
+	tty_moveup(tty, num_lines);
78
+	tty_setcol(tty, strlen(options->prompt) + strlen(search));
79
+	tty_flush(tty);
80
+}
81
+
82
+static void emit(choices_t *choices) {
83
+	const char *selection = choices_get(choices, choices->selection);
84
+	if (selection) {
85
+		/* output the selected result */
86
+		printf("%s\n", selection);
87
+	} else {
88
+		/* No match, output the query instead */
89
+		printf("%s\n", search);
90
+	}
91
+}
92
+
93
+#define KEY_CTRL(key) ((key) - ('@'))
94
+#define KEY_DEL 127
95
+#define KEY_ESC 27
96
+
97
+void tty_interface_run(tty_t *tty, choices_t *choices, options_t *options) {
98
+	if (options->init_search)
99
+		strncpy(search, options->init_search, SEARCH_SIZE_MAX);
100
+
101
+	choices_search(choices, search);
102
+	char ch;
103
+	do {
104
+		draw(tty, choices, options);
105
+		ch = tty_getchar(tty);
106
+		size_t search_size = strlen(search);
107
+		if (isprint(ch)) {
108
+			if (search_size < SEARCH_SIZE_MAX) {
109
+				search[search_size++] = ch;
110
+				search[search_size] = '\0';
111
+				choices_search(choices, search);
112
+			}
113
+		} else if (ch == KEY_DEL || ch == KEY_CTRL('H')) { /* DEL || Backspace (C-H) */
114
+			if (search_size)
115
+				search[--search_size] = '\0';
116
+			choices_search(choices, search);
117
+		} else if (ch == KEY_CTRL('U')) { /* C-U */
118
+			search_size = 0;
119
+			search[0] = '\0';
120
+			choices_search(choices, search);
121
+		} else if (ch == KEY_CTRL('W')) { /* C-W */
122
+			if (search_size)
123
+				search[--search_size] = '\0';
124
+			while (search_size && !isspace(search[--search_size]))
125
+				search[search_size] = '\0';
126
+			choices_search(choices, search);
127
+		} else if (ch == KEY_CTRL('N')) { /* C-N */
128
+			choices_next(choices);
129
+		} else if (ch == KEY_CTRL('P')) { /* C-P */
130
+			choices_prev(choices);
131
+		} else if (ch == KEY_CTRL('I')) { /* TAB (C-I) */
132
+			strncpy(search, choices_get(choices, choices->selection), SEARCH_SIZE_MAX);
133
+			choices_search(choices, search);
134
+		} else if (ch == KEY_CTRL('C') || ch == KEY_CTRL('D')) { /* ^C || ^D */
135
+			clear(tty, options);
136
+			tty_close(tty);
137
+			exit(EXIT_FAILURE);
138
+		} else if (ch == KEY_CTRL('M')) { /* CR */
139
+			clear(tty, options);
140
+
141
+			/* ttyout should be flushed before outputting on stdout */
142
+			tty_close(tty);
143
+
144
+			emit(choices);
145
+
146
+			/* Return to eventually exit successfully */
147
+			return;
148
+		} else if (ch == KEY_ESC) { /* ESC */
149
+			ch = tty_getchar(tty);
150
+			if (ch == '[' || ch == 'O') {
151
+				ch = tty_getchar(tty);
152
+				if (ch == 'A') { /* UP ARROW */
153
+					choices_prev(choices);
154
+				} else if (ch == 'B') { /* DOWN ARROW */
155
+					choices_next(choices);
156
+				}
157
+			}
158
+		}
159
+	} while (1);
160
+}