The engine already anticipated $A1/A$1/$A$1 in its grammar comment, but the
lexer had no rule for '$' at all -- any formula using it failed to parse.
- lexer.ts: '$' is now a valid word character, so "$A$1" tokenizes as one
IDENT, same as any other bare word (the lexer still doesn't interpret it,
per its "dumb lexer" design -- the parser/evaluator stay untouched, since
evaluation is purely positional and $ has no effect on a value).
- refs.ts: parseA1()/toA1() now carry colAbs/rowAbs through the $ markers
(backward compatible -- existing {col,row} destructuring is unaffected).
Added cycleReferenceLock() for Excel/Numbers-style F4: relative ->
absolute -> row-locked -> col-locked -> relative, toggling whichever
reference sits at the caret (and the correct side of an A1:B2 range).
- document.ts: shiftFormula() now honors the lock on copy (a locked axis
stays exactly as typed) but not on cut (a moved cell's reference follows
it regardless of $, since cut relocates the cell itself rather than
filling a relative offset).
- TableGrid.vue's inline cell editor and FormulaBar.vue both wire up F4 via
the shared cycleReferenceLock() helper.
- Tests: lexer/parser coverage for $ tokenizing/parsing, a new refs.spec.ts
for parseA1/toA1 round-tripping and the F4 cycle (incl. range side-
picking), and document.spec.ts cases for locked-axis copy vs. cut.
- README.md / src/engine/README.md: documented the $ semantics, F4, and
corrected a couple of now-stale claims (test count, "$ is ignored, there's
no copy/fill", "active cell is always the last-clicked one").
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The active cell (bold border/handle, formula bar target) used to track
selection.focus, so it followed the mouse/cursor while dragging or
shift-extending a range. Switch it to selection.anchor -- the first cell of
the current selection action -- so it stays put while the range grows,
matching Excel/Numbers. Tab/Enter data-entry flow already keeps anchor ==
focus while it moves the cursor, so that behaviour is unaffected.
- document.ts: activeCell/activeCellName/activeCellInput read the anchor.
- FormulaBar.vue: commits to the anchor cell to match what it displays.
- TableGrid.vue: isActive() (border + handle) and F2/type-to-edit target
the anchor; Home/End keep using the cursor's row via s.focus.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- store: copySelection/cutSelection/pasteClipboard/cancelClipboard hold an
in-memory (unpersisted) clipboard buffer of raw cell inputs + formatting.
- shiftFormula rewrites every relative cell reference in a copied/cut formula
by the move delta (the engine has no $-absolute refs, so all refs qualify).
Copy shifts every reference uniformly; cut only follows references that
point inside the moved block itself, leaving refs to untouched cells alone
and never touching a qualified Table!A1 reference.
- paste anchors on a smaller/single-cell selection (and overflows past it),
or tiles across a selection that's an exact larger multiple of the buffer.
- TableGrid: Strg+C/X/V shortcuts, context-menu entries, a dashed "clip-src"
source marker, and Escape drops a pending buffer.
- document.spec.ts: coverage for reference shifting, tiled/fill paste, cut's
inside/outside reference split, format copy, and Escape cancelling.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>