To: vim_dev@googlegroups.com Subject: Patch 8.0.1593 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.0.1593 Problem: :qall never exits with an active terminal window. Solution: Add a way to kill a job in a terminal window. Files: src/ex_cmds2.c, src/terminal.c, src/proto/terminal.pro, src/structs.h, src/channel.c, src/evalfunc.c, src/testdir/test_terminal.vim, runtime/doc/terminal.txt, runtime/doc/eval.txt *** ../vim-8.0.1592/src/ex_cmds2.c 2018-03-04 20:14:08.244064367 +0100 --- src/ex_cmds2.c 2018-03-10 17:09:46.692310947 +0100 *************** *** 2254,2260 **** /* * 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 * hidden. This is used for ":q!". */ int --- 2254,2260 ---- /* * 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 * hidden. This is used for ":q!". */ int *************** *** 2272,2277 **** --- 2272,2278 ---- tabpage_T *tp; win_T *wp; + /* Make a list of all buffers, with the most important ones first. */ FOR_ALL_BUFFERS(buf) ++bufcount; *************** *** 2284,2300 **** /* curbuf */ bufnrs[bufnum++] = curbuf->b_fnum; ! /* buf in curtab */ FOR_ALL_WINDOWS(wp) if (wp->w_buffer != curbuf) add_bufnum(bufnrs, &bufnum, wp->w_buffer->b_fnum); ! /* buf in other tab */ 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 */ FOR_ALL_BUFFERS(buf) add_bufnum(bufnrs, &bufnum, buf->b_fnum); --- 2285,2303 ---- /* curbuf */ bufnrs[bufnum++] = curbuf->b_fnum; ! ! /* buffers in current tab */ FOR_ALL_WINDOWS(wp) if (wp->w_buffer != curbuf) add_bufnum(bufnrs, &bufnum, wp->w_buffer->b_fnum); ! /* 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 buffer */ FOR_ALL_BUFFERS(buf) add_bufnum(bufnrs, &bufnum, buf->b_fnum); *************** *** 2308,2313 **** --- 2311,2324 ---- 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,2325 **** --- 2331,2337 ---- 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) *** ../vim-8.0.1592/src/terminal.c 2018-03-09 21:33:29.244607400 +0100 --- src/terminal.c 2018-03-10 19:25:43.386676907 +0100 *************** *** 137,142 **** --- 137,143 ---- #if defined(FEAT_SESSION) char_u *tl_command; #endif + char_u *tl_kill; /* last known vterm size */ int tl_rows; *************** *** 535,540 **** --- 536,548 ---- } #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. */ if (argvar->v_type == VAR_STRING && argvar->vval.v_string != NULL *************** *** 611,616 **** --- 619,631 ---- opt.jo_hidden = 1; else if ((int)(p - cmd) == 9 && STRNICMP(cmd, "norestore", 9) == 0) 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 && ep != NULL && isdigit(ep[1])) { *************** *** 644,650 **** if (*p) *p = NUL; EMSG2(_("E181: Invalid attribute: %s"), cmd); ! return; } cmd = skipwhite(p); } --- 659,665 ---- if (*p) *p = NUL; EMSG2(_("E181: Invalid attribute: %s"), cmd); ! goto theend; } cmd = skipwhite(p); } *************** *** 667,672 **** --- 682,689 ---- argvar[1].v_type = VAR_UNKNOWN; term_start(argvar, &opt, FALSE, eap->forceit); vim_free(tofree); + + theend: vim_free(opt.jo_eof_chars); } *************** *** 758,763 **** --- 775,781 ---- #ifdef FEAT_SESSION vim_free(term->tl_command); #endif + vim_free(term->tl_kill); vim_free(term->tl_status_text); vim_free(term->tl_opencmd); vim_free(term->tl_eof_chars); *************** *** 1081,1086 **** --- 1099,1154 ---- } /* + * 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. */ static void *************** *** 2922,2931 **** /* * Get the buffer from the first argument in "argvars". ! * Returns NULL when the buffer is not for a terminal window. */ static buf_T * ! term_get_buf(typval_T *argvars) { buf_T *buf; --- 2990,3000 ---- /* * Get the buffer from the first argument in "argvars". ! * Returns NULL when the buffer is not for a terminal window and logs a message ! * with "where". */ static buf_T * ! term_get_buf(typval_T *argvars, char *where) { buf_T *buf; *************** *** 2934,2940 **** --- 3003,3012 ---- buf = get_buf_tv(&argvars[0], FALSE); --emsg_off; if (buf == NULL || buf->b_term == NULL) + { + ch_log(NULL, "%s: invalid buffer argument", where); return NULL; + } return buf; } *************** *** 2980,2986 **** void f_term_dumpwrite(typval_T *argvars, typval_T *rettv UNUSED) { ! buf_T *buf = term_get_buf(argvars); term_T *term; char_u *fname; int max_height = 0; --- 3052,3058 ---- void f_term_dumpwrite(typval_T *argvars, typval_T *rettv UNUSED) { ! buf_T *buf = term_get_buf(argvars, "term_dumpwrite()"); term_T *term; char_u *fname; int max_height = 0; *************** *** 3719,3725 **** void f_term_getaltscreen(typval_T *argvars, typval_T *rettv) { ! buf_T *buf = term_get_buf(argvars); if (buf == NULL) return; --- 3791,3797 ---- void f_term_getaltscreen(typval_T *argvars, typval_T *rettv) { ! buf_T *buf = term_get_buf(argvars, "term_getaltscreen()"); if (buf == NULL) return; *************** *** 3766,3772 **** void f_term_getcursor(typval_T *argvars, typval_T *rettv) { ! buf_T *buf = term_get_buf(argvars); term_T *term; list_T *l; dict_T *d; --- 3838,3844 ---- void f_term_getcursor(typval_T *argvars, typval_T *rettv) { ! buf_T *buf = term_get_buf(argvars, "term_getcursor()"); term_T *term; list_T *l; dict_T *d; *************** *** 3800,3806 **** void f_term_getjob(typval_T *argvars, typval_T *rettv) { ! buf_T *buf = term_get_buf(argvars); rettv->v_type = VAR_JOB; rettv->vval.v_job = NULL; --- 3872,3878 ---- void f_term_getjob(typval_T *argvars, typval_T *rettv) { ! buf_T *buf = term_get_buf(argvars, "term_getjob()"); rettv->v_type = VAR_JOB; rettv->vval.v_job = NULL; *************** *** 3828,3834 **** void f_term_getline(typval_T *argvars, typval_T *rettv) { ! buf_T *buf = term_get_buf(argvars); term_T *term; int row; --- 3900,3906 ---- void f_term_getline(typval_T *argvars, typval_T *rettv) { ! buf_T *buf = term_get_buf(argvars, "term_getline()"); term_T *term; int row; *************** *** 3875,3881 **** void f_term_getscrolled(typval_T *argvars, typval_T *rettv) { ! buf_T *buf = term_get_buf(argvars); if (buf == NULL) return; --- 3947,3953 ---- void f_term_getscrolled(typval_T *argvars, typval_T *rettv) { ! buf_T *buf = term_get_buf(argvars, "term_getscrolled()"); if (buf == NULL) return; *************** *** 3888,3894 **** void f_term_getsize(typval_T *argvars, typval_T *rettv) { ! buf_T *buf = term_get_buf(argvars); list_T *l; if (rettv_list_alloc(rettv) == FAIL) --- 3960,3966 ---- void f_term_getsize(typval_T *argvars, typval_T *rettv) { ! buf_T *buf = term_get_buf(argvars, "term_getsize()"); list_T *l; if (rettv_list_alloc(rettv) == FAIL) *************** *** 3907,3913 **** void f_term_getstatus(typval_T *argvars, typval_T *rettv) { ! buf_T *buf = term_get_buf(argvars); term_T *term; char_u val[100]; --- 3979,3985 ---- void f_term_getstatus(typval_T *argvars, typval_T *rettv) { ! buf_T *buf = term_get_buf(argvars, "term_getstatus()"); term_T *term; char_u val[100]; *************** *** 3931,3937 **** void f_term_gettitle(typval_T *argvars, typval_T *rettv) { ! buf_T *buf = term_get_buf(argvars); rettv->v_type = VAR_STRING; if (buf == NULL) --- 4003,4009 ---- void f_term_gettitle(typval_T *argvars, typval_T *rettv) { ! buf_T *buf = term_get_buf(argvars, "term_gettitle()"); rettv->v_type = VAR_STRING; if (buf == NULL) *************** *** 3947,3953 **** void f_term_gettty(typval_T *argvars, typval_T *rettv) { ! buf_T *buf = term_get_buf(argvars); char_u *p; int num = 0; --- 4019,4025 ---- void f_term_gettty(typval_T *argvars, typval_T *rettv) { ! buf_T *buf = term_get_buf(argvars, "term_gettty()"); char_u *p; int num = 0; *************** *** 4005,4011 **** void f_term_scrape(typval_T *argvars, typval_T *rettv) { ! buf_T *buf = term_get_buf(argvars); VTermScreen *screen = NULL; VTermPos pos; list_T *l; --- 4077,4083 ---- void f_term_scrape(typval_T *argvars, typval_T *rettv) { ! buf_T *buf = term_get_buf(argvars, "term_scrape()"); VTermScreen *screen = NULL; VTermPos pos; list_T *l; *************** *** 4114,4120 **** void f_term_sendkeys(typval_T *argvars, typval_T *rettv) { ! buf_T *buf = term_get_buf(argvars); char_u *msg; term_T *term; --- 4186,4192 ---- void f_term_sendkeys(typval_T *argvars, typval_T *rettv) { ! buf_T *buf = term_get_buf(argvars, "term_sendkeys()"); char_u *msg; term_T *term; *************** *** 4143,4149 **** f_term_setrestore(typval_T *argvars UNUSED, typval_T *rettv UNUSED) { #if defined(FEAT_SESSION) ! buf_T *buf = term_get_buf(argvars); term_T *term; char_u *cmd; --- 4215,4221 ---- f_term_setrestore(typval_T *argvars UNUSED, typval_T *rettv UNUSED) { #if defined(FEAT_SESSION) ! buf_T *buf = term_get_buf(argvars, "term_setrestore()"); term_T *term; char_u *cmd; *************** *** 4160,4165 **** --- 4232,4258 ---- } /* + * "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 */ void *************** *** 4177,4183 **** JO2_TERM_NAME + JO2_TERM_FINISH + JO2_HIDDEN + JO2_TERM_OPENCMD + JO2_TERM_COLS + JO2_TERM_ROWS + JO2_VERTICAL + JO2_CURWIN + JO2_CWD + JO2_ENV + JO2_EOF_CHARS ! + JO2_NORESTORE) == FAIL) return; if (opt.jo_vertical) --- 4270,4276 ---- JO2_TERM_NAME + JO2_TERM_FINISH + JO2_HIDDEN + JO2_TERM_OPENCMD + JO2_TERM_COLS + JO2_TERM_ROWS + JO2_VERTICAL + JO2_CURWIN + JO2_CWD + JO2_ENV + JO2_EOF_CHARS ! + JO2_NORESTORE + JO2_TERM_KILL) == FAIL) return; if (opt.jo_vertical) *************** *** 4194,4206 **** void f_term_wait(typval_T *argvars, typval_T *rettv UNUSED) { ! buf_T *buf = term_get_buf(argvars); if (buf == NULL) - { - ch_log(NULL, "term_wait(): invalid argument"); return; - } if (buf->b_term->tl_job == NULL) { ch_log(NULL, "term_wait(): no job to wait for"); --- 4287,4296 ---- void f_term_wait(typval_T *argvars, typval_T *rettv UNUSED) { ! buf_T *buf = term_get_buf(argvars, "term_wait()"); if (buf == NULL) return; if (buf->b_term->tl_job == NULL) { ch_log(NULL, "term_wait(): no job to wait for"); *** ../vim-8.0.1592/src/proto/terminal.pro 2018-03-09 21:33:29.244607400 +0100 --- src/proto/terminal.pro 2018-03-10 16:42:12.906432147 +0100 *************** *** 2,12 **** 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_in_normal_mode(void); void term_enter_job_mode(void); int send_keys_to_term(term_T *term, int c, int typed); --- 2,12 ---- void ex_terminal(exarg_T *eap); int term_write_session(FILE *fd, win_T *wp); int term_should_restore(buf_T *buf); 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,46 **** --- 41,48 ---- 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); *** ../vim-8.0.1592/src/structs.h 2018-03-09 21:33:29.248607375 +0100 --- src/structs.h 2018-03-10 16:26:00.952400695 +0100 *************** *** 1707,1713 **** #define JO2_TERM_OPENCMD 0x0800 /* "term_opencmd" */ #define JO2_EOF_CHARS 0x1000 /* "eof_chars" */ #define JO2_NORESTORE 0x2000 /* "norestore" */ ! #define JO2_ALL 0x2FFF #define JO_MODE_ALL (JO_MODE + JO_IN_MODE + JO_OUT_MODE + JO_ERR_MODE) #define JO_CB_ALL \ --- 1707,1714 ---- #define JO2_TERM_OPENCMD 0x0800 /* "term_opencmd" */ #define JO2_EOF_CHARS 0x1000 /* "eof_chars" */ #define JO2_NORESTORE 0x2000 /* "norestore" */ ! #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,1780 **** --- 1776,1782 ---- char_u *jo_term_opencmd; int jo_term_finish; char_u *jo_eof_chars; + char_u *jo_term_kill; #endif } jobopt_T; *** ../vim-8.0.1592/src/channel.c 2018-03-09 21:33:29.248607375 +0100 --- src/channel.c 2018-03-10 16:33:37.405595372 +0100 *************** *** 4746,4795 **** { if (!(supported2 & JO2_TERM_ROWS)) break; ! opt->jo_set |= 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_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_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_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_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_term_norestore = get_tv_number(item); } #endif else if (STRCMP(hi->hi_key, "env") == 0) { if (!(supported2 & JO2_ENV)) break; ! opt->jo_set |= JO2_ENV; opt->jo_env = item->vval.v_dict; ++item->vval.v_dict->dv_refcount; } --- 4746,4802 ---- { if (!(supported2 & JO2_TERM_ROWS)) break; ! 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_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_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_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_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_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_set2 |= JO2_ENV; opt->jo_env = item->vval.v_dict; ++item->vval.v_dict->dv_refcount; } *************** *** 4803,4809 **** EMSG2(_(e_invargval), "cwd"); return FAIL; } ! opt->jo_set |= JO2_CWD; } else if (STRCMP(hi->hi_key, "waittime") == 0) { --- 4810,4816 ---- EMSG2(_(e_invargval), "cwd"); return FAIL; } ! opt->jo_set2 |= JO2_CWD; } else if (STRCMP(hi->hi_key, "waittime") == 0) { *** ../vim-8.0.1592/src/evalfunc.c 2018-03-09 21:33:29.248607375 +0100 --- src/evalfunc.c 2018-03-10 16:37:28.772175036 +0100 *************** *** 867,872 **** --- 867,873 ---- {"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}, *** ../vim-8.0.1592/src/testdir/test_terminal.vim 2018-03-09 21:33:29.248607375 +0100 --- src/testdir/test_terminal.vim 2018-03-10 20:12:39.257284108 +0100 *************** *** 5,10 **** --- 5,11 ---- endif source shared.vim + source screendump.vim let s:python = PythonProg() *************** *** 839,841 **** --- 840,887 ---- call delete('Xescape') unlet g:job 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\") + 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, "\:qall\") + 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\") + call WaitFor({-> term_getline(buf, 10) =~ '\[running]'}) + call WaitFor({-> term_getline(buf, 1) !~ '^\s*$'}) + + " set kill using term_setkill() + call term_sendkeys(buf, "\:call term_setkill(bufnr('%'), 'kill')\") + + " make Vim exit, it will kill the shell + call term_sendkeys(buf, "\:qall\") + call WaitFor({-> term_getstatus(buf) == "finished"}) + + " close the terminal window where Vim was running + quit + endfunc *** ../vim-8.0.1592/runtime/doc/terminal.txt 2018-03-03 20:46:28.759725294 +0100 --- runtime/doc/terminal.txt 2018-03-10 16:55:45.829450728 +0100 *************** *** 20,25 **** --- 20,26 ---- Terminal Modes |Terminal-mode| Cursor style |terminal-cursor-style| Special keys |terminal-special-keys| + Session |terminal-session| Unix |terminal-unix| MS-Windows |terminal-ms-windows| 2. Remote testing |terminal-testing| *************** *** 163,168 **** --- 164,174 ---- cannot be |abandon|ed. ++hidden Open the terminal in a hidden buffer, 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 *************** *** 186,193 **** 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")` 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 --- 192,203 ---- If you want to use more options use the |term_start()| function. ! 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 *************** *** 286,291 **** --- 296,317 ---- blinking will also be inverted. + Session ~ + *terminal-session* + A terminal window will be restored when using a session file, if possible and + wanted. + + If "terminal" was removed from 'sessionoptions' then no terminal windows will + be restored. + + If the job in the terminal was finished the window will not be restored. + + If the terminal can be restored, the command that was used to open it will be + used again. To change this use the |term_setrestore()| function. This can + also be used to not restore a specific terminal by setting the command to + "NONE". + + Special keys ~ *terminal-special-keys* Since the terminal emulator simulates an xterm, only escape sequences that *** ../vim-8.0.1592/runtime/doc/eval.txt 2018-03-03 21:29:46.922813914 +0100 --- runtime/doc/eval.txt 2018-03-10 20:26:12.776249783 +0100 *************** *** 2430,2435 **** --- 2435,2442 ---- 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 test_alloc_fail({id}, {countdown}, {repeat}) *************** *** 8217,8222 **** --- 8277,8284 ---- 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()* *************** *** 8302,8307 **** --- 8364,8391 ---- 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: > + terminal ++curwin ++cols=%d ++rows=%d {command} + < Make sure to escape the command properly. + + Use an empty {command} to run 'shell'. + Use "NONE" to not restore this window. + {only available when compiled with the |+terminal| feature} + term_setsize({buf}, {expr}) *term_setsize()* Not implemented yet. {only available when compiled with the |+terminal| feature} *************** *** 8344,8349 **** --- 8428,8438 ---- "curwin" use the current window, do not split the window; fails if the current buffer cannot be |abandon|ed + "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 *** ../vim-8.0.1592/src/version.c 2018-03-09 21:33:29.248607375 +0100 --- src/version.c 2018-03-10 20:22:36.889590211 +0100 *************** *** 768,769 **** --- 768,771 ---- { /* Add new patch number below this line */ + /**/ + 1593, /**/ -- Female engineers become irresistible at the age of consent and remain that way until about thirty minutes after their clinical death. Longer if it's a warm day. (Scott Adams - The Dilbert principle) /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///