feat: persist local reviews automatically in SQLite
This commit is contained in:
@@ -16,8 +16,8 @@ One editor crate; the workspace also includes its patched Crossterm dependency.
|
||||
Virtual diff leaves retain their original catalog input index for caller-owned annotations. Normal scanned leaves accept optional repository status and old-path metadata without filtering unchanged files; missing tracked deletions add virtual leaves and required directories. Metadata-only updates preserve query, selection, expansion, and view without rebuilding the catalog.
|
||||
- `diff`: owns searchable Before/After selection, category, focus and explorer visibility, request freshness, read-only comparison DTOs, aligned rows with cached grapheme-level change ranges, and shared vertical/independent horizontal offsets. Changed paths form a virtual `files::FilePicker` catalog, reusing the normal tree projection, filtering, and navigation without scanning disk. It has no subprocess or terminal dependencies.
|
||||
- `repository`: discovers jj/Git repositories and local/advertised remote revisions, builds disk snapshots and changed-file hunks, and owns isolated temporary comparison repositories with bounded, cancellable subprocesses. It never saves editor buffers or modifies active refs/checkouts.
|
||||
- `review`: owns versioned local JSON review documents, immutable source anchors, validation, bounded reads, and atomic writes with external-change protection. It has no terminal or network operations. Comparison state owns the active review; `editor_review` keeps native comment/summary buffers and the read-only overview attached to their owning tab, synchronizes drafts on review commands, and guards unsaved teardown. The floating review editor has a tab-owned native pane outside the workspace `PaneTree`; rendering overlays it on the diff owner's rectangle without changing the source layout. Dismissal retains drafts and restores source focus. Working snapshot IDs hash the private captured paths, modes, contents, and notices; anchors also retain exact file text to account for live edits without reanchoring.
|
||||
Version 2 anchors use the existing `plugin::TextPosition` type for zero-based grapheme start/end positions with an exclusive end. JSON loading accepts only the current schema and retains original disk bytes for external-write checks. Selected-comment painting reuses source-side grapheme clipping and wrapping; native selections retain higher priority.
|
||||
- `review`: owns the validated local review model, exact immutable anchors, SQLite storage through bundled rusqlite, and atomic JSON import/export with external-change protection. `Store` uses schema-versioned transactions and compare-and-swap review revisions; repository roots and persistent review IDs are independent of comparison revisions. One bounded JSON document is stored per row, retaining exact grapheme endpoints and stale snapshots. Incompatible schemas and concurrent edits fail without replacing data or advancing in-memory saved state.
|
||||
`editor_review` commits native comment/summary buffers on dismissal or switching, and verdicts/deletions immediately. It restores a sole stored review or opens the floating selector for multiple reviews. Exports retain their own file baseline and do not become automatic persistence targets; imports get fresh database identities. Review editors remain tab-owned native panes outside the workspace `PaneTree`. Storage opens lazily when a comparison is ready; the session supplies the configured or XDG default directory. Direct `Editor` callers inject a directory with `set_review_directory` to enable persistence.
|
||||
- `clipboard`: owns native plain-text provider selection and bounded subprocess I/O; it never computes selections or editing offsets.
|
||||
- `syntax`: owns Tree-sitter grammar detection, per-buffer incremental trees, bundled highlight/local/injection queries, and syntax byte spans; it has no terminal color/geometry or editing dependency.
|
||||
- `markdown`: owns native Markdown link/reference resolution, local destination decoding, and heading fragment lookup through the installed block/inline grammars. It has no editor lifecycle, terminal, LSP, or external-process dependency.
|
||||
|
||||
@@ -30,6 +30,7 @@ Vamp.configure do |config|
|
||||
config.diff_view_mode = "split" # or "unified"
|
||||
config.diff_split_orientation = "vertical" # or "horizontal"
|
||||
config.diff_follow_working_copy = true
|
||||
config.review_directory = nil # XDG data directory; override with an absolute or config-relative path
|
||||
config.bind "ctrl+h", config.core.left, mode: config.modes.tree
|
||||
config.bind "ctrl+l", config.core.right, mode: config.modes.tree
|
||||
config.plugin "plugins/greeting.rb"
|
||||
@@ -229,7 +230,7 @@ Normal and Visual Ctrl+u/Ctrl+d invoke `config.core.scroll_step_up` / `config.co
|
||||
| Normal | `follow_link` |
|
||||
| Normal | `diff`, `diff.mode.toggle`, `diff.view.toggle`, `diff.orientation.toggle`, `diff.review.toggle` |
|
||||
| Normal, Visual | `diff.comment` |
|
||||
| Normal | `diff.review.edit`, `diff.review.delete`, `diff.review.next`, `diff.review.previous`, `diff.review.overview`, `diff.review.summary`, `diff.review.verdict`, `diff.review.save`, `diff.review.open` |
|
||||
| Normal | `diff.review.edit`, `diff.review.delete`, `diff.review.next`, `diff.review.previous`, `diff.review.overview`, `diff.review.summary`, `diff.review.verdict`, `diff.review.save`, `diff.review.open`, `diff.review.new` |
|
||||
| Normal, Insert, Visual, Command, Search | `diff.close` |
|
||||
| Normal | `resolve`, `resolve.next`, `resolve.previous`, `resolve.use_source`, `resolve.source`, `resolve.base`, `resolve.next_file`, `resolve.previous_file`, `resolve.files`, `resolve.draft`, `resolve.history`, `resolve.view.toggle`, `resolve.close` |
|
||||
| Normal, Insert, Visual, Command, Search | `word_wrap.toggle` |
|
||||
@@ -324,7 +325,7 @@ config.bind "leader r o", config.core.diff.review.overview
|
||||
config.bind "leader r n", config.core.diff.review.next
|
||||
```
|
||||
|
||||
Comment and summary composition uses native editing in a floating window over the diff workspace, without adding workspace splits. Escape in Normal mode or `:pane.close` dismisses it while retaining drafts. `:w` there writes the whole local JSON review, including hidden draft text; choose its destination with `:diff.review.save path`. These actions do not publish reviews or contact a forge. See [local review behavior](editing.md#repository-diff).
|
||||
Review editing uses a floating window without adding workspace splits. Dismissing or switching editors commits text to local SQLite; verdicts and deletions commit immediately. `:w` commits explicitly; `:diff.review.save path` exports JSON without redirecting autosaves. `config.review_directory` defaults to `nil`, selecting `$XDG_DATA_HOME/vamp/reviews/` (absolute XDG path only), otherwise `~/.local/share/vamp/reviews/`. String overrides are resolved relative to the configuration file; empty/NUL-containing strings and non-string/non-nil values are rejected. These actions do not contact a forge. See [local review behavior](editing.md#repository-diff).
|
||||
|
||||
|
||||
`config.core.pane.close` and `config.core.pane.force_close` close a focused **docked** file list instead of its editor pane, without discarding buffers or exiting the comparison. A floating picker does not intercept these actions: normal close still protects underlying dirty buffers, while force-close retains its existing discard semantics. For example, bind `config.bind "ctrl+x", config.core.pane.close`; focused tree/selection overrides and printable filter input retain priority. Palette-originated close actions retain the originating dock target through force confirmation.
|
||||
|
||||
@@ -61,6 +61,8 @@ Diff checks cover staged category/filter navigation, stale request cancellation,
|
||||
|
||||
Local review checks cover native comment/summary composition and undo, Visual ranges on either side, verdicts, JSON save/reopen, external-write refusal, hidden drafts and teardown guards, immutable Working identities in Git/jj, renamed paths, live EOF, and stale anchors. Render checks cover selected/range markers, split/unified context, wrapped continuations, and narrow gutters. Live PTY smoke creates and saves a comment/summary/verdict, closes and reopens the review, edits the reopened comment, changes the reviewed source, and observes stale status without altering the saved anchor. Fixtures are local repositories; no forge writes.
|
||||
|
||||
SQLite review checks cover exact anchors across restarts and revision changes, repository isolation, independent connections and optimistic conflicts, locked/unwritable stores, incompatible schema preservation, bounded/corrupt documents, private file permissions, and XDG/config path resolution. Editor checks commit text on dismissal and switching, persist verdicts/deletions, restore a sole review, choose among multiple reviews, and keep exports independent from autosaves. Test fixtures use isolated review directories. Live PTY smoke restarts three editor processes against one temporary XDG database and verifies stable IDs, Unicode anchors, automatic summary saves, JSON export isolation, and selector/verdict persistence.
|
||||
|
||||
|
||||
Conflicted-revision regressions preserve unchanged symlinks, defer oversized unselected blobs until file selection, compare selected conflict text with jj's projection, and exclude unchanged conflicts when comparing later revisions. Live terminal smoke covers local bookmark selection and opening an actual conflicted file without changing the checkout.
|
||||
|
||||
|
||||
+9
-4
@@ -466,12 +466,17 @@ Comments, summaries, and the review overview open in a focused floating editor o
|
||||
| `:diff.review.summary` | Compose the review summary |
|
||||
| `:diff.review.verdict looks_good` | Record **Looks good** |
|
||||
| `:diff.review.verdict changes_requested` | Record **Changes requested**; `none` clears the verdict |
|
||||
| `:diff.review.save [path]` | Save the entire review as local JSON; the first save requires a path |
|
||||
| `:diff.review.open path` | Reopen a saved review for the current repository |
|
||||
| `:diff.review.save [path]` | Commit to SQLite; with a path, also export JSON for sharing |
|
||||
| `:diff.review.open [id or path]` | Show stored reviews, open a stored review ID, or import a JSON review as a new review |
|
||||
| `:diff.review.new` | Create a separate stored review for the current repository |
|
||||
|
||||
In a review text pane, **`:w` saves the entire review**, not a source file. Choose a destination with `:diff.review.save path` first, or use `:w path`. Saving includes edits in hidden comment/summary buffers. Normal close, quit, restart, comparison replacement, and source-buffer deletion protect unsaved reviews; forced close/quit retains its explicit discard semantics. An externally changed review file is never silently overwritten, including with `:w!`; use a new path or reopen it after preserving your edits.
|
||||
Reviews save automatically to a local SQLite database when you dismiss or switch comment/summary editors. Verdicts and deletions commit immediately; typing alone does not write per keystroke. **`:w`** explicitly commits the entire review, including hidden drafts. `:diff.review.save path` or `:w path` additionally exports JSON; later autosaves still go to SQLite and leave that export unchanged. Imports create a new stored review identity rather than overwriting another review.
|
||||
|
||||
Version 2 JSON is the canonical, agent-readable artifact: repository root, summary, optional verdict, and comments with stable IDs, bodies, and anchors. Each anchor retains its immutable comparison revision, side, relative path (old path for renamed Before files), and exact whole-file text including the final newline. `start` and `end` contain zero-based source `row` and extended-grapheme `column` coordinates; `end` is exclusive. Columns count complete Unicode graphemes, not bytes, code points, UTF-16 units, or screen cells. Overview/window labels display one-based rows and columns. Only the current format is supported; old review files are not migrated. Working comparisons use content-addressed snapshot IDs; captured text additionally freezes live unsaved edits. The file limit remains 64 MiB. Prefer saving outside the checkout so the review itself does not become another changed file.
|
||||
Storage defaults to `$XDG_DATA_HOME/vamp/reviews/reviews.sqlite3`, or `~/.local/share/vamp/reviews/reviews.sqlite3` when XDG_DATA_HOME is unset or relative. Configure `review_directory` to override its directory; relative configuration paths resolve against the config file. Repository identity is the canonical checkout root. Each review has a persistent database ID independent of comparison revisions, so reopening changed revisions retains stale comments. Opening a comparison automatically restores the repository's sole review. Multiple reviews open a floating selector: move to an entry and press Enter, or use `:diff.review.open id`. Use `:diff.review.new` for another independent review.
|
||||
|
||||
Write errors, locked databases, incompatible schemas, and conflicting edits from another editor instance retain dirty drafts and report the failure. Normal destructive closes remain guarded until persistence succeeds. `:w!` does not bypass database revision conflicts; export your draft before reopening the current stored review. JSON exports retain external-file-change protection. Only the current database schema is accepted; existing incompatible databases are preserved rather than reset or migrated.
|
||||
|
||||
Version 2 JSON is the agent-readable sharing format: repository root, summary, optional verdict, and comments with stable IDs, bodies, and anchors. Database review IDs and optimistic storage revisions stay internal. Each anchor retains its immutable comparison revision, side, relative path (old path for renamed Before files), and exact whole-file text including the final newline. `start` and `end` contain zero-based source `row` and extended-grapheme `column` coordinates; `end` is exclusive. Columns count complete Unicode graphemes, not bytes, code points, UTF-16 units, or screen cells. Overview/window labels display one-based rows and columns. Only the current format is supported. Working comparisons use content-addressed snapshot IDs; captured text additionally freezes live unsaved edits. Each stored or exported review is limited to 64 MiB.
|
||||
|
||||
Reopening requires the same repository root. Comments whose revision, path, or exact file text no longer matches are **stale**: their original anchors remain unchanged and their gutter markers disappear. Unloaded files are shown as **not checked** until visited; refresh the overview to update its statuses. There is no automatic reanchoring, server, authentication, or review-publishing operation.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user