Decouple vim.lsp.config `root_markers` from LSP
Problem
People keep wanting to add "find project root" logic into various linters in nvim-lint. All/most of the time the logic would match what they're already doing for their LSP servers. That's a strong indication that the "project root" concept should be a general thing, not tied to LSP to make it re-usable for plugins
Current workaround to re-use that logic - assuming a client/server is running - is something like:
require("lint").try_lint(nil, { cwd = vim.lsp.get_clients()[1].root_dir })`
Other use cases:
- https://github.com/mfussenegger/nvim-dap/discussions/1530
Expected behavior
- Have
root_markersdefinitions as part of filetype plugins or buffer variable - Have a readonly
root_dirorproject_rootbuffer ~variable~ option or a function mirroringgetcwdor something like that, which plugins can use to get the project root for the current buffer
It should be a buffer option as opposed to a variable.
Related:
- https://github.com/neovim/neovim/pull/33771
Have a readonly
root_dirorproject_rootbuffer variable
Does https://github.com/neovim/neovim/pull/33320 address that, or should we drop the :bcd idea?
I think if we add bcd, it could be used as a good fallback, but I don't think users should have their cwd forced in order for LSP to work.
Having both seems a bit much. It sounds to me like :bcd may be the wrong direction.
From what I understand bcd was wanted for different reasons. Basically the same as tcd but for buffers. So what we decide here shouldn't impact the decision of that.
From what I understand
bcdwas wanted for different reasons. Basically the same astcdbut for buffers.
No, the discussion in https://github.com/neovim/neovim/issues/33318 seems clearly motivated by "root directory" and "project" use-cases.
But also:
Note also that a requirement is to have an ergonomic way to get the buffer's "current directory". At least via getcwd(), but also maybe a buffer-local option or variable? (Need to hear comments on that.)
At least for nvim-lint and nvim-dap-python where I do have the use-cases for a project root a bcd would work I guess.
But I also already follow a cwd=project-root model and don't know all the reasons why people might have a cwd!=project-root setup.
E.g. I assume fuzzy file finders/matchers to jump around projects would need to take care to not use bcd in some cases?
I see two use cases:
- Plugins can find the project root
- Commands like
:e,:grep,:pwdor:termshould use the project root
Having (1) is a low-risk win. What about 2? Should it be in core? or left to vim-rooter style plugins that glue a 'rootdir' buffer option?
If we want (2), then how will it interact with :lcd, :tcd and getcwd()? Implementing (2) requires touching internal state, and thus is more complex. We may want to defer it, but the machinery it uses should ideally be the same as use case (1).
Have
root_markersdefinitions as part of filetype plugins or buffer variable
Based on observation of nvim-lspconfig, a filetype's root_markers may change based on the LSP client(s) attached. E.g. a javascript file may be a deno, bun, or some other kind of project.
So it doesn't seem to map to a ftplugin.
However, storing the resolved "root dir" as state on a buffer makes sense? Though multiple LSP clients could override each other's decision.
One reason that buffer-local CWD is interesting: it removes the need to store CWD in the term:// URI.
If I think through the quickfix/grep/plugin use case, I think bcd and root markers address different things.
bcd relates to where the buffer itself is. If I am creating a preview window for the quickfix list, I might want to create a scratch buffer, assign a cwd buffer option to that, and then use it to cache/setup other buffers in the same directory. Or if I want to do a grep based on other buffers in the same directory, using bcd would be more straightforward than manually hacking through bufname. It would also create a simple/universally available source of truth everything could point toward.
(bcd could also be used for convenience functions for the user where, for example, you can create a new buffer and assign it a bcd on creation, then enter a filename to save it and use bcd to fill in where the filename goes to)
Whereas root markers tells you what projects the buffer is associated with. If I'm doing a location list project grep for a buffer, I would want to know what root markers/root dirs the project is associated with, so I could use that to build out the file origins. This also points strongly toward root markers being an option rather than a buffer variable or something set in the ftplugin file, as a buffer option gives me predictable behavior I can build a "project grep" based on (or, alternatively, some kind of managed behavior in a Lua module).
(The above also implies another question - Do you store the root markers? Root dirs? Both? If you only store the root markers, they would need to be resolved, which is a file system op, each time they are used)