fix(fs): make vim.fs.root work for relative paths and unnamed buffers (#28964)

If a buffer does not have a backing file then fall back to the current
working directory.
This commit is contained in:
Gregory Anders 2024-05-24 10:48:32 -05:00 committed by GitHub
parent e71713ba2b
commit 206f8f24a2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 39 additions and 9 deletions

View File

@ -3012,7 +3012,10 @@ vim.fs.parents({start}) *vim.fs.parents()*
vim.fs.root({source}, {marker}) *vim.fs.root()*
Find the first parent directory containing a specific "marker", relative
to a buffer's directory.
to a file path or buffer.
If the buffer is unnamed (has no backing file) or has a non-empty
'buftype' then the search begins from Nvim's |current-directory|.
Example: >lua
-- Find the root of a Python project, starting from file 'main.py'
@ -3029,7 +3032,8 @@ vim.fs.root({source}, {marker}) *vim.fs.root()*
Parameters: ~
• {source} (`integer|string`) Buffer number (0 for current buffer) or
file path to begin the search from.
file path (absolute or relative to the |current-directory|)
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

View File

@ -328,8 +328,11 @@ function M.find(names, opts)
return matches
end
--- Find the first parent directory containing a specific "marker", relative to a buffer's
--- directory.
--- Find the first parent directory containing a specific "marker", relative to a file path or
--- buffer.
---
--- If the buffer is unnamed (has no backing file) or has a non-empty 'buftype' then the search
--- begins from Nvim's |current-directory|.
---
--- Example:
---
@ -346,13 +349,13 @@ end
--- end)
--- ```
---
--- @param source integer|string Buffer number (0 for current buffer) or file path to begin the
--- search from.
--- @param source integer|string Buffer number (0 for current buffer) or file path (absolute or
--- relative to the |current-directory|) 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.
--- found.
function M.root(source, marker)
assert(source, 'missing required argument: source')
assert(marker, 'missing required argument: marker')
@ -361,14 +364,18 @@ function M.root(source, marker)
if type(source) == 'string' then
path = source
elseif type(source) == 'number' then
path = vim.api.nvim_buf_get_name(source)
if vim.bo[source].buftype ~= '' then
path = assert(vim.uv.cwd())
else
path = vim.api.nvim_buf_get_name(source)
end
else
error('invalid type for argument "source": expected string or buffer number')
end
local paths = M.find(marker, {
upward = true,
path = path,
path = vim.fn.fnamemodify(path, ':p:h'),
})
if #paths == 0 then

View File

@ -310,6 +310,25 @@ describe('vim.fs', function()
it('works with a filename argument', function()
eq(test_source_path, exec_lua([[return vim.fs.root(..., '.git')]], nvim_prog))
end)
it('works with a relative path', function()
eq(
test_source_path,
exec_lua([[return vim.fs.root(..., '.git')]], vim.fs.basename(nvim_prog))
)
end)
it('uses cwd for unnamed buffers', function()
command('new')
eq(test_source_path, exec_lua([[return vim.fs.root(0, '.git')]]))
end)
it("uses cwd for buffers with non-empty 'buftype'", function()
command('new')
command('set buftype=nofile')
command('file lua://')
eq(test_source_path, exec_lua([[return vim.fs.root(0, '.git')]]))
end)
end)
describe('joinpath()', function()