]> granicus.if.org Git - vim/commitdiff
patch 8.0.1592: terminal windows in a session are not properly restored v8.0.1592
authorBram Moolenaar <Bram@vim.org>
Fri, 9 Mar 2018 20:33:34 +0000 (21:33 +0100)
committerBram Moolenaar <Bram@vim.org>
Fri, 9 Mar 2018 20:33:34 +0000 (21:33 +0100)
Problem:    Terminal windows in a session are not properly restored.
Solution:   Add "terminal" in 'sessionoptions'.  When possible restore the
            command running in a terminal.

12 files changed:
src/channel.c
src/evalfunc.c
src/ex_docmd.c
src/option.c
src/option.h
src/proto/terminal.pro
src/structs.h
src/terminal.c
src/testdir/shared.vim
src/testdir/test_mksession.vim
src/testdir/test_terminal.vim
src/version.c

index 93c809ca637d6c1fcf816b34b4312001a5d47205..7c574427a7f5133c5e83f75d93dc7f9113d0a47b 100644 (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_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)
            {
@@ -5470,6 +5477,7 @@ job_start(typval_T *argvars, jobopt_T *opt_arg)
            goto theend;
        }
 #ifdef USE_ARGV
+       /* This will modify "cmd". */
        if (mch_parse_cmd(cmd, FALSE, &argv, &argc) == FAIL)
            goto theend;
        argv[argc] = NULL;
index 9cbdf3f0b7bf35541f42ce7d3a2d25163eff4fac..aa0ec60739aae5ccdf91f42830588f52b25e1778 100644 (file)
@@ -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_setrestore",        2, 2, f_term_setrestore},
     {"term_start",     1, 2, f_term_start},
     {"term_wait",      1, 2, f_term_wait},
 #endif
index f913fb527816ee0e64f863b0306bf5efce001076..9994cb64eabbb37edd8550e2460d0fa998843093 100644 (file)
@@ -11095,6 +11095,11 @@ makeopens(
     {
        if (!(only_save_windows && buf->b_nwindows == 0)
                && !(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_p_bl)
        {
@@ -11305,7 +11310,8 @@ makeopens(
     /*
      * 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;
     if (put_line(fd, "  silent exe 'bwipe ' . s:wipebuf") == FAIL)
        return FAIL;
@@ -11465,6 +11471,12 @@ ses_do_frame(frame_T *fr)
     static int
 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
 #ifdef FEAT_QUICKFIX
            /* 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. */
     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.
         */
        if (wp->w_buffer->b_ffname != NULL
-#ifdef FEAT_QUICKFIX
+# ifdef FEAT_QUICKFIX
                && !bt_nofile(wp->w_buffer)
-#endif
+# endif
                )
        {
            /*
@@ -11554,8 +11574,7 @@ put_view(
                    || fputs(" | else | edit ", fd) < 0
                    || ses_fname(fd, wp->w_buffer, flagp, FALSE) == FAIL
                    || fputs(" | endif", fd) < 0
-                   ||
-               put_eol(fd) == FAIL)
+                   || put_eol(fd) == FAIL)
                return FAIL;
        }
        else
index 3a0a6590dbd755f9756c14a367c63fdc70d9ab60..084e5e1a63064094144e5a59a119f5dccc43facd 100644 (file)
@@ -2403,7 +2403,7 @@ static struct vimoption options[] =
     {"sessionoptions", "ssop", P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
 #ifdef FEAT_SESSION
                            (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}
 #else
                            (char_u *)NULL, PV_NONE,
index d408a2d0757daa52820ef7a921e657d70b6c35d4..09c044ecd928a0c43f5ad31ec8ace12e35c1f932 100644 (file)
@@ -751,7 +751,7 @@ EXTERN unsigned     ssop_flags;
 /* Also used for 'viewoptions'! */
 static char *(p_ssop_values[]) = {"buffers", "winpos", "resize", "winsize",
     "localoptions", "options", "help", "blank", "globals", "slash", "unix",
-    "sesdir", "curdir", "folds", "cursor", "tabpages", NULL};
+    "sesdir", "curdir", "folds", "cursor", "tabpages", "terminal", NULL};
 # endif
 # define SSOP_BUFFERS          0x001
 # define SSOP_WINPOS           0x002
@@ -769,6 +769,7 @@ static char *(p_ssop_values[]) = {"buffers", "winpos", "resize", "winsize",
 # define SSOP_FOLDS            0x2000
 # define SSOP_CURSOR           0x4000
 # define SSOP_TABPAGES         0x8000
+# define SSOP_TERMINAL         0x10000
 #endif
 EXTERN char_u  *p_sh;          /* 'shell' */
 EXTERN char_u  *p_shcf;        /* 'shellcmdflag' */
index 9c2968332455f255020808619f4606d52adeb8ff..aefa40d84db99c9ae72d95339d369511fe35dc3c 100644 (file)
@@ -1,5 +1,8 @@
 /* terminal.c */
 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);
index 7dc732c9cdc51c1a829e9b8b223bd754672f403d..d6959bc4bd1f9c0ce20a791ed82787cf079d2248 100644 (file)
@@ -1706,7 +1706,8 @@ struct channel_S {
 #define JO2_HIDDEN         0x0400      /* "hidden" */
 #define JO2_TERM_OPENCMD    0x0800     /* "term_opencmd" */
 #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_CB_ALL \
@@ -1769,6 +1770,7 @@ typedef struct
     int                jo_vertical;
     int                jo_curwin;
     int                jo_hidden;
+    int                jo_term_norestore;
     char_u     *jo_term_name;
     char_u     *jo_term_opencmd;
     int                jo_term_finish;
index 89fafebb0a9317723ab37edccd166504e098413d..547897fcfac49e1beedbd9619684635172c9aa6c 100644 (file)
  * in tl_scrollback are no longer used.
  *
  * TODO:
- * - What to store in a session file?  Shell at the prompt would be OK to
- *   restore, but others may not.  Open the window and let the user start the
- *   command?  Also see #2650.
- * - Adding WinBar to terminal window doesn't display, text isn't shifted down.
+ * - Add a flag to kill the job when Vim is exiting.  Useful when it's showing
+ *   a logfile.  Or send keys there to make it quit: "exit\r" for a shell.
  * - 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.
  * - in GUI vertical split causes problems.  Cursor is flickering. (Hirohito
  *   Higashi, 2017 Sep 19)
@@ -135,6 +134,9 @@ struct terminal_S {
     void       *tl_winpty_config;
     void       *tl_winpty;
 #endif
+#if defined(FEAT_SESSION)
+    char_u     *tl_command;
+#endif
 
     /* last known vterm size */
     int                tl_rows;
@@ -487,6 +489,52 @@ term_start(typval_T *argvar, jobopt_T *opt, int without_job, int forceit)
     if (without_job)
        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. */
     if (argvar->v_type == VAR_STRING
            && argvar->vval.v_string != NULL
@@ -561,6 +609,8 @@ ex_terminal(exarg_T *eap)
            opt.jo_curwin = 1;
        else if ((int)(p - cmd) == 6 && STRNICMP(cmd, "hidden", 6) == 0)
            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
                && ep != NULL && isdigit(ep[1]))
        {
@@ -620,6 +670,42 @@ ex_terminal(exarg_T *eap)
     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".
  */
@@ -669,6 +755,9 @@ free_terminal(buf_T *buf)
 
     term_free_vterm(term);
     vim_free(term->tl_title);
+#ifdef FEAT_SESSION
+    vim_free(term->tl_command);
+#endif
     vim_free(term->tl_status_text);
     vim_free(term->tl_opencmd);
     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
  */
@@ -4064,7 +4176,8 @@ f_term_start(typval_T *argvars, typval_T *rettv)
                    + JO_EXIT_CB + JO_CLOSE_CALLBACK + JO_OUT_IO,
                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) == FAIL)
+                   + JO2_CWD + JO2_ENV + JO2_EOF_CHARS
+                   + JO2_NORESTORE) == FAIL)
        return;
 
     if (opt.jo_vertical)
@@ -4566,6 +4679,7 @@ term_and_job_init(
 {
     create_vterm(term, term->tl_rows, term->tl_cols);
 
+    /* This will change a string in "argvar". */
     term->tl_job = job_start(argvar, opt);
     if (term->tl_job != NULL)
        ++term->tl_job->jv_refcount;
index 5fef6bd1d060436efff53721d9fb445ccfd09cef..877c5f9d7039fc8e67168342906dd80f2e5844ba 100644 (file)
@@ -270,3 +270,10 @@ func! Screenline(lnum)
   let line = join(chars, '')
   return matchstr(line, '^.\{-}\ze\s*$')
 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
index cdb6f4caf98ae5c44b07666ed79f095251faf7f5..81883b5d76f4df93d86114ec51f63fc10f7c5953 100644 (file)
@@ -7,6 +7,8 @@ if !has('multi_byte') || !has('mksession')
   finish
 endif
 
+source shared.vim
+
 func Test_mksession()
   tabnew
   let wrap_save = &wrap
@@ -99,6 +101,7 @@ func Test_mksession()
   call delete('Xtest_mks.out')
   call delete(tmpfile)
   let &wrap = wrap_save
+  set sessionoptions&
 endfunc
 
 func Test_mksession_winheight()
@@ -150,6 +153,107 @@ func Test_mksession_one_buffer_two_windows()
   call delete('Xtest_mks.out')
 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
index ac54bbc1e1af4c30b8d2edfe601fb75eceb0548e..0e04abd5c13865d567f673c277d4e7a1a22d658a 100644 (file)
@@ -30,13 +30,6 @@ func Run_shell_in_terminal(options)
   return buf
 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()
   au BufWinEnter * if &buftype == 'terminal' | let b:done = 'yes' | endif
   let buf = Run_shell_in_terminal({})
index f4e7e737b7dd9c910067915effbb846f08325f64..89b6eef14d3b734a4d1ce064310644d607439b84 100644 (file)
@@ -766,6 +766,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1592,
 /**/
     1591,
 /**/