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:
@@ -13,7 +13,7 @@ Vorbild von **Apple Numbers**. Zwei Dinge stehen im Mittelpunkt:
|
||||
```bash
|
||||
npm install
|
||||
npm run dev # Vite-Devserver
|
||||
npm run test # Vitest-Suite (Engine + Store + IO), 105 Tests
|
||||
npm run test # Vitest-Suite (Engine + Store + IO), 121 Tests
|
||||
npm run build # Typecheck + Produktionsbuild
|
||||
npm run lint # ESLint (flat config)
|
||||
```
|
||||
@@ -63,8 +63,19 @@ umbenennen, löschen, per Drag sortieren). Zoom 40–200 % pro Blatt
|
||||
- Die **diskontinuierliche Mehrfachauswahl** trägt alle folgenden Aktionen:
|
||||
Live-Aggregat (Summe/Mittel/Anzahl) in der Statusleiste, die
|
||||
**Auswahl-Formatierung** im Inspector und `Entf` (leert alle markierten Zellen).
|
||||
- Pfeiltasten / Enter kollabieren wieder auf eine Zelle. Die aktive Zelle
|
||||
(Formelleiste) ist stets die zuletzt angeklickte.
|
||||
- Pfeiltasten / Enter kollabieren wieder auf eine Zelle. Die **aktive Zelle**
|
||||
(blauer Rahmen, Formelleiste, `F2`/Tippen zum Bearbeiten) ist stets die
|
||||
**zuerst** angeklickte/gesetzte Zelle einer Auswahl — sie bleibt beim
|
||||
Ziehen/Erweitern eines Bereichs stehen, statt dem Cursor zu folgen (Excel-/
|
||||
Numbers-Verhalten). Während einer Tab/Enter-Dateneingabefolge im Bereich
|
||||
wandert sie mit dem Cursor mit.
|
||||
- **Kopieren/Ausschneiden/Einfügen** (`Strg`/`Cmd`+`C`/`X`/`V`, Kontextmenü):
|
||||
überträgt Zellinhalte **und** Formatierung. Einfügen auf eine Auswahl, die
|
||||
ein exaktes Vielfaches der kopierten Größe ist, **kachelt**; eine kleinere
|
||||
oder Einzelzell-Auswahl verankert die kopierte Fläche dort und läuft darüber
|
||||
hinaus. Formeln werden dabei **bezugsangepasst** wie in Excel/Numbers — siehe
|
||||
„`$`-Bezüge sperren" unten; ein gestrichelter Rahmen markiert die
|
||||
Kopierquelle, `Esc` verwirft den Puffer.
|
||||
|
||||
**Tastaturnavigation (Excel-nah)** — Fokus in der Tabelle:
|
||||
|
||||
@@ -78,6 +89,8 @@ umbenennen, löschen, per Drag sortieren). Zoom 40–200 % pro Blatt
|
||||
| Tippen / `F2` | Zelle editieren; Enter/Tab committen und ziehen weiter (auch im Bereich) |
|
||||
| `Pos1` / `Strg`+`Pos1` / `Ende` / `Strg`+`Ende` | Zeilenanfang / A1 / Zeilenende / letzte Zelle |
|
||||
| `Bild↑` / `Bild↓` | seitenweise (bei festen Tabellen nach Viewport-Höhe) |
|
||||
| **`Strg`/`Cmd`+`C`/`X`/`V`** | Kopieren / Ausschneiden / Einfügen (Zellen + Formatierung, bezugsangepasst) |
|
||||
| **`F4`** (im Editor/in der Formelleiste) | Bezug unter dem Cursor sperren/zyklisch umschalten: `A1` → `$A$1` → `A$1` → `$A1` → `A1` |
|
||||
|
||||
Bei **festen (scrollbaren) Tabellen** wird die aktive Zelle bei jeder Navigation
|
||||
automatisch in den sichtbaren Bereich gescrollt (unter Beachtung fixierter
|
||||
@@ -175,6 +188,16 @@ Numbers-Stil `::` sind austauschbar. Tabellennamen werden dokumentweit eindeutig
|
||||
gehalten und **case-/whitespace-tolerant** aufgelöst:
|
||||
`Kosten!A2` = `'Kosten'!A2` = `kosten!a2` = `Kosten::A2`.
|
||||
|
||||
**`$`-Bezüge sperren** (relativ/gemischt/absolut): `A1` (relativ) vs. `$A1` /
|
||||
`A$1` (Spalte bzw. Zeile gesperrt) vs. `$A$1` (beide gesperrt). Für die
|
||||
Auswertung selbst ist `$` irrelevant — sie ist rein positionsbasiert. Es zählt
|
||||
beim **Kopieren**: eine gesperrte Achse bleibt exakt wie eingegeben stehen,
|
||||
eine ungesperrte verschiebt sich um das Einfüge-Delta (siehe „Kopieren/
|
||||
Ausschneiden/Einfügen" oben). Beim **Ausschneiden** gilt das nicht — dort
|
||||
verschiebt sich die referenzierte Zelle selbst, also folgt jede Referenz
|
||||
darauf, `$` hin oder her. `F4` schaltet den Bezug unter dem Cursor durch die
|
||||
vier Kombinationen.
|
||||
|
||||
**Funktionen** (case-insensitiv, Trenner `;`): `SUM`, `AVG` (Alias `AVERAGE`),
|
||||
`MIN`, `MAX`, `COUNT`. Argumente gemischt: Einzelzellen, Bereiche (lokal oder
|
||||
`Name!…`), Zahl-Literale — z. B. `=SUM(Umsatz!B2:E2; 100; A1)`.
|
||||
@@ -285,11 +308,14 @@ Grundrechenarten: `Übersicht!B2 = =SUM(Umsatz!B2:E2)`, `D2 = =B2-C2`,
|
||||
|
||||
## Tests
|
||||
|
||||
`npm run test` — Vitest, 105 Tests:
|
||||
`npm run test` — Vitest, 121 Tests:
|
||||
|
||||
- **lexer / parser** — Präzedenz & Assoziativität (`^` vor `*/` vor `+-`, `^`
|
||||
rechts, `-2^2 = -(2^2)`), lokale/qualifizierte/quoted Bezüge, `!` **und** `::`,
|
||||
Bereiche, `;`-Argumente, Parse-Fehler.
|
||||
- **refs (`$`-Sperrachsen, `F4`)** — `parseA1`/`toA1` runden relativ/absolut/
|
||||
gemischt; `cycleReferenceLock` durchläuft die vier Kombinationen, trifft
|
||||
bei Cursor auf einem Bereich die richtige Seite, liefert `null` ohne Treffer.
|
||||
- **evaluator** — Arithmetik, leere Zelle = 0, alle Funktionen inkl. Alias,
|
||||
Cross-Table, Case-/`::`-Toleranz, jeder Fehlerfall, Fehler-Propagierung.
|
||||
- **graph** — `affected`, topologische Ordnung, Selbst-/2er-/längerer Zyklus.
|
||||
@@ -319,7 +345,14 @@ Grundrechenarten: `Übersicht!B2 = =SUM(Umsatz!B2:E2)`, `D2 = =B2-C2`,
|
||||
die aktive Zelle, erneutes Klicken schaltet eine 1×1-Auswahl ab,
|
||||
`extendActiveRange` wächst das jüngste Rechteck; Aggregat & Formatierung über
|
||||
**alle** Rechtecke; `#`-genaue Zellzahl; JSON-Round-Trip inkl. `ranges`,
|
||||
Klemmung beim Verkleinern.
|
||||
Klemmung beim Verkleinern; die aktive Zelle bleibt beim Erweitern eines
|
||||
Bereichs der **Anker** (erste Zelle), nicht die zuletzt erreichte.
|
||||
- **Zwischenablage** — Kopieren passt jede relative Referenz um das Einfüge-
|
||||
Delta an, `$`-gesperrte Achsen bleiben stehen; Einfügen kachelt eine exakt
|
||||
passende größere Auswahl bzw. verankert/überläuft eine kleinere; Formatierung
|
||||
wird mitkopiert und am Ziel ersetzt; Ausschneiden folgt trotz `$` der
|
||||
verschobenen Zelle, lässt aber unbeteiligte und tabellenfremde Referenzen
|
||||
unangetastet und leert die Quelle; `Esc` verwirft den Puffer.
|
||||
- **IO** — JSON verlustfreier Round-Trip (identisch), defekte/leere Datei →
|
||||
Fehler ohne Nebenwirkung, unbekannter Tabellenname → `#REF`, v1→v2 beim
|
||||
Import; CSV Übersicht-Export→Re-Import gleiche Zahlen, RFC-4180-Quoting,
|
||||
|
||||
Reference in New Issue
Block a user