Keys and mouse for tabs, references to other sheets, find's scope, undo across sheets, file format version 4 and XLSX with every sheet, the workbook in the architecture notes, and what workbooks cost in docs/limits.md, measured before and after.
- commit
- 3573aa234f6e00b95b8ec622e8ec238110144fdd
- parent
- d6e534848d4fedbba22d9c3f0a59305a845dfbc0
- author
- Dami <dami.etoile@gmail.com>
| M | README.md | 3 | ++ |
| M | docs/ | 26 | ++++++++++++------ |
| M | docs/ | 13 | ++++++--- |
| M | docs/ | 44 | ++++++++++++++++++++++++++++-- |
| M | docs/ | 15 | ++++++++-- |
| M | docs/ | 13 | ++++++++- |
| M | docs/ | 36 | +++++++++++++++++++++++- |
@@ -35,6 +35,9 @@ searches every command.
3535 delete rows and columns.3636 - **Formats and styles.** Currency, percent, dates and times detected as you3737 type; number formats, bold, italic, underline, strikethrough, alignment.38+- **Several sheets.** Sheet tabs on the status line, references between39+ sheets (`=Sheet2!A1`) that follow renames, pointing into another sheet40+ while typing a formula, and Sheets' keys for moving between them.3841 - **Data tools.** Freeze rows and columns, multi-column sort, filters with3942 value pickers and conditions, find and replace with regular expressions,4043 tracing precedents and dependents.@@ -16,24 +16,32 @@ oracle/ differential tests against excelize (separate module)
1616 1717 ## The engine1818 19-`internal/sheet` knows nothing about terminals. Cells live in a sparse map20-from address to cell; each cell keeps what was typed, its parsed expression,19+`internal/sheet` knows nothing about terminals. A `Workbook` holds ordered20+`Sheet`s, the named ranges, the undo history, the arithmetic setting and21+recalculation, as a Sheets spreadsheet does. Each sheet keeps its cells in22+a sparse map from address to cell, with its column widths, frozen panes,23+filter and charts; each cell keeps what was typed, its parsed expression,2124 its computed value, and its format and style.2225 2326 - **Parsing.** A hand-written Pratt parser turns formulas into an AST,2427 keeping absolute markers so references can be rewritten when cells move.2528 A printer turns ASTs back into text in Sheets' spelling.26-- **Recalculation.** Formulas record the cells and ranges they read.27- Changing a cell marks it and everything that transitively depends on it,28- then evaluates the marked cells lazily in dependency order; a cell reached29- again while it is being evaluated is part of a cycle. Volatile functions30- (TODAY, RAND, the JEV functions) are recomputed on every recalculation.29+- **Recalculation.** Formulas record the cells and ranges they read on30+ their own sheet; references that name a sheet (`Sheet2!A1`) are kept by31+ name and resolved when evaluated, so renaming rewrites them and a deleted32+ sheet's references wait, as `#REF!`, for a sheet of that name. Changing a33+ cell marks it and everything that transitively depends on it, on any34+ sheet, then evaluates the marked cells lazily in dependency order; a cell35+ reached again while it is being evaluated is part of a cycle. Volatile36+ functions (TODAY, RAND, the JEV functions) are recomputed on every37+ recalculation.3138 - **Functions.** One table (`FuncDef`) holds every function's name,3239 signature, description and arity; it drives parsing, evaluation,3340 autocomplete, in-app help and [functions.md](functions.md).3441 - **Undo.** Every mutation goes through a small set of paths that snapshot35- the cells, widths, names, charts and view state they change, so a step can36- be reversed exactly; multi-cell operations are one step.42+ the cells, widths, names, charts, view state and sheet list they change,43+ on any sheet, so a step can be reversed exactly; multi-cell operations44+ are one step. A deleted sheet keeps its cells, so undo brings it back.3745 - **JEV.** The engine never touches the network. JEV functions describe a3846 question and look up the answer in a pluggable source; `internal/jev`3947 answers from a cache and queues new questions, and the UI sends them as@@ -24,9 +24,11 @@ while typing enters the same entry into every selected cell.
2424 2525 ## Undo2626 27-Ctrl+Z undoes and Ctrl+Y or Ctrl+Shift+Z redoes, 100 steps deep. Every change28-is covered: typing, clearing, paste, fill, sort, insert and delete, formats,29-widths, names, charts, freeze and filters. A multi-cell change is one step,27+Ctrl+Z undoes and Ctrl+Y or Ctrl+Shift+Z redoes, 100 steps deep, across28+every sheet: undo shows the sheet a step changed. Every change is covered:29+typing, clearing, paste, fill, sort, insert and delete, formats, widths,30+names, charts, freeze, filters, and adding, deleting, renaming, moving and31+duplicating sheets. A multi-cell change is one step,3032 and the context line says what was undone, e.g. `Undid: clear B3:B5`.3133 3234 ## Rows and columns@@ -67,4 +69,7 @@ the active cell follows the current one, and Enter and Shift+Enter step
6769 through them. Ctrl+H adds a replacement field; Enter replaces and moves on,6870 Ctrl+Enter replaces all as one undo step. Chips toggle match case (Alt+C),6971 whole cell (Alt+W), regular expressions (Alt+R, with `$1` in replacements),70-searching formulas (Alt+=), and limiting the search to the selection (Alt+S).72+searching formulas (Alt+=). The scope chip says where to search, as Sheets'73+"Search" choice: this sheet, all sheets, or the range selected when the bar74+opened; Alt+S goes through them. Searching all sheets steps from sheet to75+sheet in tab order, and replacing all across sheets is still one undo step.@@ -1,6 +1,7 @@
11 # Files22 3-Sheets save as `.012` files (JSON, one line per cell). Other formats come3+Spreadsheets save as `.012` files (JSON, one line per cell), with every4+sheet. Other formats come45 in through File > Import, File > Open or the command line, and go out56 through File > Download. Imports run in the background with a progress67 bar; Esc cancels. Saving an imported sheet asks whether to save it as a@@ -9,8 +10,8 @@ bar; Esc cancels. Saving an imported sheet asks whether to save it as a
910 | Format | Import | Download |1011 |---|---|---|1112 | CSV, TSV | Delimiter (`,` `;` tab `\|`), UTF-8 BOM, UTF-16 and Windows-1252 detected; entries become numbers, dates, currency and percentages as if typed; formulas stay text | Values as shown, as Sheets' Download does |12-| Excel `.xlsx` | First sheet: values, formulas, number formats, bold, italic, underline, strikethrough, alignment, column widths. Formulas 012 can't read (other sheets, unknown functions) keep their values | The same, with formulas in Excel's syntax and their results cached. JEV functions and `#AND#` save as values |13-| SQLite | Pick a table or view, or type a query; a header row names the columns | The sheet or the selection as a table, first row as column names; a table of that name is replaced |13+| Excel `.xlsx` | Every sheet, opening on the one Excel showed: values, formulas (references between sheets too), workbook named ranges, number formats, bold, italic, underline, strikethrough, alignment, column widths. Formulas 012 can't read (unknown functions) keep their values | Every sheet, the same, with formulas in Excel's syntax and their results cached. JEV functions and `#AND#` save as values |14+| SQLite | Pick a table or view, or type a query; a header row names the columns | The sheet shown or the selection as a table, first row as column names; a table of that name is replaced |1415 | Parquet | Every column, with dates and timestamps; lists joined with commas | |1516 | Lotus 1-2-3 `.wk1`, `.wks` | Numbers, labels with their alignment, formats, column widths, formulas translated (references, operators, `@SUM`, `@AVG`, `@IF`, `@ROUND`, `@PMT` and 60 more) or kept as values | |1617 @@ -36,3 +37,40 @@ small object. Version 3 adds named ranges, frozen panes and a filter, and is
3637 only written when a sheet uses one of them, so older builds of 012 can open3738 everything else. Charts are an optional `charts` field that older builds3839 ignore. Saves are atomic: 012 writes a temporary file and renames it.40+41+Other formats import as one sheet named after the file (or the SQLite42+table); CSV and TSV downloads write the sheet shown, as Sheets' do.43+44+## Several sheets45+46+A file of one sheet, whose formulas name no sheet, keeps the single-sheet47+layout above, so older builds of 012 open it; a renamed sheet adds a `name`48+field they ignore. Anything else is version 4: the sheets are a list, each49+with its name and the fields a version 3 file has at the top, and named50+ranges say their sheet. The workbook's settings (named ranges, decimal51+arithmetic, the sheet shown when saved) stay at the top:52+53+```json54+{55+ "version": 4,56+ "names": {"Rent": "Budget!B1"},57+ "active": 1,58+ "sheets": [59+ {60+ "name": "Budget",61+ "cells": {62+ "A1": "Rent",63+ "B1": "1450"64+ }65+ },66+ {67+ "name": "Q3 plan",68+ "cells": {69+ "A1": "=Budget!B1*3"70+ }71+ }72+ ]73+}74+```75+76+Older builds refuse version 4 files rather than lose sheets.@@ -29,7 +29,16 @@ caret on the problem and says what's wrong on the context line, e.g.
2929 - Named ranges: `=SUM(Sales)`. Define them from Data > Named ranges or Data >3030 Define named range. Names are case-insensitive, follow the selection when3131 rows move, and renaming one rewrites the formulas that use it.32-- The worksheet is 256 columns (A to IV) by 8,192 rows, 1-2-3's size; see32+- Other sheets: `=Sheet2!A1`, `=SUM('Q3 plan'!B2:B9)`; names with spaces or33+ punctuation, or that look like a cell, go in single quotes. While typing a34+ formula, Ctrl+PgDn or clicking a tab points into another sheet and inserts35+ the reference. Renaming a sheet rewrites the formulas that use it;36+ deleting one leaves them as written, showing `#REF!` ("Unresolved sheet37+ name") until a sheet of that name exists again, as in Sheets. Inserting38+ and deleting rows moves references into that sheet from every sheet, and39+ leaves references to other sheets alone. Named ranges belong to the whole40+ file and may point into any sheet.41+- Each sheet is 256 columns (A to IV) by 8,192 rows, 1-2-3's size; see3342 [limits.md](limits.md) for what that means in practice.3443 3544 ## Building formulas@@ -74,8 +83,8 @@ circular reference.
7483 7584 Like Sheets and Excel, 012 computes in binary floating point, so7685 `=0.1+0.2=0.3` is FALSE and `=INT($4.35*100)` is 434. File > Settings >77-Decimal arithmetic (or search the palette for "decimal") switches the file to78-decimal math for money. The status line then says `decimal`, the menu shows a86+Decimal arithmetic (or search the palette for "decimal") switches the file,87+every sheet of it, to decimal math for money. The status line then says `decimal`, the menu shows a7988 check mark, and the setting is saved with the file and can be undone.8089 8190 | Computed in decimal | Stays binary |@@ -39,7 +39,10 @@ Inside the grid, 012 works like Google Sheets.
3939 | Ctrl+Shift+L / E / R | Align left, center, right |4040 | Ctrl+\ | Clear formatting |4141 | Ctrl+S, Ctrl+O, Ctrl+Q | Save, open (imports other formats), quit |42-| Ctrl+G or F5 | Go to a cell, a range or a named range |42+| Ctrl+G or F5 | Go to a cell, a range or a named range, on any sheet (`Sheet2!B3`) |43+| Ctrl+PgDn / Ctrl+PgUp, Alt+Right / Alt+Left | Next / previous sheet; while typing a formula, point into it to insert `Sheet2!A1` |44+| Shift+F11 | New sheet (also Insert > Sheet) |45+| Alt+Shift+K | Go to a sheet by name |4346 | Alt+F, Alt+E, Alt+V, Alt+I, Alt+O, Alt+H, F10, click a title | Open a menu (arrows move, Enter runs, Esc closes) |4447 | Ctrl+K, Alt+/, Ctrl+Shift+P | Search the menus: find and run any command |4548 | Right-click, Shift+F10 | Cell, column or row menu |@@ -58,6 +61,13 @@ edge of the grid scrolls. Right-click opens the cell, column or row menu.
5861 The mouse pointer changes shape over cells, resize handles and the formula5962 bar in terminals that support it (OSC 22).6063 64+The sheet tabs at the left of the status line take the mouse too: click a65+tab to show its sheet, double-click to rename it, right-click for its menu66+(rename, duplicate, delete, move left or right), drag it onto another tab to67+move it there, and click `+` to add a sheet. When the tabs don't all fit,68+`‹` and `›` step through them. While typing a formula, clicking a tab points69+into that sheet, as in Sheets.70+6171 In Ghostty and xterm, Shift+click normally starts the terminal's own text6272 selection; 012 asks the terminal to pass it through (XTSHIFTESCAPE) and6373 restores that on exit.@@ -68,5 +78,6 @@ The top three lines are the **menu bar** with the mode indicator on the
6878 right, the **formula bar** (name box, then the cell's contents or the entry6979 being typed) and the **context line**, which holds prompts, key hints,7080 formula errors and explanations. The bottom line is the **status line**:81+the sheet tabs (as tmux lists its windows, the sheet shown highlighted),7182 the file name, whether it's modified, and Sum, Avg and Count for a7283 selection.@@ -142,6 +142,38 @@ in flight, so 4000 questions at 300 ms each take 2.5 minutes, of which
142142 5 s is recalculation spread over that time. Each answer blocks the UI143143 for its recalculation (1.3 ms at 4000 cells).144144 145+## Several sheets146+147+A workbook recalculates all its sheets together. Formulas that name a148+sheet (`Data!A1`) are indexed by the sheet names they use: a changed cell149+on a sheet some formula names scans those cross-sheet formulas, as range150+users were scanned before they were indexed by column, so a change there151+costs O(changed cells x cross-sheet formulas); cells on sheets no formula152+names skip the scan. Each sheet's lookup, which resolves sheet names, is153+made once per recalculation, and the recalculation state stays on each154+sheet keyed by address, so a single-sheet workbook computes as before.155+156+| Benchmark (`internal/sheet`, `go test -bench SheetEdit`) | Edit |157+|---|---|158+| 5000 formulas on one sheet reading `A(n)` and `SUM(A1:A10)` | 2.7 ms |159+| The same 5000 formulas reading `Data!A(n)` and `SUM(Data!A1:A10)` | 3.5 ms |160+161+The stress shapes before and after workbooks (5 runs each, same machine;162+differences under about 5% are noise):163+164+| Benchmark | Before | After |165+|---|---|---|166+| RecalcAll dense 8192 x 26 | 36.1 ms | 36.7 ms |167+| RecalcAll fan-in / running | 170 / 706 ms | 177 / 742 ms |168+| Edit chain / fan-in / running | 1.64 / 168 / 694 ms | 1.92 / 173 / 726 ms |169+| Undo chain | 3.4 ms | 4.0 ms |170+| BigUndo | 140 ms, 431 k allocs | 158 ms, 856 k allocs |171+| Open 8192 x 26 / 8192 x 256 | 120 ms / 1.85 s | 120 ms / 1.91 s |172+173+Cell reads now go through one more function call to resolve the sheet,174+which is most of the 3 to 5% on read-heavy shapes; undo steps key their175+before-images by sheet and address, which is BigUndo's extra allocation.176+145177 ## Hotspots found and fixed146178 147179 Each with its benchmark before and after, in the commit that fixed it.@@ -173,8 +205,8 @@ it.
173205 the input string kept only when it differs from the canonical text174206 of the value, would bring numbers to about 20 to 40 B per cell and175207 turn every cell read in recalc from two hash lookups (20 ns) into an176- index (2 to 3 ns). This is also the change the multi-sheet refactor177- touches, so it is best planned with it.208+ index (2 to 3 ns). Cells are already per sheet in a workbook, so the209+ storage can change sheet by sheet.178210 2. **Range dependency index and shared range results** (M, 1 week).179211 Range users are now indexed by column; an interval index per column180212 (or per row block) would make finding them independent of how many