Inline code review on any git diff, right inside Neovim.
Review workflow — Two-panel layout (explorer + unified diff), markdown export with prompt flow or direct :W save, git difftool --dir-diff integration.
Inline notes — Smart add/edit on any diff line, visual-selection notes with captured code context, virtual text with visibility toggle, Telescope picker for all notes.
Navigation — File explorer with badges and note counts, ]n/[n and ]f/[f bracket motions, ? help window with all keymaps.
Safety — Unsaved-note protection on close, large diff pagination with configurable thresholds.
Running :w or :W generates a Markdown file with each note grouped under its file path, including the relevant code context:
# Review 2026-03-14
`src/foo.js`
```text {0,1}
const result = a + b;
```
revisit this calculation
`handlers/user.js`
```text {67,72}
function handleUser(user) {
if (user.name) {
return user.name;
}
}
```
Add a null check for `user` before accessing `.name`- Install the plugin (see Installation)
- Open a review:
:CodeReview - Navigate files with
j/k, pressnon any diff line to add a note, export with:w
{
"MaraniMatias/codereview.nvim",
dependencies = {
"nvim-telescope/telescope.nvim", -- optional: enables notes picker (<Space>n)
"nvim-tree/nvim-web-devicons", -- optional: file icons in the explorer
},
config = function()
require("codereview").setup({})
end,
}:CodeReview accepts any arguments you would pass to git diff.
:CodeReview " unstaged changes
:CodeReview main..feature " branch comparison
:CodeReview HEAD~3 " last 3 commits
:CodeReview --staged " staged changes only
:CodeReview -- path/to/file " single file
:CodeReview --staged -- path/to/file " staged + single file| Command | Effect |
|---|---|
:w |
Opens save prompt, writes the markdown review |
:W |
Saves directly to the auto-generated filename |
:q |
Warns if you have unsaved notes |
:q! |
Forces the review tab to close |
Notes live in memory for the current session only; exporting saves the Markdown review, not the in-editor note state.
Add this to ~/.gitconfig:
[difftool "codereview"]
cmd = nvim -c "lua require('codereview').difftool('$LOCAL', '$REMOTE')"
trustExitCode = true
[difftool]
prompt = falseThen run:
git difftool --dir-diff -t codereview
git difftool --dir-diff --cached -t codereview
git difftool --dir-diff -t codereview main..feature-branch--dir-diff gives the plugin all changed files at once, enabling the multi-file explorer.
Alternative: wrapper script
You can also point difftool at the wrapper shipped in bin/codereview:
[difftool "codereview"]
cmd = /path/to/codereview/bin/codereview "$LOCAL" "$REMOTE"
trustExitCode = trueThe wrapper automatically captures $MERGED from git's environment to build a stable, repo-relative file identity — preventing note collisions when multiple files share the same basename (e.g. src/utils/helpers.js vs src/components/helpers.js).
All keybindings are remappable via keymaps in your setup config.
| Key | Action |
|---|---|
j / k |
Navigate files and note entries |
Enter / l |
Focus the diff panel for the selected item |
h |
Toggle notes for the selected file |
za |
Expand or collapse notes for the selected file |
]f / [f |
Next or previous file |
R |
Refresh file list |
<Tab> |
Focus diff panel |
? |
Show help window |
q |
Close review |
| Key | Action |
|---|---|
n |
Smart add or edit note on current line |
V then n |
Add note from visual selection |
]n / [n |
Next or previous note in current file |
]f / [f |
Next or previous file |
L |
Load more lines for a truncated diff |
<leader>uh |
Toggle virtual text notes |
<Space>n |
Open Telescope notes picker |
<Tab> |
Focus explorer panel |
q |
Close review |
| Key | Action |
|---|---|
w |
Save note (normal mode) |
q |
Discard note without asking |
<Esc> |
Ask to save or discard |
Most users won't need any config — defaults are tuned for a typical review workflow.
require("codereview").setup({
explorer_width = 30,
keymaps = {
save = "<C-s>", -- disabled by default; set to enable a save shortcut
},
})require("codereview").setup({
diff_view = "unified", -- "split" is planned, not implemented yet
explorer_width = 30, -- width of the file explorer panel
border = "rounded", -- "rounded" | "single" | "double" | "solid" | "none"
explorer_title = " Files ",
diff_title = " Diff ",
note_truncate_len = 30, -- truncation of notes in explorer sub-items
virtual_text_truncate_len = 60, -- truncation of virtual text annotations
max_diff_lines = 1200, -- initial visible diff lines before truncation
diff_page_size = 400, -- extra lines revealed per load-more action
keymaps = {
note = "n", -- smart add/edit note on current line
toggle_virtual_text = "<leader>uh",
next_note = "]n",
prev_note = "[n",
next_file = "]f",
prev_file = "[f",
cycle_focus = "<Tab>",
save = false, -- set to e.g. "<C-s>" to enable a save shortcut
notes_picker = "<Space>n",
quit = "q",
toggle_notes = "za",
refresh = "R",
load_more_diff = "L",
},
review = {
default_filename = "review-%Y-%m-%d.md",
path = nil, -- nil = git root
context_lines = 0, -- extra lines above/below when auto-reading code from disk
},
})Large diffs keep the current behavior when they fit within max_diff_lines. When a diff exceeds that limit, CodeReview renders the first slice, shows a truncation sentinel at the bottom, and each load_more_diff action reveals another diff_page_size lines.
diff_view = "split"is not implemented yet- Notes are session-only; closing CodeReview discards in-memory notes unless you export the review
- Note anchors are based on new-file line numbers
This plugin was inspired by the talk "Programar en 2026: the human-in-the-loop" at JSConf ES by Javi Velasco @javivelasco.
