aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorluukvbaal <luukvbaal@gmail.com>2025-01-14 14:02:46 +0100
committerGitHub <noreply@github.com>2025-01-14 05:02:46 -0800
commit25d8c3a5ad7e9c5668841e66540ebe34ceda73a7 (patch)
tree139f1495e3b947676e9f011c50dd6ad525347a01
parente8ddb7a46938f8843abc1c321cfd83cee2ba0020 (diff)
downloadrneovim-25d8c3a5ad7e9c5668841e66540ebe34ceda73a7.tar.gz
rneovim-25d8c3a5ad7e9c5668841e66540ebe34ceda73a7.tar.bz2
rneovim-25d8c3a5ad7e9c5668841e66540ebe34ceda73a7.zip
feat(api): nvim_open_win() relative to tabline and laststatus #32006
Problem: Anchoring a floating window to the tabline and laststatus is cumbersome; requiring autocommands and looping over all windows/tabpages. Solution: Add new "tabline" and "laststatus" options to the `relative` field of nvim_open_win() to place a window relative to.
-rw-r--r--runtime/doc/api.txt8
-rw-r--r--runtime/doc/news.txt1
-rw-r--r--runtime/lua/vim/_meta/api.lua10
-rw-r--r--src/nvim/api/win_config.c18
-rw-r--r--src/nvim/buffer_defs.h2
-rw-r--r--src/nvim/window.c6
-rw-r--r--src/nvim/winfloat.c9
-rw-r--r--test/functional/ui/float_spec.lua91
8 files changed, 133 insertions, 12 deletions
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index 572e5e4267..5a6361d45f 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -3150,11 +3150,13 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()*
• {config} Map defining the window configuration. Keys:
• relative: Sets the window layout to "floating", placed at
(row,col) coordinates relative to:
- • "editor" The global editor grid
+ • "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.
- • "cursor" Cursor position in current window.
- • "mouse" Mouse position
• 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
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index 89504ae244..e6a1adf15b 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -187,6 +187,7 @@ API
• |nvim__ns_set()| can set properties for a namespace
• |nvim_echo()| `err` field to print error messages and `chunks` accepts
highlight group IDs.
+• |nvim_open_win()| `relative` field can be set to "laststatus" and "tabline".
DEFAULTS
diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua
index 8930f131f6..670e867c1e 100644
--- a/runtime/lua/vim/_meta/api.lua
+++ b/runtime/lua/vim/_meta/api.lua
@@ -1754,10 +1754,12 @@ function vim.api.nvim_open_term(buffer, opts) end
--- @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:
---- - "editor" The global editor grid
---- - "win" Window given by the `win` field, or current window.
---- - "cursor" Cursor position in current window.
---- - "mouse" Mouse position
+--- - "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):
diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c
index 45c9e3f56c..225189a3f9 100644
--- a/src/nvim/api/win_config.c
+++ b/src/nvim/api/win_config.c
@@ -101,10 +101,12 @@
/// @param config Map defining the window configuration. Keys:
/// - relative: Sets the window layout to "floating", placed at (row,col)
/// coordinates relative to:
-/// - "editor" The global editor grid
-/// - "win" Window given by the `win` field, or current window.
-/// - "cursor" Cursor position in current window.
-/// - "mouse" Mouse position
+/// - "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):
@@ -699,7 +701,9 @@ Dict(win_config) nvim_win_get_config(Window window, Arena *arena, Error *err)
FUNC_API_SINCE(6)
{
/// Keep in sync with FloatRelative in buffer_defs.h
- static const char *const float_relative_str[] = { "editor", "win", "cursor", "mouse" };
+ static const char *const float_relative_str[] = {
+ "editor", "win", "cursor", "mouse", "tabline", "laststatus"
+ };
/// Keep in sync with WinSplit in buffer_defs.h
static const char *const win_split_str[] = { "left", "right", "above", "below" };
@@ -805,6 +809,10 @@ static bool parse_float_relative(String relative, FloatRelative *out)
*out = kFloatRelativeCursor;
} else if (striequal(str, "mouse")) {
*out = kFloatRelativeMouse;
+ } else if (striequal(str, "tabline")) {
+ *out = kFloatRelativeTabline;
+ } else if (striequal(str, "laststatus")) {
+ *out = kFloatRelativeLaststatus;
} else {
return false;
}
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 3fe44beab9..bffb29578f 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -900,6 +900,8 @@ typedef enum {
kFloatRelativeWindow = 1,
kFloatRelativeCursor = 2,
kFloatRelativeMouse = 3,
+ kFloatRelativeTabline = 4,
+ kFloatRelativeLaststatus = 5,
} FloatRelative;
/// Keep in sync with win_split_str[] in nvim_win_get_config() (api/win_config.c)
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 68f45ec5ba..1c0d8c1027 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -835,6 +835,10 @@ void ui_ext_win_position(win_T *wp, bool validate)
col += tcol - 1;
}
}
+ } else if (c.relative == kFloatRelativeLaststatus) {
+ row += Rows - (int)p_ch - last_stl_height(false);
+ } else if (c.relative == kFloatRelativeTabline) {
+ row += tabline_height();
}
bool resort = wp->w_grid_alloc.comp_index != 0
@@ -1066,6 +1070,7 @@ win_T *win_split_ins(int size, int flags, win_T *new_wp, int dir, frame_T *to_fl
return NULL;
}
need_status = STATUS_HEIGHT;
+ win_float_anchor_laststatus();
}
bool do_equal = false;
@@ -6803,6 +6808,7 @@ void last_status(bool morewin)
{
// Don't make a difference between horizontal or vertical split.
last_status_rec(topframe, last_stl_height(morewin) > 0, global_stl_height() > 0);
+ win_float_anchor_laststatus();
}
// Remove status line from window, replacing it with a horizontal separator if needed.
diff --git a/src/nvim/winfloat.c b/src/nvim/winfloat.c
index 3e791e2beb..d11b965dfc 100644
--- a/src/nvim/winfloat.c
+++ b/src/nvim/winfloat.c
@@ -307,6 +307,15 @@ void win_check_anchored_floats(win_T *win)
}
}
+void win_float_anchor_laststatus(void)
+{
+ FOR_ALL_WINDOWS_IN_TAB(win, curtab) {
+ if (win->w_config.relative == kFloatRelativeLaststatus) {
+ win->w_pos_changed = true;
+ }
+ }
+}
+
void win_reconfig_floats(void)
{
for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) {
diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua
index be0a9b80cf..ca26c46fc5 100644
--- a/test/functional/ui/float_spec.lua
+++ b/test/functional/ui/float_spec.lua
@@ -1012,6 +1012,97 @@ describe('float window', function()
end)
end)
+ it('placed relative to tabline and laststatus', function()
+ local screen = Screen.new(20, 10)
+ screen:add_extra_attr_ids({ [100] = { bold = true, foreground = Screen.colors.Magenta } })
+ command('set showtabline=1 laststatus=1')
+ api.nvim_open_win(0, false, {
+ relative = 'laststatus',
+ border = 'single',
+ anchor = 'SE',
+ width = 5,
+ height = 1,
+ row = 0,
+ col = 1000,
+ })
+ local tabwin = api.nvim_open_win(0, false, {
+ relative = 'tabline',
+ border = 'single',
+ width = 5,
+ height = 1,
+ row = 0,
+ col = 1000,
+ })
+ screen:expect([[
+ ^ {2:┌─────┐}|
+ {1:~ }{2:│}{4: }{2:│}|
+ {1:~ }{2:└─────┘}|
+ {1:~ }|*3
+ {1:~ }{2:┌─────┐}|
+ {1:~ }{2:│}{4: }{2:│}|
+ {1:~ }{2:└─────┘}|
+ |
+ ]])
+ command('tabnew | tabnext')
+ screen:expect([[
+ {5: }{100:3}{5: Name] }{24: No Name]X}|
+ ^ {2:┌─────┐}|
+ {1:~ }{2:│}{4: }{2:│}|
+ {1:~ }{2:└─────┘}|
+ {1:~ }|*2
+ {1:~ }{2:┌─────┐}|
+ {1:~ }{2:│}{4: }{2:│}|
+ {1:~ }{2:└─────┘}|
+ |
+ ]])
+ command('vsplit')
+ screen:expect([[
+ {5: }{100:4}{5: Name] }{24: No Name]X}|
+ ^ {2:┌─────┐}|
+ {1:~ }{2:│}{4: }{2:│}|
+ {1:~ }{2:└─────┘}|
+ {1:~ }{2:│}{1:~}|
+ {1:~ }{2:┌─────┐}|
+ {1:~ }{2:│}{4: }{2:│}|
+ {1:~ }{2:└─────┘}|
+ {3:[No Name] }{2:<}|
+ |
+ ]])
+ command('quit')
+ api.nvim_win_set_config(tabwin, {
+ relative = 'tabline',
+ border = 'single',
+ width = 5,
+ height = 1,
+ row = 1,
+ col = 0,
+ })
+ screen:expect([[
+ {5: }{100:3}{5: Name] }{24: No Name]X}|
+ ^ |
+ {2:┌─────┐}{1: }|
+ {2:│}{4: }{2:│}{1: }|
+ {2:└─────┘}{1: }|
+ {1:~ }|
+ {1:~ }{2:┌─────┐}|
+ {1:~ }{2:│}{4: }{2:│}|
+ {1:~ }{2:└─────┘}|
+ |
+ ]])
+ command('tabonly')
+ screen:expect([[
+ ^ |
+ {2:┌─────┐}{1: }|
+ {2:│}{4: }{2:│}{1: }|
+ {2:└─────┘}{1: }|
+ {1:~ }|*2
+ {1:~ }{2:┌─────┐}|
+ {1:~ }{2:│}{4: }{2:│}|
+ {1:~ }{2:└─────┘}|
+ |
+ ]])
+ end)
+
local function with_ext_multigrid(multigrid)
local screen, attrs
before_each(function()