Tabellen-Umbenennung schreibt Referenzen dokumentweit um
Bisher speicherte eine Formel den Tabellennamen nur als Text im
Qualifier ("Umsatz!B2") – beim Umbenennen der Tabelle wurde nirgendwo
danach gesucht, sodass jede andere Formel, die noch den alten Namen
nannte, beim nächsten Neuberechnen #REF zeigte.
renameObject() ruft jetzt vor der eigentlichen Umbenennung eine neue
renameDocumentRefs()/rewriteRefsForRename() auf, die dieselbe
Token-Splice-Technik wiederverwendet, die für das dokumentweite
Umschreiben bei Zeilen-/Spalten-Einfügen bereits existiert: jede Formel
im Dokument wird tokenisiert, jeder Qualifier über die echte
wb.resolveTableName-Auflösung (nicht per String-Vergleich) auf die
umbenannte Tabelle geprüft, und nur die Tabellen-Namens-Textspanne
ersetzt – bei der 3-teiligen "Blatt!Tabelle::Zelle"-Form bleibt der
Blatt-Teil unangetastet, ein neuer Name mit Leerzeichen wird korrekt
gequotet. Umbenennen + Umschreiben zählen als ein Undo-Schritt.
Bewusst auf Tabellenzellen begrenzt, wie schon beim Zeilen-/Spalten-
Umschreiben (keine Makro-Aktionen, keine Funktionskörper).
Live per Playwright verifiziert: "Umsatz" per Doppelklick zu "Verkauf"
umbenannt, =SUM(Umsatz!B2:E2) in einer anderen Tabelle wurde automatisch
zu =SUM(Verkauf!B2:E2), Wert blieb unverändert (kein #REF).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1244,6 +1244,129 @@ describe('insertRow/deleteRow/insertColumn/deleteColumn (mid-grid, not just edge
|
||||
});
|
||||
});
|
||||
|
||||
describe('renaming a table rewrites every other formula\'s qualifier (document-wide, not just names re-resolving)', () => {
|
||||
const rawInput = (s: DocumentStore, table: string, a1: string) => {
|
||||
const { col, row } = coords(a1);
|
||||
return s.cellInput(tableId(s, table), col, row);
|
||||
};
|
||||
|
||||
it('a plain "Alt!B2" reference in ANOTHER table becomes "Neu!B2" and keeps evaluating correctly', () => {
|
||||
const s = fresh();
|
||||
const src = s.addTable(s.doc.activeSheetId);
|
||||
const srcName = s.tableById(src)!.name;
|
||||
const dst = s.addTable(s.doc.activeSheetId);
|
||||
const dstName = s.tableById(dst)!.name;
|
||||
setCell(s, srcName, 'B2', '10');
|
||||
setCell(s, dstName, 'A1', `=${srcName}!B2`);
|
||||
expect(val(s, dstName, 'A1')).toBe(10);
|
||||
|
||||
s.renameObject(src, 'Neu');
|
||||
expect(rawInput(s, dstName, 'A1')).toBe('=Neu!B2');
|
||||
expect(val(s, dstName, 'A1')).toBe(10); // still resolves -- no #REF
|
||||
});
|
||||
|
||||
it('the "::" qualifier form is rewritten too', () => {
|
||||
const s = fresh();
|
||||
const src = s.addTable(s.doc.activeSheetId);
|
||||
const srcName = s.tableById(src)!.name;
|
||||
const dst = s.addTable(s.doc.activeSheetId);
|
||||
const dstName = s.tableById(dst)!.name;
|
||||
setCell(s, dstName, 'A1', `=${srcName}::B2+1`);
|
||||
|
||||
s.renameObject(src, 'Neu');
|
||||
expect(rawInput(s, dstName, 'A1')).toBe('=Neu::B2+1');
|
||||
});
|
||||
|
||||
it('a range reference keeps its range, only the qualifier changes', () => {
|
||||
const s = fresh();
|
||||
const src = s.addTable(s.doc.activeSheetId);
|
||||
const srcName = s.tableById(src)!.name;
|
||||
const dst = s.addTable(s.doc.activeSheetId);
|
||||
const dstName = s.tableById(dst)!.name;
|
||||
setCell(s, dstName, 'A1', `=SUM(${srcName}!B2:B6)`);
|
||||
|
||||
s.renameObject(src, 'Neu');
|
||||
expect(rawInput(s, dstName, 'A1')).toBe('=SUM(Neu!B2:B6)');
|
||||
});
|
||||
|
||||
it('only the TABLE part of a 3-part "Blatt!Tabelle::Zelle" reference is rewritten, the sheet stays', () => {
|
||||
const s = fresh();
|
||||
const sheet2 = s.addSheet('Zweites');
|
||||
const src = s.addTable(sheet2);
|
||||
const srcName = s.tableById(src)!.name;
|
||||
s.setActiveSheet(s.doc.sheets[0].id);
|
||||
const dst = s.addTable(s.doc.sheets[0].id);
|
||||
const dstName = s.tableById(dst)!.name;
|
||||
const sheet2Name = s.sheetById(sheet2)!.name;
|
||||
setCell(s, dstName, 'A1', `=${sheet2Name}!${srcName}::B2`);
|
||||
|
||||
s.renameObject(src, 'Neu');
|
||||
expect(rawInput(s, dstName, 'A1')).toBe(`=${sheet2Name}!Neu::B2`);
|
||||
});
|
||||
|
||||
it('renaming to a name that needs quoting produces a properly quoted qualifier', () => {
|
||||
const s = fresh();
|
||||
const src = s.addTable(s.doc.activeSheetId);
|
||||
const srcName = s.tableById(src)!.name;
|
||||
const dst = s.addTable(s.doc.activeSheetId);
|
||||
const dstName = s.tableById(dst)!.name;
|
||||
setCell(s, dstName, 'A1', `=${srcName}!B2`);
|
||||
|
||||
s.renameObject(src, 'Neuer Name');
|
||||
expect(rawInput(s, dstName, 'A1')).toBe("='Neuer Name'!B2");
|
||||
});
|
||||
|
||||
it('a formula referencing a DIFFERENT, un-renamed table stays byte-identical', () => {
|
||||
const s = fresh();
|
||||
const src = s.addTable(s.doc.activeSheetId);
|
||||
const other = s.addTable(s.doc.activeSheetId);
|
||||
const otherName = s.tableById(other)!.name;
|
||||
const dst = s.addTable(s.doc.activeSheetId);
|
||||
const dstName = s.tableById(dst)!.name;
|
||||
const untouched = `=${otherName}!B2 + 1`;
|
||||
setCell(s, dstName, 'A1', untouched);
|
||||
|
||||
s.renameObject(src, 'Neu');
|
||||
expect(rawInput(s, dstName, 'A1')).toBe(untouched);
|
||||
});
|
||||
|
||||
it('a redundant self-qualified reference inside the renamed table itself is rewritten too', () => {
|
||||
const s = fresh();
|
||||
const id = s.addTable(s.doc.activeSheetId);
|
||||
const name = s.tableById(id)!.name;
|
||||
setCell(s, name, 'B2', '10');
|
||||
setCell(s, name, 'A1', `=${name}!B2`); // redundant explicit self-qualifier
|
||||
|
||||
s.renameObject(id, 'Neu');
|
||||
expect(rawInput(s, 'Neu', 'A1')).toBe('=Neu!B2');
|
||||
expect(val(s, 'Neu', 'A1')).toBe(10);
|
||||
});
|
||||
|
||||
it('the rewrite and the rename coalesce into a single undo step', () => {
|
||||
vi.useFakeTimers();
|
||||
try {
|
||||
const s = fresh();
|
||||
const src = s.addTable(s.doc.activeSheetId);
|
||||
const srcName = s.tableById(src)!.name;
|
||||
const dst = s.addTable(s.doc.activeSheetId);
|
||||
const dstName = s.tableById(dst)!.name;
|
||||
setCell(s, dstName, 'A1', `=${srcName}!B2`);
|
||||
vi.advanceTimersByTime(600); // let the setup settle first
|
||||
|
||||
s.renameObject(src, 'Neu');
|
||||
vi.advanceTimersByTime(600);
|
||||
expect(rawInput(s, dstName, 'A1')).toBe('=Neu!B2');
|
||||
|
||||
s.undo(); // one Ctrl+Z undoes BOTH the rename and the ref rewrite together
|
||||
expect(s.tableById(src)!.name).toBe(srcName);
|
||||
expect(rawInput(s, dstName, 'A1')).toBe(`=${srcName}!B2`);
|
||||
} finally {
|
||||
vi.runOnlyPendingTimers();
|
||||
vi.useRealTimers();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('dependency-graph view (cellGraphSelf/cellGraphNeighbors)', () => {
|
||||
it('cellGraphSelf describes a formula cell: label, raw text, value, table/sheet name', () => {
|
||||
const s = fresh();
|
||||
|
||||
Reference in New Issue
Block a user