aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mode-key.c6
-rw-r--r--tmux.12
-rw-r--r--tmux.h2
-rw-r--r--tty-keys.c185
-rw-r--r--utf8.c8
-rw-r--r--window-copy.c48
6 files changed, 181 insertions, 70 deletions
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 776b6faf..b0d5afae 100644
--- a/tmux.1
+++ b/tmux.1
@@ -1029,6 +1029,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 ""
@@ -1036,6 +1037,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 aa3e4967..b0b13f5c 100644
--- a/tmux.h
+++ b/tmux.h
@@ -538,12 +538,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/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;
diff --git a/utf8.c b/utf8.c
index 54cea671..87dce597 100644
--- a/utf8.c
+++ b/utf8.c
@@ -118,6 +118,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);
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 *);
@@ -404,6 +406,44 @@ window_copy_pagedown(struct window_pane *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)
{
struct window_copy_mode_data *data = wp->modedata;
@@ -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--) {