77 lines
2.9 KiB
Go
77 lines
2.9 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
// The daemons' lines (novox/hq ADR 0272, its insight of 2026-10-09): the operator types one command, `nox`
|
|
// (`mesh-cli`). The node-engine (`nox-mesh-host`) and the controller (`mesh-controller`) are daemons, and a
|
|
// person never types their names — so no text a module can show names a line of theirs. The list is the
|
|
// copy in retired-lines at the catalogue's root, which novox/hq's words.py prints (`--list lines`) and
|
|
// compares; one line each, `<daemon> <command>`, and a trailing ` …` for a command that is a line only with
|
|
// an argument after it (`mesh-controller module check` is a line; "the mesh-controller module" is prose).
|
|
|
|
// retiredLine is one daemon's line: the daemon, its command, and whether an argument must follow.
|
|
type retiredLine struct {
|
|
daemon, command string
|
|
withArgument bool
|
|
}
|
|
|
|
// readLines reads retired-lines: one line a line; blank lines and lines starting with # are not lines.
|
|
func readLines(path string) ([]retiredLine, error) {
|
|
entries, err := readList(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var out []retiredLine
|
|
for _, e := range entries {
|
|
withArgument := strings.HasSuffix(e, " …")
|
|
e = strings.TrimSuffix(e, " …")
|
|
parts := strings.Fields(e)
|
|
if len(parts) != 2 {
|
|
return nil, fmt.Errorf("retired-lines: %q is not `<daemon> <command>`", e)
|
|
}
|
|
out = append(out, retiredLine{daemon: parts[0], command: parts[1], withArgument: withArgument})
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// linePattern matches the line whole: the daemon, white space, the command. A command that is a line only
|
|
// with an argument is matched as a line is written — at the start of a line or a code span, a prompt allowed,
|
|
// with an argument after it — so prose that names the daemon beside the word ("the mesh-controller module is
|
|
// assigned") is not one.
|
|
func linePattern(l retiredLine) *regexp.Regexp {
|
|
daemon, command := regexp.QuoteMeta(l.daemon), regexp.QuoteMeta(l.command)
|
|
if l.withArgument {
|
|
tick := "`"
|
|
return regexp.MustCompile(`(?m)(?:^|` + tick + `)[ \t]*(?:\$[ \t]*)?(` + daemon + `\s+` + command + `\s+[^\s` + tick + `])`)
|
|
}
|
|
return regexp.MustCompile(`(?:^|[^\w-])(` + daemon + `\s+` + command + `)(?:$|[^\w-])`)
|
|
}
|
|
|
|
// checkLines finds every daemon's line in the texts.
|
|
func checkLines(lines []retiredLine, texts []text) []string {
|
|
var out []string
|
|
for _, l := range lines {
|
|
rx := linePattern(l)
|
|
for _, t := range texts {
|
|
if !strings.Contains(t.s, l.daemon) { // the pattern holds the daemon's name whole: the cheap test first
|
|
continue
|
|
}
|
|
if m := rx.FindStringSubmatchIndex(t.s); m != nil {
|
|
line := t.line
|
|
if line > 0 {
|
|
line += strings.Count(t.s[:m[2]], "\n")
|
|
}
|
|
out = append(out, fmt.Sprintf("%s:%d: `%s %s` is a daemon's line — the operator types `nox …` (novox/hq "+
|
|
"ADR 0272) — in %q", t.file, line, l.daemon, l.command, excerpt(t.s, m[2], m[3])))
|
|
}
|
|
}
|
|
}
|
|
sort.Strings(out)
|
|
return out
|
|
}
|