neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

commit 8a626e5c4a5254c9ec952abe6ffe1e9b6fef9b2b
parent 23d0d4c92a5758731a84977c08ce16fc8477f4d0
Author: glepnir <glephunter@gmail.com>
Date:   Thu, 27 Nov 2025 01:10:45 +0800

feat(float): 'statusline' in floating windows #36521

Problem:
Can't show 'statusline' in floating windows.

Solution:
Use window-local 'statusline' to control floating window statusline visibility.
Diffstat:
Mruntime/doc/api.txt | 134++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mruntime/doc/news.txt | 2++
Mruntime/doc/windows.txt | 2++
Mruntime/lua/vim/_meta/api.lua | 138++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mruntime/lua/vim/_meta/api_keysets.lua | 30+++++++++++++++---------------
Msrc/nvim/api/keysets_defs.h | 30+++++++++++++++---------------
Msrc/nvim/api/win_config.c | 138++++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/nvim/option.c | 1+
Msrc/nvim/optionstr.c | 9++++++++-
Msrc/nvim/statusline.c | 23+++++++++++++++++------
Msrc/nvim/window.c | 9++++++---
Msrc/nvim/winfloat.c | 32++++++++++++++++++++++++++++++--
Mtest/functional/ui/statusline_spec.lua | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
13 files changed, 407 insertions(+), 247 deletions(-)

diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt @@ -3781,25 +3781,37 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()* • {enter} (`boolean`) Enter the window (make it the current window) • {config} (`vim.api.keyset.win_config`) Map defining the window configuration. Keys: - • relative: Sets the window layout to "floating", placed at - (row,col) coordinates relative to: - • "cursor" Cursor position in current window. - • "editor" The global editor grid. - • "laststatus" 'laststatus' if present, or last row. - • "mouse" Mouse position. - • "tabline" Tabline if present, or first row. - • "win" Window given by the `win` field, or current - window. - • win: |window-ID| window to split, or relative window when - creating a float (relative="win"). • anchor: Decides which corner of the float to place at (row,col): • "NW" northwest (default) • "NE" northeast • "SW" southwest • "SE" southeast - • width: Window width (in character cells). Minimum of 1. - • height: Window height (in character cells). Minimum of 1. + • border: (`string|string[]`) (defaults to 'winborder' + option) Window border. The string form accepts the same + values as the 'winborder' option. The array form must have + a length of eight or any divisor of eight, specifying the + chars that form the border in a clockwise fashion starting + from the top-left corner. For example, the double-box + style can be specified as: > + [ "╔", "═" ,"╗", "║", "╝", "═", "╚", "║" ]. +< + If fewer than eight chars are given, they will be + repeated. An ASCII border could be specified as: > + [ "/", "-", \"\\\\\", "|" ], +< + Or one char for all sides: > + [ "x" ]. +< + Empty string can be used to hide a specific border. This + example will show only vertical borders, not horizontal: > + [ "", "", "", ">", "", "", "", "<" ] +< + By default, |hl-FloatBorder| highlight is used, which + links to |hl-WinSeparator| when not defined. Each border + side can specify an optional highlight: > + [ ["+", "MyCorner"], ["x", "MyBorder"] ]. +< • bufpos: Places float relative to buffer text (only when relative="win"). Takes a tuple of zero-indexed `[line, column]`. `row` and `col` if given are applied @@ -3807,32 +3819,46 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()* • `row=1` and `col=0` if `anchor` is "NW" or "NE" • `row=0` and `col=0` if `anchor` is "SW" or "SE" (thus like a tooltip near the buffer text). - • row: Row position in units of "screen cell height", may be - fractional. • col: Column position in units of screen cell width, may be fractional. + • external: GUI should display the window as an external + top-level window. Currently accepts no other positioning + configuration together with this. + • fixed: If true when anchor is NW or SW, the float window + would be kept fixed even if the window would be truncated. • focusable: Enable focus by user actions (wincmds, mouse events). Defaults to true. Non-focusable windows can be entered by |nvim_set_current_win()|, or, when the `mouse` field is set to true, by mouse events. See |focusable|. + • footer: (optional) Footer in window border, string or + list. List should consist of `[text, highlight]` tuples. + If string, or a tuple lacks a highlight, the default + highlight group is `FloatFooter`. + • footer_pos: Footer position. Must be set with `footer` + option. Value can be one of "left", "center", or "right". + Default is `"left"`. + • height: Window height (in character cells). Minimum of 1. + • hide: If true the floating window will be hidden and the + cursor will be invisible when focused on it. • mouse: Specify how this window interacts with mouse events. Defaults to `focusable` value. • If false, mouse events pass through this window. • If true, mouse events interact with this window normally. - • external: GUI should display the window as an external - top-level window. Currently accepts no other positioning - configuration together with this. - • zindex: Stacking order. floats with higher `zindex` go on - top on floats with lower indices. Must be larger than - zero. The following screen elements have hard-coded - z-indices: - • 100: insert completion popupmenu - • 200: message scrollback - • 250: cmdline completion popupmenu (when - wildoptions+=pum) The default value for floats are 50. - In general, values below 100 are recommended, unless - there is a good reason to overshadow builtin elements. + • noautocmd: Block all autocommands for the duration of the + call. Cannot be changed by |nvim_win_set_config()|. + • relative: Sets the window layout to "floating", placed at + (row,col) coordinates relative to: + • "cursor" Cursor position in current window. + • "editor" The global editor grid. + • "laststatus" 'laststatus' if present, or last row. + • "mouse" Mouse position. + • "tabline" Tabline if present, or first row. + • "win" Window given by the `win` field, or current + window. + • row: Row position in units of "screen cell height", may be + fractional. + • split: Split direction: "left", "right", "above", "below". • style: (optional) Configure the appearance of the window. Currently only supports one value: • "minimal" Nvim will display the window with many UI @@ -3845,31 +3871,6 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()* empty. The end-of-buffer region is hidden by setting `eob` flag of 'fillchars' to a space char, and clearing the |hl-EndOfBuffer| region in 'winhighlight'. - • border: (`string|string[]`) (defaults to 'winborder' - option) Window border. The string form accepts the same - values as the 'winborder' option. The array form must have - a length of eight or any divisor of eight, specifying the - chars that form the border in a clockwise fashion starting - from the top-left corner. For example, the double-box - style can be specified as: > - [ "╔", "═" ,"╗", "║", "╝", "═", "╚", "║" ]. -< - If fewer than eight chars are given, they will be - repeated. An ASCII border could be specified as: > - [ "/", "-", \"\\\\\", "|" ], -< - Or one char for all sides: > - [ "x" ]. -< - Empty string can be used to hide a specific border. This - example will show only vertical borders, not horizontal: > - [ "", "", "", ">", "", "", "", "<" ] -< - By default, |hl-FloatBorder| highlight is used, which - links to |hl-WinSeparator| when not defined. Each border - side can specify an optional highlight: > - [ ["+", "MyCorner"], ["x", "MyBorder"] ]. -< • title: (optional) Title in window border, string or list. List should consist of `[text, highlight]` tuples. If string, or a tuple lacks a highlight, the default @@ -3877,21 +3878,20 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()* • title_pos: Title position. Must be set with `title` option. Value can be one of "left", "center", or "right". Default is `"left"`. - • footer: (optional) Footer in window border, string or - list. List should consist of `[text, highlight]` tuples. - If string, or a tuple lacks a highlight, the default - highlight group is `FloatFooter`. - • footer_pos: Footer position. Must be set with `footer` - option. Value can be one of "left", "center", or "right". - Default is `"left"`. - • noautocmd: Block all autocommands for the duration of the - call. Cannot be changed by |nvim_win_set_config()|. - • fixed: If true when anchor is NW or SW, the float window - would be kept fixed even if the window would be truncated. - • hide: If true the floating window will be hidden and the - cursor will be invisible when focused on it. • vertical: Split vertically |:vertical|. - • split: Split direction: "left", "right", "above", "below". + • width: Window width (in character cells). Minimum of 1. + • win: |window-ID| window to split, or relative window when + creating a float (relative="win"). + • zindex: Stacking order. floats with higher `zindex` go on + top on floats with lower indices. Must be larger than + zero. The following screen elements have hard-coded + z-indices: + • 100: insert completion popupmenu + • 200: message scrollback + • 250: cmdline completion popupmenu (when + wildoptions+=pum) The default value for floats are 50. + In general, values below 100 are recommended, unless + there is a good reason to overshadow builtin elements. • _cmdline_offset: (EXPERIMENTAL) When provided, anchor the |cmdline-completion| popupmenu to this window, with an offset in screen cell width. diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt @@ -151,6 +151,8 @@ API escape sequences to the terminal when Nvim is running in the |TUI|. • |nvim_echo()| can set the |ui-messages| kind with which to emit the message. • |nvim_echo()| can create |Progress| messages +• |nvim_open_win()| floating windows can show a 'statusline'. Plugins can use + `style='minimal'` or `:setlocal statusline=` to hide the statusline. BUILD diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt @@ -127,6 +127,8 @@ 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. +Note: |floating-windows| 'statusline' is not affected by 'laststatus'. + Normally, inversion is used to display the status line. This can be changed with the |hl-StatusLine| highlight group. If no highlighting is used for the status line, the '^' character is used for the current window, and '=' for diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua @@ -1758,64 +1758,11 @@ function vim.api.nvim_open_term(buffer, opts) end --- @param buffer integer Buffer to display, or 0 for current buffer --- @param enter boolean Enter the window (make it the current window) --- @param config vim.api.keyset.win_config Map defining the window configuration. Keys: ---- - relative: Sets the window layout to "floating", placed at (row,col) ---- coordinates relative to: ---- - "cursor" Cursor position in current window. ---- - "editor" The global editor grid. ---- - "laststatus" 'laststatus' if present, or last row. ---- - "mouse" Mouse position. ---- - "tabline" Tabline if present, or first row. ---- - "win" Window given by the `win` field, or current window. ---- - win: `window-ID` window to split, or relative window when creating a ---- float (relative="win"). --- - anchor: Decides which corner of the float to place at (row,col): --- - "NW" northwest (default) --- - "NE" northeast --- - "SW" southwest --- - "SE" southeast ---- - width: Window width (in character cells). Minimum of 1. ---- - height: Window height (in character cells). Minimum of 1. ---- - bufpos: Places float relative to buffer text (only when ---- relative="win"). Takes a tuple of zero-indexed `[line, column]`. ---- `row` and `col` if given are applied relative to this ---- position, else they default to: ---- - `row=1` and `col=0` if `anchor` is "NW" or "NE" ---- - `row=0` and `col=0` if `anchor` is "SW" or "SE" ---- (thus like a tooltip near the buffer text). ---- - row: Row position in units of "screen cell height", may be fractional. ---- - col: Column position in units of screen cell width, may be fractional. ---- - focusable: Enable focus by user actions (wincmds, mouse events). ---- Defaults to true. Non-focusable windows can be entered by ---- `nvim_set_current_win()`, or, when the `mouse` field is set to true, ---- by mouse events. See `focusable`. ---- - mouse: Specify how this window interacts with mouse events. ---- Defaults to `focusable` value. ---- - If false, mouse events pass through this window. ---- - If true, mouse events interact with this window normally. ---- - external: GUI should display the window as an external ---- top-level window. Currently accepts no other positioning ---- configuration together with this. ---- - zindex: Stacking order. floats with higher `zindex` go on top on ---- floats with lower indices. Must be larger than zero. The ---- following screen elements have hard-coded z-indices: ---- - 100: insert completion popupmenu ---- - 200: message scrollback ---- - 250: cmdline completion popupmenu (when wildoptions+=pum) ---- The default value for floats are 50. In general, values below 100 are ---- recommended, unless there is a good reason to overshadow builtin ---- elements. ---- - style: (optional) Configure the appearance of the window. Currently ---- only supports one value: ---- - "minimal" Nvim will display the window with many UI options ---- disabled. This is useful when displaying a temporary ---- float where the text should not be edited. Disables ---- 'number', 'relativenumber', 'cursorline', 'cursorcolumn', ---- 'foldcolumn', 'spell' and 'list' options. 'signcolumn' ---- is changed to `auto` and 'colorcolumn' is cleared. ---- 'statuscolumn' is changed to empty. The end-of-buffer ---- region is hidden by setting `eob` flag of ---- 'fillchars' to a space char, and clearing the ---- `hl-EndOfBuffer` region in 'winhighlight'. --- - border: (`string|string[]`) (defaults to 'winborder' option) Window border. The string form --- accepts the same values as the 'winborder' option. The array form must have a length of --- eight or any divisor of eight, specifying the chars that form the border in a clockwise @@ -1839,30 +1786,83 @@ function vim.api.nvim_open_term(buffer, opts) end --- [ "", "", "", ">", "", "", "", "<" ] --- ``` --- By default, `hl-FloatBorder` highlight is used, which links to `hl-WinSeparator` when not ---- defined. Each border side can specify an optional highlight: +--- defined. Each border side can specify an optional highlight: --- ``` --- [ ["+", "MyCorner"], ["x", "MyBorder"] ]. --- ``` ---- - title: (optional) Title in window border, string or list. ---- List should consist of `[text, highlight]` tuples. ---- If string, or a tuple lacks a highlight, the default highlight group is `FloatTitle`. ---- - title_pos: Title position. Must be set with `title` option. ---- Value can be one of "left", "center", or "right". ---- Default is `"left"`. ---- - footer: (optional) Footer in window border, string or list. ---- List should consist of `[text, highlight]` tuples. ---- If string, or a tuple lacks a highlight, the default highlight group is `FloatFooter`. ---- - footer_pos: Footer position. Must be set with `footer` option. ---- Value can be one of "left", "center", or "right". ---- Default is `"left"`. ---- - noautocmd: Block all autocommands for the duration of the call. Cannot be changed by ---- `nvim_win_set_config()`. +--- - bufpos: Places float relative to buffer text (only when +--- relative="win"). Takes a tuple of zero-indexed `[line, column]`. +--- `row` and `col` if given are applied relative to this +--- position, else they default to: +--- - `row=1` and `col=0` if `anchor` is "NW" or "NE" +--- - `row=0` and `col=0` if `anchor` is "SW" or "SE" +--- (thus like a tooltip near the buffer text). +--- - col: Column position in units of screen cell width, may be fractional. +--- - external: GUI should display the window as an external +--- top-level window. Currently accepts no other positioning +--- configuration together with this. --- - fixed: If true when anchor is NW or SW, the float window --- would be kept fixed even if the window would be truncated. +--- - focusable: Enable focus by user actions (wincmds, mouse events). +--- Defaults to true. Non-focusable windows can be entered by +--- `nvim_set_current_win()`, or, when the `mouse` field is set to true, +--- by mouse events. See `focusable`. +--- - footer: (optional) Footer in window border, string or list. +--- List should consist of `[text, highlight]` tuples. +--- If string, or a tuple lacks a highlight, the default highlight group is `FloatFooter`. +--- - footer_pos: Footer position. Must be set with `footer` option. +--- Value can be one of "left", "center", or "right". +--- Default is `"left"`. +--- - height: Window height (in character cells). Minimum of 1. --- - hide: If true the floating window will be hidden and the cursor will be invisible when --- focused on it. ---- - vertical: Split vertically `:vertical`. +--- - mouse: Specify how this window interacts with mouse events. +--- Defaults to `focusable` value. +--- - If false, mouse events pass through this window. +--- - If true, mouse events interact with this window normally. +--- - noautocmd: Block all autocommands for the duration of the call. Cannot be changed by +--- `nvim_win_set_config()`. +--- - relative: Sets the window layout to "floating", placed at (row,col) +--- coordinates relative to: +--- - "cursor" Cursor position in current window. +--- - "editor" The global editor grid. +--- - "laststatus" 'laststatus' if present, or last row. +--- - "mouse" Mouse position. +--- - "tabline" Tabline if present, or first row. +--- - "win" Window given by the `win` field, or current window. +--- - row: Row position in units of "screen cell height", may be fractional. --- - split: Split direction: "left", "right", "above", "below". +--- - style: (optional) Configure the appearance of the window. Currently +--- only supports one value: +--- - "minimal" Nvim will display the window with many UI options +--- disabled. This is useful when displaying a temporary +--- float where the text should not be edited. Disables +--- 'number', 'relativenumber', 'cursorline', 'cursorcolumn', +--- 'foldcolumn', 'spell' and 'list' options. 'signcolumn' +--- is changed to `auto` and 'colorcolumn' is cleared. +--- 'statuscolumn' is changed to empty. The end-of-buffer +--- region is hidden by setting `eob` flag of +--- 'fillchars' to a space char, and clearing the +--- `hl-EndOfBuffer` region in 'winhighlight'. +--- - title: (optional) Title in window border, string or list. +--- List should consist of `[text, highlight]` tuples. +--- If string, or a tuple lacks a highlight, the default highlight group is `FloatTitle`. +--- - title_pos: Title position. Must be set with `title` option. +--- Value can be one of "left", "center", or "right". +--- Default is `"left"`. +--- - vertical: Split vertically `:vertical`. +--- - width: Window width (in character cells). Minimum of 1. +--- - win: `window-ID` window to split, or relative window when creating a +--- float (relative="win"). +--- - zindex: Stacking order. floats with higher `zindex` go on top on +--- floats with lower indices. Must be larger than zero. The +--- following screen elements have hard-coded z-indices: +--- - 100: insert completion popupmenu +--- - 200: message scrollback +--- - 250: cmdline completion popupmenu (when wildoptions+=pum) +--- The default value for floats are 50. In general, values below 100 are +--- recommended, unless there is a good reason to overshadow builtin +--- elements. --- - _cmdline_offset: (EXPERIMENTAL) When provided, anchor the `cmdline-completion` --- popupmenu to this window, with an offset in screen cell width. --- @return integer # |window-ID|, or 0 on error diff --git a/runtime/lua/vim/_meta/api_keysets.lua b/runtime/lua/vim/_meta/api_keysets.lua @@ -440,29 +440,29 @@ error('Cannot require a meta file') --- @field register? boolean --- @class vim.api.keyset.win_config ---- @field row? number ---- @field col? number ---- @field width? integer ---- @field height? integer ---- @field anchor? "NW"|"NE"|"SW"|"SE" ---- @field relative? "cursor"|"editor"|"laststatus"|"mouse"|"tabline"|"win" ---- @field split? "left"|"right"|"above"|"below" ---- @field win? integer ---- @field bufpos? integer[] --- @field external? boolean +--- @field fixed? boolean --- @field focusable? boolean +--- @field footer? any +--- @field footer_pos? "center"|"left"|"right" +--- @field hide? boolean +--- @field height? integer --- @field mouse? boolean +--- @field relative? "cursor"|"editor"|"laststatus"|"mouse"|"tabline"|"win" +--- @field row? number +--- @field style? "minimal" +--- @field noautocmd? boolean --- @field vertical? boolean +--- @field win? integer +--- @field width? integer --- @field zindex? integer +--- @field anchor? "NW"|"NE"|"SW"|"SE" --- @field border? string[]|"none"|"single"|"double"|"rounded"|"solid"|"shadow" +--- @field bufpos? integer[] +--- @field col? number +--- @field split? "left"|"right"|"above"|"below" --- @field title? any --- @field title_pos? "center"|"left"|"right" ---- @field footer? any ---- @field footer_pos? "center"|"left"|"right" ---- @field style? "minimal" ---- @field noautocmd? boolean ---- @field fixed? boolean ---- @field hide? boolean --- @field _cmdline_offset? integer --- @class vim.api.keyset.win_text_height diff --git a/src/nvim/api/keysets_defs.h b/src/nvim/api/keysets_defs.h @@ -116,29 +116,29 @@ typedef struct { typedef struct { OptionalKeys is_set__win_config_; - Float row; - Float col; - Integer width; - Integer height; - Enum("NW", "NE", "SW", "SE") anchor; - Enum("cursor", "editor", "laststatus", "mouse", "tabline", "win") relative; - Enum("left", "right", "above", "below") split; - Window win; - ArrayOf(Integer) bufpos; Boolean external; + Boolean fixed; Boolean focusable; + Object footer; + Enum("center", "left", "right") footer_pos; + Boolean hide; + Integer height; Boolean mouse; + Enum("cursor", "editor", "laststatus", "mouse", "tabline", "win") relative; + Float row; + Enum("minimal") style; + Boolean noautocmd; Boolean vertical; + Window win; + Integer width; Integer zindex; + Enum("NW", "NE", "SW", "SE") anchor; Union(ArrayOf(String), Enum("none", "single", "double", "rounded", "solid", "shadow")) border; + ArrayOf(Integer) bufpos; + Float col; + Enum("left", "right", "above", "below") split; Object title; Enum("center", "left", "right") title_pos; - Object footer; - Enum("center", "left", "right") footer_pos; - Enum("minimal") style; - Boolean noautocmd; - Boolean fixed; - Boolean hide; Integer _cmdline_offset; } Dict(win_config); diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c @@ -94,64 +94,11 @@ /// @param buffer Buffer to display, or 0 for current buffer /// @param enter Enter the window (make it the current window) /// @param config Map defining the window configuration. Keys: -/// - relative: Sets the window layout to "floating", placed at (row,col) -/// coordinates relative to: -/// - "cursor" Cursor position in current window. -/// - "editor" The global editor grid. -/// - "laststatus" 'laststatus' if present, or last row. -/// - "mouse" Mouse position. -/// - "tabline" Tabline if present, or first row. -/// - "win" Window given by the `win` field, or current window. -/// - win: |window-ID| window to split, or relative window when creating a -/// float (relative="win"). /// - anchor: Decides which corner of the float to place at (row,col): /// - "NW" northwest (default) /// - "NE" northeast /// - "SW" southwest /// - "SE" southeast -/// - width: Window width (in character cells). Minimum of 1. -/// - height: Window height (in character cells). Minimum of 1. -/// - bufpos: Places float relative to buffer text (only when -/// relative="win"). Takes a tuple of zero-indexed `[line, column]`. -/// `row` and `col` if given are applied relative to this -/// position, else they default to: -/// - `row=1` and `col=0` if `anchor` is "NW" or "NE" -/// - `row=0` and `col=0` if `anchor` is "SW" or "SE" -/// (thus like a tooltip near the buffer text). -/// - row: Row position in units of "screen cell height", may be fractional. -/// - col: Column position in units of screen cell width, may be fractional. -/// - focusable: Enable focus by user actions (wincmds, mouse events). -/// Defaults to true. Non-focusable windows can be entered by -/// |nvim_set_current_win()|, or, when the `mouse` field is set to true, -/// by mouse events. See |focusable|. -/// - mouse: Specify how this window interacts with mouse events. -/// Defaults to `focusable` value. -/// - If false, mouse events pass through this window. -/// - If true, mouse events interact with this window normally. -/// - external: GUI should display the window as an external -/// top-level window. Currently accepts no other positioning -/// configuration together with this. -/// - zindex: Stacking order. floats with higher `zindex` go on top on -/// floats with lower indices. Must be larger than zero. The -/// following screen elements have hard-coded z-indices: -/// - 100: insert completion popupmenu -/// - 200: message scrollback -/// - 250: cmdline completion popupmenu (when wildoptions+=pum) -/// The default value for floats are 50. In general, values below 100 are -/// recommended, unless there is a good reason to overshadow builtin -/// elements. -/// - style: (optional) Configure the appearance of the window. Currently -/// only supports one value: -/// - "minimal" Nvim will display the window with many UI options -/// disabled. This is useful when displaying a temporary -/// float where the text should not be edited. Disables -/// 'number', 'relativenumber', 'cursorline', 'cursorcolumn', -/// 'foldcolumn', 'spell' and 'list' options. 'signcolumn' -/// is changed to `auto` and 'colorcolumn' is cleared. -/// 'statuscolumn' is changed to empty. The end-of-buffer -/// region is hidden by setting `eob` flag of -/// 'fillchars' to a space char, and clearing the -/// |hl-EndOfBuffer| region in 'winhighlight'. /// - border: (`string|string[]`) (defaults to 'winborder' option) Window border. The string form /// accepts the same values as the 'winborder' option. The array form must have a length of /// eight or any divisor of eight, specifying the chars that form the border in a clockwise @@ -175,30 +122,83 @@ /// [ "", "", "", ">", "", "", "", "<" ] /// ``` /// By default, |hl-FloatBorder| highlight is used, which links to |hl-WinSeparator| when not -/// defined. Each border side can specify an optional highlight: +/// defined. Each border side can specify an optional highlight: /// ``` /// [ ["+", "MyCorner"], ["x", "MyBorder"] ]. /// ``` -/// - title: (optional) Title in window border, string or list. -/// List should consist of `[text, highlight]` tuples. -/// If string, or a tuple lacks a highlight, the default highlight group is `FloatTitle`. -/// - title_pos: Title position. Must be set with `title` option. -/// Value can be one of "left", "center", or "right". -/// Default is `"left"`. -/// - footer: (optional) Footer in window border, string or list. -/// List should consist of `[text, highlight]` tuples. -/// If string, or a tuple lacks a highlight, the default highlight group is `FloatFooter`. -/// - footer_pos: Footer position. Must be set with `footer` option. -/// Value can be one of "left", "center", or "right". -/// Default is `"left"`. -/// - noautocmd: Block all autocommands for the duration of the call. Cannot be changed by -/// |nvim_win_set_config()|. +/// - bufpos: Places float relative to buffer text (only when +/// relative="win"). Takes a tuple of zero-indexed `[line, column]`. +/// `row` and `col` if given are applied relative to this +/// position, else they default to: +/// - `row=1` and `col=0` if `anchor` is "NW" or "NE" +/// - `row=0` and `col=0` if `anchor` is "SW" or "SE" +/// (thus like a tooltip near the buffer text). +/// - col: Column position in units of screen cell width, may be fractional. +/// - external: GUI should display the window as an external +/// top-level window. Currently accepts no other positioning +/// configuration together with this. /// - fixed: If true when anchor is NW or SW, the float window /// would be kept fixed even if the window would be truncated. +/// - focusable: Enable focus by user actions (wincmds, mouse events). +/// Defaults to true. Non-focusable windows can be entered by +/// |nvim_set_current_win()|, or, when the `mouse` field is set to true, +/// by mouse events. See |focusable|. +/// - footer: (optional) Footer in window border, string or list. +/// List should consist of `[text, highlight]` tuples. +/// If string, or a tuple lacks a highlight, the default highlight group is `FloatFooter`. +/// - footer_pos: Footer position. Must be set with `footer` option. +/// Value can be one of "left", "center", or "right". +/// Default is `"left"`. +/// - height: Window height (in character cells). Minimum of 1. /// - hide: If true the floating window will be hidden and the cursor will be invisible when /// focused on it. -/// - vertical: Split vertically |:vertical|. +/// - mouse: Specify how this window interacts with mouse events. +/// Defaults to `focusable` value. +/// - If false, mouse events pass through this window. +/// - If true, mouse events interact with this window normally. +/// - noautocmd: Block all autocommands for the duration of the call. Cannot be changed by +/// |nvim_win_set_config()|. +/// - relative: Sets the window layout to "floating", placed at (row,col) +/// coordinates relative to: +/// - "cursor" Cursor position in current window. +/// - "editor" The global editor grid. +/// - "laststatus" 'laststatus' if present, or last row. +/// - "mouse" Mouse position. +/// - "tabline" Tabline if present, or first row. +/// - "win" Window given by the `win` field, or current window. +/// - row: Row position in units of "screen cell height", may be fractional. /// - split: Split direction: "left", "right", "above", "below". +/// - style: (optional) Configure the appearance of the window. Currently +/// only supports one value: +/// - "minimal" Nvim will display the window with many UI options +/// disabled. This is useful when displaying a temporary +/// float where the text should not be edited. Disables +/// 'number', 'relativenumber', 'cursorline', 'cursorcolumn', +/// 'foldcolumn', 'spell' and 'list' options. 'signcolumn' +/// is changed to `auto` and 'colorcolumn' is cleared. +/// 'statuscolumn' is changed to empty. The end-of-buffer +/// region is hidden by setting `eob` flag of +/// 'fillchars' to a space char, and clearing the +/// |hl-EndOfBuffer| region in 'winhighlight'. +/// - title: (optional) Title in window border, string or list. +/// List should consist of `[text, highlight]` tuples. +/// If string, or a tuple lacks a highlight, the default highlight group is `FloatTitle`. +/// - title_pos: Title position. Must be set with `title` option. +/// Value can be one of "left", "center", or "right". +/// Default is `"left"`. +/// - vertical: Split vertically |:vertical|. +/// - width: Window width (in character cells). Minimum of 1. +/// - win: |window-ID| window to split, or relative window when creating a +/// float (relative="win"). +/// - zindex: Stacking order. floats with higher `zindex` go on top on +/// floats with lower indices. Must be larger than zero. The +/// following screen elements have hard-coded z-indices: +/// - 100: insert completion popupmenu +/// - 200: message scrollback +/// - 250: cmdline completion popupmenu (when wildoptions+=pum) +/// The default value for floats are 50. In general, values below 100 are +/// recommended, unless there is a good reason to overshadow builtin +/// elements. /// - _cmdline_offset: (EXPERIMENTAL) When provided, anchor the |cmdline-completion| /// popupmenu to this window, with an offset in screen cell width. /// diff --git a/src/nvim/option.c b/src/nvim/option.c @@ -111,6 +111,7 @@ #include "nvim/undo_defs.h" #include "nvim/vim_defs.h" #include "nvim/window.h" +#include "nvim/winfloat.h" #ifdef BACKSLASH_IN_FILENAME # include "nvim/arglist.h" diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c @@ -54,6 +54,7 @@ #include "nvim/types_defs.h" #include "nvim/vim_defs.h" #include "nvim/window.h" +#include "nvim/winfloat.h" #include "optionstr.c.generated.h" @@ -1898,9 +1899,10 @@ static const char *did_set_statustabline_rulerformat(optset_T *args, bool rulerf } const char *errmsg = NULL; char *s = *varp; + bool is_stl = args->os_idx == kOptStatusline; // reset statusline to default when setting global option and empty string is being set - if (args->os_idx == kOptStatusline + if (is_stl && ((args->os_flags & OPT_GLOBAL) || !(args->os_flags & OPT_LOCAL)) && s[0] == NUL) { xfree(*varp); @@ -1908,6 +1910,11 @@ static const char *did_set_statustabline_rulerformat(optset_T *args, bool rulerf s = *varp; } + // handle floating window statusline changes + if (is_stl && win && win->w_floating) { + win_config_float(win, win->w_config); + } + if (rulerformat && *s == '%') { // set ru_wid if 'ruf' starts with "%99(" if (*++s == '-') { // ignore a '-' diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c @@ -76,9 +76,14 @@ void win_redr_status(win_T *wp) || (wild_menu_showing != 0 && !ui_has(kUIWildmenu))) { return; } + wp->w_redr_status = false; + + if (wp->w_floating && is_stl_global) { + return; + } + busy = true; - wp->w_redr_status = false; if (wp->w_status_height == 0 && !(is_stl_global && wp == curwin)) { // no status line, either global statusline is enabled or the window is a last window redraw_cmdline = true; @@ -229,7 +234,7 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler, bool u StlClickRecord *tabtab; bool is_stl_global = global_stl_height() > 0; - ScreenGrid *grid = &default_grid; + ScreenGrid *grid = wp && wp->w_floating ? &wp->w_grid_alloc : &default_grid; // There is a tiny chance that this gets called recursively: When // redrawing a status line triggers redrawing the ruler or tabline. @@ -269,10 +274,16 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler, bool u wp->w_winbar_click_defs = stl_alloc_click_defs(wp->w_winbar_click_defs, maxwidth, &wp->w_winbar_click_defs_size); } else { - row = is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp); - fillchar = fillchar_status(&group, wp); const bool in_status_line = wp->w_status_height != 0 || is_stl_global; - maxwidth = in_status_line && !is_stl_global ? wp->w_width : Columns; + if (wp->w_floating && !draw_ruler) { + row = wp->w_winrow_off + wp->w_view_height; + col = wp->w_wincol_off; + maxwidth = wp->w_view_width; + } else { + row = is_stl_global ? (Rows - (int)p_ch - 1) : W_ENDROW(wp); + maxwidth = in_status_line && !is_stl_global ? wp->w_width : Columns; + } + fillchar = fillchar_status(&group, wp); stl_clear_click_defs(wp->w_status_click_defs, wp->w_status_click_defs_size); wp->w_status_click_defs = stl_alloc_click_defs(wp->w_status_click_defs, maxwidth, &wp->w_status_click_defs_size); @@ -310,7 +321,7 @@ static void win_redr_custom(win_T *wp, bool draw_winbar, bool draw_ruler, bool u } attr = win_hl_attr(wp, (int)group); - if (in_status_line && !is_stl_global) { + if (!wp->w_floating && in_status_line && !is_stl_global) { col += wp->w_wincol; } } diff --git a/src/nvim/window.c b/src/nvim/window.c @@ -4542,6 +4542,7 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, bool trigger_enter_a prevwin = next_prevwin; last_status(false); // status line may appear or disappear + win_float_update_statusline(); win_comp_pos(); // recompute w_winrow for all windows diff_need_scrollbind = true; @@ -6787,7 +6788,9 @@ void win_set_inner_size(win_T *wp, bool valid_cursor) terminal_check_size(wp->w_buffer->terminal); } - wp->w_height_outer = (wp->w_view_height + win_border_height(wp) + wp->w_winbar_height); + int float_stl_height = wp->w_floating && wp->w_status_height ? STATUS_HEIGHT : 0; + wp->w_height_outer = (wp->w_view_height + win_border_height(wp) + wp->w_winbar_height + + float_stl_height); wp->w_width_outer = (wp->w_view_width + win_border_width(wp)); wp->w_winrow_off = wp->w_border_adj[0] + wp->w_winbar_height; wp->w_wincol_off = wp->w_border_adj[3]; @@ -6907,13 +6910,13 @@ void last_status(bool morewin) } // Remove status line from window, replacing it with a horizontal separator if needed. -static void win_remove_status_line(win_T *wp, bool add_hsep) +void win_remove_status_line(win_T *wp, bool add_hsep) { wp->w_status_height = 0; if (add_hsep) { wp->w_hsep_height = 1; } else { - win_new_height(wp, wp->w_height + STATUS_HEIGHT); + win_new_height(wp, (wp->w_floating ? wp->w_view_height : wp->w_height) + STATUS_HEIGHT); } comp_col(); diff --git a/src/nvim/winfloat.c b/src/nvim/winfloat.c @@ -104,7 +104,7 @@ win_T *win_new_float(win_T *wp, bool last, WinConfig fconfig, Error *err) win_append(lastwin_nofloating(), wp, NULL); } wp->w_floating = true; - wp->w_status_height = 0; + wp->w_status_height = *wp->w_p_stl != NUL ? STATUS_HEIGHT : 0; wp->w_winbar_height = 0; wp->w_hsep_height = 0; wp->w_vsep_width = 0; @@ -166,6 +166,15 @@ void win_set_minimal_style(win_T *wp) free_string_option(wp->w_p_stc); wp->w_p_stc = xstrdup(""); } + + // statusline: cleared (for floating windows) + if (wp->w_floating && wp->w_p_stl != NULL && *wp->w_p_stl != NUL) { + free_string_option(wp->w_p_stl); + wp->w_p_stl = xstrdup(""); + if (wp->w_status_height > 0) { + win_config_float(wp, wp->w_config); + } + } } int win_border_height(win_T *wp) @@ -180,6 +189,14 @@ int win_border_width(win_T *wp) void win_config_float(win_T *wp, WinConfig fconfig) { + // Process statusline changes before applying new height from config + bool show_stl = *wp->w_p_stl != NUL; + if (wp->w_status_height && !show_stl) { + win_remove_status_line(wp, false); + } else if (wp->w_status_height == 0 && show_stl) { + wp->w_status_height = STATUS_HEIGHT; + } + wp->w_width = MAX(fconfig.width, 1); wp->w_height = MAX(fconfig.height, 1); @@ -225,7 +242,7 @@ void win_config_float(win_T *wp, WinConfig fconfig) win_set_inner_size(wp, true); set_must_redraw(UPD_VALID); - + wp->w_redr_status = wp->w_status_height; wp->w_pos_changed = true; if (change_external || change_border) { wp->w_hl_needs_update = true; @@ -308,6 +325,17 @@ void win_check_anchored_floats(win_T *win) } } +void win_float_update_statusline(void) +{ + for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) { + bool has_status = wp->w_status_height > 0; + bool should_show = *wp->w_p_stl != NUL; + if (should_show != has_status) { + win_config_float(wp, wp->w_config); + } + } +} + void win_float_anchor_laststatus(void) { FOR_ALL_WINDOWS_IN_TAB(win, curtab) { diff --git a/test/functional/ui/statusline_spec.lua b/test/functional/ui/statusline_spec.lua @@ -892,3 +892,109 @@ describe('default statusline', function() ]]) end) end) + +describe("'statusline' in floatwin", function() + local screen + before_each(function() + clear() + screen = Screen.new(30, 20) + screen:add_extra_attr_ids({ + [101] = { foreground = Screen.colors.Magenta1, bold = true }, + [102] = { + foreground = Screen.colors.Magenta1, + underline = true, + background = Screen.colors.LightGray, + bold = true, + }, + }) + end) + + it('controlled by ":setlocal statusline" and "style"', function() + local buf = api.nvim_create_buf(false, false) + api.nvim_buf_set_lines(buf, 0, -1, false, { '1', '2', '3', '4' }) + local cfg = { + relative = 'editor', + row = 1, + col = 1, + height = 4, + width = 10, + border = 'single', + } + local win = api.nvim_open_win(buf, true, cfg) + local set_stl = [[setlocal stl=%f\ %m]] + command('set laststatus=2') + command(set_stl) + local has_stl = [[ + | + {1:~}┌──────────┐{1: }| + {1:~}│{4:^1 }│{1: }| + {1:~}│{4:2 }│{1: }| + {1:~}│{4:3 }│{1: }| + {1:~}│{4:4 }│{1: }| + {1:~}│{3:<Name] [+]}│{1: }| + {1:~}└──────────┘{1: }| + {1:~ }|*10 + {2:[No Name] }| + | + ]] + screen:expect(has_stl) + + -- setting the style will clear the statusline expression for floating windows + api.nvim_win_set_config(win, { style = 'minimal' }) + local without_stl = [[ + | + {1:~}┌──────────┐{1: }| + {1:~}│{4:^1 }│{1: }| + {1:~}│{4:2 }│{1: }| + {1:~}│{4:3 }│{1: }| + {1:~}│{4:4 }│{1: }| + {1:~}└──────────┘{1: }| + {1:~ }|*11 + {2:[No Name] }| + | + ]] + screen:expect(without_stl) + + -- no statusline is displayed because the statusline option was cleared + api.nvim_win_set_config(win, cfg) + screen:expect(without_stl) + + -- displayed after the option is reset + command(set_stl) + screen:expect(has_stl) + + -- Show in a new window in a new tab, then return to the previous tab; + -- remove the statusline of the new window, When re-entering this new tab, + -- the statusline of the new window is cleared + command('tabnew') + local win2 = api.nvim_open_win(buf, false, cfg) + command(set_stl) + screen:expect([[ + {24: }{102:2}{24:+ [No Name] }{5: }{101:2}{5:+ [No Name] }{2: }{24:X}| + ^ ┌──────────┐ | + {1:~}│{4:1 }│{1: }| + {1:~}│{4:2 }│{1: }| + {1:~}│{4:3 }│{1: }| + {1:~}│{4:4 }│{1: }| + {1:~}└──────────┘{1: }| + {1:~ }|*11 + {3:[No Name] }| + | + ]]) + command('tabfirst') + api.nvim_win_set_config(win2, { style = 'minimal' }) + command('tabnext') + screen:expect([[ + {24: }{102:2}{24:+ [No Name] }{5: }{101:2}{5:+ [No Name] }{2: }{24:X}| + ^ ┌──────────┐ | + {1:~}│{4:1 }│{1: }| + {1:~}│{4:2 }│{1: }| + {1:~}│{4:3 }│{1: }| + {1:~}│{4:4 }│{1: }| + {1:~}└──────────┘{1: }| + {1:~ }|*11 + {3:[No Name] }| + | + ]]) + end) +end)