Skip to content

feat(editor): optional side-by-side diff view#926

Open
ceyhuncicek wants to merge 2 commits into
crynta:mainfrom
ceyhuncicek:feat/split-diff-view
Open

feat(editor): optional side-by-side diff view#926
ceyhuncicek wants to merge 2 commits into
crynta:mainfrom
ceyhuncicek:feat/split-diff-view

Conversation

@ceyhuncicek

@ceyhuncicek ceyhuncicek commented Jul 2, 2026

Copy link
Copy Markdown

What

Adds a "Diff view" setting so git diffs can render side by side (original left, modified right, aligned line by line) instead of the current inline view. Inline stays the default.

Why

On files with dense changes the inline view gets hard to follow, since deletions and insertions are interleaved in one column. VS Code's side-by-side layout reads much better there. I wanted the same in Terax without forcing it on anyone, so it's a preference.

How

  • New diffViewMode preference ("inline" | "split"), exposed as a Select row under Settings > General > Editor. Follows the existing pref pattern in settings/store.ts, syncs across windows like the rest.
  • New SplitDiffView component wrapping MergeView from @codemirror/merge (already a dependency, the inline view uses unifiedMergeView from the same package). MergeView is a class managing two editors rather than an extension, so it can't go through <CodeMirror>. The wrapper builds it once per file and then updates in place: content changes land as doc-replacing dispatches and theme changes go through a compartment, so scroll position and expanded regions survive file saves and theme switches.
  • GitDiffPane picks the view from the pref, which covers both working-tree diffs and commit-file diffs. The binary/large-file patch fallback is unchanged.
  • The red/green diff palette moved to a shared lib/diffTheme.ts so the inline and split views can't drift apart when colors get tuned.
  • The AI diff pane is left alone on purpose (it has its own review flow); the setting copy says "git diffs".

No Rust changes. git_diff_content already returns both full sides of the file, so the split view had everything it needed.

Testing

  • pnpm exec tsc --noEmit clean
  • Manual smoke-test of the affected feature
  • (If you touched src-tauri/) not applicable, frontend only
  • (If you changed a #[tauri::command] signature) not applicable
  • (If UI) tested in pnpm tauri dev
  • Platforms tested: macOS

Manual flows exercised: toggled the setting with diff tabs open (open tabs switch layout live), working-tree diff and commit diff both render split, saving the file while its split diff is open keeps scroll position, Cmd+F opens search inside the split view, switching editor theme keeps scroll, binary files still fall back to the patch view. pnpm test passes (254 tests), biome clean on touched files.

Screenshots / GIFs

side-by-side-view

Notes for reviewer

  • Both sides live in one scroll container (.cm-mergeView with overflow: auto), which is how MergeView keeps the panes aligned. There is no per-pane scrolling.
  • The split view doesn't go through @uiw/react-codemirror's basicSetup, so the keymaps and gutters the inline view gets for free (searchKeymap, defaultKeymap, fold gutter, special-char highlighting) are added explicitly. If a parity gap shows up later, that extension list is the place to look.
  • Possible follow-up: apply the same mode to the AI diff pane.

@ceyhuncicek ceyhuncicek requested a review from crynta as a code owner July 2, 2026 18:39
@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a persisted "inline"/"split" diff view preference (DiffViewMode) with a General settings selector, a new SplitDiffView CodeMirror MergeView component, shared read-only extensions and diff themes, and wires GitDiffPane to render either the split view or the existing unified merge view based on the preference.

Changes

Split diff view feature

Layer / File(s) Summary
Preference storage and settings UI
src/modules/settings/store.ts, src/settings/sections/GeneralSection.tsx
Adds DiffViewMode type, Preferences.diffViewMode field, storage key, default value, load/validation logic, setDiffViewMode() setter, change-event mapping, and a "Diff view" Select control in General settings.
Shared read-only extensions and diff themes
src/modules/editor/lib/extensions.ts, src/modules/editor/lib/diffTheme.ts
Adds READONLY_EXTENSIONS and centralizes diff colors into UNIFIED_DIFF_THEME and SPLIT_DIFF_THEME for reuse across views.
SplitDiffView MergeView component
src/modules/editor/SplitDiffView.tsx
New component builds a CodeMirror MergeView for original/modified content, resolves language sync/async, updates docs and theme in-place, and cleans up on unmount/path change.
GitDiffPane integration
src/modules/editor/GitDiffPane.tsx
Reads diffViewMode, skips unified extension/language building in split mode, updates stats memo deps, reuses shared theme/extension helpers, and conditionally renders SplitDiffView vs. the unified merge view.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Possibly related issues

  • Side-by-Side Git Diff View #534: Requests a switchable side-by-side (split) diff mode alongside inline diffs, matching the new DiffViewMode preference and SplitDiffView component added here.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title follows Conventional Commits and clearly describes the new optional side-by-side diff view.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
src/modules/editor/SplitDiffView.tsx (1)

39-98: 🚀 Performance & Scalability | 🔵 Trivial

RAM cost: split mode doubles editor instances per diff tab, and tabs never unmount.

MergeView here spins up two full CodeMirror EditorViews (with their own line-numbers, fold-gutter, search, syntax-highlighting extensions) versus one for inline mode. Per TERAX.md, git-diff tabs are kept mounted (hidden, not destroyed) when switched away from, so every background diff tab left in split mode carries roughly 2x the CodeMirror memory footprint of inline mode indefinitely. Given the project's explicit "how much RAM it costs" bar for every change, worth confirming this is an acceptable tradeoff for the feature, or considering some form of idle teardown/lazy-mount for inactive split-mode diff tabs (similar in spirit to the terminal module's renderer pooling).

As per coding guidelines, "For every change ask: how much RAM it costs... whether it adds IPC round-trips or redundant requests, whether it triggers extra re-renders or wasted work".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/modules/editor/SplitDiffView.tsx` around lines 39 - 98, SplitDiffView’s
MergeView setup keeps two full EditorView instances mounted per diff tab, which
doubles RAM usage for background split-mode tabs that never unmount. Update
SplitDiffView to avoid retaining the full MergeView when inactive, or add an
idle teardown/lazy-mount path for split-mode tabs, using the existing
SplitDiffView, MergeView, and viewRef lifecycle to destroy and recreate the
editor only when needed.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/modules/editor/SplitDiffView.tsx`:
- Around line 39-98: SplitDiffView’s MergeView setup keeps two full EditorView
instances mounted per diff tab, which doubles RAM usage for background
split-mode tabs that never unmount. Update SplitDiffView to avoid retaining the
full MergeView when inactive, or add an idle teardown/lazy-mount path for
split-mode tabs, using the existing SplitDiffView, MergeView, and viewRef
lifecycle to destroy and recreate the editor only when needed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: cac04e94-aaf6-437f-9ccd-a9f8c0bc0095

📥 Commits

Reviewing files that changed from the base of the PR and between 1c7f3e4 and 9e7919f.

📒 Files selected for processing (6)
  • src/modules/editor/GitDiffPane.tsx
  • src/modules/editor/SplitDiffView.tsx
  • src/modules/editor/lib/diffTheme.ts
  • src/modules/editor/lib/extensions.ts
  • src/modules/settings/store.ts
  • src/settings/sections/GeneralSection.tsx

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