Skip to content
Permalink
Browse files

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.
  • Loading branch information
brammool committed Mar 10, 2018
1 parent b5b7562 commit 25cdd9c33b21ddbd31321c075873bb225450d2d2
Showing with 225 additions and 39 deletions.
  1. +18 −1 runtime/doc/eval.txt
  2. +10 −3 runtime/doc/terminal.txt
  3. +15 −8 src/channel.c
  4. +1 −0 src/evalfunc.c
  5. +16 −4 src/ex_cmds2.c
  6. +3 −1 src/proto/terminal.pro
  7. +3 −1 src/structs.h
  8. +111 −21 src/terminal.c
  9. +46 −0 src/testdir/test_terminal.vim
  10. +2 −0 src/version.c
@@ -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
@@ -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_scrape({buf}, {row}) List get row of a terminal screen
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_start({cmd}, {options}) Job open a terminal window and run a job
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
line is used. When {row} is invalid an empty string is
returned.

To get attributes of each character use |term_scrape()|.
{only available when compiled with the |+terminal| feature}

term_getscrolled({buf}) *term_getscrolled()*
@@ -8361,6 +8364,18 @@ term_sendkeys({buf}, {keys}) *term_sendkeys()*
means the character CTRL-X.
{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()*
Set the command to write in a session file to restore the job
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
"norestore" do not add the terminal window to a
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:
"close": close any windows
"open": open window if needed
@@ -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
@@ -166,6 +166,9 @@ Syntax ~
no window will be used.
++norestore Do not include this terminal window
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
height. If the terminal uses the full
Vim height (no window above or below
@@ -189,8 +192,12 @@ Syntax ~
If you want to use more options use the |term_start()|
function.

When the buffer associated with the terminal is unloaded or wiped out the job
is killed, similar to calling `job_stop(job, "kill")`
When the buffer associated with the terminal is forcibly unloaded or wiped out
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
buffer. Trying to close the window with `CTRL-W :quit` fails. When using
@@ -4746,50 +4746,57 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2)
{
if (!(supported2 & JO2_TERM_ROWS))
break;
opt->jo_set |= JO2_TERM_ROWS;
opt->jo_set2 |= JO2_TERM_ROWS;
opt->jo_term_rows = get_tv_number(item);
}
else if (STRCMP(hi->hi_key, "term_cols") == 0)
{
if (!(supported2 & JO2_TERM_COLS))
break;
opt->jo_set |= JO2_TERM_COLS;
opt->jo_set2 |= JO2_TERM_COLS;
opt->jo_term_cols = get_tv_number(item);
}
else if (STRCMP(hi->hi_key, "vertical") == 0)
{
if (!(supported2 & JO2_VERTICAL))
break;
opt->jo_set |= JO2_VERTICAL;
opt->jo_set2 |= JO2_VERTICAL;
opt->jo_vertical = get_tv_number(item);
}
else if (STRCMP(hi->hi_key, "curwin") == 0)
{
if (!(supported2 & JO2_CURWIN))
break;
opt->jo_set |= JO2_CURWIN;
opt->jo_set2 |= JO2_CURWIN;
opt->jo_curwin = get_tv_number(item);
}
else if (STRCMP(hi->hi_key, "hidden") == 0)
{
if (!(supported2 & JO2_HIDDEN))
break;
opt->jo_set |= JO2_HIDDEN;
opt->jo_set2 |= JO2_HIDDEN;
opt->jo_hidden = get_tv_number(item);
}
else if (STRCMP(hi->hi_key, "norestore") == 0)
{
if (!(supported2 & JO2_NORESTORE))
break;
opt->jo_set |= JO2_NORESTORE;
opt->jo_set2 |= JO2_NORESTORE;
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
else if (STRCMP(hi->hi_key, "env") == 0)
{
if (!(supported2 & JO2_ENV))
break;
opt->jo_set |= JO2_ENV;
opt->jo_set2 |= JO2_ENV;
opt->jo_env = item->vval.v_dict;
++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");
return FAIL;
}
opt->jo_set |= JO2_CWD;
opt->jo_set2 |= JO2_CWD;
}
else if (STRCMP(hi->hi_key, "waittime") == 0)
{
@@ -867,6 +867,7 @@ static struct fst
{"term_list", 0, 0, f_term_list},
{"term_scrape", 2, 2, f_term_scrape},
{"term_sendkeys", 2, 2, f_term_sendkeys},
{"term_setkill", 2, 2, f_term_setkill},
{"term_setrestore", 2, 2, f_term_setrestore},
{"term_start", 1, 2, f_term_start},
{"term_wait", 1, 2, f_term_wait},
@@ -2254,7 +2254,7 @@ add_bufnum(int *bufnrs, int *bufnump, int nr)
/*
* Return TRUE if any buffer was changed and cannot be abandoned.
* 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!".
*/
int
@@ -2272,6 +2272,7 @@ check_changed_any(
tabpage_T *tp;
win_T *wp;

/* Make a list of all buffers, with the most important ones first. */
FOR_ALL_BUFFERS(buf)
++bufcount;

@@ -2284,17 +2285,19 @@ check_changed_any(

/* curbuf */
bufnrs[bufnum++] = curbuf->b_fnum;
/* buf in curtab */

/* buffers in current tab */
FOR_ALL_WINDOWS(wp)
if (wp->w_buffer != curbuf)
add_bufnum(bufnrs, &bufnum, wp->w_buffer->b_fnum);

/* buf in other tab */
/* buffers in other tabs */
FOR_ALL_TABPAGES(tp)
if (tp != curtab)
for (wp = tp->tp_firstwin; wp != NULL; wp = wp->w_next)
add_bufnum(bufnrs, &bufnum, wp->w_buffer->b_fnum);
/* any other buf */

/* any other buffer */
FOR_ALL_BUFFERS(buf)
add_bufnum(bufnrs, &bufnum, buf->b_fnum);

@@ -2308,6 +2311,14 @@ check_changed_any(
bufref_T bufref;

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
* longer exists it's not changed, that's OK. */
if (check_changed(buf, (p_awa ? CCGD_AW : 0)
@@ -2320,6 +2331,7 @@ check_changed_any(
if (i >= bufnum)
goto theend;

/* Get here if "buf" cannot be abandoned. */
ret = TRUE;
exiting = FALSE;
#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
@@ -2,11 +2,11 @@
void ex_terminal(exarg_T *eap);
int term_write_session(FILE *fd, win_T *wp);
int term_should_restore(buf_T *buf);
void f_term_setrestore(typval_T *argvars, typval_T *rettv);
void free_terminal(buf_T *buf);
void write_to_term(buf_T *buffer, char_u *msg, channel_T *channel);
int term_job_running(term_T *term);
int term_none_open(term_T *term);
int term_try_stop_job(buf_T *buf);
int term_in_normal_mode(void);
void term_enter_job_mode(void);
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_scrape(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_wait(typval_T *argvars, typval_T *rettv);
void term_send_eof(channel_T *ch);
@@ -1707,7 +1707,8 @@ struct channel_S {
#define JO2_TERM_OPENCMD 0x0800 /* "term_opencmd" */
#define JO2_EOF_CHARS 0x1000 /* "eof_chars" */
#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_CB_ALL \
@@ -1775,6 +1776,7 @@ typedef struct
char_u *jo_term_opencmd;
int jo_term_finish;
char_u *jo_eof_chars;
char_u *jo_term_kill;
#endif
} jobopt_T;

0 comments on commit 25cdd9c

Please sign in to comment.
You can’t perform that action at this time.