feat: Allow callbacks for the documentation window
Feature Description
It would be a nice feature if we could have callbacks to run for the documentation window.
Why?
-
I find the usage of
min/maxwidth & height quite problematic as I prefer to have them as % of the window size. So, a way to change them on the fly would be nice. -
It would be great for extending the plugin.
Using a markdown previewer inside the
LSP hover window.
Whoops, should've marked it as breaking. @mikavilpas the documentation.render function has been renamed to documentation.draw but I've maintained support for .render so users can upgrade your plugin without running into issues. You'll likely want to wait for a new release to go out before migrating
Added support for custom drawing of the documentation window. The min/max width seems like we could support the typical way, with a function: completion.documentation.min_width = function() return vim.o.columns / 123 end, but I haven't gotten around to that yet
I have finally managed to get it to work.
You can find the source code here.
[!NOTE] You have to use
defer_fnortimers, if you need to do anything with the window as it's not available duringdraw
Re-opening because I'd still like to enable dynamic min/max widths: https://github.com/Saghen/blink.cmp/issues/1113#issuecomment-2629139275
@Saghen, Shouldn't a simple eval function be enough for this?
---@param val table
---@param ignore string[] Ignore these keys(e.g. some function that needs to be run on something instead of returning a value).
---@param ... any
---@return table
local function eval (val, ignore, ...)
ignore = ignore or {};
local new_val = {};
for k, v in pairs(val) do
if type(v) ~= "function" or vim.list_contains(ignore, k) then
new_val[k] = v;
else
local can_eval, _v = pcall(v, ...);
if can_eval and _v ~= nil then
new_val[k] = _v;
end
end
end
return new_val;
end
Unfortunately that would lose type checking, and something like this doesn't seem to work:
--- @generic T
--- @generic R
--- @param func T | fun(...: R): T
--- @param ... R
--- @return T
function utils.eval(func, ...)
if type(func) == 'function' then
return func(...)
else
return func
end
end
---@param bar string
---@return string
local function foo(bar) return 'foo' end
local test = utils.eval(foo, 123) -- should be `string` but get `string|function`
local test2 = utils.eval('ast') -- should be and is `string`
that would lose type checking
Can't we simply use 2 type definition, 1 exposed to the user and 1 used internally.
---@class config.window
---@param X string | fun(): string
---@class config.window__static
---@param X string
And then use a local function where the values need to be used,
---@param config config.window
---@return config.window__static
local function parse_winconf (config)
return utils.eval(config);
end
This should solve conflicts with existing type definition.
Also, I think it would be simpler to assume everything inside window can be a function, instead of handling specific options differently.
I don't love having a fourth definition for all the configs with dynamic properties (we already have types_partial.lua and reference.md). If we can't figure out type inference for it, I think I'd rather do something like below so that we compute the value at the last possible moment:
local min = utils.eval(config.min, 'some', 'args')
--- @cast min number
This is essentially what we already do, but we don't have a helper for it
Sure. I would suggest not to do any direct function call for getting the value.
nvim-cmpdoes this and there were plenty of time when I got soft locked out of my editor since it kept calling the same function and creating new error messages. I would prefer if that isn't the case with blink.
Also, I would suggest making use of autocmds(specifically :h User) as in my opinion it's much easier to set up and allows users to run their own functions on specific events without you needing to manually handle them.
You simply call
nvim_exec_autocmds()and everything else should be handled by Neovim.
Which autocmds are missing as is? I ought to document them, but you can find them scattered in the codebase (second arg to event_emitter.new()): https://github.com/search?q=repo%3ASaghen%2Fblink.cmp%20event_emitter&type=code
there were plenty of time when I got soft locked out of my editor since it kept calling the same function and creating new error messages.
Sure, the utils.eval could pcall it and then only show the error once, defaulting to the default config value on failure
but you can find them scattered in the codebase
It appears that I didn't check throughly enough. Oh well, I guess you can just ignore my previous comment.