Support $-locked references: relative, mixed and absolute
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>
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
* in-cell editing and per-column resize.
|
||||
*/
|
||||
import { computed, nextTick, onBeforeUnmount, ref } from 'vue';
|
||||
import { numberToCol } from '../engine';
|
||||
import { numberToCol, cycleReferenceLock } from '../engine';
|
||||
import { useDocument } from '../store/document';
|
||||
import { DEFAULT_ROW_H } from '../store/types';
|
||||
import ContextMenu from './ContextMenu.vue';
|
||||
@@ -472,9 +472,22 @@ function onEditKeydown(e: KeyboardEvent) {
|
||||
} else if (e.key === 'Escape') {
|
||||
e.preventDefault();
|
||||
cancelEdit();
|
||||
} else if (e.key === 'F4') {
|
||||
e.preventDefault();
|
||||
toggleReferenceLock();
|
||||
}
|
||||
e.stopPropagation();
|
||||
}
|
||||
/** F4: cycle relative -> absolute -> row-locked -> col-locked for the cell
|
||||
* reference at the caret (Excel/Numbers "Bezug sperren"). */
|
||||
function toggleReferenceLock() {
|
||||
const el = editInput.value;
|
||||
if (!el) return;
|
||||
const res = cycleReferenceLock(editValue.value, el.selectionStart ?? editValue.value.length);
|
||||
if (!res) return;
|
||||
editValue.value = res.text;
|
||||
nextTick(() => el.setSelectionRange(res.caret, res.caret));
|
||||
}
|
||||
function onEditBlur() {
|
||||
if (!committing && editing.value) commitEdit();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user