diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index fccda177d2..3d8453c5a2 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -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 diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index ce533ad0a9..b05220ee2c 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -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 diff --git a/test/functional/lua/fs_spec.lua b/test/functional/lua/fs_spec.lua index b651297db3..aba02ab01b 100644 --- a/test/functional/lua/fs_spec.lua +++ b/test/functional/lua/fs_spec.lua @@ -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()