Verbatim-safe typography, ^-punctuation quoting, full-range ^UUUU^, polyglot html

Typographic transforms (---, quote pairs, ~) no longer touch verbatim
text: @c/@code/@source_listing content and ^'...'^ spans show exactly the
characters written. "^" before any punctuation character quotes it in
every target (the apostrophe excepted: ^' opens a literal span), with the
new :resolve option on @@@target declaring per-target renderings. The
^UUUU^ code-point form accepts 4-6 hex digits, the full Unicode range.
The html output and transform spellings are polyglot (XML-valid), in
preparation for an EPUB target. New suites: transform_test, character_test
(engine), typography_test (SKS).

(from dev 07ce5ea86a0a)
This commit is contained in:
2026-08-23 20:48:26 +02:00
parent d982c0d6cc
commit 37b6ba1c4f
77 changed files with 1665 additions and 1608 deletions

View File

@@ -131,15 +131,32 @@ Register one with `klammertext-add-literal-klammer', e.g. in your init file:
;; doc/edit/sublime/Klammertext.sublime-syntax
;; * the @NAME verbatim region in doc/edit/vim/syntax/klammertext.vim
;; * the @NAME rule in doc/edit/vscode/syntaxes/klammertext.tmLanguage.json
;; All are currently seeded with just "code".
;; All are currently seeded with "code" and "c" (@c is the inline form of
;; @code and took a literal argument 2026-08-16; the miscounted stack from an
;; unrecognized "@c ... c@" shifted a whole document's indentation by one).
(defun klammertext-add-literal-klammer (name)
"Register NAME as a klammer whose literal content must not be interpreted.
NAME is the klammer name without the leading @ (e.g. \"code\")."
(add-to-list 'klammertext-literal-klammers name))
(defun klammertext--search-literal-close (name &optional bound)
"Move point past the exact close token NAME@ at or after point.
Return the position after the close, or nil if there is none before BOUND.
NAME@ preceded by a name character is verbatim content, not a close --
\"basic@\" does not close @c, and \"barcode@\" does not close @code
\(the engine's close is a whole katom). Mirrors the shared core's
find_literal_close."
(let ((close (concat (regexp-quote name) "@")) (found nil))
(while (and (setq found (re-search-forward close bound t))
(let ((b (match-beginning 0)))
(and (> b (point-min))
(klammertext--name-char-p (char-before b))))))
found))
;; Seed the list through the same entry point future users will use.
(klammertext-add-literal-klammer "code")
(klammertext-add-literal-klammer "c")
;; --- Helpers -----------------------------------------------------------
@@ -275,13 +292,12 @@ BEFORE is the character before POS."
;; BEFORE `klammertext--set-match', because `re-search-forward'
;; clobbers the match data.
(if (member name klammertext-literal-klammers)
(let ((close (concat (regexp-quote name) "@")))
(if (re-search-forward close nil t)
(let ((close-end (point)))
(put-text-property pos close-end 'font-lock-multiline t)
(goto-char (- close-end (length name) 1)))
(put-text-property pos (point-max) 'font-lock-multiline t)
(goto-char (point-max))))
(if (klammertext--search-literal-close name)
(let ((close-end (point)))
(put-text-property pos close-end 'font-lock-multiline t)
(goto-char (- close-end (length name) 1)))
(put-text-property pos (point-max) 'font-lock-multiline t)
(goto-char (point-max)))
(goto-char name-end))
;; Set the match data for the opening LAST, so it survives to the
;; highlight step.
@@ -478,8 +494,8 @@ delimiter (or skipped region) and return (POS . KIND) with KIND `open or
(let ((name (buffer-substring-no-properties (1+ hit) (point))))
(cond
((member name klammertext-literal-klammers) ; literal span: skip
(let ((close (concat (regexp-quote name) "@")))
(unless (re-search-forward close nil t) (goto-char (point-max)))))
(unless (klammertext--search-literal-close name)
(goto-char (point-max))))
((eq (char-after) ?-)) ; @name-arg : no span
(t (throw 'found (cons hit 'open))))))
(t ; name@ / bare @ : closing
@@ -574,7 +590,7 @@ name, not by depth counting."
The verbatim content is opaque, so we search for the literal close string."
(save-excursion
(goto-char (+ open-pos 1 (length name)))
(when (search-forward (concat name "@") nil t)
(when (klammertext--search-literal-close name)
(1- (point)))))
(defun klammertext--literal-match-backward (close-pos name)
@@ -586,7 +602,11 @@ CLOSE-POS, or nil. Literal spans do not nest, so the nearest preceding real
(let ((open-str (concat "@" name)) (result nil))
(while (and (not result) (search-backward open-str nil t))
(let ((op (point)))
;; The name must end where the token ends: "@c" found inside
;; "@caption" is not an opener of @c.
(unless (or (eq (char-before op) ?@) ; @@NAME = definition
(klammertext--name-char-p
(char-after (+ op 1 (length name))))
(klammertext--escaped-p op))
(setq result op))))
result)))