neovim / nvim-lspconfig

Quickstart configs for Nvim LSP
Apache License 2.0
10.46k stars 2.06k forks source link

clangd: Add option to fallback to vim.fn.getcwd() if root dir can't be determined #2060

Open tom-anders opened 2 years ago

tom-anders commented 2 years ago

Language server

clangd

Requested feature

When working with package managers like conan, using something like textDocument/definition will take you to header files outside of your project directory (e.g. in ~/.conan/data). Currently, get_root_dir return nil in this case, so clangd is not able to determine the right compile flags to use.

I propose to add an option (or even make it a default) to return vim.fn.getcwd() in this case instead. That way, clangd will do a pretty good job of inferring the right compile commands to use from your projects compile_commands.json.

Currently, my config contains this snippet:

    root_dir = function(fname)
        return lspconfig.util.root_pattern('compile_commands.json')(fname) or lspconfig.util.find_git_ancestor(fname) or vim.fn.getcwd()
    end,

An alternative could be to add the compile-commands-dir flag in this case (see https://clangd.llvm.org/faq#how-do-i-fix-errors-i-get-when-opening-headers-outside-of-my-project-directory)

glepnir commented 2 years ago

use vim.fn.getcwd is not correct way check the root dir

https://github.com/neovim/nvim-lspconfig/blob/master/CONTRIBUTING.md

bvtthead commented 1 year ago

I'm using natecraddock/workspaces.nvim to create workspaces and then checking if I have a workspace activated.

So my clangd root_dir looks like:

root_dir = function(fname)
    return get_workspace_path() or clangd.document_config.default_config.root_dir(fname)
end,

and get_workspace_path looks like:

function M.get_workspace_path()
    local workspaces = require 'workspaces'
    local saved_workspaces = workspaces.get()

    local current_workspace = workspaces.name()
    if current_workspace == nil then
        return
    end

    for _, workspace in ipairs(saved_workspaces) do
        if current_workspace == workspace.name then
            local path = workspace.path
            -- Check for trailing slash since lspconfig stores root dir without slash
            if path:sub(-1, -1) == '/' then
                path = path:sub(1, -2)
            end
            return path
        end
    end
end

The check for trailing slash is needed at time of 2315a397fd5057e3a74a09a240f606af28447ebf commit, may change.

justinmk commented 1 week ago

Is this different than single-file support? https://github.com/neovim/nvim-lspconfig/blob/b064131428f6bbbbc905f4451ba6779fda334a3a/doc/lspconfig.txt#L433-L439

Do we still need this?

glepnir commented 1 week ago

root dir of single-file support is nil which send to server.

bvtthead commented 1 week ago

This is different from single-file support, when you use go-to-definition that leads you to source code outside of your already established root-dir, it opens this new file in a new clangd instance as a "single-file" or as a project if there is a root-dir match in that new location, neither of which is the desired outcome. It loses the context of your project.

I still use the method I use above, I'm not sure there is a feasible way to do this on the lspconfig side. Maybe a separate "super-duper-root-dir" pattern that checks for a dotfile and if found, is the fallback instead of nil.

I think explicitly marking your project as a workspace or something similar and using whatever open workspace as a fallback is the least complex method.