To: vim_dev@googlegroups.com Subject: Patch 8.0.1592 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.0.1592 Problem: Terminal windows in a session are not properly restored. Solution: Add "terminal" in 'sessionoptions'. When possible restore the command running in a terminal. Files: src/option.c, src/option.h, src/ex_docmd.c, src/terminal.c, src/proto/terminal.pro, src/evalfunc.c, src/structs.h, src/channel.c, src/testdir/test_terminal.vim, src/testdir/shared.vim, src/testdir/test_mksession.vim *** ../vim-8.0.1591/src/option.c 2018-03-07 23:02:29.594336432 +0100 --- src/option.c 2018-03-08 22:09:44.716834176 +0100 *************** *** 2403,2409 **** {"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 *)0L} #else (char_u *)NULL, PV_NONE, --- 2403,2409 ---- {"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,terminal", (char_u *)0L} #else (char_u *)NULL, PV_NONE, *** ../vim-8.0.1591/src/option.h 2018-03-04 20:14:08.252064314 +0100 --- src/option.h 2018-03-08 22:11:16.736211708 +0100 *************** *** 751,757 **** /* 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}; # endif # define SSOP_BUFFERS 0x001 # define SSOP_WINPOS 0x002 --- 751,757 ---- /* 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", "terminal", NULL}; # endif # define SSOP_BUFFERS 0x001 # define SSOP_WINPOS 0x002 *************** *** 769,774 **** --- 769,775 ---- # 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' */ *** ../vim-8.0.1591/src/ex_docmd.c 2018-03-05 21:06:19.763252514 +0100 --- src/ex_docmd.c 2018-03-09 20:38:43.573014028 +0100 *************** *** 11095,11100 **** --- 11095,11105 ---- { 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,11311 **** /* * Wipe out an empty unnamed buffer we started in. */ ! if (put_line(fd, "if exists('s:wipebuf')") == FAIL) return FAIL; if (put_line(fd, " silent exe 'bwipe ' . s:wipebuf") == FAIL) return FAIL; --- 11310,11317 ---- /* * Wipe out an empty unnamed buffer we started in. */ ! 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,11470 **** --- 11471,11482 ---- 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,11542 **** /* Edit the file. Skip this when ":next" already did it. */ if (add_edit && (!did_next || wp->w_arg_idx_invalid)) { /* * Load the file. */ if (wp->w_buffer->b_ffname != NULL ! #ifdef FEAT_QUICKFIX && !bt_nofile(wp->w_buffer) ! #endif ) { /* --- 11542,11562 ---- /* 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 && !bt_nofile(wp->w_buffer) ! # endif ) { /* *************** *** 11554,11561 **** || fputs(" | else | edit ", fd) < 0 || ses_fname(fd, wp->w_buffer, flagp, FALSE) == FAIL || fputs(" | endif", fd) < 0 ! || ! put_eol(fd) == FAIL) return FAIL; } else --- 11574,11580 ---- || fputs(" | else | edit ", fd) < 0 || ses_fname(fd, wp->w_buffer, flagp, FALSE) == FAIL || fputs(" | endif", fd) < 0 ! || put_eol(fd) == FAIL) return FAIL; } else *** ../vim-8.0.1591/src/terminal.c 2018-03-04 18:07:04.284592244 +0100 --- src/terminal.c 2018-03-09 21:27:59.970681174 +0100 *************** *** 38,48 **** * 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. * - When using 'termguicolors' still use the 16 ANSI colors as-is. Helps for * 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) --- 38,47 ---- * in tl_scrollback are no longer used. * * TODO: ! * - 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,140 **** --- 134,142 ---- 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,492 **** --- 489,540 ---- 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,566 **** --- 609,616 ---- 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,625 **** --- 670,711 ---- 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,674 **** --- 755,763 ---- 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); *************** *** 4048,4053 **** --- 4137,4165 ---- } /* + * "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 */ void *************** *** 4064,4070 **** + 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) return; if (opt.jo_vertical) --- 4176,4183 ---- + 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 ! + JO2_NORESTORE) == FAIL) return; if (opt.jo_vertical) *************** *** 4566,4571 **** --- 4679,4685 ---- { 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; *** ../vim-8.0.1591/src/proto/terminal.pro 2018-02-18 22:13:06.261057963 +0100 --- src/proto/terminal.pro 2018-03-09 19:52:14.374022298 +0100 *************** *** 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); *** ../vim-8.0.1591/src/evalfunc.c 2018-03-04 20:14:08.240064393 +0100 --- src/evalfunc.c 2018-03-09 19:52:07.566063527 +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_setrestore", 2, 2, f_term_setrestore}, {"term_start", 1, 2, f_term_start}, {"term_wait", 1, 2, f_term_wait}, #endif *** ../vim-8.0.1591/src/structs.h 2018-03-08 21:46:38.878732713 +0100 --- src/structs.h 2018-03-09 21:24:03.572169369 +0100 *************** *** 1706,1712 **** #define JO2_HIDDEN 0x0400 /* "hidden" */ #define JO2_TERM_OPENCMD 0x0800 /* "term_opencmd" */ #define JO2_EOF_CHARS 0x1000 /* "eof_chars" */ ! #define JO2_ALL 0x1FFF #define JO_MODE_ALL (JO_MODE + JO_IN_MODE + JO_OUT_MODE + JO_ERR_MODE) #define JO_CB_ALL \ --- 1706,1713 ---- #define JO2_HIDDEN 0x0400 /* "hidden" */ #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 \ *************** *** 1769,1774 **** --- 1770,1776 ---- 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; *** ../vim-8.0.1591/src/channel.c 2018-02-13 15:17:49.074578201 +0100 --- src/channel.c 2018-03-09 21:23:25.436407822 +0100 *************** *** 4777,4782 **** --- 4777,4789 ---- 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,5475 **** --- 5477,5483 ---- goto theend; } #ifdef USE_ARGV + /* This will modify "cmd". */ if (mch_parse_cmd(cmd, FALSE, &argv, &argc) == FAIL) goto theend; argv[argc] = NULL; *** ../vim-8.0.1591/src/testdir/test_terminal.vim 2018-02-19 23:09:57.385619337 +0100 --- src/testdir/test_terminal.vim 2018-03-09 20:11:04.703195807 +0100 *************** *** 30,42 **** 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({}) --- 30,35 ---- *** ../vim-8.0.1591/src/testdir/shared.vim 2018-02-27 21:08:48.272513006 +0100 --- src/testdir/shared.vim 2018-03-09 20:12:00.362860532 +0100 *************** *** 270,272 **** --- 270,279 ---- 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 *** ../vim-8.0.1591/src/testdir/test_mksession.vim 2017-08-30 21:57:58.003077451 +0200 --- src/testdir/test_mksession.vim 2018-03-09 21:25:55.359468409 +0100 *************** *** 7,12 **** --- 7,14 ---- finish endif + source shared.vim + func Test_mksession() tabnew let wrap_save = &wrap *************** *** 99,104 **** --- 101,107 ---- call delete('Xtest_mks.out') call delete(tmpfile) let &wrap = wrap_save + set sessionoptions& endfunc func Test_mksession_winheight() *************** *** 150,155 **** --- 153,259 ---- 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 *** ../vim-8.0.1591/src/version.c 2018-03-08 22:03:09.867578790 +0100 --- src/version.c 2018-03-08 23:25:57.694883335 +0100 *************** *** 768,769 **** --- 768,771 ---- { /* Add new patch number below this line */ + /**/ + 1592, /**/ -- I recommend ordering large cargo containers of paper towels to make up whatever budget underruns you have. Paper products are always useful and they have the advantage of being completely flushable if you need to make room in the storage area later. (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 ///