=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/tmux/window-copy.c,v retrieving revision 1.246 retrieving revision 1.247 diff -c -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 **** ! /* $OpenBSD: window-copy.c,v 1.246 2020/02/20 07:34:57 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott --- 1,4 ---- ! /* $OpenBSD: window-copy.c,v 1.247 2020/02/24 09:53:59 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott *************** *** 119,126 **** --- 119,130 ---- 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,232 **** --- 231,252 ---- 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,701 **** --- 715,724 ---- 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,1518 **** --- 1530,1548 ---- 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,1549 **** struct session *s = cs->s; struct window_copy_mode_data *data = wme->data; const char *ws; ! u_int px, py, xx; data->lineflag = LINE_SEL_LEFT_RIGHT; data->rectflag = 0; 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); window_copy_start_selection(wme); ! if (px >= xx || !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); } return (WINDOW_COPY_CMD_REDRAW); } --- 1554,1586 ---- struct session *s = cs->s; struct window_copy_mode_data *data = wme->data; const char *ws; ! 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; 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 >= 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,2793 **** --- 2825,2831 ---- 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,2803 **** --- 2836,2851 ---- 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,2846 **** } static void ! window_copy_synchronize_cursor(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; u_int xx, yy; - xx = data->cx; yy = screen_hsize(data->backing) + data->cy - data->oy; ! switch (data->cursordrag) { ! case CURSORDRAG_ENDSEL: data->endselx = xx; data->endsely = yy; break; case CURSORDRAG_SEL: ! data->selx = xx; ! data->sely = yy; break; case CURSORDRAG_NONE: break; --- 2873,2954 ---- } static void ! 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; 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; ! /* 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 = data->cx; ! data->sely = screen_hsize(data->backing) + data->cy - data->oy; break; case CURSORDRAG_NONE: break; *************** *** 3710,3715 **** --- 3818,3872 ---- } 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,3766 **** --- 3918,3971 ---- 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,3797 **** 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; window_copy_cursor_up(wme, 0); ! 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; } } --- 3982,4005 ---- 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; window_copy_cursor_up(wme, 0); ! 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; } } *************** *** 3913,3918 **** --- 4121,4127 ---- { struct window_pane *wp; struct window_mode_entry *wme; + struct window_copy_mode_data *data; u_int x, y; if (c == NULL) *************** *** 3933,3942 **** 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); window_copy_drag_update(c, m); } --- 4142,4168 ---- c->tty.mouse_drag_update = window_copy_drag_update; c->tty.mouse_drag_release = window_copy_drag_release; ! 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); }