0
0
mirror of https://github.com/vim/vim.git synced 2025-07-26 11:04:33 -04:00

patch 8.0.1593: :qall never exits with an active terminal window

Problem:    :qall never exits with an active terminal window.
Solution:   Add a way to kill a job in a terminal window.
This commit is contained in:
Bram Moolenaar 2018-03-10 20:28:12 +01:00
parent b5b7562475
commit 25cdd9c33b
10 changed files with 225 additions and 39 deletions

View File

@ -1,4 +1,4 @@
*eval.txt* For Vim version 8.0. Last change: 2018 Mar 09 *eval.txt* For Vim version 8.0. Last change: 2018 Mar 10
VIM REFERENCE MANUAL by Bram Moolenaar VIM REFERENCE MANUAL by Bram Moolenaar
@ -2435,6 +2435,7 @@ term_gettty({buf}, [{input}]) String get the tty name of a terminal
term_list() List get the list of terminal buffers term_list() List get the list of terminal buffers
term_scrape({buf}, {row}) List get row of a terminal screen term_scrape({buf}, {row}) List get row of a terminal screen
term_sendkeys({buf}, {keys}) none send keystrokes to a terminal term_sendkeys({buf}, {keys}) none send keystrokes to a terminal
term_setkill({buf}, {how}) none set signal to stop job in terminal
term_setrestore({buf}, {command}) none set command to restore terminal term_setrestore({buf}, {command}) none set command to restore terminal
term_start({cmd}, {options}) Job open a terminal window and run a job term_start({cmd}, {options}) Job open a terminal window and run a job
term_wait({buf} [, {time}]) Number wait for screen to be updated term_wait({buf} [, {time}]) Number wait for screen to be updated
@ -8276,6 +8277,8 @@ term_getline({buf}, {row}) *term_getline()*
The first line has {row} one. When {row} is "." the cursor The first line has {row} one. When {row} is "." the cursor
line is used. When {row} is invalid an empty string is line is used. When {row} is invalid an empty string is
returned. returned.
To get attributes of each character use |term_scrape()|.
{only available when compiled with the |+terminal| feature} {only available when compiled with the |+terminal| feature}
term_getscrolled({buf}) *term_getscrolled()* term_getscrolled({buf}) *term_getscrolled()*
@ -8361,6 +8364,18 @@ term_sendkeys({buf}, {keys}) *term_sendkeys()*
means the character CTRL-X. means the character CTRL-X.
{only available when compiled with the |+terminal| feature} {only available when compiled with the |+terminal| feature}
term_setkill({buf}, {how}) *term_setkill()*
When exiting Vim or trying to close the terminal window in
another way, {how} defines whether the job in the terminal can
be stopped.
When {how} is empty (the default), the job will not be
stopped, trying to exit will result in |E947|.
Otherwise, {how} specifies what signal to send to the job.
See |job_stop()| for the values.
After sending the signal Vim will wait for up to a second to
check that the job actually stopped.
term_setrestore({buf}, {command}) *term_setrestore()* term_setrestore({buf}, {command}) *term_setrestore()*
Set the command to write in a session file to restore the job Set the command to write in a session file to restore the job
in this terminal. The line written in the session file is: > in this terminal. The line written in the session file is: >
@ -8416,6 +8431,8 @@ term_start({cmd}, {options}) *term_start()*
"hidden" do not open a window "hidden" do not open a window
"norestore" do not add the terminal window to a "norestore" do not add the terminal window to a
session file session file
"term_kill" what to do when trying to close the
terminal window, see |term_setkill()|
"term_finish" What to do when the job is finished: "term_finish" What to do when the job is finished:
"close": close any windows "close": close any windows
"open": open window if needed "open": open window if needed

View File

@ -1,4 +1,4 @@
*terminal.txt* For Vim version 8.0. Last change: 2018 Mar 09 *terminal.txt* For Vim version 8.0. Last change: 2018 Mar 10
VIM REFERENCE MANUAL by Bram Moolenaar VIM REFERENCE MANUAL by Bram Moolenaar
@ -166,6 +166,9 @@ Syntax ~
no window will be used. no window will be used.
++norestore Do not include this terminal window ++norestore Do not include this terminal window
in a session file. in a session file.
++kill={how} When trying to close the terminal
window kill the job with {how}. See
|term_setkill()| for the values.
++rows={height} Use {height} for the terminal window ++rows={height} Use {height} for the terminal window
height. If the terminal uses the full height. If the terminal uses the full
Vim height (no window above or below Vim height (no window above or below
@ -189,8 +192,12 @@ Syntax ~
If you want to use more options use the |term_start()| If you want to use more options use the |term_start()|
function. function.
When the buffer associated with the terminal is unloaded or wiped out the job When the buffer associated with the terminal is forcibly unloaded or wiped out
is killed, similar to calling `job_stop(job, "kill")` the job is killed, similar to calling `job_stop(job, "kill")` .
Closing the window normally results in |E947|. When a kill method was set
with "++kill={how}" or |term_setkill()| then closing the window will use that
way to kill or interrupt the job. For example: >
:term ++kill=term tail -f /tmp/log
So long as the job is running the window behaves like it contains a modified So long as the job is running the window behaves like it contains a modified
buffer. Trying to close the window with `CTRL-W :quit` fails. When using buffer. Trying to close the window with `CTRL-W :quit` fails. When using

View File

@ -4746,50 +4746,57 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2)
{ {
if (!(supported2 & JO2_TERM_ROWS)) if (!(supported2 & JO2_TERM_ROWS))
break; break;
opt->jo_set |= JO2_TERM_ROWS; opt->jo_set2 |= JO2_TERM_ROWS;
opt->jo_term_rows = get_tv_number(item); opt->jo_term_rows = get_tv_number(item);
} }
else if (STRCMP(hi->hi_key, "term_cols") == 0) else if (STRCMP(hi->hi_key, "term_cols") == 0)
{ {
if (!(supported2 & JO2_TERM_COLS)) if (!(supported2 & JO2_TERM_COLS))
break; break;
opt->jo_set |= JO2_TERM_COLS; opt->jo_set2 |= JO2_TERM_COLS;
opt->jo_term_cols = get_tv_number(item); opt->jo_term_cols = get_tv_number(item);
} }
else if (STRCMP(hi->hi_key, "vertical") == 0) else if (STRCMP(hi->hi_key, "vertical") == 0)
{ {
if (!(supported2 & JO2_VERTICAL)) if (!(supported2 & JO2_VERTICAL))
break; break;
opt->jo_set |= JO2_VERTICAL; opt->jo_set2 |= JO2_VERTICAL;
opt->jo_vertical = get_tv_number(item); opt->jo_vertical = get_tv_number(item);
} }
else if (STRCMP(hi->hi_key, "curwin") == 0) else if (STRCMP(hi->hi_key, "curwin") == 0)
{ {
if (!(supported2 & JO2_CURWIN)) if (!(supported2 & JO2_CURWIN))
break; break;
opt->jo_set |= JO2_CURWIN; opt->jo_set2 |= JO2_CURWIN;
opt->jo_curwin = get_tv_number(item); opt->jo_curwin = get_tv_number(item);
} }
else if (STRCMP(hi->hi_key, "hidden") == 0) else if (STRCMP(hi->hi_key, "hidden") == 0)
{ {
if (!(supported2 & JO2_HIDDEN)) if (!(supported2 & JO2_HIDDEN))
break; break;
opt->jo_set |= JO2_HIDDEN; opt->jo_set2 |= JO2_HIDDEN;
opt->jo_hidden = get_tv_number(item); opt->jo_hidden = get_tv_number(item);
} }
else if (STRCMP(hi->hi_key, "norestore") == 0) else if (STRCMP(hi->hi_key, "norestore") == 0)
{ {
if (!(supported2 & JO2_NORESTORE)) if (!(supported2 & JO2_NORESTORE))
break; break;
opt->jo_set |= JO2_NORESTORE; opt->jo_set2 |= JO2_NORESTORE;
opt->jo_term_norestore = get_tv_number(item); opt->jo_term_norestore = get_tv_number(item);
} }
else if (STRCMP(hi->hi_key, "term_kill") == 0)
{
if (!(supported2 & JO2_TERM_KILL))
break;
opt->jo_set2 |= JO2_TERM_KILL;
opt->jo_term_kill = get_tv_string_chk(item);
}
#endif #endif
else if (STRCMP(hi->hi_key, "env") == 0) else if (STRCMP(hi->hi_key, "env") == 0)
{ {
if (!(supported2 & JO2_ENV)) if (!(supported2 & JO2_ENV))
break; break;
opt->jo_set |= JO2_ENV; opt->jo_set2 |= JO2_ENV;
opt->jo_env = item->vval.v_dict; opt->jo_env = item->vval.v_dict;
++item->vval.v_dict->dv_refcount; ++item->vval.v_dict->dv_refcount;
} }
@ -4803,7 +4810,7 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2)
EMSG2(_(e_invargval), "cwd"); EMSG2(_(e_invargval), "cwd");
return FAIL; return FAIL;
} }
opt->jo_set |= JO2_CWD; opt->jo_set2 |= JO2_CWD;
} }
else if (STRCMP(hi->hi_key, "waittime") == 0) else if (STRCMP(hi->hi_key, "waittime") == 0)
{ {

View File

@ -867,6 +867,7 @@ static struct fst
{"term_list", 0, 0, f_term_list}, {"term_list", 0, 0, f_term_list},
{"term_scrape", 2, 2, f_term_scrape}, {"term_scrape", 2, 2, f_term_scrape},
{"term_sendkeys", 2, 2, f_term_sendkeys}, {"term_sendkeys", 2, 2, f_term_sendkeys},
{"term_setkill", 2, 2, f_term_setkill},
{"term_setrestore", 2, 2, f_term_setrestore}, {"term_setrestore", 2, 2, f_term_setrestore},
{"term_start", 1, 2, f_term_start}, {"term_start", 1, 2, f_term_start},
{"term_wait", 1, 2, f_term_wait}, {"term_wait", 1, 2, f_term_wait},

View File

@ -2254,7 +2254,7 @@ add_bufnum(int *bufnrs, int *bufnump, int nr)
/* /*
* Return TRUE if any buffer was changed and cannot be abandoned. * Return TRUE if any buffer was changed and cannot be abandoned.
* That changed buffer becomes the current buffer. * That changed buffer becomes the current buffer.
* When "unload" is true the current buffer is unloaded instead of making it * When "unload" is TRUE the current buffer is unloaded instead of making it
* hidden. This is used for ":q!". * hidden. This is used for ":q!".
*/ */
int int
@ -2272,6 +2272,7 @@ check_changed_any(
tabpage_T *tp; tabpage_T *tp;
win_T *wp; win_T *wp;
/* Make a list of all buffers, with the most important ones first. */
FOR_ALL_BUFFERS(buf) FOR_ALL_BUFFERS(buf)
++bufcount; ++bufcount;
@ -2284,17 +2285,19 @@ check_changed_any(
/* curbuf */ /* curbuf */
bufnrs[bufnum++] = curbuf->b_fnum; bufnrs[bufnum++] = curbuf->b_fnum;
/* buf in curtab */
/* buffers in current tab */
FOR_ALL_WINDOWS(wp) FOR_ALL_WINDOWS(wp)
if (wp->w_buffer != curbuf) if (wp->w_buffer != curbuf)
add_bufnum(bufnrs, &bufnum, wp->w_buffer->b_fnum); add_bufnum(bufnrs, &bufnum, wp->w_buffer->b_fnum);
/* buf in other tab */ /* buffers in other tabs */
FOR_ALL_TABPAGES(tp) FOR_ALL_TABPAGES(tp)
if (tp != curtab) if (tp != curtab)
for (wp = tp->tp_firstwin; wp != NULL; wp = wp->w_next) for (wp = tp->tp_firstwin; wp != NULL; wp = wp->w_next)
add_bufnum(bufnrs, &bufnum, wp->w_buffer->b_fnum); add_bufnum(bufnrs, &bufnum, wp->w_buffer->b_fnum);
/* any other buf */
/* any other buffer */
FOR_ALL_BUFFERS(buf) FOR_ALL_BUFFERS(buf)
add_bufnum(bufnrs, &bufnum, buf->b_fnum); add_bufnum(bufnrs, &bufnum, buf->b_fnum);
@ -2308,6 +2311,14 @@ check_changed_any(
bufref_T bufref; bufref_T bufref;
set_bufref(&bufref, buf); set_bufref(&bufref, buf);
#ifdef FEAT_TERMINAL
if (term_job_running(buf->b_term))
{
if (term_try_stop_job(buf) == FAIL)
break;
}
else
#endif
/* Try auto-writing the buffer. If this fails but the buffer no /* Try auto-writing the buffer. If this fails but the buffer no
* longer exists it's not changed, that's OK. */ * longer exists it's not changed, that's OK. */
if (check_changed(buf, (p_awa ? CCGD_AW : 0) if (check_changed(buf, (p_awa ? CCGD_AW : 0)
@ -2320,6 +2331,7 @@ check_changed_any(
if (i >= bufnum) if (i >= bufnum)
goto theend; goto theend;
/* Get here if "buf" cannot be abandoned. */
ret = TRUE; ret = TRUE;
exiting = FALSE; exiting = FALSE;
#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)

View File

@ -2,11 +2,11 @@
void ex_terminal(exarg_T *eap); void ex_terminal(exarg_T *eap);
int term_write_session(FILE *fd, win_T *wp); int term_write_session(FILE *fd, win_T *wp);
int term_should_restore(buf_T *buf); int term_should_restore(buf_T *buf);
void f_term_setrestore(typval_T *argvars, typval_T *rettv);
void free_terminal(buf_T *buf); void free_terminal(buf_T *buf);
void write_to_term(buf_T *buffer, char_u *msg, channel_T *channel); void write_to_term(buf_T *buffer, char_u *msg, channel_T *channel);
int term_job_running(term_T *term); int term_job_running(term_T *term);
int term_none_open(term_T *term); int term_none_open(term_T *term);
int term_try_stop_job(buf_T *buf);
int term_in_normal_mode(void); int term_in_normal_mode(void);
void term_enter_job_mode(void); void term_enter_job_mode(void);
int send_keys_to_term(term_T *term, int c, int typed); int send_keys_to_term(term_T *term, int c, int typed);
@ -41,6 +41,8 @@ void f_term_gettty(typval_T *argvars, typval_T *rettv);
void f_term_list(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_scrape(typval_T *argvars, typval_T *rettv);
void f_term_sendkeys(typval_T *argvars, typval_T *rettv); void f_term_sendkeys(typval_T *argvars, typval_T *rettv);
void f_term_setrestore(typval_T *argvars, typval_T *rettv);
void f_term_setkill(typval_T *argvars, typval_T *rettv);
void f_term_start(typval_T *argvars, typval_T *rettv); void f_term_start(typval_T *argvars, typval_T *rettv);
void f_term_wait(typval_T *argvars, typval_T *rettv); void f_term_wait(typval_T *argvars, typval_T *rettv);
void term_send_eof(channel_T *ch); void term_send_eof(channel_T *ch);

View File

@ -1707,7 +1707,8 @@ struct channel_S {
#define JO2_TERM_OPENCMD 0x0800 /* "term_opencmd" */ #define JO2_TERM_OPENCMD 0x0800 /* "term_opencmd" */
#define JO2_EOF_CHARS 0x1000 /* "eof_chars" */ #define JO2_EOF_CHARS 0x1000 /* "eof_chars" */
#define JO2_NORESTORE 0x2000 /* "norestore" */ #define JO2_NORESTORE 0x2000 /* "norestore" */
#define JO2_ALL 0x2FFF #define JO2_TERM_KILL 0x4000 /* "term_kill" */
#define JO2_ALL 0x7FFF
#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 \
@ -1775,6 +1776,7 @@ typedef struct
char_u *jo_term_opencmd; char_u *jo_term_opencmd;
int jo_term_finish; int jo_term_finish;
char_u *jo_eof_chars; char_u *jo_eof_chars;
char_u *jo_term_kill;
#endif #endif
} jobopt_T; } jobopt_T;

View File

@ -137,6 +137,7 @@ struct terminal_S {
#if defined(FEAT_SESSION) #if defined(FEAT_SESSION)
char_u *tl_command; char_u *tl_command;
#endif #endif
char_u *tl_kill;
/* last known vterm size */ /* last known vterm size */
int tl_rows; int tl_rows;
@ -535,6 +536,13 @@ term_start(typval_T *argvar, jobopt_T *opt, int without_job, int forceit)
} }
#endif #endif
if (opt->jo_term_kill != NULL)
{
char_u *p = skiptowhite(opt->jo_term_kill);
term->tl_kill = vim_strnsave(opt->jo_term_kill, p - opt->jo_term_kill);
}
/* System dependent: setup the vterm and maybe start the job in it. */ /* System dependent: setup the vterm and maybe start the job in it. */
if (argvar->v_type == VAR_STRING if (argvar->v_type == VAR_STRING
&& argvar->vval.v_string != NULL && argvar->vval.v_string != NULL
@ -611,6 +619,13 @@ ex_terminal(exarg_T *eap)
opt.jo_hidden = 1; opt.jo_hidden = 1;
else if ((int)(p - cmd) == 9 && STRNICMP(cmd, "norestore", 9) == 0) else if ((int)(p - cmd) == 9 && STRNICMP(cmd, "norestore", 9) == 0)
opt.jo_term_norestore = 1; opt.jo_term_norestore = 1;
else if ((int)(p - cmd) == 4 && STRNICMP(cmd, "kill", 4) == 0
&& ep != NULL)
{
opt.jo_set2 |= JO2_TERM_KILL;
opt.jo_term_kill = ep + 1;
p = skiptowhite(cmd);
}
else if ((int)(p - cmd) == 4 && STRNICMP(cmd, "rows", 4) == 0 else if ((int)(p - cmd) == 4 && STRNICMP(cmd, "rows", 4) == 0
&& ep != NULL && isdigit(ep[1])) && ep != NULL && isdigit(ep[1]))
{ {
@ -644,7 +659,7 @@ ex_terminal(exarg_T *eap)
if (*p) if (*p)
*p = NUL; *p = NUL;
EMSG2(_("E181: Invalid attribute: %s"), cmd); EMSG2(_("E181: Invalid attribute: %s"), cmd);
return; goto theend;
} }
cmd = skipwhite(p); cmd = skipwhite(p);
} }
@ -667,6 +682,8 @@ ex_terminal(exarg_T *eap)
argvar[1].v_type = VAR_UNKNOWN; argvar[1].v_type = VAR_UNKNOWN;
term_start(argvar, &opt, FALSE, eap->forceit); term_start(argvar, &opt, FALSE, eap->forceit);
vim_free(tofree); vim_free(tofree);
theend:
vim_free(opt.jo_eof_chars); vim_free(opt.jo_eof_chars);
} }
@ -758,6 +775,7 @@ free_terminal(buf_T *buf)
#ifdef FEAT_SESSION #ifdef FEAT_SESSION
vim_free(term->tl_command); vim_free(term->tl_command);
#endif #endif
vim_free(term->tl_kill);
vim_free(term->tl_status_text); vim_free(term->tl_status_text);
vim_free(term->tl_opencmd); vim_free(term->tl_opencmd);
vim_free(term->tl_eof_chars); vim_free(term->tl_eof_chars);
@ -1080,6 +1098,56 @@ term_none_open(term_T *term)
&& term->tl_job->jv_channel->ch_keep_open; && term->tl_job->jv_channel->ch_keep_open;
} }
/*
* Used when exiting: kill the job in "buf" if so desired.
* Return OK when the job finished.
* Return FAIL when the job is still running.
*/
int
term_try_stop_job(buf_T *buf)
{
int count;
char *how = (char *)buf->b_term->tl_kill;
#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
if ((how == NULL || *how == NUL) && (p_confirm || cmdmod.confirm))
{
char_u buff[DIALOG_MSG_SIZE];
int ret;
dialog_msg(buff, _("Kill job in \"%s\"?"), buf->b_fname);
ret = vim_dialog_yesnocancel(VIM_QUESTION, NULL, buff, 1);
if (ret == VIM_YES)
how = "kill";
else if (ret == VIM_CANCEL)
return FAIL;
}
#endif
if (how == NULL || *how == NUL)
return FAIL;
job_stop(buf->b_term->tl_job, NULL, how);
/* wait for up to a second for the job to die */
for (count = 0; count < 100; ++count)
{
/* buffer, terminal and job may be cleaned up while waiting */
if (!buf_valid(buf)
|| buf->b_term == NULL
|| buf->b_term->tl_job == NULL)
return OK;
/* call job_status() to update jv_status */
job_status(buf->b_term->tl_job);
if (buf->b_term->tl_job->jv_status >= JOB_ENDED)
return OK;
ui_delay(10L, FALSE);
mch_check_messages();
parse_queued_messages();
}
return FAIL;
}
/* /*
* Add the last line of the scrollback buffer to the buffer in the window. * Add the last line of the scrollback buffer to the buffer in the window.
*/ */
@ -2922,10 +2990,11 @@ set_terminal_default_colors(int cterm_fg, int cterm_bg)
/* /*
* Get the buffer from the first argument in "argvars". * Get the buffer from the first argument in "argvars".
* Returns NULL when the buffer is not for a terminal window. * Returns NULL when the buffer is not for a terminal window and logs a message
* with "where".
*/ */
static buf_T * static buf_T *
term_get_buf(typval_T *argvars) term_get_buf(typval_T *argvars, char *where)
{ {
buf_T *buf; buf_T *buf;
@ -2934,7 +3003,10 @@ term_get_buf(typval_T *argvars)
buf = get_buf_tv(&argvars[0], FALSE); buf = get_buf_tv(&argvars[0], FALSE);
--emsg_off; --emsg_off;
if (buf == NULL || buf->b_term == NULL) if (buf == NULL || buf->b_term == NULL)
{
ch_log(NULL, "%s: invalid buffer argument", where);
return NULL; return NULL;
}
return buf; return buf;
} }
@ -2980,7 +3052,7 @@ dump_term_color(FILE *fd, VTermColor *color)
void void
f_term_dumpwrite(typval_T *argvars, typval_T *rettv UNUSED) f_term_dumpwrite(typval_T *argvars, typval_T *rettv UNUSED)
{ {
buf_T *buf = term_get_buf(argvars); buf_T *buf = term_get_buf(argvars, "term_dumpwrite()");
term_T *term; term_T *term;
char_u *fname; char_u *fname;
int max_height = 0; int max_height = 0;
@ -3719,7 +3791,7 @@ f_term_dumpload(typval_T *argvars, typval_T *rettv)
void void
f_term_getaltscreen(typval_T *argvars, typval_T *rettv) f_term_getaltscreen(typval_T *argvars, typval_T *rettv)
{ {
buf_T *buf = term_get_buf(argvars); buf_T *buf = term_get_buf(argvars, "term_getaltscreen()");
if (buf == NULL) if (buf == NULL)
return; return;
@ -3766,7 +3838,7 @@ f_term_getattr(typval_T *argvars, typval_T *rettv)
void void
f_term_getcursor(typval_T *argvars, typval_T *rettv) f_term_getcursor(typval_T *argvars, typval_T *rettv)
{ {
buf_T *buf = term_get_buf(argvars); buf_T *buf = term_get_buf(argvars, "term_getcursor()");
term_T *term; term_T *term;
list_T *l; list_T *l;
dict_T *d; dict_T *d;
@ -3800,7 +3872,7 @@ f_term_getcursor(typval_T *argvars, typval_T *rettv)
void void
f_term_getjob(typval_T *argvars, typval_T *rettv) f_term_getjob(typval_T *argvars, typval_T *rettv)
{ {
buf_T *buf = term_get_buf(argvars); buf_T *buf = term_get_buf(argvars, "term_getjob()");
rettv->v_type = VAR_JOB; rettv->v_type = VAR_JOB;
rettv->vval.v_job = NULL; rettv->vval.v_job = NULL;
@ -3828,7 +3900,7 @@ get_row_number(typval_T *tv, term_T *term)
void void
f_term_getline(typval_T *argvars, typval_T *rettv) f_term_getline(typval_T *argvars, typval_T *rettv)
{ {
buf_T *buf = term_get_buf(argvars); buf_T *buf = term_get_buf(argvars, "term_getline()");
term_T *term; term_T *term;
int row; int row;
@ -3875,7 +3947,7 @@ f_term_getline(typval_T *argvars, typval_T *rettv)
void void
f_term_getscrolled(typval_T *argvars, typval_T *rettv) f_term_getscrolled(typval_T *argvars, typval_T *rettv)
{ {
buf_T *buf = term_get_buf(argvars); buf_T *buf = term_get_buf(argvars, "term_getscrolled()");
if (buf == NULL) if (buf == NULL)
return; return;
@ -3888,7 +3960,7 @@ f_term_getscrolled(typval_T *argvars, typval_T *rettv)
void void
f_term_getsize(typval_T *argvars, typval_T *rettv) f_term_getsize(typval_T *argvars, typval_T *rettv)
{ {
buf_T *buf = term_get_buf(argvars); buf_T *buf = term_get_buf(argvars, "term_getsize()");
list_T *l; list_T *l;
if (rettv_list_alloc(rettv) == FAIL) if (rettv_list_alloc(rettv) == FAIL)
@ -3907,7 +3979,7 @@ f_term_getsize(typval_T *argvars, typval_T *rettv)
void void
f_term_getstatus(typval_T *argvars, typval_T *rettv) f_term_getstatus(typval_T *argvars, typval_T *rettv)
{ {
buf_T *buf = term_get_buf(argvars); buf_T *buf = term_get_buf(argvars, "term_getstatus()");
term_T *term; term_T *term;
char_u val[100]; char_u val[100];
@ -3931,7 +4003,7 @@ f_term_getstatus(typval_T *argvars, typval_T *rettv)
void void
f_term_gettitle(typval_T *argvars, typval_T *rettv) f_term_gettitle(typval_T *argvars, typval_T *rettv)
{ {
buf_T *buf = term_get_buf(argvars); buf_T *buf = term_get_buf(argvars, "term_gettitle()");
rettv->v_type = VAR_STRING; rettv->v_type = VAR_STRING;
if (buf == NULL) if (buf == NULL)
@ -3947,7 +4019,7 @@ f_term_gettitle(typval_T *argvars, typval_T *rettv)
void void
f_term_gettty(typval_T *argvars, typval_T *rettv) f_term_gettty(typval_T *argvars, typval_T *rettv)
{ {
buf_T *buf = term_get_buf(argvars); buf_T *buf = term_get_buf(argvars, "term_gettty()");
char_u *p; char_u *p;
int num = 0; int num = 0;
@ -4005,7 +4077,7 @@ f_term_list(typval_T *argvars UNUSED, typval_T *rettv)
void void
f_term_scrape(typval_T *argvars, typval_T *rettv) f_term_scrape(typval_T *argvars, typval_T *rettv)
{ {
buf_T *buf = term_get_buf(argvars); buf_T *buf = term_get_buf(argvars, "term_scrape()");
VTermScreen *screen = NULL; VTermScreen *screen = NULL;
VTermPos pos; VTermPos pos;
list_T *l; list_T *l;
@ -4114,7 +4186,7 @@ f_term_scrape(typval_T *argvars, typval_T *rettv)
void void
f_term_sendkeys(typval_T *argvars, typval_T *rettv) f_term_sendkeys(typval_T *argvars, typval_T *rettv)
{ {
buf_T *buf = term_get_buf(argvars); buf_T *buf = term_get_buf(argvars, "term_sendkeys()");
char_u *msg; char_u *msg;
term_T *term; term_T *term;
@ -4143,7 +4215,7 @@ f_term_sendkeys(typval_T *argvars, typval_T *rettv)
f_term_setrestore(typval_T *argvars UNUSED, typval_T *rettv UNUSED) f_term_setrestore(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
{ {
#if defined(FEAT_SESSION) #if defined(FEAT_SESSION)
buf_T *buf = term_get_buf(argvars); buf_T *buf = term_get_buf(argvars, "term_setrestore()");
term_T *term; term_T *term;
char_u *cmd; char_u *cmd;
@ -4159,6 +4231,27 @@ f_term_setrestore(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
#endif #endif
} }
/*
* "term_setkill(buf, how)" function
*/
void
f_term_setkill(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
{
buf_T *buf = term_get_buf(argvars, "term_setkill()");
term_T *term;
char_u *how;
if (buf == NULL)
return;
term = buf->b_term;
vim_free(term->tl_kill);
how = get_tv_string_chk(&argvars[1]);
if (how != NULL)
term->tl_kill = vim_strsave(how);
else
term->tl_kill = NULL;
}
/* /*
* "term_start(command, options)" function * "term_start(command, options)" function
*/ */
@ -4177,7 +4270,7 @@ f_term_start(typval_T *argvars, typval_T *rettv)
JO2_TERM_NAME + JO2_TERM_FINISH + JO2_HIDDEN + JO2_TERM_OPENCMD JO2_TERM_NAME + JO2_TERM_FINISH + JO2_HIDDEN + JO2_TERM_OPENCMD
+ JO2_TERM_COLS + JO2_TERM_ROWS + JO2_VERTICAL + JO2_CURWIN + JO2_TERM_COLS + JO2_TERM_ROWS + JO2_VERTICAL + JO2_CURWIN
+ JO2_CWD + JO2_ENV + JO2_EOF_CHARS + JO2_CWD + JO2_ENV + JO2_EOF_CHARS
+ JO2_NORESTORE) == FAIL) + JO2_NORESTORE + JO2_TERM_KILL) == FAIL)
return; return;
if (opt.jo_vertical) if (opt.jo_vertical)
@ -4194,13 +4287,10 @@ f_term_start(typval_T *argvars, typval_T *rettv)
void void
f_term_wait(typval_T *argvars, typval_T *rettv UNUSED) f_term_wait(typval_T *argvars, typval_T *rettv UNUSED)
{ {
buf_T *buf = term_get_buf(argvars); buf_T *buf = term_get_buf(argvars, "term_wait()");
if (buf == NULL) if (buf == NULL)
{
ch_log(NULL, "term_wait(): invalid argument");
return; return;
}
if (buf->b_term->tl_job == NULL) if (buf->b_term->tl_job == NULL)
{ {
ch_log(NULL, "term_wait(): no job to wait for"); ch_log(NULL, "term_wait(): no job to wait for");

View File

@ -5,6 +5,7 @@ if !has('terminal')
endif endif
source shared.vim source shared.vim
source screendump.vim
let s:python = PythonProg() let s:python = PythonProg()
@ -839,3 +840,48 @@ func Test_terminal_response_to_control_sequence()
call delete('Xescape') call delete('Xescape')
unlet g:job unlet g:job
endfunc endfunc
" Run Vim in a terminal, then start a terminal in that Vim with a kill
" argument, check that :qall works.
func Test_terminal_qall_kill_arg()
if !CanRunVimInTerminal()
return
endif
let buf = RunVimInTerminal('', {})
" Open a terminal window and wait for the prompt to appear
call term_sendkeys(buf, ":term ++kill=kill\<CR>")
call WaitFor({-> term_getline(buf, 10) =~ '\[running]'})
call WaitFor({-> term_getline(buf, 1) !~ '^\s*$'})
" make Vim exit, it will kill the shell
call term_sendkeys(buf, "\<C-W>:qall\<CR>")
call WaitFor({-> term_getstatus(buf) == "finished"})
" close the terminal window where Vim was running
quit
endfunc
" Run Vim in a terminal, then start a terminal in that Vim with a kill
" argument, check that :qall works.
func Test_terminal_qall_kill_func()
if !CanRunVimInTerminal()
return
endif
let buf = RunVimInTerminal('', {})
" Open a terminal window and wait for the prompt to appear
call term_sendkeys(buf, ":term\<CR>")
call WaitFor({-> term_getline(buf, 10) =~ '\[running]'})
call WaitFor({-> term_getline(buf, 1) !~ '^\s*$'})
" set kill using term_setkill()
call term_sendkeys(buf, "\<C-W>:call term_setkill(bufnr('%'), 'kill')\<CR>")
" make Vim exit, it will kill the shell
call term_sendkeys(buf, "\<C-W>:qall\<CR>")
call WaitFor({-> term_getstatus(buf) == "finished"})
" close the terminal window where Vim was running
quit
endfunc

View File

@ -766,6 +766,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 */
/**/
1593,
/**/ /**/
1592, 1592,
/**/ /**/