kdesc --rewrite from the reflection klammerset; txt caption rules the block's width

Assembled from klammertext-dev e99530408517. The katom rewrite patterns
are rendered from ref/ (a table in dispatch order, patterns through @c),
the procedural listing having left mac/; a plain-text caption's rule is
as long as the table or listing it captions; the editor-support comments
name the tools' new dev/ location.
This commit is contained in:
ack committed 2026-09-22 22:53:13 +02:00
1 parent 93d649e23c
commit c016b3e5f3
15 files changed
+101 -86

No files matched your search

+1 -1
View File
@@ -51,7 +51,7 @@ are regenerated on each release — patches cannot be merged directly.
Report problems (or send patches) to the author; accepted changes are Report problems (or send patches) to the author; accepted changes are
applied to the development tree and appear in a following snapshot. applied to the development tree and appear in a following snapshot.
This snapshot was assembled from development commit `e727ce1d248d`. This snapshot was assembled from development commit `e99530408517`.
## License ## License
+8 -1
View File
@@ -201,6 +201,13 @@ void describe_katoms(bool show_regex)
std::cout << "\n" << M.apply(text_target()) << "\n\n"; std::cout << "\n" << M.apply(text_target()) << "\n\n";
} }
void show_katom_rewrite_patterns()
{
Machine M = ref_machine();
M.read_string("@katom_rewrite_patterns@");
std::cout << "\n" << M.apply(text_target()) << "\n\n";
}
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
try { try {
@@ -269,7 +276,7 @@ int main(int argc, char* argv[])
} }
if (p("rewrite")) { if (p("rewrite")) {
describe_rewrite_patterns(); show_katom_rewrite_patterns();
} }
// The --typeface subcommands operate on the Klammertext font store // The --typeface subcommands operate on the Klammertext font store
+1 -1
View File
@@ -75,7 +75,7 @@ Two kinds of artifact cannot read it and restate parts of it by hand:
## Testing ## Testing
`tst/editor_test.sh` (run by `dbg/rebuild.sh` and `make -C tst test`) `tst/editor_test.sh` (run by `dev/rebuild.sh` and `make -C tst test`)
drives the fixture pairs in `tst/editor/` through the shared core's API drives the fixture pairs in `tst/editor/` through the shared core's API
and CLI, checks idempotence, checks the Emacs implementation for and CLI, checks idempotence, checks the Emacs implementation for
byte-equality, runs the language server through a scripted LSP client byte-equality, runs the language server through a scripted LSP client
+1 -1
View File
@@ -42,7 +42,7 @@
# * the '@code'/'@c' rules in doc/edit/vscode/syntaxes/klammertext.tmLanguage.json # * the '@code'/'@c' rules in doc/edit/vscode/syntaxes/klammertext.tmLanguage.json
# #
# Installation note: editors locate this file either next to their own plugin # Installation note: editors locate this file either next to their own plugin
# files (a vendored copy, placed there by doc/make_editing_zip.sh), as # files (a vendored copy, placed there by dev/make_editing_zip.sh), as
# ../shared/klammertext_edit.py relative to the plugin directory (the layout # ../shared/klammertext_edit.py relative to the plugin directory (the layout
# of this repository), or under $KLAMMERTEXT_HOME/doc/edit/shared/. # of this repository), or under $KLAMMERTEXT_HOME/doc/edit/shared/.
# #
+1 -1
View File
@@ -27,7 +27,7 @@
# policy list. This file is only the Sublime wrapper. # policy list. This file is only the Sublime wrapper.
# #
# The shared core is located next to this file (a vendored copy — the # The shared core is located next to this file (a vendored copy — the
# installed-package layout produced by doc/make_editing_zip.sh), or in # installed-package layout produced by dev/make_editing_zip.sh), or in
# ../shared (the repository layout), or under $KLAMMERTEXT_HOME. # ../shared (the repository layout), or under $KLAMMERTEXT_HOME.
# #
# SYNC: the literal-klammer set in the shared core must agree with the @code # SYNC: the literal-klammer set in the shared core must agree with the @code
+1 -1
View File
@@ -23,7 +23,7 @@
# Sublime command wrapper. # Sublime command wrapper.
# #
# The shared core is located next to this file (a vendored copy — the # The shared core is located next to this file (a vendored copy — the
# installed-package layout produced by doc/make_editing_zip.sh), or in # installed-package layout produced by dev/make_editing_zip.sh), or in
# ../shared (the repository layout), or under $KLAMMERTEXT_HOME. # ../shared (the repository layout), or under $KLAMMERTEXT_HOME.
import os import os
+1 -1
View File
@@ -18,7 +18,7 @@
# the language server. This file is only the Sublime command wrapper. # the language server. This file is only the Sublime command wrapper.
# #
# The shared core is located next to this file (a vendored copy — the # The shared core is located next to this file (a vendored copy — the
# installed-package layout produced by doc/make_editing_zip.sh), or in # installed-package layout produced by dev/make_editing_zip.sh), or in
# ../shared (the repository layout), or under $KLAMMERTEXT_HOME. # ../shared (the repository layout), or under $KLAMMERTEXT_HOME.
import os import os
+1 -1
View File
@@ -18,7 +18,7 @@ UNAME_S := $(shell uname -s)
CPP_VERSION := c++20 CPP_VERSION := c++20
# Default compiler: clang on macOS, gcc elsewhere. gcc-built Klammertext # Default compiler: clang on macOS, gcc elsewhere. gcc-built Klammertext
# binaries crash at runtime on macOS (a documented gcc/macOS codegen issue), so # binaries crash at runtime on macOS (a documented gcc/macOS codegen issue), so
# gcc there is only a compile-time conformance check (via `./dbg/rebuild.sh # gcc there is only a compile-time conformance check (via `./dev/rebuild.sh
# gcc`, which compiles under g++ then rebuilds with clang). Override with # gcc`, which compiles under g++ then rebuilds with clang). Override with
# `make COMPILER=...`. # `make COMPILER=...`.
ifeq ($(UNAME_S),Darwin) ifeq ($(UNAME_S),Darwin)
+1 -1
View File
@@ -4,7 +4,7 @@
# Like ref/reflect.so and sks/document/document.so it links against # Like ref/reflect.so and sks/document/document.so it links against
# libklammertext.so and shares the engine's class layouts, so it is rebuilt # libklammertext.so and shares the engine's class layouts, so it is rebuilt
# whenever they are (mac/Makefile's "all" runs "make -C kpy"; com/Makefile's # whenever they are (mac/Makefile's "all" runs "make -C kpy"; com/Makefile's
# "redo" and dbg/rebuild.sh clean it -- a clean that misses a directory is a # "redo" and dev/rebuild.sh clean it -- a clean that misses a directory is a
# mixed-object hazard, not a tidiness one). # mixed-object hazard, not a tidiness one).
# #
# The module's filename carries the interpreter's ABI tag # The module's filename carries the interpreter's ABI tag
+1 -1
View File
@@ -261,7 +261,7 @@ std::string Eval_python::eval(std::string code)
(void)K::log(3, code); (void)K::log(3, code);
code = m_machine.m_state.subst(code, true); code = m_machine.m_state.subst(code, true);
// Kept as a record of a value worth watching; line comments rather than a // Kept as a record of a value worth watching; line comments rather than a
// block, so the msg() guard can see it is inert (doc/check_output_policy.sh // block, so the msg() guard can see it is inert (dev/check_output_policy.sh
// is line-based and cannot tell it is inside a /* */). // is line-based and cannot tell it is inside a /* */).
// msg() << "\n" // msg() << "\n"
// << std::string(80, '-') << "\n" // << std::string(80, '-') << "\n"
-58
View File
@@ -321,61 +321,3 @@ bool category_order_holds(const std::string& word, std::string& why, rewrite_ord
} }
return true; return true;
} }
void describe_rewrite_patterns()
{
if (verbose_level > 0) {
std::string desc_text = "doc/katom_patterns.desc";
std::cout << "\n"
<< justify(string_from_file(klammertext_filename(desc_text)))
<< "\n\n";
}
size_t width0 = 0;
size_t width1 = 0;
size_t width2 = 0;
for (const Rewrite_rule& rule : rewrite_rules) {
width0 = std::max(width0, category_name(rule.category).size());
width1 = std::max(width1, rule.description.size());
width2 = std::max(width2, rule.pattern.m_pattern.size());
}
width0 += 2;
width1 += 2;
width2 += 2;
std::cout << boldblack << std::setfill(' ')
<< std::setw(width0) << std::left << " Category"
<< std::setw(width1) << std::left << "Description"
<< std::setw(width2) << std::left << "Pattern"
<< "Replacement" << black << "\n";
// Dispatch order, not declaration order: what a reader needs is the
// sequence the rules are actually TRIED in, since that is what decides
// which one fires.
rule_category shown {};
bool first = true;
for (size_t i : rule_order(rewrite_order::by_category)) {
const Rewrite_rule& rule = rewrite_rules[i];
std::string category = category_name(rule.category);
if (first || rule.category != shown) {
shown = rule.category;
first = false;
} else {
category = ""; // name a category once, at its head
}
std::string desc = rule.description;
desc[0] = static_cast<char>(toupper(desc[0]));
std::cout << " "
<< std::left
<< std::setw(width0) << category
<< std::setw(width1) << desc
<< std::setw(width2) << rule.pattern.m_pattern
<< (rule.special ? "(not a pattern replacement)" : rule.replacement)
<< "\n";
}
if (verbose_level > 0) {
std::string notes_text = "doc/katom_patterns.notes";
std::cout << "\n\n"
<< justify(string_from_file(klammertext_filename(notes_text)))
<< "\n\n";
}
}
-6
View File
@@ -142,9 +142,3 @@ std::vector<size_t> matching_rules(const std::string& word, rewrite_order order
// state an invariant across. // state an invariant across.
bool category_order_holds(const std::string& word, std::string& why, bool category_order_holds(const std::string& word, std::string& why,
rewrite_order order = rewrite_order::by_category); rewrite_order order = rewrite_order::by_category);
// The "kdesc --rewrite" listing. It moved here with the table it reads,
// and gained the CATEGORY column -- which is the point of the categories
// reaching the user rather than only the maintainer: a reader sees the nine
// groups and their order instead of thirty positions.
void describe_rewrite_patterns();
+7
View File
@@ -48,3 +48,10 @@
@@katom_descriptions.* :: @@katom_descriptions.* ::
@eval :cpp *KLAMMERTEXT_HOME*/ref/reflect katom_descriptions @ @@ @eval :cpp *KLAMMERTEXT_HOME*/ref/reflect katom_descriptions @ @@
@@katom_rewrite_patterns.k :
Show the rewrite patterns for parsed text that is not a katom @@
@@katom_rewrite_patterns.* ::
@eval :cpp *KLAMMERTEXT_HOME*/ref/reflect katom_rewrite_patterns @ @@
+57 -12
View File
@@ -194,6 +194,24 @@ std::string argtype_definitions(Machine& machine)
} }
// Verbatim text as a table cell: a regex, a replacement string. It goes
// through @c, whose literal content is exempt from every special (a bar, a
// hash, an at sign) -- but a literal value has its caret forms processed as
// it is built, so a caret is written quoted: bare, "[^^@#|]+" would display
// as "[^@#|]+" and "[@|#^:*]" would lose its caret to the colon. The
// exception is a caret before an apostrophe: "^'" is not a quoting form (it
// opens a literal span elsewhere) and passes through as written.
static std::string code_cell(const std::string& text)
{
std::string quoted {};
for (size_t i = 0; i < text.size(); i++) {
const char c = text[i];
const bool before_apostrophe = i + 1 < text.size() && text[i + 1] == '\'';
quoted += (c == '^' && !before_apostrophe) ? "^^" : std::string(1, c);
}
return "@c :nd | " + quoted + " c@";
}
// The Regex cell of a katom type. A type the Machine assigns is matched by // The Regex cell of a katom type. A type the Machine assigns is matched by
// no regex and its cell is empty -- ws_added among them, whose m_pattern is a // no regex and its cell is empty -- ws_added among them, whose m_pattern is a
// description and not a regex (see the note in mac/katom_matching.cpp). The // description and not a regex (see the note in mac/katom_matching.cpp). The
@@ -209,18 +227,7 @@ static std::string katom_regex_cell(const Ktype& k)
if (k.m_type == katom_t::newline) { if (k.m_type == katom_t::newline) {
return "@t \\n @"; return "@t \\n @";
} }
// A literal value has its caret forms processed as it is built, so a return code_cell(k.m_pattern);
// caret is written quoted: bare, "[^^@#|]+" would display as "[^@#|]+"
// and "[@|#^:*]" would lose its caret to the colon.
// The exception is a caret before an apostrophe: "^'" is not a quoting
// form (it opens a literal span elsewhere) and passes through as written.
std::string quoted {};
for (size_t i = 0; i < k.m_pattern.size(); i++) {
const char c = k.m_pattern[i];
const bool before_apostrophe = i + 1 < k.m_pattern.size() && k.m_pattern[i + 1] == '\'';
quoted += (c == '^' && !before_apostrophe) ? "^^" : std::string(1, c);
}
return "@c :nd | " + quoted + " c@";
} }
extern "C" extern "C"
@@ -290,3 +297,41 @@ characters can be displayed by the @t-kdesc command: @t kdesc -c -u @
kt << rows.str() << "table@\n"; kt << rows.str() << "table@\n";
return kt.str(); return kt.str();
} }
extern "C"
std::string katom_rewrite_patterns([[maybe_unused]] Machine& machine)
{
std::stringstream kt {};
kt << "@s2 Katom rewrite patterns @\n\n"
<< R"(The table lists how a text is reformed or split into one or more katoms.
@i [Need a longer description here.] @
)"
<< "@table :caption The rewriting patterns for elements that are not katoms | "
<< "Category | Description | Pattern | Replacement ||\n";
// Dispatch order, not declaration order: what a reader needs is the
// sequence the rules are actually TRIED in, since that is what decides
// which one fires.
rule_category shown {};
bool first = true;
std::string b = " | ";
for (size_t i : rule_order(rewrite_order::by_category)) {
const Rewrite_rule& rule = rewrite_rules[i];
std::string category = category_name(rule.category);
if (first || rule.category != shown) {
shown = rule.category;
first = false;
} else {
category = ""; // name a category once, at its head
}
std::string desc = rule.description;
desc[0] = static_cast<char>(toupper(desc[0]));
kt << category << b << desc << b << code_cell(rule.pattern.m_pattern) << b
<< (rule.special ? "(hyphens separate word arguments)" : code_cell(rule.replacement))
<< " ||\n";
}
kt << "table@\n";
return kt.str();
}
+20
View File
@@ -244,10 +244,29 @@ def txt_insert_captions(text, K):
# "Listing 1", not "Listing 2". It was one counter for every label. # "Listing 1", not "Listing 2". It was one counter for every label.
caption_numbers = {} caption_numbers = {}
width = int(K.Target_txt_width) width = int(K.Target_txt_width)
verbatim = txt_verbatim_marker()
def block_width(lines):
# The visible width of the laid-out block adjacent to a caption:
# its lines carry the verbatim marker, and the first line that does
# not (or a blank line) ends the block.
widest = 0
for line in lines:
if not line.startswith(verbatim) or not line.strip():
break
widest = max(widest, txt_visible_length(line))
return widest
def replace(m): def replace(m):
indent_run, name, text = m.groups() indent_run, name, text = m.groups()
indent = len(indent_run) // len(nbsp) indent = len(indent_run) // len(nbsp)
indented_width = max(1, width - indent) indented_width = max(1, width - indent)
# The block the caption belongs to: below a top caption, above a
# bottom one. Its width is what the caption's rule is drawn to,
# so the rule under "Table 1 - Prices" is as long as the table's
# own rules, not as long as those words. (Before the match sits
# the caption line's own verbatim marker: drop that line.)
after = m.string[m.end():].split("\n")[1:]
before = m.string[:m.start()].split("\n")[:-1]
block = max(block_width(after), block_width(reversed(before)))
# An empty label means an unnumbered caption (":number false"). It # An empty label means an unnumbered caption (":number false"). It
# must NOT take a number from the sequence: a reader counting the # must NOT take a number from the sequence: a reader counting the
# captions they can see would then find the next one a number # captions they can see would then find the next one a number
@@ -263,6 +282,7 @@ def txt_insert_captions(text, K):
if txt_visible_length(caption) > indented_width: if txt_visible_length(caption) > indented_width:
lines = txt_wrap(caption, indented_width) lines = txt_wrap(caption, indented_width)
line_length = max(txt_visible_length(e) for e in lines) line_length = max(txt_visible_length(e) for e in lines)
line_length = max(line_length, block - indent)
lines.append("-" * line_length) lines.append("-" * line_length)
lead = txt_verbatim_marker() + indent_run lead = txt_verbatim_marker() + indent_run
# The first line's marker is already in the text, before the indent # The first line's marker is already in the text, before the indent