Permit keys in copy mode to be prefixed by a repeat count, entered with

[1-9] in vi mode, or M-[1-9] in emacs mode.

From Micah Cowan, tweaked a little by me.
This commit is contained in:
Nicholas Marriott 2010-03-02 00:32:41 +00:00
parent 56a33b157b
commit 7bc3f5dd8a
4 changed files with 167 additions and 46 deletions

View File

@ -106,6 +106,7 @@ struct mode_key_cmdstr mode_key_cmdstr_copy[] = {
{ MODEKEYCOPY_SEARCHDOWN, "search-forward" }, { MODEKEYCOPY_SEARCHDOWN, "search-forward" },
{ MODEKEYCOPY_SEARCHREVERSE, "search-reverse" }, { MODEKEYCOPY_SEARCHREVERSE, "search-reverse" },
{ MODEKEYCOPY_SEARCHUP, "search-backward" }, { MODEKEYCOPY_SEARCHUP, "search-backward" },
{ MODEKEYCOPY_STARTNUMBERPREFIX, "start-number-prefix" },
{ MODEKEYCOPY_STARTOFLINE, "start-of-line" }, { MODEKEYCOPY_STARTOFLINE, "start-of-line" },
{ MODEKEYCOPY_STARTSELECTION, "begin-selection" }, { MODEKEYCOPY_STARTSELECTION, "begin-selection" },
{ MODEKEYCOPY_TOPLINE, "top-line" }, { MODEKEYCOPY_TOPLINE, "top-line" },
@ -178,6 +179,15 @@ const struct mode_key_entry mode_key_vi_copy[] = {
{ '$', 0, MODEKEYCOPY_ENDOFLINE }, { '$', 0, MODEKEYCOPY_ENDOFLINE },
{ '/', 0, MODEKEYCOPY_SEARCHDOWN }, { '/', 0, MODEKEYCOPY_SEARCHDOWN },
{ '0', 0, MODEKEYCOPY_STARTOFLINE }, { '0', 0, MODEKEYCOPY_STARTOFLINE },
{ '1', 0, MODEKEYCOPY_STARTNUMBERPREFIX },
{ '2', 0, MODEKEYCOPY_STARTNUMBERPREFIX },
{ '3', 0, MODEKEYCOPY_STARTNUMBERPREFIX },
{ '4', 0, MODEKEYCOPY_STARTNUMBERPREFIX },
{ '5', 0, MODEKEYCOPY_STARTNUMBERPREFIX },
{ '6', 0, MODEKEYCOPY_STARTNUMBERPREFIX },
{ '7', 0, MODEKEYCOPY_STARTNUMBERPREFIX },
{ '8', 0, MODEKEYCOPY_STARTNUMBERPREFIX },
{ '9', 0, MODEKEYCOPY_STARTNUMBERPREFIX },
{ ':', 0, MODEKEYCOPY_GOTOLINE }, { ':', 0, MODEKEYCOPY_GOTOLINE },
{ '?', 0, MODEKEYCOPY_SEARCHUP }, { '?', 0, MODEKEYCOPY_SEARCHUP },
{ 'B', 0, MODEKEYCOPY_PREVIOUSSPACE }, { 'B', 0, MODEKEYCOPY_PREVIOUSSPACE },
@ -280,6 +290,15 @@ struct mode_key_tree mode_key_tree_emacs_choice;
/* emacs copy mode keys. */ /* emacs copy mode keys. */
const struct mode_key_entry mode_key_emacs_copy[] = { const struct mode_key_entry mode_key_emacs_copy[] = {
{ ' ', 0, MODEKEYCOPY_NEXTPAGE }, { ' ', 0, MODEKEYCOPY_NEXTPAGE },
{ '1' | KEYC_ESCAPE, 0, MODEKEYCOPY_STARTNUMBERPREFIX },
{ '2' | KEYC_ESCAPE, 0, MODEKEYCOPY_STARTNUMBERPREFIX },
{ '3' | KEYC_ESCAPE, 0, MODEKEYCOPY_STARTNUMBERPREFIX },
{ '4' | KEYC_ESCAPE, 0, MODEKEYCOPY_STARTNUMBERPREFIX },
{ '5' | KEYC_ESCAPE, 0, MODEKEYCOPY_STARTNUMBERPREFIX },
{ '6' | KEYC_ESCAPE, 0, MODEKEYCOPY_STARTNUMBERPREFIX },
{ '7' | KEYC_ESCAPE, 0, MODEKEYCOPY_STARTNUMBERPREFIX },
{ '8' | KEYC_ESCAPE, 0, MODEKEYCOPY_STARTNUMBERPREFIX },
{ '9' | KEYC_ESCAPE, 0, MODEKEYCOPY_STARTNUMBERPREFIX },
{ '<' | KEYC_ESCAPE, 0, MODEKEYCOPY_HISTORYTOP }, { '<' | KEYC_ESCAPE, 0, MODEKEYCOPY_HISTORYTOP },
{ '>' | KEYC_ESCAPE, 0, MODEKEYCOPY_HISTORYBOTTOM }, { '>' | KEYC_ESCAPE, 0, MODEKEYCOPY_HISTORYBOTTOM },
{ 'R' | KEYC_ESCAPE, 0, MODEKEYCOPY_TOPLINE }, { 'R' | KEYC_ESCAPE, 0, MODEKEYCOPY_TOPLINE },

11
tmux.1
View File

@ -661,7 +661,16 @@ 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 three next and previous space keys work similarly but use a space alone as
the word separator. the word separator.
.Pp .Pp
These key bindings are defined in a set of named tables: Commands in copy mode may be prefaced by an optional repeat count.
With vi key bindings, a prefix is entered using the number keys; with
emacs, the Alt (meta) key and a number begins prefix entry.
For example, to move the cursor forward by ten words, use
.Ql M-1 0 M-f
in emacs mode, and
.Ql 10w
in vi.
.Pp
Mode key bindings are defined in a set of named tables:
.Em vi-edit .Em vi-edit
and and
.Em emacs-edit .Em emacs-edit

1
tmux.h
View File

@ -477,6 +477,7 @@ enum mode_key_cmd {
MODEKEYCOPY_SEARCHDOWN, MODEKEYCOPY_SEARCHDOWN,
MODEKEYCOPY_SEARCHREVERSE, MODEKEYCOPY_SEARCHREVERSE,
MODEKEYCOPY_SEARCHUP, MODEKEYCOPY_SEARCHUP,
MODEKEYCOPY_STARTNUMBERPREFIX,
MODEKEYCOPY_STARTOFLINE, MODEKEYCOPY_STARTOFLINE,
MODEKEYCOPY_STARTSELECTION, MODEKEYCOPY_STARTSELECTION,
MODEKEYCOPY_TOPLINE, MODEKEYCOPY_TOPLINE,

View File

@ -28,6 +28,7 @@ void window_copy_free(struct window_pane *);
void window_copy_resize(struct window_pane *, u_int, u_int); void window_copy_resize(struct window_pane *, u_int, u_int);
void window_copy_key(struct window_pane *, struct client *, int); void window_copy_key(struct window_pane *, struct client *, int);
int window_copy_key_input(struct window_pane *, int); int window_copy_key_input(struct window_pane *, int);
int window_copy_key_numeric_prefix(struct window_pane *, int);
void window_copy_mouse( void window_copy_mouse(
struct window_pane *, struct client *, struct mouse_event *); struct window_pane *, struct client *, struct mouse_event *);
@ -82,6 +83,7 @@ const struct window_mode window_copy_mode = {
enum window_copy_input_type { enum window_copy_input_type {
WINDOW_COPY_OFF, WINDOW_COPY_OFF,
WINDOW_COPY_NUMERICPREFIX,
WINDOW_COPY_SEARCHUP, WINDOW_COPY_SEARCHUP,
WINDOW_COPY_SEARCHDOWN, WINDOW_COPY_SEARCHDOWN,
WINDOW_COPY_GOTOLINE, WINDOW_COPY_GOTOLINE,
@ -109,6 +111,8 @@ struct window_copy_mode_data {
const char *inputprompt; const char *inputprompt;
char *inputstr; char *inputstr;
u_int numprefix;
enum window_copy_input_type searchtype; enum window_copy_input_type searchtype;
char *searchstr; char *searchstr;
}; };
@ -135,6 +139,7 @@ window_copy_init(struct window_pane *wp)
data->inputtype = WINDOW_COPY_OFF; data->inputtype = WINDOW_COPY_OFF;
data->inputprompt = NULL; data->inputprompt = NULL;
data->inputstr = xstrdup(""); data->inputstr = xstrdup("");
data->numprefix = 0;
data->searchtype = WINDOW_COPY_OFF; data->searchtype = WINDOW_COPY_OFF;
data->searchstr = NULL; data->searchstr = NULL;
@ -229,11 +234,20 @@ window_copy_key(struct window_pane *wp, struct client *c, int key)
const char *word_separators; const char *word_separators;
struct window_copy_mode_data *data = wp->modedata; struct window_copy_mode_data *data = wp->modedata;
struct screen *s = &data->screen; struct screen *s = &data->screen;
u_int n; u_int n, np;
int keys; int keys;
enum mode_key_cmd cmd; enum mode_key_cmd cmd;
if (data->inputtype != WINDOW_COPY_OFF) { np = data->numprefix;
if (np == 0)
np = 1;
if (data->inputtype == 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) {
if (window_copy_key_input(wp, key) != 0) if (window_copy_key_input(wp, key) != 0)
goto input_off; goto input_off;
return; return;
@ -242,55 +256,69 @@ window_copy_key(struct window_pane *wp, struct client *c, int key)
cmd = mode_key_lookup(&data->mdata, key); cmd = mode_key_lookup(&data->mdata, key);
switch (cmd) { switch (cmd) {
case MODEKEYCOPY_CANCEL: case MODEKEYCOPY_CANCEL:
window_pane_reset_mode(wp); for (; np != 0; np--)
window_pane_reset_mode(wp);
break; break;
case MODEKEYCOPY_LEFT: case MODEKEYCOPY_LEFT:
window_copy_cursor_left(wp); for (; np != 0; np--)
return; window_copy_cursor_left(wp);
break;
case MODEKEYCOPY_RIGHT: case MODEKEYCOPY_RIGHT:
window_copy_cursor_right(wp); for (; np != 0; np--)
return; window_copy_cursor_right(wp);
break;
case MODEKEYCOPY_UP: case MODEKEYCOPY_UP:
window_copy_cursor_up(wp, 0); for (; np != 0; np--)
return; window_copy_cursor_up(wp, 0);
break;
case MODEKEYCOPY_DOWN: case MODEKEYCOPY_DOWN:
window_copy_cursor_down(wp, 0); for (; np != 0; np--)
return; window_copy_cursor_down(wp, 0);
break;
case MODEKEYCOPY_SCROLLUP: case MODEKEYCOPY_SCROLLUP:
window_copy_cursor_up(wp, 1); for (; np != 0; np--)
window_copy_cursor_up(wp, 1);
break; break;
case MODEKEYCOPY_SCROLLDOWN: case MODEKEYCOPY_SCROLLDOWN:
window_copy_cursor_down(wp, 1); for (; np != 0; np--)
window_copy_cursor_down(wp, 1);
break; break;
case MODEKEYCOPY_PREVIOUSPAGE: case MODEKEYCOPY_PREVIOUSPAGE:
window_copy_pageup(wp); for (; np != 0; np--)
window_copy_pageup(wp);
break; break;
case MODEKEYCOPY_NEXTPAGE: case MODEKEYCOPY_NEXTPAGE:
n = 1; n = 1;
if (screen_size_y(s) > 2) if (screen_size_y(s) > 2)
n = screen_size_y(s) - 2; n = screen_size_y(s) - 2;
if (data->oy < n) for (; np != 0; np--) {
data->oy = 0; if (data->oy < n)
else data->oy = 0;
data->oy -= n; else
data->oy -= n;
}
window_copy_update_selection(wp); window_copy_update_selection(wp);
window_copy_redraw_screen(wp); window_copy_redraw_screen(wp);
break; break;
case MODEKEYCOPY_HALFPAGEUP: case MODEKEYCOPY_HALFPAGEUP:
n = screen_size_y(s) / 2; n = screen_size_y(s) / 2;
if (data->oy + n > screen_hsize(&wp->base)) for (; np != 0; np--) {
data->oy = screen_hsize(&wp->base); if (data->oy + n > screen_hsize(&wp->base))
else data->oy = screen_hsize(&wp->base);
data->oy += n; else
data->oy += n;
}
window_copy_update_selection(wp); window_copy_update_selection(wp);
window_copy_redraw_screen(wp); window_copy_redraw_screen(wp);
break; break;
case MODEKEYCOPY_HALFPAGEDOWN: case MODEKEYCOPY_HALFPAGEDOWN:
n = screen_size_y(s) / 2; n = screen_size_y(s) / 2;
if (data->oy < n) for (; np != 0; np--) {
data->oy = 0; if (data->oy < n)
else data->oy = 0;
data->oy -= n; else
data->oy -= n;
}
window_copy_update_selection(wp); window_copy_update_selection(wp);
window_copy_redraw_screen(wp); window_copy_redraw_screen(wp);
break; break;
@ -350,28 +378,34 @@ window_copy_key(struct window_pane *wp, struct client *c, int key)
window_copy_cursor_end_of_line(wp); window_copy_cursor_end_of_line(wp);
break; break;
case MODEKEYCOPY_NEXTSPACE: case MODEKEYCOPY_NEXTSPACE:
window_copy_cursor_next_word(wp, " "); for (; np != 0; np--)
window_copy_cursor_next_word(wp, " ");
break; break;
case MODEKEYCOPY_NEXTSPACEEND: case MODEKEYCOPY_NEXTSPACEEND:
window_copy_cursor_next_word_end(wp, " "); for (; np != 0; np--)
window_copy_cursor_next_word_end(wp, " ");
break; break;
case MODEKEYCOPY_NEXTWORD: case MODEKEYCOPY_NEXTWORD:
word_separators = word_separators =
options_get_string(&wp->window->options, "word-separators"); options_get_string(&wp->window->options, "word-separators");
window_copy_cursor_next_word(wp, word_separators); for (; np != 0; np--)
window_copy_cursor_next_word(wp, word_separators);
break; break;
case MODEKEYCOPY_NEXTWORDEND: case MODEKEYCOPY_NEXTWORDEND:
word_separators = word_separators =
options_get_string(&wp->window->options, "word-separators"); options_get_string(&wp->window->options, "word-separators");
window_copy_cursor_next_word_end(wp, word_separators); for (; np != 0; np--)
window_copy_cursor_next_word_end(wp, word_separators);
break; break;
case MODEKEYCOPY_PREVIOUSSPACE: case MODEKEYCOPY_PREVIOUSSPACE:
window_copy_cursor_previous_word(wp, " "); for (; np != 0; np--)
window_copy_cursor_previous_word(wp, " ");
break; break;
case MODEKEYCOPY_PREVIOUSWORD: case MODEKEYCOPY_PREVIOUSWORD:
word_separators = word_separators =
options_get_string(&wp->window->options, "word-separators"); options_get_string(&wp->window->options, "word-separators");
window_copy_cursor_previous_word(wp, word_separators); for (; np != 0; np--)
window_copy_cursor_previous_word(wp, word_separators);
break; break;
case MODEKEYCOPY_SEARCHUP: case MODEKEYCOPY_SEARCHUP:
data->inputtype = WINDOW_COPY_SEARCHUP; data->inputtype = WINDOW_COPY_SEARCHUP;
@ -386,18 +420,33 @@ window_copy_key(struct window_pane *wp, struct client *c, int key)
switch (data->searchtype) { switch (data->searchtype) {
case WINDOW_COPY_OFF: case WINDOW_COPY_OFF:
case WINDOW_COPY_GOTOLINE: case WINDOW_COPY_GOTOLINE:
case WINDOW_COPY_NUMERICPREFIX:
break; break;
case WINDOW_COPY_SEARCHUP: case WINDOW_COPY_SEARCHUP:
if (cmd == MODEKEYCOPY_SEARCHAGAIN) if (cmd == MODEKEYCOPY_SEARCHAGAIN) {
window_copy_search_up(wp, data->searchstr); for (; np != 0; np--) {
else window_copy_search_up(
window_copy_search_down(wp, data->searchstr); wp, data->searchstr);
}
} else {
for (; np != 0; np--) {
window_copy_search_down(
wp, data->searchstr);
}
}
break; break;
case WINDOW_COPY_SEARCHDOWN: case WINDOW_COPY_SEARCHDOWN:
if (cmd == MODEKEYCOPY_SEARCHAGAIN) if (cmd == MODEKEYCOPY_SEARCHAGAIN) {
window_copy_search_down(wp, data->searchstr); for (; np != 0; np--) {
else window_copy_search_down(
window_copy_search_up(wp, data->searchstr); wp, data->searchstr);
}
} else {
for (; np != 0; np--) {
window_copy_search_up(
wp, data->searchstr);
}
}
break; break;
} }
break; break;
@ -406,13 +455,23 @@ window_copy_key(struct window_pane *wp, struct client *c, int key)
data->inputprompt = "Goto Line"; data->inputprompt = "Goto Line";
*data->inputstr = '\0'; *data->inputstr = '\0';
goto input_on; goto input_on;
case MODEKEYCOPY_STARTNUMBERPREFIX:
key &= 0xff;
if (key >= '0' && key <= '9') {
data->inputtype = WINDOW_COPY_NUMERICPREFIX;
data->numprefix = 0;
window_copy_key_numeric_prefix(wp, key);
return;
}
break;
case MODEKEYCOPY_RECTANGLETOGGLE: case MODEKEYCOPY_RECTANGLETOGGLE:
window_copy_rectangle_toggle(wp); window_copy_rectangle_toggle(wp);
return; break;
default: default:
break; break;
} }
data->numprefix = 0;
return; return;
input_on: input_on:
@ -444,9 +503,11 @@ window_copy_key_input(struct window_pane *wp, int key)
struct window_copy_mode_data *data = wp->modedata; struct window_copy_mode_data *data = wp->modedata;
struct screen *s = &data->screen; struct screen *s = &data->screen;
size_t inputlen; size_t inputlen;
u_int np;
switch (mode_key_lookup(&data->mdata, key)) { switch (mode_key_lookup(&data->mdata, key)) {
case MODEKEYEDIT_CANCEL: case MODEKEYEDIT_CANCEL:
data->numprefix = 0;
return (-1); return (-1);
case MODEKEYEDIT_BACKSPACE: case MODEKEYEDIT_BACKSPACE:
inputlen = strlen(data->inputstr); inputlen = strlen(data->inputstr);
@ -457,16 +518,23 @@ window_copy_key_input(struct window_pane *wp, int key)
*data->inputstr = '\0'; *data->inputstr = '\0';
break; break;
case MODEKEYEDIT_ENTER: case MODEKEYEDIT_ENTER:
np = data->numprefix;
if (np == 0)
np = 1;
switch (data->inputtype) { switch (data->inputtype) {
case WINDOW_COPY_OFF: case WINDOW_COPY_OFF:
case WINDOW_COPY_NUMERICPREFIX:
break; break;
case WINDOW_COPY_SEARCHUP: case WINDOW_COPY_SEARCHUP:
window_copy_search_up(wp, data->inputstr); for (; np != 0; np--)
window_copy_search_up(wp, data->inputstr);
data->searchtype = data->inputtype; data->searchtype = data->inputtype;
data->searchstr = xstrdup(data->inputstr); data->searchstr = xstrdup(data->inputstr);
break; break;
case WINDOW_COPY_SEARCHDOWN: case WINDOW_COPY_SEARCHDOWN:
window_copy_search_down(wp, data->inputstr); for (; np != 0; np--)
window_copy_search_down(wp, data->inputstr);
data->searchtype = data->inputtype; data->searchtype = data->inputtype;
data->searchstr = xstrdup(data->inputstr); data->searchstr = xstrdup(data->inputstr);
break; break;
@ -475,6 +543,7 @@ window_copy_key_input(struct window_pane *wp, int key)
*data->inputstr = '\0'; *data->inputstr = '\0';
break; break;
} }
data->numprefix = 0;
return (1); return (1);
case MODEKEY_OTHER: case MODEKEY_OTHER:
if (key < 32 || key > 126) if (key < 32 || key > 126)
@ -493,6 +562,24 @@ window_copy_key_input(struct window_pane *wp, int key)
return (0); return (0);
} }
int
window_copy_key_numeric_prefix(struct window_pane *wp, int key)
{
struct window_copy_mode_data *data = wp->modedata;
struct screen *s = &data->screen;
key &= 0xff;
if (key < '0' || key > '9')
return 1;
if (data->numprefix >= 100) /* no more than three digits */
return 0;
data->numprefix = data->numprefix * 10 + key - '0';
window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
return 0;
}
/* ARGSUSED */ /* ARGSUSED */
void void
window_copy_mouse( window_copy_mouse(
@ -762,8 +849,13 @@ window_copy_write_line(
screen_write_cursormove(ctx, screen_size_x(s) - size, 0); screen_write_cursormove(ctx, screen_size_x(s) - size, 0);
screen_write_puts(ctx, &gc, "%s", hdr); screen_write_puts(ctx, &gc, "%s", hdr);
} else if (py == last && data->inputtype != WINDOW_COPY_OFF) { } else if (py == last && data->inputtype != WINDOW_COPY_OFF) {
xoff = size = xsnprintf(hdr, sizeof hdr, if (data->inputtype == WINDOW_COPY_NUMERICPREFIX) {
"%s: %s", data->inputprompt, data->inputstr); xoff = size = xsnprintf(hdr, sizeof hdr,
"Repeat: %u", data->numprefix);
} else {
xoff = size = xsnprintf(hdr, sizeof hdr,
"%s: %s", data->inputprompt, data->inputstr);
}
screen_write_cursormove(ctx, 0, last); screen_write_cursormove(ctx, 0, last);
screen_write_puts(ctx, &gc, "%s", hdr); screen_write_puts(ctx, &gc, "%s", hdr);
} else } else