kate-deft/docs/SAM.md

4.5 KiB

katesam — sam structural-regexp editing in Kate

The Sam tool view (left sidebar) runs the plan9 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).