Skip to content

Make cut and paste a move: references follow the moved cells (#51) - #236

Merged
SuruchBoss merged 6 commits into
mainfrom
claude/new-session-m6mehm
Oct 5, 2026
Merged

SuruchBoss merged 6 commits into
mainfrom
claude/new-session-m6mehm

Conversation

@SuruchBoss

Copy link
Copy Markdown
Owner

What this changes

Closes #51. Next in the sprint-final queue: #219 → #59.

The bug: a cut and paste was a copy followed by a clear. A formula that read a cut cell went on reading the now-empty one and showed 0 without a word, and the cut formula itself was shifted the way a copy shifts. In Excel a cut and paste is a move.

The fix:

  • References follow the moved cells. When a cut is pasted, every reference into the cut block is pointed at where it landed. That covers every sheet and every range name, and $A$1 references too, since a move follows the cell. This is the new formulaEngine/moveRefs.ts, run across the workbook by moveRefsInWorkbook.
  • Moved formulas are not shifted. placeMovedBlock writes them as they were, rewriting only references into the block itself. =A1*2 moved from B1 to C5 stays =A1*2. A block that refers to itself still does at its new place.
  • A range follows only when all of it was cut. One that reaches outside the block keeps its address, as in Excel (checked: cutting G1:G2 out of =SUM(G1:G3) leaves the formula as it was).
  • Across sheets, a sheet name is added where one is now needed:
    • a bare A1 on the sheet it left becomes Sheet2!D1;
    • =Sheet1!A10 on Sheet2 becomes =Sheet2!D1 when A10 moves to Sheet2!D1, as Excel writes it (checked);
    • a moved formula's references to cells that stayed behind become Sheet1!C1.
  • A block moved onto part of itself. The source is now emptied before the block is written. On main an overlapping move cleared nothing, so moving A1:A3 to A2:A4 left the old value in A1. Now A1 is empty and references move once.
  • A cut under a filter ([area:ui] Delete / copy / Ctrl+Enter ทำงานกับแถวที่ถูกกรองซ่อนไว้ด้วย · Delete, copy and Ctrl+Enter act on rows hidden by a filter (sev:high) #50) moves only the rows it took. A range over a hidden row stays put.
  • A reference to a cell the block landed on becomes #REF!, as in Excel (checked: with F1 = =H1, cutting D1 onto H1 turns F1 into #REF! while E1 = =D1*2 follows to =H1*2). A range reaching into the landing area is left as it was, since that has not been checked.
  • One update, one undo, for every sheet that changed.

Reproduced on main first

The steps are the issue's: A1 = 5, B1 = =A1*2.

main this PR Excel
B1 after moving A1 → D1 =A1*2, shows 0 =D1*2, shows 10 =D1*2, shows 10
B1 moved → C5 =B5*2 =A1*2 =A1*2
A cell that read B1, after B1 → C5 still =B1+… (empty) =C5+… follows to C5
Moving A1:A3 → A2:A4 A1 keeps its old value A1 empty, SUM(A1:A3) → SUM(A2:A4)
A name on A1, after A1 → G7 still $A$1 $G$7
F1 = =H1, D1 cut onto H1 =H1, reads the pasted 5 =#REF! #REF! (checked)

How it was verified

  • npm run verify (all ten gates): all ten gates green on 968507d. The current head a314bbd carries that tree unchanged; it only adds the merge of main 7060a7d, which holds the same files.
Gate Result
lint · check:readme · check:screens pass
check:deps every advisory fixed or accounted for
test 2018 passed (2007 by the README's static count)
check:mutants 32/32 killed
build · check:bundle 3803/4200 KB, largest chunk 934/1300 KB
check:a11y 121 checks passed
check:e2e 59 flows passed

For PO: checked in Excel, and what is not done

The owner checked three cases in real Excel desktop (2026-10-05), and this PR matches all three:

  • #REF! for a reference to a cell the paste covered;
  • a partly cut range left as it was;
  • =Sheet2!D1 across sheets.

Still not done, and written into the README as limits:

  • A range reaching into the landing area stays as it was. Excel has not been checked for it, so nothing is pinned.
  • Formatting does not move. The source cells keep their bold, colours and number format, and the pasted cells take the block's. Excel moves the formatting and leaves the source plain.

moveRefs.ts is not in check:mutants' target list. Adding it would change which mutants the pinned seed draws, so I left the list alone. Say if you want it added in its own PR.

The branch

This is the session's branch claude/new-session-m6mehm. The work was written on a local branch on top of #232, because both change cutSelection/pasteAtSelection, with main 7060a7d (#234) merged in.

🤖 Generated with Claude Code

https://claude.ai/code/session_013pmWZsbmugfpqtYazrzfjj


Generated by Claude Code

claude added 6 commits October 5, 2026 00:52
A cut was a copy followed by a clear. A formula that read a cut cell went
on reading the now empty one and showed 0, and the cut formula itself was
shifted as a copy would be.

Pasting a cut now rewrites every reference into the cut block, on every
sheet and in every range name, to point at where it landed. The moved
formulas are written unshifted, keeping their references to cells outside
the block (with the source sheet's name when they move to another sheet).
A range follows only when all of it was cut. The source is emptied before
the block is written, so a block moved onto part of itself empties what it
left instead of keeping it. A cut under a filter moves only the rows it took.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013pmWZsbmugfpqtYazrzfjj
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013pmWZsbmugfpqtYazrzfjj
…es (#51)

Checked in real Excel: with F1 = =H1, cutting D1 and pasting it on H1
turns F1 into #REF! while E1 = =D1*2 follows to H1. A single-cell
reference to a cell the pasted block covered now becomes #REF!, with its
sheet name dropped as Excel drops it. A range reaching into the landing
area is left as it was, since that has not been checked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013pmWZsbmugfpqtYazrzfjj
# Conflicts:
#	README.en.md
#	README.md
#	public/screenshots/27-landing-stats.png
#	public/screenshots/en/27-landing-stats.png
#	public/screenshots/screenshots.json
#	public/social-preview.png
#	src/app/opengraph-image.tsx
#	src/i18n/en.ts
#	src/i18n/th.ts
@vercel

vercel Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
excel-to-go Ready Ready Preview Oct 5, 2026 3:30am UTC

@SuruchBoss
SuruchBoss merged commit b8837a9 into main Oct 5, 2026
18 checks passed

Copy link
Copy Markdown
Owner Author

PO review — merged at a314bbd (main b8837a9).

Verify (local, full): green — 2018 tests, mutants 32/32, a11y 121, e2e 59. CI 17/17 green on the same head.

Tried as a user on a production build (keyboard Ctrl+X / Ctrl+V, 1280), against what the owner checked in Microsoft Excel this morning:

  • A reference follows the moved cell: A1=5, B1 =A1*2, A1 cut to D1 → B1 reads =D1*2 = 10 (it read 0 before).
  • A moved formula keeps pointing where it did: B1 cut to C5 → C5 =D1*2; B1 emptied.
  • One undo puts the whole move back; typing after it lands.
  • A reference to a cell the block lands on becomes #REF! (=G1 after cutting A6 onto G1), as in Excel.
  • A range only partly cut stays: SUM(G11:G13) after cutting G11 alone is unchanged.
  • Across sheets: A1 cut from Sheet1 to Sheet2!D1 → Sheet1!B1 reads =Sheet2!D1+1 = 9, A1 emptied, one undo restores both sheets.
  • No page errors, no alerts.

Limits written in the README, accepted: a range that only partly overlaps the paste area stays where it was, and the source cells keep their format after the move (as main did).

Pre-existing, not this PR: a second Ctrl+V after a cut pastes the cut text again from the system clipboard. Nothing is lost; opened as #237 (sev:low, backlog).

Hold set left: #235 → #219 → #59.


Generated by Claude Code

This branch was successfully deployed

1 active deployment
Preview — a314bbd6 Deployed Oct 5, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants