Skip to content

Open in editor: jump from a diff into your editor at the first change (v0.1.4)#7

Merged
MrArcher23 merged 4 commits into
mainfrom
feat/open-in-editor
Jun 3, 2026
Merged

Open in editor: jump from a diff into your editor at the first change (v0.1.4)#7
MrArcher23 merged 4 commits into
mainfrom
feat/open-in-editor

Conversation

@MrArcher23

Copy link
Copy Markdown
Owner

What changed

A button in the file bar opens the selected file in the user's editor at the first changed line, by detecting installed editors and delegating to the editor's CLI — arrow never embeds an editor or a language server (the mirror image of Claude Code's /ide). Ships as v0.1.4.

Backend (src-tauri/src/editor.rs)

  • detect_editors() probes $PATH over a data-driven table: VS Code family (Cursor, Windsurf, Kiro, Antigravity + the separate Antigravity IDE, VSCodium, Code-OSS, Trae), Zed, Sublime, JetBrains, Kate.
  • open_in_editor() builds the per-family argv (VS Code -g {file}:{line}:{col}, Zed/Sublime positional, JetBrains --line/--column) as separate args, never a shell string (dodges vscode#39891 / cursor#3796). Two Tauri commands wired up.

Parser (src/lib.rs)

  • ContentOut.firstChangedLine = the first actually-changed line on the after side (each hunk's newStart advanced past its leading context). Honest: it points at the current on-disk file, not the reconstructed before.

Frontend

  • api.ts: detectEditors/openInEditor (Tauri-only; no-op in the browser). New OpenInEditor.svelte picker that remembers the choice.

Pre-release adversarial audit

Before shipping, a multi-agent audit reviewed the diff across 6 dimensions and adversarially verified each finding (6 confirmed real, 7 refuted). All 6 fixed:

  1. Line off-by-NfirstChangedLine used the hunk's raw newStart (top of the context window), landing ~3 lines above the first change on ~92% of files. Fixed via leading-context offset; the masking test now exercises it. Verified against real transcripts (e.g. 4→7, 6→9, 43→46).
  2. Honesty / gone-on-disk — opening a file no longer on disk would let the VS Code family fabricate a phantom empty file while arrow reported success. Now the backend rejects a non-existent (and non-absolute) path and the button is disabled when afterAvailable is false.
  3. Zombie children — the spawned launcher is reaped on a detached thread instead of dropped.
  4. Detached stdio (Stdio::null).
  5. Stale error ⚠ cleared on file change.
  6. Accessibility — the error glyph gets role/aria-label/aria-live, matching the rest of the UI.

Verification

  • cargo fmt --all --check, cargo clippy --all-targets --all-features -- -D warnings (root + src-tauri), cargo test --release20 passed, npm --prefix web run build — all green.
  • cargo tauri build.deb + AppImage (v0.1.4); tested live opening VS Code, Cursor, Kiro, Antigravity, Antigravity IDE, Zed at the correct line.
  • Deferred (documented, out of MVP): terminal editors (no TTY), JetBrains/Sublime/Kate best-effort, macOS/Windows binary resolution, smart-default cascade.

A button in the file bar opens the selected file in the user's editor at the
first changed line, delegating to the editor's CLI — arrow stays a viewer and
never embeds an editor or an LSP (the mirror image of Claude Code's /ide).

Backend (src-tauri/editor.rs): detect_editors() probes $PATH for a table of
known editors (VS Code family incl. Cursor/Windsurf/Kiro/Antigravity + the
separate Antigravity IDE/VSCodium/Trae, Zed, Sublime, JetBrains, Kate);
open_in_editor() spawns the right per-family argv (VS Code `-g file:line:col`,
Zed/Sublime positional, JetBrains `--line/--column`) as SEPARATE args, never a
shell string, to dodge the space-in-path bug (vscode#39891, cursor#3796). Two
Tauri commands wired into the handler.

Parser (lib.rs): ContentOut gains firstChangedLine (min newStart across the
session's hunks, after side) so the jump lands on the first change. Honest: it
opens the current on-disk file, not the reconstructed before.

Frontend: api.ts gains detectEditors()/openInEditor() (Tauri-only; no-op in the
browser); a small OpenInEditor.svelte picker (remembers the choice via
localStorage) in the file bar. Adds the first-changed-line parser test (20 total).
ROADMAP: a full entry for the feature (delegation model, per-family open syntax,
firstChangedLine, honesty about opening the on-disk after-file) plus the
deferred items spelled out — terminal editors (no TTY from a GUI app, would need
a terminal-emulator host), JetBrains/Sublime/Kate as best-effort (unverified
here), macOS/Windows binary resolution, and the smart-default cascade
(~/.claude/ide lockfile + $EDITOR) left as a future enhancement since the
remembered choice already suffices. CLAUDE.md: a short pointer (editor table is
data in src-tauri/editor.rs, opens the after-file, Tauri-only) and the test
count. editor.rs: note which rows are verified live vs best-effort.
An adversarial multi-agent audit surfaced 6 real issues (7 others refuted).
Fixes, all verified (20 tests, clippy -D warnings, fmt, web build; #1 checked
against real transcripts):

- Line off-by-N (parser): first_changed_line used the hunk's raw newStart, which
  is the top of the CONTEXT window — landing the cursor ~3 lines above the first
  actual change on ~92% of files. Now advance each hunk's newStart past its
  leading context (' '-prefixed lines) before taking the min. The unit test that
  masked this (fixture had no leading context) now exercises a 3-line offset.
- Honesty / gone-on-disk: open_in_editor now rejects a non-existent path (and a
  non-absolute one) instead of launching the editor on it — which in the VS Code
  family would open an empty buffer and fabricate a phantom file on save while
  arrow reported success. The button is also disabled (afterAvailable gate) when
  the file is no longer on disk.
- Zombie children: the spawned launcher is now reaped on a detached thread
  instead of dropped, so defunct processes no longer accumulate one-per-click.
- Detached stdio (Stdio::null) so a chatty launcher can't write into arrow's
  terminal when run from a shell/AppImage.
- Stale error ⚠ cleared on file change (it used to carry a previous file's
  failure onto the newly selected file).
- Accessibility: the error glyph gets role/aria-label/aria-live, matching the
  rest of the UI (previously the reason was mouse-hover-only).
Ship "Open in editor": a file-bar button that opens the selected file in the
user's editor at the first changed line, delegating to the editor's CLI (no
embedded editor/LSP). Hardened by a pre-release adversarial audit (line landed
via leading-context offset, gone-on-disk gate, child reaping, a11y).

Updates the four manifests + all lockfiles, the README status/desktop section
and test count (20), and the ROADMAP entry.
@MrArcher23 MrArcher23 merged commit 753319f into main Jun 3, 2026
3 checks passed
@MrArcher23 MrArcher23 deleted the feat/open-in-editor branch June 3, 2026 22:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant