blink.cmp icon indicating copy to clipboard operation
blink.cmp copied to clipboard

Signature doesn't show up on a JS file right after the autocompletion is accepted

Open canova opened this issue 8 months ago • 3 comments

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:

  1. When I type a trigger character like ,.
  2. 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

canova avatar Apr 26 '25 22:04 canova

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?

canova avatar Apr 26 '25 23:04 canova

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?

canova avatar Apr 27 '25 12:04 canova

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.

canova avatar Apr 27 '25 13:04 canova