Commit Graph

4 Commits

Author SHA1 Message Date
Levi Neely df287b3a7f sam X/Y: target open buffers, not the whole project
Scope X/Y to the set of documents Kate currently has open
(Application::documents()) — acme's open-window set — instead of the project
index on disk. The regex matches each buffer's local file path (or display
name for an unsaved scratch buffer). Nothing is opened or read from disk; the
live Documents are edited in place, each its own undo step.

Smaller blast radius than a project-wide sweep and closer to acme's X/Y, which
iterate open windows. Drops the ProjectIndexer dependency from the X/Y path.
Docs (SAM.md, PLAN.md) updated. 18/18 ctest green.
2026-10-08 15:11:52 +02:00
Levi Neely 6341b1ad01 sam: port sam's own regex engine (leftmost-longest, linear-time)
Replace QRegularExpression with SamRegex (src/sam/samregex.{h,cpp}), a
faithful port of plan9port src/cmd/sam/regexp.c: a Thompson/Pike NFA.

  - Leftmost-LONGEST (POSIX): matches real sam exactly, including overlapping
    alternation (a|ab on 'ab' -> 'ab'), eliminating the one remaining
    greedy-vs-longest divergence from PCRE.
  - Linear time, no backtracking: immune to catastrophic blowup ((a*)*b over
    10k 'a' returns instantly).
  - sam dialect only: . * + ? | ( ) [ ] ^ $ and \ escaping (\n = newline);
    ^/$ per-line and ./negated-classes exclude newline, all intrinsic. No
    PCRE extras (\d \w \b, lookaround, non-greedy) — sam never had them.

Port notes: shunting-yard compiler + Pike VM with per-thread capture sets and
leftmost-longest newmatch(). Fixed two porting bugs vs the C original: the
l-union aliasing of OR's left/continuation with .next, and reading the popped
subid during popator for correct capture-group ids.

SamEngine now compiles/matches via SamRegex (search, s, x/y/g/v, replacement
captures). Tests: new test_samregex (18) + updated test_samengine (31, incl.
leftmostLongestSubstitution, sam-dialect capture groups). 18/18 ctest suites.

Docs: rewrite docs/SAM.md (no more PCRE deviation; dialect + semantics are
sam's), update PLAN.md and the panel hint.
2026-10-08 14:44:05 +02:00
Levi Neely 4cdd762bb6 sam: ^/$ are per-line (MultilineOption), matching plan9 regexp(7)
Verified against plan9port: regexp(7) defines ^ as 'beginning of a line'
and $ as 'end of a line', and sam's regexp.c BOL (p==0 || prev=='\\n') /
EOL (next char '\\n') confirm per-line anchoring. The engine was matching
with buffer-wide anchors, so ,s/^/> /g only touched the first line and the
sam idiom ,x/.+/ s/^/> / failed to prefix each line.

Fix: compile every pattern with QRegularExpression::MultilineOption. '.'
still does not cross newlines (sam-faithful; PCRE default). Add 3 regression
tests (caretIsPerLine, dollarIsPerLine, samAnchorIdiom); 30/30 engine tests.

Docs: correct docs/SAM.md — ^/$ per-line is faithful, not a deviation;
reframe PCRE extras as an escape hatch (prefer structural composition), not
'free upgrades'. Fix the panel hint and PLAN.md accordingly.
2026-10-08 14:29:19 +02:00
Levi Neely 997055f0b5 docs(sam): document PCRE deviations + implications for katesam usage
Add docs/SAM.md — a user-facing reference for the regex deviation, with
examples verified against the engine:
  - leftmost-greedy (PCRE) vs leftmost-longest (sam): diverges only on
    overlapping alternation (a|ab); order alternatives longest-first.
  - ^/$ anchor buffer-wide, not per-line, by default. Workarounds:
    structural ,x/.+/ … (preferred) or the (?m) inline flag.
  - identical behaviour table (. and newlines, empty-match advance, &/\N).
  - PCRE bonuses sam lacks: \d \w \b, lookahead, non-greedy, (?i)(?m)(?s).

Cross-link from PLAN.md and surface the two biggest gotchas (buffer-wide
anchors, alternation order) directly in the Sam panel hint label.
2026-10-08 14:03:55 +02:00