Files
vamp/docs/development.md
T
hl-valdemar b9f29d258c fix: support large files in comparisons
Replace the 16 MiB comparison rejection with independent bounded per-side loading and frozen Working blobs. Bound diff computation, defer large inline highlighting, index source-row navigation, and expose actual load failures. Verified the real 52.3 MB parser through repository/session APIs and terminal rendering with EOF/start navigation.
2026-10-17 00:46:15 +02:00

109 lines
24 KiB
Markdown

# Development
[README](../README.md)
Local verification and implementation constraints. See [architecture](architecture.md) for ownership and frontend contracts.
## Verify
```sh
cargo test
cargo test -p crossterm color_scheme_notifications
cargo test -p crossterm --doc
cargo clippy --all-targets -- -D warnings
cargo fmt --package vamp --check
rustfmt --edition 2024 --check tests/unit/battery_macos.rs tests/unit/clipboard_macos.rs
for file in src/ruby_*.rb examples/config.rb examples/plugins/greeting.rb; do
ruby -c "$file" || exit
done
rubocop
```
The default workspace member is the editor. `vendor/crossterm` is included to test its minimal DEC 2031 notification decoder patch; the rest of its source retains upstream formatting. The decoder checks cover fragmented reports and keyboard recovery, and its doctests verify the added event does not break upstream examples.
`vendor/tree-sitter-honey` contains a minimal Rust binding plus the unchanged generated parser, parser header, and highlight query from [`valdemar/tree-sitter-honey`](https://github.com/valdemar/tree-sitter-honey), revision `10f1ff746fe57736ae7d7c8c402f7b265680b26c` (MIT). Builds do not depend on a sibling checkout or an installed Tree-sitter CLI. To update, copy `src/parser.c`, `src/tree_sitter/parser.h`, and `queries/highlights.scm` from the same grammar revision, update the revision recorded here, and run `cargo test --lib syntax::tests`.
## Ruby types
Use YARD `@param`, `@return`, and `@yieldparam` annotations for Ruby APIs, and `@type` for locals where inference needs help. Examples annotate DSL block parameters before the call, without adding runtime assignments or dependencies. `Vamp::Action` / `Vamp::Mode` field types and the dynamically defined mode methods use Solargraph-supported directives.
Core action methods are generated from Rust's authoritative catalog at host startup. Their namespace is annotated, but static Solargraph analysis cannot enumerate those generated members: CLI `typecheck` can misidentify `config.core.select` as inherited `Kernel#select`. Do not maintain a second action list in Ruby comments; use the [configuration action reference](configuration.md#action-reference). Runtime catalog validation remains authoritative.
## Ruby implementation and style
Rust embeds the Ruby implementation at compile time: [`ruby_values.rb`](../src/ruby_values.rb) owns value validation and core action/mode objects, [`ruby_config.rb`](../src/ruby_config.rb) owns configuration and editor contexts, [`ruby_plugin.rb`](../src/ruby_plugin.rb) owns registration and callbacks, and [`ruby_host.rb`](../src/ruby_host.rb) owns framed subprocess I/O. Rebuild the editor after changing these files; installed binaries do not load implementation files from the filesystem.
`.rubocop.yml` uses RuboCop defaults with double-quoted strings (including interpolation) and Ruby 2.6-compatible syntax. Ruby implementation and example files use frozen string literals. Narrow inline style exceptions retain physical `STDIN`/`STDOUT`/`STDERR` capture/redirection and native exception state at the process boundary; plugin output must not corrupt the protocol, and errors including `SystemExit` and `Interrupt` must discard effects and report a failure.
## Test organization
Rust tests live under [`tests/`](../tests/). Private unit tests are in [`tests/unit/`](../tests/unit/), linked from their owning modules with `#[cfg(test)]` and `#[path]`; nested macOS modules use `include!` to retain their private/platform scope. Root `tests/*.rs` files remain Cargo integration targets, including native LSP checks in [`tests/lsp.rs`](../tests/lsp.rs).
`cargo test` runs both groups; `cargo test --lib` and `cargo test --bin vamp` select the library and CLI unit suites. Do not name a unit file `tests/unit/main.rs`: Cargo would discover it as a separate integration target.
- Editor checks cover Unicode editing, multi-target state, undo/redo, saving failures, and modal paste acceptance and rejection atomicity.
- Visual-command checks preserve directed characterwise/linewise ranges and multiple carets through command cancellation and plugin dispatch, exercise remapped command entry, and submit `:diff.comment` on either historical side. Live PTY smoke selects lines, cancels a prompt, then runs `:diff.comment` directly and verifies the saved original range without using the palette.
- Presentation regressions render retained characterwise/linewise highlights during command entry in ordinary, split-diff, and unified views. Floating-review checks preserve source rectangles and the workspace layout, place the caret inside the overlay, retain dismissed drafts across tab switches, and restore source focus on Escape or pane close. Live PTY smoke edits, dismisses, reopens, and saves a floating comment and summary before returning to the unchanged diff.
- Precise-range checks cover forward/reversed characterwise and partial-multiline selections on both sides, linewise and caret/empty-line fallbacks, source-column navigation, Unicode grapheme bounds, invalid schemas, and preserved save-conflict checks. Render checks cover exact text/newline cells, wrapping, horizontal clipping, shared unified context, stale suppression, and native Visual precedence. Live PTY smoke saves and reopens a selection containing a wide and combining grapheme.
- Markdown navigation checks cover inline/reference destinations, escaped/percent-encoded paths, heading markup and duplicates, same-document fragments, dirty target reuse, failed navigation atomicity, primary-only following, and `gf` prefix cancellation/configuration overrides.
- Jump-history checks cover cross-buffer/same-document navigation, search and picker recording, counts and repeated visits, bounded history, branch truncation, closed buffers, edited-coordinate clamping, dirty text/undo preservation, independent Ctrl+i/Tab bindings, and custom bindings. Diff checks exercise Ctrl+i on either side in split/unified views, both control-letter and modified-Tab terminal events, and plain Tab pane cycling. Real LSP fixtures cover definition round-trips and delayed replies after leaving and returning to the same source location.
- Tab checks cover shared edits/history with independent pane layouts, focused views, explorers and viewports; cross-tab close guards, immutable snapshots, hidden comparison refresh/result routing, and separate repository workers. Rendered checks cover sanitized/dirty titles, active-tab visibility in narrow terminals, overlay row reservation, and exclusion of hidden diff geometry from palette polling and viewport reporting.
- Session and Ruby checks exercise real Ruby execution, command/capability ownership, effect validation, configuration fallback, plugin retirement, crash/timeout recovery, and inherited-pipe cleanup.
- For chrome changes, exercise anchor/template validation, padding/edge bounds, all builtin/custom segments in either bar, named startup values, and atomic live label batches. Rendering regressions should cover swapped bars across ordinary/split/diff/selector views, full-row center anchoring, terminal-cell glyph widths, empty-content row removal, tabs-only singleton hiding in either position, active-tab preservation, padding inside styled edges, and separate prompt geometry. Labels are session-wide presentation state: test cross-tab/read-only updates and unchanged-value redraw behavior without collecting real battery data or invoking render callbacks.
- Battery checks inject samples and refresh gates, use temporary Linux sysfs fixtures, and validate native value types/bounds without depending on the machine's charge or clock. Unit sessions do not automatically read hardware. Live smoke should compare the default indicator with OS telemetry and observe a production refresh; no-battery, explicit overrides, and disabled anchors must not fabricate values.
- Status redesign checks cover component-aware home abbreviation without save-path changes; floating command/search submit/cancel, Unicode/tiny/offset clipping and wheel isolation; footer-row reclamation; notification Escape/modal/prefix priority and covered-caret visibility; and ready-server metadata using real framed subprocess fixtures, including stacks, initialization gates, failure, and focus changes.
- Unix terminal-hook checks use isolated child processes and real PTYs to keep process-global hooks out of the parallel runner. They cover raw/alternate-screen/mouse-capture cleanup on entry failure, normal return, I/O errors, and panics during frame construction, including original panic-payload propagation.
- Redraw checks inspect actual Crossterm output during repeated scrolling: supporting terminals receive synchronized frames, terminals ignoring synchronization never paint with a visible hardware cursor, and the final caret remains visible. A protocol-state regression verifies that the terminal is not left in synchronized-output mode while the next frame is computed.
- Wheel checks render document/panel views and cover three-row scrolling, unchanged carets/selections/text, first/last-page clamping, offscreen cursor hiding, no-op/custom-bound navigation reveal, manual persistence through idle/resize/palette updates, and floating/unfocused-docked explorer selection and focus preservation. Queued-input checks verify that the next frame shows ordered wheel steps without crossing a key or resize boundary.
- Queued-navigation checks cover counted ordered motions through short rows, Normal/Visual ranges, remapped press/repeat keys, rendered caret visibility, edit/layout/prefix and non-key barriers, and the 64-key fairness bound. Burst smoke must verify the final cursor position as well as frame count; batching reduces queued redraws without increasing the OS/terminal repeat rate.
- Native LSP integration checks launch framed-protocol fixture subprocesses, covering initialization/settings, opt-in startup, synchronization, UTF-16/file-URI conversion, dirty targets, stale requests/diagnostics, and transport failures. Stack checks cover independent initialization, diagnostic merging/clearing, sibling failure isolation in active/parked buffers, same-revision rename/close identity, aggregate limits, first-server position routing, and both shutdowns. Semantic ordering uses protocol/file gates; elapsed deadlines only bound stuck subprocesses.
- Hover regression checks cover content forms, sanitization, cancelled/superseded replies, invalid results, scrolling/dismissal, Unicode clipping, and preserved editing state. Rendered checks cover longer/wider documentation visibility, Markdown heading and fenced-code colors, scrolled color coordinates, and replacement content without stale highlighting.
- Semantic-token checks cover full/range requests, legend/modifier remapping, UTF-16 boundaries and grapheme rounding, ASCII tab and Unicode column bounds, refresh/change coalescing, stale rename/close replies, null/empty/error fallback, primary/sibling retirement, and rendered cell precedence with syntax, diagnostics, search, selections, and carets. Retention checks cover typing/undo/redo in editable diffs, column/newline/Unicode remapping, overlapping-token removal, and session polling with delayed/stale server replies. Live PTY smoke holds semantic replies while typing and undoing/redoing, checking that untouched colored text is not repainted in the fallback color.
Clipboard checks use isolated providers rather than reading or overwriting the desktop clipboard.
Diff checks cover staged category/filter navigation, stale request cancellation, row alignment, read-only buffer isolation, and rendered panes. Repository integration fixtures exercise Git and Git-backed jj, advertised but unfetched remote branches/bookmarks/tags, imported historical commit/change IDs, conflicts, unusual paths, binary/mode/link/submodule changes, and unchanged active metadata. Remote fixtures are local bare copies; they never push. Git fixture identities and commit dates are fixed.
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.
Manual-resolution checks cover immediate persistence, reopen/export/import, independent stale status, conflict-retained drafts, and incompatible version preservation. CLI checks cover filename-safe dispatch, Unicode excerpts, unresolved filtering/counts, absent storage, read-only access and permissions, symlink rejection, and incompatible schema preservation.
Live PTY/CLI smoke composes and autosaves a comment, resolves it in the editor, observes an empty default CLI comment list with explicit counts, retrieves the resolved comment and exact excerpt with `--all`, and unresolves it. Each transition advances the same stored review revision; listing absent storage creates no data directory.
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.
Resolver checks use isolated real jj repositories: read-only discovery, marker-free all-side/base projections preserving outside edits and EOF, initial seed undo, hunk choice/undo, retained resolved files, jj-authoritative status, checkout/external-write refusal, cancellation, nontext bytes/types/modes/deletion, and directory-child protection. Render checks cover aligned gaps/highlights, context folding, native Draft focus/search/selection, separate History, hidden/on-demand explorer, viewport retention and narrow layouts. Live PTY smoke exercises frozen marker-free alternatives, History/Draft switching, hunk choice and guarded save confirmation without touching user repositories.
Conflict History layout checks load `resolve_history_layout` through the real Ruby configuration and render both left/base/right columns and left/right over base. They cover source contents, full-width base geometry, Tab/directional focus, synchronized wheel scrolling, read-only base protection, narrow/short fallbacks and preserved Draft text. Live PTY smoke opens both layouts, focuses/scrolls the base, returns to Resolution, takes a hunk and saves; jj confirms the checkout is no longer conflicted.
Shared-workspace parity checks compare ordinary and resolver diff geometry/content under identical chrome, focus and source cursors. Cover native picker selection/remaps, Tree/List and dock/floating actions, excerpt initialization, folded search, focus transfer on wheel, layout controls, loading/History/pane-rebuild transitions, and preserved per-file viewport state. Live PTY smoke uses Space e, Down/Enter file selection and guarded saving through the same explorer path.
Repository gutter checks cover LF/CRLF normalization, additions/replacements/deletion anchors, staged/disk/unsaved Git text, unborn and missing named files, ignored/unsupported paths, read-only colocated/non-colocated jj queries, stale results and per-buffer isolation, plus rendered modes, colors, current-line precedence, wrap/scroll and narrow/split geometry. Live PTY smoke verifies all four modes, edit/undo refresh, external Git HEAD changes, and jj parent changes without editor restart.
Normal-picker checks cover staged/disk/unsaved status, binary and mode changes, exact and ambiguous renames, deleted paths, unborn repositories, aliases and unusual filenames, ignores and safe roots, linked worktrees, and read-only colocated/non-colocated/conflicted jj parents. Catalog/render checks retain unchanged rows, query/selection/expansion and Tree/List navigation, clear stale metadata, restore recreated files, and sanitize rename labels. Live PTY smoke checks A/M/D/R colors in floating and docked pickers with the line gutter disabled, Git HEAD/jj parent refresh, and unchanged jj operation state during polling.
Diff LSP integration checks reuse the real framed-protocol subprocess with test-controlled response/publication gates and local Git comparisons. Cover both immutable historical sides and writable Working After, snapshot text/URI isolation, Unicode UTF-16 requests/targets/diagnostics, normal overlong-column clamping, hover, completion/undo, and read-only editing guards. Definition routing must distinguish ranges overlapping actual changed rows from wholly unchanged/context/omitted targets, use full location-link target ranges with exact selection reveal and exclusive endpoints, route canonical aliases and old/new rename paths, load other changed files on demand, handle added/deleted files, preserve original tabs/panes/excerpts and shared dirty targets, retain snapshot identity in ordinary tabs, and fail atomically for missing/non-UTF-8 files, invalid ranges, and unsupported URIs. Delayed/superseded replies must remain stale across caret moves, edit/undo, side/file/pane/tab changes, and comparison closure/replacement; publications for disposed snapshot identities must not reach their replacements. Private editor checks gate pending repository-load cancellation and exact-row reveal without timing assumptions. Before marking the TODO complete, run native integration/unit checks and an actual terminal smoke of focused diff LSP actions, changed-line routing, external new tabs, and immutable/editable side behavior.
Changed-file explorer checks cover reuse of the compact directory tree, removed/historical paths, file-to-directory transitions, remapped tree/selection bindings, filter/open/focus transitions, right-dock geometry, and wheel routing without edits to the underlying document.
Visibility checks preserve browsing and pane state across Ctrl+e, exclude hidden explorers from focus/wheel routing, and verify full-width review. Status checks preserve catalog associations through sorting, compaction, duplicate inputs, and file/directory collisions, and cover colored A/M/D/R markers, selected/unfocused rows, Tree/List filtering, and sanitized rename paths.
Diff styling checks cover independent snapshot language/shebang/injection detection, accepted/stale snapshot identity, real-parser cache freshness/reuse/release, syntax foreground over changed backgrounds, real empty rows versus missing counterparts, scrolling-stable diagonal hatching, and tab/wide/combining/control-character clipping. TestBackend omits wide-glyph continuation cells; terminal smoke verifies their actual styling.
## Performance and process limits
- Retained undo/redo history stores only changed rows. Insert/replay/completion transactions still use temporary full-buffer snapshots, and undo/redo reconstructs the buffer. Ordinary single-line edits with one pane and no semantic overlay modify the line in place; multiline, multi-edit, shared-pane, and semantic-overlay batches retain the general whole-buffer path. Dirty-state comparisons are cached until text or the saved baseline changes. Movement and wrapping still scan the current line.
- Syntax falls back to plain text above 1 MiB of source or a 64 KiB line, before source copies, tree construction, or per-row syntax allocations. The detected language and fallback reason remain visible; reducing the buffer below both budgets restores highlighting. These are optional syntax limits, not file-opening or editing limits.
- Saves use buffered writes before flushing, syncing, and atomically replacing the destination. External-change checks remain independent of dirty-state caching. Large-file verification must include exact saved bytes and edit/undo/redo round-trips, not startup alone.
- Blame and gutter snapshots are size-checked before joining buffer lines; files above their existing 16 MiB limits retain editing but have no annotations. Eligibility is cached with the source revision and recovers after shrinking.
- Rapid-wheel verification must replay bursts against the running editor and measure arrival at the final viewport after input stops, not just individual frame duration. Cheap frames can still accumulate a visible backlog if each queued report requires its own redraw.
- Keyboard viewport scrolling coalesces up to 64 consecutive inputs for the same resolved scroll action into one redraw. Reversals, changes between row/configured-step scrolling, and cursor motions end the batch so boundary clamping and caret reveal happen before the next action. Burst checks must include direction changes at the start/end of the buffer, not just throughput.
- PTY throughput alone cannot prove terminal presentation stays smooth: capture the editor and terminal while the user reproduces a pause. Synchronized-output commands must not reach the terminal during frame computation, even when individual frames and queued-input catch-up appear fast.
- Annotation coordinate validation fast-paths ASCII grapheme lengths using byte lengths with CRLF pair accounting, retaining Unicode segmentation for non-ASCII text. Validation remains atomic and still checks every token; large Unicode buffers or diagnostic batches can require foreground work.
- Legacy synchronous plugin invocation may pause input for up to 500 ms before a host timeout. Async plugins opt into background replies; pending requests and transport queues are bounded. Config/plugin startup has a 2-second deadline per host. Buffer context/document snapshots must fit within a 4 MiB JSON frame; a too-large frame disables that plugin, not editing.
- Repository diff requires Unix subprocess-group cancellation and Git-backed jj (or Git alone). Selected comparison sides support 64 MiB and 4,000,000 source lines each; frozen Working snapshots support 512 MiB total. Subprocess streams remain bounded to 64 MiB and commands to 60 seconds. Working text is frozen into private Git blobs; catalogs stay lazy and selected blobs load separately without concatenated batch copies. Limits and transport errors are explicit; temporary stores are removed on close. Picker/gutter annotation budgets remain separate.
- Large line comparisons skip equal prefixes/suffixes before allocating diff tokens; added/removed files bypass the diff algorithm. Myers work has a 100 ms deadline, after which unresolved regions can be conservatively shown as replacements without dropping text. Inline highlighting is viewport-lazy above 20,000 combined source rows; large/pathological lines use whole-line change shading. Source/display navigation uses indexed contiguous ranges rather than scanning all preceding rows.
- Normal-picker status uses the gutter worker's five-second command and 16 MiB stream limits. Catalogs are bounded to 100,000 paths; dirty-buffer batches to 1,024 files/64 MiB; individual overlays, conflict files, and rename candidates to 16 MiB; conflict/rename data to 64 MiB. Temporary baseline stores are retained for warm metadata polling and removed when the worker stops.
See [syntax caches](architecture.md#syntax-caches) for the current full-tree highlighting ceiling.