1
0
forked from aniani/vim

patch 8.0.0896: cannot close a terminal window when the job ends

Problem:    Cannot automaticlaly close a terminal window when the job ends.
Solution:   Add the ++close argument to :term.  Add the term_finish option to
            term_start(). (Yasuhiro  Matsumoto, closes #1950)  Also add
            ++open.
This commit is contained in:
Bram Moolenaar
2017-08-10 23:15:19 +02:00
parent 8ab3c1dc6b
commit dd693ce28b
7 changed files with 160 additions and 37 deletions

View File

@@ -8054,9 +8054,14 @@ term_start({cmd}, {options}) *term_start()*
connected to the terminal. When I/O is connected to the connected to the terminal. When I/O is connected to the
terminal then the callback function for that part is not used. terminal then the callback function for that part is not used.
There is one extra option: There are two extra options:
"term_name" name to use for the buffer name, instead of "term_name" name to use for the buffer name, instead
the command name. of the command name.
"term_finish" What todo when the job is finished:
"close": close any windows
"open": open window if needed
Note that "open" can be interruptive.
See |term++close| and |term++open|.
{only available when compiled with the |+terminal| feature} {only available when compiled with the |+terminal| feature}
term_wait({buf} [, {time}]) *term_wait()* term_wait({buf} [, {time}]) *term_wait()*

View File

@@ -1,4 +1,4 @@
*terminal.txt* For Vim version 8.0. Last change: 2017 Aug 05 *terminal.txt* For Vim version 8.0. Last change: 2017 Aug 10
VIM REFERENCE MANUAL by Bram Moolenaar VIM REFERENCE MANUAL by Bram Moolenaar
@@ -36,7 +36,7 @@ output from the job, also while editing in any other window.
Typing ~ Typing ~
When the keyboard focus is in the terminal window, typed keys will be send to When the keyboard focus is in the terminal window, typed keys will be sent to
the job. This uses a pty when possible. You can click outside of the the job. This uses a pty when possible. You can click outside of the
terminal window to move keyboard focus elsewhere. terminal window to move keyboard focus elsewhere.
@@ -47,7 +47,8 @@ See |CTRL-W| for more commands.
Special in the terminal window: *CTRL-W_.* *CTRL-W_N* Special in the terminal window: *CTRL-W_.* *CTRL-W_N*
CTRL-W . send a CTRL-W to the job in the terminal CTRL-W . send a CTRL-W to the job in the terminal
CTRL-W N go to Terminal Normal mode, see |Terminal-mode| CTRL-W N go to Terminal-Normal mode, see |Terminal-mode|
CTRL-\ CTRL-N go to Terminal-Normal mode, see |Terminal-mode|
CTRL-W " {reg} paste register {reg} *CTRL-W_quote* CTRL-W " {reg} paste register {reg} *CTRL-W_quote*
Also works with the = register to insert the result of Also works with the = register to insert the result of
evaluating an expression. evaluating an expression.
@@ -62,10 +63,8 @@ the job. For example:
'termkey' N go to terminal Normal mode, see below 'termkey' N go to terminal Normal mode, see below
'termkey' CTRL-N same as CTRL-W N 'termkey' CTRL-N same as CTRL-W N
*t_CTRL-\_CTRL-N* *t_CTRL-\_CTRL-N*
The special key combination CTRL-\ CTRL-N can be used to prefix one Normal The special key combination CTRL-\ CTRL-N can be used to switch to Normal
mode command. This is especially useful for remote commands, when you don't mode, just like this works in any other mode.
know whether Vim currently has focus in a terminal window. Note that only one
Normal mode command can be used.
Size ~ Size ~
@@ -76,7 +75,7 @@ See option 'termsize' for controlling the size of the terminal window.
Syntax ~ Syntax ~
:ter[minal] [command] *:ter* *:terminal* :[range]ter[minal] [options] [command] *:ter* *:terminal*
Open a new terminal window. Open a new terminal window.
If [command] is provided run it as a job and connect If [command] is provided run it as a job and connect
@@ -86,9 +85,27 @@ Syntax ~
A new buffer will be created, using [command] or A new buffer will be created, using [command] or
'shell' as the name, prefixed with a "!". If a buffer 'shell' as the name, prefixed with a "!". If a buffer
by this name already exists a number is added in by this name already exists a number is added in
parenthesis. E.g. if "gdb" exists the second terminal parentheses. E.g. if "gdb" exists the second terminal
buffer will use "!gdb (1)". buffer will use "!gdb (1)".
If [range] is given it is used for the terminal size.
One number specifies the number of rows. Unless the
"vertical" modifier is used, then it is the number of
columns.
Two comma separated numbers are used as "rows,cols".
E.g. `:24,80gdb` opens a terminal with 24 rows and 80
columns. However, if the terminal window spans the
Vim window with, there is no vertical split, the Vim
window width is used.
*term++close* *term++open*
Supported [options] are:
++close The terminal window will close
automatically when the job terminates.
++open When the job terminates and no window
show it, a window will be opened.
Note that this can be interruptive.
When the buffer associated with the terminal is wiped out the job is killed, When the buffer associated with the terminal is wiped out the job is killed,
similar to calling `job_stop(job, "kill")` similar to calling `job_stop(job, "kill")`
@@ -133,23 +150,26 @@ terminal. |term_setsize()| can be used only when in the first or second mode,
not when 'termsize' is "rowsXcols". not when 'termsize' is "rowsXcols".
Terminal Normal mode ~ Terminal-Job and Terminal-Normal mode ~
*Terminal-mode* *Terminal-mode*
When the job is running the contents of the terminal is under control of the When the job is running the contents of the terminal is under control of the
job. That includes the cursor position. The terminal contents can change at job. That includes the cursor position. Typed keys are sent to the job.
any time. The terminal contents can change at any time. This is called Terminal-Job
mode.
Use CTRL-W N (or 'termkey' N) to go to Terminal Normal mode. Now the contents Use CTRL-W N (or 'termkey' N) to switch to Terminal-Normal mode. Now the
of the terminal window is under control of Vim, the job output is suspended. contents of the terminal window is under control of Vim, the job output is
suspended. CTRL-\ CTRL-N does the same.
*E946* *E946*
In this mode you can move the cursor around with the usual Vim commands, In Terminal-Normal mode you can move the cursor around with the usual Vim
Visually mark text, yank text, etc. But you cannot change the contents of the commands, Visually mark text, yank text, etc. But you cannot change the
buffer. The commands that would start insert mode, such as 'i' and 'a', contents of the buffer. The commands that would start insert mode, such as
return control of the window to the job. Any pending output will now be 'i' and 'a', return to Terminal-Job mode. The window will be updated to show
displayed. the contents of the terminal.
In Terminal mode the statusline and window title show "(Terminal)". If the In Terminal-Normal mode the statusline and window title show "(Terminal)". If
job ends while in Terminal mode this changes to "(Terminal-finished)". the job ends while in Terminal-Normal mode this changes to
"(Terminal-finished)".
Unix ~ Unix ~

View File

@@ -4419,6 +4419,19 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported)
return FAIL; return FAIL;
} }
} }
else if (STRCMP(hi->hi_key, "term_finish") == 0)
{
if (!(supported & JO2_TERM_FINISH))
break;
val = get_tv_string(item);
if (STRCMP(val, "open") != 0 && STRCMP(val, "close") != 0)
{
EMSG2(_(e_invarg2), "drop");
return FAIL;
}
opt->jo_set2 |= JO2_TERM_FINISH;
opt->jo_term_finish = *val;
}
#endif #endif
else if (STRCMP(hi->hi_key, "waittime") == 0) else if (STRCMP(hi->hi_key, "waittime") == 0)
{ {

View File

@@ -1685,7 +1685,8 @@ struct channel_S {
#define JO2_OUT_MSG 0x0001 /* "out_msg" */ #define JO2_OUT_MSG 0x0001 /* "out_msg" */
#define JO2_ERR_MSG 0x0002 /* "err_msg" (JO_OUT_ << 1) */ #define JO2_ERR_MSG 0x0002 /* "err_msg" (JO_OUT_ << 1) */
#define JO2_TERM_NAME 0x0004 /* "term_name" */ #define JO2_TERM_NAME 0x0004 /* "term_name" */
#define JO2_ALL 0x0007 #define JO2_TERM_FINISH 0x0008 /* "term_finish" */
#define JO2_ALL 0x000F
#define JO_MODE_ALL (JO_MODE + JO_IN_MODE + JO_OUT_MODE + JO_ERR_MODE) #define JO_MODE_ALL (JO_MODE + JO_IN_MODE + JO_OUT_MODE + JO_ERR_MODE)
#define JO_CB_ALL \ #define JO_CB_ALL \
@@ -1743,6 +1744,7 @@ typedef struct
int jo_term_rows; int jo_term_rows;
int jo_term_cols; int jo_term_cols;
char_u *jo_term_name; char_u *jo_term_name;
int jo_term_finish;
#endif #endif
} jobopt_T; } jobopt_T;

View File

@@ -36,18 +36,14 @@
* that buffer, attributes come from the scrollback buffer tl_scrollback. * that buffer, attributes come from the scrollback buffer tl_scrollback.
* *
* TODO: * TODO:
* - When the job ends:
* - Need an option or argument to drop the window+buffer right away, to be
* used for a shell or Vim. 'termfinish'; "close", "open" (open window when
* job finishes).
* patch by Yasuhiro: #1950
* - add option values to the command: * - add option values to the command:
* :term <24x80> <close> vim notes.txt
* or use:
* :term ++24x80 ++close vim notes.txt * :term ++24x80 ++close vim notes.txt
* - When using term_finish "open" have a way to specify how the window is to
* be opened. E.g. term_opencmd "10split buffer %d".
* - support different cursor shapes, colors and attributes * - support different cursor shapes, colors and attributes
* - make term_getcursor() return type (none/block/bar/underline) and * - make term_getcursor() return type (none/block/bar/underline) and
* attributes (color, blink, etc.) * attributes (color, blink, etc.)
* - Make argument list work on MS-Windows. #1954
* - MS-Windows: no redraw for 'updatetime' #1915 * - MS-Windows: no redraw for 'updatetime' #1915
* - To set BS correctly, check get_stty(); Pass the fd of the pty. * - To set BS correctly, check get_stty(); Pass the fd of the pty.
* For the GUI fill termios with default values, perhaps like pangoterm: * For the GUI fill termios with default values, perhaps like pangoterm:
@@ -124,6 +120,7 @@ struct terminal_S {
int tl_normal_mode; /* TRUE: Terminal-Normal mode */ int tl_normal_mode; /* TRUE: Terminal-Normal mode */
int tl_channel_closed; int tl_channel_closed;
int tl_finish; /* 'c' for ++close, 'o' for ++open */
#ifdef WIN3264 #ifdef WIN3264
void *tl_winpty_config; void *tl_winpty_config;
@@ -257,6 +254,7 @@ term_start(char_u *cmd, jobopt_T *opt)
return; return;
term->tl_dirty_row_end = MAX_ROW; term->tl_dirty_row_end = MAX_ROW;
term->tl_cursor_visible = TRUE; term->tl_cursor_visible = TRUE;
term->tl_finish = opt->jo_term_finish;
ga_init2(&term->tl_scrollback, sizeof(sb_line_T), 300); ga_init2(&term->tl_scrollback, sizeof(sb_line_T), 300);
/* Open a new window or tab. */ /* Open a new window or tab. */
@@ -360,10 +358,32 @@ term_start(char_u *cmd, jobopt_T *opt)
void void
ex_terminal(exarg_T *eap) ex_terminal(exarg_T *eap)
{ {
jobopt_T opt; jobopt_T opt;
char_u *cmd;
init_job_options(&opt); init_job_options(&opt);
cmd = eap->arg;
while (*cmd && *cmd == '+' && *(cmd + 1) == '+')
{
char_u *p;
cmd += 2;
p = skiptowhite(cmd);
if ((int)(p - cmd) == 5 && STRNICMP(cmd, "close", 5) == 0)
opt.jo_term_finish = 'c';
else if ((int)(p - cmd) == 4 && STRNICMP(cmd, "open", 4) == 0)
opt.jo_term_finish = 'o';
else
{
if (*p)
*p = NUL;
EMSG2(_("E181: Invalid attribute: %s"), cmd);
return;
}
cmd = skipwhite(p);
}
if (eap->addr_count == 2) if (eap->addr_count == 2)
{ {
opt.jo_term_rows = eap->line1; opt.jo_term_rows = eap->line1;
@@ -378,7 +398,7 @@ ex_terminal(exarg_T *eap)
} }
/* TODO: get more options from before the command */ /* TODO: get more options from before the command */
term_start(eap->arg, &opt); term_start(cmd, &opt);
} }
/* /*
@@ -846,7 +866,8 @@ set_terminal_mode(term_T *term, int normal_mode)
static void static void
cleanup_vterm(term_T *term) cleanup_vterm(term_T *term)
{ {
move_terminal_to_buffer(term); if (term->tl_finish == 0)
move_terminal_to_buffer(term);
term_free_vterm(term); term_free_vterm(term);
set_terminal_mode(term, FALSE); set_terminal_mode(term, FALSE);
} }
@@ -1415,6 +1436,7 @@ term_channel_closed(channel_T *ch)
if (term->tl_job == ch->ch_job) if (term->tl_job == ch->ch_job)
{ {
term->tl_channel_closed = TRUE; term->tl_channel_closed = TRUE;
did_one = TRUE;
vim_free(term->tl_title); vim_free(term->tl_title);
term->tl_title = NULL; term->tl_title = NULL;
@@ -1423,10 +1445,29 @@ term_channel_closed(channel_T *ch)
/* Unless in Terminal-Normal mode: clear the vterm. */ /* Unless in Terminal-Normal mode: clear the vterm. */
if (!term->tl_normal_mode) if (!term->tl_normal_mode)
{
int fnum = term->tl_buffer->b_fnum;
cleanup_vterm(term); cleanup_vterm(term);
if (term->tl_finish == 'c')
{
/* ++close or term_finish == "close" */
curbuf = term->tl_buffer;
do_bufdel(DOBUF_WIPE, (char_u *)"", 1, fnum, fnum, FALSE);
break;
}
if (term->tl_finish == 'o' && term->tl_buffer->b_nwindows == 0)
{
char buf[50];
/* TODO: use term_opencmd */
vim_snprintf(buf, sizeof(buf), "botright sbuf %d", fnum);
do_cmdline_cmd((char_u *)buf);
}
}
redraw_buf_and_status_later(term->tl_buffer, NOT_VALID); redraw_buf_and_status_later(term->tl_buffer, NOT_VALID);
did_one = TRUE;
} }
if (did_one) if (did_one)
{ {
@@ -2298,7 +2339,7 @@ f_term_start(typval_T *argvars, typval_T *rettv)
&& get_job_options(&argvars[1], &opt, && get_job_options(&argvars[1], &opt,
JO_TIMEOUT_ALL + JO_STOPONEXIT JO_TIMEOUT_ALL + JO_STOPONEXIT
+ JO_EXIT_CB + JO_CLOSE_CALLBACK + JO_EXIT_CB + JO_CLOSE_CALLBACK
+ JO2_TERM_NAME) == FAIL) + JO2_TERM_NAME + JO2_TERM_FINISH) == FAIL)
return; return;
term_start(cmd, &opt); term_start(cmd, &opt);

View File

@@ -264,3 +264,43 @@ func Test_terminal_size()
bwipe! bwipe!
call assert_equal([6, 20], size) call assert_equal([6, 20], size)
endfunc endfunc
func Test_finish_close()
call assert_equal(1, winnr('$'))
" TODO: use something that takes much less than a whole second
if has('win32')
let cmd = $windir . '\system32\timeout.exe 1'
else
let cmd = 'sleep 1'
endif
exe 'terminal ++close ' . cmd
let buf = bufnr('')
call assert_equal(2, winnr('$'))
wincmd p
sleep 1200 msec
call assert_equal(1, winnr('$'))
call term_start(cmd, {'term_finish': 'close'})
call assert_equal(2, winnr('$'))
let buf = bufnr('')
wincmd p
sleep 1200 msec
call assert_equal(1, winnr('$'))
exe 'terminal ++open ' . cmd
let buf = bufnr('')
close
sleep 1200 msec
call assert_equal(2, winnr('$'))
bwipe
call term_start(cmd, {'term_finish': 'open'})
let buf = bufnr('')
close
sleep 1200 msec
call assert_equal(2, winnr('$'))
bwipe
endfunc

View File

@@ -769,6 +769,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
896,
/**/ /**/
895, 895,
/**/ /**/