1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
|
-*- mode: org -*-
#+TITLE: sisudoc spine (doc_reform) object-centric document abstraction
#+DESCRIPTION: documents - structuring, publishing in multiple formats & search
#+SUMMARY: process markup document, create document abstraction
#+FILETAGS: :spine:abstraction:
#+AUTHOR: Ralph Amissah
#+EMAIL: [[mailto:ralph.amissah@gmail.com][ralph.amissah@gmail.com]]
#+COPYRIGHT: Copyright (C) 2015 (continuously updated, current 2026) Ralph Amissah
#+LANGUAGE: en
#+STARTUP: content hideblocks hidestars noindent entitiespretty
#+PROPERTY: header-args+ :eval never-export :exports code
#+PROPERTY: header-args+ :noweb yes :padline no
#+PROPERTY: header-args+ :results silent :cache no
#+PROPERTY: header-args+ :mkdirp yes
#+OPTIONS: H:3 num:nil toc:t \n:t ::t |:t ^:nil -:t f:t *:t
- magic single double-quote → " ← FIX changes hilighting behavior (occuring
after it) in org document. INVESTIGATE (org-mode CONFIG?) FIND & FIX
- [[./doc-reform.org][doc-reform.org]] [[./][org/]]
* reserved file names
#+HEADER: :tangle "../src/sisudoc/ocda/meta/reserved_names.d"
#+HEADER: :noweb yes
#+BEGIN_SRC d
<<doc_header_including_copyright_and_license>>
/++
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;
}
}
#+END_SRC
* org includes
** project version
#+NAME: spine_version
#+HEADER: :noweb yes
#+BEGIN_SRC emacs-lisp
<<./sisudoc_spine_version_info_and_doc_header_including_copyright_and_license.org:spine_project_version()>>
#+END_SRC
** year
#+NAME: year
#+HEADER: :noweb yes
#+BEGIN_SRC emacs-lisp
<<./sisudoc_spine_version_info_and_doc_header_including_copyright_and_license.org:year()>>
#+END_SRC
** document header including copyright & license
#+NAME: doc_header_including_copyright_and_license
#+HEADER: :noweb yes
#+BEGIN_SRC emacs-lisp
<<./sisudoc_spine_version_info_and_doc_header_including_copyright_and_license.org:spine_doc_header_including_copyright_and_license()>>
#+END_SRC
* __END__
|