mirror of
				https://github.com/tmux/tmux.git
				synced 2025-10-26 12:27:15 +00:00 
			
		
		
		
	Add 'e' key in buffer mode to open the buffer in an editor.
This commit is contained in:
		| @@ -313,7 +313,7 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item) | |||||||
| 	else if (args_has(args, 'E')) | 	else if (args_has(args, 'E')) | ||||||
| 		flags |= POPUP_CLOSEEXIT; | 		flags |= POPUP_CLOSEEXIT; | ||||||
| 	if (popup_display(flags, item, px, py, w, h, nlines, lines, shellcmd, | 	if (popup_display(flags, item, px, py, w, h, nlines, lines, shellcmd, | ||||||
| 	    cmd, cwd, tc, target) != 0) | 	    cmd, cwd, tc, target, NULL, NULL) != 0) | ||||||
| 		return (CMD_RETURN_NORMAL); | 		return (CMD_RETURN_NORMAL); | ||||||
| 	return (CMD_RETURN_WAIT); | 	return (CMD_RETURN_WAIT); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -738,7 +738,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base, | |||||||
|  |  | ||||||
| 	/* | 	/* | ||||||
| 	 * Draw the screens. How they are arranged depends on where the list | 	 * Draw the screens. How they are arranged depends on where the list | ||||||
| 	 * appearsq. | 	 * appears. | ||||||
| 	 */ | 	 */ | ||||||
| 	switch (list_align) { | 	switch (list_align) { | ||||||
| 	case STYLE_ALIGN_DEFAULT: | 	case STYLE_ALIGN_DEFAULT: | ||||||
|   | |||||||
							
								
								
									
										7
									
								
								job.c
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								job.c
									
									
									
									
									
								
							| @@ -19,6 +19,7 @@ | |||||||
| #include <sys/types.h> | #include <sys/types.h> | ||||||
| #include <sys/ioctl.h> | #include <sys/ioctl.h> | ||||||
| #include <sys/socket.h> | #include <sys/socket.h> | ||||||
|  | #include <sys/wait.h> | ||||||
|  |  | ||||||
| #include <fcntl.h> | #include <fcntl.h> | ||||||
| #include <signal.h> | #include <signal.h> | ||||||
| @@ -283,6 +284,12 @@ job_check_died(pid_t pid, int status) | |||||||
| 	} | 	} | ||||||
| 	if (job == NULL) | 	if (job == NULL) | ||||||
| 		return; | 		return; | ||||||
|  | 	if (WIFSTOPPED(status)) { | ||||||
|  | 		if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU) | ||||||
|  | 			return; | ||||||
|  | 		killpg(job->pid, SIGCONT); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
| 	log_debug("job died %p: %s, pid %ld", job, job->cmd, (long) job->pid); | 	log_debug("job died %p: %s, pid %ld", job, job->cmd, (long) job->pid); | ||||||
|  |  | ||||||
| 	job->status = status; | 	job->status = status; | ||||||
|   | |||||||
| @@ -209,6 +209,12 @@ const struct options_table_entry options_table[] = { | |||||||
| 	  .default_str = "screen" | 	  .default_str = "screen" | ||||||
| 	}, | 	}, | ||||||
|  |  | ||||||
|  | 	{ .name = "editor", | ||||||
|  | 	  .type = OPTIONS_TABLE_STRING, | ||||||
|  | 	  .scope = OPTIONS_TABLE_SERVER, | ||||||
|  | 	  .default_str = _PATH_VI | ||||||
|  | 	}, | ||||||
|  |  | ||||||
| 	{ .name = "escape-time", | 	{ .name = "escape-time", | ||||||
| 	  .type = OPTIONS_TABLE_NUMBER, | 	  .type = OPTIONS_TABLE_NUMBER, | ||||||
| 	  .scope = OPTIONS_TABLE_SERVER, | 	  .scope = OPTIONS_TABLE_SERVER, | ||||||
|   | |||||||
							
								
								
									
										9
									
								
								paste.c
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								paste.c
									
									
									
									
									
								
							| @@ -296,6 +296,15 @@ paste_set(char *data, size_t size, const char *name, char **cause) | |||||||
| 	return (0); | 	return (0); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Set paste data without otherwise changing it. */ | ||||||
|  | void | ||||||
|  | paste_replace(struct paste_buffer *pb, char *data, size_t size) | ||||||
|  | { | ||||||
|  | 	free(pb->data); | ||||||
|  | 	pb->data = data; | ||||||
|  | 	pb->size = size; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* Convert start of buffer into a nice string. */ | /* Convert start of buffer into a nice string. */ | ||||||
| char * | char * | ||||||
| paste_make_sample(struct paste_buffer *pb) | paste_make_sample(struct paste_buffer *pb) | ||||||
|   | |||||||
							
								
								
									
										9
									
								
								popup.c
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								popup.c
									
									
									
									
									
								
							| @@ -40,6 +40,8 @@ struct popup_data { | |||||||
| 	struct job		 *job; | 	struct job		 *job; | ||||||
| 	struct input_ctx	 *ictx; | 	struct input_ctx	 *ictx; | ||||||
| 	int			  status; | 	int			  status; | ||||||
|  | 	popup_close_cb		  cb; | ||||||
|  | 	void			 *arg; | ||||||
|  |  | ||||||
| 	u_int			  px; | 	u_int			  px; | ||||||
| 	u_int			  py; | 	u_int			  py; | ||||||
| @@ -150,6 +152,9 @@ popup_free_cb(struct client *c) | |||||||
| 	struct cmdq_item	*item = pd->item; | 	struct cmdq_item	*item = pd->item; | ||||||
| 	u_int			 i; | 	u_int			 i; | ||||||
|  |  | ||||||
|  | 	if (pd->cb != NULL) | ||||||
|  | 		pd->cb(pd->status, pd->arg); | ||||||
|  |  | ||||||
| 	if (item != NULL) { | 	if (item != NULL) { | ||||||
| 		if (pd->ictx != NULL && | 		if (pd->ictx != NULL && | ||||||
| 		    cmdq_get_client(item) != NULL && | 		    cmdq_get_client(item) != NULL && | ||||||
| @@ -403,7 +408,7 @@ int | |||||||
| popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx, | popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx, | ||||||
|     u_int sy, u_int nlines, const char **lines, const char *shellcmd, |     u_int sy, u_int nlines, const char **lines, const char *shellcmd, | ||||||
|     const char *cmd, const char *cwd, struct client *c, |     const char *cmd, const char *cwd, struct client *c, | ||||||
|     struct cmd_find_state *fs) |     struct cmd_find_state *fs, popup_close_cb cb, void *arg) | ||||||
| { | { | ||||||
| 	struct popup_data	*pd; | 	struct popup_data	*pd; | ||||||
| 	u_int			 i; | 	u_int			 i; | ||||||
| @@ -422,6 +427,8 @@ popup_display(int flags, struct cmdq_item *item, u_int px, u_int py, u_int sx, | |||||||
| 	pd->c = c; | 	pd->c = c; | ||||||
| 	pd->c->references++; | 	pd->c->references++; | ||||||
|  |  | ||||||
|  | 	pd->cb = cb; | ||||||
|  | 	pd->arg = arg; | ||||||
| 	pd->status = 128 + SIGHUP; | 	pd->status = 128 + SIGHUP; | ||||||
|  |  | ||||||
| 	if (fs != NULL) | 	if (fs != NULL) | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								server.c
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								server.c
									
									
									
									
									
								
							| @@ -480,4 +480,5 @@ server_child_stopped(pid_t pid, int status) | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | 	job_check_died(pid, status); | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										5
									
								
								tmux.1
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								tmux.1
									
									
									
									
									
								
							| @@ -3113,6 +3113,10 @@ Set the time in milliseconds for which | |||||||
| waits after an escape is input to determine if it is part of a function or meta | waits after an escape is input to determine if it is part of a function or meta | ||||||
| key sequences. | key sequences. | ||||||
| The default is 500 milliseconds. | The default is 500 milliseconds. | ||||||
|  | .It Ic editor Ar shell-command | ||||||
|  | Set the command used when | ||||||
|  | .Nm | ||||||
|  | runs an editor. | ||||||
| .It Xo Ic exit-empty | .It Xo Ic exit-empty | ||||||
| .Op Ic on | off | .Op Ic on | off | ||||||
| .Xc | .Xc | ||||||
| @@ -5339,6 +5343,7 @@ The following keys may be used in buffer mode: | |||||||
| .It Li "P" Ta "Paste tagged buffers" | .It Li "P" Ta "Paste tagged buffers" | ||||||
| .It Li "d" Ta "Delete selected buffer" | .It Li "d" Ta "Delete selected buffer" | ||||||
| .It Li "D" Ta "Delete tagged buffers" | .It Li "D" Ta "Delete tagged buffers" | ||||||
|  | ,It Li "e" Ta "Open the buffer in an editor" | ||||||
| .It Li "f" Ta "Enter a format to filter items" | .It Li "f" Ta "Enter a format to filter items" | ||||||
| .It Li "O" Ta "Change sort field" | .It Li "O" Ta "Change sort field" | ||||||
| .It Li "r" Ta "Reverse sort order" | .It Li "r" Ta "Reverse sort order" | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								tmux.c
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								tmux.c
									
									
									
									
									
								
							| @@ -434,6 +434,7 @@ main(int argc, char **argv) | |||||||
|  |  | ||||||
| 	/* Override keys to vi if VISUAL or EDITOR are set. */ | 	/* Override keys to vi if VISUAL or EDITOR are set. */ | ||||||
| 	if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) { | 	if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) { | ||||||
|  | 		options_set_string(global_options, "editor", 0, "%s", s); | ||||||
| 		if (strrchr(s, '/') != NULL) | 		if (strrchr(s, '/') != NULL) | ||||||
| 			s = strrchr(s, '/') + 1; | 			s = strrchr(s, '/') + 1; | ||||||
| 		if (strstr(s, "vi") != NULL) | 		if (strstr(s, "vi") != NULL) | ||||||
|   | |||||||
							
								
								
									
										5
									
								
								tmux.h
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								tmux.h
									
									
									
									
									
								
							| @@ -1808,6 +1808,7 @@ void		 paste_free(struct paste_buffer *); | |||||||
| void		 paste_add(const char *, char *, size_t); | void		 paste_add(const char *, char *, size_t); | ||||||
| int		 paste_rename(const char *, const char *, char **); | int		 paste_rename(const char *, const char *, char **); | ||||||
| int		 paste_set(char *, size_t, const char *, char **); | int		 paste_set(char *, size_t, const char *, char **); | ||||||
|  | void		 paste_replace(struct paste_buffer *, char *, size_t); | ||||||
| char		*paste_make_sample(struct paste_buffer *); | char		*paste_make_sample(struct paste_buffer *); | ||||||
|  |  | ||||||
| /* format.c */ | /* format.c */ | ||||||
| @@ -2816,12 +2817,14 @@ int		 menu_display(struct menu *, int, struct cmdq_item *, u_int, | |||||||
| #define POPUP_WRITEKEYS 0x1 | #define POPUP_WRITEKEYS 0x1 | ||||||
| #define POPUP_CLOSEEXIT 0x2 | #define POPUP_CLOSEEXIT 0x2 | ||||||
| #define POPUP_CLOSEEXITZERO 0x4 | #define POPUP_CLOSEEXITZERO 0x4 | ||||||
|  | typedef void (*popup_close_cb)(int, void *); | ||||||
| u_int		 popup_width(struct cmdq_item *, u_int, const char **, | u_int		 popup_width(struct cmdq_item *, u_int, const char **, | ||||||
| 		    struct client *, struct cmd_find_state *); | 		    struct client *, struct cmd_find_state *); | ||||||
| u_int		 popup_height(u_int, const char **); | u_int		 popup_height(u_int, const char **); | ||||||
| int		 popup_display(int, struct cmdq_item *, u_int, u_int, u_int, | int		 popup_display(int, struct cmdq_item *, u_int, u_int, u_int, | ||||||
| 		    u_int, u_int, const char **, const char *, const char *, | 		    u_int, u_int, const char **, const char *, const char *, | ||||||
| 		    const char *, struct client *, struct cmd_find_state *); | 		    const char *, struct client *, struct cmd_find_state *, | ||||||
|  | 		    popup_close_cb, void *); | ||||||
|  |  | ||||||
| /* style.c */ | /* style.c */ | ||||||
| int		 style_parse(struct style *,const struct grid_cell *, | int		 style_parse(struct style *,const struct grid_cell *, | ||||||
|   | |||||||
							
								
								
									
										133
									
								
								window-buffer.c
									
									
									
									
									
								
							
							
						
						
									
										133
									
								
								window-buffer.c
									
									
									
									
									
								
							| @@ -18,9 +18,11 @@ | |||||||
|  |  | ||||||
| #include <sys/types.h> | #include <sys/types.h> | ||||||
|  |  | ||||||
|  | #include <stdio.h> | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <time.h> | #include <time.h> | ||||||
|  | #include <unistd.h> | ||||||
|  |  | ||||||
| #include "tmux.h" | #include "tmux.h" | ||||||
|  |  | ||||||
| @@ -94,6 +96,13 @@ struct window_buffer_modedata { | |||||||
| 	u_int				  item_size; | 	u_int				  item_size; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | struct window_buffer_editdata { | ||||||
|  | 	u_int			 wp_id; | ||||||
|  | 	char			*path; | ||||||
|  | 	char			*name; | ||||||
|  | 	struct paste_buffer	*pb; | ||||||
|  | }; | ||||||
|  |  | ||||||
| static struct window_buffer_itemdata * | static struct window_buffer_itemdata * | ||||||
| window_buffer_add_item(struct window_buffer_modedata *data) | window_buffer_add_item(struct window_buffer_modedata *data) | ||||||
| { | { | ||||||
| @@ -352,6 +361,126 @@ window_buffer_do_paste(void *modedata, void *itemdata, struct client *c, | |||||||
| 		mode_tree_run_command(c, NULL, data->command, item->name); | 		mode_tree_run_command(c, NULL, data->command, item->name); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | window_buffer_finish_edit(struct window_buffer_editdata *ed) | ||||||
|  | { | ||||||
|  | 	unlink(ed->path); | ||||||
|  | 	free(ed->path); | ||||||
|  | 	free(ed->name); | ||||||
|  | 	free(ed); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | window_buffer_edit_close_cb(int status, void *arg) | ||||||
|  | { | ||||||
|  | 	struct window_buffer_editdata	*ed = arg; | ||||||
|  | 	FILE				*f; | ||||||
|  | 	off_t				 len; | ||||||
|  | 	char				*buf; | ||||||
|  | 	size_t				 oldlen; | ||||||
|  | 	const char			*oldbuf; | ||||||
|  | 	struct paste_buffer		*pb; | ||||||
|  | 	struct window_pane		*wp; | ||||||
|  | 	struct window_buffer_modedata	*data; | ||||||
|  | 	struct window_mode_entry	*wme; | ||||||
|  |  | ||||||
|  | 	if (status != 0) { | ||||||
|  | 		window_buffer_finish_edit(ed); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	pb = paste_get_name(ed->name); | ||||||
|  | 	if (pb == NULL || pb != ed->pb) { | ||||||
|  | 		window_buffer_finish_edit(ed); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	f = fopen(ed->path, "r"); | ||||||
|  | 	if (f != NULL) { | ||||||
|  | 		fseeko(f, 0, SEEK_END); | ||||||
|  | 		len = ftello(f); | ||||||
|  | 		fseeko(f, 0, SEEK_SET); | ||||||
|  |  | ||||||
|  | 		if (len > 0 && | ||||||
|  | 		    (uintmax_t)len <= (uintmax_t)SIZE_MAX && | ||||||
|  | 		    (buf = malloc(len)) != NULL && | ||||||
|  | 		    fread(buf, len, 1, f) == 1) { | ||||||
|  | 			oldbuf = paste_buffer_data(pb, &oldlen); | ||||||
|  | 			if (oldlen != '\0' && | ||||||
|  | 			    oldbuf[oldlen - 1] != '\n' && | ||||||
|  | 			    buf[len - 1] == '\n') | ||||||
|  | 				len--; | ||||||
|  | 			if (len != 0) | ||||||
|  | 				paste_replace(pb, buf, len); | ||||||
|  | 		} | ||||||
|  | 		fclose(f); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	wp = window_pane_find_by_id(ed->wp_id); | ||||||
|  | 	if (wp != NULL) { | ||||||
|  | 		wme = TAILQ_FIRST(&wp->modes); | ||||||
|  | 		if (wme->mode == &window_buffer_mode) { | ||||||
|  | 			data = wme->data; | ||||||
|  | 			mode_tree_build(data->data); | ||||||
|  | 			mode_tree_draw(data->data); | ||||||
|  | 		} | ||||||
|  | 		wp->flags |= PANE_REDRAW; | ||||||
|  | 	} | ||||||
|  | 	window_buffer_finish_edit(ed); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void | ||||||
|  | window_buffer_start_edit(struct window_buffer_modedata *data, | ||||||
|  |     struct window_buffer_itemdata *item, struct client *c) | ||||||
|  | { | ||||||
|  | 	struct paste_buffer		*pb; | ||||||
|  | 	int				 fd; | ||||||
|  | 	FILE				*f; | ||||||
|  | 	const char			*buf; | ||||||
|  | 	size_t				 len; | ||||||
|  | 	struct window_buffer_editdata	*ed; | ||||||
|  | 	char				*cmd; | ||||||
|  | 	char				 path[] = _PATH_TMP "tmux.XXXXXXXX"; | ||||||
|  | 	const char			*editor; | ||||||
|  | 	u_int				 px, py, sx, sy; | ||||||
|  |  | ||||||
|  | 	if ((pb = paste_get_name(item->name)) == NULL) | ||||||
|  | 		return; | ||||||
|  | 	buf = paste_buffer_data(pb, &len); | ||||||
|  |  | ||||||
|  | 	editor = options_get_string(global_options, "editor"); | ||||||
|  | 	if (*editor == '\0') | ||||||
|  | 		return; | ||||||
|  |  | ||||||
|  | 	fd = mkstemp(path); | ||||||
|  | 	if (fd == -1) | ||||||
|  | 		return; | ||||||
|  | 	f = fdopen(fd, "w"); | ||||||
|  | 	if (fwrite(buf, len, 1, f) != 1) { | ||||||
|  | 		fclose(f); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	fclose(f); | ||||||
|  |  | ||||||
|  | 	ed = xcalloc(1, sizeof *ed); | ||||||
|  | 	ed->wp_id = data->wp->id; | ||||||
|  | 	ed->path = xstrdup(path); | ||||||
|  | 	ed->name = xstrdup(paste_buffer_name(pb)); | ||||||
|  | 	ed->pb = pb; | ||||||
|  |  | ||||||
|  | 	sx = c->tty.sx * 9 / 10; | ||||||
|  | 	sy = c->tty.sy * 9 / 10; | ||||||
|  | 	px = (c->tty.sx / 2) - (sx / 2); | ||||||
|  | 	py = (c->tty.sy / 2) - (sy / 2); | ||||||
|  |  | ||||||
|  | 	xasprintf(&cmd, "%s %s", editor, path); | ||||||
|  | 	if (popup_display(POPUP_WRITEKEYS|POPUP_CLOSEEXIT, NULL, px, py, sx, sy, | ||||||
|  | 	    0, NULL, cmd, NULL, _PATH_TMP, c, NULL, window_buffer_edit_close_cb, | ||||||
|  | 	    ed) != 0) | ||||||
|  | 		window_buffer_finish_edit(ed); | ||||||
|  | 	free(cmd); | ||||||
|  | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| window_buffer_key(struct window_mode_entry *wme, struct client *c, | window_buffer_key(struct window_mode_entry *wme, struct client *c, | ||||||
|     __unused struct session *s, __unused struct winlink *wl, key_code key, |     __unused struct session *s, __unused struct winlink *wl, key_code key, | ||||||
| @@ -365,6 +494,10 @@ window_buffer_key(struct window_mode_entry *wme, struct client *c, | |||||||
|  |  | ||||||
| 	finished = mode_tree_key(mtd, c, &key, m, NULL, NULL); | 	finished = mode_tree_key(mtd, c, &key, m, NULL, NULL); | ||||||
| 	switch (key) { | 	switch (key) { | ||||||
|  | 	case 'e': | ||||||
|  | 		item = mode_tree_get_current(mtd); | ||||||
|  | 		window_buffer_start_edit(data, item, c); | ||||||
|  | 		break; | ||||||
| 	case 'd': | 	case 'd': | ||||||
| 		item = mode_tree_get_current(mtd); | 		item = mode_tree_get_current(mtd); | ||||||
| 		window_buffer_do_delete(data, item, c, key); | 		window_buffer_do_delete(data, item, c, key); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Nicholas Marriott
					Nicholas Marriott