This plugin aims to decrease the difficulties of path management across mutliple OSs in neovim. The plugin API is heavily inspired by Python's pathlib.Path
with tweaks to fit neovim usage.
- Documentation
- Module References
PathlibPath
: base class with operations.PathlibPosixPath
: posix system specific.PathlibWindowsPath
: posix system specific.
- π Search for Keyword
local Path = require("pathlib")
local dir = Path("~/Documents") -- Same as `Path.home() / "Documents"`
local foo = dir / "foo.txt"
print(foo:basename(), foo:stem(), foo:suffix()) -- foo.txt, foo, .txt
print(foo:parent()) -- "/home/user/Documents"
local git_root = Path("/path/to/git/workdir")
assert(git_root:child(".git"):exists(), string.format("%s is not a git repo.", git_root))
require("pathlib.git").fill_git_state({ file_a, file_b, ... })
file_a.git_state.ignored -- is git ignored
file_a.git_state.status -- git status (modified, added, staged, ...)
file_a.git_state.git_root -- root directory of the repo
The API is designed so it is very easy to switch between sync and async operations. Call them inside a nvim-nio async context without any change, and the operations are converted to be async (does not block the main thread).
local foo = Path("~/Documents/foo.txt")
local content = "File Content\n"
-- # sync
local sync_bytes = foo:fs_write(content)
assert(sync_bytes == content:len(), foo.error_msg)
-- # async
require("nio").run(function()
local async_bytes = foo:fs_write(content)
assert(async_bytes == content:len(), foo.error_msg)
end)
local Path = require("pathlib")
local cwd = Path.cwd()
vim.print(string.format([[cwd: %s]], cwd))
-- Use __div to chain file tree!
local folder = Path(".") / "folder"
local foo = folder / "foo.txt"
assert(tostring(foo) == "folder/foo.txt") -- $PWD/folder/foo.txt
assert(tostring(foo:parent()) == "folder")
-- Path object is comparable
assert(foo == Path("./folder/foo.txt")) -- Path object can be created with arguments
assert(foo == Path(folder, "foo.txt")) -- Unpack any of them if you want!
-- Calculate relativily
assert(foo:is_relative_to(Path("folder")))
assert(not foo:is_relative_to(Path("./different folder")))
assert(foo:relative_to(folder) == Path("foo.txt"))
- Very fast operations to work with parents / children / siblings.
- No need to worry about path separator => OS Independent.
/
: Unix,\
: Windows
There are wrappers around vim functions such as fnamemodify
, stdpath
and getcwd
.
path:modify(":p:t:r") -- vim.fn.fnamemodify
-- Define child directory of stdpaths
Path.stdpath("data", "mason", "bin") -- vim.fn.stdpath("data") .. "/mason/bin"
local luv = vim.loop
local Path = require("pathlib")
-- Create new folder
local new_file = Path.new("./new/folder/foo.txt")
new_file:parent_assert():mkdir(Path.permission("rwxr-xr-x"), true) -- (permission, recursive)
-- Create new file and write to it
local fd = new_file:fs_open("w", Path.permission("rw-r--r--"), true)
assert(fd ~= nil, "File creation failed. " .. new_file.error_msg)
luv.fs_write(fd, "File Content\n")
luv.fs_close(fd)
-- HINT: new_file:fs_write(...) does this all at once.
-- SHORTHAND: read file content with `io.read`
local content = new_file:io_read()
assert(content == "File Content\n")
-- SHORTHAND: write to file
new_file:io_write("File Content\n")
new_file:copy(new_file:with_basename("bar.txt")) -- copy `foo.txt` to `bar.txt`
new_file:symlink_to(new_file:with_basename("baz.txt")) -- create symlink of `foo.txt` named `baz.txt`
-- Continue from above
for path in new_file:parent_assert():fs_iterdir() do
-- loop: [Path("./new/folder/foo.txt"), Path("./new/folder/bar.txt"), Path("./new/folder/baz.txt")]
end
This library uses nvim-nio under the hood to run async calls. Supported methods will turn into async calls inside a nio.run
async context and has the EXACT SAME INTERFACE.
local nio = require("nio")
local path = Path("foo.txt")
nio.run(function() -- async run (does not block the main thread)
vim.print(path:fs_stat()) -- coroutine (async)
path:fs_write("File Content\n") -- coroutine (async)
vim.print(path:fs_read()) -- coroutine (async)
vim.print("async done") -- prints last
end)
vim.print("sync here") -- prints first (maybe not if above functions end very fast)
When execution fails, function will return nil
and the error message is captured into self.error_msg
. This property holds the error message of the latest async function call.
nio.run(function ()
local path = Path("./does/not/exist.txt")
local fd = path:fs_open("r")
assert(fd, "ERROR: " .. path.error_msg)
-- fd will be nil when `:fs_open` fails. Check `self.error_msg` for the error message.
end)
- API documentation.
- PathlibPath
- PathlibPosixPath
- PathlibWindowsPath
- Git
- Git operation integration.
- Git test suite.
- List out every possible git state: ignored, staged etc.
- Create file for each state.
- Add docs for each state:
man git-diff -> RAW OUTPUT FORMAT
- Windows implementation, test environment.
- Create a CI/CD action to run on windows.
- Prepare windows specific test suite.
I'll happily accept any feature request Feel free to ask for any functionality :)
- Python
pathlib