Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

slow with vtsls for certain packages and moduleResolution in tsconfig #1115

Open
2 tasks done
Aqothy opened this issue Jan 29, 2025 · 22 comments
Open
2 tasks done

slow with vtsls for certain packages and moduleResolution in tsconfig #1115

Aqothy opened this issue Jan 29, 2025 · 22 comments
Labels
bug Something isn't working LSP Related to a specific LSP

Comments

@Aqothy
Copy link

Aqothy commented Jan 29, 2025

Make sure you have done the following

  • Updated to the latest version of blink.cmp
  • Searched for existing issues and documentation (try <C-k> on https://cmp.saghen.dev)

Bug Description

Blink cmp is working perfectly fine and fast on most of my projects (mostly web dev with vtsls), however I've noticed that for some of my projects, the completion can take up to multiple seconds to load or just not load at all and even causes neovim to lag. After comparing projects and their dependencies, I've found there to be 2 culprits of this. First is large ui libraries like react-icons and mui, but the weird thing is for react-icons, earlier versions work fine with blink, for example v4.12, but not the latest v5.3. Another thing is the moduleResolution in tsconfig, if the moduleResolution is "bundler" instead of "node" and there are these large ui libraries, blink also becomes extremely slow.

This problem doesn't happen with nvim cmp so I'm pretty sure it's not a lsp problem.

Relevant configuration

neovim version

v0.10.4

blink.cmp version

v0.11.0

@Aqothy Aqothy added the bug Something isn't working label Jan 29, 2025
@Saghen
Copy link
Owner

Saghen commented Feb 1, 2025

Do you mind providing a repro.lua with an example project so I can more easily replicate this?

@Saghen Saghen added the LSP Related to a specific LSP label Feb 1, 2025
@Aqothy
Copy link
Author

Aqothy commented Feb 1, 2025

Hi this is the repro lua I used, you would have to install vtsls globally, Install by npm install -g @vtsls/language-server, then run vtsls --stdio. Requires node >= 16 or do it with mason. I cant share an example project but I recreated the issue by creating a fresh next js project with typescript with all default options or react with vite should work too, just click enter through all the options, then installed react-icons latest version. If you go to a ts file or tsx file and test autocomplete, I did it by:
const test = [1,2,3,4,5]
test.map(whatever in here)

you can see that the completion is quite slow and sometimes even causes neovim to lag. Now a temporary fix I found was by going to tsconfig file in the next js project and changing the moduleResolution to "node" instead of "bundler", and now if you close neovim and reopen the project and test the autocomplete in the same file, the autocomplete is super fast and what is expected. Im pretty sure this not a lsp or react icon issue as the completion works fine with nvim cmp even without applying the temporary fixes I mentioned above. Anyways thanks for this awesome plugin, look forward to hearing back!

-- 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",
			version = "*",
			event = { "InsertEnter", "CmdLineEnter" },
			-- enabled = false,
			dependencies = {
				-- enable if there is any cmp sources that you want blink to use from nvim cmp
				-- {
				--     'saghen/blink.compat',
				--     version = '*',
				--     lazy = true,
				--     opts = {
				--         impersonate_nvim_cmp = true,
				--     }
				-- },
			},
			opts_extend = { "sources.default" },
			opts = {
				keymap = {
					preset = "none",
					["<C-y>"] = { "select_and_accept" },
					["<C-b>"] = { "scroll_documentation_up", "fallback" },
					["<C-f>"] = { "scroll_documentation_down", "fallback" },
					["<C-h>"] = { "hide", "fallback" },
					["<C-Space>"] = { "show", "show_documentation", "hide_documentation" },
					["<C-p>"] = { "select_prev", "fallback" },
					["<C-n>"] = { "select_next", "fallback" },
					cmdline = {
						preset = "none",
						["<C-y>"] = { "select_and_accept" },
						["<C-b>"] = { "scroll_documentation_up", "fallback" },
						["<C-f>"] = { "scroll_documentation_down", "fallback" },
						["<C-h>"] = { "hide", "fallback" },
						["<C-Space>"] = { "show", "show_documentation", "hide_documentation" },
						["<C-p>"] = { "select_prev", "fallback" },
						["<C-n>"] = { "select_next", "fallback" },
					},
				},

				-- default list of enabled providers defined so that you can extend it
				-- elsewhere in your config, without redefining it, via `opts_extend`
				sources = {
					default = { "lsp", "path", "snippets", "buffer" },

					-- adding any nvim-cmp sources here will enable them
					-- with blink.compat, need to uncomment compat in dependencies
					-- compat = {},

					-- providers = {
					-- 	lsp = {
					-- 		transform_items = function(_, items)
					-- 			-- Remove the "Text" source from lsp autocomplete
					-- 			return vim.tbl_filter(function(item)
					-- 				return item.kind ~= vim.lsp.protocol.CompletionItemKind.Text
					-- 			end, items)
					-- 		end,
					-- 	},
					-- },

					-- disable cmdline by passing empty table
					cmdline = function()
						local type = vim.fn.getcmdtype()
						-- Search forward and backward
						if type == "/" or type == "?" then
							return { "buffer" }
						end
						-- Commands
						if type == ":" or type == "@" then
							return { "cmdline" }
						end
						return {}
					end,
				},
				completion = {
					accept = {
						-- experimental auto-brackets support
						auto_brackets = {
							enabled = false,
						},
					},
					menu = {
						draw = {
							treesitter = { "lsp" },
							columns = {
								{ "label", "label_description", gap = 1 },
								{ "kind_icon", "kind", gap = 1 },
							},
						},
						border = "rounded",
					},
					list = {
						selection = { auto_insert = false },
						max_items = 10,
					},
					documentation = {
						window = { border = "rounded" },
					},
				},

				appearance = {
					-- Sets the fallback highlight groups to nvim-cmp's highlight groups
					-- Useful for when your theme doesn't support blink.cmp
					-- will be removed in a future release
					use_nvim_cmp_as_default = false,
					-- Set to 'mono' for 'Nerd Font Mono' or 'normal' for 'Nerd Font'
					-- Adjusts spacing to ensure icons are aligned
					nerd_font_variant = "mono",
				},

				-- experimental signature help support
				signature = {
					enabled = false,
				},
			},
			config = function(_, opts)
				require("blink.cmp").setup(opts)
			end,
		},
		{
			"neovim/nvim-lspconfig",
			opts = {
				servers = {
					vtsls = {
						settings = {
							complete_function_calls = true,
							vtsls = {
								enableMoveToFileCodeAction = true,
								autoUseWorkspaceTsdk = true,
								experimental = {
									maxInlayHintLength = 30,
									completion = {
										enableServerSideFuzzyMatch = true,
									},
								},
							},
							typescript = {
								updateImportsOnFileMove = { enabled = "always" },
								suggest = {
									completeFunctionCalls = true,
								},
								inlayHints = {
									enumMemberValues = { enabled = true },
									functionLikeReturnTypes = { enabled = true },
									parameterNames = { enabled = "literals" },
									parameterTypes = { enabled = true },
									propertyDeclarationTypes = { enabled = true },
									variableTypes = { enabled = false },
								},
							},
						},
					},
				},
			},
			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,
		},
	},
})

@BitInByte
Copy link

Hi, I am also having the same issue. On my case it happens both with vtsls and ts_ls. Sometimes takes a lot of time to show the results :(

@TheEros
Copy link

TheEros commented Feb 8, 2025

I am also having the same issue.

@TheEros
Copy link

TheEros commented Feb 8, 2025

0.10 is work good, the issue on 0.11

@Saghen
Copy link
Owner

Saghen commented Feb 8, 2025

Does disabling completion.trigger.prefetch_on_insert solve the issue for you?

@Aqothy
Copy link
Author

Aqothy commented Feb 8, 2025

Does disabling completion.trigger.prefetch_on_insert solve the issue for you?

disabling completion.trigger.prefetch_on_insert did not work for me either, tried downgrading to 0.10 didnt work for me either :(

I tried the same setup on a windows machine and faced the exact problem so its probably not an os problem either, perhaps ill try building from the source next to see if it fixes anything

@BitInByte
Copy link

For me it also started on 0.10. Can’t precise which version of 0.10. Also, I just switched for nvim cmp to see if it was slow in there as well and in there everything works fine.

Hopefully there will be a solution soon to switch back to blink cmp.

I have been trying to figure it out if there was something I just made that could cause this but I couldn’t :(

@Saghen
Copy link
Owner

Saghen commented Feb 9, 2025

I'm able to reproduce the stutter and I'm seeing the LSP returns a whopping 54k items, but I can't find the bottleneck inside of blink.cmp using profiler.nvim or snacks.profile

@Aqothy
Copy link
Author

Aqothy commented Feb 9, 2025

I'm able to reproduce the stutter and I'm seeing the LSP returns a whopping 54k items, but I can't find the bottleneck inside of blink.cmp using profiler.nvim or snacks.profile

Thats insane, so is that a lsp problem then, or perhaps problem with some npm libraries? Could you try changing moduleResolution to "node" instead of "bundler" in the tsconfig.json, for me that made everything super fast, how many items does lsp return in that case?

@Saghen
Copy link
Owner

Saghen commented Feb 9, 2025

Could you try changing moduleResolution to "node" instead of "bundler" in the tsconfig.json, for me that made everything super fast

Only 4.5k items in this case, much more reasonable! It looks like it's the neovim LSP implementation that's struggling to keep up. Testing using luafile test.lua with this file

local clients = vim.lsp.get_clients({ bufnr = 0, method = "textDocument/completion" })
for _, client in ipairs(clients) do
	local params = vim.lsp.util.make_position_params(0, client.offset_encoding)
	client.request("textDocument/completion", params, function(err, result)
		vim.print(client.name)
		vim.print(#result.items)
	end)
end

@Aqothy
Copy link
Author

Aqothy commented Feb 9, 2025

Could you try changing moduleResolution to "node" instead of "bundler" in the tsconfig.json, for me that made everything super fast

Only 4.5k items in this case, much more reasonable! It looks like it's the neovim LSP implementation that's struggling to keep up. Testing using luafile test.lua with this file

local clients = vim.lsp.get_clients({ bufnr = 0, method = "textDocument/completion" })
for _, client in ipairs(clients) do
	local params = vim.lsp.util.make_position_params(0, client.offset_encoding)
	client.request("textDocument/completion", params, function(err, result)
		vim.print(client.name)
		vim.print(#result.items)
	end)
end

That's strange, tbh I don't even know what the difference between node and bundler I just tested a bunch comparing projects that worked vs didn't. So is there a fix to go around this?

@Saghen
Copy link
Owner

Saghen commented Feb 9, 2025

You could try enabling these two settings in vtsls to reduce the number of items sent to neovim: https://github.com/yioneko/vtsls/blob/main/packages/service/configuration.schema.json#L1259-L1271

Ideally though, vtsls would make use of itemDefaults and resolve to send less data to neovim. Currently, it's sending 27mbs of JSON

@Aqothy
Copy link
Author

Aqothy commented Feb 9, 2025

You could try enabling these two settings in vtsls to reduce the number of items sent to neovim: https://github.com/yioneko/vtsls/blob/main/packages/service/configuration.schema.json#L1259-L1271

Ideally though, vtsls would make use of itemDefaults and resolve to send less data to neovim. Currently, it's sending 27mbs of JSON

I had the one of those options enabled already and setting entriesLimit = 3000 removed the lag but completions still quite slow, I'll try opening an issue to vtsls then, didn't think it a lsp issue at first since nvim cmp seemed to work fine. Thanks for helping me pin pointing the issue

Saghen added a commit that referenced this issue Feb 9, 2025
@Saghen
Copy link
Owner

Saghen commented Feb 9, 2025

I just noticed that we weren't sending cancellation requests to the server, which means vtsls would've been processing a request for each keystroke! However, if vtsls doesn't handle the cancellation, this would still be the case. I believe nvim-cmp debounces events by default, which we should adopt for slow LSPs sources

@Aqothy
Copy link
Author

Aqothy commented Feb 9, 2025

I just noticed that we weren't sending cancellation requests to the server, which means vtsls would've been processing a request for each keystroke! However, if vtsls doesn't handle the cancellation, this would still be the case. I believe nvim-cmp debounces events by default, which we should adopt for slow LSPs sources

Thanks for already creating the issue for vtsls, for now im just going to use modularResolution node as that seems to work the best for me, hopefully everything fixes up soon, really loving blink, again thanks for all the work you do.

@BitInByte
Copy link

BitInByte commented Feb 9, 2025

@Saghen i think that the issue might not be caused by vtsls but by ts_ls as i am also having the same issues with ts_ls.

Do you think that the debounce time would fix this issue on blink? It made sense that nvim-cmp then there’s no delay since is not waiting for any pending request to the server, ence it shows the completion list faster.

Hopefully there will be a solution soon to switch back to blink :)

@AkisArou
Copy link

I use vtsls (did not try with ts_ls) and when I enter a .ts file, the first completion item is delayed for some seconds.
Even keywords like function.
Subsequent completions are fast though.
Previous versions worked fine.

I tried v12.0.3 because I saw this issue is mentioned at a commit message: cancellation not bubbling to sources, closes #1116 #1115 and tried whether my issue is fixed, but unfortunately not.

Thanks for the plugin btw :)

@jacquesg
Copy link

From my side, 0.12.x with vtsls seems to be much snappier.

@Saghen
Copy link
Owner

Saghen commented Feb 18, 2025

If anyone else is able to confirm it's as fast as nvim-cmp, we can close this one

@Aqothy
Copy link
Author

Aqothy commented Feb 18, 2025

If anyone else is able to confirm it's as fast as nvim-cmp, we can close this one

It's definitely faster and doesn't cause neovim to stutter anymore, but it's still not as fast completions with other languages or nvim cmp, that's my experience with it building from source.

@AkisArou
Copy link

AkisArou commented Feb 19, 2025

In some projects, it is fast.
In others, nvim-cmp is significantly faster and completion loads instantly on each file load, while blink takes a few seconds to load the completion items.
I am referring to TypeScript projects (using vtls).
EDIT: both v0.12.4 & build from source

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working LSP Related to a specific LSP
Projects
None yet
Development

No branches or pull requests

6 participants