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

Brackets added to JS class member variable in certain contexts

Open fin-w opened this issue 5 months ago • 5 comments

EDIT: Still exists in v1.5.0

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

As you can see in the video, the class member variable has brackets added in certain contexts.

  • It only adds them to the second element in the array in my testing, but even then only after deleting a certain number of characters.
  • Blink doesn't add brackets outside the array (as shown in the video).
  • It does not add them in the FIRST use of the variable in the array I'm editing (not shown in video).
  • Nor when you delete the func2() call that follows as the second arg of func1 (i.e. delete the second arg of func1 so it only accepts an array)

I'm using the Typescript LS (https://github.com/fin-w/.config-nvim/blob/main/lua/config/lspconfig.lua#L41) I can trigger this on the latest nightly Neovim build, and v11.2, on separate machines that use the same Neovim config.

The code required to replicate this is below, I've made it as minimal as I can though probably it could be even simpler and still trigger the problem:

class EmptyClass { };
function func1(a: string[], b: EmptyClass) { return a[0] };
function func2(a: EmptyClass) { return a; }

export class MyClass {
    private runningCounter = 0;

    myClassFunc(myClassFuncVar: EmptyClass): string {
        let result = func1(["" + this.runningCounter, "" + this.runningCounter], func2(myClassFuncVar));
        return result;
    }
}

The video of the problem is below:

https://github.com/user-attachments/assets/422bdefe-de30-4291-9036-fb5e128786f3

Relevant configuration

https://github.com/fin-w/.config-nvim/blob/main/lua/config/blink.lua

neovim version

NVIM v0.12.0-dev-2031287e93
Build type: RelWithDebInfo
LuaJIT 2.1.1744317938

blink.cmp version

v1.4.1

fin-w avatar Jul 08 '25 17:07 fin-w

Do you mind retesting with vtsls instead of ts_ls? The official typescript language server infamously behaves poorly so want to rule that out

saghen avatar Jul 08 '25 19:07 saghen

I get precisely the same behaviour with vtsls so it looks like it's not a problem with the language server.

fin-w avatar Jul 08 '25 20:07 fin-w

Note I just tried using tsgo and couldn't reproduce the issue.

Both ts_ls and vstls are built around tsserver, so it might be a problem with the LSP after all?

soifou avatar Jul 24 '25 16:07 soifou

That could be because tsgo doesn't support semantic tokens yet, which is how these brackets are getting added. Though, I have a feeling it's an LSP issue regardless

saghen avatar Jul 24 '25 17:07 saghen

That could be because tsgo doesn't support semantic tokens yet

Ah good point, this is indeed not yet implemented.

I looked briefly at the code, and it 'works' using a defer. Isn't it because the highlighter:send_request() call above is debounced, which might add a slight delay before the updated tokens are received?

diff --git a/lua/blink/cmp/completion/brackets/semantic.lua b/lua/blink/cmp/completion/brackets/semantic.lua
index 9cf1769..5f14ac8 100644
--- a/lua/blink/cmp/completion/brackets/semantic.lua
+++ b/lua/blink/cmp/completion/brackets/semantic.lua
@@ -96,14 +96,19 @@ function semantic.add_brackets_via_semantic_token(ctx, filetype, item)
     -- semantic tokens debounced, so manually request a refresh to avoid latency
     highlighter:send_request()

-    -- first check if a semantic token already exists at the current cursor position
-    -- we get the token 1 character before the cursor (`bar|` would check `r`)
-    local tokens = vim.lsp.semantic_tokens.get_at_pos(0, cursor[1] - 1, cursor[2] - 1)
-    if tokens ~= nil then semantic.process_request(tokens) end
-    if semantic.request == nil then
+    -- Delay execution by 10ms
+    vim.defer_fn(function()
+      -- first check if a semantic token already exists at the current cursor position
+      -- we get the token 1 character before the cursor (`bar|` would check `r`)
+      local tokens = vim.lsp.semantic_tokens.get_at_pos(0, cursor[1] - 1, cursor[2] - 1)
+      if tokens ~= nil and #tokens > 0 then
+        vim.notify(tokens[1].type)
+        semantic.process_request(tokens)
+      end
+
       -- a matching token exists, and brackets were added
-      return resolve(true)
-    end
+      if semantic.request == nil then return resolve(true) end
+    end, 10)

     -- listen for LspTokenUpdate events until timeout
     semantic.timer:start(config.semantic_token_resolution.timeout_ms, 0, semantic.finish_request)

soifou avatar Jul 24 '25 17:07 soifou