cat / 012
clone

a spreadsheet for the terminal: lotus 1-2-3 looks, google sheets keys and formulas, one go binary

Document sheets in the user and developer docs

Dami committed 3573aa2
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>
7 files changed, +128 -22
MREADME.md3++
Mdocs/architecture.md26++++++++++++------
Mdocs/data.md13++++++---
Mdocs/files.md44++++++++++++++++++++++++++++--
Mdocs/formulas.md15++++++++--
Mdocs/keys.md13++++++++-
Mdocs/limits.md36+++++++++++++++++++++++-
M README.md +3 -0
M docs/architecture.md +17 -9
@@ -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
M docs/data.md +9 -4
@@ -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.
M docs/files.md +41 -3
@@ -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.
M docs/formulas.md +12 -3
@@ -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 |
M docs/keys.md +12 -1
@@ -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.
M docs/limits.md +34 -2
@@ -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