A Neovim plugin for managing persistent, project-scoped file and directory bookmarks.
- Project-aware: Automatically detects project boundaries and scopes marks per project
- Persistent: Marks survive across Neovim sessions
- File and directory support: Bookmark both files and directories
- Automatic keybindings: Creates jump keybindings dynamically as you add marks
- Buffer-based editor: Edit all marks for a project in a dedicated buffer
- Relative paths: Stores paths relative to project root for portability
Comparison with Vim's built-in marks
| Feature | Vim marks | filemarks.nvim |
|---|---|---|
| Persist across sessions | Limited (uppercase marks only) | Yes (all marks) |
| Project-scoped | No | Yes |
| Directory support | No | Yes |
| Custom keybindings | No | Yes |
| Buffer-based editor | No | Yes |
| Relative paths | No | Yes |
Using neovim native vim.pack
vim.pack.add({ src = "https://github.com/anoopkcn/filemarks.nvim" })
require('filemarks').setup()Using lazy.nvim
{
'anoopkcn/filemarks.nvim',
config = function()
require('filemarks').setup()
end
}Using packer.nvim
use {
'anoopkcn/filemarks.nvim',
config = function()
require('filemarks').setup()
end
}Using vim-plug
Plug 'anoopkcn/filemarks.nvim'
lua << EOF
require('filemarks').setup()
EOFAfter installation, the plugin provides default keybindings (using <leader>):
In the examples the {key} can be any single character (a-z, 0-9, punctuation).
Action keybindings:
<leader>Ma- Add filemark for current file<leader>Md- Add directory mark<leader>Mr- Remove filemark<leader>Ml- List/edit filemarks for current project
Jump keybindings (created automatically):
<leader>m{key}- Jump to mark{key}
" 1. Open your main project file
:edit src/main.lua
" 2. Mark it with key 'm'
:FilemarksAdd m
" or press <leader>Ma and enter 'm'
" 3. Later, from anywhere in the project, jump back
<leader>mm
" Opens src/main.lua instantlyDefault configuration:
require('filemarks').setup({
goto_prefix = "<leader>m", -- Prefix for jump keybindings
action_prefix = "<leader>M", -- Prefix for action keybindings
storage_path = vim.fn.stdpath("state") .. "/filemarks.json",
project_markers = { ".git", ".hg", ".svn" }, -- Files/dirs that mark project root
-- Command (string) or function used to open directory marks.
-- Examples:
-- "Explore" -- netrw
-- "Oil %s" -- stevearc/oil.nvim
-- function(path) vim.cmd("Neotree " .. vim.fn.fnameescape(path)) end
dir_open_cmd = nil, -- If nil, shows "file explorer not set" for directory marks
-- Command (string) or function used to open the list buffer in a window.
-- Examples:
-- "rightbelow vsplit"
-- function() vim.cmd("topleft split") end
list_open_cmd = nil,
})require('filemarks').setup({
goto_prefix = "'", -- Use ' like Vim's built-in marks
action_prefix = "<leader>b", -- Custom action prefix
})Set a prefix to empty string to disable that category of keybindings:
require('filemarks').setup({
goto_prefix = "", -- Disable jump keybindings
action_prefix = "", -- Disable action keybindings
})require('filemarks').setup({
project_markers = { ".git", "package.json", "Cargo.toml", "pyproject.toml" },
})require('filemarks').setup({
-- Open the list in a rightbelow vertical split by default
list_open_cmd = "rightbelow vsplit",
})Add or update a filemark.
:FilemarksAdd " Prompt for key, mark current file
:FilemarksAdd m " Mark current file as 'm'
:FilemarksAdd c src/config.lua " Mark config.lua as 'c'Add or update a directory mark.
:FilemarksAddDir " Prompt for key, mark current directory context
:FilemarksAddDir d " Mark current directory as 'd'
:FilemarksAddDir t tests/ " Mark tests/ directory as 't'When called without a directory path:
- Uses netrw directory if in a netrw buffer
- Falls back to current file's directory
- Falls back to current working directory
Opening a directory mark uses dir_open_cmd. If it is nil, you'll see
"Filemarks: file explorer not set". Set it to "Explore", "Oil %s", or a
custom function to choose your file viewer.
Remove a filemark from the current project.
:FilemarksRemove " Prompt for key
:FilemarksRemove m " Remove mark 'm'Open an editor buffer to view and edit all marks for the current project.
:FilemarksList
:rightbelow vertical FilemarksList " Use command modifiers to choose the split/tabThe editor buffer format:
# Filemarks for /path/to/project
# Format: <key><space><path>. Lines starting with # are comments.
# Directories are shown with a trailing /
# Delete/Comment a line to remove it. Save to persist changes.
c src/config.lua
d tests/
m main.go
Edit the buffer and save (:w) to persist changes. Keybindings are automatically updated.
Jump to a mark (typically accessed via jump keybindings instead).
:FilemarksOpen m " Jump to mark 'm'" Mark key files in your project
m -> src/main.rs (main entry point)
c -> config/settings.toml (configuration)
r -> src/routes.rs (routing)
t -> tests/ (test directory)Marks are project-specific, so you can use the same keys across different projects:
Project A:
m -> app/main.js
c -> config/app.json
Project B:
m -> cmd/main.go
c -> config.yaml
" In a directory you frequently visit
:FilemarksAddDir d
" Jump back anytime
<leader>md
" Opens directory with your configured explorer (see dir_open_cmd)Initialize the plugin with configuration options.
require('filemarks').setup({
goto_prefix = "<leader>m",
})Programmatically add a filemark.
local filemarks = require('filemarks')
-- Mark current file
filemarks.add('m')
-- Mark specific file
filemarks.add('c', 'src/config.lua')Programmatically add a directory mark.
filemarks.add_dir('d', 'tests/')Remove a filemark.
filemarks.remove('m')Open the marks editor.
filemarks.list()Jump to a mark.
filemarks.open('m')Filemarks are stored in a JSON file at the configured storage_path (default: ~/.local/state/nvim/filemarks.json).
The storage format:
{
"/absolute/path/to/project1": {
"m": "src/main.lua",
"c": "config/init.lua",
"d": "tests/"
},
"/absolute/path/to/project2": {
"m": "main.go",
"t": "main_test.go"
}
}To sync marks across machines:
- Set
storage_pathto a synced directory:
require('filemarks').setup({
storage_path = "~/Dropbox/.config/nvim/filemarks.json",
})- Ensure project paths are consistent across machines, or use relative paths within projects.
MIT
harpoon - Getting you where you want with the fewest keystrokes.