cat / 012
clone

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

cat/module-path
012 / scripts / doccheck / families.go
families.go 4.2K
1package main23import (4 "os"5 "path"6 "path/filepath"7 "regexp"8 "slices"9 "strings"10)1112// The pictures' color schemes: docs/contributing/site.md has a table of13// families, each a dark and a light scheme and the pages in it. Every14// page that shows a picture is in one family, and so is every picture15// it shows: a still's docScreens entry names its family, and a tape's16// Set Theme is one of the family's two schemes.1718const familiesDoc = "docs/contributing/site.md"1920var (21 // familyRow is a row of the table: | Family | `dark` | `light` | pages |.22 familyRow = regexp.MustCompile("(?m)^\\| ([^|`]+?) \\| `([^`]+)` \\| `([^`]+)` \\| (.+) \\|$")23 setTheme = regexp.MustCompile(`(?m)^Set Theme\s+"([^"]+)"`)24 // stillFamily is a docScreens entry's name and family.25 stillFamily = regexp.MustCompile(`(?m)^\s*\{name: "([^"]+)", from:.*family: "([^"]+)"\}`)26)2728type family struct {29 name, dark, light string30 pages []string // pages and folders, relative to docs/31}3233// readFamilies reads the families table.34func readFamilies() ([]family, error) {35 src, err := os.ReadFile(familiesDoc)36 if err != nil {37 return nil, err38 }39 var out []family40 for _, m := range familyRow.FindAllStringSubmatch(string(src), -1) {41 f := family{name: m[1], dark: m[2], light: m[3]}42 for _, p := range strings.Split(m[4], ",") {43 f.pages = append(f.pages, strings.Trim(strings.TrimSpace(p), "`"))44 }45 out = append(out, f)46 }47 return out, nil48}4950// familyOf is the family of a page under docs/: the one naming the page,51// or else the deepest folder holding it; nil when none does.52func familyOf(fams []family, page string) *family {53 rel := strings.TrimPrefix(page, "docs/")54 var best *family55 bestLen := -156 for i, f := range fams {57 for _, p := range f.pages {58 if (p == rel || strings.HasSuffix(p, "/") && strings.HasPrefix(rel, p)) && len(p) > bestLen {59 best, bestLen = &fams[i], len(p)60 }61 }62 }63 return best64}6566// checkFamilies reports pictures in a family other than the pages67// showing them, and pages showing pictures in no family. shown maps a68// media path to the docs showing it.69func checkFamilies(shown map[string]map[string]string) []string {70 fams, err := readFamilies()71 if err != nil {72 return []string{familiesDoc + ": " + err.Error()}73 }74 if len(fams) == 0 {75 return []string{familiesDoc + ": no table of the pictures' families"}76 }77 // check reports each docs page showing media whose family doesn't78 // pass ok.79 var out []string80 check := func(source string, media []string, ok func(*family) bool, want string) {81 for _, m := range media {82 for _, page := range sortedKeys(shown["docs/media/"+m]) {83 if !strings.HasPrefix(page, "docs/") {84 continue // the README and the landing page show every topic's85 }86 switch f := familyOf(fams, page); {87 case f == nil:88 out = append(out, page+": shows "+m+" but is in no family of "+familiesDoc)89 case !ok(f):90 out = append(out, source+": "+want+", but "+page+", which shows "+m+", is in the family "+f.name)91 }92 }93 }94 }95 setup, _ := os.ReadFile("demos/lib/setup.tape")96 tapes, _ := filepath.Glob("demos/*.tape")97 for _, t := range tapes {98 src, err := os.ReadFile(t)99 if err != nil {100 continue101 }102 themes := setTheme.FindAllStringSubmatch(string(setup)+string(src), -1)103 if len(themes) == 0 {104 out = append(out, t+": no Set Theme, here or in demos/lib/setup.tape")105 continue106 }107 theme := themes[len(themes)-1][1]108 var made []string109 for _, re := range []*regexp.Regexp{tapeOutput, tapeScreenshot} {110 for _, m := range re.FindAllStringSubmatch(string(src), -1) {111 made = append(made, path.Base(m[1]))112 }113 }114 check(t, made, func(f *family) bool { return theme == f.dark || theme == f.light }, "its theme is "+theme)115 }116 if src, err := os.ReadFile(stillsSource); err == nil {117 for _, m := range stillFamily.FindAllStringSubmatch(string(src), -1) {118 name, fam := m[1], m[2]119 if !slices.ContainsFunc(fams, func(f family) bool { return f.name == fam }) {120 out = append(out, stillsSource+": the still "+name+" is in the family "+fam+", which "+familiesDoc+" doesn't list")121 }122 check(stillsSource, []string{name + "-dark.png", name + "-light.png"},123 func(f *family) bool { return f.name == fam }, "the still "+name+" is in the family "+fam)124 }125 }126 return out127}