mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-03 17:24:29 +00:00 
			
		
		
		
	feat(lsp): improve control over placement of floating windows (#24494)
This commit is contained in:
		@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user