https://github.com/adrianmross/review-mode.nvim
Fast GitHub pull request review mode for ordinary Neovim buffers
https://github.com/adrianmross/review-mode.nvim
code-review github neovim nvim-plugin pull-request
Last synced: 4 days ago
JSON representation
Fast GitHub pull request review mode for ordinary Neovim buffers
- Host: GitHub
- URL: https://github.com/adrianmross/review-mode.nvim
- Owner: adrianmross
- License: mit
- Created: 2026-05-26T22:43:04.000Z (30 days ago)
- Default Branch: main
- Last Pushed: 2026-06-11T00:05:05.000Z (15 days ago)
- Last Synced: 2026-06-11T00:11:42.133Z (15 days ago)
- Topics: code-review, github, neovim, nvim-plugin, pull-request
- Language: Lua
- Homepage:
- Size: 212 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# review-mode.nvim
Fast GitHub pull request review mode for ordinary Neovim buffers.
The goal is to keep review inside normal files instead of a dedicated diff UI:
- opens the first changed file when review mode starts
- uses Gitsigns against the PR base branch for gutter changes
- jumps between PR hunks, PR comments, and changed files
- shows changed files/folders in `nvim-tree`
- loads GitHub review comments asynchronously with a small disk cache
- tracks viewed/unviewed PR files locally, with optional GitHub-backed viewed sync
- opens the base version of the current file in a side-by-side diff split
- creates line or visual-range PR comments and suggestions through `gh`
- opens quick PR actions for status, checks, browser handoff, URL copy, and thread resolution
- can use snacks.nvim or Telescope for action and viewed-file pickers, with the native picker as fallback
## Requirements
- Neovim 0.10+
- `git`
- GitHub CLI `gh`, authenticated for the target repository
- optional: `lewis6991/gitsigns.nvim`
- optional: `nvim-tree/nvim-tree.lua`
- optional: `folke/snacks.nvim` or `nvim-telescope/telescope.nvim` for picker UI
The plugin assumes the current checkout is a PR branch and compares
`origin/...HEAD`, where `` comes from `gh pr view`.
## Install
With `lazy.nvim`:
```lua
{
"adrianmross/review-mode.nvim",
dependencies = {
"lewis6991/gitsigns.nvim",
},
opts = {},
keys = {
{ "rm", "ReviewMode", desc = "Review mode" },
{ "rN", "ReviewModeStop", desc = "Review mode stop" },
{ "ra", "ReviewModeActions", desc = "Review actions" },
{ "rd", "ReviewModeOldToggle", desc = "Review diff" },
{ "rD", "ReviewModeDiffLayoutToggle", desc = "Review diff layout" },
{ "rf", "ReviewModeDiffFullToggle", desc = "Review diff full file" },
{ "rv", "ReviewModeViewedToggle", desc = "Toggle file viewed" },
{ "rl", "ReviewModeViewedList", desc = "Review viewed files list" },
{ "rV", "ReviewModeViewedFeatureToggle", desc = "Review toggle viewed state" },
{ "rC", "ReviewModeCommentsToggle", desc = "Review toggle comments" },
{ "rs", "ReviewModeViewedSync", desc = "Review sync viewed" },
{ "rS", "ReviewModeViewedSyncToggle", desc = "Review toggle viewed sync" },
{ "rc", "ReviewModeThread", desc = "Review line comments" },
{ "rr", "ReviewModeReply", desc = "Review reply" },
{ "rR", "ReviewModeResolveThread", desc = "Review resolve thread" },
{
"rp",
function()
require("review_mode").comment()
end,
mode = { "n", "v" },
desc = "Review comment",
},
{ "]h", "ReviewModeNextHunk", desc = "Next PR hunk" },
{ "[h", "ReviewModePrevHunk", desc = "Previous PR hunk" },
{ "]c", "ReviewModeNextComment", desc = "Next PR comment" },
{ "[c", "ReviewModePrevComment", desc = "Previous PR comment" },
{ "]f", "ReviewModeNextFile", desc = "Next changed file" },
{ "[f", "ReviewModePrevFile", desc = "Previous changed file" },
},
}
```
Suggested navigation uses hunk keys for changed regions and comment keys for
review discussion:
- `]h` / `[h` jump to the next/previous PR hunk
- `]c` / `[c` jump to the next/previous PR comment
- `]f` / `[f` jump to the next/previous changed file
## nvim-tree Integration
To decorate changed files and parent directories in `nvim-tree`, include the
decorator in your `nvim-tree` setup:
```lua
require("nvim-tree").setup({
renderer = {
decorators = {
"Git",
"Open",
"Hidden",
"Modified",
"Bookmark",
"Diagnostics",
"Copied",
require "review_mode.integrations.nvim_tree",
"Cut",
},
},
})
```
Unviewed changed files and closed parent folders are marked with `☐ N`, where
`N` is the number of unviewed changed files under that node. Viewed files and
closed folders are marked with `✓`; files and closed folders with unresolved
comments are also marked with ` N`. An open folder hides these folder markers
because its children show the same state inline. A folder switches to viewed
after every changed file under it is viewed.
## Commands
- `:ReviewMode` starts normal-buffer Review Mode
- `:ReviewModeActions` opens an action picker for common PR actions, using the configured picker provider
- `:ReviewModeBrowser` opens the current PR in your browser
- `:ReviewModeCopyUrl` copies the current PR URL to registers
- `:ReviewModeChecks` shows `gh pr checks` output in a floating preview
- `:ReviewModeStatus` shows current PR status in a floating preview
- `:ReviewModeStop` stops review mode and clears plugin state
- `:ReviewModeRefresh` reloads changed files and comments
- `:ReviewModeNextHunk` jumps to the next PR hunk
- `:ReviewModePrevHunk` jumps to the previous PR hunk
- `:ReviewModeNextComment` jumps to the next PR comment
- `:ReviewModePrevComment` jumps to the previous PR comment
- `:ReviewModeNextFile` jumps to the next changed file
- `:ReviewModePrevFile` jumps to the previous changed file
- `:ReviewModeOldToggle` toggles the base version or unified diff for the current file
- `:ReviewModeDiffLayoutToggle` toggles the open diff between side-by-side and unified layout
- `:ReviewModeDiffFullToggle` toggles the open diff between condensed context and full-file context
- `:ReviewModeThread` shows comments on the current line
- `:ReviewModeReply` replies to the latest comment on the current line
- `:ReviewModeResolveThread` resolves the PR review thread on the current line
- `:ReviewModeUnresolveThread` unresolves the PR review thread on the current line
- `:ReviewModeComment` creates a PR comment on the current line or visual range
- `:ReviewModeSuggest` creates a GitHub suggestion comment on the current line or visual range
- `:ReviewModeViewedToggle` toggles viewed state for the current PR file
- `:ReviewModeViewedNext` marks the current PR file viewed and jumps to the next unviewed file
- `:ReviewModeViewedFeatureToggle` toggles viewed-state tracking on or off
- `:ReviewModeCommentsToggle` toggles PR comments on or off
- `:ReviewModeViewedList [all|viewed|unviewed]` opens a fuzzy PR file menu with diff stats and preview; press `Space` or `t` in the native picker, or ``/`` in external pickers, to toggle viewed state
- `:ReviewModeViewedClear` clears local viewed state for the current PR
- `:ReviewModeViewedSync` pulls viewed state from GitHub
- `:ReviewModeViewedSyncToggle` toggles GitHub viewed-state sync
- `:ReviewModeSummary` shows file, comment, thread, and viewed-sync counts.
## gh-dash / Worktree Handoff
For external launchers, set `GH_REVIEW_REPO` and `GH_REVIEW_PR` before opening
Neovim, then run `+ReviewMode`:
```sh
GH_REVIEW_REPO=adrianmross/example GH_REVIEW_PR=123 nvim +ReviewMode
```
If those variables are not set, the plugin asks `gh` for the current repo and PR.
## Picker Providers
ReviewMode uses its native picker by default, but `picker.provider = "auto"`
will use snacks.nvim when available, then Telescope, then the native picker.
Set `picker.provider = "native"`, `"snacks"`, or `"telescope"` to prefer a
specific provider. `:ReviewModeActions` uses the provider for the action list,
and `:ReviewModeViewedList` uses it for changed-file search and diff preview.
## Options
```lua
require("review_mode").setup({
auto_open_first_change = true,
comments = {
enabled = true,
cache_ttl_seconds = 300,
sign_text = "",
sign_hl_group = "DiagnosticInfo",
virtual_text = true,
},
diff = {
fast_diffopt = "internal,filler,closeoff,indent-heuristic,linematch:0",
full_file = false,
layout = "side_by_side",
partial_line_highlights = true,
unified_context = 3,
use_fast_diffopt = true,
},
gitsigns = {
enabled = true,
},
nvim_tree = {
enabled = true,
show_comments = true,
show_viewed = true,
},
picker = {
provider = "auto", -- "auto" | "native" | "snacks" | "telescope"
},
viewed = {
enabled = true,
sync = false,
state_path = nil,
},
performance = {
ui_refresh_debounce_ms = 50,
hunk_prefetch = {
enabled = true,
count = 8,
concurrency = 2,
focused_delay_ms = 0,
gitsigns_delay_ms = 5,
},
background_hunk_scan = {
enabled = true,
max_files = 5000,
delay_ms = 250,
},
},
commands = true,
})
```
`ReviewMode` loads PR metadata and changed-file status asynchronously. If
`GH_REVIEW_BASE` is set by a launcher, changed-file loading starts immediately
without waiting for GitHub metadata. Hunk locations are loaded lazily per file,
with immediate focused-file prefetch, opportunistic `gitsigns.nvim` hunk-cache
reuse, and an optional delayed background scan for PRs under
`performance.background_hunk_scan.max_files`.
External launchers can provide `GH_REVIEW_REPO`, `GH_REVIEW_PR`,
`GH_REVIEW_BASE`, and `GH_REVIEW_HEAD` to avoid startup discovery calls.
Viewed state is persisted in `stdpath("state")/review-mode-state.json` by
default. Set `viewed.sync = true` or run `:ReviewModeViewedSyncToggle` to pull
GitHub's PR file viewed state at startup and push local viewed/unviewed toggles
back to GitHub.
The built-in side-by-side old-version split remains the default diff backend.
Set `diff.layout = "unified"` or run `:ReviewModeDiffLayoutToggle` to use an
inline unified diff buffer in the current window instead. Closing unified mode
restores the original file buffer. Set `diff.full_file = true` or run
`:ReviewModeDiffFullToggle` to show full-file context; condensed unified diffs use
`diff.unified_context` common lines around each hunk, and condensed side-by-side
diffs fold unchanged regions in both diff windows. When `diff.use_fast_diffopt`
is enabled, side-by-side diffs temporarily apply `diff.fast_diffopt`, then
restore the previous `diffopt` when the split closes. Unified diffs highlight
changed spans inside modified lines with `DiffText`; set
`diff.partial_line_highlights = false` to disable those inline spans.
## Notes
GitHub only accepts review comments on diff lines. If you comment on a line that
is not part of the PR diff, GitHub may reject the request.
## Release Workflow
Pull requests run the same validation as local development with
`devenv test --no-eval-cache`. Behavior, command, config, validation, and
release-infrastructure changes also need a release-bearing Conventional Commit
such as `fix:`/`feat:`/`perf:`, a `.changeset/*.md` file, or a direct
`CHANGELOG.md` update so release intent is visible during review.
Release Please owns the final release PR, changelog update, tag, and GitHub
Release. The current released version is tracked in
`.release-please-manifest.json`; `scripts/release-check.sh` verifies that the
manifest, latest changelog section, and release tag agree.
Local release checks:
```sh
devenv test --no-eval-cache
bash scripts/release-check.sh
```