# katesam — sam structural-regexp editing in Kate The **Sam** tool view (left sidebar) runs the plan9 [sam](https://doc.cat-v.org/plan_9/4th_edition/papers/sam/) command language against the active document, or project-wide via `X`/`Y`. Type a program, press **Ctrl+Return** or click **Run**. Each Run is one undo step (Ctrl+Z reverts the whole Run; multi-file `X`/`Y` undoes per file). ## The regex engine katesam uses a **faithful port of sam's own regular-expression engine** (`src/sam/samregex.{h,cpp}`, from plan9port `src/cmd/sam/regexp.c`), not PCRE. This is a Thompson/Pike NFA, which gives the two properties sam relies on: - **Leftmost-longest** (POSIX) matching, so results match real sam exactly, including overlapping alternation. - **Linear time**, with no backtracking — immune to catastrophic blowup. A pattern like `(a*)*b` against thousands of `a` returns instantly. There is **no PCRE deviation anymore**: the dialect, the match semantics, and the anchors are sam's. ### Dialect The sam dialect is deliberately small (sam `regexp(7)`): ``` . any character except newline * + ? zero-or-more / one-or-more / zero-or-one | alternation ( ) grouping and capture (\1..\9 in replacements) [ ] [^ ] character class / negated class (ranges a-z) ^ $ beginning / end of a LINE \c literal c (escapes a metacharacter); \n is newline ``` There is intentionally **no** `\d \w \s \b`, no lookaround, no non-greedy, and no inline flags. sam never had them. Use character classes (`[0-9]`, `[a-zA-Z_]`) and structural composition (`x y g v`) instead — that is the sam way, and patterns stay portable to real sam. ### Semantics (all verified against the port) | behaviour | katesam | note | |---|---|---| | overlapping alternation `a\|ab` on `ab` | matches `ab` | leftmost-**longest**; order of branches does not matter | | `.` crosses newline | **no** | matches sam | | `^` / `$` | per-line | `^` = start of a line, `$` = end of a line | | `[^z]+` crosses newline | no | negated classes also exclude `\n`, as in sam | | empty-match global `s/x*/-/g` on `axbx` | `-a-b-` | one advance per null match | | `&` whole match, `\1..\9` groups in replacement | yes | up to 9 capture groups | | pathological `(a*)*b` | linear time | NFA, no backtracking | ### Anchors are per-line `^` matches the beginning of any line and `$` the end of any line — built into the engine (sam `regexp.c`: `BOL` fires at offset 0 or after `\n`; `EOL` before `\n`). So: | program | input | result | |---|---|---| | `,s/^/> /g` | `a⏎b⏎c` | `> a⏎> b⏎> c` | | `,s/$/;/g` | `a⏎b⏎c` | `a;⏎b;⏎c;` | The structural idiom is equivalent and composes with guards/loops: ``` ,x/.+/ s/^/> / prefix every (non-empty) line → > a⏎> b⏎> c ,x/.+/ a/;/ append ";" to every line → a;⏎b;⏎c; ``` ## Composing edits — the sam way sam's power is structural composition, not clever single regexes. Descend into matches with `x`/`y`, guard with `g`/`v`, group with `{}`: ``` ,x/[a-zA-Z_]+/ g/^[A-Z]/ c/CONST/ every identifier starting uppercase → CONST ,x/"[^"]*"/ s/foo/bar/g foo→bar only inside double-quoted strings 0/start/,/end/ x/[a-z]+/ s/.*/[&]/ bracket every lowercase word in a region ``` Multi-file `X`/`Y` applies an inner program across the **open buffers**: ``` X/\.cpp$/ ,s/old_api/new_api/g rewrite every open .cpp buffer Y/_test\./ ,x/TODO/ d drop TODO lines in non-test buffers ``` (`X`/`Y` iterate the set of documents Kate currently has open — acme's open-window set — not the project on disk. The regex matches each buffer's file path, or its display name for an unsaved scratch buffer. Nothing is opened or read from disk; edits land in the live buffers, each its own undo step. Review and save deliberately.) ## Practical guidance - Use character classes, not PCRE shorthands: `[0-9]` not `\d`, `[a-zA-Z_]` not `\w`. - Prefer structural composition (`x g v {}`) over one dense pattern. - `^`/`$` are per-line; anchor directly (`,s/^…`) or loop (`,x/.+/ …`). - Empty-match globals (`s/x*/…/g`) advance one position per null match — safe, but as always with `*`, check the result. ## Scope Supported: the full address language (`#n n 0 $ . '` `/re/ ?re?` compound `+ - , ;`) and commands `a c i d s p = m t k x y g v {}` plus shell filters `< > | !`. Out of scope (Kate manages files / its own undo): the multi-file menu (`b B n D`), external file I/O (`e r w f`), `"re"` file-addressing, and sam's own `u` (use Kate's Ctrl+Z).