mirror of
				https://github.com/tmux/tmux.git
				synced 2025-10-26 12:27:15 +00:00 
			
		
		
		
	Add support for keys to jump between matching brackets - C-M-f and C-M-b
in emacs, % in vi. Suggested by and help from Chris Barber in GitHub issue 1666.
This commit is contained in:
		| @@ -335,7 +335,9 @@ key_bindings_init(void) | ||||
| 		"bind -Tcopy-mode M-> send -X history-bottom", | ||||
| 		"bind -Tcopy-mode M-R send -X top-line", | ||||
| 		"bind -Tcopy-mode M-b send -X previous-word", | ||||
| 		"bind -Tcopy-mode C-M-b send -X previous-matching-bracket", | ||||
| 		"bind -Tcopy-mode M-f send -X next-word-end", | ||||
| 		"bind -Tcopy-mode C-M-f send -X next-matching-bracket", | ||||
| 		"bind -Tcopy-mode M-m send -X back-to-indentation", | ||||
| 		"bind -Tcopy-mode M-r send -X middle-line", | ||||
| 		"bind -Tcopy-mode M-v send -X page-up", | ||||
| @@ -408,6 +410,7 @@ key_bindings_init(void) | ||||
| 		"bind -Tcopy-mode-vi w send -X next-word", | ||||
| 		"bind -Tcopy-mode-vi { send -X previous-paragraph", | ||||
| 		"bind -Tcopy-mode-vi } send -X next-paragraph", | ||||
| 		"bind -Tcopy-mode-vi % send -X next-matching-bracket", | ||||
| 		"bind -Tcopy-mode-vi MouseDown1Pane select-pane", | ||||
| 		"bind -Tcopy-mode-vi MouseDrag1Pane select-pane\\; send -X begin-selection", | ||||
| 		"bind -Tcopy-mode-vi MouseDragEnd1Pane send -X copy-selection-and-cancel", | ||||
|   | ||||
							
								
								
									
										221
									
								
								window-copy.c
									
									
									
									
									
								
							
							
						
						
									
										221
									
								
								window-copy.c
									
									
									
									
									
								
							| @@ -994,6 +994,218 @@ window_copy_cmd_middle_line(struct window_copy_cmd_state *cs) | ||||
| 	return (WINDOW_COPY_CMD_REDRAW); | ||||
| } | ||||
|  | ||||
| static enum window_copy_cmd_action | ||||
| window_copy_cmd_previous_matching_bracket(struct window_copy_cmd_state *cs) | ||||
| { | ||||
| 	struct window_mode_entry	*wme = cs->wme; | ||||
| 	u_int				 np = wme->prefix; | ||||
| 	struct window_copy_mode_data	*data = wme->data; | ||||
| 	struct screen			*s = data->backing; | ||||
| 	char				 open[] = "{[(", close[] = "}])"; | ||||
| 	char				 tried, found, start, *cp; | ||||
| 	u_int				 px, py, xx, yy, n; | ||||
| 	struct grid_cell		 gc; | ||||
| 	int				 failed; | ||||
|  | ||||
| 	for (; np != 0; np--) { | ||||
| 		/* Get cursor position and line length. */ | ||||
| 		px = data->cx; | ||||
| 		py = screen_hsize(s) + data->cy - data->oy; | ||||
| 		xx = window_copy_find_length(wme, py); | ||||
| 		yy = screen_hsize(s) + screen_size_y(s) - 1; | ||||
| 		if (xx == 0) | ||||
| 			break; | ||||
|  | ||||
| 		/* | ||||
| 		 * Get the current character. If not on a bracket, try the | ||||
| 		 * previous. If still not, then behave like previous-word. | ||||
| 		 */ | ||||
| 		tried = 0; | ||||
| 	retry: | ||||
| 		grid_get_cell(s->grid, px, py, &gc); | ||||
| 		if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING)) | ||||
| 			cp = NULL; | ||||
| 		else { | ||||
| 			found = *gc.data.data; | ||||
| 			cp = strchr(close, found); | ||||
| 		} | ||||
| 		if (cp == NULL) { | ||||
| 			if (data->modekeys == MODEKEY_EMACS) { | ||||
| 				if (!tried && px > 0) { | ||||
| 					px--; | ||||
| 					tried = 1; | ||||
| 					goto retry; | ||||
| 				} | ||||
| 				window_copy_cursor_previous_word(wme, "}]) "); | ||||
| 				px = data->cx; | ||||
| 				continue; | ||||
| 			} | ||||
| 			continue; | ||||
| 		} | ||||
| 		start = open[cp - close]; | ||||
|  | ||||
| 		/* Walk backward until the matching bracket is reached. */ | ||||
| 		n = 1; | ||||
| 		failed = 0; | ||||
| 		do { | ||||
| 			if (px == 0) { | ||||
| 				if (py == 0) { | ||||
| 					failed = 1; | ||||
| 					break; | ||||
| 				} | ||||
| 				do { | ||||
| 					py--; | ||||
| 					xx = window_copy_find_length(wme, py); | ||||
| 				} while (xx == 0 && py > 0); | ||||
| 				if (xx == 0 && py == 0) { | ||||
| 					failed = 1; | ||||
| 					break; | ||||
| 				} | ||||
| 				px = xx - 1; | ||||
| 			} else | ||||
| 				px--; | ||||
|  | ||||
| 			grid_get_cell(s->grid, px, py, &gc); | ||||
| 			if (gc.data.size == 1 && | ||||
| 			    (~gc.flags & GRID_FLAG_PADDING)) { | ||||
| 				if (*gc.data.data == found) | ||||
| 					n++; | ||||
| 				else if (*gc.data.data == start) | ||||
| 					n--; | ||||
| 			} | ||||
| 		} while (n != 0); | ||||
|  | ||||
| 		/* Move the cursor to the found location if any. */ | ||||
| 		if (!failed) | ||||
| 			window_copy_scroll_to(wme, px, py); | ||||
| 	} | ||||
|  | ||||
| 	return (WINDOW_COPY_CMD_NOTHING); | ||||
| } | ||||
|  | ||||
|  | ||||
| static enum window_copy_cmd_action | ||||
| window_copy_cmd_next_matching_bracket(struct window_copy_cmd_state *cs) | ||||
| { | ||||
| 	struct window_mode_entry	*wme = cs->wme; | ||||
| 	u_int				 np = wme->prefix; | ||||
| 	struct window_copy_mode_data	*data = wme->data; | ||||
| 	struct screen			*s = data->backing; | ||||
| 	char				 open[] = "{[(", close[] = "}])"; | ||||
| 	char				 tried, found, end, *cp; | ||||
| 	u_int				 px, py, xx, yy, sx, sy, n; | ||||
| 	struct grid_cell		 gc; | ||||
| 	int				 failed; | ||||
| 	struct grid_line		*gl; | ||||
|  | ||||
| 	for (; np != 0; np--) { | ||||
| 		/* Get cursor position and line length. */ | ||||
| 		px = data->cx; | ||||
| 		py = screen_hsize(s) + data->cy - data->oy; | ||||
| 		xx = window_copy_find_length(wme, py); | ||||
| 		yy = screen_hsize(s) + screen_size_y(s) - 1; | ||||
| 		if (xx == 0) | ||||
| 			break; | ||||
|  | ||||
| 		/* | ||||
| 		 * Get the current character. If not on a bracket, try the | ||||
| 		 * next. If still not, then behave like next-word. | ||||
| 		 */ | ||||
| 		tried = 0; | ||||
| 	retry: | ||||
| 		grid_get_cell(s->grid, px, py, &gc); | ||||
| 		if (gc.data.size != 1 || (gc.flags & GRID_FLAG_PADDING)) | ||||
| 			cp = NULL; | ||||
| 		else { | ||||
| 			found = *gc.data.data; | ||||
|  | ||||
| 			/* | ||||
| 			 * In vi mode, attempt to move to previous bracket if a | ||||
| 			 * closing bracket is found first. If this fails, | ||||
| 			 * return to the original cursor position. | ||||
| 			 */ | ||||
| 			cp = strchr(close, found); | ||||
| 			if (cp != NULL && data->modekeys == MODEKEY_VI) { | ||||
| 				sx = data->cx; | ||||
| 				sy = screen_hsize(s) + data->cy - data->oy; | ||||
|  | ||||
| 				window_copy_scroll_to(wme, px, py); | ||||
| 				window_copy_cmd_previous_matching_bracket(cs); | ||||
|  | ||||
| 				px = data->cx; | ||||
| 				py = screen_hsize(s) + data->cy - data->oy; | ||||
| 				grid_get_cell(s->grid, px, py, &gc); | ||||
| 				if (gc.data.size != 1 || | ||||
| 				    (gc.flags & GRID_FLAG_PADDING) || | ||||
| 				    strchr(close, *gc.data.data) == NULL) | ||||
| 					window_copy_scroll_to(wme, sx, sy); | ||||
| 				break; | ||||
| 			} | ||||
|  | ||||
| 			cp = strchr(open, found); | ||||
| 		} | ||||
| 		if (cp == NULL) { | ||||
| 			if (data->modekeys == MODEKEY_EMACS) { | ||||
| 				if (!tried && px <= xx) { | ||||
| 					px++; | ||||
| 					tried = 1; | ||||
| 					goto retry; | ||||
| 				} | ||||
| 				window_copy_cursor_next_word_end(wme, "{[( "); | ||||
| 				px = data->cx; | ||||
| 				continue; | ||||
| 			} | ||||
| 			/* For vi, continue searching for bracket until EOL. */ | ||||
| 			if (px > xx) { | ||||
| 				if (py == yy) | ||||
| 					continue; | ||||
| 				gl = grid_get_line(s->grid, py); | ||||
| 				if (~gl->flags & GRID_LINE_WRAPPED) | ||||
| 					continue; | ||||
| 				if (gl->cellsize > s->grid->sx) | ||||
| 					continue; | ||||
| 				px = 0; | ||||
| 				py++; | ||||
| 				xx = window_copy_find_length(wme, py); | ||||
| 			} else | ||||
| 				px++; | ||||
| 			goto retry; | ||||
| 		} | ||||
| 		end = close[cp - open]; | ||||
|  | ||||
| 		/* Walk forward until the matching bracket is reached. */ | ||||
| 		n = 1; | ||||
| 		failed = 0; | ||||
| 		do { | ||||
| 			if (px > xx) { | ||||
| 				if (py == yy) { | ||||
| 					failed = 1; | ||||
| 					break; | ||||
| 				} | ||||
| 				px = 0; | ||||
| 				py++; | ||||
| 				xx = window_copy_find_length(wme, py); | ||||
| 			} else | ||||
| 				px++; | ||||
|  | ||||
| 			grid_get_cell(s->grid, px, py, &gc); | ||||
| 			if (gc.data.size == 1 && | ||||
| 			    (~gc.flags & GRID_FLAG_PADDING)) { | ||||
| 				if (*gc.data.data == found) | ||||
| 					n++; | ||||
| 				else if (*gc.data.data == end) | ||||
| 					n--; | ||||
| 			} | ||||
| 		} while (n != 0); | ||||
|  | ||||
| 		/* Move the cursor to the found location if any. */ | ||||
| 		if (!failed) | ||||
| 			window_copy_scroll_to(wme, px, py); | ||||
| 	} | ||||
|  | ||||
| 	return (WINDOW_COPY_CMD_NOTHING); | ||||
| } | ||||
|  | ||||
| static enum window_copy_cmd_action | ||||
| window_copy_cmd_next_paragraph(struct window_copy_cmd_state *cs) | ||||
| { | ||||
| @@ -1613,6 +1825,8 @@ static const struct { | ||||
| 	  window_copy_cmd_jump_to_forward }, | ||||
| 	{ "middle-line", 0, 0, | ||||
| 	  window_copy_cmd_middle_line }, | ||||
| 	{ "next-matching-bracket", 0, 0, | ||||
| 	  window_copy_cmd_next_matching_bracket }, | ||||
| 	{ "next-paragraph", 0, 0, | ||||
| 	  window_copy_cmd_next_paragraph }, | ||||
| 	{ "next-space", 0, 0, | ||||
| @@ -1631,6 +1845,8 @@ static const struct { | ||||
| 	  window_copy_cmd_page_down_and_cancel }, | ||||
| 	{ "page-up", 0, 0, | ||||
| 	  window_copy_cmd_page_up }, | ||||
| 	{ "previous-matching-bracket", 0, 0, | ||||
| 	  window_copy_cmd_previous_matching_bracket }, | ||||
| 	{ "previous-paragraph", 0, 0, | ||||
| 	  window_copy_cmd_previous_paragraph }, | ||||
| 	{ "previous-space", 0, 0, | ||||
| @@ -3147,6 +3363,11 @@ window_copy_cursor_previous_word(struct window_mode_entry *wme, | ||||
|  | ||||
| 			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; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 nicm
					nicm