Skip to content

Very slow startup time on WSL #3704

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

Closed
m4r1vs opened this issue Apr 10, 2025 · 8 comments · Fixed by #3778 or #3808
Closed

Very slow startup time on WSL #3704

m4r1vs opened this issue Apr 10, 2025 · 8 comments · Fixed by #3778 or #3808
Labels
bug Something isn't working

Comments

@m4r1vs
Copy link

m4r1vs commented Apr 10, 2025

Description

When running my Neovim configuration on WSL, every plugin loads almost as quickly as natively on Linux. However, lspconfig takes almost 2 seconds to require all configs (takes about 20ms on native Linux). Here is the ouput of nvim --startuptime:

029.652  000.032  000.032: require('vim.health')
029.654  000.068  000.036: require('vim.deprecated.health')
029.749  000.042  000.042: require('vim.iter')
030.889  000.072  000.072: require('vim.lsp.log')
031.225  000.332  000.332: require('vim.lsp.protocol')
031.337  000.108  000.108: require('vim.lsp.util')
031.414  000.026  000.026: require('vim.lsp.sync')
031.417  000.065  000.039: require('vim.lsp._changetracking')
031.497  000.038  000.038: require('vim.lsp._transport')
031.508  000.089  000.051: require('vim.lsp.rpc')
031.564  000.818  000.151: require('vim.lsp')
032.171  000.606  000.606: require('lspconfig.util')
032.186  001.473  000.049: sourcing /home/mn/.local/share/nvim/lazy/nvim-lspconfig/plugin/lspconfig.lua
032.191  001.499  000.025: sourcing nvim_exec2() called at /home/mn/.config/nvim/init.lua:0
032.197  000.002  000.002: sourcing nvim_exec2() called at /home/mn/.config/nvim/init.lua:0
032.206  000.001  000.001: sourcing nvim_exec2() called at /home/mn/.config/nvim/init.lua:0
032.310  000.028  000.028: require('lspconfig.async')
032.313  000.073  000.045: require('lspconfig.configs')
032.317  002.056  000.481: require('lspconfig')
032.347  000.029  000.029: require('lspconfig.configs.hyprls')
097.042  000.107  000.107: require('lspconfig.manager')
098.081  000.056  000.056: require('lspconfig.configs.docker_compose_language_service')
161.425  000.083  000.083: require('lspconfig.configs.helm_ls')
226.029  000.103  000.103: require('lspconfig.configs.bashls')
295.113  000.277  000.277: require('lspconfig.configs.kotlin_language_server')
360.411  000.125  000.125: require('lspconfig.configs.clangd')
428.449  000.157  000.157: require('lspconfig.configs.cssls')
493.817  000.100  000.100: require('lspconfig.configs.dockerls')
558.305  000.134  000.134: require('lspconfig.configs.eslint')
622.795  000.116  000.116: require('lspconfig.configs.gitlab_ci_ls')
687.363  000.098  000.098: require('lspconfig.configs.golangci_lint_ls')
751.771  000.115  000.115: require('lspconfig.configs.gopls')
822.704  000.112  000.112: require('lspconfig.configs.html')
889.923  000.102  000.102: require('vim.lsp.completion')
890.009  000.286  000.183: require('vim.lsp.handlers')
890.019  000.414  000.128: require('lspconfig.configs.jdtls')
955.248  000.097  000.097: require('lspconfig.configs.jsonls')
1022.903  000.202  000.202: require('lspconfig.configs.lua_ls')
1091.062  000.133  000.133: require('lspconfig.configs.marksman')
1158.942  000.143  000.143: require('lspconfig.configs.nil_ls')
1225.097  000.115  000.115: require('lspconfig.configs.nixd')
1292.061  000.135  000.135: require('lspconfig.configs.pyright')
1357.667  000.157  000.157: require('lspconfig.configs.rust_analyzer')
1425.558  000.140  000.140: require('lspconfig.configs.sourcekit')
1494.042  000.118  000.118: require('lspconfig.configs.taplo')
1559.123  000.131  000.131: require('lspconfig.configs.terraformls')
1625.734  000.219  000.219: require('lspconfig.configs.texlab')
1692.442  000.160  000.160: require('lspconfig.configs.tinymist')
1762.981  000.234  000.234: require('lspconfig.configs.vtsls')
1834.273  000.158  000.158: require('lspconfig.configs.yamlls')
1906.146  000.255  000.255: require('lspconfig.configs.zls')
1975.347  000.086  000.086: require('cmp_nvim_lsp.source')
1975.353  000.227  000.141: require('cmp_nvim_lsp')
1976.097  000.371  000.371: sourcing /home/mn/.local/share/nvim/lazy/leap/plugin/init.lua

What could be the cause of this? I have tried different Linux distributions on WSL but all of them show this issue..

@m4r1vs m4r1vs added the bug Something isn't working label Apr 10, 2025
@m4r1vs
Copy link
Author

m4r1vs commented Apr 10, 2025

From past experience debugging WSL issues, my best guess is that this is caused by the storage location of the required files. If they are stored somewhere directly on Windows, stuff gets slow fast.

Edit: This is not the case, all configs are properly stored at /home/mn/.local/share/nvim/lazy/nvim-lspconfig/lua/lspconfig/configs/. Nothing unusual

@m4r1vs
Copy link
Author

m4r1vs commented Apr 10, 2025

util.root_pattern is called in lots of configs but does not seem to be the cause of this

@m4r1vs
Copy link
Author

m4r1vs commented Apr 10, 2025

Okay, I have found the cause for this. If my PATH contains programs outside WSL, lspconfig is extremely slow. However, if my PATH only contains binaries inside the WSL system, it is quick as normal.

Not sure if this should (or can) be addressed by lspconfig or not?

@justinmk
Copy link
Member

If my PATH contains programs outside WSL, lspconfig is extremely slow.

Thanks for debugging that! Likely caused by exepath() :

local function sanitize_cmd(cmd)
if cmd and type(cmd) == 'table' and not vim.tbl_isempty(cmd) then
local original = cmd[1]
cmd[1] = vim.fn.exepath(cmd[1])

or maybe there's an executable() call somewhere. (related: neovim/neovim#31506 )

Proposal

If you find that dropping the exepath() call fixes the issue, maybe we can just remove it. I don't quite see why it's necessary. (It's good to have explicit, full paths, but this is way too late in the lifecycle...)

@jrop
Copy link
Contributor

jrop commented Apr 25, 2025

@justinmk I've been running with this patch for a few days and this speeds things up nice and snappy for me in WSL:

diff --git a/lua/lspconfig/configs.lua b/lua/lspconfig/configs.lua
index b32b8001..3b9967c1 100644
--- a/lua/lspconfig/configs.lua
+++ b/lua/lspconfig/configs.lua
@@ -24,17 +24,6 @@ local configs = {}
 --- @field root_dir? string|fun(filename: string, bufnr: number)
 --- @field commands? table<string, lspconfig.Config.command>

---- @param cmd any
-local function sanitize_cmd(cmd)
-  if cmd and type(cmd) == 'table' and not vim.tbl_isempty(cmd) then
-    local original = cmd[1]
-    cmd[1] = vim.fn.exepath(cmd[1])
-    if #cmd[1] == 0 then
-      cmd[1] = original
-    end
-  end
-end
-
 ---@param t table
 ---@param config_name string
 ---@param config_def table Config definition read from `lspconfig.configs.<name>`.
@@ -64,8 +53,6 @@ function configs.__newindex(t, config_name, config_def)

     local config = tbl_deep_extend('keep', user_config, default_config)

-    sanitize_cmd(config.cmd)
-
     if util.on_setup then
       pcall(util.on_setup, config, user_config)
     end

@justinmk
Copy link
Member

justinmk commented Apr 25, 2025

SGTM. Feel free to send a PR. The old lua/lspconfig/ configs are deprecated, but targeted bug fixes are fine. Will plan to tag a release before merging your change.

@lucaSartore
Copy link
Contributor

@justinmk I am afraid this may have caused some unintended consequences.

Basically, all my LSPs have stopped working on both of my Windows machines a few days ago (however, my Linux machine is still working).

I’ll use Python as an example:

When I open a Python file, I get the following error:

...gram Files/Neovim/share/nvim/runtime/lua/vim/lsp/rpc.lua:800: Spawning language server with cmd: `{ "pyright-langserver", "--stdio" }` failed. The language server is either not installed, missing from PATH, or not executable.

After a short investigation, I found that:

:lua print(vim.loop.spawn("lua-language-server.cmd",{}))

Output:

uv_process_t: 0x02703f84cf00 2908

Meanwhile:

:lua print(vim.loop.spawn("lua-language-server",{}))

Output:

nil ENOENT: no such file or directory ENOENT

So essentially, the spawn command doesn’t work properly if the execution command does not have the .cmd extension.

At this point, I managed to solve the issue with the following code:

require("lspconfig").pyright.setup({
    cmd = { 'pyright-langserver.cmd', '--stdio' },
    capabilities = capabilities,
})

However, I still wasn’t satisfied and tried to understand why this started happening now.

After a bit of binary search on the Git history, I identified the commit 8bb2fac as the one that caused the issue. Reverting nvim-lspconfig to the previous commit fixes the issue.

So, if it isn’t clear yet, the reason this did not fail before was that the sanitize_cmd function added a .bat at the end.

I propose we revert the commit immediately and maybe edit the function so that it is executed only on Windows. I can send you a PR for that if you agree.

@justinmk
Copy link
Member

I propose we revert the commit immediately and maybe edit the function so that it is executed only on Windows. I can send you a PR for that if you agree.

Let's try that. And add a comment explaining why exepath() is there.

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