Skip to content

refactor(ui): centralize diff section row planning#359

Merged
benvinegar merged 1 commit into
mainfrom
refactor/diff-section-row-plan
May 23, 2026
Merged

refactor(ui): centralize diff section row planning#359
benvinegar merged 1 commit into
mainfrom
refactor/diff-section-row-plan

Conversation

@benvinegar
Copy link
Copy Markdown
Member

Summary

  • Add diffSectionRowPlan as the shared file-level row planning layer for diff sections.
  • Move diff section geometry into src/ui/diff/ so row planning, geometry, and windowing live together.
  • Update rendering, geometry, copy-selection, viewport, and benchmark imports to use the new locations.

Testing

  • bun run typecheck
  • bun test src/ui/diff/diffSectionGeometry.test.ts src/ui/diff/rowWindowing.test.ts src/ui/components/panes/copySelection.test.ts src/ui/AppHost.scroll-regression.test.tsx
  • bun run lint
  • bun run format:check

This PR description was generated by Pi using OpenAI GPT-5 Codex

@benvinegar benvinegar force-pushed the refactor/diff-section-row-plan branch from 3228212 to e71c4c1 Compare May 23, 2026 23:06
@benvinegar benvinegar marked this pull request as ready for review May 23, 2026 23:33
@benvinegar benvinegar merged commit 0d9638e into main May 23, 2026
5 checks passed
@benvinegar benvinegar deleted the refactor/diff-section-row-plan branch May 23, 2026 23:33
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 23, 2026

Greptile Summary

This PR centralizes the diff-section row-planning pipeline into a new diffSectionRowPlan.ts module and moves diffSectionGeometry from src/ui/lib/ to src/ui/diff/, so row planning, geometry, and windowing all live in one place. The remaining 14 files are import-path updates to point at the new location.

  • diffSectionRowPlan.ts (new): encapsulates buildBaseRows → expandCollapsedRows → buildReviewRenderPlan plus lineNumberDigits into a single buildDiffSectionRowPlan function consumed by both PierreDiffView (rendering) and measureDiffSectionGeometry (geometry/caching).
  • diffSectionGeometry.ts (moved + refactored): drops its own private buildPlannedSectionRows helper and delegates entirely to buildDiffSectionRowPlan; introduces DiffSectionRowHeightOptions to bundle height-measurement inputs.
  • PierreDiffView.tsx: replaces four separate useMemo calls (rows, expandedRows, plannedRows, lineNumberDigits) with a single sectionRowPlan memo; functionally correct but note the memo-consolidation trade-off on note-visibility updates.

Confidence Score: 4/5

Safe to merge; the refactoring correctly preserves existing rendering and geometry behavior, with import updates verified across all call sites.

The consolidation of four useMemo calls into one in PierreDiffView means that when visibleAgentNotes changes (e.g., during scroll), the comparatively expensive buildSplitRows/buildStackRows base-row build now runs unnecessarily alongside the cheap buildReviewRenderPlan update. For large diffs this could cause extra work on every note-visibility update, though the output values remain correct.

src/ui/diff/PierreDiffView.tsx — the memo consolidation warrants a quick check on scroll performance with agent notes active on large diffs.

Important Files Changed

Filename Overview
src/ui/diff/diffSectionRowPlan.ts New shared row-planning layer; correctly encapsulates buildBaseRows → expandCollapsedRows → buildReviewRenderPlan pipeline with lineNumberDigits
src/ui/diff/PierreDiffView.tsx Replaces four separate useMemos with a single sectionRowPlan memo; functionally correct but loses the optimization that prevented base-row rebuilds on visibleAgentNotes changes
src/ui/diff/diffSectionGeometry.ts Moved from src/ui/lib/; delegates row building to diffSectionRowPlan; correctly preserves caching and geometry measurement logic; minor EMPTY_EXPANDED_GAP_KEYS duplication
src/ui/components/panes/copySelection.ts Import path update only for DiffSectionGeometry and DiffSectionRowBounds
src/ui/components/panes/DiffPane.tsx Import path update only for measureDiffSectionGeometry and DiffSectionGeometry

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    subgraph Rendering["PierreDiffView (rendering)"]
        A["useMemo: buildDiffSectionRowPlan(...)"] --> B["plannedRows + lineNumberDigits"]
    end

    subgraph Geometry["measureDiffSectionGeometry (geometry + caching)"]
        C["buildDiffSectionRowPlan(...)"] --> D["plannedRows + lineNumberDigits"]
        D --> E["measurePlannedDiffSectionRowHeight x N"]
        E --> F["DiffSectionGeometry"]
    end

    subgraph Plan["diffSectionRowPlan.ts (NEW shared layer)"]
        G["buildBaseRows (buildSplitRows / buildStackRows)"] --> H["expandCollapsedRows"]
        H --> I["findMaxLineNumberInRows -> lineNumberDigits"]
        H --> J["buildReviewRenderPlan -> plannedRows"]
    end

    A -->|delegates| Plan
    C -->|delegates| Plan
Loading
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
src/ui/diff/PierreDiffView.tsx:192-216
**Unnecessary base-row rebuild when only `visibleAgentNotes` changes**

The single consolidated `useMemo` now reruns `buildBaseRows` (which calls `buildSplitRows`/`buildStackRows` over every hunk) and `expandCollapsedRows` whenever `visibleAgentNotes` changes. In the old code those two stages were in a separate memo keyed on `[file, layout, resolvedHighlighted, theme]`, so an agent-note visibility change only triggered the cheap `buildReviewRenderPlan` stage.

For large diffs the base-row build is non-trivial, and `visibleAgentNotes` can change during scroll. The refactoring loses that optimization by flattening all four memos into one.

### Issue 2 of 2
src/ui/diff/diffSectionGeometry.ts:17
**Duplicated `EMPTY_EXPANDED_GAP_KEYS` sentinel**

This constant is already defined in `diffSectionRowPlan.ts` (line 14) for the same purpose. Since `measureDiffSectionGeometry` passes the value down to `buildDiffSectionRowPlan`, the sentinel in this file could be replaced by importing (or relying on) the default in `buildDiffSectionRowPlan`'s parameter list, keeping one source of truth.

Reviews (1): Last reviewed commit: "refactor(ui): centralize diff section ro..." | Re-trigger Greptile

Comment on lines +192 to 216
const sectionRowPlan = useMemo(
() =>
file
? layout === "split"
? buildSplitRows(file, resolvedHighlighted, theme)
: buildStackRows(file, resolvedHighlighted, theme)
: [],
[file, layout, resolvedHighlighted, theme],
);
const expansionSide = file?.metadata.type === "deleted" ? "old" : "new";
const fileHasSourceFetcher = Boolean(file?.sourceFetcher);
const gapToggleHandler = useMemo(
() => (fileHasSourceFetcher ? onToggleGap : undefined),
[fileHasSourceFetcher, onToggleGap],
);
const expandedRows = useMemo(
() =>
expandCollapsedRows(rows, {
layout,
buildDiffSectionRowPlan({
expandedKeys: expandedGapKeys,
file,
highlightedDiff: resolvedHighlighted,
layout,
showHunkHeaders,
sourceLineSpans,
sourceStatus,
side: expansionSide,
theme,
visibleAgentNotes,
}),
[rows, layout, expandedGapKeys, sourceLineSpans, sourceStatus, expansionSide],
);
const plannedRows = useMemo(
() =>
file
? buildReviewRenderPlan({
fileId: file.id,
rows: expandedRows,
showHunkHeaders,
visibleAgentNotes,
})
: [],
[file, expandedRows, showHunkHeaders, visibleAgentNotes],
[
expandedGapKeys,
file,
layout,
resolvedHighlighted,
showHunkHeaders,
sourceLineSpans,
sourceStatus,
theme,
visibleAgentNotes,
],
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Unnecessary base-row rebuild when only visibleAgentNotes changes

The single consolidated useMemo now reruns buildBaseRows (which calls buildSplitRows/buildStackRows over every hunk) and expandCollapsedRows whenever visibleAgentNotes changes. In the old code those two stages were in a separate memo keyed on [file, layout, resolvedHighlighted, theme], so an agent-note visibility change only triggered the cheap buildReviewRenderPlan stage.

For large diffs the base-row build is non-trivial, and visibleAgentNotes can change during scroll. The refactoring loses that optimization by flattening all four memos into one.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/ui/diff/PierreDiffView.tsx
Line: 192-216

Comment:
**Unnecessary base-row rebuild when only `visibleAgentNotes` changes**

The single consolidated `useMemo` now reruns `buildBaseRows` (which calls `buildSplitRows`/`buildStackRows` over every hunk) and `expandCollapsedRows` whenever `visibleAgentNotes` changes. In the old code those two stages were in a separate memo keyed on `[file, layout, resolvedHighlighted, theme]`, so an agent-note visibility change only triggered the cheap `buildReviewRenderPlan` stage.

For large diffs the base-row build is non-trivial, and `visibleAgentNotes` can change during scroll. The refactoring loses that optimization by flattening all four memos into one.

How can I resolve this? If you propose a fix, please make it concise.

import type { PlannedReviewRow } from "./reviewRenderPlan";
import { measureRenderedRowHeight } from "./renderRows";

const EMPTY_EXPANDED_GAP_KEYS: ReadonlySet<string> = new Set();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Duplicated EMPTY_EXPANDED_GAP_KEYS sentinel

This constant is already defined in diffSectionRowPlan.ts (line 14) for the same purpose. Since measureDiffSectionGeometry passes the value down to buildDiffSectionRowPlan, the sentinel in this file could be replaced by importing (or relying on) the default in buildDiffSectionRowPlan's parameter list, keeping one source of truth.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/ui/diff/diffSectionGeometry.ts
Line: 17

Comment:
**Duplicated `EMPTY_EXPANDED_GAP_KEYS` sentinel**

This constant is already defined in `diffSectionRowPlan.ts` (line 14) for the same purpose. Since `measureDiffSectionGeometry` passes the value down to `buildDiffSectionRowPlan`, the sentinel in this file could be replaced by importing (or relying on) the default in `buildDiffSectionRowPlan`'s parameter list, keeping one source of truth.

How can I resolve this? If you propose a fix, please make it concise.

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