Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .changeset/calm-rows-compare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
94 changes: 34 additions & 60 deletions packages/hunk/src/ui/diff/DiffRowView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,40 @@ export interface DiffRowViewProps {
/**
* Render one diff row, memoized to avoid unnecessary rerenders.
*
* The comparator checks every handler by reference, so callers (DiffSectionBody) must pass
* identity-stable callbacks — e.g. one shared onHoverRow that receives the row key — or the memo
* React's shallow comparison checks every handler by reference, so callers (DiffSectionBody) must
* pass identity-stable callbacks — e.g. one shared onHoverRow that receives the row key — or memo

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 Memoization Change Lacks Coverage

Removing the custom comparator changes this component's runtime memoization behavior, but the PR adds no regression coverage. The repository requires tests for every code change that modifies runtime behavior, so this requirement must be satisfied before merging. Add a test showing that a row rerenders when each rendering-relevant prop changes.

Context Used: guidelines.mdc Cursor rule (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/hunk/src/ui/diff/DiffRowView.tsx
Line: 109

Comment:
**Memoization Change Lacks Coverage**

Removing the custom comparator changes this component's runtime memoization behavior, but the PR adds no regression coverage. The repository requires tests for every code change that modifies runtime behavior, so this requirement must be satisfied before merging. Add a test showing that a row rerenders when each rendering-relevant prop changes.

**Context Used:** guidelines.mdc Cursor rule ([source](https://github.com/modem-dev/modem/blob/main/.cursor/rules/guidelines.mdc))

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I’m not adding an exhaustive per-prop rerender test here. That would duplicate React’s documented shallow-comparison contract in our test suite—the same maintenance burden this PR removes. Existing row rendering tests cover the rendering-relevant props, and a local before/after spot check of the focused DiffRowView/add-note tests showed no material timing change (8 runs: mean -0.1%, median +0.7%).

Responded by Pi using gpt-5.6-sol.

This comment was generated by Pi using gpt-5.6-sol

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.

That’s fair. An exhaustive per-prop rerender test would duplicate React’s documented memo shallow-comparison contract and recreate the maintenance burden this change removes. Given the existing row rendering coverage and the focused before/after check, I’m withdrawing the coverage request for this PR.

* silently degrades to re-rendering every visible row per parent render.
*/
export const DiffRowView = memo(
function DiffRowViewComponent({
plannedRow,
row,
export const DiffRowView = memo(function DiffRowViewComponent({
plannedRow,
row,
width,
lineNumberDigits,
showLineNumbers,
showHunkHeaders,
wrapLines,
codeHorizontalOffset,
theme,
selected,
copySelectedRowRange,
copySelectedSide,
cursorHighlight,
lineHighlights,
anchorId,
noteGuideSide,
showAddNoteBadge,
onHoverRow,
onStartUserNoteAtHunk,
onToggleGap,
}: DiffRowViewProps) {
const resolvedPlannedRow =
plannedRow ?? (row ? legacyPlannedDiffRow(row, anchorId, noteGuideSide) : undefined);
if (!resolvedPlannedRow) {
return null;
}

return renderRow(
resolvedPlannedRow,
width,
lineNumberDigits,
showLineNumbers,
Expand All @@ -125,61 +151,9 @@ export const DiffRowView = memo(
copySelectedSide,
cursorHighlight,
lineHighlights,
anchorId,
noteGuideSide,
showAddNoteBadge,
onHoverRow,
onStartUserNoteAtHunk,
onToggleGap,
}: DiffRowViewProps) {
const resolvedPlannedRow =
plannedRow ?? (row ? legacyPlannedDiffRow(row, anchorId, noteGuideSide) : undefined);
if (!resolvedPlannedRow) {
return null;
}

return renderRow(
resolvedPlannedRow,
width,
lineNumberDigits,
showLineNumbers,
showHunkHeaders,
wrapLines,
codeHorizontalOffset,
theme,
selected,
copySelectedRowRange,
copySelectedSide,
cursorHighlight,
lineHighlights,
showAddNoteBadge,
onHoverRow,
onStartUserNoteAtHunk,
onToggleGap,
);
},
(previous, next) => {
return (
previous.plannedRow === next.plannedRow &&
previous.row === next.row &&
previous.width === next.width &&
previous.lineNumberDigits === next.lineNumberDigits &&
previous.showLineNumbers === next.showLineNumbers &&
previous.showHunkHeaders === next.showHunkHeaders &&
previous.wrapLines === next.wrapLines &&
previous.codeHorizontalOffset === next.codeHorizontalOffset &&
previous.theme === next.theme &&
previous.selected === next.selected &&
previous.copySelectedRowRange === next.copySelectedRowRange &&
previous.copySelectedSide === next.copySelectedSide &&
previous.cursorHighlight === next.cursorHighlight &&
previous.lineHighlights === next.lineHighlights &&
previous.anchorId === next.anchorId &&
previous.noteGuideSide === next.noteGuideSide &&
previous.showAddNoteBadge === next.showAddNoteBadge &&
previous.onHoverRow === next.onHoverRow &&
previous.onStartUserNoteAtHunk === next.onStartUserNoteAtHunk &&
previous.onToggleGap === next.onToggleGap
);
},
);
);
});
Loading