diff options
| author | Ralph Amissah <ralph.amissah@gmail.com> | 2026-09-21 16:12:56 -0400 |
|---|---|---|
| committer | Ralph Amissah <ralph.amissah@gmail.com> | 2026-09-23 08:22:15 -0400 |
| commit | 8b81e9a11c7af642c4d0d8584a9cb348cc887d6e (patch) | |
| tree | 8ba9c356ff71bf18f5623c22d30a8b2513321cfe /src/sisudoc/ocda | |
| parent | uid and paths separator "~" in place of ":" (diff) | |
ocda: warn if heading claims reserved segment name
spine automatically builds some segments, including: (toc, endnotes,
glossary, bibliography, bookindex, blurb, _the_title) these are now
identified as reserved names and a user is now warned if any of these
names have been manually assigned to a heading by markup. A document
still builds but to disambiguate the ocn of the heading is attached to
the markup (reserved) name and this is seeded before a document not
after. It is read off the finished abstraction rather than reported by
the parser, and beside the ocn alignment check for the same reason: it
is a statement about a document rather than a step in building one.
WARNING reserved segment name: the_autonomous_contract... [en]
heading 1 at ocn 135 asks for "endnotes", which spine gives its
own generated section
it is named "endnotes-135" instead; ...
A warning: the document is correct and complete and the name it ends
up with works.
--strict makes it a failure for the run, as it does for ocn alignment,
and by the same reasoning: the outputs are written and can be looked at,
and the exit status is taken at the end.
Two of the thirty-six sample documents have reserved segment names,
"1~endnotes" heading. Output is unchanged: nothing here touches the
abstraction.
(assisted by Claude-Code)
Diffstat (limited to 'src/sisudoc/ocda')
| -rw-r--r-- | src/sisudoc/ocda/meta/metadoc_from_src.d | 12 | ||||
| -rw-r--r-- | src/sisudoc/ocda/meta/metadoc_from_src_functions.d | 15 | ||||
| -rw-r--r-- | src/sisudoc/ocda/meta/reserved_names.d | 206 |
3 files changed, 225 insertions, 8 deletions
diff --git a/src/sisudoc/ocda/meta/metadoc_from_src.d b/src/sisudoc/ocda/meta/metadoc_from_src.d index 2731573..3670a39 100644 --- a/src/sisudoc/ocda/meta/metadoc_from_src.d +++ b/src/sisudoc/ocda/meta/metadoc_from_src.d @@ -97,6 +97,18 @@ template docAbstraction() { anchor_tag = ""; _heading_anchor_tags_seen = _reserved_heading_anchor_tags(); } + /+ ↓ seeded here, on the way in, which is the seeding that matters. + . + The two assignments in scope(exit) and at the tail of this function run + after a document has been abstracted, so they seed the *next* one. The + first document of a run had nothing before it and began with the set as + declared, empty: it alone was not held to the reserved names, and a heading + of its own claiming "endnotes" kept the name of the generated section. + Which document that was depended on what else was in the run, so a + document's abstraction depended on its company, and a single document + re-parsed to check an artefact made in a collection run disagreed with it. + +/ + _heading_anchor_tags_seen = _reserved_heading_anchor_tags(); mixin spineNode; int[string] node_para_int_ = node_metadata_para_int; string[string] node_para_str_ = node_metadata_para_str; diff --git a/src/sisudoc/ocda/meta/metadoc_from_src_functions.d b/src/sisudoc/ocda/meta/metadoc_from_src_functions.d index 0deeb72..c5a63cd 100644 --- a/src/sisudoc/ocda/meta/metadoc_from_src_functions.d +++ b/src/sisudoc/ocda/meta/metadoc_from_src_functions.d @@ -77,16 +77,15 @@ template docAbstractionFunctions() { these names means the document's heading is disambiguated the same way a repeated anchor tag is, and the generated section keeps the name that the table of contents links to. + . + The list itself is in sisudoc.ocda.meta.reserved_names, with the + check that reports such a heading to its author. Two lists would be + two answers to what spine has taken. +/ bool[string] _reserved_heading_anchor_tags() { - bool[string] _reserved; - foreach (_name; [ - "toc", "endnotes", "glossary", "bibliography", "bookindex", "blurb", - "_the_title", - ]) { - _reserved[_name] = true; - } - return _reserved; + import sisudoc.ocda.meta.reserved_names; + mixin spineReservedNames _rn; + return _rn.reservedHeadingAnchorTags(); } string lev_anchor_tag; string[string][string] tag_assoc; diff --git a/src/sisudoc/ocda/meta/reserved_names.d b/src/sisudoc/ocda/meta/reserved_names.d new file mode 100644 index 0000000..8e49ae5 --- /dev/null +++ b/src/sisudoc/ocda/meta/reserved_names.d @@ -0,0 +1,206 @@ +/+ +- Name: SisuDoc Spine, Doc Reform [a part of] + - Description: documents, structuring, processing, publishing, search + - static content generator + + - Author: Ralph Amissah + [ralph.amissah@gmail.com] + + - Copyright: (C) 2015 (continuously updated, current 2026) Ralph Amissah, All Rights Reserved. + + - License: AGPL 3 or later: + + Spine (SiSU), a framework for document structuring, publishing and + search + + Copyright (C) Ralph Amissah + + This program is free software: you can redistribute it and/or modify it + under the terms of the GNU AFERO General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program. If not, see [https://www.gnu.org/licenses/]. + + If you have Internet connection, the latest version of the AGPL should be + available at these locations: + [https://www.fsf.org/licensing/licenses/agpl.html] + [https://www.gnu.org/licenses/agpl.html] + + - Spine (by Doc Reform, related to SiSU) uses standard: + - docReform markup syntax + - standard SiSU markup syntax with modified headers and minor modifications + - docReform object numbering + - standard SiSU object citation numbering & system + + - Homepages: + [https://www.sisudoc.org] + [https://www.doc-reform.org] + + - Git + [https://git.sisudoc.org/] + ++/ +/++ + the segment names spine keeps for itself<br><br> + + the one list of them, and the check that says when a document's own + heading has claimed one<br><br> + + [sisudoc.ocda.meta.reserved_names] ++/ +module sisudoc.ocda.meta.reserved_names; +@safe: +/+ ↓ spine names the sections it generates, these names are reserved. + . + The table of contents, the gathered endnotes, the glossary, the + bibliography, the book index and the blurb are not in the markup: spine + makes them, and gives each a segment name of its own. A document whose + own heading asks for one of those names ("1~endnotes Endnote") is asking + for a name that is already taken, and before this was noticed both wrote + into the same file: the generated section and the author's heading, with + the navigation listing that file twice, the second time behind its own + earlier entry in reading order. + . + The parser therefore seeds the set of heading anchor names it has seen + with these before it reads a document, so that a heading claiming one is + disambiguated by ocn suffix the same way a repeated anchor tag is, and + the generated section keeps the name the table of contents links to. + . + Two things live here rather than in the parser: + . + the list so that the parser's seeding and the check below cannot + come to hold different ideas of what is reserved; + . + the check which runs over a finished abstraction, names the + headings that were renamed, and is what tells an author + that a heading of theirs is not called what they wrote. + A warning: the document is correct and complete, and the + name it ends up with works. --strict makes it a failure, + for the run that is meant to be publishable. + . + The renaming is not something the author has to accept. A heading given + any other anchor name ("1~my-notes Endnote") keeps it, and the warning + goes away. ++/ +/+ ↓ mixed in as a named mixin (mixin spineReservedNames _name;) and its + imports are inside its functions rather than at template scope: what a + mixin template declares at its own scope is visible in the scope it is + mixed into, and an import there can hijack a name the host was already + resolving by UFCS. ++/ +template spineReservedNames() { + /+ ↓ the names spine makes for itself, in one place. + _the_title is the head section's own; the rest are the generated + sections, and are the section keys of the abstraction. + +/ + string[] reservedHeadingAnchorNames() { + return [ + "toc", "endnotes", "glossary", "bibliography", "bookindex", "blurb", + "_the_title", + ]; + } + /+ ↓ the same, as the set the parser seeds itself with +/ + bool[string] reservedHeadingAnchorTags() { + bool[string] _reserved; + foreach (_name; reservedHeadingAnchorNames()) { _reserved[_name] = true; } + return _reserved; + } + /+ ↓ one heading that asked for a name spine had already taken +/ + struct ST_ReservedNameUse { + string section; // where in the abstraction it sits + string reserved; // the name it asked for + string anchor; // the name it was given instead + string level; // as marked up, :A :B 1 2 ... + int ocn; + } + /+ ↓ was this heading renamed out of the way of a reserved name? + . + Read off the anchor rather than reported by the parser: the parser + renames by appending the object's number, so "endnotes" becomes + "endnotes-135" on the object whose ocn is 135, and that pairing is what + identifies it. Requiring the number to be the object's own is what keeps + a heading an author really did call "endnotes-135" from being reported as + something it is not. + +/ + private string _reservedNameClaimedBy(O)(O obj) { + import std.conv : to; + string _anchor = obj.tags.anchor_tag_html; + if (_anchor.length == 0) { return ""; } + string _suffix = "-" ~ obj.metainfo.ocn.to!string; + if (_anchor.length <= _suffix.length) { return ""; } + if (_anchor[($ - _suffix.length) .. $] != _suffix) { return ""; } + string _stem = _anchor[0 .. ($ - _suffix.length)]; + foreach (_name; reservedHeadingAnchorNames()) { + if (_stem == _name) { return _name; } + } + return ""; + } + /+ ↓ every heading of a document that claimed a reserved name. + document order, the section order the .ssp is written in, so that two + runs over the same document report it the same way round. + +/ + ST_ReservedNameUse[] reservedNameHeadings(D)(D doc) { + import std.algorithm : canFind, sort; + import std.conv : to; + ST_ReservedNameUse[] _out; + string[] _sections = ["head", "toc", "body", "endnotes", + "glossary", "bibliography", "bookindex", "blurb", "tail"]; + string[] _extra; + foreach (_k; doc.abstraction.byKey) { + if (!_sections.canFind(_k)) { _extra ~= _k; } + } + foreach (_k; _extra.sort) { _sections ~= _k; } + foreach (section; _sections) { + if (section !in doc.abstraction) { continue; } + foreach (obj; doc.abstraction[section]) { + if (obj.metainfo.is_a != "heading") { continue; } + string _claimed = _reservedNameClaimedBy(obj); + if (_claimed.length == 0) { continue; } + /+ ↓ the generated sections carry the reserved names themselves and are + not what this is about: they are headings spine made, and they keep + the plain name. Only an object that was renamed reaches here, and a + generated one never is. + +/ + ST_ReservedNameUse _u; + _u.section = section; + _u.reserved = _claimed; + _u.anchor = obj.tags.anchor_tag_html; + _u.level = obj.metainfo.marked_up_level; + _u.ocn = obj.metainfo.ocn.to!int; + _out ~= _u; + } + } + return _out; + } + /+ ↓ the report, as the lines to print. + the document is named once and its headings indented under it, as the ocn + alignment check does, so that a run over a collection reads as a list of + documents. + +/ + string[] reservedNameReportLines( + string doc_key, + string lang, + ST_ReservedNameUse[] _uses, + ) { + import std.conv : to; + string[] _out; + if (_uses.length == 0) { return _out; } + _out ~= "WARNING reserved segment name: " ~ doc_key + ~ ((lang.length > 0) ? " [" ~ lang ~ "]" : ""); + foreach (_u; _uses) { + _out ~= " heading " ~ _u.level ~ " at ocn " ~ _u.ocn.to!string + ~ " asks for \"" ~ _u.reserved + ~ "\", which spine gives its own generated section"; + _out ~= " it is named \"" ~ _u.anchor + ~ "\" instead; give the heading another name to keep one of your own"; + } + return _out; + } +} |
