changeset 11933:d033653d3df8 v8.0.0846

patch 8.0.0846: cannot get the name of the pty of a job commit https://github.com/vim/vim/commit/7c9aec4ac86ccc455c0859d9393253141e3f77b6 Author: Bram Moolenaar <Bram@vim.org> Date: Thu Aug 3 13:51:25 2017 +0200 patch 8.0.0846: cannot get the name of the pty of a job Problem: Cannot get the name of the pty of a job. Solution: Add the "tty" entry to the job info. (Ozaki Kiichi, closes https://github.com/vim/vim/issues/1920) Add the term_gettty() function.
author Christian Brabandt <cb@256bit.org>
date Thu, 03 Aug 2017 14:00:06 +0200
parents 6dd3262cbf09
children 695ef5fe791b
files runtime/doc/eval.txt src/channel.c src/evalfunc.c src/os_unix.c src/proto/terminal.pro src/structs.h src/terminal.c src/testdir/test_terminal.vim src/version.c
diffstat 9 files changed, 75 insertions(+), 26 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -1,4 +1,4 @@
-*eval.txt*	For Vim version 8.0.  Last change: 2017 Aug 01
+*eval.txt*	For Vim version 8.0.  Last change: 2017 Aug 03
 
 
 		  VIM REFERENCE MANUAL	  by Bram Moolenaar
@@ -2376,6 +2376,7 @@ term_getline({buf}, {row})	String	get a 
 term_getsize({buf})		List	get the size of a terminal
 term_getstatus({buf})		String	get the status of a terminal
 term_gettitle({buf})		String	get the title of a terminal
+term_gettty({buf})		String	get the tty name of a terminal
 term_list()			List	get the list of terminal buffers
 term_scrape({buf}, {row})	List	get row of a terminal screen
 term_sendkeys({buf}, {keys})	none	send keystrokes to a terminal
@@ -5192,6 +5193,8 @@ job_info({job})						*job_info()*
 		Returns a Dictionary with information about {job}:
 		   "status"	what |job_status()| returns
 		   "channel"	what |job_getchannel()| returns
+		   "process"	process ID
+		   "tty"	controlling terminal name, empty when none
 		   "exitval"	only valid when "status" is "dead"
 		   "exit_cb"	function to be called on exit
 		   "stoponexit"	|job-stoponexit|
@@ -7930,6 +7933,7 @@ term_getcursor({buf})					*term_getcurso
 term_getjob({buf})					*term_getjob()*
 		Get the Job associated with terminal window {buf}.
 		{buf} is used as with |term_getsize()|.
+		Returns |v:null| when there is no job.
 
 term_getline({buf}, {row})				*term_getline()*
 		Get a line of text from the terminal window of {buf}.
@@ -7943,9 +7947,9 @@ term_getsize({buf})					*term_getsize()*
 		numbers: [rows, cols].  This is the size of the terminal, not
 		the window containing the terminal.
 
-		{buf} must be the buffer number of a terminal window. If the
-		buffer does not exist or is not a terminal window, an empty
-		list is returned.
+		{buf} must be the buffer number of a terminal window.  Use an
+		empty string for the current buffer.  If the buffer does not
+		exist or is not a terminal window, an empty list is returned.
 
 term_getstatus({buf})					*term_getstatus()*
 		Get the status of terminal {buf}. This returns a comma
@@ -7967,6 +7971,11 @@ term_gettitle({buf})					*term_gettitle(
 		buffer does not exist or is not a terminal window, an empty
 		string is returned.
 
+term_gettty({buf})					*term_gettty()*
+		Get the name of the controlling terminal associated with
+		terminal window {buf}.
+		{buf} is used as with |term_getsize()|.
+
 term_list()						*term_list()*
 		Return a list with the buffer numbers of all buffers for
 		terminal windows.
@@ -7982,7 +7991,7 @@ term_scrape({buf}, {row})				*term_scrap
 		    "chars"	character(s) at the cell
 		    "fg"	foreground color as #rrggbb
 		    "bg"	background color as #rrggbb
-		    "attr"	attributes of the cell, use term_getattr()
+		    "attr"	attributes of the cell, use |term_getattr()|
 		    		to get the individual flags
 		    "width"	cell width: 1 or 2
 
--- a/src/channel.c
+++ b/src/channel.c
@@ -1016,11 +1016,9 @@ ch_close_part(channel_T *channel, ch_par
 	{
 	    /* When using a pty the same FD is set on multiple parts, only
 	     * close it when the last reference is closed. */
-	    if ((part == PART_IN || channel->ch_part[PART_IN].ch_fd != *fd)
-		    && (part == PART_OUT
-				    || channel->ch_part[PART_OUT].ch_fd != *fd)
-		    && (part == PART_ERR
-				   || channel->ch_part[PART_ERR].ch_fd != *fd))
+	    if ((part == PART_IN || channel->CH_IN_FD != *fd)
+		    && (part == PART_OUT || channel->CH_OUT_FD != *fd)
+		    && (part == PART_ERR || channel->CH_ERR_FD != *fd))
 		fd_close(*fd);
 	}
 	*fd = INVALID_FD;
@@ -4592,6 +4590,7 @@ job_free_contents(job_T *job)
     }
     mch_clear_job(job);
 
+    vim_free(job->jv_tty_name);
     vim_free(job->jv_stoponexit);
     free_callback(job->jv_exit_cb, job->jv_exit_partial);
 }
@@ -5164,6 +5163,8 @@ job_info(job_T *job, dict_T *dict)
     nr = job->jv_proc_info.dwProcessId;
 #endif
     dict_add_nr_str(dict, "process", nr, NULL);
+    dict_add_nr_str(dict, "tty", 0L,
+		   job->jv_tty_name != NULL ? job->jv_tty_name : (char_u *)"");
 
     dict_add_nr_str(dict, "exitval", job->jv_exitval, NULL);
     dict_add_nr_str(dict, "exit_cb", 0L, job->jv_exit_cb);
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -838,6 +838,7 @@ static struct fst
     {"term_getsize",	1, 1, f_term_getsize},
     {"term_getstatus",	1, 1, f_term_getstatus},
     {"term_gettitle",	1, 1, f_term_gettitle},
+    {"term_gettty",	1, 1, f_term_gettty},
     {"term_list",	0, 0, f_term_list},
     {"term_scrape",	1, 2, f_term_scrape},
     {"term_sendkeys",	2, 2, f_term_sendkeys},
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -4170,7 +4170,7 @@ set_default_child_environment(void)
  * When successful both file descriptors are stored.
  */
     static void
-open_pty(int *pty_master_fd, int *pty_slave_fd)
+open_pty(int *pty_master_fd, int *pty_slave_fd, char_u **namep)
 {
     char	*tty_name;
 
@@ -4190,6 +4190,8 @@ open_pty(int *pty_master_fd, int *pty_sl
 	    close(*pty_master_fd);
 	    *pty_master_fd = -1;
 	}
+	else if (namep != NULL)
+	    *namep = vim_strsave((char_u *)tty_name);
     }
 }
 #endif
@@ -4384,7 +4386,7 @@ mch_call_shell(
 	 * If the slave can't be opened, close the master pty.
 	 */
 	if (p_guipty && !(options & (SHELL_READ|SHELL_WRITE)))
-	    open_pty(&pty_master_fd, &pty_slave_fd);
+	    open_pty(&pty_master_fd, &pty_slave_fd, NULL);
 	/*
 	 * If not opening a pty or it didn't work, try using pipes.
 	 */
@@ -5189,9 +5191,9 @@ error:
 mch_job_start(char **argv, job_T *job, jobopt_T *options)
 {
     pid_t	pid;
-    int		fd_in[2];	/* for stdin */
-    int		fd_out[2];	/* for stdout */
-    int		fd_err[2];	/* for stderr */
+    int		fd_in[2] = {-1, -1};	/* for stdin */
+    int		fd_out[2] = {-1, -1};	/* for stdout */
+    int		fd_err[2] = {-1, -1};	/* for stderr */
     int		pty_master_fd = -1;
     int		pty_slave_fd = -1;
     channel_T	*channel = NULL;
@@ -5209,15 +5211,9 @@ mch_job_start(char **argv, job_T *job, j
 
     /* default is to fail */
     job->jv_status = JOB_FAILED;
-    fd_in[0] = -1;
-    fd_in[1] = -1;
-    fd_out[0] = -1;
-    fd_out[1] = -1;
-    fd_err[0] = -1;
-    fd_err[1] = -1;
 
     if (options->jo_pty)
-	open_pty(&pty_master_fd, &pty_slave_fd);
+	open_pty(&pty_master_fd, &pty_slave_fd, &job->jv_tty_name);
 
     /* TODO: without the channel feature connect the child to /dev/null? */
     /* Open pipes for stdin, stdout, stderr. */
--- a/src/proto/terminal.pro
+++ b/src/proto/terminal.pro
@@ -23,6 +23,7 @@ void f_term_getline(typval_T *argvars, t
 void f_term_getsize(typval_T *argvars, typval_T *rettv);
 void f_term_getstatus(typval_T *argvars, typval_T *rettv);
 void f_term_gettitle(typval_T *argvars, typval_T *rettv);
+void f_term_gettty(typval_T *argvars, typval_T *rettv);
 void f_term_list(typval_T *argvars, typval_T *rettv);
 void f_term_scrape(typval_T *argvars, typval_T *rettv);
 void f_term_sendkeys(typval_T *argvars, typval_T *rettv);
--- a/src/structs.h
+++ b/src/structs.h
@@ -1478,6 +1478,7 @@ struct jobvar_S
     PROCESS_INFORMATION	jv_proc_info;
     HANDLE		jv_job_object;
 #endif
+    char_u	*jv_tty_name;	/* controlling tty, allocated */
     jobstatus_T	jv_status;
     char_u	*jv_stoponexit; /* allocated */
     int		jv_exitval;
@@ -1537,18 +1538,20 @@ typedef enum {
     JIO_OUT
 } job_io_T;
 
+#define CH_PART_FD(part)	ch_part[part].ch_fd
+
 /* Ordering matters, it is used in for loops: IN is last, only SOCK/OUT/ERR
  * are polled. */
 typedef enum {
     PART_SOCK = 0,
-#define CH_SOCK_FD	ch_part[PART_SOCK].ch_fd
+#define CH_SOCK_FD	CH_PART_FD(PART_SOCK)
 #ifdef FEAT_JOB_CHANNEL
     PART_OUT,
-# define CH_OUT_FD	ch_part[PART_OUT].ch_fd
+# define CH_OUT_FD	CH_PART_FD(PART_OUT)
     PART_ERR,
-# define CH_ERR_FD	ch_part[PART_ERR].ch_fd
+# define CH_ERR_FD	CH_PART_FD(PART_ERR)
     PART_IN,
-# define CH_IN_FD	ch_part[PART_IN].ch_fd
+# define CH_IN_FD	CH_PART_FD(PART_IN)
 #endif
     PART_COUNT
 } ch_part_T;
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -57,12 +57,16 @@
  * - add 't' to mode()
  * - set 'filetype' to "terminal"?
  * - use win_del_lines() to make scroll-up efficient.
+ * - Make StatusLineTerm adjust UserN highlighting like StatusLineNC does, see
+ *   use of hightlight_stlnc[].
  * - implement term_setsize()
  * - add test for giving error for invalid 'termsize' value.
  * - support minimal size when 'termsize' is "rows*cols".
  * - support minimal size when 'termsize' is empty?
  * - implement "term" for job_start(): more job options when starting a
  *   terminal.
+ * - support ":term NONE" to open a terminal with a pty but not running a job
+ *   in it.  The pty can be passed to gdb to run the executable in.
  * - if the job in the terminal does not support the mouse, we can use the
  *   mouse in the Terminal window for copy/paste.
  * - when 'encoding' is not utf-8, or the job is using another encoding, setup
@@ -97,6 +101,10 @@ struct terminal_S {
     job_T	*tl_job;
     buf_T	*tl_buffer;
 
+    /* used when tl_job is NULL and only a pty was created */
+    int		tl_tty_fd;
+    char_u	*tl_tty_name;
+
     int		tl_terminal_mode;
     int		tl_channel_closed;
 
@@ -1925,6 +1933,26 @@ f_term_gettitle(typval_T *argvars, typva
 }
 
 /*
+ * "term_gettty(buf)" function
+ */
+    void
+f_term_gettty(typval_T *argvars, typval_T *rettv)
+{
+    buf_T	*buf = term_get_buf(argvars);
+    char_u	*p;
+
+    rettv->v_type = VAR_STRING;
+    if (buf == NULL)
+	return;
+    if (buf->b_term->tl_job != NULL)
+	p = buf->b_term->tl_job->jv_tty_name;
+    else
+	p = buf->b_term->tl_tty_name;
+    if (p != NULL)
+	rettv->vval.v_string = vim_strsave(p);
+}
+
+/*
  * "term_list()" function
  */
     void
@@ -2216,6 +2244,7 @@ term_and_job_init(term_T *term, int rows
     if (term->tl_winpty == NULL)
 	goto failed;
 
+    /* TODO: if the command is "NONE" only create a pty. */
     spawn_config = winpty_spawn_config_new(
 	    WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN |
 		WINPTY_SPAWN_FLAG_EXIT_AFTER_SHUTDOWN,
@@ -2359,6 +2388,7 @@ term_and_job_init(term_T *term, int rows
 
     create_vterm(term, rows, cols);
 
+    /* TODO: if the command is "NONE" only create a pty. */
     argvars[0].v_type = VAR_STRING;
     argvars[0].vval.v_string = cmd;
     setup_job_options(&opt, rows, cols);
--- a/src/testdir/test_terminal.vim
+++ b/src/testdir/test_terminal.vim
@@ -30,6 +30,12 @@ endfunc
 
 func Test_terminal_basic()
   let buf = Run_shell_in_terminal()
+  if has("unix")
+    call assert_match("^/dev/", job_info(g:job).tty)
+    call assert_match("^/dev/", term_gettty(''))
+  else
+    call assert_equal("", job_info(g:job).tty)
+  endif
   call Stop_shell_in_terminal(buf)
   call term_wait(buf)
 
--- a/src/version.c
+++ b/src/version.c
@@ -770,6 +770,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    846,
+/**/
     845,
 /**/
     844,