There currently are a number of differences between tmux's jump commands [fFtT,;] in copy mode and the behavior of the corresponding keys in vi:
1. The repeat count is not reset after a jump with [fFtT], but it is reset after the `repeat movement' keys [;,]. For example, in tmux `3fa;;' jumps to the seventh (3+3+1) occurrence of `a' whereas vi would jump to the fifth (3+1+1). 2. The repeat count to [tT] has no effect: the cursor gets stuck in front of the first match and the count is then decremented to zero with no further movement. In vi, t and T are equivalent to f and F plus one step back in the direction of the original cursor position. 3. If the repeat count for [fF] exceeds the number of matching characters in the direction of the movement, the cursor is moved to the last match. vi wouldn't move the cursor at all. This patch does a number of things (the first two are cosmetic): a. Rename WINDOW_COPY_JUMPFORWARD to WINDOW_COPY_JUMP in order to match the corresponding MODEKEYCOPY_JUMP and window_copy_cursor_jump() more closely. Similarly for WINDOW_COPY_JUMPTOFORWARD. b. Replace a few if .. else if .. snakes by equivalent switch statements. c. Fix 1. by setting data->numprefix = -1 after a jump. d. Take care of the repeat count inside the jump{,_back} routines to be able to fix 3. without too much hassle. Use the same logic inside window_copy_cursor_jump() and window_copy_cursor_jump_back(). e. Eliminate essentially duplicated code by handling the jump_to{,_back} movements by combining jump{,_back} with cursor_{left,right}. To do so, add a return value to jump{,_back} indicating success (cursor movement). This fixes 2. In case the current behavior in 3. should be desired in emacs mode, I have an additional diff ready for that. Index: window-copy.c =================================================================== RCS file: /cvs/src/usr.bin/tmux/window-copy.c,v retrieving revision 1.120 diff -u -p -r1.120 window-copy.c --- window-copy.c 9 Nov 2014 15:13:01 -0000 1.120 +++ window-copy.c 10 Nov 2014 11:14:42 -0000 @@ -73,8 +73,8 @@ void window_copy_cursor_left(struct wind void window_copy_cursor_right(struct window_pane *); void window_copy_cursor_up(struct window_pane *, int); void window_copy_cursor_down(struct window_pane *, int); -void window_copy_cursor_jump(struct window_pane *); -void window_copy_cursor_jump_back(struct window_pane *); +int window_copy_cursor_jump(struct window_pane *); +int window_copy_cursor_jump_back(struct window_pane *); void window_copy_cursor_jump_to(struct window_pane *); void window_copy_cursor_jump_to_back(struct window_pane *); void window_copy_cursor_next_word(struct window_pane *, const char *); @@ -99,9 +99,9 @@ enum window_copy_input_type { WINDOW_COPY_NUMERICPREFIX, WINDOW_COPY_SEARCHUP, WINDOW_COPY_SEARCHDOWN, - WINDOW_COPY_JUMPFORWARD, + WINDOW_COPY_JUMP, WINDOW_COPY_JUMPBACK, - WINDOW_COPY_JUMPTOFORWARD, + WINDOW_COPY_JUMPTO, WINDOW_COPY_JUMPTOBACK, WINDOW_COPY_GOTOLINE, }; @@ -375,46 +375,52 @@ window_copy_key(struct window_pane *wp, enum mode_key_cmd cmd; const char *arg, *ss; - np = data->numprefix; - if (np <= 0) - np = 1; - - if (data->inputtype == WINDOW_COPY_JUMPFORWARD || - data->inputtype == WINDOW_COPY_JUMPBACK || - data->inputtype == WINDOW_COPY_JUMPTOFORWARD || - data->inputtype == WINDOW_COPY_JUMPTOBACK) { + switch (data->inputtype) { + case WINDOW_COPY_OFF: + break; + case WINDOW_COPY_JUMP: + case WINDOW_COPY_JUMPBACK: + case WINDOW_COPY_JUMPTO: + case WINDOW_COPY_JUMPTOBACK: /* Ignore keys with modifiers. */ if ((key & KEYC_MASK_MOD) == 0) { data->jumpchar = key; - if (data->inputtype == WINDOW_COPY_JUMPFORWARD) { - for (; np != 0; np--) - window_copy_cursor_jump(wp); - } else if (data->inputtype == WINDOW_COPY_JUMPBACK) { - for (; np != 0; np--) - window_copy_cursor_jump_back(wp); - } else if (data->inputtype == WINDOW_COPY_JUMPTOFORWARD) { - for (; np != 0; np--) - window_copy_cursor_jump_to(wp); - } else if (data->inputtype == WINDOW_COPY_JUMPTOBACK) { - for (; np != 0; np--) - window_copy_cursor_jump_to_back(wp); + switch (data->inputtype) { + case WINDOW_COPY_JUMP: + (void)window_copy_cursor_jump(wp); + break; + case WINDOW_COPY_JUMPBACK: + (void)window_copy_cursor_jump_back(wp); + break; + case WINDOW_COPY_JUMPTO: + window_copy_cursor_jump_to(wp); + break; + case WINDOW_COPY_JUMPTOBACK: + window_copy_cursor_jump_to_back(wp); + break; } } data->jumptype = data->inputtype; data->inputtype = WINDOW_COPY_OFF; + data->numprefix = -1; window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); return; - } else if (data->inputtype == WINDOW_COPY_NUMERICPREFIX) { + case WINDOW_COPY_NUMERICPREFIX: if (window_copy_key_numeric_prefix(wp, key) == 0) return; data->inputtype = WINDOW_COPY_OFF; window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); - } else if (data->inputtype != WINDOW_COPY_OFF) { + break; + default: if (window_copy_key_input(wp, key) != 0) goto input_off; return; } + np = data->numprefix; + if (np <= 0) + np = 1; + cmd = mode_key_lookup(&data->mdata, key, &arg); switch (cmd) { case MODEKEYCOPY_APPENDSELECTION: @@ -611,42 +617,44 @@ window_copy_key(struct window_pane *wp, for (; np != 0; np--) window_copy_cursor_previous_word(wp, word_separators); break; - case MODEKEYCOPY_JUMP: - data->inputtype = WINDOW_COPY_JUMPFORWARD; - data->inputprompt = "Jump Forward"; - *data->inputstr = '\0'; - window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); - return; /* skip numprefix reset */ case MODEKEYCOPY_JUMPAGAIN: - if (data->jumptype == WINDOW_COPY_JUMPFORWARD) { - for (; np != 0; np--) - window_copy_cursor_jump(wp); - } else if (data->jumptype == WINDOW_COPY_JUMPBACK) { - for (; np != 0; np--) - window_copy_cursor_jump_back(wp); - } else if (data->jumptype == WINDOW_COPY_JUMPTOFORWARD) { - for (; np != 0; np--) - window_copy_cursor_jump_to(wp); - } else if (data->jumptype == WINDOW_COPY_JUMPTOBACK) { - for (; np != 0; np--) - window_copy_cursor_jump_to_back(wp); + switch (data->jumptype) { + case WINDOW_COPY_JUMP: + (void)window_copy_cursor_jump(wp); + break; + case WINDOW_COPY_JUMPBACK: + (void)window_copy_cursor_jump_back(wp); + break; + case WINDOW_COPY_JUMPTO: + window_copy_cursor_jump_to(wp); + break; + case WINDOW_COPY_JUMPTOBACK: + window_copy_cursor_jump_to_back(wp); + break; } break; case MODEKEYCOPY_JUMPREVERSE: - if (data->jumptype == WINDOW_COPY_JUMPFORWARD) { - for (; np != 0; np--) - window_copy_cursor_jump_back(wp); - } else if (data->jumptype == WINDOW_COPY_JUMPBACK) { - for (; np != 0; np--) - window_copy_cursor_jump(wp); - } else if (data->jumptype == WINDOW_COPY_JUMPTOFORWARD) { - for (; np != 0; np--) - window_copy_cursor_jump_to_back(wp); - } else if (data->jumptype == WINDOW_COPY_JUMPTOBACK) { - for (; np != 0; np--) - window_copy_cursor_jump_to(wp); + switch (data->jumptype) { + case WINDOW_COPY_JUMP: + (void)window_copy_cursor_jump_back(wp); + break; + case WINDOW_COPY_JUMPBACK: + (void)window_copy_cursor_jump(wp); + break; + case WINDOW_COPY_JUMPTO: + window_copy_cursor_jump_to_back(wp); + break; + case WINDOW_COPY_JUMPTOBACK: + window_copy_cursor_jump_to(wp); + break; } break; + case MODEKEYCOPY_JUMP: + data->inputtype = WINDOW_COPY_JUMP; + data->inputprompt = "Jump Forward"; + *data->inputstr = '\0'; + window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); + return; /* skip numprefix reset */ case MODEKEYCOPY_JUMPBACK: data->inputtype = WINDOW_COPY_JUMPBACK; data->inputprompt = "Jump Back"; @@ -654,7 +662,7 @@ window_copy_key(struct window_pane *wp, window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); return; /* skip numprefix reset */ case MODEKEYCOPY_JUMPTO: - data->inputtype = WINDOW_COPY_JUMPTOFORWARD; + data->inputtype = WINDOW_COPY_JUMPTO; data->inputprompt = "Jump To"; *data->inputstr = '\0'; window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1); @@ -678,9 +686,9 @@ window_copy_key(struct window_pane *wp, switch (data->searchtype) { case WINDOW_COPY_OFF: case WINDOW_COPY_GOTOLINE: - case WINDOW_COPY_JUMPFORWARD: + case WINDOW_COPY_JUMP: case WINDOW_COPY_JUMPBACK: - case WINDOW_COPY_JUMPTOFORWARD: + case WINDOW_COPY_JUMPTO: case WINDOW_COPY_JUMPTOBACK: case WINDOW_COPY_NAMEDBUFFER: case WINDOW_COPY_NUMERICPREFIX: @@ -803,9 +811,9 @@ window_copy_key_input(struct window_pane switch (data->inputtype) { case WINDOW_COPY_OFF: - case WINDOW_COPY_JUMPFORWARD: + case WINDOW_COPY_JUMP: case WINDOW_COPY_JUMPBACK: - case WINDOW_COPY_JUMPTOFORWARD: + case WINDOW_COPY_JUMPTO: case WINDOW_COPY_JUMPTOBACK: case WINDOW_COPY_NUMERICPREFIX: break; @@ -1915,7 +1923,7 @@ window_copy_cursor_down(struct window_pa window_copy_cursor_start_of_line(wp); } -void +int window_copy_cursor_jump(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; @@ -1923,26 +1931,35 @@ window_copy_cursor_jump(struct window_pa const struct grid_cell *gc; struct utf8_data ud; u_int px, py, xx; + int np; + + if ((np = data->numprefix) <= 0) + np = 1; - px = data->cx + 1; + px = data->cx; py = screen_hsize(back_s) + data->cy - data->oy; xx = window_copy_find_length(wp, py); - while (px < xx) { + while (px < xx - 1 && np > 0) { + px++; gc = grid_peek_cell(back_s->grid, px, py); grid_cell_get(gc, &ud); if (!(gc->flags & GRID_FLAG_PADDING) && - ud.size == 1 && *ud.data == data->jumpchar) { - window_copy_update_cursor(wp, px, data->cy); - if (window_copy_update_selection(wp, 1)) - window_copy_redraw_lines(wp, data->cy, 1); - return; - } - px++; + ud.size == 1 && *ud.data == data->jumpchar) + np--; } + + if (np == 0) { + window_copy_update_cursor(wp, px, data->cy); + if (window_copy_update_selection(wp, 1)) + window_copy_redraw_lines(wp, data->cy, 1); + return (0); + } + + return (1); } -void +int window_copy_cursor_jump_back(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; @@ -1950,85 +1967,45 @@ window_copy_cursor_jump_back(struct wind const struct grid_cell *gc; struct utf8_data ud; u_int px, py; + int np; + + if ((np = data->numprefix) <= 0) + np = 1; px = data->cx; py = screen_hsize(back_s) + data->cy - data->oy; - if (px > 0) + while (px > 0 && np > 0) { px--; - - for (;;) { gc = grid_peek_cell(back_s->grid, px, py); grid_cell_get(gc, &ud); if (!(gc->flags & GRID_FLAG_PADDING) && - ud.size == 1 && *ud.data == data->jumpchar) { - window_copy_update_cursor(wp, px, data->cy); - if (window_copy_update_selection(wp, 1)) - window_copy_redraw_lines(wp, data->cy, 1); - return; - } - if (px == 0) - break; - px--; + ud.size == 1 && *ud.data == data->jumpchar) + np--; + } + + if (np == 0) { + window_copy_update_cursor(wp, px, data->cy); + if (window_copy_update_selection(wp, 1)) + window_copy_redraw_lines(wp, data->cy, 1); + return (0); } + + return (1); } void window_copy_cursor_jump_to(struct window_pane *wp) { - struct window_copy_mode_data *data = wp->modedata; - struct screen *back_s = data->backing; - const struct grid_cell *gc; - struct utf8_data ud; - u_int px, py, xx; - - px = data->cx + 1; - py = screen_hsize(back_s) + data->cy - data->oy; - xx = window_copy_find_length(wp, py); - - while (px < xx) { - gc = grid_peek_cell(back_s->grid, px, py); - grid_cell_get(gc, &ud); - if (!(gc->flags & GRID_FLAG_PADDING) && - ud.size == 1 && *ud.data == data->jumpchar) { - window_copy_update_cursor(wp, px - 1, data->cy); - if (window_copy_update_selection(wp, 1)) - window_copy_redraw_lines(wp, data->cy, 1); - return; - } - px++; - } + if (window_copy_cursor_jump(wp) == 0) /* cursor moved to the right */ + window_copy_cursor_left(wp); /* one step back */ } void window_copy_cursor_jump_to_back(struct window_pane *wp) { - struct window_copy_mode_data *data = wp->modedata; - struct screen *back_s = data->backing; - const struct grid_cell *gc; - struct utf8_data ud; - u_int px, py; - - px = data->cx; - py = screen_hsize(back_s) + data->cy - data->oy; - - if (px > 0) - px--; - - for (;;) { - gc = grid_peek_cell(back_s->grid, px, py); - grid_cell_get(gc, &ud); - if (!(gc->flags & GRID_FLAG_PADDING) && - ud.size == 1 && *ud.data == data->jumpchar) { - window_copy_update_cursor(wp, px + 1, data->cy); - if (window_copy_update_selection(wp, 1)) - window_copy_redraw_lines(wp, data->cy, 1); - return; - } - if (px == 0) - break; - px--; - } + if (window_copy_cursor_jump_back(wp) == 0) /* cursor moved left */ + window_copy_cursor_right(wp); /* one step forward */ } void