katom types from ref/ in groups, @table :leading in lines of text and :rulesep, @code under txt, one table style attribute, literal-klammer options (from dev 2e453744f5e9)

kdesc --katoms is Klammertext rendered from ref/, the types in titled groups,
with --katoms full for the regex column.  @table :leading L is L lines of the
surrounding text in html as in pdf (default 1), and :rulesep spaces the
horizontal rules.  The html table has one style attribute, so :column_width
fractions take effect again.  @code and @source_listing render in plain
text; captions are numbered per kind there, and a table needs no blank line
around it.  @c takes :nd, and an option-shaped word before a bar inside
literal code is code.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
ackandClaude Fable 5.1 committed 2026-09-21 22:37:13 +02:00
1 parent 63956c00e5
commit 217c0f3fc5
23 files changed
+625 -290

No files matched your search

+4 -3
View File
@@ -13,15 +13,16 @@ katom when no bar is written, "^@code :filename f :pattern p" DISPLAYED THOSE
WORDS as the listing rather than acting on them.
@@
@@code.html,tex :: @eval code_block.Code(K) @ @@
@@code.html,tex,txt :: @eval code_block.Code(K) @ @@
@@c.k code_text.literal :
@@c.k :nd.bool | code_text.literal :
A word or phrase displayed verbatim in a line. Like ^@code, its content is
LITERAL -- nothing in it is interpreted as Klammertext -- so it must be closed
with the named delimiter "c^@". ^@c is the in-line form and ^@code the block
form of the same thing; before 2026-08-16 this parameter was an ordinary
string, so a "^#" or a bare "^@" inside it was read as Klammertext and usually
failed the file, which is not what "verbatim" can mean.
If :nd (no delimiters) is true, then the txt target delimiters are excluded.
@@
@@c.html,tex,txt :: @eval code_block.Code_fragment(K) eval@
@@ -44,4 +45,4 @@ quietly different listing.
Named "^@source_file" until 2026-08-16. # retired-ok
@@
@@source_listing.html,tex :: @eval :cwd *K_input_dir* code_block.Source(K) @ @@
@@source_listing.html,tex,txt :: @eval :cwd *K_input_dir* code_block.Source(K) @ @@
+73 -7
View File
@@ -5,11 +5,13 @@
import re
import klammer_base
import kutil
import html_util
import txt_util
import phases
from html_util import E
import latex_util as L
def escape_newlines(s):
# A newline becomes the marker the html paragraph pass turns back into a
# line break, so a verbatim source file keeps its lines. Used by Source.
@@ -119,7 +121,13 @@ def tex_line(line):
a monospace font, which LaTeX will not collapse -- so the code's
indentation and internal alignment survive.
"""
return quote_specials(escape_latex(line)).replace(" ", "~")
# An apostrophe is hidden as a marker: after a quoted caret it would
# otherwise read back as "^" + "^'", the opening of a literal span, and
# "@c \^' c@" -- the regex of that very katom -- failed the document
# with a span that does not end. html_line hides it with the other
# typographic characters; the marker decodes to the plain character.
quoted = quote_specials(escape_latex(line)).replace("'", klammer_base.escape_ktesc("'"))
return quoted.replace(" ", "~")
def widest_line(lines):
r"""The line to measure the block's width with.
@@ -325,12 +333,64 @@ class Code(klammer_base.Klammer_base):
# As in tex: the caption goes through the shared helper, so it sits
# in the same position container as the listing and moves with it.
if self.number or self.caption:
# A left-placed (or flowing) listing's caption is flush with it,
# as in tex, where such a listing is unboxed and its caption a
# paragraph on the same indent; a centred or right-placed one is
# boxed there and its caption centred over the box, which is
# the helper's default here too.
align = "left" if self.hpos in ("left", "none") else None
return html_util.add_caption(
listing, "Listing", self.number, self.caption,
self.caption_style, self.hpos, self.caption_side, True,
self.caption_size, offset=self.offset)
self.caption_size, offset=self.offset, caption_align=align)
return html_util.hpos_container(listing, self.hpos, self.offset).str()
def txt_indent(self):
# The block's indent in columns, by @table's rule: a left- or
# right-placed block is inset by its :offset (0 = flush), ":hpos
# none" flows with no indent, and "center" -- not attempted in plain
# text -- keeps the block convention, Target_txt_indent.
if self.hpos in ("left", "right"):
return int(kutil.parse_width("txt", self.offset)[0])
if self.hpos == "none":
return 0
return int(self.Target_txt_indent)
def txt(self):
# The listing is LAID OUT here, line by line, and says so with the
# verbatim marker (phases.txt_lay_out) so the justification pass
# leaves it alone -- a blank line in the code included, which holds
# a marker and therefore does not end the paragraph.
#
# Each line is prepared exactly as txt()'s inline sibling prepares a
# fragment: the Klammertext specials QUOTED, because this result is
# re-read as Klammertext (raw, a "#" removed the rest of its line and
# a "code@" in a quoted source file closed the document's own span:
# "A named end katom does not match"), and the typographic characters
# hidden, so "--x" stays two hyphens. Spaces become the nbsp marker
# AFTER that, not "~": hide_typographic has already hidden the
# code's own tildes, and a "~" written here would be turned into a
# space by the txt target's transform only as long as it survived.
lines = self.text.strip("\n").split("\n")
indent = txt_util.NBSP * self.txt_indent()
def prepared(line):
line = klammer_base.hide_typographic(quote_specials(line.rstrip()))
return line.replace(" ", txt_util.NBSP)
width = max(len(line.rstrip()) for line in lines)
rule = indent + klammer_base.hide_typographic("-" * width)
body = [indent + prepared(line) for line in lines]
# A caption above the listing is ruled off by the caption pass, so
# the listing draws its own top rule only when nothing does.
captioned = self.number or self.caption
caption_above = captioned and self.caption_side in ("top", "left")
if not caption_above:
body.insert(0, rule)
body.append(rule)
result = phases.txt_lay_out("\n".join(body))
return txt_util.add_caption(
result, "Listing", self.number, self.caption, self.caption_side,
self.txt_indent())
class Code_fragment(klammer_base.Klammer_base):
def __init__(self, K):
@@ -356,10 +416,13 @@ class Code_fragment(klammer_base.Klammer_base):
# without a beginning) and the typographically active characters
# hidden, so a "--" in code stays two hyphens.
result = klammer_base.hide_typographic(quote_specials(self.code_text.strip()))
# Surround code symbols with ‹ and ›, though there are conflicts in Swiss
# German, Italian and some Scandinavian languages. Should be clear in
# context, though.
result = "\u2039" + result + "\u203A"
# If *nd* ("no delimiters) is false (the default), surround code symbols
# with the characters ‹ and ›. There are conflicts in Swiss German,
# Italian and some Scandinavian languages, but the meaning should be
# clear in context.
if not self.nd:
result = "\u2039" + result + "\u203A"
return result
def extract_marked_region(src, marker, filename):
@@ -424,6 +487,9 @@ class Source(Code):
# not exist until the file has been read. File content carries no
# whitespace markers anyway, since those come from the katomizer.
klammer_base.Klammer_base.__init__(self, K)
# The filename arrives with the target's escapes as markers -- under
# tex an underscore is one -- and a marker names no file.
self.filename = klammer_base.unescape_ktesc(self.filename)
try:
with open(self.filename) as fp:
text = fp.read()
+5 -1
View File
@@ -30,7 +30,11 @@
.code_block {
display: flex;
align-items: center;
margin: 0 0 0 1rem;
/* No left margin: it was 1rem, an html-only inset with no tex
counterpart, which set the code a rem to the right of its own
flush-left caption and of the same listing's pdf. A listing's
inset is :offset, in both targets (the hpos_margin precedent). */
margin: 0;
padding: 0;
}