Patch 8.2.1662
Problem: :mksession does not restore shared terminal buffer properly.
Solution: Keep a hashtab with terminal buffers. (Rob Pilling, closes #6930)
Files: src/hashtab.c, src/proto/terminal.pro, src/session.c,
src/terminal.c, src/testdir/test_mksession.vim
*** ../vim-8.2.1661/src/hashtab.c 2020-07-14 16:15:27.576652590 +0200
--- src/hashtab.c 2020-09-11 20:25:10.560975560 +0200
***************
*** 81,87 ****
vim_free(ht->ht_array);
}
! #if defined(FEAT_SPELL) || defined(PROTO)
/*
* Free the array of a hash table and all the keys it contains. The keys must
* have been allocated. "off" is the offset from the start of the allocate
--- 81,87 ----
vim_free(ht->ht_array);
}
! #if defined(FEAT_SPELL) || defined(FEAT_TERMINAL) || defined(PROTO)
/*
* Free the array of a hash table and all the keys it contains. The keys must
* have been allocated. "off" is the offset from the start of the allocate
*** ../vim-8.2.1661/src/proto/terminal.pro 2020-02-02 15:25:10.097976117
+0100
--- src/proto/terminal.pro 2020-09-11 20:22:35.637566597 +0200
***************
*** 2,8 ****
void init_job_options(jobopt_T *opt);
buf_T *term_start(typval_T *argvar, char **argv, jobopt_T *opt, int flags);
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 free_unused_terminals(void);
--- 2,8 ----
void init_job_options(jobopt_T *opt);
buf_T *term_start(typval_T *argvar, char **argv, jobopt_T *opt, int flags);
void ex_terminal(exarg_T *eap);
! int term_write_session(FILE *fd, win_T *wp, hashtab_T *terminal_bufs);
int term_should_restore(buf_T *buf);
void free_terminal(buf_T *buf);
void free_unused_terminals(void);
*** ../vim-8.2.1661/src/session.c 2020-04-16 21:04:38.597969228 +0200
--- src/session.c 2020-09-11 20:33:10.267264224 +0200
***************
*** 305,312 ****
win_T *wp,
int add_edit, // add ":edit" command to view
unsigned *flagp, // vop_flags or ssop_flags
! int current_arg_idx) // current argument index of the
window, use
! // -1 if unknown
{
win_T *save_curwin;
int f;
--- 305,316 ----
win_T *wp,
int add_edit, // add ":edit" command to view
unsigned *flagp, // vop_flags or ssop_flags
! int current_arg_idx // current argument index of the
window, use
! // -1 if unknown
! #ifdef FEAT_TERMINAL
! , hashtab_T *terminal_bufs
! #endif
! )
{
win_T *save_curwin;
int f;
***************
*** 349,355 ****
# ifdef FEAT_TERMINAL
if (bt_terminal(wp->w_buffer))
{
! if (term_write_session(fd, wp) == FAIL)
return FAIL;
}
else
--- 353,359 ----
# ifdef FEAT_TERMINAL
if (bt_terminal(wp->w_buffer))
{
! if (term_write_session(fd, wp, terminal_bufs) == FAIL)
return FAIL;
}
else
***************
*** 588,593 ****
--- 592,603 ----
frame_T *tab_topframe;
int cur_arg_idx = 0;
int next_arg_idx = 0;
+ int ret = FAIL;
+ #ifdef FEAT_TERMINAL
+ hashtab_T terminal_bufs;
+
+ hash_init(&terminal_bufs);
+ #endif
if (ssop_flags & SSOP_BUFFERS)
only_save_windows = FALSE; // Save ALL buffers
***************
*** 596,620 ****
// sessionable variables.
#ifdef FEAT_EVAL
if (put_line(fd, "let v:this_session=expand(\"<sfile>:p\")") == FAIL)
! return FAIL;
if (ssop_flags & SSOP_GLOBALS)
if (store_session_globals(fd) == FAIL)
! return FAIL;
#endif
// Close all windows and tabs but one.
if (put_line(fd, "silent only") == FAIL)
! return FAIL;
if ((ssop_flags & SSOP_TABPAGES)
&& put_line(fd, "silent tabonly") == FAIL)
! return FAIL;
// Now a :cd command to the session directory or the current directory
if (ssop_flags & SSOP_SESDIR)
{
if (put_line(fd, "exe \"cd \" . escape(expand(\"<sfile>:p:h\"), ' ')")
== FAIL)
! return FAIL;
}
else if (ssop_flags & SSOP_CURDIR)
{
--- 606,630 ----
// sessionable variables.
#ifdef FEAT_EVAL
if (put_line(fd, "let v:this_session=expand(\"<sfile>:p\")") == FAIL)
! goto fail;
if (ssop_flags & SSOP_GLOBALS)
if (store_session_globals(fd) == FAIL)
! goto fail;
#endif
// Close all windows and tabs but one.
if (put_line(fd, "silent only") == FAIL)
! goto fail;
if ((ssop_flags & SSOP_TABPAGES)
&& put_line(fd, "silent tabonly") == FAIL)
! goto fail;
// Now a :cd command to the session directory or the current directory
if (ssop_flags & SSOP_SESDIR)
{
if (put_line(fd, "exe \"cd \" . escape(expand(\"<sfile>:p:h\"), ' ')")
== FAIL)
! goto fail;
}
else if (ssop_flags & SSOP_CURDIR)
{
***************
*** 625,631 ****
|| put_eol(fd) == FAIL)
{
vim_free(sname);
! return FAIL;
}
vim_free(sname);
}
--- 635,641 ----
|| put_eol(fd) == FAIL)
{
vim_free(sname);
! goto fail;
}
vim_free(sname);
}
***************
*** 633,659 ****
// If there is an empty, unnamed buffer we will wipe it out later.
// Remember the buffer number.
if (put_line(fd, "if expand('%') == '' && !&modified && line('$') <= 1 &&
getline(1) == ''") == FAIL)
! return FAIL;
if (put_line(fd, " let s:wipebuf = bufnr('%')") == FAIL)
! return FAIL;
if (put_line(fd, "endif") == FAIL)
! return FAIL;
// Now save the current files, current buffer first.
if (put_line(fd, "set shortmess=aoO") == FAIL)
! return FAIL;
// the global argument list
if (ses_arglist(fd, "argglobal", &global_alist.al_ga,
!(ssop_flags & SSOP_CURDIR), &ssop_flags) == FAIL)
! return FAIL;
if (ssop_flags & SSOP_RESIZE)
{
// Note: after the restore we still check it worked!
if (fprintf(fd, "set lines=%ld columns=%ld" , Rows, Columns) < 0
|| put_eol(fd) == FAIL)
! return FAIL;
}
#ifdef FEAT_GUI
--- 643,669 ----
// If there is an empty, unnamed buffer we will wipe it out later.
// Remember the buffer number.
if (put_line(fd, "if expand('%') == '' && !&modified && line('$') <= 1 &&
getline(1) == ''") == FAIL)
! goto fail;
if (put_line(fd, " let s:wipebuf = bufnr('%')") == FAIL)
! goto fail;
if (put_line(fd, "endif") == FAIL)
! goto fail;
// Now save the current files, current buffer first.
if (put_line(fd, "set shortmess=aoO") == FAIL)
! goto fail;
// the global argument list
if (ses_arglist(fd, "argglobal", &global_alist.al_ga,
!(ssop_flags & SSOP_CURDIR), &ssop_flags) == FAIL)
! goto fail;
if (ssop_flags & SSOP_RESIZE)
{
// Note: after the restore we still check it worked!
if (fprintf(fd, "set lines=%ld columns=%ld" , Rows, Columns) < 0
|| put_eol(fd) == FAIL)
! goto fail;
}
#ifdef FEAT_GUI
***************
*** 665,671 ****
{
// Note: after the restore we still check it worked!
if (fprintf(fd, "winpos %d %d", x, y) < 0 || put_eol(fd) == FAIL)
! return FAIL;
}
}
#endif
--- 675,681 ----
{
// Note: after the restore we still check it worked!
if (fprintf(fd, "winpos %d %d", x, y) < 0 || put_eol(fd) == FAIL)
! goto fail;
}
}
#endif
***************
*** 677,683 ****
if (p_stal == 1 && first_tabpage->tp_next != NULL)
{
if (put_line(fd, "set stal=2") == FAIL)
! return FAIL;
restore_stal = TRUE;
}
--- 687,693 ----
if (p_stal == 1 && first_tabpage->tp_next != NULL)
{
if (put_line(fd, "set stal=2") == FAIL)
! goto fail;
restore_stal = TRUE;
}
***************
*** 695,703 ****
// later local options won't be copied to the new tabs.
FOR_ALL_TABPAGES(tp)
if (tp->tp_next != NULL && put_line(fd, "tabnew") == FAIL)
! return FAIL;
if (first_tabpage->tp_next != NULL && put_line(fd, "tabrewind") == FAIL)
! return FAIL;
}
for (tabnr = 1; ; ++tabnr)
{
--- 705,713 ----
// later local options won't be copied to the new tabs.
FOR_ALL_TABPAGES(tp)
if (tp->tp_next != NULL && put_line(fd, "tabnew") == FAIL)
! goto fail;
if (first_tabpage->tp_next != NULL && put_line(fd, "tabrewind") == FAIL)
! goto fail;
}
for (tabnr = 1; ; ++tabnr)
{
***************
*** 739,751 ****
)
{
if (need_tabnext && put_line(fd, "tabnext") == FAIL)
! return FAIL;
need_tabnext = FALSE;
if (fputs("edit ", fd) < 0
|| ses_fname(fd, wp->w_buffer, &ssop_flags, TRUE)
== FAIL)
! return FAIL;
if (!wp->w_arg_idx_invalid)
edited_win = wp;
break;
--- 749,761 ----
)
{
if (need_tabnext && put_line(fd, "tabnext") == FAIL)
! goto fail;
need_tabnext = FALSE;
if (fputs("edit ", fd) < 0
|| ses_fname(fd, wp->w_buffer, &ssop_flags, TRUE)
== FAIL)
! goto fail;
if (!wp->w_arg_idx_invalid)
edited_win = wp;
break;
***************
*** 754,770 ****
// If no file got edited create an empty tab page.
if (need_tabnext && put_line(fd, "tabnext") == FAIL)
! return FAIL;
// Save current window layout.
if (put_line(fd, "set splitbelow splitright") == FAIL)
! return FAIL;
if (ses_win_rec(fd, tab_topframe) == FAIL)
! return FAIL;
if (!p_sb && put_line(fd, "set nosplitbelow") == FAIL)
! return FAIL;
if (!p_spr && put_line(fd, "set nosplitright") == FAIL)
! return FAIL;
// Check if window sizes can be restored (no windows omitted).
// Remember the window number of the current window after restoring.
--- 764,780 ----
// If no file got edited create an empty tab page.
if (need_tabnext && put_line(fd, "tabnext") == FAIL)
! goto fail;
// Save current window layout.
if (put_line(fd, "set splitbelow splitright") == FAIL)
! goto fail;
if (ses_win_rec(fd, tab_topframe) == FAIL)
! goto fail;
if (!p_sb && put_line(fd, "set nosplitbelow") == FAIL)
! goto fail;
if (!p_spr && put_line(fd, "set nosplitright") == FAIL)
! goto fail;
// Check if window sizes can be restored (no windows omitted).
// Remember the window number of the current window after restoring.
***************
*** 781,787 ****
// Go to the first window.
if (put_line(fd, "wincmd t") == FAIL)
! return FAIL;
// If more than one window, see if sizes can be restored.
// First set 'winheight' and 'winwidth' to 1 to avoid the windows being
--- 791,797 ----
// Go to the first window.
if (put_line(fd, "wincmd t") == FAIL)
! goto fail;
// If more than one window, see if sizes can be restored.
// First set 'winheight' and 'winwidth' to 1 to avoid the windows being
***************
*** 794,802 ****
|| put_line(fd, "set winheight=1") == FAIL
|| put_line(fd, "set winminwidth=0") == FAIL
|| put_line(fd, "set winwidth=1") == FAIL)
! return FAIL;
if (nr > 1 && ses_winsizes(fd, restore_size, tab_firstwin) == FAIL)
! return FAIL;
// Restore the tab-local working directory if specified
// Do this before the windows, so that the window-local directory can
--- 804,812 ----
|| put_line(fd, "set winheight=1") == FAIL
|| put_line(fd, "set winminwidth=0") == FAIL
|| put_line(fd, "set winwidth=1") == FAIL)
! goto fail;
if (nr > 1 && ses_winsizes(fd, restore_size, tab_firstwin) == FAIL)
! goto fail;
// Restore the tab-local working directory if specified
// Do this before the windows, so that the window-local directory can
***************
*** 806,812 ****
if (fputs("tcd ", fd) < 0
|| ses_put_fname(fd, tp->tp_localdir, &ssop_flags) == FAIL
|| put_eol(fd) == FAIL)
! return FAIL;
did_lcd = TRUE;
}
--- 816,822 ----
if (fputs("tcd ", fd) < 0
|| ses_put_fname(fd, tp->tp_localdir, &ssop_flags) == FAIL
|| put_eol(fd) == FAIL)
! goto fail;
did_lcd = TRUE;
}
***************
*** 815,825 ****
{
if (!ses_do_win(wp))
continue;
! if (put_view(fd, wp, wp != edited_win, &ssop_flags,
! cur_arg_idx) == FAIL)
! return FAIL;
if (nr > 1 && put_line(fd, "wincmd w") == FAIL)
! return FAIL;
next_arg_idx = wp->w_arg_idx;
}
--- 825,838 ----
{
if (!ses_do_win(wp))
continue;
! if (put_view(fd, wp, wp != edited_win, &ssop_flags, cur_arg_idx
! #ifdef FEAT_TERMINAL
! , &terminal_bufs
! #endif
! ) == FAIL)
! goto fail;
if (nr > 1 && put_line(fd, "wincmd w") == FAIL)
! goto fail;
next_arg_idx = wp->w_arg_idx;
}
***************
*** 831,842 ****
// Restore cursor to the current window if it's not the first one.
if (cnr > 1 && (fprintf(fd, "%dwincmd w", cnr) < 0
|| put_eol(fd) == FAIL))
! return FAIL;
// Restore window sizes again after jumping around in windows, because
// the current window has a minimum size while others may not.
if (nr > 1 && ses_winsizes(fd, restore_size, tab_firstwin) == FAIL)
! return FAIL;
// Don't continue in another tab page when doing only the current one
// or when at the last tab page.
--- 844,855 ----
// Restore cursor to the current window if it's not the first one.
if (cnr > 1 && (fprintf(fd, "%dwincmd w", cnr) < 0
|| put_eol(fd) == FAIL))
! goto fail;
// Restore window sizes again after jumping around in windows, because
// the current window has a minimum size while others may not.
if (nr > 1 && ses_winsizes(fd, restore_size, tab_firstwin) == FAIL)
! goto fail;
// Don't continue in another tab page when doing only the current one
// or when at the last tab page.
***************
*** 848,857 ****
{
if (fprintf(fd, "tabnext %d", tabpage_index(curtab)) < 0
|| put_eol(fd) == FAIL)
! return FAIL;
}
if (restore_stal && put_line(fd, "set stal=1") == FAIL)
! return FAIL;
// Now put the remaining buffers into the buffer list.
// This is near the end, so that when 'hidden' is set we don't create
extra
--- 861,870 ----
{
if (fprintf(fd, "tabnext %d", tabpage_index(curtab)) < 0
|| put_eol(fd) == FAIL)
! goto fail;
}
if (restore_stal && put_line(fd, "set stal=1") == FAIL)
! goto fail;
// Now put the remaining buffers into the buffer list.
// This is near the end, so that when 'hidden' is set we don't create
extra
***************
*** 872,909 ****
if (fprintf(fd, "badd +%ld ", buf->b_wininfo == NULL ? 1L
: buf->b_wininfo->wi_fpos.lnum) < 0
|| ses_fname(fd, buf, &ssop_flags, TRUE) == FAIL)
! return FAIL;
}
}
// Wipe out an empty unnamed buffer we started in.
if (put_line(fd, "if exists('s:wipebuf') && len(win_findbuf(s:wipebuf))
== 0")
== FAIL)
! return FAIL;
if (put_line(fd, " silent exe 'bwipe ' . s:wipebuf") == FAIL)
! return FAIL;
if (put_line(fd, "endif") == FAIL)
! return FAIL;
if (put_line(fd, "unlet! s:wipebuf") == FAIL)
! return FAIL;
// Re-apply 'winheight', 'winwidth' and 'shortmess'.
if (fprintf(fd, "set winheight=%ld winwidth=%ld shortmess=%s",
p_wh, p_wiw, p_shm) < 0 || put_eol(fd) == FAIL)
! return FAIL;
// Re-apply 'winminheight' and 'winminwidth'.
if (fprintf(fd, "set winminheight=%ld winminwidth=%ld",
p_wmh, p_wmw) < 0 || put_eol(fd) == FAIL)
! return FAIL;
// Lastly, execute the x.vim file if it exists.
if (put_line(fd, "let s:sx = expand(\"<sfile>:p:r\").\"x.vim\"") == FAIL
|| put_line(fd, "if filereadable(s:sx)") == FAIL
|| put_line(fd, " exe \"source \" . fnameescape(s:sx)") == FAIL
|| put_line(fd, "endif") == FAIL)
! return FAIL;
! return OK;
}
/*
--- 885,927 ----
if (fprintf(fd, "badd +%ld ", buf->b_wininfo == NULL ? 1L
: buf->b_wininfo->wi_fpos.lnum) < 0
|| ses_fname(fd, buf, &ssop_flags, TRUE) == FAIL)
! goto fail;
}
}
// Wipe out an empty unnamed buffer we started in.
if (put_line(fd, "if exists('s:wipebuf') && len(win_findbuf(s:wipebuf))
== 0")
== FAIL)
! goto fail;
if (put_line(fd, " silent exe 'bwipe ' . s:wipebuf") == FAIL)
! goto fail;
if (put_line(fd, "endif") == FAIL)
! goto fail;
if (put_line(fd, "unlet! s:wipebuf") == FAIL)
! goto fail;
// Re-apply 'winheight', 'winwidth' and 'shortmess'.
if (fprintf(fd, "set winheight=%ld winwidth=%ld shortmess=%s",
p_wh, p_wiw, p_shm) < 0 || put_eol(fd) == FAIL)
! goto fail;
// Re-apply 'winminheight' and 'winminwidth'.
if (fprintf(fd, "set winminheight=%ld winminwidth=%ld",
p_wmh, p_wmw) < 0 || put_eol(fd) == FAIL)
! goto fail;
// Lastly, execute the x.vim file if it exists.
if (put_line(fd, "let s:sx = expand(\"<sfile>:p:r\").\"x.vim\"") == FAIL
|| put_line(fd, "if filereadable(s:sx)") == FAIL
|| put_line(fd, " exe \"source \" . fnameescape(s:sx)") == FAIL
|| put_line(fd, "endif") == FAIL)
! goto fail;
! ret = OK;
! fail:
! #ifdef FEAT_TERMINAL
! hash_clear_all(&terminal_bufs, 0);
! #endif
! return ret;
}
/*
***************
*** 1084,1089 ****
--- 1102,1112 ----
char_u *viewFile = NULL;
unsigned *flagp;
#endif
+ #ifdef FEAT_TERMINAL
+ hashtab_T terminal_bufs;
+
+ hash_init(&terminal_bufs);
+ #endif
if (eap->cmdidx == CMD_mksession || eap->cmdidx == CMD_mkview)
{
***************
*** 1240,1247 ****
}
else
{
! failed |= (put_view(fd, curwin, !using_vdir, flagp,
! -1) == FAIL);
}
if (put_line(fd, "let &so = s:so_save | let &siso = s:siso_save")
== FAIL)
--- 1263,1273 ----
}
else
{
! failed |= (put_view(fd, curwin, !using_vdir, flagp, -1
! #ifdef FEAT_TERMINAL
! , &terminal_bufs
! #endif
! ) == FAIL);
}
if (put_line(fd, "let &so = s:so_save | let &siso = s:siso_save")
== FAIL)
*** ../vim-8.2.1661/src/terminal.c 2020-09-06 18:22:47.245500821 +0200
--- src/terminal.c 2020-09-11 20:31:11.743675062 +0200
***************
*** 935,943 ****
* 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
--- 935,964 ----
* Return FAIL if writing fails.
*/
int
! term_write_session(FILE *fd, win_T *wp, hashtab_T *terminal_bufs)
{
! const int bufnr = wp->w_buffer->b_fnum;
! term_T *term = wp->w_buffer->b_term;
!
! if (wp->w_buffer->b_nwindows > 1)
! {
! // There are multiple views into this terminal buffer. We don't want to
! // create the terminal multiple times. If it's the first time, create,
! // otherwise link to the first buffer.
! char id_as_str[NUMBUFLEN];
! hashitem_T *entry;
!
! vim_snprintf(id_as_str, sizeof(id_as_str), "%d", bufnr);
!
! entry = hash_find(terminal_bufs, (char_u *)id_as_str);
! if (!HASHITEM_EMPTY(entry))
! {
! // we've already opened this terminal buffer
! if (fprintf(fd, "execute 'buffer ' . s:term_buf_%d", bufnr) < 0)
! return FAIL;
! return put_eol(fd);
! }
! }
// Create the terminal and run the command. This is not without
// risk, but let's assume the user only creates a session when this
***************
*** 951,956 ****
--- 972,990 ----
#endif
if (term->tl_command != NULL && fputs((char *)term->tl_command, fd) < 0)
return FAIL;
+ if (put_eol(fd) != OK)
+ return FAIL;
+
+ if (fprintf(fd, "let s:term_buf_%d = bufnr()", bufnr) < 0)
+ return FAIL;
+
+ if (wp->w_buffer->b_nwindows > 1)
+ {
+ char *hash_key = alloc(NUMBUFLEN);
+
+ vim_snprintf(hash_key, NUMBUFLEN, "%d", bufnr);
+ hash_add(terminal_bufs, (char_u *)hash_key);
+ }
return put_eol(fd);
}
*** ../vim-8.2.1661/src/testdir/test_mksession.vim 2020-08-12
18:50:31.883655785 +0200
--- src/testdir/test_mksession.vim 2020-09-11 20:22:35.637566597 +0200
***************
*** 455,460 ****
--- 455,486 ----
call delete('Xtest_mks.out')
endfunc
+ func Test_mksession_terminal_shared_windows()
+ terminal
+ let term_buf = bufnr()
+ new
+ execute "buffer" term_buf
+ mksession! Xtest_mks.out
+
+ let lines = readfile('Xtest_mks.out')
+ let found_creation = 0
+ let found_use = 0
+
+ for line in lines
+ if line =~ '^terminal'
+ let found_creation = 1
+ call assert_match('terminal ++curwin ++cols=\d\+ ++rows=\d\+', line)
+ elseif line =~ "^execute 'buffer ' . s:term_buf_" . term_buf . "$"
+ let found_use = 1
+ endif
+ endfor
+
+ call assert_true(found_creation && found_use)
+
+ call StopShellInTerminal(term_buf)
+ call delete('Xtest_mks.out')
+ endfunc
+
endif " has('terminal')
" Test :mkview with a file argument.
*** ../vim-8.2.1661/src/version.c 2020-09-11 19:28:16.019436292 +0200
--- src/version.c 2020-09-11 20:24:34.165112150 +0200
***************
*** 752,753 ****
--- 752,755 ----
{ /* Add new patch number below this line */
+ /**/
+ 1662,
/**/
--
BRIDGEKEEPER: What is your favorite colour?
GAWAIN: Blue ... No yelloooooww!
"Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD
/// Bram Moolenaar -- [email protected] -- 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 ///
--
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
---
You received this message because you are subscribed to the Google Groups
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/vim_dev/202009111837.08BIbt3E944604%40masaka.moolenaar.net.