patch 9.2.0083: Cannot have a mutli-line statusline
Commit:
https://github.com/vim/vim/commit/a4186316baed94eb3279888c7de9baa78cfd936d
Author: Hirohito Higashi <
h.eas...@gmail.com>
Date: Sun Mar 1 16:57:08 2026 +0000
patch 9.2.0083: Cannot have a mutli-line statusline
Problem: Cannot have a mutli-line statusline.
Solution: Add support for multi-line statusline
(Hirohito Higashi).
closes: #19123
Signed-off-by: Hirohito Higashi <
h.eas...@gmail.com>
Signed-off-by: Christian Brabandt <
c...@256bit.org>
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 47948bdf7..e1239855b 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -1,4 +1,4 @@
-*options.txt* For Vim version 9.2. Last change: 2026 Feb 25
+*options.txt* For Vim version 9.2. Last change: 2026 Mar 01
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -8519,6 +8519,9 @@ A jump table for the options with a short description can be found at |Q_op|.
applied to StatusLineNC for the statusline of non-current
windows.
The number N must be between 1 and 9. See |hl-User1..9|
+ *stl-%@*
+ @ - Insert a line break (for the |tabpanel| or when the status line
+ 'statuslineopt' has a "maxheight" value greater than 1).
When displaying a flag, Vim removes the leading comma, if any, when
that flag comes right after plaintext. This will make a nice display
@@ -8576,6 +8579,34 @@ A jump table for the options with a short description can be found at |Q_op|.
:function VarExists(var, val)
: if exists(a:var) | return a:val | else | return '' | endif
:endfunction
+<
+ *'statuslineopt'* *'stlo'*
+'statuslineopt' 'stlo' string (default "")
+ global
+ {not available when compiled without the |+statusline|
+ feature}
+ Optional settings for |status-line|. It can consist of the following
+ items. All are optional. Items must be separated by a comma.
+
+ maxheight:{n} Set the maximum status line height to {n}.
+ {n} must be 1 or greater.
+ If not present, 1 is used.
+ Check whether {n} is applicable to all windows
+ on all tab pages, and if not, set it to the
+ closest possible value.
+ The option value is updated to the actual
+ applied value. For example, setting
+ maxheight:999 may result in a smaller value
+ depending on the available space.
+ When {n} is set to 2 or more, you can use "@"
+ in 'statusline' to display information on
+ multiple lines in the status line.
+ See |stl-%@|.
+
+ Examples: >
+ :set stlo=
+ :set stlo=maxheight:2
+ :set stlo+=maxheight:3
<
*'suffixes'* *'su'*
'suffixes' 'su' string (default ".bak,~,.o,.h,.info,.swp,.obj")
@@ -8762,11 +8793,11 @@ A jump table for the options with a short description can be found at |Q_op|.
You can use |g:actual_curtabpage| within a function assigned to
tabpanel. |g:actual_curtabpage| represents current tab's label number.
- The option value can contain "
" to force line breaks: >
+ You can use "%@" or "
" to insert a new line: >
set tabpanel=%!TabPanel()
function! TabPanel() abort
- return printf("(%2d)
%%f", g:actual_curtabpage)
+ return "(" .. g:actual_curtabpage .. ")%@ %f"
endfunction
<
The result is:
@@ -8780,8 +8811,8 @@ A jump table for the options with a short description can be found at |Q_op|.
| |
| |
<
- Note: using "
" is considered experimental and may change in the
- future; a %-atom may be used instead.
+ Note: using "
" is considered experimental and deprecated, prefer
+ the |stl-%@| atom instead.
*'tabpanelopt'* *'tplo'*
'tabpanelopt' 'tplo' string (default "")
diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt
index 6fff5cfc1..2d4cf57e7 100644
--- a/runtime/doc/quickref.txt
+++ b/runtime/doc/quickref.txt
@@ -1,4 +1,4 @@
-*quickref.txt* For Vim version 9.2. Last change: 2026 Feb 14
+*quickref.txt* For Vim version 9.2. Last change: 2026 Mar 01
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -939,6 +939,7 @@ Short explanation of each option: *option-list*
'splitright' 'spr' new window is put right of the current one
'startofline' 'sol' commands move cursor to first non-blank in line
'statusline' 'stl' custom format for the status line
+'statuslineopt' 'stlo' additional options for the |status-line|
'suffixes' 'su' suffixes that are ignored with multiple match
'suffixesadd' 'sua' suffixes added when searching for a file
'swapfile' 'swf' whether to use a swapfile for a buffer
diff --git a/runtime/doc/tags b/runtime/doc/tags
index 017362863..1cac3072d 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -1034,7 +1034,9 @@ $quote eval.txt /*$quote*
'stal' options.txt /*'stal'*
'startofline' options.txt /*'startofline'*
'statusline' options.txt /*'statusline'*
+'statuslineopt' options.txt /*'statuslineopt'*
'stl' options.txt /*'stl'*
+'stlo' options.txt /*'stlo'*
'stmp' options.txt /*'stmp'*
'stpl' options.txt /*'stpl'*
'sts' options.txt /*'sts'*
@@ -10527,6 +10529,7 @@ static-tag tagsrch.txt /*static-tag*
status-line windows.txt /*status-line*
statusmsg-variable eval.txt /*statusmsg-variable*
stl-%! options.txt /*stl-%!*
+stl-%@ options.txt /*stl-%@*
stl-%{ options.txt /*stl-%{*
str2blob() builtin.txt /*str2blob()*
str2float() builtin.txt /*str2float()*
diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt
index 8261c32ef..614051bc2 100644
--- a/runtime/doc/version9.txt
+++ b/runtime/doc/version9.txt
@@ -1,4 +1,4 @@
-*version9.txt* For Vim version 9.2. Last change: 2026 Feb 25
+*version9.txt* For Vim version 9.2. Last change: 2026 Mar 01
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -52593,6 +52593,7 @@ Other ~
- The new |xdg.vim| script for full XDG compatibility is included.
- |ConPTY| support is considered stable as of Windows 11.
- Support for "dap" channel mode for the |debug-adapter-protocol|.
+- |status-line| can use several lines, see 'statuslineopt'.
*changed-9.3*
Changed~
@@ -52607,6 +52608,11 @@ Autocommands: ~
|SessionLoadPre| before loading a |Session| file
+Options: ~
+
+'statuslineopt' Extra options for the 'statusline', e.g. use the
+ "maxheight" suboption to use several lines.
+
==============================================================================
PATCHES *patches-9.3* *bug-fixes-9.3*
*patches-after-9.2*
diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt
index eee53cbdd..37b7289b2 100644
--- a/runtime/doc/windows.txt
+++ b/runtime/doc/windows.txt
@@ -1,4 +1,4 @@
-*windows.txt* For Vim version 9.2. Last change: 2026 Feb 24
+*windows.txt* For Vim version 9.2. Last change: 2026 Mar 01
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -125,6 +125,7 @@ when the last window also has a status line:
You can change the contents of the status line with the 'statusline' option.
This option can be local to the window, so that you can have a different
status line in each window.
+You can change the height of the status line with the 'statuslineopt' option.
Normally, inversion is used to display the status line. This can be changed
with the 's' character in the 'highlight' option. For example, "sb" sets it
diff --git a/runtime/optwin.vim b/runtime/optwin.vim
index 7987a4877..28d91abc5 100644
--- a/runtime/optwin.vim
+++ b/runtime/optwin.vim
@@ -1,7 +1,7 @@
" These commands create the option window.
"
" Maintainer: The Vim Project <
https://github.com/vim/vim>
-" Last Change: 2025 Nov 27
+" Last Change: 2026 Mar 01
" Former Maintainer: Bram Moolenaar <
Br...@vim.org>
" If there already is an option window, jump to that one.
@@ -490,8 +490,10 @@ call append("$", " set ls=" . &ls)
if has("statusline")
call <SID>AddOption("statusline", gettext("alternate format to be used for a status line"))
call <SID>OptionG("stl", &stl)
+ call append("$", " " .. s:local_to_window)
+ call <SID>AddOption("statuslineopt", gettext("optional settings for the status line"))
+ call <SID>OptionG("stlo", &stlo)
endif
-call append("$", " " .. s:local_to_window)
call <SID>AddOption("equalalways", gettext("make all windows the same size when adding/removing windows"))
call <SID>BinOptionG("ea", &ea)
call <SID>AddOption("eadirection", gettext("in which direction 'equalalways' works: \"ver\", \"hor\" or \"both\""))
diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim
index f75436a2a..4c96b6e7a 100644
--- a/runtime/syntax/vim.vim
+++ b/runtime/syntax/vim.vim
@@ -2,7 +2,7 @@
" Language: Vim script
" Maintainer: Hirohito Higashi <h.east.727 ATMARK
gmail.com>
" Doug Kearns <
dougk...@gmail.com>
-" Last Change: 2026 Feb 20
+" Last Change: 2026 Mar 01
" Former Maintainer: Charles E. Campbell
" DO NOT CHANGE DIRECTLY.
@@ -70,9 +70,9 @@ syn keyword vimOption contained efm errorformat ek esckeys ei eventignore eiw ev
syn keyword vimOption contained hi history hk hkmap hkp hkmapp hls hlsearch icon iconstring ic ignorecase imaf imactivatefunc imak imactivatekey imc imcmdline imd imdisable imi iminsert ims imsearch imsf imstatusfunc imst imstyle inc include inex includeexpr is incsearch inde indentexpr indk indentkeys inf infercase im insertmode isf isfname isi isident isk iskeyword isp isprint js joinspaces jop jumpoptions key kmp keymap km keymodel kpc keyprotocol kp keywordprg lmap langmap lm langmenu lnr langnoremap lrm langremap ls laststatus lz lazyredraw lhi lhistory lbr linebreak lines lsp linespace lisp lop lispoptions lw lispwords list lcs listchars lpl loadplugins luadll magic mef makeef menc makeencoding mp makeprg mps matchpairs mat matchtime mco maxcombine mfd maxfuncdepth skipwhite nextgroup=vimSetEqual,vimSetMod
syn keyword vimOption contained mmd maxmapdepth mm maxmem mmp maxmempattern mmt maxmemtot msc maxsearchcount mis menuitems mopt messagesopt msm mkspellmem ml modeline mle modelineexpr mls modelines ma modifiable mod modified more mouse mousef mousefocus mh mousehide mousem mousemodel mousemev mousemoveevent mouses mouseshape mouset mousetime mzq mzquantum mzschemedll mzschemegcdll nf nrformats nu number nuw numberwidth ofu omnifunc odev opendevice opfunc operatorfunc ost osctimeoutlen pp packpath para paragraphs paste pt pastetoggle pex patchexpr pm patchmode pa path perldll pi preserveindent pvh previewheight pvp previewpopup pvw previewwindow pdev printdevice penc printencoding pexpr printexpr pfn printfont pheader printheader pmbcs printmbcharset pmbfn printmbfont skipwhite nextgroup=vimSetEqual,vimSetMod
syn keyword vimOption contained popt printoptions prompt pb pumborder ph pumheight pmw pummaxwidth pw pumwidth pythondll pythonhome pythonthreedll pythonthreehome pyx pyxversion qftf quickfixtextfunc qe quoteescape ro readonly rdt redrawtime re regexpengine rnu relativenumber remap rop renderoptions report rs restorescreen ri revins rl rightleft rlc rightleftcmd rubydll ru ruler ruf rulerformat rtp runtimepath scr scroll scb scrollbind scf scrollfocus sj scrolljump so scrolloff sbo scrollopt sect sections secure sel selection slm selectmode ssop sessionoptions sh shell shcf shellcmdflag sp shellpipe shq shellquote srr shellredir ssl shellslash stmp shelltemp st shelltype sxe shellxescape sxq shellxquote sr shiftround sw shiftwidth shm shortmess sn shortname sbr showbreak skipwhite nextgroup=vimSetEqual,vimSetMod
-syn keyword vimOption contained sc showcmd sloc showcmdloc sft showfulltag sm showmatch smd showmode stal showtabline stpl showtabpanel ss sidescroll siso sidescrolloff scl signcolumn scs smartcase si smartindent sta smarttab sms smoothscroll sts softtabstop spell spc spellcapcheck spf spellfile spl spelllang spo spelloptions sps spellsuggest sb splitbelow spk splitkeep spr splitright sol startofline stl statusline su suffixes sua suffixesadd swf swapfile sws swapsync swb switchbuf smc synmaxcol syn syntax tcl tabclose tal tabline tpm tabpagemax tpl tabpanel tplo tabpanelopt ts tabstop tbs tagbsearch tc tagcase tfu tagfunc tl taglength tr tagrelative tag tags tgst tagstack tcldll term tbidi termbidi tenc termencoding tgc termguicolors twk termwinkey twsl termwinscroll skipwhite nextgroup=vimSetEqual,vimSetMod
-syn keyword vimOption contained tws termwinsize twt termwintype terse ta textauto tx textmode tw textwidth tsr thesaurus tsrfu thesaurusfunc top tildeop to timeout tm timeoutlen title titlelen titleold titlestring tb toolbar tbis toolbariconsize ttimeout ttm ttimeoutlen tbi ttybuiltin tf ttyfast ttym ttymouse tsl ttyscroll tty ttytype udir undodir udf undofile ul undolevels ur undoreload uc updatecount ut updatetime vsts varsofttabstop vts vartabstop vbs verbose vfile verbosefile vdir viewdir vop viewoptions vi viminfo vif viminfofile ve virtualedit vb visualbell warn wiv weirdinvert ww whichwrap wc wildchar wcm wildcharm wig wildignore wic wildignorecase wmnu wildmenu wim wildmode wop wildoptions wak winaltkeys wcr wincolor wi window wfb winfixbuf wfh winfixheight skipwhite nextgroup=vimSetEqual,vimSetMod
-syn keyword vimOption contained wfw winfixwidth wh winheight wmh winminheight wmw winminwidth winptydll wiw winwidth wse wlseat wst wlsteal wtm wltimeoutlen wrap wm wrapmargin ws wrapscan write wa writeany wb writebackup wd writedelay xtermcodes skipwhite nextgroup=vimSetEqual,vimSetMod
+syn keyword vimOption contained sc showcmd sloc showcmdloc sft showfulltag sm showmatch smd showmode stal showtabline stpl showtabpanel ss sidescroll siso sidescrolloff scl signcolumn scs smartcase si smartindent sta smarttab sms smoothscroll sts softtabstop spell spc spellcapcheck spf spellfile spl spelllang spo spelloptions sps spellsuggest sb splitbelow spk splitkeep spr splitright sol startofline stl statusline stlo statuslineopt su suffixes sua suffixesadd swf swapfile sws swapsync swb switchbuf smc synmaxcol syn syntax tcl tabclose tal tabline tpm tabpagemax tpl tabpanel tplo tabpanelopt ts tabstop tbs tagbsearch tc tagcase tfu tagfunc tl taglength tr tagrelative tag tags tgst tagstack tcldll term tbidi termbidi tenc termencoding tgc termguicolors twk termwinkey skipwhite nextgroup=vimSetEqual,vimSetMod
+syn keyword vimOption contained twsl termwinscroll tws termwinsize twt termwintype terse ta textauto tx textmode tw textwidth tsr thesaurus tsrfu thesaurusfunc top tildeop to timeout tm timeoutlen title titlelen titleold titlestring tb toolbar tbis toolbariconsize ttimeout ttm ttimeoutlen tbi ttybuiltin tf ttyfast ttym ttymouse tsl ttyscroll tty ttytype udir undodir udf undofile ul undolevels ur undoreload uc updatecount ut updatetime vsts varsofttabstop vts vartabstop vbs verbose vfile verbosefile vdir viewdir vop viewoptions vi viminfo vif viminfofile ve virtualedit vb visualbell warn wiv weirdinvert ww whichwrap wc wildchar wcm wildcharm wig wildignore wic wildignorecase wmnu wildmenu wim wildmode wop wildoptions wak winaltkeys wcr wincolor wi window wfb winfixbuf skipwhite nextgroup=vimSetEqual,vimSetMod
+syn keyword vimOption contained wfh winfixheight wfw winfixwidth wh winheight wmh winminheight wmw winminwidth winptydll wiw winwidth wse wlseat wst wlsteal wtm wltimeoutlen wrap wm wrapmargin ws wrapscan write wa writeany wb writebackup wd writedelay xtermcodes skipwhite nextgroup=vimSetEqual,vimSetMod
" vimOptions: These are the turn-off setting variants {{{2
" GEN_SYN_VIM: vimOption turn-off, START_STR='syn keyword vimOption contained', END_STR=''
@@ -109,9 +109,9 @@ syn keyword vimOptionVarName contained efm errorformat ek esckeys ei eventignore
syn keyword vimOptionVarName contained hi history hk hkmap hkp hkmapp hls hlsearch icon iconstring ic ignorecase imaf imactivatefunc imak imactivatekey imc imcmdline imd imdisable imi iminsert ims imsearch imsf imstatusfunc imst imstyle inc include inex includeexpr is incsearch inde indentexpr indk indentkeys inf infercase im insertmode isf isfname isi isident isk iskeyword isp isprint js joinspaces jop jumpoptions key kmp keymap km keymodel kpc keyprotocol kp keywordprg lmap langmap lm langmenu lnr langnoremap lrm langremap ls laststatus lz lazyredraw lhi lhistory lbr linebreak lines lsp linespace lisp lop lispoptions lw lispwords list lcs listchars lpl loadplugins luadll magic mef makeef menc makeencoding mp makeprg mps matchpairs mat matchtime mco maxcombine
syn keyword vimOptionVarName contained mfd maxfuncdepth mmd maxmapdepth mm maxmem mmp maxmempattern mmt maxmemtot msc maxsearchcount mis menuitems mopt messagesopt msm mkspellmem ml modeline mle modelineexpr mls modelines ma modifiable mod modified more mouse mousef mousefocus mh mousehide mousem mousemodel mousemev mousemoveevent mouses mouseshape mouset mousetime mzq mzquantum mzschemedll mzschemegcdll nf nrformats nu number nuw numberwidth ofu omnifunc odev opendevice opfunc operatorfunc ost osctimeoutlen pp packpath para paragraphs paste pt pastetoggle pex patchexpr pm patchmode pa path perldll pi preserveindent pvh previewheight pvp previewpopup pvw previewwindow pdev printdevice penc printencoding pexpr printexpr pfn printfont pheader printheader pmbcs printmbcharset
syn keyword vimOptionVarName contained pmbfn printmbfont popt printoptions prompt pb pumborder ph pumheight pmw pummaxwidth pw pumwidth pythondll pythonhome pythonthreedll pythonthreehome pyx pyxversion qftf quickfixtextfunc qe quoteescape ro readonly rdt redrawtime re regexpengine rnu relativenumber remap rop renderoptions report rs restorescreen ri revins rl rightleft rlc rightleftcmd rubydll ru ruler ruf rulerformat rtp runtimepath scr scroll scb scrollbind scf scrollfocus sj scrolljump so scrolloff sbo scrollopt sect sections secure sel selection slm selectmode ssop sessionoptions sh shell shcf shellcmdflag sp shellpipe shq shellquote srr shellredir ssl shellslash stmp shelltemp st shelltype sxe shellxescape sxq shellxquote sr shiftround sw shiftwidth shm shortmess
-syn keyword vimOptionVarName contained sn shortname sbr showbreak sc showcmd sloc showcmdloc sft showfulltag sm showmatch smd showmode stal showtabline stpl showtabpanel ss sidescroll siso sidescrolloff scl signcolumn scs smartcase si smartindent sta smarttab sms smoothscroll sts softtabstop spell spc spellcapcheck spf spellfile spl spelllang spo spelloptions sps spellsuggest sb splitbelow spk splitkeep spr splitright sol startofline stl statusline su suffixes sua suffixesadd swf swapfile sws swapsync swb switchbuf smc synmaxcol syn syntax tcl tabclose tal tabline tpm tabpagemax tpl tabpanel tplo tabpanelopt ts tabstop tbs tagbsearch tc tagcase tfu tagfunc tl taglength tr tagrelative tag tags tgst tagstack tcldll term tbidi termbidi tenc termencoding tgc termguicolors
-syn keyword vimOptionVarName contained twk termwinkey twsl termwinscroll tws termwinsize twt termwintype terse ta textauto tx textmode tw textwidth tsr thesaurus tsrfu thesaurusfunc top tildeop to timeout tm timeoutlen title titlelen titleold titlestring tb toolbar tbis toolbariconsize ttimeout ttm ttimeoutlen tbi ttybuiltin tf ttyfast ttym ttymouse tsl ttyscroll tty ttytype udir undodir udf undofile ul undolevels ur undoreload uc updatecount ut updatetime vsts varsofttabstop vts vartabstop vbs verbose vfile verbosefile vdir viewdir vop viewoptions vi viminfo vif viminfofile ve virtualedit vb visualbell warn wiv weirdinvert ww whichwrap wc wildchar wcm wildcharm wig wildignore wic wildignorecase wmnu wildmenu wim wildmode wop wildoptions wak winaltkeys wcr wincolor
-syn keyword vimOptionVarName contained wi window wfb winfixbuf wfh winfixheight wfw winfixwidth wh winheight wmh winminheight wmw winminwidth winptydll wiw winwidth wse wlseat wst wlsteal wtm wltimeoutlen wrap wm wrapmargin ws wrapscan write wa writeany wb writebackup wd writedelay xtermcodes
+syn keyword vimOptionVarName contained sn shortname sbr showbreak sc showcmd sloc showcmdloc sft showfulltag sm showmatch smd showmode stal showtabline stpl showtabpanel ss sidescroll siso sidescrolloff scl signcolumn scs smartcase si smartindent sta smarttab sms smoothscroll sts softtabstop spell spc spellcapcheck spf spellfile spl spelllang spo spelloptions sps spellsuggest sb splitbelow spk splitkeep spr splitright sol startofline stl statusline stlo statuslineopt su suffixes sua suffixesadd swf swapfile sws swapsync swb switchbuf smc synmaxcol syn syntax tcl tabclose tal tabline tpm tabpagemax tpl tabpanel tplo tabpanelopt ts tabstop tbs tagbsearch tc tagcase tfu tagfunc tl taglength tr tagrelative tag tags tgst tagstack tcldll term tbidi termbidi tenc termencoding
+syn keyword vimOptionVarName contained tgc termguicolors twk termwinkey twsl termwinscroll tws termwinsize twt termwintype terse ta textauto tx textmode tw textwidth tsr thesaurus tsrfu thesaurusfunc top tildeop to timeout tm timeoutlen title titlelen titleold titlestring tb toolbar tbis toolbariconsize ttimeout ttm ttimeoutlen tbi ttybuiltin tf ttyfast ttym ttymouse tsl ttyscroll tty ttytype udir undodir udf undofile ul undolevels ur undoreload uc updatecount ut updatetime vsts varsofttabstop vts vartabstop vbs verbose vfile verbosefile vdir viewdir vop viewoptions vi viminfo vif viminfofile ve virtualedit vb visualbell warn wiv weirdinvert ww whichwrap wc wildchar wcm wildcharm wig wildignore wic wildignorecase wmnu wildmenu wim wildmode wop wildoptions wak winaltkeys
+syn keyword vimOptionVarName contained wcr wincolor wi window wfb winfixbuf wfh winfixheight wfw winfixwidth wh winheight wmh winminheight wmw winminwidth winptydll wiw winwidth wse wlseat wst wlsteal wtm wltimeoutlen wrap wm wrapmargin ws wrapscan write wa writeany wb writebackup wd writedelay xtermcodes
" GEN_SYN_VIM: vimOption term output code variable, START_STR='syn keyword vimOptionVarName contained', END_STR=''
syn keyword vimOptionVarName contained t_AB t_AF t_AU t_AL t_al t_bc t_BE t_BD t_cd t_ce t_Ce t_CF t_cl t_cm t_Co t_CS t_Cs t_cs t_CV t_da t_db t_DL t_dl t_ds t_Ds t_EC t_EI t_fs t_fd t_fe t_GP t_IE t_IS t_ke t_ks t_le t_mb t_md t_me t_mr t_ms t_nd t_op t_RF t_RB t_RC t_RI t_Ri t_RK t_RS t_RT t_RV t_Sb t_SC t_se t_Sf t_SH t_SI t_Si t_so t_SR t_sr t_ST t_Te t_te t_TE t_ti t_TI t_Ts t_ts t_u7 t_ue t_us t_Us t_ut t_vb t_ve t_vi t_VS t_vs t_WP t_WS t_XM t_xn t_xs t_ZH t_ZR t_8f t_8b t_8u t_xo
syn keyword vimOptionVarName contained t_F1 t_F2 t_F3 t_F4 t_F5 t_F6 t_F7 t_F8 t_F9 t_k1 t_K1 t_k2 t_k3 t_K3 t_k4 t_K4 t_k5 t_K5 t_k6 t_K6 t_k7 t_K7 t_k8 t_K8 t_k9 t_K9 t_KA t_kb t_kB t_KB t_KC t_kd t_kD t_KD t_KE t_KF t_KG t_kh t_KH t_kI t_KI t_KJ t_KK t_kl t_KL t_kN t_kP t_kr t_ku
@@ -134,8 +134,8 @@ syn keyword vimErrSetting contained invakm invaltkeymap invanti invantialias inv
syn case ignore
" GEN_SYN_VIM: vimAutoEvent, START_STR='syn keyword vimAutoEvent contained', END_STR='skipwhite nextgroup=vimAutoEventSep,@vimAutocmdPattern'
syn keyword vimAutoEvent contained BufAdd BufCreate BufDelete BufEnter BufFilePost BufFilePre BufHidden BufLeave BufNew BufNewFile BufRead BufReadCmd BufReadPost BufReadPre BufUnload BufWinEnter BufWinLeave BufWipeout BufWrite BufWriteCmd BufWritePost BufWritePre CmdlineChanged CmdlineEnter CmdlineLeave CmdlineLeavePre CmdUndefined CmdwinEnter CmdwinLeave ColorScheme ColorSchemePre CompleteChanged CompleteDone CompleteDonePre CursorHold CursorHoldI CursorMoved CursorMovedC CursorMovedI DiffUpdated DirChanged DirChangedPre EncodingChanged ExitPre FileAppendCmd FileAppendPost FileAppendPre FileChangedRO FileChangedShell FileChangedShellPost FileEncoding FileReadCmd FileReadPost FileReadPre FileType FileWriteCmd FileWritePost FileWritePre FilterReadPost FilterReadPre skipwhite nextgroup=vimAutoEventSep,@vimAutocmdPattern
-syn keyword vimAutoEvent contained FilterWritePost FilterWritePre FocusGained FocusLost FuncUndefined GUIEnter GUIFailed InsertChange InsertCharPre InsertEnter InsertLeave InsertLeavePre KeyInputPre MenuPopup ModeChanged OptionSet QuickFixCmdPost QuickFixCmdPre QuitPre RemoteReply SafeState SafeStateAgain SessionLoadPost SessionWritePost ShellCmdPost ShellFilterPost SigUSR1 SourceCmd SourcePost SourcePre SpellFileMissing StdinReadPost StdinReadPre SwapExists Syntax TabClosed TabClosedPre TabEnter TabLeave TabNew TermChanged TerminalOpen TerminalWinOpen TermResponse TermResponseAll TextChanged TextChangedI TextChangedP TextChangedT TextYankPost VimEnter VimLeave VimLeavePre VimResized VimResume VimSuspend WinClosed WinEnter WinLeave WinNew WinNewPre WinResized skipwhite nextgroup=vimAutoEventSep,@vimAutocmdPattern
-syn keyword vimAutoEvent contained WinScrolled skipwhite nextgroup=vimAutoEventSep,@vimAutocmdPattern
+syn keyword vimAutoEvent contained FilterWritePost FilterWritePre FocusGained FocusLost FuncUndefined GUIEnter GUIFailed InsertChange InsertCharPre InsertEnter InsertLeave InsertLeavePre KeyInputPre MenuPopup ModeChanged OptionSet QuickFixCmdPost QuickFixCmdPre QuitPre RemoteReply SafeState SafeStateAgain SessionLoadPost SessionLoadPre SessionWritePost ShellCmdPost ShellFilterPost SigUSR1 SourceCmd SourcePost SourcePre SpellFileMissing StdinReadPost StdinReadPre SwapExists Syntax TabClosed TabClosedPre TabEnter TabLeave TabNew TermChanged TerminalOpen TerminalWinOpen TermResponse TermResponseAll TextChanged TextChangedI TextChangedP TextChangedT TextYankPost VimEnter VimLeave VimLeavePre VimResized VimResume VimSuspend WinClosed WinEnter WinLeave WinNew WinNewPre skipwhite nextgroup=vimAutoEventSep,@vimAutocmdPattern
+syn keyword vimAutoEvent contained WinResized WinScrolled skipwhite nextgroup=vimAutoEventSep,@vimAutocmdPattern
syn keyword vimAutoEvent contained User skipwhite nextgroup=vimUserAutoEvent
syn match vimUserAutoEvent contained "\<\h\w*\>" skipwhite nextgroup=vimUserAutoEventSep,vimAutocmdMod,vimAutocmdBlock
diff --git a/src/buffer.c b/src/buffer.c
index d96f2fdc1..f2d3a6271 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -45,6 +45,12 @@ static int buf_same_ino(buf_T *buf, stat_T *stp);
static int otherfile_buf(buf_T *buf, char_u *ffname);
#endif
static int value_changed(char_u *str, char_u **last);
+#if defined(FEAT_STL_OPT) || defined(FEAT_GUI_TABLINE)
+static int build_stl_str_hl_local(stl_mode_T mode, win_T *wp,
+ char_u *out, size_t outlen, char_u **fmt_arg,
+ char_u *opt_name, int opt_scope, int fillchar, int maxwidth,
+ stl_hlrec_T **hltab, stl_hlrec_T **tabtab, int *lbreaks);
+#endif
static int append_arg_number(win_T *wp, char_u *buf, size_t buflen, int add_file);
static void free_buffer(buf_T *);
static void free_buffer_stuff(buf_T *buf, int free_options);
@@ -4366,6 +4372,94 @@ build_stl_str_hl(
int maxwidth,
stl_hlrec_T **hltab, // return: HL attributes (can be NULL)
stl_hlrec_T **tabtab) // return: tab page nrs (can be NULL)
+{
+ return build_stl_str_hl_local(STL_MODE_SINGLE, wp, out, outlen, &fmt,
+ opt_name, opt_scope, fillchar, maxwidth, hltab, tabtab, NULL);
+}
+
+ int
+build_stl_str_hl_mline(
+ win_T *wp,
+ char_u *out, // buffer to write into != NameBuff
+ size_t outlen, // length of out[]
+ char_u **fmt, // (in/out)
+ char_u *opt_name, // option name corresponding to "fmt"
+ int opt_scope, // scope for "opt_name"
+ int fillchar,
+ int maxwidth,
+ stl_hlrec_T **hltab, // return: HL attributes (can be NULL)
+ stl_hlrec_T **tabtab) // return: tab page nrs (can be NULL)
+{
+ return build_stl_str_hl_local(STL_MODE_MULTI, wp, out, outlen, fmt,
+ opt_name, opt_scope, fillchar, maxwidth, hltab, tabtab, NULL);
+}
+
+# ifdef ENABLE_STL_MODE_MULTI_NL
+ int
+build_stl_str_hl_mline_nl(
+ win_T *wp,
+ char_u *out, // buffer to write into != NameBuff
+ size_t outlen, // length of out[]
+ char_u **fmt, // (in/out)
+ char_u *opt_name, // option name corresponding to "fmt"
+ int opt_scope, // scope for "opt_name"
+ int fillchar,
+ int maxwidth,
+ stl_hlrec_T **hltab, // return: HL attributes (can be NULL)
+ stl_hlrec_T **tabtab) // return: tab page nrs (can be NULL)
+{
+ return build_stl_str_hl_local(STL_MODE_MULTI_NL, wp, out, outlen, fmt,
+ opt_name, opt_scope, fillchar, maxwidth, hltab, tabtab, NULL);
+}
+# endif
+
+ int
+get_stl_rendered_height(
+ win_T *wp,
+ char_u *fmt,
+ char_u *opt_name, // option name corresponding to "fmt"
+ int opt_scope) // scope for "opt_name"
+{
+ int rendered_height = 0;
+ char_u buf[MAXPATHL] = {0};
+
+ (void)build_stl_str_hl_local(STL_MODE_GET_RENDERED_HEIGHT,
+ wp, buf, sizeof(buf), &fmt,
+ opt_name, opt_scope, 0, 0, NULL, NULL, &rendered_height);
+ return rendered_height;
+}
+
+/*
+ * mode:
+ * STL_MODE_SINGLE:
+ * - Does not accept line breaks ("%@")
+ * STL_MODE_MULTI:
+ * - Accept line breaks
+ * - Update fmt_arg to the start of the next line or last NUL position
+ * STL_MODE_GET_RENDERED_HEIGHT:
+ * - Just get stl rendered height
+ * - Update rendered_height (if not NULL)
+ *
+ * NOTE:
+ * Line break counting is performed here because `%{% ... %}` items
+ * are re-evaluated on redraw and may change the final rendered text.
+ * Therefore, layout decisions must be based on the fully evaluated statusline
+ * string, not on pre-evaluation parsing.
+ */
+static int
+build_stl_str_hl_local(
+ stl_mode_T mode,
+ win_T *wp,
+ char_u *out, // buffer to write into != NameBuff
+ size_t outlen, // length of out[]
+ char_u **fmt_arg,
+ char_u *opt_name, // option name corresponding to "fmt"
+ int opt_scope, // scope for "opt_name"
+ int fillchar,
+ int maxwidth,
+ stl_hlrec_T **hltab, // return: HL attributes (can be NULL)
+ stl_hlrec_T **tabtab, // return: tab page nrs (can be NULL)
+ int *rendered_height) // return: stl rendered height (can be NULL)
{
linenr_T lnum;
colnr_T len;
@@ -4400,6 +4494,7 @@ build_stl_str_hl(
char_u opt;
# define TMPLEN 70
char_u buf_tmp[TMPLEN];
+ char_u *fmt = *fmt_arg;
char_u *usefmt = fmt;
stl_hlrec_T *sp;
int save_redraw_not_allowed = redraw_not_allowed;
@@ -4408,6 +4503,7 @@ build_stl_str_hl(
// matter?
// int called_emsg_before = called_emsg;
int did_emsg_before = did_emsg;
+ int rheight = 1; // stl rendered height
// When inside update_screen() we do not want redrawing a statusline,
// ruler, title, etc. to trigger another redraw, it may cause an endless
@@ -4534,7 +4630,23 @@ build_stl_str_hl(
* Handle up to the next '%' or the end.
*/
while (*s != NUL && *s != '%' && p + 1 < out + outlen)
+# ifdef ENABLE_STL_MODE_MULTI_NL
+ {
+ if (*s == '
' || *s == '
')
+ {
+ if (mode == STL_MODE_MULTI_NL)
+ {
+ s++;
+ goto find_linebreak;
+ }
+ else if (mode == STL_MODE_GET_RENDERED_HEIGHT)
+ rheight++;
+ }
+# endif
*p++ = *s++;
+# ifdef ENABLE_STL_MODE_MULTI_NL
+ }
+# endif
if (*s == NUL || p + 1 >= out + outlen)
break;
@@ -4544,6 +4656,21 @@ build_stl_str_hl(
s++;
if (*s == NUL) // ignore trailing %
break;
+
+ if (*s == STL_LINEBREAK)
+ {
+ if (mode == STL_MODE_MULTI
+# ifdef ENABLE_STL_MODE_MULTI_NL
+ || mode == STL_MODE_MULTI_NL
+# endif
+ )
+ {
+ s++;
+ break;
+ }
+ else if (mode == STL_MODE_GET_RENDERED_HEIGHT)
+ rheight++;
+ }
if (*s == '%')
{
if (p + 1 >= out + outlen)
@@ -5201,14 +5328,52 @@ build_stl_str_hl(
vim_free(str);
curitem++;
}
+# ifdef ENABLE_STL_MODE_MULTI_NL
+find_linebreak:
+# endif
*p = NUL;
outputlen = (size_t)(p - out);
itemcnt = curitem;
+ if (mode == STL_MODE_MULTI
+# ifdef ENABLE_STL_MODE_MULTI_NL
+ || mode == STL_MODE_MULTI_NL
+# endif
+ )
+ {
+ // In multi-line mode, ownership of the format buffer is transferred
+ // back to the caller via *fmt_arg.
+ // When "%!" evaluation occurred (usefmt != fmt), "usefmt" is the newly
+ // allocated eval result and "fmt" is the caller's original buffer
+ // which is no longer needed. "s" points into "usefmt", so we free
+ // "fmt" and shift the remaining format string within "usefmt" for the
+ // caller's next iteration.
+# ifdef FEAT_EVAL
+ if (usefmt != fmt)
+ vim_free(fmt);
+# endif
+ size_t fmt_remain_len = strlen((char *)s);
+
+ mch_memmove(usefmt, s, fmt_remain_len);
+ usefmt[fmt_remain_len] = NUL;
+ *fmt_arg = usefmt;
+ }
+ else
+ {
+ // In single-line mode, the caller retains ownership of its buffer.
+ // Free the eval result if "%!" was used.
# ifdef FEAT_EVAL
- if (usefmt != fmt)
- vim_free(usefmt);
+ if (usefmt != fmt)
+ vim_free(usefmt);
# endif
+ }
+
+ if (mode == STL_MODE_GET_RENDERED_HEIGHT)
+ {
+ if (rendered_height != NULL)
+ *rendered_height = rheight;
+ return 0;
+ }
width = vim_strsize(out);
if (maxwidth > 0 && width > maxwidth)
diff --git a/src/drawscreen.c b/src/drawscreen.c
index e5de9abf1..aff03cb87 100644
--- a/src/drawscreen.c
+++ b/src/drawscreen.c
@@ -457,6 +457,7 @@ win_redr_status(win_T *wp, int ignore_pum UNUSED)
int row;
int fillchar;
int attr;
+ int i;
static int busy = FALSE;
// It's possible to get here recursively when 'statusline' (indirectly)
@@ -535,8 +536,6 @@ win_redr_status(win_T *wp, int ignore_pum UNUSED)
}
else if (has_mbyte)
{
- int i;
-
// Count total number of display cells.
plen = mb_string2cells(p, -1);
@@ -560,7 +559,8 @@ win_redr_status(win_T *wp, int ignore_pum UNUSED)
}
screen_puts(p, row, wp->w_wincol, attr);
- screen_fill(row, row + 1, plen + wp->w_wincol,
+ for (i = 0; i < wp->w_status_height; i++)
+ screen_fill(row + i, row + i + 1, plen + wp->w_wincol,
this_ru_col + wp->w_wincol, fillchar, fillchar, attr);
if ((NameBufflen = get_keymap_str(wp, (char_u *)"<%s>", NameBuff, MAXPATHL)) > 0
&& (this_ru_col - plen) > (NameBufflen + 1))
@@ -590,7 +590,8 @@ win_redr_status(win_T *wp, int ignore_pum UNUSED)
fillchar = fillchar_status(&attr, wp);
else
fillchar = fillchar_vsep(&attr, wp);
- screen_putchar(fillchar, row, W_ENDCOL(wp), attr);
+ for (i = 0; i < wp->w_status_height; i++)
+ screen_putchar(fillchar, row + i, W_ENDCOL(wp), attr);
}
busy = FALSE;
}
diff --git a/src/feature.h b/src/feature.h
index 376e40994..24928edf6 100644
--- a/src/feature.h
+++ b/src/feature.h
@@ -309,8 +309,9 @@
#endif
/*
- * +statusline 'statusline', 'rulerformat' and special format of
- * 'titlestring' and 'iconstring' options.
+ * +statusline 'statusline', 'statuslineopt', 'rulerformat' and
+ * special format of 'titlestring' and 'iconstring'
+ * options.
*/
#ifdef FEAT_NORMAL
# define FEAT_STL_OPT
diff --git a/src/gui.c b/src/gui.c
index 9bb9c98e8..a805c1423 100644
--- a/src/gui.c
+++ b/src/gui.c
@@ -4979,13 +4979,15 @@ xy2win(int x, int y, mouse_find_T popup)
else
update_mouseshape(SHAPE_IDX_MORE);
}
- else if (row > wp->w_height) // below status line
+ else if (row >= wp->w_height + wp->w_status_height) // below status line
update_mouseshape(SHAPE_IDX_CLINE);
else if (!(State & MODE_CMDLINE) && wp->w_vsep_width > 0 && col == wp->w_width
- && (row != wp->w_height || !stl_connected(wp)) && msg_scrolled == 0)
+ && (!(row >= wp->w_height && row < wp->w_height
+ + wp->w_status_height) || !stl_connected(wp)) && msg_scrolled == 0)
update_mouseshape(SHAPE_IDX_VSEP);
else if (!(State & MODE_CMDLINE) && wp->w_status_height > 0
- && row == wp->w_height && msg_scrolled == 0)
+ && row >= wp->w_height && row < wp->w_height + wp->w_status_height
+ && msg_scrolled == 0)
update_mouseshape(SHAPE_IDX_STATUS);
else
update_mouseshape(-2);
diff --git a/src/option.h b/src/option.h
index e482b8666..46b8daf09 100644
--- a/src/option.h
+++ b/src/option.h
@@ -363,9 +363,10 @@ typedef enum {
#define STL_TRUNCMARK '<' // truncation mark if line is too long
#define STL_USER_HL '*' // highlight from (User)1..9 or 0
#define STL_HIGHLIGHT '#' // highlight name
+#define STL_LINEBREAK '@' // insert a line break
#define STL_TABPAGENR 'T' // tab page label nr
#define STL_TABCLOSENR 'X' // tab page close nr
-#define STL_ALL ((char_u *) "fFtcvVlLknoObBrRhHmYyWwMqpPaNS{#")
+#define STL_ALL ((char_u *) "fFtcvVlLknoObBrRhHmYyWwMqpPaNS{#@")
// flags used for parsed 'wildmode'
#define WIM_FULL 0x01
@@ -942,6 +943,7 @@ EXTERN int p_ssl; // 'shellslash'
#endif
#ifdef FEAT_STL_OPT
EXTERN char_u *p_stl; // 'statusline'
+EXTERN char_u *p_stlo; // 'statuslineopt'
#endif
EXTERN int p_sr; // 'shiftround'
EXTERN long p_sw; // 'shiftwidth'
diff --git a/src/optiondefs.h b/src/optiondefs.h
index 70dba7601..650943480 100644
--- a/src/optiondefs.h
+++ b/src/optiondefs.h
@@ -2516,6 +2516,14 @@ static struct vimoption options[] =
(char_u *)&p_stl, PV_STL, did_set_statusline, NULL,
#else
(char_u *)NULL, PV_NONE, NULL, NULL,
+#endif
+ {(char_u *)"", (char_u *)0L} SCTX_INIT},
+ {"statuslineopt" ,"stlo", P_STRING|P_VI_DEF|P_ALLOCED|P_RSTAT|P_MLE
+ |P_ONECOMMA|P_COLON|P_NODUP,
+#ifdef FEAT_STL_OPT
+ (char_u *)&p_stlo, PV_NONE, did_set_statuslineopt, expand_set_statuslineopt,
+#else
+ (char_u *)NULL, PV_NONE, NULL, NULL,
#endif
{(char_u *)"", (char_u *)0L} SCTX_INIT},
{"suffixes", "su", P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
diff --git a/src/optionstr.c b/src/optionstr.c
index 7e2ac6567..d4287b46c 100644
--- a/src/optionstr.c
+++ b/src/optionstr.c
@@ -96,6 +96,9 @@ static char *(p_ssop_values[]) = {"buffers", "winpos", "resize", "winsize",
"sesdir", "curdir", "folds", "cursor", "tabpages", "terminal", "skiprtp",
NULL};
#endif
+#if defined(FEAT_STL_OPT)
+static char *(p_stlo_values[]) = {"maxheight:", NULL};
+#endif
// Keep in sync with SWB_ flags in option.h
static char *(p_swb_values[]) = {"useopen", "usetab", "split", "newtab", "vsplit", "uselast", NULL};
static char *(p_spk_values[]) = {"cursor", "screen", "topline", NULL};
@@ -664,6 +667,11 @@ check_stl_option(char_u *s)
if (!*s)
break;
s++;
+ if (*s == STL_LINEBREAK)
+ {
+ s++;
+ continue;
+ }
if (*s == '%' || *s == STL_TRUNCMARK || *s == STL_SEPARATE)
{
s++;
@@ -4187,7 +4195,50 @@ expand_set_splitkeep(optexpand_T *args, int *numMatches, char_u ***matches)
char *
did_set_statusline(optset_T *args)
{
- return parse_statustabline_rulerformat(args, FALSE);
+ char *ret = parse_statustabline_rulerformat(args, FALSE);
+
+ if (ret != NULL)
+ return ret;
+ frame_change_statusline_height();
+
+ return NULL;
+}
+
+/*
+ * The 'statuslineopt' option is changed.
+ */
+ char *
+did_set_statuslineopt(optset_T *args)
+{
+ char_u **varp = (char_u **)args->os_varp;
+
+ if (statuslineopt_changed(*varp) == FAIL)
+ return e_invalid_argument;
+
+ frame_change_statusline_height();
+
+ if (*varp != empty_option)
+ {
+ // Update the maxheight value to the actual value set.
+ // Note: Must be changed if p_stlo_values are changed.
+ free_string_option(*varp);
+ vim_snprintf((char *)IObuff, IOSIZE, "maxheight:%d",
+ statusline_height(NULL));
+ *varp = vim_strsave(IObuff);
+ }
+
+ return NULL;
+}
+
+ int
+expand_set_statuslineopt(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+ return expand_set_opt_string(
+ args,
+ p_stlo_values,
+ ARRAY_LENGTH(p_stlo_values) - 1,
+ numMatches,
+ matches);
}
#endif
diff --git a/src/proto/
buffer.pro b/src/proto/
buffer.pro
index cb4de270d..db939b381 100644
--- a/src/proto/
buffer.pro
+++ b/src/proto/
buffer.pro
@@ -50,6 +50,9 @@ void maketitle(void);
void resettitle(void);
void free_titles(void);
int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, char_u *opt_name, int opt_scope, int fillchar, int maxwidth, stl_hlrec_T **hltab, stl_hlrec_T **tabtab);
+int build_stl_str_hl_mline(win_T *wp, char_u *out, size_t outlen, char_u **fmt, char_u *opt_name, int opt_scope, int fillchar, int maxwidth, stl_hlrec_T **hltab, stl_hlrec_T **tabtab);
+int build_stl_str_hl_mline_nl(win_T *wp, char_u *out, size_t outlen, char_u **fmt, char_u *opt_name, int opt_scope, int fillchar, int maxwidth, stl_hlrec_T **hltab, stl_hlrec_T **tabtab);
+int get_stl_rendered_height(win_T *wp, char_u *fmt, char_u *opt_name, int opt_scope);
int get_rel_pos(win_T *wp, char_u *buf, int buflen);
char_u *fix_fname(char_u *fname);
void fname_expand(buf_T *buf, char_u **ffname, char_u **sfname);
diff --git a/src/proto/
optionstr.pro b/src/proto/
optionstr.pro
index b60037024..7e55d3b19 100644
--- a/src/proto/
optionstr.pro
+++ b/src/proto/
optionstr.pro
@@ -167,6 +167,8 @@ int expand_set_spellsuggest(optexpand_T *args, int *numMatches, char_u ***matche
char *did_set_splitkeep(optset_T *args);
int expand_set_splitkeep(optexpand_T *args, int *numMatches, char_u ***matches);
char *did_set_statusline(optset_T *args);
+char *did_set_statuslineopt(optset_T *args);
+int expand_set_statuslineopt(optexpand_T *args, int *numMatches, char_u ***matches);
char *did_set_swapsync(optset_T *args);
int expand_set_swapsync(optexpand_T *args, int *numMatches, char_u ***matches);
char *did_set_switchbuf(optset_T *args);
diff --git a/src/proto/
window.pro b/src/proto/
window.pro
index 62d8bc639..7bc2c114c 100644
--- a/src/proto/
window.pro
+++ b/src/proto/
window.pro
@@ -90,6 +90,9 @@ void win_new_width(win_T *wp, int width);
void win_comp_scroll(win_T *wp);
void command_height(void);
void last_status(int morewin);
+void frame_change_statusline_height(void);
+int statuslineopt_changed(char_u *stlopt);
+int statusline_height(win_T *wp);
int tabline_height(void);
int last_stl_height(int morewin);
int min_rows(void);
diff --git a/src/screen.c b/src/screen.c
index 905f18499..1e9c9a7d5 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -1188,6 +1188,7 @@ win_redr_custom(
int width;
int n;
int len;
+ int stlh_cnt;
int fillchar;
char_u buf[MAXPATHL];
char_u *stl;
@@ -1278,64 +1279,80 @@ win_redr_custom(
ewp = wp == NULL ? curwin : wp;
p_crb_save = ewp->w_p_crb;
ewp->w_p_crb = FALSE;
+ stlh_cnt = draw_ruler || stl == p_tal ? 1 : wp->w_status_height;
// Make a copy, because the statusline may include a function call that
// might change the option value and free the memory.
stl = vim_strsave(stl);
- width = build_stl_str_hl(ewp, buf, sizeof(buf),
- (stl == NULL) ? (char_u *)"" : stl, opt_name, opt_scope,
- fillchar, maxwidth, &hltab, &tabtab);
- vim_free(stl);
- ewp->w_p_crb = p_crb_save;
+ char_u *stl_tmp = (stl == NULL) ? (char_u *)"" : stl;
+ int col_save = col;
- // Make all characters printable.
- p = transstr(buf);
- if (p != NULL)
+ for (int i = 0; i < stlh_cnt; i++)
{
- len = vim_snprintf((char *)buf, sizeof(buf), "%s", p);
- vim_free(p);
- }
- else
- len = (int)STRLEN(buf);
+ col = col_save;
+ buf[0] = NUL;
+ width = build_stl_str_hl_mline(ewp, buf, sizeof(buf),
+ &stl_tmp,
+ opt_name, opt_scope,
+ fillchar, maxwidth, &hltab, &tabtab);
+
+ // Make all characters printable.
+ p = transstr(buf);
+ if (p != NULL)
+ {
+ len = vim_snprintf((char *)buf, sizeof(buf), "%s", p);
+ vim_free(p);
+ }
+ else
+ len = (int)STRLEN(buf);
- // fill up with "fillchar"
- while (width < maxwidth && len < (int)sizeof(buf) - 1)
- {
- len += (*mb_char2bytes)(fillchar, buf + len);
- ++width;
- }
- buf[len] = NUL;
+ // fill up with "fillchar"
+ while (width < maxwidth && len < (int)sizeof(buf) - 1)
+ {
+ len += (*mb_char2bytes)(fillchar, buf + len);
+ ++width;
+ }
+ buf[len] = NUL;
- /*
- * Draw each snippet with the specified highlighting.
- */
- curattr = attr;
- p = buf;
- for (n = 0; hltab[n].start != NULL; n++)
- {
- len = (int)(hltab[n].start - p);
- screen_puts_len(p, len, row, col, curattr);
- col += vim_strnsize(p, len);
- p = hltab[n].start;
-
- if (hltab[n].userhl == 0)
- curattr = attr;
- else if (hltab[n].userhl < 0)
- curattr = syn_id2attr(-hltab[n].userhl);
+ /*
+ * Draw each snippet with the specified highlighting.
+ */
+ curattr = attr;
+ p = buf;
+ for (n = 0; hltab[n].start != NULL; n++)
+ {
+ len = (int)(hltab[n].start - p);
+ screen_puts_len(p, len, row + i, col, curattr);
+ col += vim_strnsize(p, len);
+ p = hltab[n].start;
+
+ if (hltab[n].userhl == 0)
+ curattr = attr;
+ else if (hltab[n].userhl < 0)
+ curattr = syn_id2attr(-hltab[n].userhl);
# ifdef FEAT_TERMINAL
- else if (wp != NULL && wp != curwin && bt_terminal(wp->w_buffer)
- && wp->w_status_height != 0)
- curattr = highlight_stltermnc[hltab[n].userhl - 1];
- else if (wp != NULL && bt_terminal(wp->w_buffer)
- && wp->w_status_height != 0)
- curattr = highlight_stlterm[hltab[n].userhl - 1];
+ else if (wp != NULL && wp != curwin && bt_terminal(wp->w_buffer)
+ && wp->w_status_height != 0)
+ curattr = highlight_stltermnc[hltab[n].userhl - 1];
+ else if (wp != NULL && bt_terminal(wp->w_buffer)
+ && wp->w_status_height != 0)
+ curattr = highlight_stlterm[hltab[n].userhl - 1];
# endif
- else if (wp != NULL && wp != curwin && wp->w_status_height != 0)
- curattr = highlight_stlnc[hltab[n].userhl - 1];
- else
- curattr = highlight_user[hltab[n].userhl - 1];
+ else if (wp != NULL && wp != curwin && wp->w_status_height != 0)
+ curattr = highlight_stlnc[hltab[n].userhl - 1];
+ else
+ curattr = highlight_user[hltab[n].userhl - 1];
+ }
+ screen_puts(p, row + i, col, curattr);
}
- screen_puts(p, row, col, curattr);
+ ewp->w_p_crb = p_crb_save;
+
+ // Note: In the loop, build_stl_str_hl_mline() may replace stl_tmp with
+ // a newly allocated buffer (when "%!" evaluation occurs), freeing the
+ // original "stl" internally. After the loop, stl_tmp must be freed
+ // instead of stl, as it holds the current buffer ownership.
+ if (stl != NULL)
+ vim_free(stl_tmp);
if (wp == NULL)
{
diff --git a/src/structs.h b/src/structs.h
index 805d9ada3..f2c1188a9 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -4050,8 +4050,11 @@ struct window_S
// status/command/winbar line(s)
int w_prev_winrow; // previous winrow used for 'splitkeep'
int w_prev_height; // previous height used for 'splitkeep'
-
- int w_status_height; // number of status lines (0 or 1)
+ int w_status_height; // number of status lines.
+ // If 'statuslineopt' was changed, this
+ // member will be the previous value until
+ // call function
+ // frame_change_statusline_height().
int w_wincol; // Leftmost column of window in screen.
int w_width; // Width of window, excluding separation.
int w_vsep_width; // Number of separator columns (0 or 1).
diff --git a/src/tabpanel.c b/src/tabpanel.c
index f2bccea1b..12d644683 100644
--- a/src/tabpanel.c
+++ b/src/tabpanel.c
@@ -245,7 +245,7 @@ screen_puts_len_for_tabpanel(
int attr,
tabpanel_T *pargs)
{
- int j, k;
+ int j;
int chlen;
int chcells;
char_u buf[IOSIZE];
@@ -257,7 +257,30 @@ screen_puts_len_for_tabpanel(
&& pargs->maxrow <= *pargs->prow - pargs->offsetrow)
break;
- if (p[j] == '
' || p[j] == '
')
+ if (has_mbyte)
+ chlen = (*mb_ptr2len)(p + j);
+ else
+ chlen = 1;
+
+ for (int k = 0; k < chlen; k++)
+ buf[k] = p[j + k];
+ buf[chlen] = NUL;
+ j += chlen;
+
+ // Make all characters printable.
+ temp = transstr(buf);
+ if (temp != NULL)
+ {
+ vim_strncpy(buf, temp, sizeof(buf) - 1);
+ vim_free(temp);
+ }
+
+ if (has_mbyte)
+ chcells = (*mb_ptr2cells)(buf);
+ else
+ chcells = 1;
+
+ if (pargs->col_end < (*pargs->pcol) + chcells)
{
// fill the tailing area of current row.
if (*pargs->prow - pargs->offsetrow >= 0
@@ -266,62 +289,23 @@ screen_puts_len_for_tabpanel(
*pargs->prow - pargs->offsetrow,
*pargs->prow - pargs->offsetrow + 1,
*pargs->pcol, pargs->col_end, attr);
- (*pargs->prow)++;
- *pargs->pcol = pargs->col_start;
- j++;
- }
- else
- {
- if (has_mbyte)
- chlen = (*mb_ptr2len)(p + j);
- else
- chlen = (int)STRLEN(p + j);
-
- for (k = 0; k < chlen; k++)
- buf[k] = p[j + k];
- buf[chlen] = NUL;
- j += chlen;
-
- // Make all characters printable.
- temp = transstr(buf);
- if (temp != NULL)
- {
- vim_strncpy(buf, temp, sizeof(buf) - 1);
- vim_free(temp);
- }
-
- if (has_mbyte)
- chcells = (*mb_ptr2cells)(buf);
- else
- chcells = 1;
+ *pargs->pcol = pargs->col_end;
- if (pargs->col_end < (*pargs->pcol) + chcells)
- {
- // fill the tailing area of current row.
- if (*pargs->prow - pargs->offsetrow >= 0
- && *pargs->prow - pargs->offsetrow < pargs->maxrow)
- screen_fill_tailing_area(tplmode,
- *pargs->prow - pargs->offsetrow,
- *pargs->prow - pargs->offsetrow + 1,
- *pargs->pcol, pargs->col_end, attr);
- *pargs->pcol = pargs->col_end;
-
- if (pargs->col_end < chcells)
- break;
- }
+ if (pargs->col_end < chcells)
+ break;
+ }
- if (*pargs->pcol + chcells <= pargs->col_end)
- {
- int off = (tpl_align == ALIGN_RIGHT)
- ? topframe->fr_width
- : 0;
- if (TPLMODE_REDRAW == tplmode
- && (*pargs->prow - pargs->offsetrow >= 0
- && *pargs->prow - pargs->offsetrow < pargs->maxrow))
- screen_puts(buf, *pargs->prow - pargs->offsetrow,
- *pargs->pcol + off, attr);
- *pargs->pcol += chcells;
- }
+ if (*pargs->pcol + chcells <= pargs->col_end)
+ {
+ int off = (tpl_align == ALIGN_RIGHT)
+ ? topframe->fr_width
+ : 0;
+ if (tplmode == TPLMODE_REDRAW
+ && (*pargs->prow - pargs->offsetrow >= 0
+ && *pargs->prow - pargs->offsetrow < pargs->maxrow))
+ screen_puts(buf, *pargs->prow - pargs->offsetrow,
+ *pargs->pcol + off, attr);
+ *pargs->pcol += chcells;
}
}
}
@@ -382,35 +366,20 @@ draw_tabpanel_default(int tplmode, tabpanel_T *pargs)
}
/*
- * default tabpanel drawing behavior if 'tabpanel' option is NOT empty.
+ * Draw tabpanel content with highlight handling.
+ * Processes hltab entries and fills tailing area.
*/
static void
-draw_tabpanel_userdefined(int tplmode, tabpanel_T *pargs)
+draw_tabpanel_with_highlight(
+ int tplmode,
+ char_u *buf,
+ stl_hlrec_T *hltab,
+ tabpanel_T *pargs)
{
char_u *p;
- int p_crb_save;
- char_u buf[IOSIZE];
- stl_hlrec_T *hltab;
- stl_hlrec_T *tabtab;
int curattr;
int n;
- // Temporarily reset 'cursorbind', we don't want a side effect from moving
- // the cursor away and back.
- p_crb_save = pargs->cwp->w_p_crb;
- pargs->cwp->w_p_crb = FALSE;
-
- // Make a copy, because the statusline may include a function call that
- // might change the option value and free the memory.
- p = vim_strsave(pargs->user_defined);
-
- build_stl_str_hl(pargs->cwp, buf, sizeof(buf),
- p, opt_name, opt_scope,
- TPL_FILLCHAR, pargs->col_end - pargs->col_start, &hltab, &tabtab);
-
- vim_free(p);
- pargs->cwp->w_p_crb = p_crb_save;
-
curattr = pargs->attr;
p = buf;
for (n = 0; hltab[n].start != NULL; n++)
@@ -448,54 +417,6 @@ draw_tabpanel_userdefined(int tplmode, tabpanel_T *pargs)
*pargs->pcol = pargs->col_end;
}
- static char_u *
-starts_with_percent_and_bang(tabpanel_T *pargs)
-{
- int len = 0;
- char_u *usefmt = p_tpl;
- int did_emsg_before = did_emsg;
-
- if (usefmt == NULL)
- return NULL;
-
- len = (int)STRLEN(usefmt);
-
- if (len == 0)
- return NULL;
-
-#ifdef FEAT_EVAL
- // if "fmt" was set insecurely it needs to be evaluated in the sandbox
- int use_sandbox = was_set_insecurely(curwin, opt_name, opt_scope);
-
- // When the format starts with "%!" then evaluate it as an expression and
- // use the result as the actual format string.
- if (len > 1 && usefmt[0] == '%' && usefmt[1] == '!')
- {
- typval_T tv;
- char_u *p = NULL;
-
- tv.v_type = VAR_NUMBER;
- tv.vval.v_number = pargs->cwp->w_id;
- set_var((char_u *)"g:tabpanel_winid", &tv, FALSE);
-
- p = eval_to_string_safe(usefmt + 2, use_sandbox, FALSE, FALSE);
- if (p != NULL)
- usefmt = p;
-
- do_unlet((char_u *)"g:tabpanel_winid", TRUE);
-
- if (did_emsg > did_emsg_before)
- {
- usefmt = NULL;
- set_string_option_direct(opt_name, -1, (char_u *)"",
- OPT_FREE | opt_scope, SID_ERROR);
- }
- }
-#endif
-
- return usefmt;
-}
-
/*
* do something by tplmode for drawing tabpanel.
*/
@@ -545,6 +466,7 @@ do_by_tplmode(
if (tplmode == TPLMODE_GET_CURTAB_ROW)
{
*pcurtab_row = row;
+ do_unlet((char_u *)"g:actual_curtabpage", TRUE);
break;
}
}
@@ -562,54 +484,41 @@ do_by_tplmode(
args.wp = tp->tp_firstwin;
}
- char_u *usefmt = starts_with_percent_and_bang(&args);
- if (usefmt != NULL)
- {
- char_u buf[IOSIZE];
- char_u *p = usefmt;
- size_t i = 0;
+ char_u *usefmt = vim_strsave(p_tpl);
- while (p[i] != NUL)
+ if (usefmt != NULL && *usefmt != NUL)
+ {
+ while (*usefmt != NUL)
{
- while (p[i] == '
' || p[i] == '
')
- {
- // fill the tailing area of current row.
- if (row - args.offsetrow >= 0
- && row - args.offsetrow < args.maxrow)
- screen_fill_tailing_area(tplmode,
- row - args.offsetrow,
- row - args.offsetrow + 1,
- col, args.col_end, args.attr);
- row++;
- col = col_start;
- p++;
- }
+ char_u buf[IOSIZE];
+ stl_hlrec_T *hltab;
+ stl_hlrec_T *tabtab;
- while (p[i] != '
' && p[i] != '
' && p[i] != NUL)
- {
- if (i + 1 >= sizeof(buf))
- break;
- buf[i] = p[i];
- i++;
- }
- buf[i] = NUL;
+ if (args.maxrow <= row - args.offsetrow)
+ break;
+
+ buf[0] = NUL;
+#ifdef ENABLE_STL_MODE_MULTI_NL
+ (void)build_stl_str_hl_mline_nl
+#else
+ (void)build_stl_str_hl_mline
+#endif
+ (args.cwp, buf, sizeof(buf),
+ &usefmt, opt_name, opt_scope, TPL_FILLCHAR,
+ args.col_end - args.col_start, &hltab, &tabtab);
- args.user_defined = buf;
args.prow = &row;
args.pcol = &col;
- draw_tabpanel_userdefined(tplmode, &args);
- // p_tpl could have been freed in build_stl_str_hl()
- if (p_tpl == NULL || *p_tpl == NUL)
+
+ draw_tabpanel_with_highlight(tplmode, buf, hltab, &args);
+
+ // Move to next line for %@
+ if (*usefmt != NUL)
{
- usefmt = NULL;
- break;
+ row++;
+ col = col_start;
}
-
- p += i;
- i = 0;
}
- if (usefmt != p_tpl)
- VIM_CLEAR(usefmt);
}
else
{
@@ -619,6 +528,7 @@ do_by_tplmode(
draw_tabpanel_default(tplmode, &args);
}
+ vim_free(usefmt);
do_unlet((char_u *)"g:actual_curtabpage", TRUE);
tp = tp->tp_next;
diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak
index 31f66d7ba..07bea0702 100644
--- a/src/testdir/Make_all.mak
+++ b/src/testdir/Make_all.mak
@@ -312,6 +312,7 @@ NEW_TESTS = \
test_startup_utf8 \
test_stat \
test_statusline \
+ test_statuslineopt \
test_substitute \
test_suspend \
test_swap \
@@ -578,6 +579,7 @@ NEW_TESTS_RES = \
test_startup.res \
test_stat.res \
test_statusline.res \
+ test_statuslineopt.res \
test_substitute.res \
test_suspend.res \
test_swap.res \
diff --git a/src/testdir/dumps/Test_multistatusline_highlight_01.dump b/src/testdir/dumps/Test_multistatusline_highlight_01.dump
new file mode 100644
index 000000000..7ae7167ca
--- /dev/null
+++ b/src/testdir/dumps/Test_multistatusline_highlight_01.dump
@@ -0,0 +1,6 @@
+> +0&#ffffff0@74
+|~+0#4040ff13&| @73
+|L+3#0000000&|1|A|0|1| @29|L+0&#ffff4012|1|A|0|2| +3&#ffffff0@29|L+0&&|1|A|0|3
+|L|2|B|0|1| +3&&@29|L|2|B|0|2| @29|L+0&#ffff4012|2|B|0|3
+|L|3|C|0|1| +3&#ffffff0@29|L+0&&|3|C|0|2| +3&&@29|L|3|C|0|3
+| +0&&@74
diff --git a/src/testdir/dumps/Test_multistatusline_highlight_02.dump b/src/testdir/dumps/Test_multistatusline_highlight_02.dump
new file mode 100644
index 000000000..d50a5ea0c
--- /dev/null
+++ b/src/testdir/dumps/Test_multistatusline_highlight_02.dump
@@ -0,0 +1,6 @@
+> +0&#ffffff0@74
+|~+0#4040ff13&| @73
+|L+3#0000000&|1|A|0|1| @29|L+0&#ffff4012|1|A|0|2| +3&#ffffff0@29|L+0#ffffff16#ff404010|1|A|0|3
+|L|2|B|0|1| +3#0000000#ffffff0@29|L|2|B|0|2| @29|L+0&#ffff4012|2|B|0|3
+|L|3|C|0|1| +3&#ffffff0@29|L+0#ffffff16#ff404010|3|C|0|2| +3#0000000#ffffff0@29|L|3|C|0|3
+| +0&&@74
diff --git a/src/testdir/dumps/Test_tabpanel_commandline_0.dump b/src/testdir/dumps/Test_tabpanel_cmdline_compl_0.dump
similarity index 100%
rename from src/testdir/dumps/Test_tabpanel_commandline_0.dump
rename to src/testdir/dumps/Test_tabpanel_cmdline_compl_0.dump
diff --git a/src/testdir/dumps/Test_tabpanel_commandline_1.dump b/src/testdir/dumps/Test_tabpanel_cmdline_compl_1.dump
similarity index 100%
rename from src/testdir/dumps/Test_tabpanel_commandline_1.dump
rename to src/testdir/dumps/Test_tabpanel_cmdline_compl_1.dump
diff --git a/src/testdir/dumps/Test_tabpanel_eval_tabpanel_statusline_tabline_0.dump b/src/testdir/dumps/Test_tabpanel_eval_0.dump
similarity index 100%
rename from src/testdir/dumps/Test_tabpanel_eval_tabpanel_statusline_tabline_0.dump
rename to src/testdir/dumps/Test_tabpanel_eval_0.dump
diff --git a/src/testdir/dumps/Test_tabpanel_eval_tabpanel_statusline_tabline_1.dump b/src/testdir/dumps/Test_tabpanel_eval_1.dump
similarity index 100%
rename from src/testdir/dumps/Test_tabpanel_eval_tabpanel_statusline_tabline_1.dump
rename to src/testdir/dumps/Test_tabpanel_eval_1.dump
diff --git a/src/testdir/dumps/Test_tabpanel_eval_tabpanel_with_linebreaks_0.dump b/src/testdir/dumps/Test_tabpanel_eval_with_linebreaks_0.dump
similarity index 91%
rename from src/testdir/dumps/Test_tabpanel_eval_tabpanel_with_linebreaks_0.dump
rename to src/testdir/dumps/Test_tabpanel_eval_with_linebreaks_0.dump
index 80f06ce74..d28904c78 100644
--- a/src/testdir/dumps/Test_tabpanel_eval_tabpanel_with_linebreaks_0.dump
+++ b/src/testdir/dumps/Test_tabpanel_eval_with_linebreaks_0.dump
@@ -7,4 +7,4 @@
|t+2#0000000&|o|p| @6|~+0#4040ff13&| @33
|$+2#0000000&| |[|c@2|]| @1|$|~+0#4040ff13&| @33
|b+2#0000000&|o|t@1|o|m| @3|~+0#4040ff13&| @33
-| +1#0000000&@9|"+0&&|c@2|"| |[|N|e|w|]| @23
+| +1#0000000&@9| +0&&@34
diff --git a/src/testdir/dumps/Test_tabpanel_eval_tabpanel_with_linebreaks_1.dump b/src/testdir/dumps/Test_tabpanel_eval_with_linebreaks_1.dump
similarity index 100%
rename from src/testdir/dumps/Test_tabpanel_eval_tabpanel_with_linebreaks_1.dump
rename to src/testdir/dumps/Test_tabpanel_eval_with_linebreaks_1.dump
diff --git a/src/testdir/dumps/Test_tabpanel_noeval_tabpanel_statusline_tabline_0.dump b/src/testdir/dumps/Test_tabpanel_noeval_0.dump
similarity index 100%
rename from src/testdir/dumps/Test_tabpanel_noeval_tabpanel_statusline_tabline_0.dump
rename to src/testdir/dumps/Test_tabpanel_noeval_0.dump
diff --git a/src/testdir/dumps/Test_tabpanel_noeval_tabpanel_statusline_tabline_1.dump b/src/testdir/dumps/Test_tabpanel_noeval_1.dump
similarity index 100%
rename from src/testdir/dumps/Test_tabpanel_noeval_tabpanel_statusline_tabline_1.dump
rename to src/testdir/dumps/Test_tabpanel_noeval_1.dump
diff --git a/src/testdir/dumps/Test_tabpanel_tabline_and_tabpanel_0.dump b/src/testdir/dumps/Test_tabpanel_with_tabline_0.dump
similarity index 100%
rename from src/testdir/dumps/Test_tabpanel_tabline_and_tabpanel_0.dump
rename to src/testdir/dumps/Test_tabpanel_with_tabline_0.dump
diff --git a/src/testdir/test_statuslineopt.vim b/src/testdir/test_statuslineopt.vim
new file mode 100644
index 000000000..cc137a98b
--- /dev/null
+++ b/src/testdir/test_statuslineopt.vim
@@ -0,0 +1,151 @@
+" Test 'statuslineopt' with 'statusline'
+"
+
+source util/screendump.vim
+
+def SetUp()
+ set laststatus=2
+ set ch=1
+enddef
+
+def TearDown()
+ set laststatus&
+ set statusline&
+ set statuslineopt&
+ set ch&
+ :only
+enddef
+
+def s:Assert_match_statusline(winid: number, stlh: number, expect: list<string>): void
+ if has('gui_running')
+ redraw!
+ sleep 1m
+ endif
+ var wi = getwininfo(winid)[0]
+ var winh = wi.winrow + wi.height
+ var lines = [winh, winh + wi.status_height - 1]
+ var actual = mapnew(g:ScreenLines(lines, &columns), (_, v) =>
+ v[wi.wincol - 1 : wi.wincol - 1 + wi.width - 1])
+ assert_equal(stlh, wi.status_height)
+ for i in range(len(expect))
+ assert_match(expect[i], actual[i], $'[{i}]')
+ endfor
+enddef
+
+def Test_statuslineopt()
+ set statuslineopt=maxheight:2
+ &statusline = "AAA"
+ var wid = win_getid()
+ var stlh = 2
+ s:Assert_match_statusline(wid, stlh, ['^AAA *', '^ *'])
+ &statusline = "AAA%@BBB"
+ s:Assert_match_statusline(wid, stlh, ['^AAA *', '^BBB *'])
+ &statusline = "AAA%@BBB%@CCC"
+ s:Assert_match_statusline(wid, stlh, ['^AAA *', '^BBB *'])
+
+ set statuslineopt=maxheight:3
+ stlh = 3
+ s:Assert_match_statusline(wid, stlh, ['^AAA *', '^BBB *', '^CCC *'])
+ &statusline = "AAA%@BBB"
+ s:Assert_match_statusline(wid, stlh, ['^AAA *', '^BBB *', '^ *'])
+
+ # Best effort
+ &statusline = "AAA%@BBB%@CCC"
+ set statuslineopt=maxheight:999
+ stlh = &lines - &ch - 1
+ s:Assert_match_statusline(wid, stlh, ['^AAA *', '^BBB *', '^CCC *', '^ *'])
+
+ # Single line
+ set statuslineopt=maxheight:1
+ stlh = 1
+ s:Assert_match_statusline(wid, stlh, ['^AAA *'])
+enddef
+
+def Test_statuslineopt_multi_win()
+ &statusline = "AAA%@BBB%@BB3"
+ var wid1 = win_getid()
+ new ccc
+ &l:statusline = "CCC%@DDD%@DD3"
+ var wid2 = win_getid()
+ set statuslineopt=maxheight:2
+ var stlh = 2
+ s:Assert_match_statusline(wid1, stlh, ['^AAA *', '^BBB *'])
+ s:Assert_match_statusline(wid2, stlh, ['^CCC *', '^DDD *'])
+
+ vnew eee
+ &l:statusline = "EEE%@FFF%@FF3"
+ var wid3 = win_getid()
+ s:Assert_match_statusline(wid1, stlh, ['^AAA *', '^BBB *'])
+ s:Assert_match_statusline(wid2, stlh, ['^CCC *', '^DDD *'])
+ s:Assert_match_statusline(wid3, stlh, ['^EEE *', '^FFF *'])
+
+ quit
+ new eee
+ wid3 = win_getid()
+ s:Assert_match_statusline(wid1, stlh, ['^AAA *', '^BBB *'])
+ s:Assert_match_statusline(wid2, stlh, ['^CCC *', '^DDD *'])
+ s:Assert_match_statusline(wid3, stlh, ['^EEE *', '^FFF *'])
+
+ # Best effort
+ set statuslineopt=maxheight:999
+ stlh = (&lines - &ch - 3) / 3
+ s:Assert_match_statusline(wid1, stlh, ['^AAA *', '^BBB *', '^BB3 *', '^ *'])
+ s:Assert_match_statusline(wid2, stlh, ['^CCC *', '^DDD *', '^DD3 *', '^ *'])
+ s:Assert_match_statusline(wid3, stlh, ['^EEE *', '^FFF *', '^FF3 *', '^ *'])
+
+ # Single line
+ set statuslineopt=maxheight:1
+ stlh = 1
+ s:Assert_match_statusline(wid1, stlh, ['^AAA *'])
+ s:Assert_match_statusline(wid2, stlh, ['^CCC *'])
+ s:Assert_match_statusline(wid3, stlh, ['^EEE *'])
+enddef
+
+let g:StloStatusVar = ''
+def g:StloStatusLine(): string
+ return g:StloStatusVar
+enddef
+
+def Test_statuslineopt_expr()
+ new bbb.txt
+ g:StloStatusVar = 'A001%@A002%@%t'
+ set statuslineopt=maxheight:3
+ &statusline = "%!StloStatusLine()"
+ var wid = win_getid()
+ var stlh = 3
+ s:Assert_match_statusline(wid, stlh, ['^A001 *', '^A002 *', '^bbb\.txt *'])
+
+ &statusline = "%{%StloStatusLine()%}"
+ s:Assert_match_statusline(wid, stlh, ['^A001 *', '^A002 *', '^bbb\.txt *'])
+ g:StloStatusVar = 'B00001%@B002'
+ s:Assert_match_statusline(wid, stlh, ['^B00001 *', '^B002 *', '^ *'])
+enddef
+
+func Test_multistatusline_highlight()
+ CheckScreendump
+
+ let lines =<< trim END
+ func MyStatusLine()
+ return 'L1A01%=%#Search#L1A02%*%=%2*L1A03%*%@'
+ \ .. '%2*L2B01%*%=L2B02%=%#Search#L2B03%*%@'
+ \ .. '%#Search#L3C01%*%=%2*L3C02%*%=L3C03%@'
+ endfunc
+
+ set laststatus=2
+ set statuslineopt=maxheight:3
+ set statusline=%!MyStatusLine()
+ END
+ call writefile(lines, 'XTest_multistatusline_highlight', 'D')
+
+ let buf = g:RunVimInTerminal('-S XTest_multistatusline_highlight', {'rows': 6})
+ call term_sendkeys(buf, "\<C-L>")
+ call VerifyScreenDump(buf, 'Test_multistatusline_highlight_01', {})
+ call term_sendkeys(buf, ":hi link User2 Error\<CR>\<C-L>")
+ call VerifyScreenDump(buf, 'Test_multistatusline_highlight_02', {})
+ call term_sendkeys(buf, ":hi link User2 NONE\<CR>\<C-L>")
+ call VerifyScreenDump(buf, 'Test_multistatusline_highlight_01', {})
+
+ call StopVimInTerminal(buf)
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_tabpanel.vim b/src/testdir/test_tabpanel.vim
index 495969326..6e401c0fd 100644
--- a/src/testdir/test_tabpanel.vim
+++ b/src/testdir/test_tabpanel.vim
@@ -98,7 +98,7 @@ func Call_cmd_funcs()
let g:results = [getcmdpos(), getcmdscreenpos(), getcmdline()]
endfunc
-function Test_tabpanel_cmdline()
+function Test_tabpanel_cmdline_pos()
let save_showtabline = &showtabline
let g:results = []
cnoremap <expr> <F2> Call_cmd_funcs()
@@ -133,6 +133,29 @@ function Test_tabpanel_cmdline()
let &showtabline = save_showtabline
endfunc
+function Test_tabpanel_cmdline_compl()
+ CheckScreendump
+
+ let lines =<< trim END
+ set showtabpanel=2
+ set tabpanelopt=columns:10
+ set showtabline=0
+ tabnew
+ END
+ call writefile(lines, 'XTest_tabpanel_cmdline_compl', 'D')
+
+ let buf = RunVimInTerminal('-S XTest_tabpanel_cmdline_compl', {'rows': 10, 'cols': 45})
+ call term_sendkeys(buf, ":ab\<Tab>")
+ call VerifyScreenDump(buf, 'Test_tabpanel_cmdline_compl_0', {})
+
+ call term_sendkeys(buf, "\<Esc>")
+ call term_sendkeys(buf, ":set wildoptions=pum\<CR>")
+ call term_sendkeys(buf, ":ab\<Tab>")
+ call VerifyScreenDump(buf, 'Test_tabpanel_cmdline_compl_1', {})
+
+ call StopVimInTerminal(buf)
+endfunc
+
function Test_tabpanel_mouse()
let save_showtabline = &showtabline
let save_mouse = &mouse
@@ -233,7 +256,7 @@ function Test_tabpanel_drawing()
function MyTabPanel()
let n = g:actual_curtabpage
let hi = n == tabpagenr() ? 'TabLineSel' : 'TabLine'
- let label = printf("
%%#%sTabNumber#%d:%%#%s#", hi, n, hi)
+ let label = printf("%%@%%#%sTabNumber#%d:%%#%s#", hi, n, hi)
let label ..= '%1*%f%*'
return label
endfunction
@@ -342,12 +365,16 @@ function Test_tabpanel_drawing_fill_tailing()
let &tabpanel = "abc"
redraw!
" Check whether "abc" is cleared
- let &tabpanel = "
TOP
%f
BOTTOM"
+ let &tabpanel = "%@TOP%@%f%@BOTTOM"
END
call writefile(lines, 'XTest_tabpanel_fill_tailing', 'D')
let buf = RunVimInTerminal('-S XTest_tabpanel_fill_tailing', {'rows': 10, 'cols': 45})
+ call VerifyScreenDump(buf, 'Test_tabpanel_drawing_fill_tailing_0', {})
+ " TODO: If line breaks within 'tabpanel' using "
" are no longer supported,
+ " delete the following two lines:
+ call term_sendkeys(buf, ':let &tabpanel = "
TOP
%f
BOTTOM"' .. "\<CR>")
call VerifyScreenDump(buf, 'Test_tabpanel_drawing_fill_tailing_0', {})
call StopVimInTerminal(buf)
@@ -464,30 +491,7 @@ function Test_tabpanel_visual()
call StopVimInTerminal(buf)
endfunc
-function Test_tabpanel_commandline()
- CheckScreendump
-
- let lines =<< trim END
- set showtabpanel=2
- set tabpanelopt=columns:10
- set showtabline=0
- tabnew
- END
- call writefile(lines, 'XTest_tabpanel_commandline', 'D')
-
- let buf = RunVimInTerminal('-S XTest_tabpanel_commandline', {'rows': 10, 'cols': 45})
- call term_sendkeys(buf, ":ab\<Tab>")
- call VerifyScreenDump(buf, 'Test_tabpanel_commandline_0', {})
-
- call term_sendkeys(buf, "\<Esc>")
- call term_sendkeys(buf, ":set wildoptions=pum\<CR>")
- call term_sendkeys(buf, ":ab\<Tab>")
- call VerifyScreenDump(buf, 'Test_tabpanel_commandline_1', {})
-
- call StopVimInTerminal(buf)
-endfunc
-
-function Test_tabpanel_tabline_and_tabpanel()
+function Test_tabpanel_with_tabline()
CheckScreendump
let lines =<< trim END
@@ -501,10 +505,10 @@ function Test_tabpanel_tabline_and_tabpanel()
tabnew
e ccc.txt
END
- call writefile(lines, 'XTest_tabpanel_tabline_and_tabpanel', 'D')
+ call writefile(lines, 'XTest_tabpanel_with_tabline', 'D')
- let buf = RunVimInTerminal('-S XTest_tabpanel_tabline_and_tabpanel', {'rows': 10, 'cols': 45})
- call VerifyScreenDump(buf, 'Test_tabpanel_tabline_and_tabpanel_0', {})
+ let buf = RunVimInTerminal('-S XTest_tabpanel_with_tabline', {'rows': 10, 'cols': 45})
+ call VerifyScreenDump(buf, 'Test_tabpanel_with_tabline_0', {})
call StopVimInTerminal(buf)
endfunc
@@ -582,7 +586,7 @@ endfunc
""" call StopVimInTerminal(buf)
"""endfunc
-function Test_tabpanel_eval_tabpanel_statusline_tabline()
+function Test_tabpanel_eval()
CheckScreendump
let lines =<< trim END
@@ -602,17 +606,17 @@ function Test_tabpanel_eval_tabpanel_statusline_tabline()
tabnew
e ccc
END
- call writefile(lines, 'XTest_tabpanel_eval_tabpanel_statusline_tabline', 'D')
+ call writefile(lines, 'XTest_tabpanel_eval', 'D')
- let buf = RunVimInTerminal('-S XTest_tabpanel_eval_tabpanel_statusline_tabline', {'rows': 10, 'cols': 45})
- call VerifyScreenDump(buf, 'Test_tabpanel_eval_tabpanel_statusline_tabline_0', {})
+ let buf = RunVimInTerminal('-S XTest_tabpanel_eval', {'rows': 10, 'cols': 45})
+ call VerifyScreenDump(buf, 'Test_tabpanel_eval_0', {})
call term_sendkeys(buf, ":set tabpanelopt+=align:right\<CR>")
- call VerifyScreenDump(buf, 'Test_tabpanel_eval_tabpanel_statusline_tabline_1', {})
+ call VerifyScreenDump(buf, 'Test_tabpanel_eval_1', {})
call StopVimInTerminal(buf)
endfunc
-function Test_tabpanel_noeval_tabpanel_statusline_tabline()
+function Test_tabpanel_noeval()
CheckScreendump
let lines =<< trim END
@@ -629,22 +633,23 @@ function Test_tabpanel_noeval_tabpanel_statusline_tabline()
tabnew
e ccc
END
- call writefile(lines, 'XTest_tabpanel_noeval_tabpanel_statusline_tabline', 'D')
+ call writefile(lines, 'XTest_tabpanel_noeval', 'D')
- let buf = RunVimInTerminal('-S XTest_tabpanel_noeval_tabpanel_statusline_tabline', {'rows': 10, 'cols': 45})
- call VerifyScreenDump(buf, 'Test_tabpanel_noeval_tabpanel_statusline_tabline_0', {})
+ let buf = RunVimInTerminal('-S XTest_tabpanel_noeval', {'rows': 10, 'cols': 45})
+ call VerifyScreenDump(buf, 'Test_tabpanel_noeval_0', {})
call term_sendkeys(buf, ":set tabpanelopt+=align:right\<CR>")
- call VerifyScreenDump(buf, 'Test_tabpanel_noeval_tabpanel_statusline_tabline_1', {})
+ call VerifyScreenDump(buf, 'Test_tabpanel_noeval_1', {})
call StopVimInTerminal(buf)
endfunc
-function Test_tabpanel_eval_tabpanel_with_linebreaks()
+function Test_tabpanel_eval_with_linebreaks()
CheckScreendump
let lines =<< trim END
+ let g:ExprRetVal = "top%@$%=[%f]%=$%@bottom"
function Expr()
- return "top
$%=[%f]%=$
bottom"
+ return g:ExprRetVal
endfunction
set showtabpanel=2
set tabpanel=%!Expr()
@@ -656,12 +661,20 @@ function Test_tabpanel_eval_tabpanel_with_linebreaks()
tabnew
e ccc
END
- call writefile(lines, 'XTest_tabpanel_eval_tabpanel_with_linebreaks', 'D')
+ call writefile(lines, 'XTest_tabpanel_eval_with_linebreaks', 'D')
- let buf = RunVimInTerminal('-S XTest_tabpanel_eval_tabpanel_with_linebreaks', {'rows': 10, 'cols': 45})
- call VerifyScreenDump(buf, 'Test_tabpanel_eval_tabpanel_with_linebreaks_0', {})
+ let buf = RunVimInTerminal('-S XTest_tabpanel_eval_with_linebreaks', {'rows': 10, 'cols': 45})
+ call term_sendkeys(buf, "\<C-L>") " Clear cmdline area
+ call VerifyScreenDump(buf, 'Test_tabpanel_eval_with_linebreaks_0', {})
+ call term_sendkeys(buf, ":set tabpanelopt+=align:right\<CR>")
+ call VerifyScreenDump(buf, 'Test_tabpanel_eval_with_linebreaks_1', {})
+ " TODO: If line breaks within 'tabpanel' using "
" are no longer supported,
+ " delete the following five lines:
+ call term_sendkeys(buf, ':let g:ExprRetVal = "top
$%=[%f]%=$
bottom"' .. "\<CR>")
+ call term_sendkeys(buf, ":set tabpanelopt=columns:10\<CR>")
+ call VerifyScreenDump(buf, 'Test_tabpanel_eval_with_linebreaks_0', {})
call term_sendkeys(buf, ":set tabpanelopt+=align:right\<CR>")
- call VerifyScreenDump(buf, 'Test_tabpanel_eval_tabpanel_with_linebreaks_1', {})
+ call VerifyScreenDump(buf, 'Test_tabpanel_eval_with_linebreaks_1', {})
call StopVimInTerminal(buf)
endfunc
diff --git a/src/testdir/util/gen_opt_test.vim b/src/testdir/util/gen_opt_test.vim
index 7431122e1..c39642cd6 100644
--- a/src/testdir/util/gen_opt_test.vim
+++ b/src/testdir/util/gen_opt_test.vim
@@ -312,6 +312,7 @@ let test_values = {
\ ['xxx', '-1', 'timeout:', 'best,double', 'double,fast']],
\ 'splitkeep': [['', 'cursor', 'screen', 'topline'], ['xxx']],
\ 'statusline': [['', 'xxx'], ['%$', '%{', '%{%', '%{%}', '%(', '%)']],
+ \ 'statuslineopt': [['', 'maxheight:1'], ['xxx', 'maxheight', 'fixedheight:3']],
\ 'swapsync': [['', 'sync', 'fsync'], ['xxx']],
\ 'switchbuf': [['', 'useopen', 'usetab', 'split', 'vsplit', 'newtab',
\ 'uselast', 'split,newtab'],
diff --git a/src/version.c b/src/version.c
index 4c5c755a3..7ede284b4 100644
--- a/src/version.c
+++ b/src/version.c
@@ -734,6 +734,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 83,
/**/
82,
/**/
diff --git a/src/vim.h b/src/vim.h
index 02e02c332..1e75d8a2b 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -1692,7 +1692,8 @@ typedef UINT32_TYPEDEF UINT32_T;
#define MIN_COLUMNS 12 // minimal columns for screen
#define MIN_LINES 2 // minimal lines for screen
#define MIN_CMDHEIGHT 1 // minimal height for command line
-#define STATUS_HEIGHT 1 // height of a status line under a window
+#define STATUS_HEIGHT 1 // default height of a status line under a
+ // window
#ifdef FEAT_MENU // height of a status line under a window
# define WINBAR_HEIGHT(wp) (wp)->w_winbar_height
# define VISIBLE_HEIGHT(wp) ((wp)->w_height + (wp)->w_winbar_height)
@@ -2451,6 +2452,19 @@ typedef enum {
ESTACK_SCRIPT,
} estack_arg_T;
+// For temporarily backward compatibility, to be removed soon.
+#define ENABLE_STL_MODE_MULTI_NL
+
+// Argument for build_stl_str_hl_local().
+typedef enum {
+ STL_MODE_SINGLE, // Does not accept line breaks "%@"
+ STL_MODE_MULTI, // Accept line breaks "%@"
+ STL_MODE_GET_RENDERED_HEIGHT, // Just get stl rendered height
+#ifdef ENABLE_STL_MODE_MULTI_NL
+ STL_MODE_MULTI_NL, // Accept line breaks "%@" and "
"
+#endif
+} stl_mode_T;
+
// Return value of match_keyprotocol()
typedef enum {
KEYPROTOCOL_NONE,
diff --git a/src/window.c b/src/window.c
index 62089bdec..e8142ca20 100644
--- a/src/window.c
+++ b/src/window.c
@@ -53,6 +53,10 @@ static void win_goto_ver(int up, long count);
static void win_goto_hor(int left, long count);
static void frame_add_height(frame_T *frp, int n);
static void last_status_rec(frame_T *fr, int statusline);
+#if defined(FEAT_STL_OPT)
+static void frame_change_statusline_height_rec(frame_T *frp,
+ bool actual_change);
+#endif
static void frame_flatten(frame_T *frp);
static void winframe_restore(win_T *wp, int dir, frame_T *unflat_altfr);
@@ -94,6 +98,11 @@ static int close_disallowed = 0;
// to avoid that winframe_remove() is called recursively
static int frame_locked = 0;
+#if defined(FEAT_STL_OPT)
+static int stlo_mh = STATUS_HEIGHT;
+static int stlh_effort;
+#endif
+
/*
* Disallow changing the window layout (split window, close window, move
* window). Resizing is still allowed.
@@ -1035,7 +1044,7 @@ win_split_ins(
emsg(_(e_not_enough_room));
goto theend;
}
- need_status = STATUS_HEIGHT;
+ need_status = statusline_height(oldwin);
}
#ifdef FEAT_GUI
@@ -1140,7 +1149,7 @@ win_split_ins(
*/
// Current window requires at least 1 space.
wmh1 = (p_wmh == 0 ? 1 : p_wmh) + WINBAR_HEIGHT(curwin);
- needed = wmh1 + STATUS_HEIGHT;
+ needed = wmh1 + statusline_height(oldwin);
if (flags & WSP_ROOM)
needed += p_wh - wmh1;
if (flags & (WSP_BOT | WSP_TOP))
@@ -1179,18 +1188,23 @@ win_split_ins(
oldwin_height = oldwin->w_height;
if (need_status)
{
- oldwin->w_status_height = STATUS_HEIGHT;
- oldwin_height -= STATUS_HEIGHT;
+ oldwin->w_status_height = statusline_height(oldwin);
+ oldwin_height -= oldwin->w_status_height;
}
if (new_size == 0)
- new_size = oldwin_height / 2;
- if (new_size > available - minheight - STATUS_HEIGHT)
- new_size = available - minheight - STATUS_HEIGHT;
+ {
+ if (lastwin == firstwin && p_ls == 0)
+ new_size = (oldwin_height - statusline_height(oldwin) + 1) / 2;
+ else
+ new_size = (oldwin_height - oldwin->w_status_height + 1) / 2;
+ }
+ if (new_size > available - minheight - statusline_height(oldwin))
+ new_size = available - minheight - statusline_height(oldwin);
if (new_size < wmh1)
new_size = wmh1;
// if it doesn't fit in the current window, need win_equal()
- if (oldwin_height - new_size - STATUS_HEIGHT < p_wmh)
+ if (oldwin_height - new_size - statusline_height(oldwin) < p_wmh)
do_equal = TRUE;
// We don't like to take lines for the new window from a
@@ -1203,11 +1217,11 @@ win_split_ins(
set_fraction(oldwin);
did_set_fraction = TRUE;
- win_setheight_win(oldwin->w_height + new_size + STATUS_HEIGHT,
- oldwin);
+ win_setheight_win(oldwin->w_height + new_size
+ + statusline_height(oldwin), oldwin);
oldwin_height = oldwin->w_height;
if (need_status)
- oldwin_height -= STATUS_HEIGHT;
+ oldwin_height -= statusline_height(oldwin);
}
// Only make all windows the same height if one of them (except oldwin)
@@ -1221,7 +1235,7 @@ win_split_ins(
if (frp->fr_win != oldwin && frp->fr_win != NULL
&& (frp->fr_win->w_height > new_size
|| frp->fr_win->w_height > oldwin_height - new_size
- - STATUS_HEIGHT))
+ - statusline_height(oldwin)))
{
do_equal = TRUE;
break;
@@ -1346,16 +1360,16 @@ win_split_ins(
if (need_status)
{
- win_new_height(oldwin, oldwin->w_height - 1);
+ win_new_height(oldwin, oldwin->w_height - need_status);
oldwin->w_status_height = need_status;
}
if (flags & (WSP_TOP | WSP_BOT))
{
// set height and row of new window to full height
wp->w_winrow = tabline_height();
- win_new_height(wp, curfrp->fr_height - (p_ls > 0)
- - WINBAR_HEIGHT(wp));
- wp->w_status_height = (p_ls > 0);
+ win_new_height(wp, curfrp->fr_height
+ - statusline_height(curfrp->fr_win) - WINBAR_HEIGHT(wp));
+ wp->w_status_height = statusline_height(curfrp->fr_win);
}
else
{
@@ -1424,27 +1438,28 @@ win_split_ins(
+ WINBAR_HEIGHT(wp);
if (!((flags & WSP_BOT) && p_ls == 0))
- new_fr_height -= STATUS_HEIGHT;
+ new_fr_height -= statusline_height(curfrp->fr_win);
if (flags & WSP_BOT)
frame_add_statusline(curfrp);
frame_new_height(curfrp, new_fr_height, flags & WSP_TOP, FALSE,
FALSE);
}
else
- win_new_height(oldwin, oldwin_height - (new_size + STATUS_HEIGHT));
+ win_new_height(oldwin, oldwin_height - (new_size
+ + statusline_height(wp)));
if (before) // new window above current one
{
wp->w_winrow = oldwin->w_winrow;
- wp->w_status_height = STATUS_HEIGHT;
- oldwin->w_winrow += wp->w_height + STATUS_HEIGHT;
+ wp->w_status_height = statusline_height(wp);
+ oldwin->w_winrow += wp->w_height + statusline_height(wp);
}
else // new window below current one
{
wp->w_winrow = oldwin->w_winrow + VISIBLE_HEIGHT(oldwin)
- + STATUS_HEIGHT;
+ + statusline_height(oldwin);
wp->w_status_height = old_status_height;
if (!(flags & WSP_BOT))
- oldwin->w_status_height = STATUS_HEIGHT;
+ oldwin->w_status_height = statusline_height(oldwin);
}
frame_fix_height(wp);
frame_fix_height(oldwin);
@@ -1742,7 +1757,7 @@ make_windows(
{
// Each window needs at least 'winminheight' lines and a status line.
maxcount = (VISIBLE_HEIGHT(curwin) + curwin->w_status_height
- - (p_wh - p_wmh)) / (p_wmh + STATUS_HEIGHT);
+ - (p_wh - p_wmh)) / (p_wmh + statusline_height(curwin));
}
if (maxcount < 2)
@@ -1773,8 +1788,8 @@ make_windows(
else
{
if (win_split(curwin->w_height - (curwin->w_height - todo
- * STATUS_HEIGHT) / (todo + 1)
- - STATUS_HEIGHT, WSP_ABOVE) == FAIL)
+ * statusline_height(curwin)) / (todo + 1)
+ - statusline_height(curwin), WSP_ABOVE) == FAIL)
break;
}
@@ -2104,6 +2119,9 @@ win_equal(
win_equal_rec(next_curwin == NULL ? curwin : next_curwin, current,
topframe, dir, firstwin->w_wincol, tabline_height(),
topframe->fr_width, topframe->fr_height);
+#if defined(FEAT_STL_OPT)
+ frame_change_statusline_height();
+#endif
if (!is_aucmd_win(next_curwin))
win_fix_scroll(TRUE);
}
@@ -2301,10 +2319,11 @@ win_equal_rec(
n = frame_minheight(topfr, NOWIN);
// add one for the bottom window if it doesn't have a statusline
if (row + height == cmdline_row && p_ls == 0)
- extra_sep = 1;
+ extra_sep = statusline_height(NULL);
else
extra_sep = 0;
- totwincount = (n + extra_sep) / (p_wmh + 1);
+ totwincount = (n + extra_sep) / (p_wmh
+ + statusline_height(NULL));
has_next_curwin = frame_has_win(topfr, next_curwin);
/*
@@ -2343,7 +2362,8 @@ win_equal_rec(
else
// These windows don't use up room.
totwincount -= (n + (fr->fr_next == NULL
- ? extra_sep : 0)) / (p_wmh + 1);
+ ? extra_sep : 0))
+ / (p_wmh + statusline_height(NULL));
room -= new_size - n;
if (room < 0)
{
@@ -2394,7 +2414,7 @@ win_equal_rec(
// Compute the maximum number of windows vert. in "fr".
n = frame_minheight(fr, NOWIN);
wincount = (n + (fr->fr_next == NULL ? extra_sep : 0))
- / (p_wmh + 1);
+ / (p_wmh + statusline_height(NULL));
m = frame_minheight(fr, next_curwin);
if (has_next_curwin)
hnc = frame_has_win(fr, next_curwin);
@@ -4121,7 +4141,7 @@ frame_add_statusline(frame_T *frp)
if (frp->fr_layout == FR_LEAF)
{
wp = frp->fr_win;
- wp->w_status_height = STATUS_HEIGHT;
+ wp->w_status_height = statusline_height(wp);
}
else if (frp->fr_layout == FR_ROW)
{
@@ -6377,6 +6397,9 @@ win_comp_pos(void)
int col = TPL_LCOL();
frame_comp_pos(topframe, &row, &col);
+#if defined(FEAT_STL_OPT)
+ frame_change_statusline_height();
+#endif
}
/*
@@ -6445,6 +6468,9 @@ win_ensure_size(void)
win_setheight(int height)
{
win_setheight_win(height, curwin);
+#if defined(FEAT_STL_OPT)
+ frame_change_statusline_height();
+#endif
}
/*
@@ -7522,6 +7548,9 @@ last_status(
{
// Don't make a difference between horizontal or vertical split.
last_status_rec(topframe, last_stl_height(morewin) > 0);
+#if defined(FEAT_STL_OPT)
+ frame_change_statusline_height();
+#endif
}
static void
@@ -7536,7 +7565,7 @@ last_status_rec(frame_T *fr, int statusline)
if (wp->w_status_height != 0 && !statusline)
{
// remove status line
- win_new_height(wp, wp->w_height + 1);
+ win_new_height(wp, wp->w_height + wp->w_status_height);
wp->w_status_height = 0;
comp_col();
}
@@ -7558,15 +7587,16 @@ last_status_rec(frame_T *fr, int statusline)
else
fp = fp->fr_parent;
}
- wp->w_status_height = 1;
+ wp->w_status_height = statusline_height(wp);
if (fp != fr)
{
- frame_new_height(fp, fp->fr_height - 1, FALSE, FALSE, FALSE);
+ frame_new_height(fp, fp->fr_height - wp->w_status_height,
+ FALSE, FALSE, FALSE);
frame_fix_height(wp);
win_comp_pos();
}
else
- win_new_height(wp, wp->w_height - 1);
+ win_new_height(wp, wp->w_height - wp->w_status_height);
comp_col();
redraw_all_later(UPD_SOME_VALID);
}
@@ -7589,6 +7619,125 @@ last_status_rec(frame_T *fr, int statusline)
}
}
+#if defined(FEAT_STL_OPT)
+/*
+ * Set a status line height to windows at the bottom of "frp".
+ * Note: Does not check if there is room!
+ */
+ void
+frame_change_statusline_height(void)
+{
+ tabpage_T *tp;
+
+ stlh_effort = stlo_mh;
+ FOR_ALL_TABPAGES(tp)
+ frame_change_statusline_height_rec(tp->tp_topframe, false);
+
+ stlo_mh = stlh_effort;
+ FOR_ALL_TABPAGES(tp)
+ frame_change_statusline_height_rec(tp->tp_topframe, true);
+
+ comp_col();
+ redraw_all_later(UPD_SOME_VALID);
+}
+
+ static void
+frame_change_statusline_height_rec(frame_T *frp, bool actual_change)
+{
+ if (frp->fr_layout == FR_LEAF)
+ {
+ win_T *wp = frp->fr_win;
+
+ if (wp->w_height > 0 && wp->w_status_height > 0)
+ {
+ if (actual_change)
+ {
+ wp->w_status_height = stlo_mh;
+ if (wp->w_status_height > frp->fr_height - wp->w_winbar_height
+ - p_wmh)
+ {
+ wp->w_status_height = frp->fr_height - wp->w_winbar_height
+ - p_wmh;
+ }
+ win_new_height(wp, frp->fr_height - wp->w_status_height
+ - wp->w_winbar_height);
+ }
+ else
+ {
+ if (frp->fr_height - wp->w_winbar_height - p_wmh < stlh_effort)
+ stlh_effort = frp->fr_height - wp->w_winbar_height - p_wmh;
+ }
+ }
+ }
+ else if (frp->fr_layout == FR_ROW)
+ {
+ // Handle all the frames in the row.
+ for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next)
+ frame_change_statusline_height_rec(frp, actual_change);
+ }
+ else // frp->fr_layout == FR_COL
+ {
+ // Handle all the frames in the column.
+ for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next)
+ frame_change_statusline_height_rec(frp, actual_change);
+ }
+}
+
+/*
+ * Check "stlopt" as 'statuslineopt' and update the members of "wp".
+ * This is called when 'statuslineopt' is changed.
+ * Returns FAIL for failure, OK otherwise.
+ */
+ int
+statuslineopt_changed(
+ char_u *stlopt
+ )
+{
+ char_u *p;
+ int l_stlo_mh = 1;
+
+ p = stlopt;
+
+ while (*p != NUL)
+ {
+ // Note: Keep this in sync with p_stlo_values
+ if (STRNCMP(p, "maxheight:", 10) == 0 && VIM_ISDIGIT(p[10]))
+ {
+ p += 10;
+ l_stlo_mh = getdigits(&p);
+ if (l_stlo_mh < 1)
+ return FAIL;
+ }
+ if (*p != ',' && *p != NUL)
+ return FAIL;
+ if (*p == ',')
+ ++p;
+ }
+
+ stlo_mh = l_stlo_mh;
+
+ return OK;
+}
+#endif
+
+/*
+ * Return the number of lines used by the status line.
+ * "wp" is not used currently because 'statuslineopt' is a global option.
+ * NULL is passed when called from layout calculations (e.g. 'laststatus')
+ * where no specific window context is available.
+ */
+ int
+statusline_height(win_T *wp UNUSED)
+{
+ return
+#if defined(FEAT_STL_OPT)
+ stlo_mh
+#else
+ STATUS_HEIGHT
+#endif
+ ;
+}
+
/*
* Return the number of lines used by the tab page line.
*/