Permalink
Browse files

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.
  • Loading branch information...
brammool committed Aug 10, 2017
1 parent 8ab3c1d commit dd693ce28b158ff573129ee30fe5b886544a03c2
Showing with 161 additions and 38 deletions.
  1. +8 −3 runtime/doc/eval.txt
  2. +42 −22 runtime/doc/terminal.txt
  3. +13 −0 src/channel.c
  4. +3 −1 src/structs.h
  5. +53 −12 src/terminal.c
  6. +40 −0 src/testdir/test_terminal.vim
  7. +2 −0 src/version.c
View
@@ -8054,9 +8054,14 @@ term_start({cmd}, {options}) *term_start()*
connected to the terminal. When I/O is connected to the
terminal then the callback function for that part is not used.
There is one extra option:
"term_name" name to use for the buffer name, instead of
the command name.
There are two extra options:
"term_name" name to use for the buffer name, instead
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}
term_wait({buf} [, {time}]) *term_wait()*
View
@@ -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
@@ -36,7 +36,7 @@ output from the job, also while editing in any other window.
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
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*
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*
Also works with the = register to insert the result of
evaluating an expression.
@@ -62,10 +63,8 @@ the job. For example:
'termkey' N go to terminal Normal mode, see below
'termkey' CTRL-N same as CTRL-W N
*t_CTRL-\_CTRL-N*
The special key combination CTRL-\ CTRL-N can be used to prefix one Normal
mode command. This is especially useful for remote commands, when you don't
know whether Vim currently has focus in a terminal window. Note that only one
Normal mode command can be used.
The special key combination CTRL-\ CTRL-N can be used to switch to Normal
mode, just like this works in any other mode.
Size ~
@@ -76,7 +75,7 @@ See option 'termsize' for controlling the size of the terminal window.
Syntax ~
:ter[minal] [command] *:ter* *:terminal*
:[range]ter[minal] [options] [command] *:ter* *:terminal*
Open a new terminal window.
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
'shell' as the name, prefixed with a "!". If a buffer
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)".
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,
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".
Terminal Normal mode ~
Terminal-Job and Terminal-Normal mode ~
*Terminal-mode*
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
any time.
job. That includes the cursor position. Typed keys are sent to the job.
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
of the terminal window is under control of Vim, the job output is suspended.
Use CTRL-W N (or 'termkey' N) to switch to Terminal-Normal mode. Now the
contents of the terminal window is under control of Vim, the job output is
suspended. CTRL-\ CTRL-N does the same.
*E946*
In this mode you can move the cursor around with the usual Vim commands,
Visually mark text, yank text, etc. But you cannot change the contents of the
buffer. The commands that would start insert mode, such as 'i' and 'a',
return control of the window to the job. Any pending output will now be
displayed.
In Terminal mode the statusline and window title show "(Terminal)". If the
job ends while in Terminal mode this changes to "(Terminal-finished)".
In Terminal-Normal mode you can move the cursor around with the usual Vim
commands, Visually mark text, yank text, etc. But you cannot change the
contents of the buffer. The commands that would start insert mode, such as
'i' and 'a', return to Terminal-Job mode. The window will be updated to show
the contents of the terminal.
In Terminal-Normal mode the statusline and window title show "(Terminal)". If
the job ends while in Terminal-Normal mode this changes to
"(Terminal-finished)".
Unix ~
View
@@ -4419,6 +4419,19 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported)
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
else if (STRCMP(hi->hi_key, "waittime") == 0)
{
View
@@ -1685,7 +1685,8 @@ struct channel_S {
#define JO2_OUT_MSG 0x0001 /* "out_msg" */
#define JO2_ERR_MSG 0x0002 /* "err_msg" (JO_OUT_ << 1) */
#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_CB_ALL \
@@ -1743,6 +1744,7 @@ typedef struct
int jo_term_rows;
int jo_term_cols;
char_u *jo_term_name;
int jo_term_finish;
#endif
} jobopt_T;
View
@@ -36,18 +36,14 @@
* that buffer, attributes come from the scrollback buffer tl_scrollback.
*
* 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:
* :term <24x80> <close> vim notes.txt
* or use:
* :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
* - make term_getcursor() return type (none/block/bar/underline) and
* attributes (color, blink, etc.)
* - Make argument list work on MS-Windows. #1954
* - MS-Windows: no redraw for 'updatetime' #1915
* - To set BS correctly, check get_stty(); Pass the fd of the pty.
* 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_channel_closed;
int tl_finish; /* 'c' for ++close, 'o' for ++open */
#ifdef WIN3264
void *tl_winpty_config;
@@ -257,6 +254,7 @@ term_start(char_u *cmd, jobopt_T *opt)
return;
term->tl_dirty_row_end = MAX_ROW;
term->tl_cursor_visible = TRUE;
term->tl_finish = opt->jo_term_finish;
ga_init2(&term->tl_scrollback, sizeof(sb_line_T), 300);
/* Open a new window or tab. */
@@ -360,10 +358,32 @@ term_start(char_u *cmd, jobopt_T *opt)
void
ex_terminal(exarg_T *eap)
{
jobopt_T opt;
jobopt_T opt;
char_u *cmd;
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)
{
opt.jo_term_rows = eap->line1;
@@ -378,7 +398,7 @@ ex_terminal(exarg_T *eap)
}
/* 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
cleanup_vterm(term_T *term)
{
move_terminal_to_buffer(term);
if (term->tl_finish == 0)
move_terminal_to_buffer(term);
term_free_vterm(term);
set_terminal_mode(term, FALSE);
}
@@ -1415,6 +1436,7 @@ term_channel_closed(channel_T *ch)
if (term->tl_job == ch->ch_job)
{
term->tl_channel_closed = TRUE;
did_one = TRUE;
vim_free(term->tl_title);
term->tl_title = NULL;
@@ -1423,10 +1445,29 @@ term_channel_closed(channel_T *ch)
/* Unless in Terminal-Normal mode: clear the vterm. */
if (!term->tl_normal_mode)
{
int fnum = term->tl_buffer->b_fnum;
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);
did_one = TRUE;
}
if (did_one)
{
@@ -2298,7 +2339,7 @@ f_term_start(typval_T *argvars, typval_T *rettv)
&& get_job_options(&argvars[1], &opt,
JO_TIMEOUT_ALL + JO_STOPONEXIT
+ JO_EXIT_CB + JO_CLOSE_CALLBACK
+ JO2_TERM_NAME) == FAIL)
+ JO2_TERM_NAME + JO2_TERM_FINISH) == FAIL)
return;
term_start(cmd, &opt);
@@ -264,3 +264,43 @@ func Test_terminal_size()
bwipe!
call assert_equal([6, 20], size)
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
@@ -769,6 +769,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
896,
/**/
895,
/**/

0 comments on commit dd693ce

Please sign in to comment.