Annotate code with review comments directly in Neovim. Like GitHub PR reviews, but local, offline, and stored as JSON files alongside the code.
No external dependencies. Pure Lua + Neovim API.
- Line & range annotations -- annotate a single line or a visual selection
- Floating window viewer -- see all reviews on the current line with timestamps and git info
- Virtual text hints -- inline indicators showing review count per line
- Staleness detection -- reviews track the git commit they were created on and flag when HEAD moves
- Navigation -- jump between reviewed lines with
]r/[r(wraps around) - Picker integration -- search all reviews across the project with Telescope or snacks.nvim (optional, neither required)
- File-local storage -- reviews stored as hidden JSON files next to the source, easy to gitignore or share
- Neovim >= 0.9
- Optional: telescope.nvim or snacks.nvim (picker) for project-wide search
{
"aneveux/localreview.nvim",
config = function()
require("localreview").setup()
end,
}Clone or add this repo to your runtimepath, then call:
require("localreview").setup()| Key | Mode | Action |
|---|---|---|
<leader>ra |
n, v | Annotate current line or visual selection |
<leader>rv |
n | View reviews on current line |
<leader>rd |
n | Delete review on current line |
]r |
n | Jump to next review |
[r |
n | Jump to previous review |
<leader>rt |
n | Open review picker (auto-detects Telescope or snacks.nvim) |
To disable all default keybindings:
require("localreview").setup({ keys = false })| Command | Description |
|---|---|
:LocalReviewAnnotate |
Add review (supports :'<,'>LocalReviewAnnotate for ranges) |
:LocalReviewView |
View reviews on current line |
:LocalReviewDelete |
Delete review on current line |
:LocalReviewNext |
Jump to next review |
:LocalReviewPrev |
Jump to previous review |
:LocalReviewPick |
Open review picker (auto-detects Telescope or snacks.nvim) |
:LocalReviewTelescope |
Telescope picker (requires telescope.nvim) |
All options with defaults:
require("localreview").setup({
keys = {
annotate = "<leader>ra",
view = "<leader>rv",
delete = "<leader>rd",
next_review = "]r",
prev_review = "[r",
telescope = "<leader>rt",
},
-- Picker backend for project-wide search: "auto" | "telescope" | "snacks"
-- "auto" prefers snacks.nvim, then telescope, whichever is loaded
picker = "auto",
virtual_text = {
enabled = true,
hl_group = "Comment",
stale_hl_group = "LocalReviewStale",
},
git = {
track_commit = true,
},
}):LocalReviewPick (and <leader>rt) open a project-wide review picker. The backend is
auto-detected β snacks.nvim if loaded, otherwise Telescope. Force one with picker = "snacks"
or picker = "telescope" in setup().
No setup needed. If snacks.nvim is installed with the
picker enabled, :LocalReviewPick uses it automatically.
Optionally load the extension after telescope to also get :Telescope localreview:
require("telescope").load_extension("localreview")Then use :Telescope localreview, :LocalReviewPick, or the <leader>rt keybinding.
The thorn plugin for Claude Code can process and summarize .reviews.json files, letting you discuss review annotations with Claude directly in your terminal.
Reviews are stored as hidden JSON files next to each source file:
foo.lua -> .foo.lua.reviews.json
To ignore review files in version control:
*.reviews.jsonTo share reviews with your team, commit them instead.
- Line drift: Reviews are stored by line number. Within a session, virtual text tracks edits (insertions/deletions above a review move the indicator). However, when you reopen the file, reviews appear at their original stored line numbers because the JSON file is not updated as lines shift. If you add 10 lines above a review, it will point at the wrong line on next open. This is a known trade-off for file-local, dependency-free storage.
# Install test dependency
make deps
# Run tests
make test
# Format code
make formatMIT