Skip to content

Find/replace in the active file and follow workspace symlinks#39

Merged
GordonBeeming merged 6 commits into
mainfrom
gb/find-replace-and-symlinks
Jun 30, 2026
Merged

Find/replace in the active file and follow workspace symlinks#39
GordonBeeming merged 6 commits into
mainfrom
gb/find-replace-and-symlinks

Conversation

@GordonBeeming

Copy link
Copy Markdown
Owner

Summary

  • Find in File is now find-and-replace. Cmd+F opens the app's own overlay instead of CodeMirror's panel, the arrow keys cycle matches and select each one in the editor with focus kept in the field, the results preview scrolls to follow the active match, and Cmd+R reveals a replace row with Replace and Replace All. Select All and the other standard edit keybinds now work in every textbox after wiring the macOS Edit menu's Select All item.
  • The editor follows symlinks instead of rejecting them. A target inside the workspace opens and expands silently; one that escapes the workspace prompts first with Cancel / Trust once / Trust for workspace, where "once" lasts the session and "workspace" is persisted. Symlinked entries are marked in the tree, with a distinct icon and the target path when they point outside the workspace. Renaming or deleting a symlink only affects the link, never its target.

Test plan

  • cargo test (159 pass) — new tests cover symlink resolution, logical-path listing for symlinked dirs, FileEntry symlink facts, and rename/delete of a symlink touching only the link
  • npm test (307 pass) — new tests cover the find/replace keyboard flow, the result-window math, and the external-symlink trust prompt
  • npm run build, npm run menu:check
  • Manual: an in-workspace symlink (.claude/skills) opens and expands; an external symlink shows the trust prompt and follows after trust; Cmd+F find/replace works with the editor focused

🤖 Generated with Claude Code

https://claude.ai/code/session_014F5fq81S92ZZi59Xp9aGue

Two editor changes.

Find in File is now a real find-and-replace. Cmd+F opens the app's own overlay
instead of CodeMirror's panel, the arrow keys step through matches and select
each one in the editor while focus stays in the field, the results preview
scrolls to keep the active match visible, and Cmd+R reveals a replace row with
Replace and Replace All. Standard edit keybinds (Select All, word jumps,
cut/copy/paste) now work in every textbox after wiring the macOS Edit menu's
Select All item.

Symlinks used to be rejected outright. The editor now follows them: a target
inside the workspace opens and expands with no fuss, and a target that escapes
the workspace asks first with a Cancel / Trust once / Trust for workspace
prompt. "Trust once" lasts the session, "Trust for workspace" is persisted.
Symlinked entries get a link icon in the tree, with a separate mark and the
target path shown when they point outside the workspace. Renaming or deleting a
symlink only touches the link, never its target.

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: GitButler <gitbutler@gitbutler.com>
@GordonBeeming GordonBeeming marked this pull request as ready for review June 30, 2026 15:50
Copilot AI review requested due to automatic review settings June 30, 2026 15:50

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for handling external symbolic links with user trust prompts, alongside a new find-and-replace feature in the active file. The review feedback highlights several critical improvements: first, the workspace root path should be canonicalized before performing starts_with checks in workspace.rs to ensure consistent path comparisons; second, multiple document changes dispatched to CodeMirror 6 in EditorPane.tsx must be sorted and deduplicated to prevent runtime crashes; and finally, side effects in App.tsx should be moved out of the React state updater callback to avoid anti-patterns.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src-tauri/src/workspace.rs
Comment thread src-tauri/src/workspace.rs
Comment thread src/EditorPane.tsx Outdated
Comment thread src/App.tsx

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR enhances the IDE’s in-editor search UX by adding a first-party Find/Replace overlay for the active file, and improves workspace filesystem support by following symlinks with a user trust gate when targets escape the workspace root.

Changes:

  • Add Find/Replace-in-active-file overlay (Cmd/Ctrl+F, Cmd/Ctrl+R) with match navigation, replace/replace-all commands, and preview-window scrolling.
  • Follow workspace symlinks (including directory listing) with metadata surfaced to the UI and a trust prompt for external targets (session/workspace trust).
  • Extend the Tauri/HTTP API surface and persisted UI state to carry symlink/trust metadata and allow external symlink traversal when permitted.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/tauri.ts Extends frontend API wrappers and types for symlink metadata and external-symlink allowance.
src/tauri.test.ts Updates API transport tests to assert the new allowExternalSymlinks argument is forwarded.
src/styles.css Adds styling for symlink markers in the tree and the new Find/Replace overlay layout/actions.
src/EditorPane.tsx Removes CodeMirror search keymap and adds editor-side replace application + match-range reveal selection.
src/editorCommands.ts Introduces replace-related editor commands and payload types for match-targeted edits.
src/currentFileSearch.ts Adds currentFileResultWindow helper for scrolling the match preview window with the active match.
src/currentFileSearch.test.ts Adds tests for the new preview-window behavior and edge cases.
src/App.tsx Implements Find/Replace overlay behavior, match navigation, replace command dispatch, and external symlink trust prompt/persistence.
src/App.test.tsx Adds tests for keyboard-driven find/replace flow and external symlink trust prompt behavior; updates existing expectations for new args.
src-tauri/src/workspace.rs Adds symlink metadata to FileEntry, enables safe symlink following with external trust gating, and ensures rename/delete affect links not targets.
src-tauri/src/workspace_index.rs Ensures background indexing never follows external symlinks and populates default symlink metadata in indexed entries.
src-tauri/src/lib.rs Threads allow_external_symlinks through Tauri commands and persists trust_external_symlinks; wires macOS Edit menu Select All.
src-tauri/src/http_server.rs Keeps hosted API behavior consistent with “never follow external symlinks” for background/hosted endpoints.
src-tauri/src/git_attribution.rs Updates path resolution for attribution with the new symlink-aware resolver + revised error reason.
Comments suppressed due to low confidence (1)

src/tauri.ts:493

  • allowExternalSymlinks is only included in invokeArgs, but hosted mode uses the request URL and doesn't serialize invokeArgs. As a result, hosted read_file requests cannot opt into following external symlinks after trust is granted.
  const params = new URLSearchParams({ path });
  if (maxOpenBytes !== undefined) {
    params.set("maxOpenBytes", String(maxOpenBytes));
  }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/tauri.ts
Comment thread src/tauri.ts
Comment thread src/App.tsx
Comment thread src/EditorPane.tsx Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 57a2adc592

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src-tauri/src/workspace.rs Outdated
Comment thread src/App.tsx Outdated
GordonBeeming and others added 2 commits July 1, 2026 02:04
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: GitButler <gitbutler@gitbutler.com>
- Report a symlinked file's target mtime so saving through the link
  doesn't false-positive "file changed on disk"
- Sort and de-overlap replace ranges before dispatch so CodeMirror can't
  throw on an out-of-order payload; give the change array an explicit type
- Move the replace-toggle focus side effect out of the state updater
- Drop Alt+Enter as a Replace All trigger; only Cmd/Ctrl+Enter
- Replace All now recomputes over the full file, not the capped preview set

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: GitButler <gitbutler@gitbutler.com>
Copilot AI review requested due to automatic review settings June 30, 2026 16:10

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 4 comments.

Comment thread src-tauri/src/git_attribution.rs Outdated
Comment thread src-tauri/src/workspace.rs Outdated
Comment thread src/EditorPane.tsx Outdated
Comment thread src/App.tsx
GordonBeeming and others added 2 commits July 1, 2026 02:25
Clears RUSTSEC-2026-0190 (unsoundness in anyhow's Error::downcast_mut),
which fails the cargo audit --deny warnings gate in CI.

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: GitButler <gitbutler@gitbutler.com>
- Git attribution no longer follows symlinks outside the workspace, so it
  can't read external files past the trust gate (in-workspace links still work)
- Thread the trust grant into the rename source resolution so renaming an
  entry inside a trusted external symlinked dir works like create/write
- Clamp replace offsets to the line's start as well as its end, guarding
  against malformed negative column offsets
- Bound Replace All to a sane match limit instead of an unbounded scan, and
  report when the cap is hit

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: GitButler <gitbutler@gitbutler.com>
Copilot AI review requested due to automatic review settings June 30, 2026 16:30

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 15 changed files in this pull request and generated 1 comment.

Comment thread src/App.tsx Outdated
The find input auto-closes the overlay on blur when the query is empty.
Clicking the Replace toggle or focusing the replace input blurred the find
input and could close the whole find/replace UI before the user typed
anything. The blur handler now ignores focus moves that stay inside the
find/replace group.

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: GitButler <gitbutler@gitbutler.com>
@GordonBeeming GordonBeeming merged commit 22fe8a2 into main Jun 30, 2026
2 of 4 checks passed
@GordonBeeming GordonBeeming deleted the gb/find-replace-and-symlinks branch June 30, 2026 16:55
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.

2 participants