;; content-on-sx — global find/replace across every text-bearing field. ;; ;; Replaces every occurrence of `from` with `to` in the text-bearing fields of ;; a document, tree-wide (via the transform layer): ;; - the `text` of text / heading / code / quote / callout blocks ;; - the `alt` of image blocks ;; - each item of list blocks ;; - every header and cell of table blocks ;; This is exactly the set asText / stats / summary draw prose from, so a rename ;; via content/find-replace and a word count over asText stay consistent. ;; Immutable; case-sensitive. ;; ;; A text field may be a plain string OR a list of rich-text runs (Phase 5, ;; run = (text marks href)). fr-rep-text rewrites per run, preserving each run's ;; marks/href; a match that physically straddles two runs is not joined (the ;; replacement would have no single mark set) — each run is rewritten in place. ;; ;; Requires (loaded by harness): block.sx, transform.sx (content/map-blocks), ;; table.sx (CtTable ivars). (define fr-in? (fn (x xs) (cond ((= (len xs) 0) false) ((= (first xs) x) true) (else (fr-in? x (rest xs)))))) (define fr-rep (fn (s from to) (replace (str s) from to))) ;; rewrite a text-bearing field that is either a plain string or a runs list (define fr-rep-text (fn (v from to) (if (list? v) (map (fn (r) (list (fr-rep (nth r 0) from to) (nth r 1) (nth r 2))) v) (fr-rep v from to)))) ;; Blocks whose prose content find/replace rewrites (matches asText's set). (define fr-has-text? (fn (b) (fr-in? (blk-type b) (list "text" "heading" "code" "quote" "callout" "image" "list" "table")))) ;; Per-type field rewrite. Each branch returns a new (copy-on-write) block. (define fr-rewrite (fn (b from to) (let ((t (blk-type b))) (cond ((= t "image") (blk-set b "alt" (fr-rep (blk-get b "alt") from to))) ((= t "list") (let ((items (blk-get b "items"))) (if (list? items) (blk-set b "items" (map (fn (it) (fr-rep it from to)) items)) b))) ((= t "table") (let ((hs (blk-get b "headers")) (rs (blk-get b "rows"))) (let ((b1 (if (list? hs) (blk-set b "headers" (map (fn (h) (fr-rep h from to)) hs)) b))) (if (list? rs) (blk-set b1 "rows" (map (fn (r) (if (list? r) (map (fn (c) (fr-rep c from to)) r) r)) rs)) b1)))) (else (blk-set b "text" (fr-rep-text (blk-get b "text") from to))))))) (define content/find-replace (fn (doc from to) (content/map-blocks doc fr-has-text? (fn (b) (fr-rewrite b from to)))))