Full restore
If you want to restore layout with multiple termina buffers, you must restore other windows and buffers too, so it's in fact a full buffer layout restore, it's usage is not limited to terminal buffers. Three things must be done to achive that:
- Split windows according to saved layout. We can use result of
winlayout()
to do this.
- Load buffer in each window. We must record
bufnr
in each window when we save winlayout()
.
- Resize each window. We can use
:h winrestcmd()
to do this.
Two recursions are used to save and restore layout:
command SaveBufferLayout call s:save_buffer_layout()
command RestoreBufferLayout call s:restore_buffer_layout()
function s:save_buffer_layout() abort
let s:buf_layout = winlayout()
let s:resize_cmd = winrestcmd()
call s:add_buf_to_layout(s:buf_layout)
endfunction
" add bufnr to leaf
function s:add_buf_to_layout(layout) abort
if a:layout[0] ==# 'leaf'
call add(a:layout, winbufnr(a:layout[1]))
else
for child_layout in a:layout[1]
call s:add_buf_to_layout(child_layout)
endfor
endif
endfunction
function s:restore_buffer_layout() abort
if !has_key(s:, 'buf_layout')
return
endif
" create clean window
new
wincmd o
" recursively restore buffers
call s:apply_layout(s:buf_layout)
" resize
exe s:resize_cmd
endfunction
function s:apply_layout(layout) abort
if a:layout[0] ==# 'leaf'
if bufexists(a:layout[2])
exe printf('b %d', a:layout[2])
endif
else
" split cols or rows, split n-1 times
let split_method = a:layout[0] ==# 'col' ? 'rightbelow split' : 'rightbelow vsplit'
let wins = [win_getid()]
for child_layout in a:layout[1][1:]
exe split_method
let wins += [win_getid()]
endfor
" recursive into child windows
for index in range(len(wins) )
call win_gotoid(wins[index])
call s:apply_layout(a:layout[1][index])
endfor
endif
endfunction
It's possible to make it work for multiple layouts, you can the newest script here.
Original manual workaround
It's trouble and buggy to toggle multiple terminal windows, you can step back, split by manual, then load the hided terminal buffer by mark
or buffer number.
You can create global mark for terminal buffer by <c-w>:mark X
or <c-w>:kX
, hide it, and load it back with 'X
, here is a map that let you create mark with <c-w>m{capital letter}
:
tnoremap <expr> <c-w>m printf('<c-w>:mark %s<cr>', nr2char(getchar()))
Note that you can also combine the split
and load
together in a single command:
new +'X
You can also use :ls R
or :ls F
to list terminal buffer.
winlayout
:h winlayout()
The result is a nested List containing the layout of windows
in a tabpage.
...
For a leaf window, it returns:
['leaf', {winid}]
For horizontally split windows, which form a column, it
returns:
['col', [{nested list of windows}]]
For vertically split windows, which form a row, it returns:
['row', [{nested list of windows}]]
leaf
item contains :h winid
, it's an uique id across tabs
.
This is a horizontally splited col
, it has 3 leafs:
┌─────┐
│ │
├─────┤
│ │
├─────┤
│ │
└─────┘
This is a vertically splited row
, it has 3 leafs:
┌────┬────┬────┐
│ │ │ │
└────┴────┴────┘
They can be nested
, this layout is a row
, it's splited into two col
, each
col
is splited into two leafs:
┌──────┬──────┐
│ 1 │ 3 │
├──────│──────┤
│ 2 │ 4 │
└──────┴──────┘
this layout is a col
, it's splited into two row
, each
row
is splited into two leafs:
┌──────┬──────┐
│ 1 │ 2 │
├─────────────┤
│ 3 │ 4 │
└──────┴──────┘
Note that :h winnr()
for above two layouts are different.