mirror of
https://github.com/neovim/neovim.git
synced 2024-09-17 20:58:20 -04:00
feat(fs): add vim.fs.root (#28477)
vim.fs.root() is a function for finding a project root relative to a buffer using one or more "root markers". This is useful for LSP and could be useful for other "projects" designs, as well as for any plugins which work with a "projects" concept.
This commit is contained in:
parent
16513b3033
commit
38b9c322c9
@ -34,7 +34,7 @@ Follow these steps to get LSP features:
|
||||
vim.lsp.start({
|
||||
name = 'my-server-name',
|
||||
cmd = {'name-of-language-server-executable'},
|
||||
root_dir = vim.fs.dirname(vim.fs.find({'setup.py', 'pyproject.toml'}, { upward = true })[1]),
|
||||
root_dir = vim.fs.root(0, {'setup.py', 'pyproject.toml'}),
|
||||
})
|
||||
<
|
||||
3. Check that the server attached to the buffer: >
|
||||
@ -836,7 +836,7 @@ start({config}, {opts}) *vim.lsp.start()*
|
||||
vim.lsp.start({
|
||||
name = 'my-server-name',
|
||||
cmd = {'name-of-language-server-executable'},
|
||||
root_dir = vim.fs.dirname(vim.fs.find({'pyproject.toml', 'setup.py'}, { upward = true })[1]),
|
||||
root_dir = vim.fs.root(0, {'pyproject.toml', 'setup.py'}),
|
||||
})
|
||||
<
|
||||
|
||||
@ -848,7 +848,7 @@ start({config}, {opts}) *vim.lsp.start()*
|
||||
|vim.lsp.start_client()|.
|
||||
• `root_dir` path to the project root. By default this is used to decide
|
||||
if an existing client should be re-used. The example above uses
|
||||
|vim.fs.find()| and |vim.fs.dirname()| to detect the root by traversing
|
||||
|vim.fs.root()| and |vim.fs.dirname()| to detect the root by traversing
|
||||
the file system upwards starting from the current directory until either
|
||||
a `pyproject.toml` or `setup.py` file is found.
|
||||
• `workspace_folders` list of `{ uri:string, name: string }` tables
|
||||
|
@ -2891,13 +2891,6 @@ vim.fs.find({names}, {opts}) *vim.fs.find()*
|
||||
narrow the search to find only that type.
|
||||
|
||||
Examples: >lua
|
||||
-- location of Cargo.toml from the current buffer's path
|
||||
local cargo = vim.fs.find('Cargo.toml', {
|
||||
upward = true,
|
||||
stop = vim.uv.os_homedir(),
|
||||
path = vim.fs.dirname(vim.api.nvim_buf_get_name(0)),
|
||||
})
|
||||
|
||||
-- list all test directories under the runtime directory
|
||||
local test_dirs = vim.fs.find(
|
||||
{'test', 'tst', 'testdir'},
|
||||
@ -3013,6 +3006,35 @@ vim.fs.parents({start}) *vim.fs.parents()*
|
||||
(`nil`)
|
||||
(`string?`)
|
||||
|
||||
vim.fs.root({source}, {marker}) *vim.fs.root()*
|
||||
Find the first parent directory containing a specific "marker", relative
|
||||
to a buffer's directory.
|
||||
|
||||
Example: >lua
|
||||
-- Find the root of a Python project, starting from file 'main.py'
|
||||
vim.fs.root(vim.fs.joinpath(vim.env.PWD, 'main.py'), {'pyproject.toml', 'setup.py' })
|
||||
|
||||
-- Find the root of a git repository
|
||||
vim.fs.root(0, '.git')
|
||||
|
||||
-- Find the parent directory containing any file with a .csproj extension
|
||||
vim.fs.root(0, function(name, path)
|
||||
return name:match('%.csproj$') ~= nil
|
||||
end)
|
||||
<
|
||||
|
||||
Parameters: ~
|
||||
• {source} (`integer|string`) Buffer number (0 for current buffer) or
|
||||
file path to begin the search from.
|
||||
• {marker} (`string|string[]|fun(name: string, path: string): boolean`)
|
||||
A marker, or list of markers, to search for. If a function,
|
||||
the function is called for each evaluated item and should
|
||||
return true if {name} and {path} are a match.
|
||||
|
||||
Return: ~
|
||||
(`string?`) Directory path containing one of the given markers, or nil
|
||||
if no directory was found.
|
||||
|
||||
|
||||
==============================================================================
|
||||
Lua module: vim.glob *vim.glob*
|
||||
|
@ -365,6 +365,9 @@ The following new APIs and features were added.
|
||||
|
||||
• Added built-in |commenting| support.
|
||||
|
||||
• |vim.fs.root()| finds project root directories from a list of "root
|
||||
markers".
|
||||
|
||||
==============================================================================
|
||||
CHANGED FEATURES *news-changed*
|
||||
|
||||
|
@ -197,13 +197,6 @@ end
|
||||
--- Examples:
|
||||
---
|
||||
--- ```lua
|
||||
--- -- location of Cargo.toml from the current buffer's path
|
||||
--- local cargo = vim.fs.find('Cargo.toml', {
|
||||
--- upward = true,
|
||||
--- stop = vim.uv.os_homedir(),
|
||||
--- path = vim.fs.dirname(vim.api.nvim_buf_get_name(0)),
|
||||
--- })
|
||||
---
|
||||
--- -- list all test directories under the runtime directory
|
||||
--- local test_dirs = vim.fs.find(
|
||||
--- {'test', 'tst', 'testdir'},
|
||||
@ -334,6 +327,56 @@ function M.find(names, opts)
|
||||
return matches
|
||||
end
|
||||
|
||||
--- Find the first parent directory containing a specific "marker", relative to a buffer's
|
||||
--- directory.
|
||||
---
|
||||
--- Example:
|
||||
---
|
||||
--- ```lua
|
||||
--- -- Find the root of a Python project, starting from file 'main.py'
|
||||
--- vim.fs.root(vim.fs.joinpath(vim.env.PWD, 'main.py'), {'pyproject.toml', 'setup.py' })
|
||||
---
|
||||
--- -- Find the root of a git repository
|
||||
--- vim.fs.root(0, '.git')
|
||||
---
|
||||
--- -- Find the parent directory containing any file with a .csproj extension
|
||||
--- vim.fs.root(0, function(name, path)
|
||||
--- return name:match('%.csproj$') ~= nil
|
||||
--- end)
|
||||
--- ```
|
||||
---
|
||||
--- @param source integer|string Buffer number (0 for current buffer) or file path to begin the
|
||||
--- search from.
|
||||
--- @param marker (string|string[]|fun(name: string, path: string): boolean) A marker, or list
|
||||
--- of markers, to search for. If a function, the function is called for each
|
||||
--- evaluated item and should return true if {name} and {path} are a match.
|
||||
--- @return string? # Directory path containing one of the given markers, or nil if no directory was
|
||||
--- found.
|
||||
function M.root(source, marker)
|
||||
assert(source, 'missing required argument: source')
|
||||
assert(marker, 'missing required argument: marker')
|
||||
|
||||
local path ---@type string
|
||||
if type(source) == 'string' then
|
||||
path = source
|
||||
elseif type(source) == 'number' then
|
||||
path = vim.api.nvim_buf_get_name(source)
|
||||
else
|
||||
error('invalid type for argument "source": expected string or buffer number')
|
||||
end
|
||||
|
||||
local paths = M.find(marker, {
|
||||
upward = true,
|
||||
path = path,
|
||||
})
|
||||
|
||||
if #paths == 0 then
|
||||
return nil
|
||||
end
|
||||
|
||||
return vim.fs.dirname(paths[1])
|
||||
end
|
||||
|
||||
--- Split a Windows path into a prefix and a body, such that the body can be processed like a POSIX
|
||||
--- path. The path must use forward slashes as path separator.
|
||||
---
|
||||
|
@ -210,7 +210,7 @@ end
|
||||
--- vim.lsp.start({
|
||||
--- name = 'my-server-name',
|
||||
--- cmd = {'name-of-language-server-executable'},
|
||||
--- root_dir = vim.fs.dirname(vim.fs.find({'pyproject.toml', 'setup.py'}, { upward = true })[1]),
|
||||
--- root_dir = vim.fs.root(0, {'pyproject.toml', 'setup.py'}),
|
||||
--- })
|
||||
--- ```
|
||||
---
|
||||
@ -219,9 +219,9 @@ end
|
||||
--- - `name` arbitrary name for the LSP client. Should be unique per language server.
|
||||
--- - `cmd` command string[] or function, described at |vim.lsp.start_client()|.
|
||||
--- - `root_dir` path to the project root. By default this is used to decide if an existing client
|
||||
--- should be re-used. The example above uses |vim.fs.find()| and |vim.fs.dirname()| to detect the
|
||||
--- root by traversing the file system upwards starting from the current directory until either
|
||||
--- a `pyproject.toml` or `setup.py` file is found.
|
||||
--- should be re-used. The example above uses |vim.fs.root()| and |vim.fs.dirname()| to detect
|
||||
--- the root by traversing the file system upwards starting from the current directory until
|
||||
--- either a `pyproject.toml` or `setup.py` file is found.
|
||||
--- - `workspace_folders` list of `{ uri:string, name: string }` tables specifying the project root
|
||||
--- folders used by the language server. If `nil` the property is derived from `root_dir` for
|
||||
--- convenience.
|
||||
|
@ -7,6 +7,8 @@ local eq = t.eq
|
||||
local mkdir_p = n.mkdir_p
|
||||
local rmdir = n.rmdir
|
||||
local nvim_dir = n.nvim_dir
|
||||
local command = n.command
|
||||
local api = n.api
|
||||
local test_build_dir = t.paths.test_build_dir
|
||||
local test_source_path = t.paths.test_source_path
|
||||
local nvim_prog = n.nvim_prog
|
||||
@ -278,6 +280,38 @@ describe('vim.fs', function()
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('root()', function()
|
||||
before_each(function()
|
||||
command('edit test/functional/fixtures/tty-test.c')
|
||||
end)
|
||||
|
||||
it('works with a single marker', function()
|
||||
eq(test_source_path, exec_lua([[return vim.fs.root(0, '.git')]]))
|
||||
end)
|
||||
|
||||
it('works with multiple markers', function()
|
||||
local bufnr = api.nvim_get_current_buf()
|
||||
eq(
|
||||
vim.fs.joinpath(test_source_path, 'test/functional/fixtures'),
|
||||
exec_lua([[return vim.fs.root(..., {'CMakeLists.txt', '.git'})]], bufnr)
|
||||
)
|
||||
end)
|
||||
|
||||
it('works with a function', function()
|
||||
---@type string
|
||||
local result = exec_lua([[
|
||||
return vim.fs.root(0, function(name, path)
|
||||
return name:match('%.txt$')
|
||||
end)
|
||||
]])
|
||||
eq(vim.fs.joinpath(test_source_path, 'test/functional/fixtures'), result)
|
||||
end)
|
||||
|
||||
it('works with a filename argument', function()
|
||||
eq(test_source_path, exec_lua([[return vim.fs.root(..., '.git')]], nvim_prog))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('joinpath()', function()
|
||||
it('works', function()
|
||||
eq('foo/bar/baz', vim.fs.joinpath('foo', 'bar', 'baz'))
|
||||
|
Loading…
Reference in New Issue
Block a user