Signature doesn't show up on a JS file right after the autocompletion is accepted
Make sure you have done the following
- [x] Updated to the latest version of
blink.cmp - [x] Searched for existing issues and documentation (try
<C-k>on https://cmp.saghen.dev)
Bug Description
I just migrated to blink.nvim, thanks for the project, it's great!
I was trying the signature feature and encountered and interesting behavior on JS files. When I'm on a lua file, the signature shows up correctly after I accept and autocomplete suggestion. But when I'm on a JS file (with the ts_ls lsp), the signature help window doesn't appear directly when I accept an autocomplete suggestion.
I'm not so sure why it's happening. But it seems like it's starting to work either:
- When I type a trigger character like
,. - When I delete the parentheses and type them back again.
I also captured a screen recording to show the current behavior I'm seeing. This is a simple test.js file in an empty directory that I added for this test:
https://github.com/user-attachments/assets/2d37c038-f950-4a54-9261-4ad235f9ecc6
As you can see, on both cases, when I accept the suggestion, it doesn't show me anything.
I did some printf debugging, and noticed that in Lua, we are triggering this through this if branch: https://github.com/Saghen/blink.cmp/blob/f2e4f6aae833c5c2866d203666910005363779d7/lua/blink/cmp/signature/trigger.lua#L76-L77
But in JS, we are not entering inside of this if branch because trigger.context is nil. I'm not so sure yet why this is the case though. I'm not familiar with the code.
If you have any pointers into how to debug this issue, I'm happy to help.
Also, here's a repro.lua file for this one:
-- Run with `nvim -u repro.lua`
vim.env.LAZY_STDPATH = '.repro'
load(vim.fn.system('curl -s https://raw.githubusercontent.com/folke/lazy.nvim/main/bootstrap.lua'))()
---@diagnostic disable-next-line: missing-fields
require('lazy.minit').repro({
spec = {
{
'saghen/blink.cmp',
-- please test on `main` if possible
-- otherwise, remove this line and set `version = '*'`
build = 'cargo build --release',
opts = {
signature = { enabled = true },
},
},
{
'neovim/nvim-lspconfig',
opts = {
servers = {
ts_ls = {},
},
},
config = function(_, opts)
local lspconfig = require('lspconfig')
for server, config in pairs(opts.servers) do
-- passing config.capabilities to blink.cmp merges with the capabilities in your
-- `opts[server].capabilities, if you've defined it
config.capabilities = require('blink.cmp').get_lsp_capabilities()
lspconfig[server].setup(config)
end
end,
},
},
})
typescript language server needs to be installed for this. You can do it with npm install -g typescript typescript-language-server.
Relevant configuration
signature = { enabled = true }
neovim version
v0.11.0
blink.cmp version
Current main branch, f2e4f6aae833c5c2866d203666910005363779d7
Ah, I was putting some printf more to see if there are any differences. And I saw that when I accept the suggestion on Lua, this functions gets called: https://github.com/Saghen/blink.cmp/blob/f2e4f6aae833c5c2866d203666910005363779d7/lua/blink/cmp/init.lua#L268-L274
But in a JS file, it doesn't get called. I think that call comes from there:
https://github.com/Saghen/blink.cmp/blob/f2e4f6aae833c5c2866d203666910005363779d7/lua/blink/cmp/sources/lsp/commands.lua#L7
Does it mean that editor.action.triggerParameterHints is not supported by the typescript lsp?
Had some time to look into it a bit more and noticed that show_if_on_trigger_character is getting called from here:
https://github.com/Saghen/blink.cmp/blob/f2e4f6aae833c5c2866d203666910005363779d7/lua/blink/cmp/completion/accept/init.lua#L114
But checked the char_under_cursor inside of that function and saw that the character was ) on the JS code I showed above.
I tried some other languages and I was left more confused. I tried a simple rust code like this:
fn hello(a: i32, b: f32) {}
fn main() {
hello(a, b); // <-- auto completing this by writing `he` and accepting it.
}
(After the autocomplete rust_analyzer puts the parameters in there directly too) . The char_under_cursor was a space character, because vim.api.nvim_win_get_cursor(0)[2] was returning 4 which corresponds to the indentation right before the h. I'm not 100% sure why we are getting weird positions like this.
One potential explanation might be that maybe blink is executing this map function very early, while neovim is not done pasting the changes and/or updating the cursor position. So we are trying to read an intermediate cursor position that will be updated on the next render?
Yep, I can verify that it gets fixed when I wrap these lines with vim.defer_fn. Here's the hacky code that fixes it:
...
:map(function()
vim.defer_fn(function()
require('blink.cmp.completion.trigger').show_if_on_trigger_character({ is_accept = true })
require('blink.cmp.signature.trigger').show_if_on_trigger_character()
callback()
end, 10)
end)
I also tried wrapping it with a vim.schedule call, but it didn't work. Similarly vim.defer_fn(function() .. end, 0) doesn't work (with a zero timeout). So I assume it takes some time to update these and not updated directly on the next render.
The code I shown here is not great because we have a :catch below, and vim.defer_fn will walkaround that. Not so sure what the best way for this is.