aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/lsp.txt18
-rw-r--r--runtime/doc/news.txt2
-rw-r--r--runtime/lua/vim/lsp/handlers.lua4
-rw-r--r--runtime/lua/vim/lsp/util.lua24
-rw-r--r--test/functional/plugin/lsp/utils_spec.lua96
5 files changed, 136 insertions, 8 deletions
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
index d2d7e9faac..1e57b85153 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -1580,7 +1580,8 @@ hover({_}, {result}, {ctx}, {config}) *vim.lsp.handlers.hover()*
• {config} (table) Configuration table.
• border: (default=nil)
• Add borders to the floating window
- • See |nvim_open_win()|
+ • See |vim.lsp.util.open_floating_preview()| for more
+ options.
*vim.lsp.handlers.signature_help()*
signature_help({_}, {result}, {ctx}, {config})
@@ -1599,7 +1600,8 @@ signature_help({_}, {result}, {ctx}, {config})
• {config} (table) Configuration table.
• border: (default=nil)
• Add borders to the floating window
- • See |nvim_open_win()|
+ • See |vim.lsp.util.open_floating_preview()| for more
+ options
==============================================================================
@@ -1791,6 +1793,13 @@ make_floating_popup_options({width}, {height}, {opts})
• focusable (string or table) override `focusable`
• zindex (string or table) override `zindex`, defaults to 50
• relative ("mouse"|"cursor") defaults to "cursor"
+ • anchor_bias ("auto"|"above"|"below") defaults to "auto"
+ • "auto": place window based on which side of the cursor
+ has more lines
+ • "above": place the window above the cursor unless there
+ are not enough lines to display the full window height.
+ • "below": place the window below the cursor unless there
+ are not enough lines to display the full window height.
Return: ~
(table) Options
@@ -1892,8 +1901,9 @@ open_floating_preview({contents}, {syntax}, {opts})
Parameters: ~
• {contents} (table) of lines to show in window
• {syntax} (string) of syntax to set for opened buffer
- • {opts} (table) with optional fields (additional keys are passed
- on to |nvim_open_win()|)
+ • {opts} (table) with optional fields (additional keys are filtered
+ with |vim.lsp.util.make_floating_popup_options()| before
+ they are passed on to |nvim_open_win()|)
• height: (integer) height of floating window
• width: (integer) width of floating window
• wrap: (boolean, default true) wrap long lines
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index 972f37f0e9..cd977a8b5f 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -120,6 +120,8 @@ The following new APIs and features were added.
indicator to see if a server supports a feature. Instead use
`client.supports_method(<method>)`. It considers both the dynamic
capabilities and static `server_capabilities`.
+ • Added a new `anchor_bias` option to |lsp-handlers| to aid in positioning of
+ floating windows.
• Treesitter
• Bundled parsers and queries (highlight, folds) for Markdown, Python, and
diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index a6b70ac911..81d4d6cceb 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -355,7 +355,7 @@ end
---@param config table Configuration table.
--- - border: (default=nil)
--- - Add borders to the floating window
---- - See |nvim_open_win()|
+--- - See |vim.lsp.util.open_floating_preview()| for more options.
function M.hover(_, result, ctx, config)
config = config or {}
config.focus_id = ctx.method
@@ -442,7 +442,7 @@ M[ms.textDocument_implementation] = location_handler
---@param config table Configuration table.
--- - border: (default=nil)
--- - Add borders to the floating window
---- - See |nvim_open_win()|
+--- - See |vim.lsp.util.open_floating_preview()| for more options
function M.signature_help(_, result, ctx, config)
config = config or {}
config.focus_id = ctx.method
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index a6d17afa1b..e76fd15612 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -1087,6 +1087,12 @@ end
--- - focusable (string or table) override `focusable`
--- - zindex (string or table) override `zindex`, defaults to 50
--- - relative ("mouse"|"cursor") defaults to "cursor"
+--- - anchor_bias ("auto"|"above"|"below") defaults to "auto"
+--- - "auto": place window based on which side of the cursor has more lines
+--- - "above": place the window above the cursor unless there are not enough lines
+--- to display the full window height.
+--- - "below": place the window below the cursor unless there are not enough lines
+--- to display the full window height.
---@return table Options
function M.make_floating_popup_options(width, height, opts)
validate({
@@ -1105,7 +1111,20 @@ function M.make_floating_popup_options(width, height, opts)
or vim.fn.winline() - 1
local lines_below = vim.fn.winheight(0) - lines_above
- if lines_above < lines_below then
+ local anchor_bias = opts.anchor_bias or 'auto'
+
+ local anchor_below
+
+ if anchor_bias == 'below' then
+ anchor_below = (lines_below > lines_above) or (height <= lines_below)
+ elseif anchor_bias == 'above' then
+ local anchor_above = (lines_above > lines_below) or (height <= lines_above)
+ anchor_below = not anchor_above
+ else
+ anchor_below = lines_below > lines_above
+ end
+
+ if anchor_below then
anchor = anchor .. 'N'
height = math.min(lines_below, height)
row = 1
@@ -1635,7 +1654,8 @@ end
---
---@param contents table of lines to show in window
---@param syntax string of syntax to set for opened buffer
----@param opts table with optional fields (additional keys are passed on to |nvim_open_win()|)
+---@param opts table with optional fields (additional keys are filtered with |vim.lsp.util.make_floating_popup_options()|
+--- before they are passed on to |nvim_open_win()|)
--- - height: (integer) height of floating window
--- - width: (integer) width of floating window
--- - wrap: (boolean, default true) wrap long lines
diff --git a/test/functional/plugin/lsp/utils_spec.lua b/test/functional/plugin/lsp/utils_spec.lua
index c91fffa90f..f544255d81 100644
--- a/test/functional/plugin/lsp/utils_spec.lua
+++ b/test/functional/plugin/lsp/utils_spec.lua
@@ -1,4 +1,6 @@
local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local feed = helpers.feed
local eq = helpers.eq
local exec_lua = helpers.exec_lua
@@ -85,4 +87,98 @@ describe('vim.lsp.util', function()
eq(expected, stylize_markdown(lines, opts))
end)
end)
+
+ describe("make_floating_popup_options", function ()
+
+ local function assert_anchor(anchor_bias, expected_anchor)
+ local opts = exec_lua([[
+ local args = { ... }
+ local anchor_bias = args[1]
+ return vim.lsp.util.make_floating_popup_options(30, 10, { anchor_bias = anchor_bias })
+ ]], anchor_bias)
+
+ eq(expected_anchor, string.sub(opts.anchor, 1, 1))
+ end
+
+ local screen
+ before_each(function ()
+ helpers.clear()
+ screen = Screen.new(80, 80)
+ screen:attach()
+ feed("79i<CR><Esc>") -- fill screen with empty lines
+ end)
+
+ describe('when on the first line it places window below', function ()
+ before_each(function ()
+ feed('gg')
+ end)
+
+ it('for anchor_bias = "auto"', function ()
+ assert_anchor('auto', 'N')
+ end)
+
+ it('for anchor_bias = "above"', function ()
+ assert_anchor('above', 'N')
+ end)
+
+ it('for anchor_bias = "below"', function ()
+ assert_anchor('below', 'N')
+ end)
+ end)
+
+ describe('when on the last line it places window above', function ()
+ before_each(function ()
+ feed('G')
+ end)
+
+ it('for anchor_bias = "auto"', function ()
+ assert_anchor('auto', 'S')
+ end)
+
+ it('for anchor_bias = "above"', function ()
+ assert_anchor('above', 'S')
+ end)
+
+ it('for anchor_bias = "below"', function ()
+ assert_anchor('below', 'S')
+ end)
+ end)
+
+ describe('with 20 lines above, 59 lines below', function ()
+ before_each(function ()
+ feed('gg20j')
+ end)
+
+ it('places window below for anchor_bias = "auto"', function ()
+ assert_anchor('auto', 'N')
+ end)
+
+ it('places window above for anchor_bias = "above"', function ()
+ assert_anchor('above', 'S')
+ end)
+
+ it('places window below for anchor_bias = "below"', function ()
+ assert_anchor('below', 'N')
+ end)
+ end)
+
+ describe('with 59 lines above, 20 lines below', function ()
+ before_each(function ()
+ feed('G20k')
+ end)
+
+ it('places window above for anchor_bias = "auto"', function ()
+ assert_anchor('auto', 'S')
+ end)
+
+ it('places window above for anchor_bias = "above"', function ()
+ assert_anchor('above', 'S')
+ end)
+
+ it('places window below for anchor_bias = "below"', function ()
+ assert_anchor('below', 'N')
+ end)
+ end)
+ end)
+
end)