From 52f091dfd559ba6f4ad9ddeb40411423c4914cd4 Mon Sep 17 00:00:00 2001 From: John Hawthorn Date: Sun, 19 Jun 2016 23:08:00 -0700 Subject: Extract tty interface to own file --- src/tty_interface.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 src/tty_interface.c (limited to 'src/tty_interface.c') diff --git a/src/tty_interface.c b/src/tty_interface.c new file mode 100644 index 0000000..69c3ab1 --- /dev/null +++ b/src/tty_interface.c @@ -0,0 +1,160 @@ +#include +#include +#include +#include + +#include "match.h" +#include "tty_interface.h" +#include "../config.h" + +static char search[SEARCH_SIZE_MAX + 1]; + +static void clear(tty_t *tty, options_t *options) { + tty_setcol(tty, 0); + size_t line = 0; + while (line++ < options->num_lines) { + tty_newline(tty); + } + tty_clearline(tty); + tty_moveup(tty, line - 1); + tty_flush(tty); +} + +static void draw_match(tty_t *tty, const char *choice, int selected, options_t *options) { + int n = strlen(search); + size_t positions[n + 1]; + for (int i = 0; i < n + 1; i++) + positions[i] = -1; + + double score = match_positions(search, choice, &positions[0]); + + size_t maxwidth = tty_getwidth(tty); + + if (options->show_scores) + tty_printf(tty, "(%5.2f) ", score); + + if (selected) + tty_setinvert(tty); + + for (size_t i = 0, p = 0; choice[i] != '\0'; i++) { + if (i + 1 < maxwidth) { + if (positions[p] == i) { + tty_setfg(tty, TTY_COLOR_HIGHLIGHT); + p++; + } else { + tty_setfg(tty, TTY_COLOR_NORMAL); + } + tty_printf(tty, "%c", choice[i]); + } else { + tty_printf(tty, "$"); + break; + } + } + tty_setnormal(tty); +} + +static void draw(tty_t *tty, choices_t *choices, options_t *options) { + unsigned int num_lines = options->num_lines; + size_t start = 0; + size_t current_selection = choices->selection; + if (current_selection + options->scrolloff >= num_lines) { + start = current_selection + options->scrolloff - num_lines + 1; + if (start + num_lines >= choices_available(choices)) { + start = choices_available(choices) - num_lines; + } + } + tty_setcol(tty, 0); + tty_printf(tty, "%s%s", options->prompt, search); + tty_clearline(tty); + for (size_t i = start; i < start + num_lines; i++) { + tty_printf(tty, "\n"); + tty_clearline(tty); + const char *choice = choices_get(choices, i); + if (choice) { + draw_match(tty, choice, i == choices->selection, options); + } + } + tty_moveup(tty, num_lines); + tty_setcol(tty, strlen(options->prompt) + strlen(search)); + tty_flush(tty); +} + +static void emit(choices_t *choices) { + const char *selection = choices_get(choices, choices->selection); + if (selection) { + /* output the selected result */ + printf("%s\n", selection); + } else { + /* No match, output the query instead */ + printf("%s\n", search); + } +} + +#define KEY_CTRL(key) ((key) - ('@')) +#define KEY_DEL 127 +#define KEY_ESC 27 + +void tty_interface_run(tty_t *tty, choices_t *choices, options_t *options) { + if (options->init_search) + strncpy(search, options->init_search, SEARCH_SIZE_MAX); + + choices_search(choices, search); + char ch; + do { + draw(tty, choices, options); + ch = tty_getchar(tty); + size_t search_size = strlen(search); + if (isprint(ch)) { + if (search_size < SEARCH_SIZE_MAX) { + search[search_size++] = ch; + search[search_size] = '\0'; + choices_search(choices, search); + } + } else if (ch == KEY_DEL || ch == KEY_CTRL('H')) { /* DEL || Backspace (C-H) */ + if (search_size) + search[--search_size] = '\0'; + choices_search(choices, search); + } else if (ch == KEY_CTRL('U')) { /* C-U */ + search_size = 0; + search[0] = '\0'; + choices_search(choices, search); + } else if (ch == KEY_CTRL('W')) { /* C-W */ + if (search_size) + search[--search_size] = '\0'; + while (search_size && !isspace(search[--search_size])) + search[search_size] = '\0'; + choices_search(choices, search); + } else if (ch == KEY_CTRL('N')) { /* C-N */ + choices_next(choices); + } else if (ch == KEY_CTRL('P')) { /* C-P */ + choices_prev(choices); + } else if (ch == KEY_CTRL('I')) { /* TAB (C-I) */ + strncpy(search, choices_get(choices, choices->selection), SEARCH_SIZE_MAX); + choices_search(choices, search); + } else if (ch == KEY_CTRL('C') || ch == KEY_CTRL('D')) { /* ^C || ^D */ + clear(tty, options); + tty_close(tty); + exit(EXIT_FAILURE); + } else if (ch == KEY_CTRL('M')) { /* CR */ + clear(tty, options); + + /* ttyout should be flushed before outputting on stdout */ + tty_close(tty); + + emit(choices); + + /* Return to eventually exit successfully */ + return; + } else if (ch == KEY_ESC) { /* ESC */ + ch = tty_getchar(tty); + if (ch == '[' || ch == 'O') { + ch = tty_getchar(tty); + if (ch == 'A') { /* UP ARROW */ + choices_prev(choices); + } else if (ch == 'B') { /* DOWN ARROW */ + choices_next(choices); + } + } + } + } while (1); +} -- cgit v1.2.3