]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0083: Cannot have a mutli-line statusline v9.2.0083
authorHirohito Higashi <h.east.727@gmail.com>
Sun, 1 Mar 2026 16:57:08 +0000 (16:57 +0000)
committerChristian Brabandt <cb@256bit.org>
Sun, 1 Mar 2026 16:57:08 +0000 (16:57 +0000)
Problem:  Cannot have a mutli-line statusline.
Solution: Add support for multi-line statusline
          (Hirohito Higashi).

closes: #19123

Signed-off-by: Hirohito Higashi <h.east.727@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
38 files changed:
runtime/doc/options.txt
runtime/doc/quickref.txt
runtime/doc/tags
runtime/doc/version9.txt
runtime/doc/windows.txt
runtime/optwin.vim
runtime/syntax/vim.vim
src/buffer.c
src/drawscreen.c
src/feature.h
src/gui.c
src/option.h
src/optiondefs.h
src/optionstr.c
src/proto/buffer.pro
src/proto/optionstr.pro
src/proto/window.pro
src/screen.c
src/structs.h
src/tabpanel.c
src/testdir/Make_all.mak
src/testdir/dumps/Test_multistatusline_highlight_01.dump [new file with mode: 0644]
src/testdir/dumps/Test_multistatusline_highlight_02.dump [new file with mode: 0644]
src/testdir/dumps/Test_tabpanel_cmdline_compl_0.dump [moved from src/testdir/dumps/Test_tabpanel_commandline_0.dump with 100% similarity]
src/testdir/dumps/Test_tabpanel_cmdline_compl_1.dump [moved from src/testdir/dumps/Test_tabpanel_commandline_1.dump with 100% similarity]
src/testdir/dumps/Test_tabpanel_eval_0.dump [moved from src/testdir/dumps/Test_tabpanel_eval_tabpanel_statusline_tabline_0.dump with 100% similarity]
src/testdir/dumps/Test_tabpanel_eval_1.dump [moved from src/testdir/dumps/Test_tabpanel_eval_tabpanel_statusline_tabline_1.dump with 100% similarity]
src/testdir/dumps/Test_tabpanel_eval_with_linebreaks_0.dump [moved from src/testdir/dumps/Test_tabpanel_eval_tabpanel_with_linebreaks_0.dump with 91% similarity]
src/testdir/dumps/Test_tabpanel_eval_with_linebreaks_1.dump [moved from src/testdir/dumps/Test_tabpanel_eval_tabpanel_with_linebreaks_1.dump with 100% similarity]
src/testdir/dumps/Test_tabpanel_noeval_0.dump [moved from src/testdir/dumps/Test_tabpanel_noeval_tabpanel_statusline_tabline_0.dump with 100% similarity]
src/testdir/dumps/Test_tabpanel_noeval_1.dump [moved from src/testdir/dumps/Test_tabpanel_noeval_tabpanel_statusline_tabline_1.dump with 100% similarity]
src/testdir/dumps/Test_tabpanel_with_tabline_0.dump [moved from src/testdir/dumps/Test_tabpanel_tabline_and_tabpanel_0.dump with 100% similarity]
src/testdir/test_statuslineopt.vim [new file with mode: 0644]
src/testdir/test_tabpanel.vim
src/testdir/util/gen_opt_test.vim
src/version.c
src/vim.h
src/window.c

index 47948bdf726e252aceb709664e36c788f1fd4035..e1239855ba5c1b23afce165414f1cd7d1a6fd70b 100644 (file)
@@ -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 "\n" to force line breaks: >
+       You can use "%@" or "\n" to insert a new line: >
 
                set tabpanel=%!TabPanel()
                function! TabPanel() abort
-                 return printf("(%2d)\n  %%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 "\n" is considered experimental and may change in the
-       future; a %-atom may be used instead.
+       Note: using "\n" is considered experimental and deprecated, prefer
+       the |stl-%@| atom instead.
 
                                        *'tabpanelopt'* *'tplo'*
 'tabpanelopt' 'tplo'   string  (default "")
index 6fff5cfc194cbd955fd8a97f84f68883f4d75f8a..2d4cf57e784a51290e735400dc96a7d92323600e 100644 (file)
@@ -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
index 017362863b79e8fcdfa7bf5cb287055fe2ad6dc1..1cac3072d761bdb219e38997d334cdf71ee7e0c9 100644 (file)
@@ -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()*
index 8261c32effb053292b970fd816241f2e2df4b152..614051bc20d81735eb02af750102b1fa6c68d004 100644 (file)
@@ -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*
index eee53cbdd7721e5e5062c5bb77a5a7f64a845ec0..37b7289b23b7d05329ac58944f65214ecf177d0e 100644 (file)
@@ -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
index 7987a48772ea0997a1d00ac81ce0e772f34ff756..28d91abc5c798d7bb914e0b6f94115bfd1e4c186 100644 (file)
@@ -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 <Bram@vim.org>
 
 " If there already is an option window, jump to that one.
@@ -490,8 +490,10 @@ call append("$", " \tset 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("$", "\t" .. s:local_to_window)
+  call <SID>AddOption("statuslineopt", gettext("optional settings for the status line"))
+  call <SID>OptionG("stlo", &stlo)
 endif
-call append("$", "\t" .. 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\""))
index f75436a2ae6ee2e73c4944712ba1dbe2b20d5f43..4c96b6e7a1138c8061efe19e3d9b928b49e7ef96 100644 (file)
@@ -2,7 +2,7 @@
 " Language:       Vim script
 " Maintainer:     Hirohito Higashi <h.east.727 ATMARK gmail.com>
 "         Doug Kearns <dougkearns@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
index d96f2fdc1ad6a5514fb41fb0f15e077333bf9742..f2d3a627152603884d64f6c01b4e3e5b94613f21 100644 (file)
@@ -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 == '\n' || *s == '\r')
+           {
+               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)
index e5de9abf119d359fdcf176362a27d2b14687abbd..aff03cb87224ad98447599c24bbe6be95147ddf3 100644 (file)
@@ -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;
 }
index 376e40994ec4747628ad288d15fe8ff691fbbe6f..24928edf61ff7fea3a3177245b564fee7cbafcd4 100644 (file)
 #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
index 9bb9c98e8f7c7f593529995c8209431e4ed107cd..a805c1423c13b23d269998f03cca5a5448fedd0b 100644 (file)
--- 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);
index e482b86663adf53fa0ac2ab9068010e9552311a9..46b8daf09e33d5c0525eb5df24779e974432b1ba 100644 (file)
@@ -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'
index 70dba7601ef5a22540e36e8ad5ccbf04cb8d1b2f..65094348091cb2aa8093ad99c9fdd93154a99fd3 100644 (file)
@@ -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,
index 7e2ac656769bb0674e4a5846805f48509568ef35..d4287b46cc9621e1891b96ed15c25607c2b4126a 100644 (file)
@@ -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
 
index cb4de270d8365ff5d3bfaadf0ad7ad67fe671a09..db939b381d6d5f67e7bddd3d95ad0ef38cf2971a 100644 (file)
@@ -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);
index b60037024f79bfd15a80aabb1fd8c1b6d53bce33..7e55d3b19990a5359084d578ec88aca54a3a69a5 100644 (file)
@@ -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);
index 62d8bc639aac2fe873ce79eca48a3813350f45ca..7bc2c114cc65d12d38249c611ee36188aefa69f7 100644 (file)
@@ -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);
index 905f18499a4aa3b5a31527dd1e028f1e702c4ca9..1e9c9a7d5dd889d33099bea9592f0e17a92b4f80 100644 (file)
@@ -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)
     {
index 805d9ada3ff55cade9ab9ca084c8301bed509e0c..f2c1188a93604ac3bbc8fbe42e18792e40dd40ea 100644 (file)
@@ -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).
index f2bccea1b9bcfab6c3dbe9c40bd7b28e7b0a4f70..12d6446832e6c7c014ddc503e54b9ab43dff3b5d 100644 (file)
@@ -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] == '\n' || p[j] == '\r')
+       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] == '\n' || p[i] == '\r')
-               {
-                   // 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] != '\n' && p[i] != '\r' && 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;
index 31f66d7baf1cd4a9f09ef3aef2d3600b758ddea8..07bea0702a709bbe2ff019fc6c5b55d334da6eeb 100644 (file)
@@ -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 (file)
index 0000000..7ae7167
--- /dev/null
@@ -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 (file)
index 0000000..d50a5ea
--- /dev/null
@@ -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
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 80f06ce7455c2aaf058a4bdae196509c3667a3c0..d28904c781994a5c26b2da35e43cad494851a5f6 100644 (file)
@@ -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/test_statuslineopt.vim b/src/testdir/test_statuslineopt.vim
new file mode 100644 (file)
index 0000000..cc137a9
--- /dev/null
@@ -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
index 4959693260778ff00b24d6150c60992c12e356af..6e401c0fda3df781137a324c40f282bab677803e 100644 (file)
@@ -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("\n%%#%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 = "\nTOP\n%f\nBOTTOM"
+    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 "\n" are no longer supported,
+  " delete the following two lines:
+  call term_sendkeys(buf, ':let &tabpanel = "\nTOP\n%f\nBOTTOM"' .. "\<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\n$%=[%f]%=$\nbottom"
+      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 "\n" are no longer supported,
+  " delete the following five lines:
+  call term_sendkeys(buf, ':let g:ExprRetVal = "top\n$%=[%f]%=$\nbottom"' .. "\<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
index 7431122e1255b4cb019ec24568e6546423bc9350..c39642cd655f39aa2d9db599ad6e264fc8359003 100644 (file)
@@ -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'],
index 4c5c755a3566a815e06fe960059be3fcd6ac18d0..7ede284b48da1bb3fef2f109a0d65d33e84153cc 100644 (file)
@@ -734,6 +734,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    83,
 /**/
     82,
 /**/
index 02e02c3324c3943bce8154b986c5cccae6c665f2..1e75d8a2be60f1811b7d4d56c4f4dba05dd83365 100644 (file)
--- 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 "\n"
+#endif
+} stl_mode_T;
+
 // Return value of match_keyprotocol()
 typedef enum {
     KEYPROTOCOL_NONE,
index 62089bdec9c9086bb89870d2041b3585b04e774d..e8142ca202b9450a8f2b280d925f56c2b1ce8dd0 100644 (file)
@@ -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.
  */