Add in-app help, formula reference outlines, underline and auto-closing
- Help dialog (?, F1, Datei > Hilfe): user topics, all shortcuts and the formula functions with one search over everything. Topics and shortcuts are plain data in data/help.ts; the function list comes from codeReference.ts so new built-ins appear by themselves. - While a formula is edited, the grid outlines each referenced cell/range in the colour of its position in the text, and the outline can be dragged to move or resize the reference, as in Excel. Point-mode picks (click, drag, arrow keys) are outlined too, so keyboard picking is no longer blind. - Underline as a cell format, plus Mod+Shift+B/I/U toggles that switch on unless every selected cell already has the format. - A formula committed with open parentheses or an open text literal gets the missing closers appended; anything that still doesn't parse is left alone so the usual error shows instead of a guess. - Cells and tables with a fill but no text colour get dark or light text by WCAG contrast instead of the theme colour (no black on black).
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import {
|
||||
parseFormula,
|
||||
completeFormula,
|
||||
parseFunctionBlock,
|
||||
parseMacroBlock,
|
||||
collectRefs,
|
||||
@@ -489,3 +490,21 @@ describe('collectRefsInBlock -- reference walk used by the macro save-time quali
|
||||
expect(refs).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('completeFormula -- closing what Enter left open', () => {
|
||||
it('appends missing closing parentheses', () => {
|
||||
expect(completeFormula('=SUMME(A1:A5')).toBe('=SUMME(A1:A5)');
|
||||
expect(completeFormula('=RUNDEN(SUMME(A1;B1)*(2+3')).toBe('=RUNDEN(SUMME(A1;B1)*(2+3))');
|
||||
expect(completeFormula('=SUMME(A1 ')).toBe('=SUMME(A1 )');
|
||||
});
|
||||
it('closes an open text literal before the parentheses', () => {
|
||||
expect(completeFormula('=WENN(A1>0;"ja')).toBe('=WENN(A1>0;"ja")');
|
||||
});
|
||||
it('leaves valid formulas, plain text and unfixable input alone', () => {
|
||||
expect(completeFormula('=SUMME(A1:A5)')).toBe('=SUMME(A1:A5)');
|
||||
expect(completeFormula('Hallo (Welt')).toBe('Hallo (Welt');
|
||||
expect(completeFormula('=SUMME(A1+')).toBe('=SUMME(A1+');
|
||||
expect(completeFormula('=A1)')).toBe('=A1)');
|
||||
expect(completeFormula('=(')).toBe('=(');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { parseA1, toA1, cycleReferenceLocks, currentIdentifierAtCaret } from '../refs';
|
||||
import { parseA1, toA1, cycleReferenceLocks, currentIdentifierAtCaret, formulaRefSpans } from '../refs';
|
||||
|
||||
describe('parseA1 / toA1: $-locked (absolute/mixed) references', () => {
|
||||
it('parseA1 reports which axes are $-locked', () => {
|
||||
@@ -121,3 +121,30 @@ describe('currentIdentifierAtCaret -- function-name autocomplete detection', ()
|
||||
expect(currentIdentifierAtCaret('=SUM("x', 7)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('formulaRefSpans -- references with their position in the text', () => {
|
||||
const texts = (f: string) => formulaRefSpans(f).map((s) => f.slice(s.start, s.end));
|
||||
|
||||
it('finds cells, ranges and whole lines in text order', () => {
|
||||
expect(texts('=SUMME(A1:B3;$C$2)*D4+B:B')).toEqual(['A1:B3', '$C$2', 'D4', 'B:B']);
|
||||
const [r] = formulaRefSpans('=A1:$B$3');
|
||||
expect(r.a).toEqual({ col: 0, row: 0, colAbs: false, rowAbs: false });
|
||||
expect(r.b).toEqual({ col: 1, row: 2, colAbs: true, rowAbs: true });
|
||||
expect(formulaRefSpans('=2:5')[0]).toMatchObject({ whole: 'row', a: { row: 1 }, b: { row: 4 } });
|
||||
});
|
||||
|
||||
it('includes the qualifier in the span and reports it separately', () => {
|
||||
const f = "='Kosten Q3'!A1+Analyse!Umsatz::B2:C4";
|
||||
expect(texts(f)).toEqual(["'Kosten Q3'!A1", 'Analyse!Umsatz::B2:C4']);
|
||||
const [q, s] = formulaRefSpans(f);
|
||||
expect(q).toMatchObject({ qualifier: 'Kosten Q3', refStart: f.indexOf('A1') });
|
||||
expect(s).toMatchObject({ qualifier: 'Umsatz', sheetQualifier: 'Analyse' });
|
||||
});
|
||||
|
||||
it('skips function names, strings and named ranges; tolerates unfinished input', () => {
|
||||
expect(texts('=LOG10(A1)&"B2"&Umsatz_Q1')).toEqual(['A1']);
|
||||
expect(texts('=SUMME(A1:A5;B')).toEqual(['A1:A5']);
|
||||
expect(texts('=WENN(A1;"offen')).toEqual([]);
|
||||
expect(texts('A1')).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user