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:
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)