mirror of
https://github.com/vim/vim.git
synced 2025-09-06 21:53:38 -04:00
patch 9.1.0116: win_split_ins may not check available room
Problem: win_split_ins has no check for E36 when moving an existing window Solution: check for room and fix the issues in f_win_splitmove() (Sean Dewar) win_split_ins has no check for E36 when moving an existing window, allowing for layouts with many overlapping zero-sized windows to be created (which may also cause drawing issues with tablines and such). f_win_splitmove also has some bugs. So check for room and fix the issues in f_win_splitmove. Handle failure in the two relevant win_split_ins callers by restoring the original layout, and factor the common logic into win_splitmove. Don't check for room when opening an autocommand window, as it's a temporary window that's rarely interacted with or drawn anyhow, and is rather important for some autocommands. Issues fixed in f_win_splitmove: - Error if splitting is disallowed. - Fix heap-use-after-frees if autocommands fired from switching to "targetwin" close "wp" or "oldwin". - Fix splitting the wrong window if autocommands fired from switching to "targetwin" switch to a different window. - Ensure -1 is returned for all errors. Also handle allocation failure a bit earlier in make_snapshot (callers, except win_splitmove, don't really care if a snapshot can't be made, so just ignore the return value). Note: Test_smoothscroll_in_zero_width_window failed after these changes with E36, as it was using the previous behaviour to create a zero-width window. I've fixed the test such that it fails with UBSAN as expected when v9.0.1367 is reverted (and simplified it too). related: #14042 Signed-off-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
parent
15935e7f54
commit
0fd44a5ad8
@ -1607,7 +1607,7 @@ aucmd_prepbuf(
|
|||||||
p_acd = FALSE;
|
p_acd = FALSE;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
(void)win_split_ins(0, WSP_TOP, auc_win, 0);
|
(void)win_split_ins(0, WSP_TOP | WSP_FORCE_ROOM, auc_win, 0);
|
||||||
(void)win_comp_pos(); // recompute window positions
|
(void)win_comp_pos(); // recompute window positions
|
||||||
p_ea = save_ea;
|
p_ea = save_ea;
|
||||||
#ifdef FEAT_AUTOCHDIR
|
#ifdef FEAT_AUTOCHDIR
|
||||||
|
@ -952,60 +952,18 @@ f_win_screenpos(typval_T *argvars, typval_T *rettv)
|
|||||||
list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_wincol + 1);
|
list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_wincol + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Move the window wp into a new split of targetwin in a given direction
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
win_move_into_split(win_T *wp, win_T *targetwin, int size, int flags)
|
|
||||||
{
|
|
||||||
int dir;
|
|
||||||
int height = wp->w_height;
|
|
||||||
win_T *oldwin = curwin;
|
|
||||||
|
|
||||||
if (wp == targetwin)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Jump to the target window
|
|
||||||
if (curwin != targetwin)
|
|
||||||
win_goto(targetwin);
|
|
||||||
|
|
||||||
// Remove the old window and frame from the tree of frames
|
|
||||||
(void)winframe_remove(wp, &dir, NULL);
|
|
||||||
win_remove(wp, NULL);
|
|
||||||
last_status(FALSE); // may need to remove last status line
|
|
||||||
(void)win_comp_pos(); // recompute window positions
|
|
||||||
|
|
||||||
// Split a window on the desired side and put the old window there
|
|
||||||
(void)win_split_ins(size, flags, wp, dir);
|
|
||||||
|
|
||||||
// If splitting horizontally, try to preserve height
|
|
||||||
if (size == 0 && !(flags & WSP_VERT))
|
|
||||||
{
|
|
||||||
win_setheight_win(height, wp);
|
|
||||||
if (p_ea)
|
|
||||||
win_equal(wp, TRUE, 'v');
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(FEAT_GUI)
|
|
||||||
// When 'guioptions' includes 'L' or 'R' may have to remove or add
|
|
||||||
// scrollbars. Have to update them anyway.
|
|
||||||
gui_may_update_scrollbars();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (oldwin != curwin)
|
|
||||||
win_goto(oldwin);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "win_splitmove()" function
|
* "win_splitmove()" function
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
f_win_splitmove(typval_T *argvars, typval_T *rettv)
|
f_win_splitmove(typval_T *argvars, typval_T *rettv)
|
||||||
{
|
{
|
||||||
win_T *wp;
|
win_T *wp, *targetwin;
|
||||||
win_T *targetwin;
|
win_T *oldwin = curwin;
|
||||||
int flags = 0, size = 0;
|
int flags = 0, size = 0;
|
||||||
|
|
||||||
|
rettv->vval.v_number = -1;
|
||||||
|
|
||||||
if (in_vim9script()
|
if (in_vim9script()
|
||||||
&& (check_for_number_arg(argvars, 0) == FAIL
|
&& (check_for_number_arg(argvars, 0) == FAIL
|
||||||
|| check_for_number_arg(argvars, 1) == FAIL
|
|| check_for_number_arg(argvars, 1) == FAIL
|
||||||
@ -1020,7 +978,6 @@ f_win_splitmove(typval_T *argvars, typval_T *rettv)
|
|||||||
|| win_valid_popup(wp) || win_valid_popup(targetwin))
|
|| win_valid_popup(wp) || win_valid_popup(targetwin))
|
||||||
{
|
{
|
||||||
emsg(_(e_invalid_window_number));
|
emsg(_(e_invalid_window_number));
|
||||||
rettv->vval.v_number = -1;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1040,7 +997,24 @@ f_win_splitmove(typval_T *argvars, typval_T *rettv)
|
|||||||
size = (int)dict_get_number(d, "size");
|
size = (int)dict_get_number(d, "size");
|
||||||
}
|
}
|
||||||
|
|
||||||
win_move_into_split(wp, targetwin, size, flags);
|
// Check if we can split the target before we bother switching windows.
|
||||||
|
if (check_split_disallowed(targetwin) == FAIL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (curwin != targetwin)
|
||||||
|
win_goto(targetwin);
|
||||||
|
|
||||||
|
// Autocommands may have sent us elsewhere or closed "wp" or "oldwin".
|
||||||
|
if (curwin == targetwin && win_valid(wp))
|
||||||
|
{
|
||||||
|
if (win_splitmove(wp, size, flags) == OK)
|
||||||
|
rettv->vval.v_number = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
emsg(_(e_autocommands_caused_command_to_abort));
|
||||||
|
|
||||||
|
if (oldwin != curwin && win_valid(oldwin))
|
||||||
|
win_goto(oldwin);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -4,7 +4,9 @@ win_T *prevwin_curwin(void);
|
|||||||
win_T *swbuf_goto_win_with_buf(buf_T *buf);
|
win_T *swbuf_goto_win_with_buf(buf_T *buf);
|
||||||
void do_window(int nchar, long Prenum, int xchar);
|
void do_window(int nchar, long Prenum, int xchar);
|
||||||
void get_wincmd_addr_type(char_u *arg, exarg_T *eap);
|
void get_wincmd_addr_type(char_u *arg, exarg_T *eap);
|
||||||
|
int check_split_disallowed(win_T *wp);
|
||||||
int win_split(int size, int flags);
|
int win_split(int size, int flags);
|
||||||
|
int win_splitmove(win_T *wp, int size, int flags);
|
||||||
int win_split_ins(int size, int flags, win_T *new_wp, int dir);
|
int win_split_ins(int size, int flags, win_T *new_wp, int dir);
|
||||||
int win_valid_popup(win_T *win);
|
int win_valid_popup(win_T *win);
|
||||||
int win_valid(win_T *win);
|
int win_valid(win_T *win);
|
||||||
@ -88,7 +90,7 @@ int only_one_window(void);
|
|||||||
void check_lnums(int do_curwin);
|
void check_lnums(int do_curwin);
|
||||||
void check_lnums_nested(int do_curwin);
|
void check_lnums_nested(int do_curwin);
|
||||||
void reset_lnums(void);
|
void reset_lnums(void);
|
||||||
void make_snapshot(int idx);
|
int make_snapshot(int idx);
|
||||||
void restore_snapshot(int idx, int close_curwin);
|
void restore_snapshot(int idx, int close_curwin);
|
||||||
int win_hasvertsplit(void);
|
int win_hasvertsplit(void);
|
||||||
int get_win_number(win_T *wp, win_T *first_win);
|
int get_win_number(win_T *wp, win_T *first_win);
|
||||||
|
@ -290,6 +290,16 @@ func Test_window_split_no_room()
|
|||||||
for s in range(1, hor_split_count) | split | endfor
|
for s in range(1, hor_split_count) | split | endfor
|
||||||
call assert_fails('split', 'E36:')
|
call assert_fails('split', 'E36:')
|
||||||
|
|
||||||
|
botright vsplit
|
||||||
|
wincmd |
|
||||||
|
let layout = winlayout()
|
||||||
|
let restcmd = winrestcmd()
|
||||||
|
call assert_fails('wincmd J', 'E36:')
|
||||||
|
call assert_fails('wincmd K', 'E36:')
|
||||||
|
call assert_equal(layout, winlayout())
|
||||||
|
call assert_equal(restcmd, winrestcmd())
|
||||||
|
only
|
||||||
|
|
||||||
" N vertical windows need >= 2*(N - 1) + 1 columns:
|
" N vertical windows need >= 2*(N - 1) + 1 columns:
|
||||||
" - 1 column + 1 separator for each window (except last window)
|
" - 1 column + 1 separator for each window (except last window)
|
||||||
" - 1 column for the last window which does not have separator
|
" - 1 column for the last window which does not have separator
|
||||||
@ -302,7 +312,35 @@ func Test_window_split_no_room()
|
|||||||
for s in range(1, ver_split_count) | vsplit | endfor
|
for s in range(1, ver_split_count) | vsplit | endfor
|
||||||
call assert_fails('vsplit', 'E36:')
|
call assert_fails('vsplit', 'E36:')
|
||||||
|
|
||||||
|
split
|
||||||
|
wincmd |
|
||||||
|
let layout = winlayout()
|
||||||
|
let restcmd = winrestcmd()
|
||||||
|
call assert_fails('wincmd H', 'E36:')
|
||||||
|
call assert_fails('wincmd L', 'E36:')
|
||||||
|
call assert_equal(layout, winlayout())
|
||||||
|
call assert_equal(restcmd, winrestcmd())
|
||||||
|
|
||||||
|
" Check that the last statusline isn't lost.
|
||||||
|
set laststatus=0
|
||||||
|
let restcmd = winrestcmd()
|
||||||
|
wincmd j
|
||||||
|
call setwinvar(winnr('k'), '&statusline', '@#')
|
||||||
|
let last_stl_row = win_screenpos(0)[0] - 1
|
||||||
|
redraw
|
||||||
|
call assert_equal('@#|', GetScreenStr(last_stl_row))
|
||||||
|
call assert_equal('~ |', GetScreenStr(&lines - &cmdheight))
|
||||||
|
call assert_fails('wincmd H', 'E36:')
|
||||||
|
call assert_fails('wincmd L', 'E36:')
|
||||||
|
call assert_equal(layout, winlayout())
|
||||||
|
call assert_equal(restcmd, winrestcmd())
|
||||||
|
call setwinvar(winnr('k'), '&statusline', '=-')
|
||||||
|
redraw
|
||||||
|
call assert_equal('=-|', GetScreenStr(last_stl_row))
|
||||||
|
call assert_equal('~ |', GetScreenStr(&lines - &cmdheight))
|
||||||
|
|
||||||
%bw!
|
%bw!
|
||||||
|
set laststatus&
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Test_window_exchange()
|
func Test_window_exchange()
|
||||||
@ -1097,6 +1135,44 @@ func Test_win_splitmove()
|
|||||||
tabnew
|
tabnew
|
||||||
call assert_fails('call win_splitmove(1, win_getid(1, 1))', 'E957:')
|
call assert_fails('call win_splitmove(1, win_getid(1, 1))', 'E957:')
|
||||||
tabclose
|
tabclose
|
||||||
|
|
||||||
|
split
|
||||||
|
augroup WinSplitMove
|
||||||
|
au!
|
||||||
|
au WinEnter * ++once call win_gotoid(win_getid(winnr('#')))
|
||||||
|
augroup END
|
||||||
|
call assert_fails('call win_splitmove(winnr(), winnr("#"))', 'E855:')
|
||||||
|
|
||||||
|
augroup WinSplitMove
|
||||||
|
au!
|
||||||
|
au WinLeave * ++once quit
|
||||||
|
augroup END
|
||||||
|
call assert_fails('call win_splitmove(winnr(), winnr("#"))', 'E855:')
|
||||||
|
|
||||||
|
split
|
||||||
|
split
|
||||||
|
augroup WinSplitMove
|
||||||
|
au!
|
||||||
|
au WinEnter * ++once let s:triggered = v:true
|
||||||
|
\| call assert_fails('call win_splitmove(winnr("$"), winnr())', 'E242:')
|
||||||
|
augroup END
|
||||||
|
quit
|
||||||
|
call assert_equal(v:true, s:triggered)
|
||||||
|
unlet! s:triggered
|
||||||
|
|
||||||
|
new
|
||||||
|
augroup WinSplitMove
|
||||||
|
au!
|
||||||
|
au BufHidden * ++once let s:triggered = v:true
|
||||||
|
\| call assert_fails('call win_splitmove(winnr("#"), winnr())', 'E1159:')
|
||||||
|
augroup END
|
||||||
|
hide
|
||||||
|
call assert_equal(v:true, s:triggered)
|
||||||
|
unlet! s:triggered
|
||||||
|
|
||||||
|
au! WinSplitMove
|
||||||
|
augroup! WinSplitMove
|
||||||
|
%bw!
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
" Test for the :only command
|
" Test for the :only command
|
||||||
@ -2061,23 +2137,75 @@ func Test_new_help_window_on_error()
|
|||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Test_smoothscroll_in_zero_width_window()
|
func Test_smoothscroll_in_zero_width_window()
|
||||||
let save_lines = &lines
|
set cpo+=n number smoothscroll
|
||||||
let save_columns = &columns
|
set winwidth=99999 winminwidth=0
|
||||||
|
|
||||||
winsize 0 24
|
vsplit
|
||||||
set cpo+=n
|
call assert_equal(0, winwidth(winnr('#')))
|
||||||
exe "noremap 0 \<C-W>n\<C-W>L"
|
call win_execute(win_getid(winnr('#')), "norm! \<C-Y>")
|
||||||
norm 000000
|
|
||||||
set number smoothscroll
|
|
||||||
exe "norm \<C-Y>"
|
|
||||||
|
|
||||||
only!
|
only!
|
||||||
let &lines = save_lines
|
set winwidth& winminwidth&
|
||||||
let &columns = save_columns
|
set cpo-=n nonumber nosmoothscroll
|
||||||
set cpo-=n
|
|
||||||
unmap 0
|
|
||||||
set nonumber nosmoothscroll
|
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func Test_splitmove_flatten_frame()
|
||||||
|
split
|
||||||
|
vsplit
|
||||||
|
|
||||||
|
wincmd L
|
||||||
|
let layout = winlayout()
|
||||||
|
wincmd K
|
||||||
|
wincmd L
|
||||||
|
call assert_equal(winlayout(), layout)
|
||||||
|
|
||||||
|
only!
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_splitmove_autocmd_window_no_room()
|
||||||
|
" Open as many windows as possible
|
||||||
|
while v:true
|
||||||
|
try
|
||||||
|
split
|
||||||
|
catch /E36:/
|
||||||
|
break
|
||||||
|
endtry
|
||||||
|
endwhile
|
||||||
|
while v:true
|
||||||
|
try
|
||||||
|
vsplit
|
||||||
|
catch /E36:/
|
||||||
|
break
|
||||||
|
endtry
|
||||||
|
endwhile
|
||||||
|
|
||||||
|
wincmd j
|
||||||
|
vsplit
|
||||||
|
call assert_fails('wincmd H', 'E36:')
|
||||||
|
call assert_fails('wincmd J', 'E36:')
|
||||||
|
call assert_fails('wincmd K', 'E36:')
|
||||||
|
call assert_fails('wincmd L', 'E36:')
|
||||||
|
|
||||||
|
edit unload me
|
||||||
|
enew
|
||||||
|
bunload! unload\ me
|
||||||
|
augroup SplitMoveAucmdWin
|
||||||
|
au!
|
||||||
|
au BufEnter * ++once let s:triggered = v:true
|
||||||
|
\| call assert_equal('autocmd', win_gettype())
|
||||||
|
augroup END
|
||||||
|
let layout = winlayout()
|
||||||
|
let restcmd = winrestcmd()
|
||||||
|
" bufload opening the autocommand window shouldn't give E36.
|
||||||
|
call bufload('unload me')
|
||||||
|
call assert_equal(v:true, s:triggered)
|
||||||
|
call assert_equal(winlayout(), layout)
|
||||||
|
call assert_equal(winrestcmd(), restcmd)
|
||||||
|
|
||||||
|
unlet! s:triggered
|
||||||
|
au! SplitMoveAucmdWin
|
||||||
|
augroup! SplitMoveAucmdWin
|
||||||
|
%bw!
|
||||||
|
endfunc
|
||||||
|
|
||||||
" vim: shiftwidth=2 sts=2 expandtab
|
" vim: shiftwidth=2 sts=2 expandtab
|
||||||
|
@ -704,6 +704,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 */
|
||||||
|
/**/
|
||||||
|
116,
|
||||||
/**/
|
/**/
|
||||||
115,
|
115,
|
||||||
/**/
|
/**/
|
||||||
|
@ -1255,6 +1255,7 @@ extern int (*dyn_libintl_wputenv)(const wchar_t *envstring);
|
|||||||
#define WSP_BELOW 0x40 // put new window below/right
|
#define WSP_BELOW 0x40 // put new window below/right
|
||||||
#define WSP_ABOVE 0x80 // put new window above/left
|
#define WSP_ABOVE 0x80 // put new window above/left
|
||||||
#define WSP_NEWLOC 0x100 // don't copy location list
|
#define WSP_NEWLOC 0x100 // don't copy location list
|
||||||
|
#define WSP_FORCE_ROOM 0x200 // ignore "not enough room" errors
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* arguments for gui_set_shellsize()
|
* arguments for gui_set_shellsize()
|
||||||
|
228
src/window.c
228
src/window.c
@ -17,7 +17,6 @@ static void frame_setheight(frame_T *curfrp, int height);
|
|||||||
static void frame_setwidth(frame_T *curfrp, int width);
|
static void frame_setwidth(frame_T *curfrp, int width);
|
||||||
static void win_exchange(long);
|
static void win_exchange(long);
|
||||||
static void win_rotate(int, int);
|
static void win_rotate(int, int);
|
||||||
static void win_totop(int size, int flags);
|
|
||||||
static void win_equal_rec(win_T *next_curwin, int current, frame_T *topfr, int dir, int col, int row, int width, int height);
|
static void win_equal_rec(win_T *next_curwin, int current, frame_T *topfr, int dir, int col, int row, int width, int height);
|
||||||
static void trigger_winnewpre(void);
|
static void trigger_winnewpre(void);
|
||||||
static void trigger_winclosed(win_T *win);
|
static void trigger_winclosed(win_T *win);
|
||||||
@ -31,7 +30,7 @@ static void win_fix_cursor(int normal);
|
|||||||
static void frame_new_height(frame_T *topfrp, int height, int topfirst, int wfh);
|
static void frame_new_height(frame_T *topfrp, int height, int topfirst, int wfh);
|
||||||
static int frame_fixed_height(frame_T *frp);
|
static int frame_fixed_height(frame_T *frp);
|
||||||
static int frame_fixed_width(frame_T *frp);
|
static int frame_fixed_width(frame_T *frp);
|
||||||
static void frame_add_statusline(frame_T *frp);
|
static void frame_add_statusline(frame_T *frp, int adjust_winheight);
|
||||||
static void frame_new_width(frame_T *topfrp, int width, int leftfirst, int wfw);
|
static void frame_new_width(frame_T *topfrp, int width, int leftfirst, int wfw);
|
||||||
static void frame_add_vsep(frame_T *frp);
|
static void frame_add_vsep(frame_T *frp);
|
||||||
static int frame_minwidth(frame_T *topfrp, win_T *next_curwin);
|
static int frame_minwidth(frame_T *topfrp, win_T *next_curwin);
|
||||||
@ -55,12 +54,15 @@ static void win_goto_hor(int left, long count);
|
|||||||
static void frame_add_height(frame_T *frp, int n);
|
static void frame_add_height(frame_T *frp, int n);
|
||||||
static void last_status_rec(frame_T *fr, int statusline);
|
static void last_status_rec(frame_T *fr, int statusline);
|
||||||
|
|
||||||
static void make_snapshot_rec(frame_T *fr, frame_T **frp);
|
static int make_snapshot_rec(frame_T *fr, frame_T **frp, int snap_wins);
|
||||||
static void clear_snapshot(tabpage_T *tp, int idx);
|
static void clear_snapshot(tabpage_T *tp, int idx);
|
||||||
static void clear_snapshot_rec(frame_T *fr);
|
static void clear_snapshot_rec(frame_T *fr);
|
||||||
static int check_snapshot_rec(frame_T *sn, frame_T *fr);
|
static int check_snapshot_rec(frame_T *sn, frame_T *fr);
|
||||||
static win_T *restore_snapshot_rec(frame_T *sn, frame_T *fr);
|
static win_T *restore_snapshot_rec(frame_T *sn, frame_T *fr);
|
||||||
static win_T *get_snapshot_curwin(int idx);
|
static win_T *get_snapshot_curwin(int idx);
|
||||||
|
static frame_T *make_full_snapshot(void);
|
||||||
|
static void restore_full_snapshot(frame_T *sn);
|
||||||
|
static void restore_full_snapshot_rec(frame_T *sn);
|
||||||
|
|
||||||
static int frame_check_height(frame_T *topfrp, int height);
|
static int frame_check_height(frame_T *topfrp, int height);
|
||||||
static int frame_check_width(frame_T *topfrp, int width);
|
static int frame_check_width(frame_T *topfrp, int width);
|
||||||
@ -494,9 +496,15 @@ newwindow:
|
|||||||
case 'H':
|
case 'H':
|
||||||
case 'L':
|
case 'L':
|
||||||
CHECK_CMDWIN;
|
CHECK_CMDWIN;
|
||||||
win_totop((int)Prenum,
|
if (ONE_WINDOW)
|
||||||
((nchar == 'H' || nchar == 'L') ? WSP_VERT : 0)
|
beep_flush();
|
||||||
| ((nchar == 'H' || nchar == 'K') ? WSP_TOP : WSP_BOT));
|
else
|
||||||
|
{
|
||||||
|
int dir = ((nchar == 'H' || nchar == 'L') ? WSP_VERT : 0)
|
||||||
|
| ((nchar == 'H' || nchar == 'K') ? WSP_TOP : WSP_BOT);
|
||||||
|
|
||||||
|
(void)win_splitmove(curwin, (int)Prenum, dir);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// make all windows the same width and/or height
|
// make all windows the same width and/or height
|
||||||
@ -858,18 +866,18 @@ cmd_with_count(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If "split_disallowed" is set give an error and return FAIL.
|
* If "split_disallowed" is set for "wp", give an error and return FAIL.
|
||||||
* Otherwise return OK.
|
* Otherwise return OK.
|
||||||
*/
|
*/
|
||||||
static int
|
int
|
||||||
check_split_disallowed(void)
|
check_split_disallowed(win_T *wp)
|
||||||
{
|
{
|
||||||
if (split_disallowed > 0)
|
if (split_disallowed > 0)
|
||||||
{
|
{
|
||||||
emsg(_(e_cant_split_window_while_closing_another));
|
emsg(_(e_cant_split_window_while_closing_another));
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
if (curwin->w_buffer->b_locked_split)
|
if (wp->w_buffer->b_locked_split)
|
||||||
{
|
{
|
||||||
emsg(_(e_cannot_split_window_when_closing_buffer));
|
emsg(_(e_cannot_split_window_when_closing_buffer));
|
||||||
return FAIL;
|
return FAIL;
|
||||||
@ -898,7 +906,7 @@ win_split(int size, int flags)
|
|||||||
if (ERROR_IF_ANY_POPUP_WINDOW)
|
if (ERROR_IF_ANY_POPUP_WINDOW)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
if (check_split_disallowed() == FAIL)
|
if (check_split_disallowed(curwin) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
// When the ":tab" modifier was used open a new tab page instead.
|
// When the ":tab" modifier was used open a new tab page instead.
|
||||||
@ -968,7 +976,7 @@ win_split_ins(
|
|||||||
// add a status line when p_ls == 1 and splitting the first window
|
// add a status line when p_ls == 1 and splitting the first window
|
||||||
if (ONE_WINDOW && p_ls == 1 && oldwin->w_status_height == 0)
|
if (ONE_WINDOW && p_ls == 1 && oldwin->w_status_height == 0)
|
||||||
{
|
{
|
||||||
if (VISIBLE_HEIGHT(oldwin) <= p_wmh && new_wp == NULL)
|
if (!(flags & WSP_FORCE_ROOM) && VISIBLE_HEIGHT(oldwin) <= p_wmh)
|
||||||
{
|
{
|
||||||
emsg(_(e_not_enough_room));
|
emsg(_(e_not_enough_room));
|
||||||
goto theend;
|
goto theend;
|
||||||
@ -1026,7 +1034,7 @@ win_split_ins(
|
|||||||
available = oldwin->w_frame->fr_width;
|
available = oldwin->w_frame->fr_width;
|
||||||
needed += minwidth;
|
needed += minwidth;
|
||||||
}
|
}
|
||||||
if (available < needed && new_wp == NULL)
|
if (!(flags & WSP_FORCE_ROOM) && available < needed)
|
||||||
{
|
{
|
||||||
emsg(_(e_not_enough_room));
|
emsg(_(e_not_enough_room));
|
||||||
goto theend;
|
goto theend;
|
||||||
@ -1109,7 +1117,7 @@ win_split_ins(
|
|||||||
available = oldwin->w_frame->fr_height;
|
available = oldwin->w_frame->fr_height;
|
||||||
needed += minheight;
|
needed += minheight;
|
||||||
}
|
}
|
||||||
if (available < needed && new_wp == NULL)
|
if (!(flags & WSP_FORCE_ROOM) && available < needed)
|
||||||
{
|
{
|
||||||
emsg(_(e_not_enough_room));
|
emsg(_(e_not_enough_room));
|
||||||
goto theend;
|
goto theend;
|
||||||
@ -1360,7 +1368,7 @@ win_split_ins(
|
|||||||
if (!((flags & WSP_BOT) && p_ls == 0))
|
if (!((flags & WSP_BOT) && p_ls == 0))
|
||||||
new_fr_height -= STATUS_HEIGHT;
|
new_fr_height -= STATUS_HEIGHT;
|
||||||
if (flags & WSP_BOT)
|
if (flags & WSP_BOT)
|
||||||
frame_add_statusline(curfrp);
|
frame_add_statusline(curfrp, FALSE);
|
||||||
frame_new_height(curfrp, new_fr_height, flags & WSP_TOP, FALSE);
|
frame_new_height(curfrp, new_fr_height, flags & WSP_TOP, FALSE);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1900,35 +1908,69 @@ win_rotate(int upwards, int count)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Move the current window to the very top/bottom/left/right of the screen.
|
* Move "wp" into a new split in a given direction, possibly relative to the
|
||||||
|
* current window.
|
||||||
|
* "wp" must be valid in the current tabpage.
|
||||||
|
* Returns FAIL for failure, OK otherwise.
|
||||||
*/
|
*/
|
||||||
static void
|
int
|
||||||
win_totop(int size, int flags)
|
win_splitmove(win_T *wp, int size, int flags)
|
||||||
{
|
{
|
||||||
int dir;
|
int dir;
|
||||||
int height = curwin->w_height;
|
int height = wp->w_height;
|
||||||
|
frame_T *frp;
|
||||||
|
|
||||||
if (ONE_WINDOW)
|
if (ONE_WINDOW)
|
||||||
|
return OK; // nothing to do
|
||||||
|
if (check_split_disallowed(wp) == FAIL)
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
// Undoing changes to frames if splitting fails is complicated.
|
||||||
|
// Save a full snapshot to restore instead.
|
||||||
|
frp = make_full_snapshot();
|
||||||
|
if (frp == NULL)
|
||||||
{
|
{
|
||||||
beep_flush();
|
emsg(_(e_out_of_memory));
|
||||||
return;
|
return FAIL;
|
||||||
}
|
}
|
||||||
if (check_split_disallowed() == FAIL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Remove the window and frame from the tree of frames.
|
// Remove the window and frame from the tree of frames.
|
||||||
(void)winframe_remove(curwin, &dir, NULL);
|
(void)winframe_remove(wp, &dir, NULL);
|
||||||
win_remove(curwin, NULL);
|
win_remove(wp, NULL);
|
||||||
last_status(FALSE); // may need to remove last status line
|
last_status(FALSE); // may need to remove last status line
|
||||||
(void)win_comp_pos(); // recompute window positions
|
(void)win_comp_pos(); // recompute window positions
|
||||||
|
|
||||||
// Split a window on the desired side and put the window there.
|
// Split a window on the desired side and put "wp" there.
|
||||||
(void)win_split_ins(size, flags, curwin, dir);
|
if (win_split_ins(size, flags, wp, dir) == FAIL)
|
||||||
if (!(flags & WSP_VERT))
|
|
||||||
{
|
{
|
||||||
win_setheight(height);
|
// Restore the previous layout from the snapshot.
|
||||||
|
vim_free(wp->w_frame);
|
||||||
|
restore_full_snapshot(frp);
|
||||||
|
|
||||||
|
// Vertical separators to the left may have been lost. Restore them.
|
||||||
|
frp = wp->w_frame;
|
||||||
|
if (frp->fr_parent->fr_layout == FR_ROW && frp->fr_prev != NULL)
|
||||||
|
frame_add_vsep(frp->fr_prev);
|
||||||
|
|
||||||
|
// Statuslines above may have been lost. Restore them.
|
||||||
|
if (frp->fr_parent->fr_layout == FR_COL && frp->fr_prev != NULL)
|
||||||
|
frame_add_statusline(frp->fr_prev, TRUE);
|
||||||
|
|
||||||
|
win_append(wp->w_prev, wp);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
clear_snapshot_rec(frp);
|
||||||
|
|
||||||
|
// If splitting horizontally, try to preserve height.
|
||||||
|
if (size == 0 && !(flags & WSP_VERT))
|
||||||
|
{
|
||||||
|
win_setheight_win(height, wp);
|
||||||
if (p_ea)
|
if (p_ea)
|
||||||
win_equal(curwin, TRUE, 'v');
|
{
|
||||||
|
// Equalize windows. Note that win_split_ins autocommands may have
|
||||||
|
// made a window other than "wp" current.
|
||||||
|
win_equal(curwin, curwin == wp, 'v');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(FEAT_GUI)
|
#if defined(FEAT_GUI)
|
||||||
@ -1936,6 +1978,7 @@ win_totop(int size, int flags)
|
|||||||
// scrollbars. Have to update them anyway.
|
// scrollbars. Have to update them anyway.
|
||||||
gui_may_update_scrollbars();
|
gui_may_update_scrollbars();
|
||||||
#endif
|
#endif
|
||||||
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -3849,30 +3892,34 @@ frame_fixed_width(frame_T *frp)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Add a status line to windows at the bottom of "frp".
|
* Add a status line to windows at the bottom of "frp".
|
||||||
* Note: Does not check if there is room!
|
* If "adjust_winheight" is set, reduce the height of windows without a
|
||||||
|
* statusline to accommodate one; otherwise, there is no check for room!
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
frame_add_statusline(frame_T *frp)
|
frame_add_statusline(frame_T *frp, int adjust_winheight)
|
||||||
{
|
{
|
||||||
win_T *wp;
|
win_T *wp;
|
||||||
|
|
||||||
if (frp->fr_layout == FR_LEAF)
|
if (frp->fr_layout == FR_LEAF)
|
||||||
{
|
{
|
||||||
wp = frp->fr_win;
|
wp = frp->fr_win;
|
||||||
|
if (adjust_winheight && wp->w_status_height == 0
|
||||||
|
&& wp->w_height >= STATUS_HEIGHT) // don't make it negative
|
||||||
|
wp->w_height -= STATUS_HEIGHT - wp->w_status_height;
|
||||||
wp->w_status_height = STATUS_HEIGHT;
|
wp->w_status_height = STATUS_HEIGHT;
|
||||||
}
|
}
|
||||||
else if (frp->fr_layout == FR_ROW)
|
else if (frp->fr_layout == FR_ROW)
|
||||||
{
|
{
|
||||||
// Handle all the frames in the row.
|
// Handle all the frames in the row.
|
||||||
FOR_ALL_FRAMES(frp, frp->fr_child)
|
FOR_ALL_FRAMES(frp, frp->fr_child)
|
||||||
frame_add_statusline(frp);
|
frame_add_statusline(frp, adjust_winheight);
|
||||||
}
|
}
|
||||||
else // frp->fr_layout == FR_COL
|
else // frp->fr_layout == FR_COL
|
||||||
{
|
{
|
||||||
// Only need to handle the last frame in the column.
|
// Only need to handle the last frame in the column.
|
||||||
for (frp = frp->fr_child; frp->fr_next != NULL; frp = frp->fr_next)
|
for (frp = frp->fr_child; frp->fr_next != NULL; frp = frp->fr_next)
|
||||||
;
|
;
|
||||||
frame_add_statusline(frp);
|
frame_add_statusline(frp, adjust_winheight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7498,29 +7545,44 @@ reset_lnums(void)
|
|||||||
/*
|
/*
|
||||||
* Create a snapshot of the current frame sizes.
|
* Create a snapshot of the current frame sizes.
|
||||||
* "idx" is SNAP_HELP_IDX or SNAP_AUCMD_IDX.
|
* "idx" is SNAP_HELP_IDX or SNAP_AUCMD_IDX.
|
||||||
|
* Return FAIL if out of memory, OK otherwise.
|
||||||
*/
|
*/
|
||||||
void
|
int
|
||||||
make_snapshot(int idx)
|
make_snapshot(int idx)
|
||||||
{
|
{
|
||||||
clear_snapshot(curtab, idx);
|
clear_snapshot(curtab, idx);
|
||||||
make_snapshot_rec(topframe, &curtab->tp_snapshot[idx]);
|
if (make_snapshot_rec(topframe, &curtab->tp_snapshot[idx], FALSE) == FAIL)
|
||||||
|
{
|
||||||
|
clear_snapshot(curtab, idx);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static int
|
||||||
make_snapshot_rec(frame_T *fr, frame_T **frp)
|
make_snapshot_rec(frame_T *fr, frame_T **frp, int snap_wins)
|
||||||
{
|
{
|
||||||
*frp = ALLOC_CLEAR_ONE(frame_T);
|
*frp = ALLOC_CLEAR_ONE(frame_T);
|
||||||
if (*frp == NULL)
|
if (*frp == NULL)
|
||||||
return;
|
return FAIL;
|
||||||
(*frp)->fr_layout = fr->fr_layout;
|
(*frp)->fr_layout = fr->fr_layout;
|
||||||
(*frp)->fr_width = fr->fr_width;
|
(*frp)->fr_width = fr->fr_width;
|
||||||
(*frp)->fr_height = fr->fr_height;
|
(*frp)->fr_height = fr->fr_height;
|
||||||
if (fr->fr_next != NULL)
|
if (fr->fr_next != NULL)
|
||||||
make_snapshot_rec(fr->fr_next, &((*frp)->fr_next));
|
{
|
||||||
|
if (make_snapshot_rec(fr->fr_next, &((*frp)->fr_next), snap_wins)
|
||||||
|
== FAIL)
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
if (fr->fr_child != NULL)
|
if (fr->fr_child != NULL)
|
||||||
make_snapshot_rec(fr->fr_child, &((*frp)->fr_child));
|
{
|
||||||
if (fr->fr_layout == FR_LEAF && fr->fr_win == curwin)
|
if (make_snapshot_rec(fr->fr_child, &((*frp)->fr_child), snap_wins)
|
||||||
(*frp)->fr_win = curwin;
|
== FAIL)
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
if (fr->fr_layout == FR_LEAF && (snap_wins || fr->fr_win == curwin))
|
||||||
|
(*frp)->fr_win = fr->fr_win;
|
||||||
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -7657,6 +7719,86 @@ restore_snapshot_rec(frame_T *sn, frame_T *fr)
|
|||||||
return wp;
|
return wp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return a snapshot of all frames in the current tabpage and which windows are
|
||||||
|
* in them, or NULL if out of memory.
|
||||||
|
* Use clear_snapshot_rec to free the snapshot.
|
||||||
|
*/
|
||||||
|
static frame_T *
|
||||||
|
make_full_snapshot(void)
|
||||||
|
{
|
||||||
|
frame_T *frp;
|
||||||
|
|
||||||
|
if (make_snapshot_rec(topframe, &frp, TRUE) == FAIL)
|
||||||
|
{
|
||||||
|
clear_snapshot_rec(frp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return frp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Restore all frames in the full snapshot "sn" for the current tabpage.
|
||||||
|
* Caller must ensure that the screen size didn't change, no windows with frames
|
||||||
|
* in the snapshot were freed, and windows with frames not in the snapshot are
|
||||||
|
* removed from their frames!
|
||||||
|
* Doesn't restore changed window vertical separators or statuslines.
|
||||||
|
* Frees the old frames. Don't call clear_snapshot_rec on "sn" afterwards!
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
restore_full_snapshot(frame_T *sn)
|
||||||
|
{
|
||||||
|
if (sn == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
clear_snapshot_rec(topframe);
|
||||||
|
restore_full_snapshot_rec(sn);
|
||||||
|
curtab->tp_topframe = topframe = sn;
|
||||||
|
last_status(FALSE);
|
||||||
|
|
||||||
|
// If the amount of space available changed, first try setting the sizes of
|
||||||
|
// windows with 'winfix{width,height}'. If that doesn't result in the right
|
||||||
|
// size, forget about that option.
|
||||||
|
if (topframe->fr_width != Columns)
|
||||||
|
{
|
||||||
|
frame_new_width(topframe, Columns, FALSE, TRUE);
|
||||||
|
if (!frame_check_width(topframe, Columns))
|
||||||
|
frame_new_width(topframe, Columns, FALSE, FALSE);
|
||||||
|
}
|
||||||
|
if (topframe->fr_height != ROWS_AVAIL)
|
||||||
|
{
|
||||||
|
frame_new_height(topframe, ROWS_AVAIL, FALSE, TRUE);
|
||||||
|
if (!frame_check_height(topframe, ROWS_AVAIL))
|
||||||
|
frame_new_height(topframe, ROWS_AVAIL, FALSE, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
win_comp_pos();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
restore_full_snapshot_rec(frame_T *sn)
|
||||||
|
{
|
||||||
|
if (sn == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (sn->fr_child != NULL)
|
||||||
|
sn->fr_child->fr_parent = sn;
|
||||||
|
if (sn->fr_next != NULL)
|
||||||
|
{
|
||||||
|
sn->fr_next->fr_parent = sn->fr_parent;
|
||||||
|
sn->fr_next->fr_prev = sn;
|
||||||
|
}
|
||||||
|
if (sn->fr_win != NULL)
|
||||||
|
{
|
||||||
|
sn->fr_win->w_frame = sn;
|
||||||
|
// Resize window to fit the frame.
|
||||||
|
frame_new_height(sn, sn->fr_height, FALSE, FALSE);
|
||||||
|
frame_new_width(sn, sn->fr_width, FALSE, FALSE);
|
||||||
|
}
|
||||||
|
restore_full_snapshot_rec(sn->fr_child);
|
||||||
|
restore_full_snapshot_rec(sn->fr_next);
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(FEAT_GUI) || defined(PROTO)
|
#if defined(FEAT_GUI) || defined(PROTO)
|
||||||
/*
|
/*
|
||||||
* Return TRUE if there is any vertically split window.
|
* Return TRUE if there is any vertically split window.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user