neovim / nvim-lspconfig

Quickstart configs for Nvim LSP
Apache License 2.0
10.62k stars 2.08k forks source link

Error detecting root for sumneko_lua on Windows #1266

Closed TheLeoP closed 2 years ago

TheLeoP commented 3 years ago

Description

If I open a .lua file of my plugin folder using / for its path, lspconfig doesn't initialize the server. If I open the same file using \ for its path, lspconfig does initialize the server, but sometimes fails finding the root of the project (sometimes the root is found on the directory containing .git and sometimes the root is set to the directory containing the .lua file opened).

Neovim version

NVIM v0.5.0
Build type: RelWithDebInfo
LuaJIT 2.1.0-beta3
Compilation: C:/Program Files (x86)/Microsoft Visual Studio/2017/Enterprise/VC/Tools/MSVC/14.16.27023/bin/Hostx86/x64/cl.exe /DWIN32 /D_WINDOWS /W3 -DNVIM_TS_HAS_SET_MATCH_LIMIT /MD /Zi /O2 /Ob1 /DNDEBUG /W3 -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE -DWIN32 -D_WIN32_WINNT=0x0600 -DINCLUDE_GENERATED_DECLARATIONS -DNVIM_MSGPACK_HAS_FLOAT32 -DNVIM_UNIBI_HAS_VAR_FROM -DMIN_LOG_LEVEL=3 -ID:/a/neovim/neovim/build/config -ID:/a/neovim/neovim/src -ID:/a/neovim/neovim/nvim-deps/usr/include -ID:/a/neovim/neovim/build/src/nvim/auto -ID:/a/neovim/neovim/build/include
Compilado por runneradmin@fv-az152-786

Nvim-lspconfig version

aa0b9fd746d73a5ebbc72c732c645e96423d4504

Operating system and version

Windows 10 Pro. Version: 2004.

Affected language servers

sumneko_lua

Steps to reproduce

  1. nvim -nu min.lua
  2. :e C:\Users\pcx\AppData\Local\nvim-data\site\pack\packer\start\fix_1086\lua\telescope\builtin\files.lua
  3. :LspInfo
  4. nvim -nu min.lua (open a new instance of Neovim)
  5. :e C:/Users/pcx/AppData/Local/nvim-data/site/pack/packer/start/fix_1086/lua/telescope/builtin/files.lua
  6. :LspInfo

Notes:

Actual behavior

Expected behavior

Both instances should have a sumneko_lua instance attached to them (a different instance each one) with the root C:\Users\pcx\AppData\Local\nvim-data\site\pack\packer\start\fix_1086

Minimal config

local home_dir = vim.api.nvim_eval('$HOME')

vim.cmd [[set runtimepath=$VIMRUNTIME]]
vim.cmd [[set packpath=C:/tmp/nvim/site]]

local package_root = 'C:/tmp/nvim/site/pack'
local install_path = package_root .. '/packer/start/packer.nvim'

local function load_plugins()
  require('packer').startup {
    {
      'wbthomason/packer.nvim',
      'neovim/nvim-lspconfig',
    },
    config = {
      package_root = package_root,
      compile_path = install_path .. '/plugin/packer_compiled.lua',
    },
  }
end

_G.load_config = function()
  vim.lsp.set_log_level 'trace'
  local nvim_lsp = require 'lspconfig'
  local on_attach = function(_, bufnr)
    local function buf_set_keymap(...)
      vim.api.nvim_buf_set_keymap(bufnr, ...)
    end
    local function buf_set_option(...)
      vim.api.nvim_buf_set_option(bufnr, ...)
    end

    buf_set_option('omnifunc', 'v:lua.vim.lsp.omnifunc')

    -- Mappings.
    local opts = { noremap = true, silent = true }
    buf_set_keymap('n', 'gD', '<Cmd>lua vim.lsp.buf.declaration()<CR>', opts)
    buf_set_keymap('n', 'gd', '<Cmd>lua vim.lsp.buf.definition()<CR>', opts)
    buf_set_keymap('n', 'K', '<Cmd>lua vim.lsp.buf.hover()<CR>', opts)
    buf_set_keymap('n', 'gi', '<cmd>lua vim.lsp.buf.implementation()<CR>', opts)
    buf_set_keymap('n', '<C-k>', '<cmd>lua vim.lsp.buf.signature_help()<CR>', opts)
    buf_set_keymap('n', '<space>wa', '<cmd>lua vim.lsp.buf.add_workspace_folder()<CR>', opts)
    buf_set_keymap('n', '<space>wr', '<cmd>lua vim.lsp.buf.remove_workspace_folder()<CR>', opts)
    buf_set_keymap('n', '<space>wl', '<cmd>lua print(vim.inspect(vim.lsp.buf.list_workspace_folders()))<CR>', opts)
    buf_set_keymap('n', '<space>D', '<cmd>lua vim.lsp.buf.type_definition()<CR>', opts)
    buf_set_keymap('n', '<space>rn', '<cmd>lua vim.lsp.buf.rename()<CR>', opts)
    buf_set_keymap('n', 'gr', '<cmd>lua vim.lsp.buf.references()<CR>', opts)
    buf_set_keymap('n', '<space>e', '<cmd>lua vim.lsp.diagnostic.show_line_diagnostics()<CR>', opts)
    buf_set_keymap('n', '[d', '<cmd>lua vim.lsp.diagnostic.goto_prev()<CR>', opts)
    buf_set_keymap('n', ']d', '<cmd>lua vim.lsp.diagnostic.goto_next()<CR>', opts)
    buf_set_keymap('n', '<space>q', '<cmd>lua vim.lsp.diagnostic.set_loclist()<CR>', opts)
  end

  -- Add the server that troubles you here
  local name = 'sumneko_lua'
  local sumneko_root_path = home_dir .. "/.lua-lsp/lua-language-server"
  local sumneko_binary = home_dir .. "/.lua-lsp/lua-language-server/bin/" .. "Windows" .. "/lua-language-server"
  local cmd = {sumneko_binary, "-E", sumneko_root_path .. "/main.lua"} -- needed for elixirls, omnisharp, sumneko_lua
  if not name then
    print 'You have not defined a server name, please edit minimal_init.lua'
  end
  if not nvim_lsp[name].document_config.default_config.cmd and not cmd then
    print [[You have not defined a server default cmd for a server
      that requires it please edit minimal_init.lua]]
  end

  nvim_lsp[name].setup {
    cmd = cmd,
    on_attach = on_attach,
  }

  print [[You can find your log at $HOME/.cache/nvim/lsp.log. Please paste in a github issue under a details tag as described in the issue template.]]
end

if vim.fn.isdirectory(install_path) == 0 then
  vim.fn.system { 'git', 'clone', 'https://github.com/wbthomason/packer.nvim', install_path }
  load_plugins()
  require('packer').sync()
  vim.cmd [[autocmd User PackerComplete ++once lua load_config()]]
else
  load_plugins()
  require('packer').sync()
  _G.load_config()
end

Health check

health#lspconfig#check

Checking language server protocol configuration

LSP log

https://gist.github.com/TheLeoP/cb7a14a7ff94736c91bc8616ad623273

justinmk commented 3 years ago
  • The buffer opened on the first Neovim instance (opened with \) has a sumneko_lua instance attached to it, but the root directory of the instance is C:\Users\pcx\AppData\Local\nvim-data\site\pack\packer\start\fix_1086\lua\telescope\builtin\ (wich doesn't have a .git, but it's the directory of the file I opened).

what do you expect the root directory to be and how should the code find it?

TheLeoP commented 3 years ago

In this case, the root folder should be the fix_1086 directory because it's the root of a git repository (has a .git). But instead the root used is .../fix_1086/lua/telescope/builtin

TheLeoP commented 3 years ago

I realized I wasn't giving a reproducible example, so I tried to make one. While doing that, I discovered I'm facing two different bugs:

The first one occurs when I try to open a file with an absolute path using / and then using \. If I use \ for the absolute path, the sumneko_lua server starts correctly, but if I use / for the path, the server doesn't starts at all. The reason I couldn't reproduce that error using a simple lua project before was because I was using relative paths. Also, for some reason, if anywhere on the path I use // instead of just /, the servers starts normally (C:path/to/my/lua/file/foo.lua doesn't start the server but C:path/to//my/lua/file/foo.lua does start the server and founds the correct root of the project if the root contains a .git)

The second bug is the one causing lspconfig to not find the root of my plugins and start the server anyway. I will try to reproduce it with a simple lua project so anyone could reproduce too later.

Should I close this issue and open one separated for each bug?

kaylynb commented 3 years ago

I think this might have to do with buffer names being varied based on how they are opened in vim.

For nvim-lspconfig, if the buffer name has any forward slashes on Windows, it fails (usually in util.root_pattern).

See: https://github.com/neovim/neovim/issues/3912 https://github.com/vim/vim/issues/541

Opening a file with vim-dirvish, for example, will set the buffer name to forward slashes on all platforms: https://github.com/justinmk/vim-dirvish/issues/37

It seems that language servers can also return varying paths. For example, tsserver returns a lowercase drive letter when navigating to definition, which can cause an additional instance to be created.

Example: C:\tsconfigroot\src\index.tsx --> navigate to definition of App c:\tsconfigroot\src\App.tsx

I'm not really sure the best way to handle this, but it might make sense for lspconfig to somehow normalize the path, especially since it's being used as a key for client instance resolution.

Windows paths are kind of a mess. They can actually be case sensitive depending on configuration, and UNC paths have different root chars.

It looks like loop.fs_realpath should always get the same case/slash output according to the libuv win32 realpath code. It may be a good choice for normalizing the path on Windows.

I'm testing a fix here: https://github.com/kaylynb/nvim-lspconfig/compare/master...path_windows, but there are so many different language server configs I don't know what it may break.

justinmk commented 3 years ago

I'm testing a fix here: kaylynb/nvim-lspconfig@master...path_windows, but there are so many different language server configs I don't know what it may break.

We (the vim.lsp subsystem) should always use forward slashes, and always absolute paths, internally. If we aren't already doing that, it needs to be fixed.

The is zero reason to use \ slashes on Windows in a LSP plugin.

kaylynb commented 3 years ago

That seems reasonable. I just checked fs_stat and it seems to work with both types of slashes for UNC paths as well.

Just fixing the slashes doesn't fix the c: vs C: drive root issue. I also just remembered that the root of a UNC path is not a directory, so some of the ancestor walking code might be affected by that.

mjlbach commented 2 years ago

We (the vim.lsp subsystem) should always use forward slashes, and always absolute paths, internally. If we aren't already doing that, it needs to be fixed.

The main issue is we trust nvim_buf_get_name() to be sanitized/consistent, which it's not. I think we should normalize internally lspconfig to use forward slash/escaped strings. The drive letter issue is a bug which I filed in core https://github.com/neovim/neovim/issues/16331, which we can temporarily workaround trivially.

I don't think we should sanitize the drive path before sending to start_client though, only for internal use in the tables that track which buffers are attached to which servers and the autocommands.

mg979 commented 2 years ago

I'm having a similar problem just opening a session in Windows. Note the forward slashes in the path of the buffer. What happens is that it gets a path with forward slashes and since it's Windows, it expects backslashes, and it can't find the root dir. I use vim-dirvish, so many times the buffer name contains forward slashes.

"D:/Gianmaria/Cloud/vim/lua/settings/lsp/lua.lua" [Unix] 33L, 1481C
Errore/i eseguendo FileType Autocommands for "lua":
E5108: Error executing lua ...oop\apps\neovim\0.6.0\share\nvim\runtime/lua/vim/lsp.lua:239: cmd_cwd: expected directory, got D:/Gianmaria/Cloud/vim/lua/settings/lsp/lua.lua
stack traceback:
    [C]: in function 'error'
    vim/shared.lua:608: in function 'validate'
    ...oop\apps\neovim\0.6.0\share\nvim\runtime/lua/vim/lsp.lua:239: in function 'validate_client_config'
    ...oop\apps\neovim\0.6.0\share\nvim\runtime/lua/vim/lsp.lua:688: in function 'start_client'
    ...te\pack\npacks\opt\nvim-lspconfig/lua/lspconfig/util.lua:267: in function 'add'
    ...pack\npacks\opt\nvim-lspconfig/lua/lspconfig/configs.lua:220: in function 'try_add'
    [string ":lua"]:1: in main chunk

Locally I can fix it making this: https://github.com/neovim/nvim-lspconfig/blob/80e101e7ec12835737f35452f07a94d9cc2e690d/lua/lspconfig/util.lua#L129-L133

into this

    dirname = function(path)
      if not path or #path == 0 then
        return
      end
      if is_windows then
        path = path:gsub('/', '\\')
      end
      local result = path:gsub(strip_sep_pat, ''):gsub(strip_dir_pat, '')
R00dRallec commented 2 years ago

Locally I can fix it making this:

https://github.com/neovim/nvim-lspconfig/blob/80e101e7ec12835737f35452f07a94d9cc2e690d/lua/lspconfig/util.lua#L129-L133

into this

    dirname = function(path)
      if not path or #path == 0 then
        return
      end
      if is_windows then
        path = path:gsub('/', '\\')
      end
      local result = path:gsub(strip_sep_pat, ''):gsub(strip_dir_pat, '')

I can confirm the above mentioned fix works on my machine as well (Windows 10 + Neovim 0.6).

I observed the issue with the default netrw. Neovim seems to be able to open files in Windows using single forward slashes without problems, but nvim-lspconfig is not able to handle this. E.g.:

:e C:/Development/Project/main.c

works fine with Neovim, but breaks the nvim-lspconfig, as the root directory cannot be extracted.