This patch introduces support for the quick "jump-to-char" commands from
vi, for tmux's copy mode. They are bound to the same keys in both vi
mode and emacs. This patch expects to be applied after the other two
patches I recently rebased (cmd-prefixes and word-separators).

The new bindings let you do:
        f <char>                jump forward to the next <char>
        t <char>                jump to just before the next <char>
        F <char>                jump back to the previous <char>
        T <char>                jump back to just after the prev <char>
        ;                       repeat the last jump command
        ,                       repeat the last jump command,
                                but reverse the direction

This makes it extremely convenient to jump between, say, path
components, or colon-separated PATH environment variables. It's also
handy just for normal navigation, as just looking at the character where
you want to be, typing f or F <key> and then a few ";", and you can get
there more quickly than you would have by other movement commands.

-- 
Micah J. Cowan
http://micah.cowan.name/
Index: mode-key.c
===================================================================
--- mode-key.c.orig
+++ mode-key.c
@@ -88,6 +88,12 @@ struct mode_key_cmdstr mode_key_cmdstr_c
 	{ MODEKEYCOPY_GOTOLINE, "goto-line" },
 	{ MODEKEYCOPY_HISTORYBOTTOM, "history-bottom" },
 	{ MODEKEYCOPY_HISTORYTOP, "history-top" },
+	{ MODEKEYCOPY_JUMPTOBEFORECHARBACK, "jump-to-before-char-back" },
+	{ MODEKEYCOPY_JUMPTOBEFORECHAR, "jump-to-before-char" },
+	{ MODEKEYCOPY_JUMPTOCHARAGAIN, "jump-to-char-again" },
+	{ MODEKEYCOPY_JUMPTOCHARAGAINREVERSE, "jump-to-char-again-reverse" },
+	{ MODEKEYCOPY_JUMPTOCHARBACK, "jump-to-char-back" },
+	{ MODEKEYCOPY_JUMPTOCHAR, "jump-to-char" },
 	{ MODEKEYCOPY_LEFT, "cursor-left" },
 	{ MODEKEYCOPY_RECTANGLETOGGLE, "rectangle-toggle" },
 	{ MODEKEYCOPY_MIDDLELINE, "middle-line" },
@@ -177,6 +183,8 @@ struct mode_key_tree mode_key_tree_vi_ch
 const struct mode_key_entry mode_key_vi_copy[] = {
 	{ ' ',			0, MODEKEYCOPY_STARTSELECTION },
 	{ '$',			0, MODEKEYCOPY_ENDOFLINE },
+	{ ',',			0, MODEKEYCOPY_JUMPTOCHARAGAINREVERSE },
+	{ ';',			0, MODEKEYCOPY_JUMPTOCHARAGAIN },
 	{ '/',			0, MODEKEYCOPY_SEARCHDOWN },
 	{ '0',			0, MODEKEYCOPY_STARTOFLINE },
 	{ '1',			0, MODEKEYCOPY_STARTNUMBERPREFIX },
@@ -192,6 +200,7 @@ const struct mode_key_entry mode_key_vi_
 	{ '?',			0, MODEKEYCOPY_SEARCHUP },
 	{ 'B',			0, MODEKEYCOPY_PREVIOUSSPACE },
 	{ 'E',			0, MODEKEYCOPY_NEXTSPACEEND },
+	{ 'F',			0, MODEKEYCOPY_JUMPTOCHARBACK },
 	{ 'G',			0, MODEKEYCOPY_HISTORYBOTTOM },
 	{ 'H',			0, MODEKEYCOPY_TOPLINE },
 	{ 'J',			0, MODEKEYCOPY_SCROLLDOWN },
@@ -199,6 +208,7 @@ const struct mode_key_entry mode_key_vi_
 	{ 'L',			0, MODEKEYCOPY_BOTTOMLINE },
 	{ 'M',			0, MODEKEYCOPY_MIDDLELINE },
 	{ 'N',			0, MODEKEYCOPY_SEARCHREVERSE },
+	{ 'T',			0, MODEKEYCOPY_JUMPTOBEFORECHARBACK },
 	{ 'W',			0, MODEKEYCOPY_NEXTSPACE },
 	{ '\002' /* C-b */,	0, MODEKEYCOPY_PREVIOUSPAGE },
 	{ '\003' /* C-c */,	0, MODEKEYCOPY_CANCEL },
@@ -213,6 +223,7 @@ const struct mode_key_entry mode_key_vi_
 	{ '^',			0, MODEKEYCOPY_BACKTOINDENTATION },
 	{ 'b',			0, MODEKEYCOPY_PREVIOUSWORD },
 	{ 'e',                  0, MODEKEYCOPY_NEXTWORDEND },
+	{ 'f',			0, MODEKEYCOPY_JUMPTOCHAR },
 	{ 'g',			0, MODEKEYCOPY_HISTORYTOP },
 	{ 'h',			0, MODEKEYCOPY_LEFT },
 	{ 'j',			0, MODEKEYCOPY_DOWN },
@@ -220,6 +231,7 @@ const struct mode_key_entry mode_key_vi_
 	{ 'l',			0, MODEKEYCOPY_RIGHT },
 	{ 'n',			0, MODEKEYCOPY_SEARCHAGAIN },
 	{ 'q',			0, MODEKEYCOPY_CANCEL },
+	{ 't',			0, MODEKEYCOPY_JUMPTOBEFORECHAR },
 	{ 'v',			0, MODEKEYCOPY_RECTANGLETOGGLE },
 	{ 'w',			0, MODEKEYCOPY_NEXTWORD },
 	{ KEYC_BSPACE,		0, MODEKEYCOPY_LEFT },
@@ -290,6 +302,8 @@ struct mode_key_tree mode_key_tree_emacs
 /* emacs copy mode keys. */
 const struct mode_key_entry mode_key_emacs_copy[] = {
 	{ ' ',			0, MODEKEYCOPY_NEXTPAGE },
+	{ ',',			0, MODEKEYCOPY_JUMPTOCHARAGAINREVERSE },
+	{ ';',			0, MODEKEYCOPY_JUMPTOCHARAGAIN },
 	{ '1' | KEYC_ESCAPE,	0, MODEKEYCOPY_STARTNUMBERPREFIX },
 	{ '2' | KEYC_ESCAPE,	0, MODEKEYCOPY_STARTNUMBERPREFIX },
 	{ '3' | KEYC_ESCAPE,	0, MODEKEYCOPY_STARTNUMBERPREFIX },
@@ -301,8 +315,11 @@ const struct mode_key_entry mode_key_ema
 	{ '9' | KEYC_ESCAPE,	0, MODEKEYCOPY_STARTNUMBERPREFIX },
 	{ '<' | KEYC_ESCAPE,    0, MODEKEYCOPY_HISTORYTOP },
 	{ '>' | KEYC_ESCAPE,    0, MODEKEYCOPY_HISTORYBOTTOM },
+	{ 'F',			0, MODEKEYCOPY_JUMPTOCHARBACK },
+	{ 'N',			0, MODEKEYCOPY_SEARCHREVERSE },
 	{ 'R' | KEYC_ESCAPE,	0, MODEKEYCOPY_TOPLINE },
 	{ 'R',			0, MODEKEYCOPY_RECTANGLETOGGLE },
+	{ 'T',			0, MODEKEYCOPY_JUMPTOBEFORECHARBACK },
 	{ '\000' /* C-Space */,	0, MODEKEYCOPY_STARTSELECTION },
 	{ '\001' /* C-a */,	0, MODEKEYCOPY_STARTOFLINE },
 	{ '\002' /* C-b */,	0, MODEKEYCOPY_LEFT },
@@ -319,12 +336,14 @@ const struct mode_key_entry mode_key_ema
 	{ '\033' /* Escape */,	0, MODEKEYCOPY_CANCEL },
 	{ 'N',			0, MODEKEYCOPY_SEARCHREVERSE },
 	{ 'b' | KEYC_ESCAPE,	0, MODEKEYCOPY_PREVIOUSWORD },
+	{ 'f',			0, MODEKEYCOPY_JUMPTOCHAR },
 	{ 'f' | KEYC_ESCAPE,	0, MODEKEYCOPY_NEXTWORDEND },
 	{ 'g',			0, MODEKEYCOPY_GOTOLINE },
 	{ 'm' | KEYC_ESCAPE,	0, MODEKEYCOPY_BACKTOINDENTATION },
 	{ 'n',			0, MODEKEYCOPY_SEARCHAGAIN },
 	{ 'q',			0, MODEKEYCOPY_CANCEL },
 	{ 'r' | KEYC_ESCAPE,	0, MODEKEYCOPY_MIDDLELINE },
+	{ 't',			0, MODEKEYCOPY_JUMPTOBEFORECHAR },
 	{ 'v' | KEYC_ESCAPE,	0, MODEKEYCOPY_PREVIOUSPAGE },
 	{ 'w' | KEYC_ESCAPE,	0, MODEKEYCOPY_COPYSELECTION },
 	{ KEYC_DOWN | KEYC_CTRL,0, MODEKEYCOPY_SCROLLDOWN },
Index: tmux.h
===================================================================
--- tmux.h.orig
+++ tmux.h
@@ -458,6 +458,12 @@ enum mode_key_cmd {
 	MODEKEYCOPY_HALFPAGEUP,
 	MODEKEYCOPY_HISTORYBOTTOM,
 	MODEKEYCOPY_HISTORYTOP,
+	MODEKEYCOPY_JUMPTOBEFORECHARBACK,
+	MODEKEYCOPY_JUMPTOBEFORECHAR,
+	MODEKEYCOPY_JUMPTOCHARAGAIN,
+	MODEKEYCOPY_JUMPTOCHARAGAINREVERSE,
+	MODEKEYCOPY_JUMPTOCHARBACK,
+	MODEKEYCOPY_JUMPTOCHAR,
 	MODEKEYCOPY_LEFT,
 	MODEKEYCOPY_MIDDLELINE,
 	MODEKEYCOPY_NEXTPAGE,
Index: window-copy.c
===================================================================
--- window-copy.c.orig
+++ window-copy.c
@@ -64,6 +64,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_char(struct window_pane *);
+void	window_copy_cursor_jump_char_back(struct window_pane *);
 void	window_copy_cursor_next_word(struct window_pane *, const char *);
 void	window_copy_cursor_next_word_end(struct window_pane *, const char *);
 void	window_copy_cursor_previous_word(struct window_pane *, const char *);
@@ -85,6 +87,7 @@ enum window_copy_input_type {
 	WINDOW_COPY_NUMERICPREFIX,
 	WINDOW_COPY_SEARCHUP,
 	WINDOW_COPY_SEARCHDOWN,
+	WINDOW_COPY_JUMPCHAR,
 	WINDOW_COPY_GOTOLINE,
 };
 
@@ -114,6 +117,10 @@ struct window_copy_mode_data {
 
 	enum window_copy_input_type searchtype;
 	char	       *searchstr;
+
+	int		charsearchforward;
+	int		stop_before;
+	char		searchchar;
 };
 
 struct screen *
@@ -143,6 +150,10 @@ window_copy_init(struct window_pane *wp)
 	data->searchtype = WINDOW_COPY_OFF;
 	data->searchstr = NULL;
 
+	data->charsearchforward = 1;
+	data->stop_before = 0;
+	data->searchchar = '\0';
+
 	s = &data->screen;
 	screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
 	if (options_get_number(&wp->window->options, "mode-mouse"))
@@ -235,7 +246,22 @@ window_copy_key(struct window_pane *wp, 
 	if (np == 0)
 		np = 1;
 
-	if (data->inputtype == WINDOW_COPY_NUMERICPREFIX) {
+	if (data->inputtype == WINDOW_COPY_JUMPCHAR) {
+		/* Ignore keys with modifiers. */
+		if ((key & 0xff00) == 0) {
+			data->searchchar = key;
+			if (data->charsearchforward) {
+				for ( ; np != 0; np--)
+					window_copy_cursor_jump_char(wp);
+			}  else {
+				for ( ; np != 0; np--)
+					window_copy_cursor_jump_char_back(wp);
+			}
+		}
+		data->inputtype = WINDOW_COPY_OFF;
+		window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
+		return;
+	} if (data->inputtype == WINDOW_COPY_NUMERICPREFIX) {
 		if (window_copy_key_numeric_prefix(wp, key) == 0)
 			return;
 		data->inputtype = WINDOW_COPY_OFF;
@@ -400,6 +426,56 @@ window_copy_key(struct window_pane *wp, 
 		for (; np != 0; np--)
 			window_copy_cursor_previous_word(wp, word_separators);
 		break;
+	case MODEKEYCOPY_JUMPTOBEFORECHARBACK:
+		data->charsearchforward = 0;
+		data->stop_before = 1;
+		data->inputtype = WINDOW_COPY_JUMPCHAR;
+		data->inputprompt = "Jump to char";
+		*data->inputstr = '\0';
+		window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
+		return; /* skip numprefix reset */
+	case MODEKEYCOPY_JUMPTOBEFORECHAR:
+		data->charsearchforward = 1;
+		data->stop_before = 1;
+		data->inputtype = WINDOW_COPY_JUMPCHAR;
+		data->inputprompt = "Jump to char";
+		*data->inputstr = '\0';
+		window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
+		return; /* skip numprefix reset */
+	case MODEKEYCOPY_JUMPTOCHARBACK:
+		data->charsearchforward = 0;
+		data->stop_before = 0;
+		data->inputtype = WINDOW_COPY_JUMPCHAR;
+		data->inputprompt = "Jump to char";
+		*data->inputstr = '\0';
+		window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
+		return; /* skip numprefix reset */
+	case MODEKEYCOPY_JUMPTOCHAR:
+		data->charsearchforward = 1;
+		data->stop_before = 0;
+		data->inputtype = WINDOW_COPY_JUMPCHAR;
+		data->inputprompt = "Jump to char";
+		*data->inputstr = '\0';
+		window_copy_redraw_lines(wp, screen_size_y(s) - 1, 1);
+		return; /* skip numprefix reset */
+	case MODEKEYCOPY_JUMPTOCHARAGAIN:
+		if (data->charsearchforward) {
+			for ( ; np != 0; np--)
+				window_copy_cursor_jump_char(wp);
+		}  else {
+			for ( ; np != 0; np--)
+				window_copy_cursor_jump_char_back(wp);
+		}
+		break;
+	case MODEKEYCOPY_JUMPTOCHARAGAINREVERSE:
+		if (data->charsearchforward) {
+			for ( ; np != 0; np--)
+				window_copy_cursor_jump_char_back(wp);
+		}  else {
+			for ( ; np != 0; np--)
+				window_copy_cursor_jump_char(wp);
+		}
+		break;
 	case MODEKEYCOPY_SEARCHUP:
 		data->inputtype = WINDOW_COPY_SEARCHUP;
 		data->inputprompt = "Search Up";
@@ -413,6 +489,7 @@ window_copy_key(struct window_pane *wp, 
 		switch (data->searchtype) {
 		case WINDOW_COPY_OFF:
 		case WINDOW_COPY_GOTOLINE:
+		case WINDOW_COPY_JUMPCHAR:
 		case WINDOW_COPY_NUMERICPREFIX:
 			break;
 		case WINDOW_COPY_SEARCHUP:
@@ -510,6 +587,7 @@ window_copy_key_input(struct window_pane
 	case MODEKEYEDIT_ENTER:
 		switch (data->inputtype) {
 		case WINDOW_COPY_OFF:
+		case WINDOW_COPY_JUMPCHAR:
 		case WINDOW_COPY_NUMERICPREFIX:
 			break;
 		case WINDOW_COPY_SEARCHUP:
@@ -1326,6 +1404,70 @@ window_copy_cursor_down(struct window_pa
 }
 
 void
+window_copy_cursor_jump_char(struct window_pane *wp)
+{
+	struct window_copy_mode_data	*data = wp->modedata;
+	struct screen			*base_s = &wp->base;
+	const struct grid_cell		*gc;
+	uint				 px, py, xx;
+
+	px = data->cx + 1;
+	py = screen_hsize(base_s) + data->cy - data->oy;
+	xx = window_copy_find_length(wp, py);
+	if (data->stop_before)
+		px++;
+
+	while (px < xx) {
+		gc = grid_peek_cell(base_s->grid, px, py);
+		if ((gc->flags & (GRID_FLAG_PADDING|GRID_FLAG_UTF8)) == 0
+		    && gc->data == data->searchchar) {
+
+			if (data->stop_before)
+				px--;
+			window_copy_update_cursor(wp, px, data->cy);
+			if (window_copy_update_selection(wp))
+				window_copy_redraw_lines(wp, data->cy, 1);
+			return;
+		}
+		px++;
+	}
+}
+
+void
+window_copy_cursor_jump_char_back(struct window_pane *wp)
+{
+	struct window_copy_mode_data	*data = wp->modedata;
+	struct screen			*base_s = &wp->base;
+	const struct grid_cell		*gc;
+	uint				 px, py;
+
+	px = data->cx;
+	py = screen_hsize(base_s) + data->cy - data->oy;
+
+	if (px > 0)
+		px--;
+	if (data->stop_before && px > 0)
+		px--;
+
+	for (;;) {
+		gc = grid_peek_cell(base_s->grid, px, py);
+		if ((gc->flags & (GRID_FLAG_PADDING|GRID_FLAG_UTF8)) == 0
+		    && gc->data == data->searchchar) {
+
+			if (data->stop_before)
+				px++;
+			window_copy_update_cursor(wp, px, data->cy);
+			if (window_copy_update_selection(wp))
+				window_copy_redraw_lines(wp, data->cy, 1);
+			return;
+		}
+		if (px == 0)
+			break;
+		px--;
+	}
+}
+
+void
 window_copy_cursor_next_word(struct window_pane *wp, const char *separators)
 {
 	struct window_copy_mode_data	*data = wp->modedata;
Index: tmux.1
===================================================================
--- tmux.1.orig
+++ tmux.1
@@ -605,7 +605,7 @@ The keys available depend on whether ema
 .Ic mode-keys
 option).
 The following keys are supported as appropriate for the mode:
-.Bl -column "FunctionXXXXXXXXXXXXXX" "viXXXXXXXXXX" "emacs" -offset indent
+.Bl -column "FunctionXXXXXXXXXXXXXXXXX" "viXXXXXXXXXX" "emacs" -offset indent
 .It Sy "Function" Ta Sy "vi" Ta Sy "emacs"
 .It Li "Back to indentation" Ta "^" Ta "M-m"
 .It Li "Bottom of history" Ta "G" Ta "M-<"
@@ -624,6 +624,12 @@ The following keys are supported as appr
 .It Li "Go to line" Ta ":" Ta "g"
 .It Li "Half page down" Ta "C-d" Ta "M-Down"
 .It Li "Half page up" Ta "C-u" Ta "M-Up"
+.It Li "Jump to char" Ta "f" Ta "f"
+.It Li "Jump to char (back)" Ta "F" Ta "F"
+.It Li "Jump to before char" Ta "t" Ta "t"
+.It Li "Jump to before char (back)" Ta "T" Ta "T"
+.It Li "Jump to char again" Ta ";" Ta ";"
+.It Li "Jump to char again, reverse" Ta "," Ta ","
 .It Li "Next page" Ta "C-f" Ta "Page down"
 .It Li "Next space" Ta "W" Ta ""
 .It Li "Next space, end of word" Ta "E" Ta ""
------------------------------------------------------------------------------
Download Intel&reg; Parallel Studio Eval
Try the new software tools for yourself. Speed compiling, find bugs 
proactively, and fine-tune applications for parallel performance. 
See why Intel Parallel Studio got high marks during beta.
http://p.sf.net/sfu/intel-sw-dev
_______________________________________________
tmux-users mailing list
tmux-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/tmux-users

Reply via email to