1
0
forked from aniani/vim

patch 8.0.1592: terminal windows in a session are not properly restored

Problem:    Terminal windows in a session are not properly restored.
Solution:   Add "terminal" in 'sessionoptions'.  When possible restore the
            command running in a terminal.
This commit is contained in:
Bram Moolenaar
2018-03-09 21:33:34 +01:00
parent 20586cb4f4
commit 4d8bac8bf5
12 changed files with 274 additions and 20 deletions

View File

@@ -4777,6 +4777,13 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported, int supported2)
opt->jo_set |= JO2_HIDDEN; opt->jo_set |= JO2_HIDDEN;
opt->jo_hidden = get_tv_number(item); 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 #endif
else if (STRCMP(hi->hi_key, "env") == 0) else if (STRCMP(hi->hi_key, "env") == 0)
{ {
@@ -5470,6 +5477,7 @@ job_start(typval_T *argvars, jobopt_T *opt_arg)
goto theend; goto theend;
} }
#ifdef USE_ARGV #ifdef USE_ARGV
/* This will modify "cmd". */
if (mch_parse_cmd(cmd, FALSE, &argv, &argc) == FAIL) if (mch_parse_cmd(cmd, FALSE, &argv, &argc) == FAIL)
goto theend; goto theend;
argv[argc] = NULL; argv[argc] = NULL;

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_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},
#endif #endif

View File

@@ -11095,6 +11095,11 @@ makeopens(
{ {
if (!(only_save_windows && buf->b_nwindows == 0) if (!(only_save_windows && buf->b_nwindows == 0)
&& !(buf->b_help && !(ssop_flags & SSOP_HELP)) && !(buf->b_help && !(ssop_flags & SSOP_HELP))
#ifdef FEAT_TERMINAL
/* skip terminal buffers: finished ones are not useful, others
* will be resurrected and result in a new buffer */
&& !bt_terminal(buf)
#endif
&& buf->b_fname != NULL && buf->b_fname != NULL
&& buf->b_p_bl) && buf->b_p_bl)
{ {
@@ -11305,7 +11310,8 @@ makeopens(
/* /*
* Wipe out an empty unnamed buffer we started in. * Wipe out an empty unnamed buffer we started in.
*/ */
if (put_line(fd, "if exists('s:wipebuf')") == FAIL) if (put_line(fd, "if exists('s:wipebuf') && s:wipebuf != bufnr('%')")
== FAIL)
return FAIL; return FAIL;
if (put_line(fd, " silent exe 'bwipe ' . s:wipebuf") == FAIL) if (put_line(fd, " silent exe 'bwipe ' . s:wipebuf") == FAIL)
return FAIL; return FAIL;
@@ -11465,6 +11471,12 @@ ses_do_frame(frame_T *fr)
static int static int
ses_do_win(win_T *wp) ses_do_win(win_T *wp)
{ {
#ifdef FEAT_TERMINAL
if (bt_terminal(wp->w_buffer))
return !term_is_finished(wp->w_buffer)
&& (ssop_flags & SSOP_TERMINAL)
&& term_should_restore(wp->w_buffer);
#endif
if (wp->w_buffer->b_fname == NULL if (wp->w_buffer->b_fname == NULL
#ifdef FEAT_QUICKFIX #ifdef FEAT_QUICKFIX
/* When 'buftype' is "nofile" can't restore the window contents. */ /* When 'buftype' is "nofile" can't restore the window contents. */
@@ -11530,13 +11542,21 @@ put_view(
/* Edit the file. Skip this when ":next" already did it. */ /* Edit the file. Skip this when ":next" already did it. */
if (add_edit && (!did_next || wp->w_arg_idx_invalid)) if (add_edit && (!did_next || wp->w_arg_idx_invalid))
{ {
# ifdef FEAT_TERMINAL
if (bt_terminal(wp->w_buffer))
{
if (term_write_session(fd, wp) == FAIL)
return FAIL;
}
else
# endif
/* /*
* Load the file. * Load the file.
*/ */
if (wp->w_buffer->b_ffname != NULL if (wp->w_buffer->b_ffname != NULL
#ifdef FEAT_QUICKFIX # ifdef FEAT_QUICKFIX
&& !bt_nofile(wp->w_buffer) && !bt_nofile(wp->w_buffer)
#endif # endif
) )
{ {
/* /*
@@ -11554,8 +11574,7 @@ put_view(
|| fputs(" | else | edit ", fd) < 0 || fputs(" | else | edit ", fd) < 0
|| ses_fname(fd, wp->w_buffer, flagp, FALSE) == FAIL || ses_fname(fd, wp->w_buffer, flagp, FALSE) == FAIL
|| fputs(" | endif", fd) < 0 || fputs(" | endif", fd) < 0
|| || put_eol(fd) == FAIL)
put_eol(fd) == FAIL)
return FAIL; return FAIL;
} }
else else

View File

@@ -2403,7 +2403,7 @@ static struct vimoption options[] =
{"sessionoptions", "ssop", P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP, {"sessionoptions", "ssop", P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
#ifdef FEAT_SESSION #ifdef FEAT_SESSION
(char_u *)&p_ssop, PV_NONE, (char_u *)&p_ssop, PV_NONE,
{(char_u *)"blank,buffers,curdir,folds,help,options,tabpages,winsize", {(char_u *)"blank,buffers,curdir,folds,help,options,tabpages,winsize,terminal",
(char_u *)0L} (char_u *)0L}
#else #else
(char_u *)NULL, PV_NONE, (char_u *)NULL, PV_NONE,

View File

@@ -751,7 +751,7 @@ EXTERN unsigned ssop_flags;
/* Also used for 'viewoptions'! */ /* Also used for 'viewoptions'! */
static char *(p_ssop_values[]) = {"buffers", "winpos", "resize", "winsize", static char *(p_ssop_values[]) = {"buffers", "winpos", "resize", "winsize",
"localoptions", "options", "help", "blank", "globals", "slash", "unix", "localoptions", "options", "help", "blank", "globals", "slash", "unix",
"sesdir", "curdir", "folds", "cursor", "tabpages", NULL}; "sesdir", "curdir", "folds", "cursor", "tabpages", "terminal", NULL};
# endif # endif
# define SSOP_BUFFERS 0x001 # define SSOP_BUFFERS 0x001
# define SSOP_WINPOS 0x002 # define SSOP_WINPOS 0x002
@@ -769,6 +769,7 @@ static char *(p_ssop_values[]) = {"buffers", "winpos", "resize", "winsize",
# define SSOP_FOLDS 0x2000 # define SSOP_FOLDS 0x2000
# define SSOP_CURSOR 0x4000 # define SSOP_CURSOR 0x4000
# define SSOP_TABPAGES 0x8000 # define SSOP_TABPAGES 0x8000
# define SSOP_TERMINAL 0x10000
#endif #endif
EXTERN char_u *p_sh; /* 'shell' */ EXTERN char_u *p_sh; /* 'shell' */
EXTERN char_u *p_shcf; /* 'shellcmdflag' */ EXTERN char_u *p_shcf; /* 'shellcmdflag' */

View File

@@ -1,5 +1,8 @@
/* terminal.c */ /* terminal.c */
void ex_terminal(exarg_T *eap); 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 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);

View File

@@ -1706,7 +1706,8 @@ struct channel_S {
#define JO2_HIDDEN 0x0400 /* "hidden" */ #define JO2_HIDDEN 0x0400 /* "hidden" */
#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_ALL 0x1FFF #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_MODE_ALL (JO_MODE + JO_IN_MODE + JO_OUT_MODE + JO_ERR_MODE)
#define JO_CB_ALL \ #define JO_CB_ALL \
@@ -1769,6 +1770,7 @@ typedef struct
int jo_vertical; int jo_vertical;
int jo_curwin; int jo_curwin;
int jo_hidden; int jo_hidden;
int jo_term_norestore;
char_u *jo_term_name; char_u *jo_term_name;
char_u *jo_term_opencmd; char_u *jo_term_opencmd;
int jo_term_finish; int jo_term_finish;

View File

@@ -38,11 +38,10 @@
* in tl_scrollback are no longer used. * in tl_scrollback are no longer used.
* *
* TODO: * TODO:
* - What to store in a session file? Shell at the prompt would be OK to * - Add a flag to kill the job when Vim is exiting. Useful when it's showing
* restore, but others may not. Open the window and let the user start the * a logfile. Or send keys there to make it quit: "exit\r" for a shell.
* command? Also see #2650.
* - Adding WinBar to terminal window doesn't display, text isn't shifted down.
* - When using 'termguicolors' still use the 16 ANSI colors as-is. Helps for * - When using 'termguicolors' still use the 16 ANSI colors as-is. Helps for
* - Adding WinBar to terminal window doesn't display, text isn't shifted down.
* a job that uses 16 colors while Vim is using > 256. * a job that uses 16 colors while Vim is using > 256.
* - in GUI vertical split causes problems. Cursor is flickering. (Hirohito * - in GUI vertical split causes problems. Cursor is flickering. (Hirohito
* Higashi, 2017 Sep 19) * Higashi, 2017 Sep 19)
@@ -135,6 +134,9 @@ struct terminal_S {
void *tl_winpty_config; void *tl_winpty_config;
void *tl_winpty; void *tl_winpty;
#endif #endif
#if defined(FEAT_SESSION)
char_u *tl_command;
#endif
/* last known vterm size */ /* last known vterm size */
int tl_rows; int tl_rows;
@@ -487,6 +489,52 @@ term_start(typval_T *argvar, jobopt_T *opt, int without_job, int forceit)
if (without_job) if (without_job)
return curbuf; return curbuf;
#if defined(FEAT_SESSION)
/* Remember the command for the session file. */
if (opt->jo_term_norestore)
{
term->tl_command = vim_strsave((char_u *)"NONE");
}
else if (argvar->v_type == VAR_STRING)
{
char_u *cmd = argvar->vval.v_string;
if (cmd != NULL && STRCMP(cmd, p_sh) != 0)
term->tl_command = vim_strsave(cmd);
}
else if (argvar->v_type == VAR_LIST
&& argvar->vval.v_list != NULL
&& argvar->vval.v_list->lv_len > 0)
{
garray_T ga;
listitem_T *item;
ga_init2(&ga, 1, 100);
for (item = argvar->vval.v_list->lv_first;
item != NULL; item = item->li_next)
{
char_u *s = get_tv_string_chk(&item->li_tv);
char_u *p;
if (s == NULL)
break;
p = vim_strsave_fnameescape(s, FALSE);
if (p == NULL)
break;
ga_concat(&ga, p);
vim_free(p);
ga_append(&ga, ' ');
}
if (item == NULL)
{
ga_append(&ga, NUL);
term->tl_command = ga.ga_data;
}
else
ga_clear(&ga);
}
#endif
/* 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
@@ -561,6 +609,8 @@ ex_terminal(exarg_T *eap)
opt.jo_curwin = 1; opt.jo_curwin = 1;
else if ((int)(p - cmd) == 6 && STRNICMP(cmd, "hidden", 6) == 0) else if ((int)(p - cmd) == 6 && STRNICMP(cmd, "hidden", 6) == 0)
opt.jo_hidden = 1; 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, "rows", 4) == 0 else if ((int)(p - cmd) == 4 && STRNICMP(cmd, "rows", 4) == 0
&& ep != NULL && isdigit(ep[1])) && ep != NULL && isdigit(ep[1]))
{ {
@@ -620,6 +670,42 @@ ex_terminal(exarg_T *eap)
vim_free(opt.jo_eof_chars); vim_free(opt.jo_eof_chars);
} }
#if defined(FEAT_SESSION) || defined(PROTO)
/*
* Write a :terminal command to the session file to restore the terminal in
* window "wp".
* Return FAIL if writing fails.
*/
int
term_write_session(FILE *fd, win_T *wp)
{
term_T *term = wp->w_buffer->b_term;
/* Create the terminal and run the command. This is not without
* risk, but let's assume the user only creates a session when this
* will be OK. */
if (fprintf(fd, "terminal ++curwin ++cols=%d ++rows=%d ",
term->tl_cols, term->tl_rows) < 0)
return FAIL;
if (term->tl_command != NULL && fputs((char *)term->tl_command, fd) < 0)
return FAIL;
return put_eol(fd);
}
/*
* Return TRUE if "buf" has a terminal that should be restored.
*/
int
term_should_restore(buf_T *buf)
{
term_T *term = buf->b_term;
return term != NULL && (term->tl_command == NULL
|| STRCMP(term->tl_command, "NONE") != 0);
}
#endif
/* /*
* Free the scrollback buffer for "term". * Free the scrollback buffer for "term".
*/ */
@@ -669,6 +755,9 @@ free_terminal(buf_T *buf)
term_free_vterm(term); term_free_vterm(term);
vim_free(term->tl_title); vim_free(term->tl_title);
#ifdef FEAT_SESSION
vim_free(term->tl_command);
#endif
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);
@@ -4047,6 +4136,29 @@ f_term_sendkeys(typval_T *argvars, typval_T *rettv)
} }
} }
/*
* "term_setrestore(buf, command)" function
*/
void
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;
if (buf == NULL)
return;
term = buf->b_term;
vim_free(term->tl_command);
cmd = get_tv_string_chk(&argvars[1]);
if (cmd != NULL)
term->tl_command = vim_strsave(cmd);
else
term->tl_command = NULL;
#endif
}
/* /*
* "term_start(command, options)" function * "term_start(command, options)" function
*/ */
@@ -4064,7 +4176,8 @@ f_term_start(typval_T *argvars, typval_T *rettv)
+ JO_EXIT_CB + JO_CLOSE_CALLBACK + JO_OUT_IO, + JO_EXIT_CB + JO_CLOSE_CALLBACK + JO_OUT_IO,
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) == FAIL) + JO2_CWD + JO2_ENV + JO2_EOF_CHARS
+ JO2_NORESTORE) == FAIL)
return; return;
if (opt.jo_vertical) if (opt.jo_vertical)
@@ -4566,6 +4679,7 @@ term_and_job_init(
{ {
create_vterm(term, term->tl_rows, term->tl_cols); create_vterm(term, term->tl_rows, term->tl_cols);
/* This will change a string in "argvar". */
term->tl_job = job_start(argvar, opt); term->tl_job = job_start(argvar, opt);
if (term->tl_job != NULL) if (term->tl_job != NULL)
++term->tl_job->jv_refcount; ++term->tl_job->jv_refcount;

View File

@@ -270,3 +270,10 @@ func! Screenline(lnum)
let line = join(chars, '') let line = join(chars, '')
return matchstr(line, '^.\{-}\ze\s*$') return matchstr(line, '^.\{-}\ze\s*$')
endfunc endfunc
" Stops the shell running in terminal "buf".
func Stop_shell_in_terminal(buf)
call term_sendkeys(a:buf, "exit\r")
let job = term_getjob(a:buf)
call WaitFor({-> job_status(job) == "dead"})
endfunc

View File

@@ -7,6 +7,8 @@ if !has('multi_byte') || !has('mksession')
finish finish
endif endif
source shared.vim
func Test_mksession() func Test_mksession()
tabnew tabnew
let wrap_save = &wrap let wrap_save = &wrap
@@ -99,6 +101,7 @@ func Test_mksession()
call delete('Xtest_mks.out') call delete('Xtest_mks.out')
call delete(tmpfile) call delete(tmpfile)
let &wrap = wrap_save let &wrap = wrap_save
set sessionoptions&
endfunc endfunc
func Test_mksession_winheight() func Test_mksession_winheight()
@@ -150,6 +153,107 @@ func Test_mksession_one_buffer_two_windows()
call delete('Xtest_mks.out') call delete('Xtest_mks.out')
endfunc endfunc
if has('terminal')
func Test_mksession_terminal_shell()
terminal
mksession! Xtest_mks.out
let lines = readfile('Xtest_mks.out')
let term_cmd = ''
for line in lines
if line =~ '^terminal'
let term_cmd = line
elseif line =~ 'badd.*' . &shell
call assert_report('unexpected shell line: ' . line)
endif
endfor
call assert_match('terminal ++curwin ++cols=\d\+ ++rows=\d\+\s*$', term_cmd)
call Stop_shell_in_terminal(bufnr('%'))
call delete('Xtest_mks.out')
endfunc
func Test_mksession_terminal_no_restore_cmdarg()
terminal ++norestore
mksession! Xtest_mks.out
let lines = readfile('Xtest_mks.out')
let term_cmd = ''
for line in lines
if line =~ '^terminal'
call assert_report('session must not restore teminal')
endif
endfor
call Stop_shell_in_terminal(bufnr('%'))
call delete('Xtest_mks.out')
endfunc
func Test_mksession_terminal_no_restore_funcarg()
call term_start(&shell, {'norestore': 1})
mksession! Xtest_mks.out
let lines = readfile('Xtest_mks.out')
let term_cmd = ''
for line in lines
if line =~ '^terminal'
call assert_report('session must not restore teminal')
endif
endfor
call Stop_shell_in_terminal(bufnr('%'))
call delete('Xtest_mks.out')
endfunc
func Test_mksession_terminal_no_restore_func()
terminal
call term_setrestore(bufnr('%'), 'NONE')
mksession! Xtest_mks.out
let lines = readfile('Xtest_mks.out')
let term_cmd = ''
for line in lines
if line =~ '^terminal'
call assert_report('session must not restore teminal')
endif
endfor
call Stop_shell_in_terminal(bufnr('%'))
call delete('Xtest_mks.out')
endfunc
func Test_mksession_terminal_no_ssop()
terminal
set sessionoptions-=terminal
mksession! Xtest_mks.out
let lines = readfile('Xtest_mks.out')
let term_cmd = ''
for line in lines
if line =~ '^terminal'
call assert_report('session must not restore teminal')
endif
endfor
call Stop_shell_in_terminal(bufnr('%'))
call delete('Xtest_mks.out')
set sessionoptions&
endfunc
func Test_mksession_terminal_restore_other()
terminal
call term_setrestore(bufnr('%'), 'other')
mksession! Xtest_mks.out
let lines = readfile('Xtest_mks.out')
let term_cmd = ''
for line in lines
if line =~ '^terminal'
let term_cmd = line
endif
endfor
call assert_match('terminal ++curwin ++cols=\d\+ ++rows=\d\+ other', term_cmd)
call Stop_shell_in_terminal(bufnr('%'))
call delete('Xtest_mks.out')
endfunc
endif " has('terminal')
" vim: shiftwidth=2 sts=2 expandtab " vim: shiftwidth=2 sts=2 expandtab

View File

@@ -30,13 +30,6 @@ func Run_shell_in_terminal(options)
return buf return buf
endfunc endfunc
" Stops the shell started by Run_shell_in_terminal().
func Stop_shell_in_terminal(buf)
call term_sendkeys(a:buf, "exit\r")
call WaitFor('job_status(g:job) == "dead"')
call assert_equal('dead', job_status(g:job))
endfunc
func Test_terminal_basic() func Test_terminal_basic()
au BufWinEnter * if &buftype == 'terminal' | let b:done = 'yes' | endif au BufWinEnter * if &buftype == 'terminal' | let b:done = 'yes' | endif
let buf = Run_shell_in_terminal({}) let buf = Run_shell_in_terminal({})

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 */
/**/
1592,
/**/ /**/
1591, 1591,
/**/ /**/