From 8f36458ce8373ec142991fe4bd84d9b8e3bc766b Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Jun 2021 07:56:47 +0000 Subject: [PATCH] More accurate vi(1) word navigation in copy mode and on the status line. This changes the meaning of the word-separators option - setting it to the empty string is equivalent to the previous behavior. From Will Noble in GitHub issue 2693. --- usr.bin/tmux/grid-reader.c | 165 ++++++++++++++++----------- usr.bin/tmux/options-table.c | 8 +- usr.bin/tmux/status.c | 209 ++++++++++++++++++++++++++--------- usr.bin/tmux/tmux.1 | 18 ++- usr.bin/tmux/tmux.h | 8 +- usr.bin/tmux/utf8.c | 4 +- usr.bin/tmux/window-copy.c | 104 +++++++++-------- 7 files changed, 333 insertions(+), 183 deletions(-) diff --git a/usr.bin/tmux/grid-reader.c b/usr.bin/tmux/grid-reader.c index caecceb40db..183b4a4c0ad 100644 --- a/usr.bin/tmux/grid-reader.c +++ b/usr.bin/tmux/grid-reader.c @@ -1,4 +1,4 @@ -/* $OpenBSD: grid-reader.c,v 1.5 2021/06/10 07:22:37 nicm Exp $ */ +/* $OpenBSD: grid-reader.c,v 1.6 2021/06/10 07:56:47 nicm Exp $ */ /* * Copyright (c) 2020 Anindya Mukherjee @@ -153,6 +153,29 @@ grid_reader_cursor_end_of_line(struct grid_reader *gr, int wrap, int all) gr->cx = grid_reader_line_length(gr); } +/* Handle line wrapping while moving the cursor. */ +static int +grid_reader_handle_wrap(struct grid_reader *gr, u_int *xx, u_int *yy) +{ + /* + * Make sure the cursor lies within the grid reader's bounding area, + * wrapping to the next line as necessary. Return zero if the cursor + * would wrap past the bottom of the grid. + */ + while (gr->cx > *xx) { + if (gr->cy == *yy) + return (0); + grid_reader_cursor_start_of_line(gr, 0); + grid_reader_cursor_down(gr); + + if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED) + *xx = gr->gd->sx - 1; + else + *xx = grid_reader_line_length(gr); + } + return (1); +} + /* Check if character under cursor is in set. */ int grid_reader_in_set(struct grid_reader *gr, const char *set) @@ -170,7 +193,6 @@ void grid_reader_cursor_next_word(struct grid_reader *gr, const char *separators) { u_int xx, yy; - int expected = 0; /* Do not break up wrapped words. */ if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED) @@ -180,33 +202,35 @@ grid_reader_cursor_next_word(struct grid_reader *gr, const char *separators) yy = gr->gd->hsize + gr->gd->sy - 1; /* - * If we started inside a word, skip over word characters. Then skip - * over separators till the next word. + * When navigating via spaces (for example with next-space) separators + * should be empty. * - * expected is initially set to 0 for the former and then 1 for the - * latter. It is finally set to 0 when the beginning of the next word is - * found. + * If we started on a separator that is not whitespace, skip over + * subsequent separators that are not whitespace. Otherwise, if we + * started on a non-whitespace character, skip over subsequent + * characters that are neither whitespace nor separators. Then, skip + * over whitespace (if any) until the next non-whitespace character. */ - do { - while (gr->cx > xx || - grid_reader_in_set(gr, separators) == expected) { - /* Move down if we are past the end of the line. */ - if (gr->cx > xx) { - if (gr->cy == yy) - return; - grid_reader_cursor_start_of_line(gr, 0); - grid_reader_cursor_down(gr); - - if (grid_get_line(gr->gd, gr->cy)->flags & - GRID_LINE_WRAPPED) - xx = gr->gd->sx - 1; - else - xx = grid_reader_line_length(gr); - } else + if (!grid_reader_handle_wrap(gr, &xx, &yy)) + return; + if (!grid_reader_in_set(gr, WHITESPACE)) { + if (grid_reader_in_set(gr, separators)) { + do gr->cx++; + while (grid_reader_handle_wrap(gr, &xx, &yy) && + grid_reader_in_set(gr, separators) && + !grid_reader_in_set(gr, WHITESPACE)); + } else { + do + gr->cx++; + while (grid_reader_handle_wrap(gr, &xx, &yy) && + !(grid_reader_in_set(gr, separators) || + grid_reader_in_set(gr, WHITESPACE))); } - expected = !expected; - } while (expected == 1); + } + while (grid_reader_handle_wrap(gr, &xx, &yy) && + grid_reader_in_set(gr, WHITESPACE)) + gr->cx++; } /* Move cursor to the end of the next word. */ @@ -214,7 +238,6 @@ void grid_reader_cursor_next_word_end(struct grid_reader *gr, const char *separators) { u_int xx, yy; - int expected = 1; /* Do not break up wrapped words. */ if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED) @@ -224,49 +247,54 @@ grid_reader_cursor_next_word_end(struct grid_reader *gr, const char *separators) yy = gr->gd->hsize + gr->gd->sy - 1; /* - * If we started on a separator, skip over separators. Then skip over - * word characters till the next separator. + * When navigating via spaces (for example with next-space), separators + * should be empty in both modes. * - * expected is initially set to 1 for the former and then 1 for the - * latter. It is finally set to 1 when the end of the next word is - * found. + * If we started on a whitespace, move until reaching the first + * non-whitespace character. If that character is a separator, treat + * subsequent separators as a word, and continue moving until the first + * non-separator. Otherwise, continue moving until the first separator + * or whitespace. */ - do { - while (gr->cx > xx || - grid_reader_in_set(gr, separators) == expected) { - /* Move down if we are past the end of the line. */ - if (gr->cx > xx) { - if (gr->cy == yy) - return; - grid_reader_cursor_start_of_line(gr, 0); - grid_reader_cursor_down(gr); - - if (grid_get_line(gr->gd, gr->cy)->flags & - GRID_LINE_WRAPPED) - xx = gr->gd->sx - 1; - else - xx = grid_reader_line_length(gr); - } else + + while (grid_reader_handle_wrap(gr, &xx, &yy)) { + if (grid_reader_in_set(gr, WHITESPACE)) + gr->cx++; + else if (grid_reader_in_set(gr, separators)) { + do + gr->cx++; + while (grid_reader_handle_wrap(gr, &xx, &yy) && + grid_reader_in_set(gr, separators) && + !grid_reader_in_set(gr, WHITESPACE)); + return; + } else { + do gr->cx++; + while (grid_reader_handle_wrap(gr, &xx, &yy) && + !(grid_reader_in_set(gr, WHITESPACE) || + grid_reader_in_set(gr, separators))); + return; } - expected = !expected; - } while (expected == 0); + } } /* Move to the previous place where a word begins. */ void grid_reader_cursor_previous_word(struct grid_reader *gr, const char *separators, - int already) + int already, int stop_at_eol) { - int oldx, oldy, r; + int oldx, oldy, at_eol, word_is_letters; /* Move back to the previous word character. */ - if (already || grid_reader_in_set(gr, separators)) { + if (already || grid_reader_in_set(gr, WHITESPACE)) { for (;;) { if (gr->cx > 0) { gr->cx--; - if (!grid_reader_in_set(gr, separators)) + if (!grid_reader_in_set(gr, WHITESPACE)) { + word_is_letters = + !grid_reader_in_set(gr, separators); break; + } } else { if (gr->cy == 0) return; @@ -274,17 +302,21 @@ grid_reader_cursor_previous_word(struct grid_reader *gr, const char *separators, grid_reader_cursor_end_of_line(gr, 0, 0); /* Stop if separator at EOL. */ - if (gr->cx > 0) { + if (stop_at_eol && gr->cx > 0) { oldx = gr->cx; gr->cx--; - r = grid_reader_in_set(gr, separators); + at_eol = grid_reader_in_set(gr, + WHITESPACE); gr->cx = oldx; - if (r) + if (at_eol) { + word_is_letters = 0; break; + } } } } - } + } else + word_is_letters = !grid_reader_in_set(gr, separators); /* Move back to the beginning of this word. */ do { @@ -292,15 +324,16 @@ grid_reader_cursor_previous_word(struct grid_reader *gr, const char *separators, oldy = gr->cy; if (gr->cx == 0) { if (gr->cy == 0 || - ~grid_get_line(gr->gd, gr->cy - 1)->flags & - GRID_LINE_WRAPPED) + (~grid_get_line(gr->gd, gr->cy - 1)->flags & + GRID_LINE_WRAPPED)) break; grid_reader_cursor_up(gr); grid_reader_cursor_end_of_line(gr, 0, 1); } if (gr->cx > 0) gr->cx--; - } while (!grid_reader_in_set(gr, separators)); + } while (!grid_reader_in_set(gr, WHITESPACE) && + word_is_letters != grid_reader_in_set(gr, separators)); gr->cx = oldx; gr->cy = oldy; } @@ -324,17 +357,17 @@ grid_reader_cursor_jump(struct grid_reader *gr, const struct utf8_data *jc) memcmp(gc.data.data, jc->data, gc.data.size) == 0) { gr->cx = px; gr->cy = py; - return 1; + return (1); } px++; } if (py == yy || !(grid_get_line(gr->gd, py)->flags & GRID_LINE_WRAPPED)) - return 0; + return (0); px = 0; } - return 0; + return (0); } /* Jump back to character. */ @@ -354,16 +387,16 @@ grid_reader_cursor_jump_back(struct grid_reader *gr, const struct utf8_data *jc) memcmp(gc.data.data, jc->data, gc.data.size) == 0) { gr->cx = px - 1; gr->cy = py - 1; - return 1; + return (1); } } if (py == 1 || !(grid_get_line(gr->gd, py - 2)->flags & GRID_LINE_WRAPPED)) - return 0; + return (0); xx = grid_line_length(gr->gd, py - 2); } - return 0; + return (0); } /* Jump back to the first non-blank character of the line. */ diff --git a/usr.bin/tmux/options-table.c b/usr.bin/tmux/options-table.c index c093ebba757..1588b637e2d 100644 --- a/usr.bin/tmux/options-table.c +++ b/usr.bin/tmux/options-table.c @@ -1,4 +1,4 @@ -/* $OpenBSD: options-table.c,v 1.142 2021/06/10 07:50:03 nicm Exp $ */ +/* $OpenBSD: options-table.c,v 1.143 2021/06/10 07:56:47 nicm Exp $ */ /* * Copyright (c) 2011 Nicholas Marriott @@ -755,7 +755,11 @@ const struct options_table_entry options_table[] = { { .name = "word-separators", .type = OPTIONS_TABLE_STRING, .scope = OPTIONS_TABLE_SESSION, - .default_str = " ", + /* + * The set of non-alphanumeric printable ASCII characters minus the + * underscore. + */ + .default_str = "!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", .text = "Characters considered to separate words." }, diff --git a/usr.bin/tmux/status.c b/usr.bin/tmux/status.c index 6ee43499872..fe7dc117b8d 100644 --- a/usr.bin/tmux/status.c +++ b/usr.bin/tmux/status.c @@ -1,4 +1,4 @@ -/* $OpenBSD: status.c,v 1.224 2021/06/10 07:50:04 nicm Exp $ */ +/* $OpenBSD: status.c,v 1.225 2021/06/10 07:56:47 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -876,17 +876,25 @@ status_prompt_translate_key(struct client *c, key_code key, key_code *new_key) *new_key = KEYC_BSPACE; return (1); case 'b': - case 'B': *new_key = 'b'|KEYC_META; return (1); + case 'B': + *new_key = 'B'|KEYC_VI; + return (1); case 'd': *new_key = '\025'; return (1); case 'e': + *new_key = 'e'|KEYC_VI; + return (1); case 'E': + *new_key = 'E'|KEYC_VI; + return (1); case 'w': + *new_key = 'w'|KEYC_VI; + return (1); case 'W': - *new_key = 'f'|KEYC_META; + *new_key = 'W'|KEYC_VI; return (1); case 'p': *new_key = '\031'; /* C-y */ @@ -1061,16 +1069,125 @@ status_prompt_replace_complete(struct client *c, const char *s) return (1); } +/* Prompt forward to the next beginning of a word. */ +static void +status_prompt_forward_word(struct client *c, size_t size, int vi, + const char *separators) +{ + size_t idx = c->prompt_index; + int word_is_separators; + + /* In emacs mode, skip until the first non-whitespace character. */ + if (!vi) + while (idx != size && + status_prompt_space(&c->prompt_buffer[idx])) + idx++; + + /* Can't move forward if we're already at the end. */ + if (idx == size) { + c->prompt_index = idx; + return; + } + + /* Determine the current character class (separators or not). */ + word_is_separators = status_prompt_in_list(separators, + &c->prompt_buffer[idx]) && + !status_prompt_space(&c->prompt_buffer[idx]); + + /* Skip ahead until the first space or opposite character class. */ + do { + idx++; + if (status_prompt_space(&c->prompt_buffer[idx])) { + /* In vi mode, go to the start of the next word. */ + if (vi) + while (idx != size && + status_prompt_space(&c->prompt_buffer[idx])) + idx++; + break; + } + } while (idx != size && word_is_separators == status_prompt_in_list( + separators, &c->prompt_buffer[idx])); + + c->prompt_index = idx; +} + +/* Prompt forward to the next end of a word. */ +static void +status_prompt_end_word(struct client *c, size_t size, const char *separators) +{ + size_t idx = c->prompt_index; + int word_is_separators; + + /* Can't move forward if we're already at the end. */ + if (idx == size) + return; + + /* Find the next word. */ + do { + idx++; + if (idx == size) { + c->prompt_index = idx; + return; + } + } while (status_prompt_space(&c->prompt_buffer[idx])); + + /* Determine the character class (separators or not). */ + word_is_separators = status_prompt_in_list(separators, + &c->prompt_buffer[idx]); + + /* Skip ahead until the next space or opposite character class. */ + do { + idx++; + if (idx == size) + break; + } while (!status_prompt_space(&c->prompt_buffer[idx]) && + word_is_separators == status_prompt_in_list(separators, + &c->prompt_buffer[idx])); + + /* Back up to the previous character to stop at the end of the word. */ + c->prompt_index = idx - 1; +} + +/* Prompt backward to the previous beginning of a word. */ +static void +status_prompt_backward_word(struct client *c, const char *separators) +{ + size_t idx = c->prompt_index; + int word_is_separators; + + /* Find non-whitespace. */ + while (idx != 0) { + --idx; + if (!status_prompt_space(&c->prompt_buffer[idx])) + break; + } + word_is_separators = status_prompt_in_list(separators, + &c->prompt_buffer[idx]); + + /* Find the character before the beginning of the word. */ + while (idx != 0) { + --idx; + if (status_prompt_space(&c->prompt_buffer[idx]) || + word_is_separators != status_prompt_in_list(separators, + &c->prompt_buffer[idx])) { + /* Go back to the word. */ + idx++; + break; + } + } + c->prompt_index = idx; +} + /* Handle keys in prompt. */ int status_prompt_key(struct client *c, key_code key) { struct options *oo = c->session->options; char *s, *cp, prefix = '='; - const char *histstr, *ws = NULL, *keystring; + const char *histstr, *separators = NULL, *keystring; size_t size, idx; struct utf8_data tmp; - int keys; + int keys, word_is_separators; if (c->prompt_flags & PROMPT_KEY) { keystring = key_string_lookup_key(key, 0); @@ -1173,20 +1290,24 @@ process_key: } break; case '\027': /* C-w */ - ws = options_get_string(oo, "word-separators"); + separators = options_get_string(oo, "word-separators"); idx = c->prompt_index; - /* Find a non-separator. */ + /* Find non-whitespace. */ while (idx != 0) { idx--; - if (!status_prompt_in_list(ws, &c->prompt_buffer[idx])) + if (!status_prompt_space(&c->prompt_buffer[idx])) break; } + word_is_separators = status_prompt_in_list(separators, + &c->prompt_buffer[idx]); - /* Find the separator at the beginning of the word. */ + /* Find the character before the beginning of the word. */ while (idx != 0) { idx--; - if (status_prompt_in_list(ws, &c->prompt_buffer[idx])) { + if (status_prompt_space(&c->prompt_buffer[idx]) || + word_is_separators != status_prompt_in_list( + separators, &c->prompt_buffer[idx])) { /* Go back to the word. */ idx++; break; @@ -1208,50 +1329,32 @@ process_key: c->prompt_index = idx; goto changed; - case 'f'|KEYC_META: case KEYC_RIGHT|KEYC_CTRL: - ws = options_get_string(oo, "word-separators"); - - /* Find a word. */ - while (c->prompt_index != size) { - idx = ++c->prompt_index; - if (!status_prompt_in_list(ws, &c->prompt_buffer[idx])) - break; - } - - /* Find the separator at the end of the word. */ - while (c->prompt_index != size) { - idx = ++c->prompt_index; - if (status_prompt_in_list(ws, &c->prompt_buffer[idx])) - break; - } - - /* Back up to the end-of-word like vi. */ - if (options_get_number(oo, "status-keys") == MODEKEY_VI && - c->prompt_index != 0) - c->prompt_index--; - + case 'f'|KEYC_META: + separators = options_get_string(oo, "word-separators"); + status_prompt_forward_word(c, size, 0, separators); + goto changed; + case 'E'|KEYC_VI: + status_prompt_end_word(c, size, ""); + goto changed; + case 'e'|KEYC_VI: + separators = options_get_string(oo, "word-separators"); + status_prompt_end_word(c, size, separators); + goto changed; + case 'W'|KEYC_VI: + status_prompt_forward_word(c, size, 1, ""); + goto changed; + case 'w'|KEYC_VI: + separators = options_get_string(oo, "word-separators"); + status_prompt_forward_word(c, size, 1, separators); + goto changed; + case 'B'|KEYC_VI: + status_prompt_backward_word(c, ""); goto changed; - case 'b'|KEYC_META: case KEYC_LEFT|KEYC_CTRL: - ws = options_get_string(oo, "word-separators"); - - /* Find a non-separator. */ - while (c->prompt_index != 0) { - idx = --c->prompt_index; - if (!status_prompt_in_list(ws, &c->prompt_buffer[idx])) - break; - } - - /* Find the separator at the beginning of the word. */ - while (c->prompt_index != 0) { - idx = --c->prompt_index; - if (status_prompt_in_list(ws, &c->prompt_buffer[idx])) { - /* Go back to the word. */ - c->prompt_index++; - break; - } - } + case 'b'|KEYC_META: + separators = options_get_string(oo, "word-separators"); + status_prompt_backward_word(c, separators); goto changed; case KEYC_UP: case '\020': /* C-p */ @@ -1339,8 +1442,10 @@ append_key: return (0); if (key <= 0x7f) utf8_set(&tmp, key); - else + else if (KEYC_IS_UNICODE(key)) utf8_to_data(key, &tmp); + else + return (0); c->prompt_buffer = xreallocarray(c->prompt_buffer, size + 2, sizeof *c->prompt_buffer); diff --git a/usr.bin/tmux/tmux.1 b/usr.bin/tmux/tmux.1 index 626ff832dde..2d55b9f61ea 100644 --- a/usr.bin/tmux/tmux.1 +++ b/usr.bin/tmux/tmux.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: tmux.1,v 1.840 2021/06/10 07:52:56 nicm Exp $ +.\" $OpenBSD: tmux.1,v 1.841 2021/06/10 07:56:47 nicm Exp $ .\" .\" Copyright (c) 2007 Nicholas Marriott .\" @@ -1782,19 +1782,18 @@ commands) or when the cursor reaches the bottom (for scrolling commands). .Ql -no-clear variants do not clear the selection. .Pp -The next and previous word keys use space and the -.Ql - , -.Ql _ -and -.Ql @ -characters as word delimiters by default, but this can be adjusted by -setting the +The next and previous word keys skip over whitespace and treat consecutive +runs of either word separators or other letters as words. +Word separators can be customized with the .Em word-separators session option. Next word moves to the start of the next word, next word end to the end of the next word and previous word to the start of the previous word. The three next and previous space keys work similarly but use a space alone as the word separator. +Setting +.Em word-separators +to the empty string makes next/previous word equivalent to next/previous space. .Pp The jump commands enable quick movement within a line. For instance, typing @@ -3974,9 +3973,6 @@ If set to both, a bell and a message are produced. Sets the session's conception of what characters are considered word separators, for the purposes of the next and previous word commands in copy mode. -The default is -.Ql \ -_@ . -.El .Pp Available window options are: .Pp diff --git a/usr.bin/tmux/tmux.h b/usr.bin/tmux/tmux.h index 20002042708..765dc37821f 100644 --- a/usr.bin/tmux/tmux.h +++ b/usr.bin/tmux/tmux.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tmux.h,v 1.1109 2021/06/10 07:50:04 nicm Exp $ */ +/* $OpenBSD: tmux.h,v 1.1110 2021/06/10 07:56:47 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -130,6 +130,7 @@ struct winlink; #define KEYC_CURSOR 0x04000000000000ULL #define KEYC_IMPLIED_META 0x08000000000000ULL #define KEYC_BUILD_MODIFIERS 0x10000000000000ULL +#define KEYC_VI 0x20000000000000ULL /* Masks for key bits. */ #define KEYC_MASK_MODIFIERS 0x00f00000000000ULL @@ -589,6 +590,9 @@ struct msg_write_close { int stream; }; +/* Character classes. */ +#define WHITESPACE " " + /* Mode keys. */ #define MODEKEY_EMACS 0 #define MODEKEY_VI 1 @@ -2639,7 +2643,7 @@ void grid_reader_cursor_end_of_line(struct grid_reader *, int, int); void grid_reader_cursor_next_word(struct grid_reader *, const char *); void grid_reader_cursor_next_word_end(struct grid_reader *, const char *); void grid_reader_cursor_previous_word(struct grid_reader *, const char *, - int); + int, int); int grid_reader_cursor_jump(struct grid_reader *, const struct utf8_data *); int grid_reader_cursor_jump_back(struct grid_reader *, diff --git a/usr.bin/tmux/utf8.c b/usr.bin/tmux/utf8.c index 63b60540995..81932ee1ba5 100644 --- a/usr.bin/tmux/utf8.c +++ b/usr.bin/tmux/utf8.c @@ -1,4 +1,4 @@ -/* $OpenBSD: utf8.c,v 1.57 2020/09/16 18:37:55 nicm Exp $ */ +/* $OpenBSD: utf8.c,v 1.58 2021/06/10 07:56:47 nicm Exp $ */ /* * Copyright (c) 2008 Nicholas Marriott @@ -65,7 +65,7 @@ static struct utf8_index_tree utf8_index_tree = RB_INITIALIZER(utf8_index_tree); static u_int utf8_next_index; #define UTF8_GET_SIZE(uc) (((uc) >> 24) & 0x1f) -#define UTF8_GET_WIDTH(flags) (((uc) >> 29) - 1) +#define UTF8_GET_WIDTH(uc) (((uc) >> 29) - 1) #define UTF8_SET_SIZE(size) (((utf8_char)(size)) << 24) #define UTF8_SET_WIDTH(width) ((((utf8_char)(width)) + 1) << 29) diff --git a/usr.bin/tmux/window-copy.c b/usr.bin/tmux/window-copy.c index dabc95e917c..90bf7f90a09 100644 --- a/usr.bin/tmux/window-copy.c +++ b/usr.bin/tmux/window-copy.c @@ -1,4 +1,4 @@ -/* $OpenBSD: window-copy.c,v 1.322 2021/04/05 08:43:48 nicm Exp $ */ +/* $OpenBSD: window-copy.c,v 1.323 2021/06/10 07:56:47 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -128,7 +128,7 @@ static void window_copy_cursor_next_word_end_pos(struct window_mode_entry *, static void window_copy_cursor_next_word_end(struct window_mode_entry *, const char *, int); static void window_copy_cursor_previous_word_pos(struct window_mode_entry *, - const char *, int, u_int *, u_int *); + const char *, u_int *, u_int *); static void window_copy_cursor_previous_word(struct window_mode_entry *, const char *, int); static void window_copy_scroll_up(struct window_mode_entry *, u_int); @@ -255,7 +255,7 @@ struct window_copy_mode_data { SEL_LINE, /* select one line at a time */ } selflag; - const char *ws; /* word separators */ + const char *separators; /* word separators */ u_int dx; /* drag start position */ u_int dy; @@ -1321,7 +1321,7 @@ window_copy_cmd_previous_matching_bracket(struct window_copy_cmd_state *cs) tried = 1; goto retry; } - window_copy_cursor_previous_word(wme, "}]) ", 1); + window_copy_cursor_previous_word(wme, close, 1); } continue; } @@ -1433,8 +1433,7 @@ window_copy_cmd_next_matching_bracket(struct window_copy_cmd_state *cs) tried = 1; goto retry; } - window_copy_cursor_next_word_end(wme, "{[( ", - 0); + window_copy_cursor_next_word_end(wme, open, 0); continue; } /* For vi, continue searching for bracket until EOL. */ @@ -1506,7 +1505,7 @@ window_copy_cmd_next_space(struct window_copy_cmd_state *cs) u_int np = wme->prefix; for (; np != 0; np--) - window_copy_cursor_next_word(wme, " "); + window_copy_cursor_next_word(wme, ""); return (WINDOW_COPY_CMD_NOTHING); } @@ -1517,7 +1516,7 @@ window_copy_cmd_next_space_end(struct window_copy_cmd_state *cs) u_int np = wme->prefix; for (; np != 0; np--) - window_copy_cursor_next_word_end(wme, " ", 0); + window_copy_cursor_next_word_end(wme, "", 0); return (WINDOW_COPY_CMD_NOTHING); } @@ -1525,13 +1524,13 @@ static enum window_copy_cmd_action window_copy_cmd_next_word(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; - struct session *s = cs->s; u_int np = wme->prefix; - const char *ws; + const char *separators; + + separators = options_get_string(cs->s->options, "word-separators"); - ws = options_get_string(s->options, "word-separators"); for (; np != 0; np--) - window_copy_cursor_next_word(wme, ws); + window_copy_cursor_next_word(wme, separators); return (WINDOW_COPY_CMD_NOTHING); } @@ -1539,13 +1538,13 @@ static enum window_copy_cmd_action window_copy_cmd_next_word_end(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; - struct session *s = cs->s; u_int np = wme->prefix; - const char *ws; + const char *separators; + + separators = options_get_string(cs->s->options, "word-separators"); - ws = options_get_string(s->options, "word-separators"); for (; np != 0; np--) - window_copy_cursor_next_word_end(wme, ws, 0); + window_copy_cursor_next_word_end(wme, separators, 0); return (WINDOW_COPY_CMD_NOTHING); } @@ -1618,7 +1617,7 @@ window_copy_cmd_previous_space(struct window_copy_cmd_state *cs) u_int np = wme->prefix; for (; np != 0; np--) - window_copy_cursor_previous_word(wme, " ", 1); + window_copy_cursor_previous_word(wme, "", 1); return (WINDOW_COPY_CMD_NOTHING); } @@ -1626,13 +1625,13 @@ static enum window_copy_cmd_action window_copy_cmd_previous_word(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; - struct session *s = cs->s; u_int np = wme->prefix; - const char *ws; + const char *separators; + + separators = options_get_string(cs->s->options, "word-separators"); - ws = options_get_string(s->options, "word-separators"); for (; np != 0; np--) - window_copy_cursor_previous_word(wme, ws, 1); + window_copy_cursor_previous_word(wme, separators, 1); return (WINDOW_COPY_CMD_NOTHING); } @@ -1778,18 +1777,20 @@ static enum window_copy_cmd_action window_copy_cmd_select_word(struct window_copy_cmd_state *cs) { struct window_mode_entry *wme = cs->wme; - struct session *s = cs->s; + struct options *session_options = cs->s->options; struct window_copy_mode_data *data = wme->data; u_int px, py, nextx, nexty; + data->lineflag = LINE_SEL_LEFT_RIGHT; data->rectflag = 0; data->selflag = SEL_WORD; data->dx = data->cx; data->dy = screen_hsize(data->backing) + data->cy - data->oy; - data->ws = options_get_string(s->options, "word-separators"); - window_copy_cursor_previous_word(wme, data->ws, 0); + data->separators = options_get_string(session_options, + "word-separators"); + window_copy_cursor_previous_word(wme, data->separators, 0); px = data->cx; py = screen_hsize(data->backing) + data->cy - data->oy; data->selrx = px; @@ -1805,8 +1806,8 @@ window_copy_cmd_select_word(struct window_copy_cmd_state *cs) nexty++; } if (px >= window_copy_find_length(wme, py) || - !window_copy_in_set(wme, nextx, nexty, data->ws)) - window_copy_cursor_next_word_end(wme, data->ws, 1); + !window_copy_in_set(wme, nextx, nexty, WHITESPACE)) + window_copy_cursor_next_word_end(wme, data->separators, 1); else { window_copy_update_cursor(wme, px, data->cy); if (window_copy_update_selection(wme, 1, 1)) @@ -3730,8 +3731,8 @@ window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin, begin = 0; if (data->dy > yy || (data->dy == yy && data->dx > xx)) { /* Right to left selection. */ - window_copy_cursor_previous_word_pos(wme, data->ws, 0, - &xx, &yy); + window_copy_cursor_previous_word_pos(wme, + data->separators, &xx, &yy); begin = 1; /* Reset the end. */ @@ -3740,9 +3741,10 @@ window_copy_synchronize_cursor_end(struct window_mode_entry *wme, int begin, } else { /* Left to right selection. */ if (xx >= window_copy_find_length(wme, yy) || - !window_copy_in_set(wme, xx + 1, yy, data->ws)) + !window_copy_in_set(wme, xx + 1, yy, WHITESPACE)) { window_copy_cursor_next_word_end_pos(wme, - data->ws, &xx, &yy); + data->separators, &xx, &yy); + } /* Reset the start. */ data->selx = data->selrx; @@ -4666,19 +4668,19 @@ window_copy_cursor_next_word_end_pos(struct window_mode_entry *wme, struct screen *back_s = data->backing; struct grid_reader gr; u_int px, py, hsize; - int keys; px = data->cx; hsize = screen_hsize(back_s); py = hsize + data->cy - data->oy; grid_reader_start(&gr, back_s->grid, px, py); - keys = options_get_number(oo, "mode-keys"); - if (keys == MODEKEY_VI && !grid_reader_in_set(&gr, separators)) - grid_reader_cursor_right(&gr, 0, 0); - grid_reader_cursor_next_word_end(&gr, separators); - if (keys == MODEKEY_VI) + if (options_get_number(oo, "mode-keys") == MODEKEY_VI) { + if (!grid_reader_in_set(&gr, WHITESPACE)) + grid_reader_cursor_right(&gr, 0, 0); + grid_reader_cursor_next_word_end(&gr, separators); grid_reader_cursor_left(&gr, 1); + } else + grid_reader_cursor_next_word_end(&gr, separators); grid_reader_get_cursor(&gr, &px, &py); *ppx = px; *ppy = py; @@ -4695,7 +4697,6 @@ window_copy_cursor_next_word_end(struct window_mode_entry *wme, struct screen *back_s = data->backing; struct grid_reader gr; u_int px, py, oldy, hsize; - int keys; px = data->cx; hsize = screen_hsize(back_s); @@ -4703,12 +4704,13 @@ window_copy_cursor_next_word_end(struct window_mode_entry *wme, oldy = data->cy; grid_reader_start(&gr, back_s->grid, px, py); - keys = options_get_number(oo, "mode-keys"); - if (keys == MODEKEY_VI && !grid_reader_in_set(&gr, separators)) - grid_reader_cursor_right(&gr, 0, 0); - grid_reader_cursor_next_word_end(&gr, separators); - if (keys == MODEKEY_VI) + if (options_get_number(oo, "mode-keys") == MODEKEY_VI) { + if (!grid_reader_in_set(&gr, WHITESPACE)) + grid_reader_cursor_right(&gr, 0, 0); + grid_reader_cursor_next_word_end(&gr, separators); grid_reader_cursor_left(&gr, 1); + } else + grid_reader_cursor_next_word_end(&gr, separators); grid_reader_get_cursor(&gr, &px, &py); window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s), data->oy, oldy, px, py, no_reset); @@ -4717,7 +4719,7 @@ window_copy_cursor_next_word_end(struct window_mode_entry *wme, /* Compute the previous place where a word begins. */ static void window_copy_cursor_previous_word_pos(struct window_mode_entry *wme, - const char *separators, int already, u_int *ppx, u_int *ppy) + const char *separators, u_int *ppx, u_int *ppy) { struct window_copy_mode_data *data = wme->data; struct screen *back_s = data->backing; @@ -4729,7 +4731,8 @@ window_copy_cursor_previous_word_pos(struct window_mode_entry *wme, py = hsize + data->cy - data->oy; grid_reader_start(&gr, back_s->grid, px, py); - grid_reader_cursor_previous_word(&gr, separators, already); + grid_reader_cursor_previous_word(&gr, separators, /* already= */ 0, + /* stop_at_eol= */ 1); grid_reader_get_cursor(&gr, &px, &py); *ppx = px; *ppy = py; @@ -4744,6 +4747,11 @@ window_copy_cursor_previous_word(struct window_mode_entry *wme, struct screen *back_s = data->backing; struct grid_reader gr; u_int px, py, oldy, hsize; + int stop_at_eol; + + stop_at_eol = + options_get_number(wme->wp->window->options, "mode-keys") + == MODEKEY_EMACS; px = data->cx; hsize = screen_hsize(back_s); @@ -4751,7 +4759,7 @@ window_copy_cursor_previous_word(struct window_mode_entry *wme, oldy = data->cy; grid_reader_start(&gr, back_s->grid, px, py); - grid_reader_cursor_previous_word(&gr, separators, already); + grid_reader_cursor_previous_word(&gr, separators, already, stop_at_eol); grid_reader_get_cursor(&gr, &px, &py); window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py); } @@ -4893,10 +4901,10 @@ window_copy_start_drag(struct client *c, struct mouse_event *m) data->selflag = SEL_CHAR; switch (data->selflag) { case SEL_WORD: - if (data->ws != NULL) { + if (data->separators != NULL) { window_copy_update_cursor(wme, x, y); - window_copy_cursor_previous_word_pos(wme, data->ws, 0, - &x, &y); + window_copy_cursor_previous_word_pos(wme, + data->separators, &x, &y); y -= screen_hsize(data->backing) - data->oy; } window_copy_update_cursor(wme, x, y); -- 2.20.1