functions.md is generated into docs/reference/ with its front matter, config.md's reference into docs/reference/config.md. The docs tests walk every folder of docs/, and the macro, file format and keys tests read their pages' new paths. Usage text, option descriptions, recorded macro comments and code comments name the pages where they are now.
- commit
- c0adea8b20fb40eb2c2dd9f80851daa960575264
- parent
- dc684acbcc695f109dce887c1b745d0c10bf2639
- author
- Dami <dami.etoile@gmail.com>
@@ -17,7 +17,7 @@ import (
1717 "github.com/FelineStateMachine/012/internal/ui/theme"1818 )1919 20-// runConfig is `012 config ...`: see docs/config.md.20+// runConfig is `012 config ...`: see docs/reference/config.md.2121 func runConfig(args []string, e env) error {2222 cfg, err := loadConfig(e, nil)2323 if err != nil {@@ -52,7 +52,7 @@ func system() env {
5252 func usage() error {5353 return errors.New("usage: 012 " + config.FlagUsage() + " [file]: a " + sheet.FileExt +5454 " sheet, or a .csv, .tsv, .xlsx, .sqlite, .parquet or .wk1 file to import\n" +55- " 012 serve [flags] [dir]: serve sheets in dir over SSH (see docs/ssh.md)\n" +55+ " 012 serve [flags] [dir]: serve sheets in dir over SSH (see docs/terminal/ssh.md)\n" +5656 " 012 config [path|edit|default|themes|set-key|delete-key]")5757 }5858 @@ -195,7 +195,7 @@ The JEV model to ask; the service's default when empty.
195195 196196 #### `log-file`197197 198-Append telemetry events to this JSON log file. See docs/observability.md.198+Append telemetry events to this JSON log file. See docs/contributing/observability.md.199199 200200 | | |201201 |---|---|@@ -232,7 +232,7 @@ Send telemetry to this OTLP/HTTP collector, e.g. http://localhost:4318. The othe
232232 233233 #### `serve-listen`234234 235-The address 012 serve listens on. Anything but the loopback address lets other machines reach it (with an authorized key). See docs/ssh.md.235+The address 012 serve listens on. Anything but the loopback address lets other machines reach it (with an authorized key). See docs/terminal/ssh.md.236236 237237 | | |238238 |---|---|@@ -3,7 +3,7 @@
33 // Stress benchmarks of chart drawing: every chart type as text and as an44 // image, at a typical size and a large one, over a few categories and55 // over a full column of them (only what fits is drawn). Only built with6-// -tags stress (see `make stress` and docs/limits.md).6+// -tags stress (see `make stress` and docs/contributing/limits.md).77 package chart88 99 import (@@ -305,13 +305,13 @@ func TestDir(t *testing.T) {
305305 }306306 }307307 308-var updateDocs = flag.Bool("update-docs", false, "rewrite the reference in docs/config.md")308+var updateDocs = flag.Bool("update-docs", false, "rewrite the reference in docs/reference/config.md")309309 310-// TestConfigDoc keeps docs/config.md's reference in step with the310+// TestConfigDoc keeps docs/reference/config.md's reference in step with the311311 // registry: run `go test ./internal/config -update-docs` after changing312312 // an option.313313 func TestConfigDoc(t *testing.T) {314- const path = "../../docs/config.md"314+ const path = "../../docs/reference/config.md"315315 data, err := os.ReadFile(path)316316 if err != nil {317317 t.Fatal(err)@@ -5,7 +5,7 @@ import (
55 "strings"66 )77 8-// DocsMarker divides docs/config.md: the introduction above it is8+// DocsMarker divides docs/reference/config.md: the introduction above it is99 // written by hand, the reference below it is Reference's output.1010 const DocsMarker = "<!-- Generated from internal/config/registry.go by `go test ./internal/config -update-docs`. Don't edit below. -->"1111 @@ -1,6 +1,7 @@
11 package config22 33 import (4+ "io/fs"45 "os"56 "path/filepath"67 "regexp"@@ -19,7 +20,7 @@ var (
1920 )2021 2122 // notOptions are variables the docs name that aren't options: the JEV key22-// never goes through the config (see docs/jev.md).23+// never goes through the config (see docs/formulas/jev.md).2324 var notOptions = []string{"TYPESAFE_API_KEY"}2425 2526 // TestDocsNameOptions checks the settings and variables the docs name are@@ -27,7 +28,13 @@ var notOptions = []string{"TYPESAFE_API_KEY"}
2728 // comment naming the file, `# ~/.config/012/config`; other blocks (theme2829 // files, shell) aren't read.2930 func TestDocsNameOptions(t *testing.T) {30- paths, err := filepath.Glob("../../docs/*.md")31+ var paths []string32+ err := filepath.WalkDir("../../docs", func(p string, d fs.DirEntry, err error) error {33+ if err == nil && !d.IsDir() && filepath.Ext(p) == ".md" {34+ paths = append(paths, p)35+ }36+ return err37+ })3138 if err != nil {3239 t.Fatal(err)3340 }@@ -98,7 +98,7 @@ func DefaultFile() string {
9898 var b strings.Builder9999 b.WriteString("# 012 configuration. Lines are key = value; # starts a comment.\n")100100 b.WriteString("# Flags and environment variables override this file.\n")101- b.WriteString("# See `012 config` for the values in effect and docs/config.md for more.\n")101+ b.WriteString("# See `012 config` for the values in effect and docs/reference/config.md for more.\n")102102 for _, g := range Groups {103103 fmt.Fprintf(&b, "\n# ---- %s ----\n", g)104104 for _, o := range Options {@@ -2,7 +2,7 @@
22 // "key = value" lines in <user config dir>/012/config, with # comments and33 // config-file includes. One registry (Options) says what every option is44 // called, its type, default, environment variable, flag and meaning; the5-// parser, `012 config`, docs/config.md and the in-app Settings all come5+// parser, `012 config`, docs/reference/config.md and the in-app Settings all come66 // from it.77 //88 // Settings are process-wide. Settings that belong to a workbook (decimal@@ -43,7 +43,7 @@ func (k Kind) String() string {
4343 type Option struct {4444 Name string4545 Kind Kind46- Group string // heading in docs/config.md and `012 config`46+ Group string // heading in docs/reference/config.md and `012 config`4747 Default string // as it would be written in the file4848 Values []string // for Enum4949 Env []string // environment variables that set it, first set wins@@ -73,7 +73,7 @@ const (
7373 var Groups = []string{GroupAppearance, GroupData, GroupJEV, GroupTelemetry, GroupServe, GroupFiles}7474 7575 // Options is every setting. Add an option here and read it with76-// Config.String, Bool or List; parsing, `012 config`, docs/config.md76+// Config.String, Bool or List; parsing, `012 config`, docs/reference/config.md7777 // (go test ./internal/config -update-docs) and Settings follow.7878 var Options = []Option{7979 {Name: "theme", Group: GroupAppearance, Default: "terminal", Env: []string{"O12_THEME"}, Flag: "--theme", Live: true,@@ -113,7 +113,7 @@ var Options = []Option{
113113 Desc: "The JEV model to ask; the service's default when empty."},114114 115115 {Name: "log-file", Kind: Path, Group: GroupTelemetry, Env: []string{"O12_LOG"}, Flag: "--log",116- Desc: "Append telemetry events to this JSON log file. See docs/observability.md."},116+ Desc: "Append telemetry events to this JSON log file. See docs/contributing/observability.md."},117117 {Name: "log-level", Kind: Enum, Group: GroupTelemetry, Default: "info", Values: []string{"debug", "info", "warn", "error"},118118 Env: []string{"O12_LOG_LEVEL"},119119 Desc: "The least severe telemetry event recorded; debug adds every frame and command."},@@ -124,7 +124,7 @@ var Options = []Option{
124124 125125 {Name: "serve-listen", Kind: Address, Group: GroupServe, Default: "127.0.0.1:2312",126126 Desc: "The address 012 serve listens on. Anything but the loopback address lets other machines " +127- "reach it (with an authorized key). See docs/ssh.md."},127+ "reach it (with an authorized key). See docs/terminal/ssh.md."},128128 {Name: "serve-authorized-keys", Kind: Path, Group: GroupServe, Default: "~/.ssh/authorized_keys",129129 Desc: "The public keys allowed to log in to 012 serve, in OpenSSH's authorized_keys format."},130130 {Name: "serve-host-key", Kind: Path, Group: GroupServe,@@ -3,7 +3,7 @@
33 // Stress benchmarks of import and export: the real datasets fetched by44 // scripts/stress-data.sh into .deps/stress (skipped when missing), and55 // synthetic sheets written in every export format. Only built with6-// -tags stress (see `make stress` and docs/limits.md).6+// -tags stress (see `make stress` and docs/contributing/limits.md).77 package fileio88 99 import (@@ -14,10 +14,10 @@ import (
1414 "testing"1515 )1616 17-var updateDocs = flag.Bool("update-docs", false, "rewrite docs/functions.md from the function table")17+var updateDocs = flag.Bool("update-docs", false, "rewrite docs/reference/functions.md from the function table")1818 1919 // docsPath is the generated function reference.20-const docsPath = "../../docs/functions.md"20+const docsPath = "../../docs/reference/functions.md"2121 2222 // categories names the group of the functions each file defines, in the2323 // order the reference lists them.@@ -37,7 +37,7 @@ var categories = []struct{ file, title string }{
3737 {"jev.go", "JEV (hosted model)"},3838 }3939 40-// TestFunctionsDoc keeps docs/functions.md in step with the function40+// TestFunctionsDoc keeps docs/reference/functions.md in step with the function4141 // table, as help is: run `go test ./internal/functions -run FunctionsDoc4242 // -update-docs` after adding a function.4343 func TestFunctionsDoc(t *testing.T) {@@ -54,7 +54,7 @@ func TestFunctionsDoc(t *testing.T) {
5454 }5555 want, err := os.ReadFile(docsPath)5656 if err != nil || string(want) != got {57- t.Errorf("docs/functions.md is stale; run go test ./internal/functions -run FunctionsDoc -update-docs")57+ t.Errorf("docs/reference/functions.md is stale; run go test ./internal/functions -run FunctionsDoc -update-docs")5858 }5959 }6060 @@ -95,7 +95,7 @@ func definingFiles() (map[string]string, error) {
9595 9696 func functionsDoc(byFile map[string]string) string {9797 var b strings.Builder98- b.WriteString("# Functions\n\n")98+ b.WriteString("---\ntitle: \"Functions\"\nsidebar_position: 3\n---\n\n# Functions\n\n")9999 b.WriteString("<!-- Generated from the engine's function table by TestFunctionsDoc; do not edit. -->\n\n")100100 fmt.Fprintf(&b, "012 has %d functions. They follow Google Sheets' names, arguments and\n", len(Funcs()))101101 b.WriteString("semantics; `[brackets]` mark optional arguments. Function names are\n")@@ -5,7 +5,7 @@
55 // library and golang.org/x/sys: on macOS and Linux it runs the system's66 // own command line tools (security, secret-tool), handing them the77 // secret on stdin so it never appears in a process list; on Windows it8-// calls the Credential Manager API directly. See docs/architecture.md.8+// calls the Credential Manager API directly. See docs/contributing/architecture.md.99 package keyring1010 1111 import (@@ -13,7 +13,7 @@ import (
1313 )1414 1515 // The functions scripts call. Each checks its arguments, then makes one16-// call to the Host through Env.Do. docs/macros.md documents them; keep16+// call to the Host through Env.Do. docs/reference/macro-api.md documents them; keep1717 // the two in step.1818 1919 type builtinFn func(args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error)@@ -17,7 +17,7 @@ import (
1717 )1818 1919 // Opening a file named on the ssh command line, and keeping a session's20-// unsaved work when it ends without the user quitting. See docs/ssh.md.20+// unsaved work when it ends without the user quitting. See docs/terminal/ssh.md.2121 2222 // fileArg is the file an exec request names (ssh -t host file.012), ""2323 // for a shell request. The request is never run: its one word is a file@@ -5,7 +5,7 @@
55 // not for sharing: public-key auth against an authorized_keys file only,66 // on the loopback address unless told otherwise, with no port77 // forwarding, no subsystems, and no commands: an exec request is only8-// ever a file name to open. See docs/ssh.md.8+// ever a file name to open. See docs/terminal/ssh.md.99 package serve1010 1111 import (@@ -6,7 +6,7 @@
66 // arriving back while every session types at once, and the CPU spent per77 // frame. The clients run in the same process, so heap and CPU include88 // their side of the connections: upper bounds for the server. Only built9-// with -tags stress (see `make stress` and docs/limits.md).9+// with -tags stress (see `make stress` and docs/contributing/limits.md).1010 package serve1111 1212 import (@@ -7,12 +7,12 @@ import (
77 "testing"88 )99 10-// TestFileDocNamesFields checks docs/files.md names every field the .01211-// format has, and docs/macros.md a macro's, so a new field comes with its10+// TestFileDocNamesFields checks docs/files/format.md names every field the .01211+// format has, and docs/reference/macro-api.md a macro's, so a new field comes with its1212 // documentation.1313 func TestFileDocNamesFields(t *testing.T) {14- files := readDoc(t, "../../docs/files.md")15- macros := readDoc(t, "../../docs/macros.md")14+ files := readDoc(t, "../../docs/files/format.md")15+ macros := readDoc(t, "../../docs/reference/macro-api.md")1616 seen := map[reflect.Type]bool{reflect.TypeFor[fileMacro](): true}1717 text := files1818 var walk func(reflect.Type, string)@@ -6,7 +6,7 @@ import "slices"
66 // estimated by step.size. When a new step takes the history past it, the77 // oldest steps are dropped first; the newest step is always kept, however88 // large, so any single change can be undone. 256 MB keeps MaxUndo steps9-// that each rewrite a whole column (about 250 MB, see docs/limits.md).9+// that each rewrite a whole column (about 250 MB, see docs/contributing/limits.md).1010 const MaxUndoBytes = 256 << 201111 1212 // undoBudget is MaxUndoBytes; tests lower it.@@ -450,7 +450,7 @@ func TestPivotFileRoundTrip(t *testing.T) {
450450 }451451 }452452 453-// The example in docs/files.md reads.453+// The example in docs/files/format.md reads.454454 func TestPivotFileExample(t *testing.T) {455455 file := `{"version": 5, "sheets": [456456 {"name": "Sales", "cells": {"A1": "Region", "B1": "Item", "C1": "Year", "D1": "Units", "A2": "East", "B2": "Pens", "C2": "2026", "D2": "3"}},@@ -11,7 +11,7 @@ import (
1111 // address. The rest of the engine reads and writes cells only through1212 // these methods, never the map beneath, so the representation can change1313 // (to column blocks of compact values, with formulas and formats in side14-// tables; see docs/limits.md) without touching it.14+// tables; see docs/contributing/limits.md) without touching it.1515 //1616 // Next to the map, two occupancy indexes (occupancy.go) record which1717 // cells are stored and which have contents, so a range is read at the@@ -2,7 +2,7 @@
22 33 // Stress benchmarks of the engine: build and full recalc, single-edit44 // latency per dependency topology, undo, and whole-sheet operations. They5-// only build with -tags stress (see `make stress` and docs/limits.md).5+// only build with -tags stress (see `make stress` and docs/contributing/limits.md).66 package sheet_test77 88 import (@@ -1,6 +1,6 @@
11 // Command benchrec turns `go test -bench` output into run records, one22 // JSON line per benchmark, appended to a results file that DuckDB (and3-// ClickHouse) read; see scripts/stress and docs/observability.md. It also3+// ClickHouse) read; see scripts/stress and docs/contributing/observability.md. It also44 // prints a compact table of the run.55 //66 // go test -bench . -benchmem ./... | go run ./internal/stress/benchrec -out runs.jsonl@@ -1,5 +1,5 @@
11 // Package stress builds synthetic worst-case sheets for the stress2-// benchmarks (see docs/limits.md and `make stress`). Every generator is2+// benchmarks (see docs/contributing/limits.md and `make stress`). Every generator is33 // deterministic, so runs compare, and builds through the public API the44 // way importers do: Load cell by cell, then one RecalcAll.55 package stress@@ -5,7 +5,7 @@
55 // and/or an OTLP/HTTP endpoint (012 --otlp URL, or66 // OTEL_EXPORTER_OTLP_ENDPOINT) that gets the events as logs, spans as77 // traces and frame summaries as metrics. Stdout belongs to the terminal8-// UI. See docs/observability.md.8+// UI. See docs/contributing/observability.md.99 //1010 // Events carry sizes, counts and durations only: never cell contents,1111 // file contents or secrets.@@ -1,6 +1,7 @@
11 package ui22 33 import (4+ "io/fs"45 "os"56 "path/filepath"67 "regexp"@@ -14,17 +15,29 @@ import (
1415 // The docs name menus, commands and keys; these tests keep what they name1516 // true of the registry. scripts/doccheck checks links, the index and media.1617 17-// userDocs reads README.md and docs/*.md with fenced code blocks and18+// docPages lists the Markdown pages under docs/, in every folder.19+func docPages(t *testing.T) []string {20+ t.Helper()21+ var paths []string22+ err := filepath.WalkDir("../../docs", func(p string, d fs.DirEntry, err error) error {23+ if err == nil && !d.IsDir() && filepath.Ext(p) == ".md" {24+ paths = append(paths, p)25+ }26+ return err27+ })28+ if err != nil {29+ t.Fatal(err)30+ }31+ return paths32+}33+34+// userDocs reads README.md and the docs with fenced code blocks and1835 // inline code removed and each paragraph on one line, so a menu path1936 // wrapped across lines reads whole. The generated function reference is2037 // left out: it names no menus.2138 func userDocs(t *testing.T) map[string]string {2239 t.Helper()23- paths, err := filepath.Glob("../../docs/*.md")24- if err != nil {25- t.Fatal(err)26- }27- paths = append(paths, "../../README.md")40+ paths := append(docPages(t), "../../README.md")2841 out := map[string]string{}2942 for _, p := range paths {3043 if filepath.Base(p) == "functions.md" {@@ -154,7 +167,7 @@ var (
154167 // command line (`:settings.vim`) or in scripts (run("format.bold")),155168 // are registered.156169 func TestDocsCommandIDs(t *testing.T) {157- for _, p := range []string{"../../docs/keys.md", "../../docs/macros.md"} {170+ for _, p := range []string{"../../docs/reference/keys.md", "../../docs/reference/macro-api.md"} {158171 data, err := os.ReadFile(p)159172 if err != nil {160173 t.Fatal(err)@@ -169,10 +182,10 @@ func TestDocsCommandIDs(t *testing.T) {
169182 }170183 }171184 172-// TestKeysDocCoversKeymap checks docs/keys.md lists every key bound to a185+// TestKeysDocCoversKeymap checks docs/reference/keys.md lists every key bound to a173186 // command, written as the menus and help show it.174187 func TestKeysDocCoversKeymap(t *testing.T) {175- data, err := os.ReadFile("../../docs/keys.md")188+ data, err := os.ReadFile("../../docs/reference/keys.md")176189 if err != nil {177190 t.Fatal(err)178191 }@@ -184,7 +197,7 @@ func TestKeysDocCoversKeymap(t *testing.T) {
184197 label := keyLabel(k)185198 re := regexp.MustCompile(`(^|[\s,|(/])` + regexp.QuoteMeta(label) + `($|[\s,|)/.;:])`)186199 if !re.MatchString(doc) {187- t.Errorf("docs/keys.md doesn't list %s (%s, %s)", label, id, commands[id].title)200+ t.Errorf("docs/reference/keys.md doesn't list %s (%s, %s)", label, id, commands[id].title)188201 }189202 }190203 var vimKeys []string@@ -201,10 +214,10 @@ func TestKeysDocCoversKeymap(t *testing.T) {
201214 for _, k := range vimKeys {202215 if strings.Contains(k, "+") || len(k) > 2 {203216 if !strings.Contains(doc, keyLabel(k)) && !strings.Contains(doc, theme.KeyLabel(k)) {204- t.Errorf("docs/keys.md doesn't list the vim key %s", k)217+ t.Errorf("docs/reference/keys.md doesn't list the vim key %s", k)205218 }206219 } else if !strings.Contains(doc, "`"+k+"`") {207- t.Errorf("docs/keys.md doesn't list the vim key `%s`", k)220+ t.Errorf("docs/reference/keys.md doesn't list the vim key `%s`", k)208221 }209222 }210223 }@@ -16,7 +16,7 @@ import (
1616 // record what you do (with absolute or relative references), save it1717 // with a name and an optional Ctrl+Alt+Shift+digit shortcut, run it from1818 // the menu, the palette or its shortcut, and manage saved macros. Macros19-// are Starlark scripts kept in the .012 file (see docs/macros.md);19+// are Starlark scripts kept in the .012 file (see docs/reference/macro-api.md);2020 // recorded ones are scripts too, readable and editable. Recording is in2121 // macrorec.go, running in macrorun.go, what scripts act on in2222 // macrohost.go, and the manager in macromanage.go.@@ -9,12 +9,12 @@ import (
99 "github.com/FelineStateMachine/012/internal/macro"1010 )1111 12-// docs/macros.md documents the scripting API and names command ids; these12+// docs/reference/macro-api.md documents the scripting API and names command ids; these1313 // tests keep it honest.1414 1515 func readMacroDoc(t *testing.T) string {1616 t.Helper()17- data, err := os.ReadFile("../../docs/macros.md")17+ data, err := os.ReadFile("../../docs/reference/macro-api.md")1818 if err != nil {1919 t.Fatal(err)2020 }@@ -33,7 +33,7 @@ func TestMacroDocCommandIDs(t *testing.T) {
3333 }3434 for _, id := range ids {3535 if c, ok := commands[id[1]]; !ok || c.macro == macroNever {36- t.Errorf("docs/macros.md names %q, which scripts can't run", id[1])36+ t.Errorf("docs/reference/macro-api.md names %q, which scripts can't run", id[1])3737 }3838 }3939 }@@ -43,7 +43,7 @@ func TestMacroDocFunctions(t *testing.T) {
4343 doc := readMacroDoc(t)4444 for _, name := range macro.Functions() {4545 if !strings.Contains(doc, "`"+name+"(") {46- t.Errorf("docs/macros.md doesn't document %s", name)46+ t.Errorf("docs/reference/macro-api.md doesn't document %s", name)4747 }4848 }4949 }@@ -255,7 +255,7 @@ func (h scriptHost) Run(id string, answer *string) error {
255255 c, ok := commands[id]256256 switch {257257 case !ok:258- return fmt.Errorf("no command %q; the palette (Ctrl+K) lists them, and docs/macros.md how to find ids", id)258+ return fmt.Errorf("no command %q; the palette (Ctrl+K) lists them, and docs/reference/macro-api.md how to find ids", id)259259 case c.macro == macroNever:260260 return fmt.Errorf("%s (%s) can't run in a macro", c.title, id)261261 case !c.available(m):@@ -200,7 +200,7 @@ func (m *Model) newMacro() tea.Cmd {
200200 }201201 202202 const newMacroTemplate = `# A macro written by hand. It runs from the top as one undo step;203-# docs/macros.md lists what it can call. For example:203+# docs/reference/macro-api.md lists what it can call. For example:204204 #205205 # set("A1", "Total")206206 # set_formula("B1", "SUM(B2:B100)")@@ -256,8 +256,8 @@ func (m *Model) recordingLine() string {
256256 func recordingHeader(relative bool) string {257257 if relative {258258 return "Recorded with relative references: moves count from the active cell.\n" +259- "Edit it in Data > Macros > Manage macros; docs/macros.md lists what it can call."259+ "Edit it in Data > Macros > Manage macros; docs/reference/macro-api.md lists what it can call."260260 }261261 return "Recorded with absolute references: cells are named as they were.\n" +262- "Edit it in Data > Macros > Manage macros; docs/macros.md lists what it can call."262+ "Edit it in Data > Macros > Manage macros; docs/reference/macro-api.md lists what it can call."263263 }@@ -26,7 +26,7 @@ import (
2626 // served directory, and the next session opening that file offers it2727 // back. The directory is hidden, so names typed in a session never reach2828 // it (internal/confine); only this file reads and writes it. See29-// docs/ssh.md.29+// docs/terminal/ssh.md.3030 3131 // RecoveryDir is the directory, inside the served one, recovery files3232 // are kept in.@@ -4,7 +4,7 @@
44 // through to the next frame, on big sheets at typical and large terminal55 // sizes. A frame is View plus what Bubble Tea does with it: parse the66 // string into a cell buffer and diff it onto the terminal. Only built7-// with -tags stress (see `make stress` and docs/limits.md).7+// with -tags stress (see `make stress` and docs/contributing/limits.md).88 package ui99 1010 import (