nvim_runtime_lua

vim.fs

Methods14

function M.parents(start: string) -> (fun(_, dir: string) -> nil | string, nil, nil | string)

Iterate over all the parents of the given path (not expanded/resolved, the caller must do that).

Example:

local root_dir
for dir in vim.fs.parents(vim.api.nvim_buf_get_name(0)) do
  if vim.fn.isdirectory(dir .. '/.git') == 1 then
    root_dir = dir
    break
  end
end

if root_dir then
  print('Found git repository at', root_dir)
end
Parameters
startstring

Initial path.

Returns
fun(_, dir: string) -> nil | string

Iterator

nil
nil | string

@since 10

function M.dirname<T>(file: T) -> T

Gets the parent directory of the given path (not expanded/resolved, the caller must do that).

Parameters
fileT

Path

Returns
T

Parent directory of file

@since 10

function M.basename<T>(file: T) -> T

Gets the basename of the given path (not expanded/resolved).

Parameters
fileT

Path

Returns
T

Basename of file

@since 10

function M.joinpath(...: string) -> string

Concatenates partial paths (one absolute or relative path followed by zero or more relative paths). Slashes are normalized: redundant slashes are removed, and (on Windows) backslashes are replaced with forward-slashes. Empty segments are removed. Paths are not expanded/resolved.

Examples:

  • "foo/", "/bar" => "foo/bar"
  • "", "after/plugin" => "after/plugin"
  • Windows: "afoo", "bar" => "a/foo/bar"

@since 12

function M.slug(path: string, opts: sub<table,maxlen> | nil) -> string

Gets a filesystem-safe, mnemonic slug (readable prefix + short hash) of an arbitrary filepath or other "identity string".

  • The input is normalized so equivalent paths produce the same result.
  • A hash of the normalized input is appended to prevent collisions.
  • Unsafe chars are replaced with "-".
  • $HOME is replaced with "~".
  • UNC paths (Windows) are prefixed with "=unc-".
  • If opts.maxlen is exceeded, the result will be truncated to {head}~~~{tail}-{hash8}.
  • If the sanitized name is empty, the reserved label =special will be used.

Examples:

vim.print(vim.fs.slug('/tmp/test/foo.md'))           --> "tmp-test-foo.md-{hash}"
vim.print(vim.fs.slug('C:/src/project/main.c'))      --> "C--src-project-main.c-{hash}"
vim.print(vim.fs.slug(vim.fn.expand('~/file.txt')))  --> "~-file.txt-{hash}"
vim.print(vim.fs.slug('---'))                        --> "=special-{hash}"
vim.print(vim.fs.slug(('/a/very/long/path'):rep(10) .. '/file.txt', { maxlen = 60 }))
   --> "a-very-long-~~~-path-a-very-long-path-file.txt-{hash}"
Parameters
pathstring

Filepath (or other identity string).

optssub<table,maxlen> | nil

: (integer, default: 180) Max length (bytes) of the result.

Returns
string

Filesystem-safe, mnemonic slug.

@since 15

function M.dir(path: string, opts: vim.fs.dir.Opts | nil) -> fun() -> ...nil | string

Gets an iterator over items found in path (normalized via |vim.fs.normalize()|).

Example:

for name, type, err in vim.fs.dir(path, { err = true }) do
  if err then
    -- Failed to scan directory {name} (may be the root {path} itself).
  end
end
Parameters
pathstring

Directory to iterate over, expanded (unless opts.plain=true) and normalized.

optsvim.fs.dir.Opts | nil
Returns
fun() -> ...nil | string

Iterator over items in {path}, yielding (name, type, err):

  • name: Basename of the item relative to {path}.
  • type: One of: "file", "directory", "link", "fifo", "socket", "char", "block", "unknown".
  • err: Error string, or nil. Only if opts.err=true. If the root {path} itself could not
  • be scanned, yields a single (name, nil, err) item.

@since 10

function M.find(names: string | string[] | fun(name: string, path: string) -> boolean, opts: vim.fs.find.Opts | nil) -> (string[], string[])

Find files or directories (or other items as specified by opts.type) in the given path.

Finds items given in {names} starting from {path}. If {upward} is "true" then the search traverses upward through parent directories; otherwise, the search traverses downward. Note that downward searches are recursive and may search through many directories! If {stop} is non-nil, then the search stops when the directory given in {stop} is reached. The search terminates when {limit} (default 1) matches are found. You can set {type} to "file", "directory", "link", "socket", "char", "block", or "fifo" to narrow the search to find only that type.

Examples:

-- List all test directories under the runtime directory.
local dirs = vim.fs.find(
  { 'test', 'tst', 'testdir' },
  { limit = math.huge, type = 'directory', path = './runtime/' }
)

-- Get all "lib/*.cpp" and "lib/*.hpp" files, using Lua patterns.
-- Or use `vim.glob.to_lpeg(…):match(…)` for glob/wildcard matching.
local files = vim.fs.find(function(name, path)
  return name:match('.*%.[ch]pp$') and path:match('[/\\]lib$')
end, { limit = math.huge, type = 'file' })
Parameters
namesstring | string[] | fun(name: string, path: string) -> boolean

Names of the items to find. Must be base names, paths and globs are not supported when {names} is a string or a table. If {names} is a function, it is called for each traversed item with args:

  • name: base name of the current item
  • path: full path of the current item

The function should return true if the given item is considered a match.

optsvim.fs.find.Opts | nil

Optional keyword arguments:

Returns
string[]

Normalized paths |vim.fs.normalize()| of all matching items.

string[]

Errors collected while searching.

@since 10

function M.root(source: string | integer, marker: string | string[] | fun(name: string, path: string) -> boolean[] | string | fun(name: string, path: string) -> boolean) -> nil | string

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|.

Examples:

-- 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 vim.fs.ext(name) == 'csproj'
end)

-- Find the first ancestor directory containing EITHER "stylua.toml" or ".luarc.json"; if
-- not found, find the first ancestor containing ".git":
vim.fs.root(0, { { 'stylua.toml', '.luarc.json' }, '.git' })
Parameters
sourcestring | integer

Buffer number (0 for current buffer) or file path (absolute or relative, expanded via abspath()) to begin the search from.

markerstring | string[] | fun(name: string, path: string) -> boolean[] | string | fun(name: string, path: string) -> boolean
Returns
nil | string

Directory path containing one of the given markers, or nil if no directory was found.

@since 12

function M.normalize(path: string, opts: vim.fs.normalize.Opts | nil) -> string

Normalize a path to a standard format. Expands environment variables, and tilde "~" at the beginning of the path. Resolves "." and ".." components, except when the path is relative and resolving it would produce an absolute path.

  • "." as the only part in a relative path:
  • "." => "."
  • "././" => "."
  • ".." when it leads outside the current directory
  • "foo/../../bar" => "../bar"
  • "../../foo" => "../../foo"
  • ".." in the root directory returns the root directory.
  • "/../../" => "/"

On Windows, backslashes (\) are converted to forward slashes (/).

Examples:

[[C:\Users\jdoe]]                         --> "C:/Users/jdoe"
"~/src/neovim"                            --> "/home/jdoe/src/neovim"
"$XDG_CONFIG_HOME/nvim/init.vim"          --> "/Users/jdoe/.config/nvim/init.vim"
"~/src/nvim/api/../tui/./tui.c"           --> "/home/jdoe/src/nvim/tui/tui.c"
"./foo/bar"                               --> "foo/bar"
"foo/../../../bar"                        --> "../../bar"
"/home/jdoe/../../../bar"                 --> "/bar"
"C:foo/../../baz"                         --> "C:../baz"
"C:/foo/../../baz"                        --> "C:/baz"
[[\\?\UNC\server\share\foo\..\..\..\bar]] --> "//?/UNC/server/share/bar"
Parameters
pathstring

Path to normalize

Returns
string

: Normalized path

@since 10

function M.mkdir(path: string, opts: vim.fs.mkdir.Opts | nil) -> nil

Creates a directory.

Parameters
pathstring

Path to create (not expanded/resolved).

optsvim.fs.mkdir.Opts | nil

Optional keyword arguments.

Returns
nil

@since 15

function M.rm(path: string, opts: vim.fs.rm.Opts | nil) -> nil

Removes a file or directory.

Removes symlinks without touching the origin. To remove the origin, resolve it explicitly with |uv.fs_realpath()|:

vim.fs.rm(vim.uv.fs_realpath('symlink-dir'), { recursive = true })
Parameters
pathstring

Path to remove (not expanded/resolved).

optsvim.fs.rm.Opts | nil
Returns
nil

@since 13

function M.abspath(path: string, opts: vim.fs.abspath.Opts | nil) -> Absolute string

Converts path to an absolute path. Expands tilde (~) at the beginning of the path (unless plain=true). Does not check if the path exists, normalize the path, resolve symlinks or hardlinks (including "." and ".."), or expand environment variables. If the path is already absolute, it is returned unchanged. Converts \ path separators to /.

Parameters
pathstring

Path

Returns
Absolutestring

path

@since 13

function M.relpath(base: string, target: string, opts: nil | table) -> nil | string

Gets target path relative to base, or nil if base is not an ancestor.

Example:

vim.fs.relpath('/var', '/var/lib') -- 'lib'
vim.fs.relpath('/var', '/usr/bin') -- nil
Parameters
basestring
targetstring
optsnil | table

Reserved for future use

Returns
nil | string

@since 13

function M.ext(file: string, opts: nil | table) -> Extension string

Return the file's last extension, if any.

Similar to |fnamemodify()| with the |::e| modifier. The extension does not include a leading period.

Examples:

vim.fs.ext('archive.tar.gz') -- 'gz'
vim.fs.ext('~/.git') -- ''
vim.fs.ext('plugin/myplug.lua') -- 'lua'
Parameters
filestring

Path

optsnil | table

Reserved for future use

Returns
Extensionstring

of {file}

@since 14