Skip to main content

Module cleanup

Module cleanup 

Source
Expand description

Turn selected text into something worth hearing.

Order matters: code fences are dropped before anything inspects their contents, and whitespace is collapsed last so earlier removals do not leave gaps.

URLs get special handling. After code fences are dropped, the remaining text is segmented into alternating runs of non-URL text and URL matches. Transforms that must never touch URL text — hyphenation rejoin, markdown stripping, acronym spelling — run only on the non-URL segments; the UrlPolicy replacement (the literal word “link”, a bare host, or the URL verbatim) is computed only for the URL segments. The results are concatenated back together in their original order.

This replaces an earlier placeholder-based scheme (hide URLs behind \u{E000}<index>\u{E001} markers, restore after the markdown/acronym passes) that assumed those private-use codepoints never occur in real input. That assumption doesn’t hold: Nerd Font and Powerline glyphs live in exactly that codepoint range, and this daemon’s primary input is terminal selections, so users routinely paste text containing them. Segmentation makes no assumption about which codepoints appear anywhere in the input — URL text and non-URL text are simply never in the same string at the same time while the URL-unsafe transforms run.

The two remaining passes — the control-character strip and whitespace collapse — run once, globally, on the concatenated result, and that is safe:

  • The control-character strip is unconditional and must stay that way: it is the only thing standing between an embedded NUL and a downstream FFI CString::new call, and a test pins that guarantee. Running it globally cannot corrupt a URL span because it only ever removes characters, and a legitimate URL cannot contain a control character in the first place — there is nothing there to protect.
  • Whitespace collapse is safe to run globally because the URL regex’s exclusion set already excludes whitespace from a URL match, so a URL span can never contain, start with, or end with whitespace. Collapsing whitespace runs elsewhere in the string can therefore never reach into a URL span or merge two URL spans together.

Two of the transforms that do run per-segment inside clean_non_url are anchor-based, and an anchor evaluated on an isolated segment does not necessarily correspond to a real boundary in the original, unsegmented input. Both are handled the same way: clean_non_url is given the real character that precedes (and, for ACRONYM, follows) the segment in the original input, and a sentinel is temporarily glued onto the segment edge so the anchor sees what it would have seen unsegmented, then stripped back off before the result is used.

  • LIST_OR_HEADING is anchored on ^ (line start, via (?m)). Handing it an isolated segment is wrong: position 0 of a segment that begins right after a URL is not a line start in the original text, but ^ would match there anyway (position 0 of any string it is handed is a line-start match, per (?m) semantics), misreading ordinary punctuation that follows a URL (-, #, 1.) as a bullet/heading/list marker. Position 0 of a segment is a genuine line start only when prev — the character immediately preceding the segment in the original input — is absent (segment starts at input position 0) or is a newline. When prev is anything else, a sentinel character is prepended before LIST_OR_HEADING runs, then stripped back off; (?m)^ still correctly matches after any newline inside the segment, since the sentinel only occupies the position before the segment, not any position within it. The sentinel used is '\u{1}' (SOH, a C0 control character): it is not \n, so it never itself becomes a line start for ^ to match after; it is not whitespace, so LIST_OR_HEADING’s \s* cannot absorb it and then continue matching into the segment’s real leading whitespace; and it is none of #, -, *, +, or a digit, so it can never itself begin a marker match. No other transform runs while it is present — it is pushed and popped within a single tightly-scoped step — so there is no window for it to be matched or mangled by EMPHASIS, HYPHEN_BREAK, or anything else. It also cannot leak into output even if some future bug skipped the strip-back-off step: the global, unconditional control-character filter at the end of clean removes every control character except \n/\t, and SOH is neither. A test (bullet_immediately_followed_by_url_is_still_stripped) confirms a marker is still stripped when a URL immediately follows it, and another (marker_after_interior_newline_is_still_stripped) confirms the sentinel does not suppress a legitimate match after a newline inside the segment.
  • ACRONYM is anchored on \b (word boundary) at both ends. Evaluated on an isolated segment, \b at position 0 or at the end of the string is computed against “nothing” on the outside — even when the original input actually had an alphanumeric character right there (typically the edge of an adjacent URL), which would have suppressed the boundary. To reproduce full-string semantics, clean_non_url is given the real character that precedes and follows the segment in the original input; if that neighbor is alphanumeric, a one-character lowercase-letter sentinel is temporarily glued onto that side of the segment before ACRONYM runs (lowercase so it can never itself match [A-Z]{3,}), reproducing the same “word character on the other side” \b would have seen, and is stripped back off afterward.

LIST_OR_HEADING and HYPHEN_BREAK both run inside clean_non_url, and their relative order is load-bearing in the other direction: hyphenation rejoin must run first. A hyphen-wrapped word can wrap onto a line that is itself a marker line ("machine-\n- learning"), and HYPHEN_BREAK needs to see the marker’s leading - still in place to know there is a non-word character between the two word halves and decline to touch them; if the marker were stripped first, HYPHEN_BREAK would see "machine-\nlearning" and rejoin it, silently fusing what was — genuinely ambiguously, see the test below — either a wrapped hyphenated word or a new list item.

Functions§

clean