Preserve absolute reference ($) markers in formula operations#22
Conversation
Previously, shifting, relocating, and redirecting formulas would strip the $ markers from cell references like $A$1, $A1, or A$1. This caused incorrect behavior when copy-pasting or inserting rows/columns in formulas that used absolute references. Add ARef type and parseARef/toASref helpers to preserve $ flags through all formula rewriting paths (shift, relocate, redirect, move). Co-Authored-By: Claude Opus 4.6 <[email protected]>
📝 WalkthroughWalkthroughThe changes add support for absolute cell reference handling ($-prefixed) in formula shifting operations. A new Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
packages/sheet/src/model/coordinates.ts (1)
345-360: Unreachable code path inabsColdetection.The
ref.includes('!$')check on line 346 appears to be dead code. The regex on line 349 (^(\$?)([A-Za-z]+)(\$?)(\d+)$) will throw an error for any cross-sheet reference containing!, so this condition can never be true for a valid input that passes the regex check.Looking at the call sites in
shifting.ts,parseARefis always called on local references after the cross-sheet prefix has already been stripped (e.g.,text.toUpperCase()wheretextexcludes the sheet prefix).🔧 Suggested simplification
export function parseARef(ref: Sref): ARef { - const absCol = ref.startsWith('$') || ref.includes('!$'); + const absCol = ref.startsWith('$'); // Find the column-row boundary to detect row dollar sign. // After optional leading '$' and column letters, check for '$' before digits. const match = ref.match(/^(\$?)([A-Za-z]+)(\$?)(\d+)$/);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/sheet/src/model/coordinates.ts` around lines 345 - 360, The absCol detection in parseARef incorrectly checks ref.includes('!$'), which is unreachable because the regex /^(\$?)([A-Za-z]+)(\$?)(\d+)$/ used later rejects any string containing '!', so remove the dead includes('!$') branch and compute absCol solely from ref.startsWith('$'); update the attendant comment to state that the code assumes any sheet prefix has been stripped before calling parseARef and keep the existing regex-based validation and absRow handling (match[3] === '$') intact.packages/sheet/src/model/shifting.ts (1)
80-88: Consider extracting repeated abs marker spreading pattern.The pattern for preserving absolute markers is repeated verbosely here and in several other places (
shiftSref,shiftARef,relocateARef):{ ...moved, ...(aref.absCol ? { absCol: true } : {}), ...(aref.absRow ? { absRow: true } : {}) }This works correctly, but a small helper could reduce duplication and improve readability.
🔧 Optional helper to reduce repetition
/** Copies absCol/absRow flags from source ARef to a Ref */ function withAbsMarkers(ref: Ref, source: ARef): ARef { return { ...ref, ...(source.absCol ? { absCol: true } : {}), ...(source.absRow ? { absRow: true } : {}), }; }Then usage becomes:
-result += toASref({ ...moved, ...(aref.absCol ? { absCol: true } : {}), ...(aref.absRow ? { absRow: true } : {}) }); +result += toASref(withAbsMarkers(moved, aref));🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/sheet/src/model/shifting.ts` around lines 80 - 88, The repeated pattern that re-applies absolute markers ({ ...(aref.absCol ? { absCol: true } : {}), ...(aref.absRow ? { absRow: true } : {}) }) should be extracted into a small helper to reduce duplication and improve readability; add a function (e.g., withAbsMarkers(ref: Ref, source: ARef): ARef) that returns { ...ref, ...(source.absCol ? { absCol: true } : {}), ...(source.absRow ? { absRow: true } : {}) } and replace occurrences in this file where parseARef, moveRef and toASref are combined (and similar sites in shiftSref, shiftARef, relocateARef) to call withAbsMarkers(movedOrNewRef, originalARef) before passing to toASref.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@packages/sheet/src/model/coordinates.ts`:
- Around line 345-360: The absCol detection in parseARef incorrectly checks
ref.includes('!$'), which is unreachable because the regex
/^(\$?)([A-Za-z]+)(\$?)(\d+)$/ used later rejects any string containing '!', so
remove the dead includes('!$') branch and compute absCol solely from
ref.startsWith('$'); update the attendant comment to state that the code assumes
any sheet prefix has been stripped before calling parseARef and keep the
existing regex-based validation and absRow handling (match[3] === '$') intact.
In `@packages/sheet/src/model/shifting.ts`:
- Around line 80-88: The repeated pattern that re-applies absolute markers ({
...(aref.absCol ? { absCol: true } : {}), ...(aref.absRow ? { absRow: true } :
{}) }) should be extracted into a small helper to reduce duplication and improve
readability; add a function (e.g., withAbsMarkers(ref: Ref, source: ARef): ARef)
that returns { ...ref, ...(source.absCol ? { absCol: true } : {}),
...(source.absRow ? { absRow: true } : {}) } and replace occurrences in this
file where parseARef, moveRef and toASref are combined (and similar sites in
shiftSref, shiftARef, relocateARef) to call withAbsMarkers(movedOrNewRef,
originalARef) before passing to toASref.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 2da6a9aa-cd32-40a2-b360-117ee1d27150
📒 Files selected for processing (4)
packages/sheet/src/model/coordinates.tspackages/sheet/src/model/shifting.tspackages/sheet/src/model/types.tspackages/sheet/test/sheet/shifting.test.ts
hackerwins
left a comment
There was a problem hiding this comment.
Thanks for your contribution.
* Slides: text-edit + adjust drag entry on grouped elements (#22, #31) Two view-layer paths assumed `slide.elements` is flat: - `enterEditMode` resolved the click target via `Array.prototype.find`, so double-click on text/shape elements nested inside a group silently no-op'd. Slide 22 of the Yorkie deck (text "웹/모바일..." in the right column) reproduced this — `Selection.doubleClick` drilled in correctly but the mount path bailed. - `startAdjustmentDrag` used the same flat `find`, so the yellow adjustment diamond on a grouped shape armed but never wrote. Slide 31's Dogfooding pentagonArrow reproduced this. The render-side mask for the editing element and the live preview for adjustments had the matching flat `slide.elements.map` bug — even if the entry paths were fixed in isolation, the canvas would still ghost-paint the element under the in-place editor / miss the live preview for grouped shapes. Both paths now resolve elements via the existing recursive helpers (`findElement`, `buildElementWorldLookup`) and pass world-frame elements where downstream code expects world coords (overlay text-box mount, world↔local adjustment conversion). The store-side mutation APIs (`requireElement`, `updateElementFrame`, `updateElementData`, `withTextElement`, `withShapeText`) already walk the tree via `findElementPath`, so the writes work transparently. Live-verified on slide 22 (text edit enters at the correct world position, no canvas ghost) and slide 31 (Dogfooding arrow's adjustments commit after drag). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * Slides: skip autofit-grow write when ancestor group is transformed Code review caught a silent miswrite hole in the prior commit: text-edit autofit-grow at commit time writes the editor-reported content height (world / canvas-logical coords) straight into `frame.h` via `store.updateElementFrame`, which expects LOCAL coords. For top-level elements local === world; for groups with rotation 0 + unit scale, w/h/rotation also match. But for a rotated or non-unit-scale group, the world height would be stored as local height, scrambling the element's frame. Detect transformed ancestor by comparing local vs world frame (w/h/rotation) at enter time and skip the autofit-grow write when they diverge. Properly composing the inverse ancestor transform is the right long-term fix, but until then this guard prevents the silent corruption. Scope is unchanged: slide 22's groups have rotation 0 + refSize === frame, so the autofit-grow path still runs there. Also annotate the todo's out-of-scope section with the matching `startConnectorEndpointDrag` flat-find pattern (editor.ts:3135) so future audits don't miss it. PPTX import can produce connectors inside groups; the editor-side group op forbids it, so the user-reachable failure mode is import-only — deferred. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
Summary
Add ARef type and parseARef/toASref helpers to preserve $ flags through all formula rewriting paths (shift, relocate, redirect, move).
Why
Previously, shifting, relocating, and redirecting formulas would strip the $ markers from cell references like $A$1, $A1, or A$1. This caused incorrect behavior when copy-pasting or inserting rows/columns in formulas that used absolute references.
Linked Issues
Fixes #
Verification
CI automatically posts a verification summary comment on this PR with
per-lane results for both
verify:selfandverify:integration.Skip reason (if applicable):
Risk Assessment
Notes for Reviewers
Summary by CodeRabbit
Release Notes
New Features
Tests