Add a sheet qualifier: "Blatt!Tabelle::Zelle", table names unique per sheet

Table names were document-unique; now they're unique per sheet only --
two different sheets may each hold a table called "Umsatz". A plain,
unqualified reference (Umsatz!B2 / Umsatz::B2, unchanged from before)
resolves to the table on the SAME sheet as the formula referencing it
when the name is otherwise ambiguous, falling back to the single
document-wide match when it isn't ambiguous at all -- exactly as
before for every existing formula. The new explicit 3-part form
(Analyse!Umsatz::B2, quoted where a name has spaces) always targets
the named sheet's table regardless of where the formula itself lives,
for the case that's still genuinely ambiguous.

Engine: parser.ts's afterFirstQualifierToken() disambiguates via
lookahead -- "X!Y::" (a name, then "::") is the new 3-part sheet+table
form; a bare "X!Y" keeps its original single-qualifier meaning, so
every formula written before this exists still parses identically.
CellNode/RangeNode gain an optional sheetQualifier field.
Workbook.resolveTableName(name, sheetName?) implements the resolution
policy; TableModel gains sheetName. precedentsOf() now also sets
homeTableId (previously only evaluateCell did), so graph edges use the
same same-sheet-preference resolution an evaluation would.

Store: table name uniqueness checks (addTable, renameObject) are now
scoped to the target sheet instead of the whole document. Point-mode's
qualifyRef() inserts the full "Blatt!Tabelle::" form only when the
clicked table's name is genuinely ambiguous and lives on a different
sheet than the editor; otherwise the simple form, unchanged.

200 tests passing (19 new), vue-tsc and eslint clean, verified live in
the running app: renamed a Blatt-2 table to the same name as Analyse's
seed "Umsatz", confirmed an unqualified reference resolves per-sheet,
an explicit sheet-qualified one always targets the named sheet, and
the original sheet's own formulas are unaffected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
wkh
2026-09-02 16:16:21 +02:00
co-authored by Claude Sonnet 5
parent fdde4ed5a7
commit 7340307a70
11 changed files with 483 additions and 67 deletions
+7 -2
View File
@@ -10,7 +10,12 @@ import { Workbook, type TableModel } from '../workbook';
import { FormulaError, isError, keyOf } from '../types';
// same small A1 helper used by workbook.spec.ts
function sheet(id: string, name: string, cells: Record<string, string>): TableModel {
function sheet(
id: string,
name: string,
cells: Record<string, string>,
sheetName = 'Blatt1',
): TableModel {
const inputs = new Map<string, string>();
for (const [a1, raw] of Object.entries(cells)) {
const m = /^([A-Z]+)([0-9]+)$/.exec(a1)!;
@@ -18,7 +23,7 @@ function sheet(id: string, name: string, cells: Record<string, string>): TableMo
const row = Number(m[2]) - 1;
inputs.set(`${col},${row}`, raw);
}
return { id, name, cols: 8, rows: 30, inputs };
return { id, name, sheetName, cols: 8, rows: 30, inputs };
}
const val = (wb: Workbook, sheetId: string, a1: string) => {
const m = /^([A-Z]+)([0-9]+)$/.exec(a1)!;