From d3546cc85cc0ed80011ec35c105ae92e1e254148 Mon Sep 17 00:00:00 2001 From: nicm Date: Tue, 26 Apr 2016 22:19:22 +0000 Subject: Simplify next key matching and fix problems with meta and Unicode keys. --- tty-keys.c | 185 ++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 115 insertions(+), 70 deletions(-) diff --git a/tty-keys.c b/tty-keys.c index 105f99f7..efbc1f28 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -33,14 +33,17 @@ * into a ternary tree. */ -void tty_keys_add1(struct tty_key **, const char *, key_code); -void tty_keys_add(struct tty *, const char *, key_code); -void tty_keys_free1(struct tty_key *); -struct tty_key *tty_keys_find1(struct tty_key *, const char *, size_t, +static void tty_keys_add1(struct tty_key **, const char *, key_code); +static void tty_keys_add(struct tty *, const char *, key_code); +static void tty_keys_free1(struct tty_key *); +static struct tty_key *tty_keys_find1(struct tty_key *, const char *, size_t, size_t *); -struct tty_key *tty_keys_find(struct tty *, const char *, size_t, size_t *); -void tty_keys_callback(int, short, void *); -int tty_keys_mouse(struct tty *, const char *, size_t, size_t *); +static struct tty_key *tty_keys_find(struct tty *, const char *, size_t, + size_t *); +static int tty_keys_next1(struct tty *, const char *, size_t, key_code *, + size_t *); +static void tty_keys_callback(int, short, void *); +static int tty_keys_mouse(struct tty *, const char *, size_t, size_t *); /* Default raw keys. */ struct tty_default_key_raw { @@ -316,7 +319,7 @@ const struct tty_default_key_code tty_default_code_keys[] = { }; /* Add key to tree. */ -void +static void tty_keys_add(struct tty *tty, const char *s, key_code key) { struct tty_key *tk; @@ -334,7 +337,7 @@ tty_keys_add(struct tty *tty, const char *s, key_code key) } /* Add next node to the tree. */ -void +static void tty_keys_add1(struct tty_key **tkp, const char *s, key_code key) { struct tty_key *tk; @@ -409,7 +412,7 @@ tty_keys_free(struct tty *tty) } /* Free a single key. */ -void +static void tty_keys_free1(struct tty_key *tk) { if (tk->next != NULL) @@ -422,7 +425,7 @@ tty_keys_free1(struct tty_key *tk) } /* Lookup a key in the tree. */ -struct tty_key * +static struct tty_key * tty_keys_find(struct tty *tty, const char *buf, size_t len, size_t *size) { *size = 0; @@ -430,7 +433,7 @@ tty_keys_find(struct tty *tty, const char *buf, size_t len, size_t *size) } /* Find the next node. */ -struct tty_key * +static struct tty_key * tty_keys_find1(struct tty_key *tk, const char *buf, size_t len, size_t *size) { /* If the node is NULL, this is the end of the tree. No match. */ @@ -460,6 +463,56 @@ tty_keys_find1(struct tty_key *tk, const char *buf, size_t len, size_t *size) return (tty_keys_find1(tk, buf, len, size)); } +/* Look up part of the next key. */ +static int +tty_keys_next1(struct tty *tty, const char *buf, size_t len, key_code *key, + size_t *size) +{ + struct tty_key *tk, *tk1; + struct utf8_data ud; + enum utf8_state more; + u_int i; + wchar_t wc; + + log_debug("next key is %zu (%.*s)", len, (int)len, buf); + + /* Empty buffer is a partial key. */ + if (len == 0) + return (1); + + /* Is this a known key? */ + tk = tty_keys_find(tty, buf, len, size); + if (tk != NULL) { + tk1 = tk; + do + log_debug("keys in list: %#llx", tk->key); + while ((tk1 = tk1->next) != NULL); + *key = tk->key; + return (tk->next != NULL); + } + + /* Is this valid UTF-8? */ + more = utf8_open(&ud, (u_char)*buf); + if (more == UTF8_MORE) { + *size = ud.size; + if (len < ud.size) + return (1); + for (i = 1; i < ud.size; i++) + more = utf8_append(&ud, (u_char)buf[i]); + if (more != UTF8_DONE) + return (0); + + if (utf8_combine(&ud, &wc) != UTF8_DONE) + return (0); + *key = wc; + + log_debug("UTF-8 key %.*s %#llx", (int)ud.size, buf, *key); + return (0); + } + + return (-1); +} + /* * Process at least one key in the buffer and invoke tty->key_callback. Return * 0 if there are no further keys, or 1 if there could be more in the buffer. @@ -467,17 +520,12 @@ tty_keys_find1(struct tty_key *tk, const char *buf, size_t len, size_t *size) key_code tty_keys_next(struct tty *tty) { - struct tty_key *tk; - struct timeval tv; - const char *buf; - size_t len, size; - cc_t bspace; - int delay, expired = 0; - key_code key; - struct utf8_data ud; - enum utf8_state more; - u_int i; - wchar_t wc; + struct timeval tv; + const char *buf; + size_t len, size; + cc_t bspace; + int delay, expired = 0; + key_code key; /* Get key buffer. */ buf = EVBUFFER_DATA(tty->event->input); @@ -485,7 +533,7 @@ tty_keys_next(struct tty *tty) if (len == 0) return (0); - log_debug("keys are %zu (%.*s)", len, (int) len, buf); + log_debug("keys are %zu (%.*s)", len, (int)len, buf); /* Is this a mouse key press? */ switch (tty_keys_mouse(tty, buf, len, &size)) { @@ -501,68 +549,65 @@ tty_keys_next(struct tty *tty) goto partial_key; } - /* Look for matching key string and return if found. */ - tk = tty_keys_find(tty, buf, len, &size); - if (tk != NULL) { - if (tk->next != NULL) +first_key: + /* If escape is at the start, try without it. */ + if (*buf == '\033') { + switch (tty_keys_next1 (tty, buf + 1, len - 1, &key, &size)) { + case 0: /* found */ + if (key != KEYC_UNKNOWN) + key |= KEYC_ESCAPE; + size++; /* include escape */ + goto complete_key; + case -1: /* not found */ + break; + case 1: + if (expired) + goto complete_key; goto partial_key; - key = tk->key; + } + } + + /* Try with the escape. */ + switch (tty_keys_next1 (tty, buf, len, &key, &size)) { + case 0: /* found */ goto complete_key; + case -1: /* not found */ + break; + case 1: + if (expired) + goto complete_key; + goto partial_key; } - /* Try to parse a key with an xterm-style modifier. */ + /* Is this an an xterm(1) key? */ switch (xterm_keys_find(buf, len, &size, &key)) { case 0: /* found */ goto complete_key; case -1: /* not found */ break; case 1: + if (expired) + break; goto partial_key; } -first_key: - /* Is this a meta key? */ - if (len >= 2 && buf[0] == '\033') { - if (buf[1] != '\033') { - key = buf[1] | KEYC_ESCAPE; + /* + * If this starts with escape and is at least two keys, it must be + * complete even if the timer has not expired, because otherwise + * tty_keys_next1 would have found a partial key. If just an escape + * alone, it needs to wait for the timer first. + */ + if (*buf == '\033') { + if (len >= 2) { + key = (u_char)buf[1] | KEYC_ESCAPE; size = 2; goto complete_key; } - - tk = tty_keys_find(tty, buf + 1, len - 1, &size); - if (tk != NULL && (!expired || tk->next == NULL)) { - size++; /* include escape */ - if (tk->next != NULL) - goto partial_key; - key = tk->key; - if (key != KEYC_UNKNOWN) - key |= KEYC_ESCAPE; - goto complete_key; - } - } - - /* Is this valid UTF-8? */ - if ((more = utf8_open(&ud, (u_char)*buf) == UTF8_MORE)) { - size = ud.size; - if (len < size) { - if (expired) - goto discard_key; + if (!expired) goto partial_key; - } - for (i = 1; i < size; i++) - more = utf8_append(&ud, (u_char)buf[i]); - if (more != UTF8_DONE) - goto discard_key; - - if (utf8_combine(&ud, &wc) != UTF8_DONE) - goto discard_key; - key = wc; - - log_debug("UTF-8 key %.*s %#llx", (int)size, buf, key); - goto complete_key; } - /* No key found, take first. */ + /* No longer key found, use the first character. */ key = (u_char)*buf; size = 1; @@ -578,7 +623,7 @@ first_key: goto complete_key; partial_key: - log_debug("partial key %.*s", (int) len, buf); + log_debug("partial key %.*s", (int)len, buf); /* If timer is going, check for expiration. */ if (tty->flags & TTY_TIMER) { @@ -640,7 +685,7 @@ discard_key: } /* Key timer callback. */ -void +static void tty_keys_callback(__unused int fd, __unused short events, void *data) { struct tty *tty = data; @@ -655,7 +700,7 @@ tty_keys_callback(__unused int fd, __unused short events, void *data) * Handle mouse key input. Returns 0 for success, -1 for failure, 1 for partial * (probably a mouse sequence but need more data). */ -int +static int tty_keys_mouse(struct tty *tty, const char *buf, size_t len, size_t *size) { struct mouse_event *m = &tty->mouse; -- cgit From 23fdbc9ea6a6f5c93f042043f0407ed5d9bd0e5b Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 27 Apr 2016 09:36:25 +0000 Subject: Loads of platforms appear to have old or broken Unicode character type information and are missing widths for relatively common Unicode characters (so mbtowc() works, but wcwidth() fails). So if wcwidth() returns -1, assume a width of 1 instead of ignoring the character. --- utf8.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/utf8.c b/utf8.c index 56281aa2..22ab62c1 100644 --- a/utf8.c +++ b/utf8.c @@ -119,6 +119,14 @@ utf8_width(wchar_t wc) width = wcwidth(wc); if (width < 0 || width > 0xff) { log_debug("Unicode %04x, wcwidth() %d", wc, width); + + /* + * Many platforms have no width for relatively common + * characters (wcwidth() returns -1); assume width 1 in this + * case and hope for the best. + */ + if (width < 0) + return (1); return (-1); } return (width); -- cgit From 1cedf78284a563b01e9394fa5b38c50470558868 Mon Sep 17 00:00:00 2001 From: nicm Date: Wed, 27 Apr 2016 09:39:09 +0000 Subject: Add next/previous paragraph, from J Raynor. --- mode-key.c | 6 ++++++ tmux.1 | 2 ++ tmux.h | 2 ++ window-copy.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+) diff --git a/mode-key.c b/mode-key.c index aed161bb..aaa5e0d0 100644 --- a/mode-key.c +++ b/mode-key.c @@ -140,12 +140,14 @@ const struct mode_key_cmdstr mode_key_cmdstr_copy[] = { { MODEKEYCOPY_RECTANGLETOGGLE, "rectangle-toggle" }, { MODEKEYCOPY_MIDDLELINE, "middle-line" }, { MODEKEYCOPY_NEXTPAGE, "page-down" }, + { MODEKEYCOPY_NEXTPARAGRAPH, "next-paragraph" }, { MODEKEYCOPY_NEXTSPACE, "next-space" }, { MODEKEYCOPY_NEXTSPACEEND, "next-space-end" }, { MODEKEYCOPY_NEXTWORD, "next-word" }, { MODEKEYCOPY_NEXTWORDEND, "next-word-end" }, { MODEKEYCOPY_OTHEREND, "other-end" }, { MODEKEYCOPY_PREVIOUSPAGE, "page-up" }, + { MODEKEYCOPY_PREVIOUSPARAGRAPH, "previous-paragraph" }, { MODEKEYCOPY_PREVIOUSSPACE, "previous-space" }, { MODEKEYCOPY_PREVIOUSWORD, "previous-word" }, { MODEKEYCOPY_RIGHT, "cursor-right" }, @@ -335,6 +337,8 @@ const struct mode_key_entry mode_key_vi_copy[] = { { 'q', 0, MODEKEYCOPY_CANCEL }, { 'v', 0, MODEKEYCOPY_RECTANGLETOGGLE }, { 'w', 0, MODEKEYCOPY_NEXTWORD }, + { '{', 0, MODEKEYCOPY_PREVIOUSPARAGRAPH }, + { '}', 0, MODEKEYCOPY_NEXTPARAGRAPH }, { KEYC_BSPACE, 0, MODEKEYCOPY_LEFT }, { KEYC_DOWN | KEYC_CTRL, 0, MODEKEYCOPY_SCROLLDOWN }, { KEYC_DOWN, 0, MODEKEYCOPY_DOWN }, @@ -483,6 +487,8 @@ const struct mode_key_entry mode_key_emacs_copy[] = { { 't', 0, MODEKEYCOPY_JUMPTO }, { 'v' | KEYC_ESCAPE, 0, MODEKEYCOPY_PREVIOUSPAGE }, { 'w' | KEYC_ESCAPE, 0, MODEKEYCOPY_COPYSELECTION }, + { '{' | KEYC_ESCAPE, 0, MODEKEYCOPY_PREVIOUSPARAGRAPH }, + { '}' | KEYC_ESCAPE, 0, MODEKEYCOPY_NEXTPARAGRAPH }, { KEYC_DOWN | KEYC_CTRL, 0, MODEKEYCOPY_SCROLLDOWN }, { KEYC_DOWN | KEYC_ESCAPE, 0, MODEKEYCOPY_HALFPAGEDOWN }, { KEYC_DOWN, 0, MODEKEYCOPY_DOWN }, diff --git a/tmux.1 b/tmux.1 index a304e76b..d2bfe32b 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1025,6 +1025,7 @@ The following keys are supported as appropriate for the mode: .It Li "Jump to backward" Ta "T" Ta "" .It Li "Jump to forward" Ta "t" Ta "" .It Li "Next page" Ta "C-f" Ta "Page down" +.It Li "Next paragraph" Ta "}" Ta "M-}" .It Li "Next space" Ta "W" Ta "" .It Li "Next space, end of word" Ta "E" Ta "" .It Li "Next word" Ta "w" Ta "" @@ -1032,6 +1033,7 @@ The following keys are supported as appropriate for the mode: .It Li "Other end of selection" Ta "o" Ta "" .It Li "Paste buffer" Ta "p" Ta "C-y" .It Li "Previous page" Ta "C-b" Ta "Page up" +.It Li "Previous paragraph" Ta "{" Ta "M-{" .It Li "Previous space" Ta "B" Ta "" .It Li "Previous word" Ta "b" Ta "M-b" .It Li "Quit mode" Ta "q" Ta "Escape" diff --git a/tmux.h b/tmux.h index b2445fce..0b868537 100644 --- a/tmux.h +++ b/tmux.h @@ -536,12 +536,14 @@ enum mode_key_cmd { MODEKEYCOPY_LEFT, MODEKEYCOPY_MIDDLELINE, MODEKEYCOPY_NEXTPAGE, + MODEKEYCOPY_NEXTPARAGRAPH, MODEKEYCOPY_NEXTSPACE, MODEKEYCOPY_NEXTSPACEEND, MODEKEYCOPY_NEXTWORD, MODEKEYCOPY_NEXTWORDEND, MODEKEYCOPY_OTHEREND, MODEKEYCOPY_PREVIOUSPAGE, + MODEKEYCOPY_PREVIOUSPARAGRAPH, MODEKEYCOPY_PREVIOUSSPACE, MODEKEYCOPY_PREVIOUSWORD, MODEKEYCOPY_RECTANGLETOGGLE, diff --git a/window-copy.c b/window-copy.c index 5c907c3d..a97d6dbe 100644 --- a/window-copy.c +++ b/window-copy.c @@ -27,6 +27,8 @@ struct screen *window_copy_init(struct window_pane *); void window_copy_free(struct window_pane *); void window_copy_pagedown(struct window_pane *); +void window_copy_next_paragraph(struct window_pane *); +void window_copy_previous_paragraph(struct window_pane *); void window_copy_resize(struct window_pane *, u_int, u_int); void window_copy_key(struct window_pane *, struct client *, struct session *, key_code, struct mouse_event *); @@ -403,6 +405,44 @@ window_copy_pagedown(struct window_pane *wp) window_copy_redraw_screen(wp); } +void +window_copy_previous_paragraph(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + u_int ox, oy; + + oy = screen_hsize(data->backing) + data->cy - data->oy; + ox = window_copy_find_length(wp, oy); + + while (oy > 0 && window_copy_find_length(wp, oy) == 0) + oy--; + + while (oy > 0 && window_copy_find_length(wp, oy) > 0) + oy--; + + window_copy_scroll_to(wp, 0, oy); +} + +void +window_copy_next_paragraph(struct window_pane *wp) +{ + struct window_copy_mode_data *data = wp->modedata; + struct screen *s = &data->screen; + u_int maxy, ox, oy; + + oy = screen_hsize(data->backing) + data->cy - data->oy; + maxy = screen_hsize(data->backing) + screen_size_y(s) - 1; + + while (oy < maxy && window_copy_find_length(wp, oy) == 0) + oy++; + + while (oy < maxy && window_copy_find_length(wp, oy) > 0) + oy++; + + ox = window_copy_find_length(wp, oy); + window_copy_scroll_to(wp, ox, oy); +} + void window_copy_resize(struct window_pane *wp, u_int sx, u_int sy) { @@ -548,6 +588,14 @@ window_copy_key(struct window_pane *wp, struct client *c, struct session *sess, for (; np != 0; np--) window_copy_pagedown(wp); break; + case MODEKEYCOPY_PREVIOUSPARAGRAPH: + for (; np != 0; np--) + window_copy_previous_paragraph(wp); + break; + case MODEKEYCOPY_NEXTPARAGRAPH: + for (; np != 0; np--) + window_copy_next_paragraph(wp); + break; case MODEKEYCOPY_HALFPAGEUP: n = screen_size_y(s) / 2; for (; np != 0; np--) { -- cgit