=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/tmux/window-copy.c,v retrieving revision 1.246 retrieving revision 1.247 diff -u -r1.246 -r1.247 --- src/usr.bin/tmux/window-copy.c 2020/02/20 07:34:57 1.246 +++ src/usr.bin/tmux/window-copy.c 2020/02/24 09:53:59 1.247 @@ -1,4 +1,4 @@ -/* $OpenBSD: window-copy.c,v 1.246 2020/02/20 07:34:57 nicm Exp $ */ +/* $OpenBSD: window-copy.c,v 1.247 2020/02/24 09:53:59 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -119,8 +119,12 @@ static void window_copy_cursor_jump_to_back(struct window_mode_entry *); static void window_copy_cursor_next_word(struct window_mode_entry *, const char *); +static void window_copy_cursor_next_word_end_pos(struct window_mode_entry *, + const char *, u_int *, u_int *); static void window_copy_cursor_next_word_end(struct window_mode_entry *, const char *); +static void window_copy_cursor_previous_word_pos(struct window_mode_entry *, + const char *, int, 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); @@ -227,6 +231,22 @@ int rectflag; /* in rectangle copy mode? */ int scroll_exit; /* exit on scroll to end? */ + enum { + SEL_CHAR, /* select one char at a time */ + SEL_WORD, /* select one word at a time */ + SEL_LINE, /* select one line at a time */ + } selflag; + + const char *ws; /* word separators */ + + u_int dx; /* drag start position */ + u_int dy; + + u_int selrx; /* selection reset positions */ + u_int selry; + u_int endselrx; + u_int endselry; + u_int cx; u_int cy; @@ -695,7 +715,10 @@ struct client *c = cs->c; struct mouse_event *m = cs->m; struct window_copy_mode_data *data = wme->data; + struct options *oo = cs->s->options; + data->ws = options_get_string(oo, "word-separators"); + if (m != NULL) { window_copy_start_drag(c, m); return (WINDOW_COPY_CMD_NOTHING); @@ -1507,12 +1530,19 @@ data->lineflag = LINE_SEL_LEFT_RIGHT; data->rectflag = 0; + data->selflag = SEL_LINE; + data->dx = data->cx; + data->dy = screen_hsize(data->backing) + data->cy - data->oy; window_copy_cursor_start_of_line(wme); + data->selrx = data->cx; + data->selry = screen_hsize(data->backing) + data->cy - data->oy; window_copy_start_selection(wme); for (; np > 1; np--) window_copy_cursor_down(wme, 0); window_copy_cursor_end_of_line(wme); + data->endselrx = data->cx; + data->endselry = screen_hsize(data->backing) + data->cy - data->oy; return (WINDOW_COPY_CMD_REDRAW); } @@ -1524,26 +1554,33 @@ struct session *s = cs->s; struct window_copy_mode_data *data = wme->data; const char *ws; - u_int px, py, xx; + u_int px, py; 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; px = data->cx; py = screen_hsize(data->backing) + data->cy - data->oy; - xx = window_copy_find_length(wme, py); ws = options_get_string(s->options, "word-separators"); window_copy_cursor_previous_word(wme, ws, 0); + data->selrx = data->cx; + data->selry = screen_hsize(data->backing) + data->cy - data->oy; window_copy_start_selection(wme); - if (px >= xx || !window_copy_in_set(wme, px + 1, py, ws)) + if (px >= window_copy_find_length(wme, py) || + !window_copy_in_set(wme, px + 1, py, ws)) window_copy_cursor_next_word_end(wme, ws); else { window_copy_update_cursor(wme, px, data->cy); if (window_copy_update_selection(wme, 1)) window_copy_redraw_lines(wme, data->cy, 1); } + data->endselrx = data->cx; + data->endselry = screen_hsize(data->backing) + data->cy - data->oy; return (WINDOW_COPY_CMD_REDRAW); } @@ -2788,6 +2825,7 @@ window_copy_redraw_selection(struct window_mode_entry *wme, u_int old_y) { struct window_copy_mode_data *data = wme->data; + struct grid *gd = data->backing->grid; u_int new_y, start, end; new_y = data->cy; @@ -2798,6 +2836,16 @@ start = new_y; end = old_y; } + + /* + * In word selection mode the first word on the line below the cursor + * might be selected, so add this line to the redraw area. + */ + if (data->selflag == SEL_WORD) { + /* Last grid line in data coordinates. */ + if (end < gd->sy + data->oy - 1) + end++; + } window_copy_redraw_lines(wme, start, end - start + 1); } @@ -2825,22 +2873,82 @@ } static void -window_copy_synchronize_cursor(struct window_mode_entry *wme) +window_copy_synchronize_cursor_end(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; u_int xx, yy; + int begin = 0; - xx = data->cx; yy = screen_hsize(data->backing) + data->cy - data->oy; + switch (data->selflag) { + case SEL_WORD: + xx = data->cx; + if (data->ws == NULL) + break; + 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); + begin = 1; - switch (data->cursordrag) { - case CURSORDRAG_ENDSEL: + /* Reset the end. */ + data->endselx = data->endselrx; + data->endsely = data->endselry; + } 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_cursor_next_word_end_pos(wme, + data->ws, &xx, &yy); + + /* Reset the start. */ + data->selx = data->selrx; + data->sely = data->selry; + } + break; + case SEL_LINE: + if (data->dy > yy) { + /* Right to left selection. */ + xx = 0; + begin = 1; + + /* Reset the end. */ + data->endselx = data->endselrx; + data->endsely = data->endselry; + } else { + /* Left to right selection. */ + xx = window_copy_find_length(wme, yy); + + /* Reset the start. */ + data->selx = data->selrx; + data->sely = data->selry; + } + break; + case SEL_CHAR: + xx = data->cx; + break; + } + if (begin) { + data->selx = xx; + data->sely = yy; + } else { data->endselx = xx; data->endsely = yy; + } +} + +static void +window_copy_synchronize_cursor(struct window_mode_entry *wme) +{ + struct window_copy_mode_data *data = wme->data; + + switch (data->cursordrag) { + case CURSORDRAG_ENDSEL: + window_copy_synchronize_cursor_end(wme); break; case CURSORDRAG_SEL: - data->selx = xx; - data->sely = yy; + data->selx = data->cx; + data->sely = screen_hsize(data->backing) + data->cy - data->oy; break; case CURSORDRAG_NONE: break; @@ -3710,6 +3818,55 @@ } static void +window_copy_cursor_next_word_end_pos(struct window_mode_entry *wme, + const char *separators, u_int *ppx, u_int *ppy) +{ + struct window_pane *wp = wme->wp; + struct window_copy_mode_data *data = wme->data; + struct options *oo = wp->window->options; + struct screen *back_s = data->backing; + u_int px, py, xx, yy; + int keys, expected = 1; + + px = data->cx; + py = screen_hsize(back_s) + data->cy - data->oy; + xx = window_copy_find_length(wme, py); + yy = screen_hsize(back_s) + screen_size_y(back_s) - 1; + + keys = options_get_number(oo, "mode-keys"); + if (keys == MODEKEY_VI && !window_copy_in_set(wme, px, py, separators)) + px++; + + /* + * First skip past any word characters, then any non-word characters. + * + * expected is initially set to 1 for the former and then 0 for the + * latter. + */ + do { + while (px > xx || + window_copy_in_set(wme, px, py, separators) == expected) { + /* Move down if we're past the end of the line. */ + if (px > xx) { + if (py == yy) + return; + py++; + px = 0; + xx = window_copy_find_length(wme, py); + } else + px++; + } + expected = !expected; + } while (expected == 0); + + if (keys == MODEKEY_VI && px != 0) + px--; + + *ppx = px; + *ppy = py; +} + +static void window_copy_cursor_next_word_end(struct window_mode_entry *wme, const char *separators) { @@ -3761,6 +3918,54 @@ window_copy_redraw_lines(wme, data->cy, 1); } +/* 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) +{ + struct window_copy_mode_data *data = wme->data; + u_int px, py; + + px = data->cx; + py = screen_hsize(data->backing) + data->cy - data->oy; + + /* Move back to the previous word character. */ + if (already || window_copy_in_set(wme, px, py, separators)) { + for (;;) { + if (px > 0) { + px--; + if (!window_copy_in_set(wme, px, py, + separators)) + break; + } else { + if (data->cy == 0 && + (screen_hsize(data->backing) == 0 || + data->oy >= + screen_hsize(data->backing) - 1)) + goto out; + py--; + + py = screen_hsize(data->backing) + data->cy - + data->oy; + px = window_copy_find_length(wme, py); + + /* Stop if separator at EOL. */ + if (px > 0 && window_copy_in_set(wme, px - 1, + py, separators)) + break; + } + } + } + + /* Move back to the beginning of this word. */ + while (px > 0 && !window_copy_in_set(wme, px - 1, py, separators)) + px--; + +out: + *ppx = px; + *ppy = py; +} + /* Move to the previous place where a word begins. */ static void window_copy_cursor_previous_word(struct window_mode_entry *wme, @@ -3777,21 +3982,24 @@ for (;;) { if (px > 0) { px--; - if (!window_copy_in_set(wme, px, py, separators)) + if (!window_copy_in_set(wme, px, py, + separators)) break; } else { if (data->cy == 0 && (screen_hsize(data->backing) == 0 || - data->oy >= screen_hsize(data->backing) - 1)) + data->oy >= + screen_hsize(data->backing) - 1)) goto out; window_copy_cursor_up(wme, 0); - py = screen_hsize(data->backing) + data->cy - data->oy; + py = screen_hsize(data->backing) + data->cy - + data->oy; px = window_copy_find_length(wme, py); /* Stop if separator at EOL. */ - if (px > 0 && - window_copy_in_set(wme, px - 1, py, separators)) + if (px > 0 && window_copy_in_set(wme, px - 1, + py, separators)) break; } } @@ -3913,6 +4121,7 @@ { struct window_pane *wp; struct window_mode_entry *wme; + struct window_copy_mode_data *data; u_int x, y; if (c == NULL) @@ -3933,10 +4142,27 @@ c->tty.mouse_drag_update = window_copy_drag_update; c->tty.mouse_drag_release = window_copy_drag_release; - window_copy_update_cursor(wme, x, y); - window_copy_start_selection(wme); - window_copy_redraw_screen(wme); + data = wme->data; + switch (data->selflag) { + case SEL_WORD: + if (data->ws) { + window_copy_update_cursor(wme, x, y); + window_copy_cursor_previous_word_pos(wme, + data->ws, 0, &x, &y); + y -= screen_hsize(data->backing) - data->oy; + } + window_copy_update_cursor(wme, x, y); + break; + case SEL_LINE: + window_copy_update_cursor(wme, 0, y); + break; + case SEL_CHAR: + window_copy_update_cursor(wme, x, y); + window_copy_start_selection(wme); + break; + } + window_copy_redraw_screen(wme); window_copy_drag_update(c, m); }