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:
1 parent
63956c00e5
commit
217c0f3fc5
23 files changed
+625
-290
No files matched your search
+69
-20
@@ -147,6 +147,36 @@ def tex_nl_marker():
|
||||
def tex_resolve_markers(text, K):
|
||||
return text.replace(tex_nl_marker(), "\\newline")
|
||||
|
||||
def txt_runs(par):
|
||||
"""A paragraph as runs of lines: [(laid_out, text), ...].
|
||||
|
||||
"Laid out or prose" was decided ONCE per paragraph, by its first line,
|
||||
so a laid-out block written directly after prose -- no blank line
|
||||
between them -- was re-flowed with it, every one of its lines marked
|
||||
or not: a generator whose prose ran straight into its @table put the
|
||||
table's header on the last line of the sentence (2026-09-20). A blank
|
||||
line should not be what protects a block that has already said, line
|
||||
by line, that it is laid out.
|
||||
|
||||
So a line that BEGINS WITH THE VERBATIM MARKER is laid out wherever it
|
||||
stands, and the paragraph divides into runs at each change. Only the
|
||||
marker divides: it is explicit and per line (txt_lay_out). A leading
|
||||
space or nbsp keeps the older, whole-paragraph meaning and only on the
|
||||
FIRST line, since a later line of prose may legitimately begin with
|
||||
one (a footnote's hanging indent).
|
||||
"""
|
||||
if par.startswith((" ", txt_nbsp_marker())):
|
||||
return [(True, par)]
|
||||
mark = txt_verbatim_marker()
|
||||
runs = []
|
||||
for line in par.split("\n"):
|
||||
laid_out = line.startswith(mark)
|
||||
if runs and runs[-1][0] == laid_out:
|
||||
runs[-1][1].append(line)
|
||||
else:
|
||||
runs.append((laid_out, [line]))
|
||||
return [(laid_out, "\n".join(lines)) for laid_out, lines in runs]
|
||||
|
||||
def txt_justify_blocks(text, K):
|
||||
delim = '__DIVIDE__'
|
||||
text = re.sub("\n\n+", delim, text)
|
||||
@@ -178,13 +208,20 @@ def txt_justify_blocks(text, K):
|
||||
# verbatim paragraph was not left alone, it was DELETED: the one
|
||||
# branch that had nothing to do also did not copy the text through.
|
||||
# An indented paragraph vanished from the output with no error.
|
||||
#
|
||||
# The decision is per RUN of lines, not per paragraph (txt_runs):
|
||||
# prose is filled, a laid-out run is copied through, and the runs
|
||||
# stay on consecutive lines as they were written.
|
||||
nbsp = txt_nbsp_marker()
|
||||
if par and not txt_is_laid_out(par):
|
||||
label = re.match("(?:" + re.escape(nbsp) + r")*\[\d+\] ", par)
|
||||
par = "\n".join(txt_wrap(
|
||||
par, int(K.Target_txt_width),
|
||||
subsequent_indent=" " * len(label.group(0)) if label else ""))
|
||||
result += par + "\n\n"
|
||||
filled = []
|
||||
for laid_out, run in txt_runs(par):
|
||||
if run and not laid_out:
|
||||
label = re.match("(?:" + re.escape(nbsp) + r")*\[\d+\] ", run)
|
||||
run = "\n".join(txt_wrap(
|
||||
run, int(K.Target_txt_width),
|
||||
subsequent_indent=" " * len(label.group(0)) if label else ""))
|
||||
filled.append(run)
|
||||
result += "\n".join(filled) + "\n\n"
|
||||
result = re.sub(" *" + txt_vspace_marker() + " *", "\n", result)
|
||||
result = re.sub(r"\n? *" + txt_nl_marker() + r" *\n?", "\n", result)
|
||||
result = result.replace(txt_verbatim_marker(), "")
|
||||
@@ -192,33 +229,45 @@ def txt_justify_blocks(text, K):
|
||||
return result
|
||||
|
||||
def txt_insert_captions(text, K):
|
||||
# The caption's INDENT is the block's, read off the run of nbsp markers
|
||||
# written before the caption marker (txt_util.add_caption) -- not
|
||||
# Target_txt_indent, which put the caption of a flush block two columns
|
||||
# to the right of the block it captions. Every line produced here is
|
||||
# marked laid-out: a flush caption begins with neither a space nor an
|
||||
# nbsp, and an unmarked first line made the justification pass re-flow
|
||||
# the caption, its rule and the whole block under it into one paragraph.
|
||||
mark = kutil.caption_delimiter()
|
||||
caption_rgx = re.compile(f"{mark}(.*?){mark}(.*?){mark}", re.S)
|
||||
caption_number = 0
|
||||
nbsp = txt_nbsp_marker()
|
||||
caption_rgx = re.compile(
|
||||
f"((?:{re.escape(nbsp)})*){mark}(.*?){mark}(.*?){mark}", re.S)
|
||||
# One sequence PER LABEL, as in html and tex: a listing after a table is
|
||||
# "Listing 1", not "Listing 2". It was one counter for every label.
|
||||
caption_numbers = {}
|
||||
width = int(K.Target_txt_width)
|
||||
indent = int(K.Target_txt_indent)
|
||||
indented_width = width - indent
|
||||
def replace(m):
|
||||
nonlocal caption_number
|
||||
name, text = m.groups()
|
||||
indent_run, name, text = m.groups()
|
||||
indent = len(indent_run) // len(nbsp)
|
||||
indented_width = max(1, width - indent)
|
||||
# An empty label means an unnumbered caption (":number false"). It
|
||||
# must NOT take a number from the sequence: a reader counting the
|
||||
# captions they can see would then find the next one a number
|
||||
# ahead. The same is true of html and tex, where an unnumbered
|
||||
# caption emits no marker and so never reaches their counter.
|
||||
if name:
|
||||
caption_number += 1
|
||||
caption = f"{name} {caption_number} - {text}"
|
||||
caption_numbers[name] = caption_numbers.get(name, 0) + 1
|
||||
caption = f"{name} {caption_numbers[name]} - {text}"
|
||||
else:
|
||||
caption = text
|
||||
# Visible widths throughout: a caption may carry style markers.
|
||||
lines = [caption]
|
||||
if txt_visible_length(caption) > indented_width:
|
||||
caption = "\n".join(txt_wrap(
|
||||
caption, indented_width,
|
||||
subsequent_indent=txt_nbsp_marker()*indent))
|
||||
line_length = max([txt_visible_length(e) for e in caption.split("\n")])
|
||||
line = txt_nbsp_marker() * indent + "-" * line_length
|
||||
return caption + "\n" + line
|
||||
lines = txt_wrap(caption, indented_width)
|
||||
line_length = max(txt_visible_length(e) for e in lines)
|
||||
lines.append("-" * line_length)
|
||||
lead = txt_verbatim_marker() + indent_run
|
||||
# The first line's marker is already in the text, before the indent
|
||||
# run this match begins with (add_caption); the others get theirs.
|
||||
return indent_run + ("\n" + lead).join(lines)
|
||||
result = caption_rgx.sub(replace, text)
|
||||
return result
|
||||
|
||||
|
||||
Reference in new issue
Block a user