feat(lsp): improve control over placement of floating windows (#24494)

This commit is contained in:
Grace Petryk 2023-09-10 01:02:23 -07:00 committed by GitHub
parent bb38c066a9
commit 5e3cf9fb4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 136 additions and 8 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

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