docbook 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/CLAUDE.md +19 -0
- data/CODE_OF_CONDUCT.md +10 -0
- data/README.adoc +335 -0
- data/Rakefile +12 -0
- data/docs/.lycheeignore +33 -0
- data/docs/Gemfile +10 -0
- data/docs/INDEX.adoc +67 -0
- data/docs/_config.yml +186 -0
- data/docs/advanced/element-classes.adoc +185 -0
- data/docs/advanced/frontend-customization.adoc +193 -0
- data/docs/advanced/index.adoc +14 -0
- data/docs/advanced/templates.adoc +123 -0
- data/docs/features/element-coverage.adoc +373 -0
- data/docs/features/html-output/data-model.adoc +285 -0
- data/docs/features/html-output/directory-mode.adoc +180 -0
- data/docs/features/html-output/index.adoc +90 -0
- data/docs/features/html-output/single-file-mode.adoc +125 -0
- data/docs/features/index-generation.adoc +197 -0
- data/docs/features/index.adoc +63 -0
- data/docs/features/numbering.adoc +183 -0
- data/docs/features/toc-generation.adoc +150 -0
- data/docs/features/xinclude/fragid-schemes.adoc +287 -0
- data/docs/features/xinclude/index.adoc +119 -0
- data/docs/features/xinclude/text-includes.adoc +123 -0
- data/docs/features/xinclude/xml-includes.adoc +167 -0
- data/docs/getting-started/index.adoc +50 -0
- data/docs/getting-started/installation.adoc +113 -0
- data/docs/getting-started/quick-start.adoc +161 -0
- data/docs/guides/converting-article.adoc +188 -0
- data/docs/guides/converting-book.adoc +192 -0
- data/docs/guides/index.adoc +12 -0
- data/docs/guides/roundtrip-testing.adoc +129 -0
- data/docs/interfaces/cli/format-command.adoc +109 -0
- data/docs/interfaces/cli/index.adoc +73 -0
- data/docs/interfaces/cli/roundtrip-command.adoc +125 -0
- data/docs/interfaces/cli/to-html-command.adoc +178 -0
- data/docs/interfaces/cli/validate-command.adoc +104 -0
- data/docs/interfaces/index.adoc +101 -0
- data/docs/interfaces/ruby-api/html-output.adoc +186 -0
- data/docs/interfaces/ruby-api/index.adoc +111 -0
- data/docs/interfaces/ruby-api/parsing.adoc +202 -0
- data/docs/interfaces/ruby-api/xinclude.adoc +162 -0
- data/docs/interfaces/ruby-api/xref-resolution.adoc +156 -0
- data/docs/lychee.toml +42 -0
- data/docs/reference/cli-options.adoc +155 -0
- data/docs/reference/content-block-types.adoc +243 -0
- data/docs/reference/glossary.adoc +119 -0
- data/docs/reference/index.adoc +12 -0
- data/docs/reference/supported-elements.adoc +749 -0
- data/docs/understanding/architecture.adoc +145 -0
- data/docs/understanding/content-pipeline.adoc +102 -0
- data/docs/understanding/data-models.adoc +156 -0
- data/docs/understanding/index.adoc +34 -0
- data/exe/docbook +7 -0
- data/frontend/dist/app.css +1 -0
- data/frontend/dist/app.iife.js +24 -0
- data/frontend/package-lock.json +1445 -0
- data/frontend/package.json +22 -0
- data/frontend/src/App.vue +230 -0
- data/frontend/src/app.ts +8 -0
- data/frontend/src/components/AppSidebar.vue +116 -0
- data/frontend/src/components/AppendixSection.vue +39 -0
- data/frontend/src/components/BlockRenderer.vue +358 -0
- data/frontend/src/components/ChapterSection.vue +32 -0
- data/frontend/src/components/ContentRenderer.vue +13 -0
- data/frontend/src/components/EbookContainer.vue +147 -0
- data/frontend/src/components/EbookTopBar.vue +116 -0
- data/frontend/src/components/PartSection.vue +44 -0
- data/frontend/src/components/ReferenceEntry.vue +80 -0
- data/frontend/src/components/SearchModal.vue +286 -0
- data/frontend/src/components/SectionContent.vue +31 -0
- data/frontend/src/components/SettingsPanel.vue +236 -0
- data/frontend/src/components/TocTreeItem.vue +135 -0
- data/frontend/src/composables/useEbookStore.ts +191 -0
- data/frontend/src/composables/useSearch.ts +249 -0
- data/frontend/src/env.d.ts +7 -0
- data/frontend/src/stores/documentStore.ts +221 -0
- data/frontend/src/stores/uiStore.ts +98 -0
- data/frontend/src/styles.css +253 -0
- data/frontend/tsconfig.json +24 -0
- data/frontend/tsconfig.node.json +11 -0
- data/frontend/vite.config.ts +30 -0
- data/lib/docbook/cli.rb +123 -0
- data/lib/docbook/document.rb +67 -0
- data/lib/docbook/elements/abbrev.rb +16 -0
- data/lib/docbook/elements/acknowledgements.rb +22 -0
- data/lib/docbook/elements/address.rb +18 -0
- data/lib/docbook/elements/alt.rb +16 -0
- data/lib/docbook/elements/annotation.rb +18 -0
- data/lib/docbook/elements/appendix.rb +34 -0
- data/lib/docbook/elements/article.rb +31 -0
- data/lib/docbook/elements/att.rb +15 -0
- data/lib/docbook/elements/attribution.rb +20 -0
- data/lib/docbook/elements/audioobject.rb +18 -0
- data/lib/docbook/elements/author.rb +18 -0
- data/lib/docbook/elements/bibliography.rb +24 -0
- data/lib/docbook/elements/bibliomixed.rb +40 -0
- data/lib/docbook/elements/biblioref.rb +20 -0
- data/lib/docbook/elements/blockquote.rb +26 -0
- data/lib/docbook/elements/book.rb +36 -0
- data/lib/docbook/elements/buildtarget.rb +16 -0
- data/lib/docbook/elements/callout.rb +22 -0
- data/lib/docbook/elements/calloutlist.rb +22 -0
- data/lib/docbook/elements/caution.rb +30 -0
- data/lib/docbook/elements/chapter.rb +62 -0
- data/lib/docbook/elements/citation.rb +16 -0
- data/lib/docbook/elements/citerefentry.rb +26 -0
- data/lib/docbook/elements/citetitle.rb +20 -0
- data/lib/docbook/elements/classname.rb +16 -0
- data/lib/docbook/elements/code.rb +16 -0
- data/lib/docbook/elements/colophon.rb +22 -0
- data/lib/docbook/elements/computeroutput.rb +18 -0
- data/lib/docbook/elements/copyright.rb +17 -0
- data/lib/docbook/elements/danger.rb +28 -0
- data/lib/docbook/elements/date.rb +16 -0
- data/lib/docbook/elements/dedication.rb +22 -0
- data/lib/docbook/elements/dir.rb +16 -0
- data/lib/docbook/elements/emphasis.rb +18 -0
- data/lib/docbook/elements/entry.rb +30 -0
- data/lib/docbook/elements/entrytbl.rb +22 -0
- data/lib/docbook/elements/equation.rb +26 -0
- data/lib/docbook/elements/example.rb +30 -0
- data/lib/docbook/elements/fieldsynopsis.rb +21 -0
- data/lib/docbook/elements/figure.rb +28 -0
- data/lib/docbook/elements/filename.rb +16 -0
- data/lib/docbook/elements/firstname.rb +16 -0
- data/lib/docbook/elements/firstterm.rb +18 -0
- data/lib/docbook/elements/footnote.rb +22 -0
- data/lib/docbook/elements/footnoteref.rb +21 -0
- data/lib/docbook/elements/foreignphrase.rb +18 -0
- data/lib/docbook/elements/formalpara.rb +20 -0
- data/lib/docbook/elements/function.rb +16 -0
- data/lib/docbook/elements/glossary.rb +24 -0
- data/lib/docbook/elements/glossdef.rb +18 -0
- data/lib/docbook/elements/glossentry.rb +26 -0
- data/lib/docbook/elements/glosssee.rb +18 -0
- data/lib/docbook/elements/glossseealso.rb +18 -0
- data/lib/docbook/elements/glossterm.rb +18 -0
- data/lib/docbook/elements/holder.rb +16 -0
- data/lib/docbook/elements/honorific.rb +16 -0
- data/lib/docbook/elements/imagedata.rb +29 -0
- data/lib/docbook/elements/imageobject.rb +22 -0
- data/lib/docbook/elements/important.rb +30 -0
- data/lib/docbook/elements/index.rb +26 -0
- data/lib/docbook/elements/indexdiv.rb +22 -0
- data/lib/docbook/elements/indexentry.rb +22 -0
- data/lib/docbook/elements/indexterm.rb +34 -0
- data/lib/docbook/elements/info.rb +25 -0
- data/lib/docbook/elements/informalexample.rb +24 -0
- data/lib/docbook/elements/informalfigure.rb +20 -0
- data/lib/docbook/elements/inlinemediaobject.rb +22 -0
- data/lib/docbook/elements/itemizedlist.rb +21 -0
- data/lib/docbook/elements/legalnotice.rb +22 -0
- data/lib/docbook/elements/link.rb +26 -0
- data/lib/docbook/elements/listitem.rb +32 -0
- data/lib/docbook/elements/literal.rb +16 -0
- data/lib/docbook/elements/literallayout.rb +22 -0
- data/lib/docbook/elements/mediaobject.rb +26 -0
- data/lib/docbook/elements/msg.rb +20 -0
- data/lib/docbook/elements/msgexplan.rb +22 -0
- data/lib/docbook/elements/msgset.rb +22 -0
- data/lib/docbook/elements/note.rb +30 -0
- data/lib/docbook/elements/orderedlist.rb +23 -0
- data/lib/docbook/elements/orgname.rb +16 -0
- data/lib/docbook/elements/para.rb +61 -0
- data/lib/docbook/elements/paragraph_like.rb +18 -0
- data/lib/docbook/elements/parameter.rb +16 -0
- data/lib/docbook/elements/part.rb +38 -0
- data/lib/docbook/elements/personname.rb +22 -0
- data/lib/docbook/elements/phrase.rb +20 -0
- data/lib/docbook/elements/preface.rb +58 -0
- data/lib/docbook/elements/primary.rb +16 -0
- data/lib/docbook/elements/procedure.rb +24 -0
- data/lib/docbook/elements/productname.rb +18 -0
- data/lib/docbook/elements/productnumber.rb +16 -0
- data/lib/docbook/elements/programlisting.rb +28 -0
- data/lib/docbook/elements/property.rb +16 -0
- data/lib/docbook/elements/pubdate.rb +16 -0
- data/lib/docbook/elements/publishername.rb +16 -0
- data/lib/docbook/elements/quotation.rb +24 -0
- data/lib/docbook/elements/quote.rb +18 -0
- data/lib/docbook/elements/refentry.rb +32 -0
- data/lib/docbook/elements/refentrytitle.rb +16 -0
- data/lib/docbook/elements/reference.rb +26 -0
- data/lib/docbook/elements/refmeta.rb +29 -0
- data/lib/docbook/elements/refmiscinfo.rb +16 -0
- data/lib/docbook/elements/refname.rb +16 -0
- data/lib/docbook/elements/refnamediv.rb +22 -0
- data/lib/docbook/elements/refpurpose.rb +16 -0
- data/lib/docbook/elements/refsect1.rb +22 -0
- data/lib/docbook/elements/refsect2.rb +22 -0
- data/lib/docbook/elements/refsect3.rb +22 -0
- data/lib/docbook/elements/refsection.rb +32 -0
- data/lib/docbook/elements/releaseinfo.rb +16 -0
- data/lib/docbook/elements/remark.rb +20 -0
- data/lib/docbook/elements/replaceable.rb +16 -0
- data/lib/docbook/elements/row.rb +15 -0
- data/lib/docbook/elements/screen.rb +28 -0
- data/lib/docbook/elements/secondary.rb +16 -0
- data/lib/docbook/elements/sect1.rb +26 -0
- data/lib/docbook/elements/sect2.rb +24 -0
- data/lib/docbook/elements/sect3.rb +24 -0
- data/lib/docbook/elements/sect4.rb +24 -0
- data/lib/docbook/elements/sect5.rb +22 -0
- data/lib/docbook/elements/section.rb +66 -0
- data/lib/docbook/elements/see.rb +16 -0
- data/lib/docbook/elements/seealso.rb +18 -0
- data/lib/docbook/elements/set.rb +26 -0
- data/lib/docbook/elements/setindex.rb +24 -0
- data/lib/docbook/elements/sidebar.rb +22 -0
- data/lib/docbook/elements/simplesect.rb +32 -0
- data/lib/docbook/elements/step.rb +26 -0
- data/lib/docbook/elements/substeps.rb +18 -0
- data/lib/docbook/elements/subtitle.rb +16 -0
- data/lib/docbook/elements/surname.rb +16 -0
- data/lib/docbook/elements/table.rb +24 -0
- data/lib/docbook/elements/tag.rb +20 -0
- data/lib/docbook/elements/tbody.rb +15 -0
- data/lib/docbook/elements/term.rb +37 -0
- data/lib/docbook/elements/tertiary.rb +16 -0
- data/lib/docbook/elements/textobject.rb +18 -0
- data/lib/docbook/elements/tfoot.rb +15 -0
- data/lib/docbook/elements/tgroup.rb +21 -0
- data/lib/docbook/elements/thead.rb +15 -0
- data/lib/docbook/elements/tip.rb +30 -0
- data/lib/docbook/elements/title.rb +16 -0
- data/lib/docbook/elements/toc.rb +24 -0
- data/lib/docbook/elements/tocdiv.rb +22 -0
- data/lib/docbook/elements/tocentry.rb +20 -0
- data/lib/docbook/elements/topic.rb +26 -0
- data/lib/docbook/elements/type.rb +16 -0
- data/lib/docbook/elements/uri.rb +16 -0
- data/lib/docbook/elements/userinput.rb +18 -0
- data/lib/docbook/elements/variable.rb +16 -0
- data/lib/docbook/elements/variablelist.rb +19 -0
- data/lib/docbook/elements/varlistentry.rb +17 -0
- data/lib/docbook/elements/version.rb +16 -0
- data/lib/docbook/elements/videoobject.rb +18 -0
- data/lib/docbook/elements/warning.rb +30 -0
- data/lib/docbook/elements/xref.rb +18 -0
- data/lib/docbook/elements/year.rb +16 -0
- data/lib/docbook/elements.rb +204 -0
- data/lib/docbook/models/document_metadata.rb +30 -0
- data/lib/docbook/models/document_root.rb +33 -0
- data/lib/docbook/models/index_entry.rb +28 -0
- data/lib/docbook/models/reading_position.rb +22 -0
- data/lib/docbook/models/section_number.rb +18 -0
- data/lib/docbook/models/section_root.rb +29 -0
- data/lib/docbook/models/toc_node.rb +24 -0
- data/lib/docbook/models.rb +16 -0
- data/lib/docbook/output/data.rb +196 -0
- data/lib/docbook/output/html.rb +1450 -0
- data/lib/docbook/output/index_generator.rb +281 -0
- data/lib/docbook/output.rb +8 -0
- data/lib/docbook/services/document_builder.rb +258 -0
- data/lib/docbook/services/element_to_hash.rb +287 -0
- data/lib/docbook/services/index_generator.rb +134 -0
- data/lib/docbook/services/numbering_service.rb +186 -0
- data/lib/docbook/services/toc_generator.rb +138 -0
- data/lib/docbook/services.rb +14 -0
- data/lib/docbook/templates/document.html.liquid +20 -0
- data/lib/docbook/templates/partials/_head.liquid +39 -0
- data/lib/docbook/templates/partials/_scripts.liquid +10 -0
- data/lib/docbook/version.rb +5 -0
- data/lib/docbook/xinclude_resolver.rb +217 -0
- data/lib/docbook/xref_resolver.rb +78 -0
- data/lib/docbook.rb +17 -0
- data/sig/docbook.rbs +4 -0
- metadata +385 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: Section Numbering
|
|
4
|
+
parent: Features
|
|
5
|
+
nav_order: 3
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Section Numbering
|
|
9
|
+
|
|
10
|
+
The `NumberingBuilder` class computes formatted section numbers for all numbered document divisions. Numbering follows DocBook conventions: Roman numerals for Parts, Arabic numerals for Chapters, hierarchical dot notation for Sections, and Alpha letters for Appendices.
|
|
11
|
+
|
|
12
|
+
## Numbering Schemes
|
|
13
|
+
|
|
14
|
+
[cols="1,2,3"]
|
|
15
|
+
|===
|
|
16
|
+
| Division | Scheme | Examples
|
|
17
|
+
|
|
18
|
+
| Part | Roman numerals (I, II, III, ...)
|
|
19
|
+
| Part I, Part II, Part III
|
|
20
|
+
|
|
21
|
+
| Chapter | Arabic numerals scoped per Part
|
|
22
|
+
| 1, 2, 3 (resets at each Part)
|
|
23
|
+
|
|
24
|
+
| Section | Hierarchical dot notation
|
|
25
|
+
| 1.1, 1.2, 1.2.3, 2.1
|
|
26
|
+
|
|
27
|
+
| Appendix | Alpha letters (A, B, C, ...)
|
|
28
|
+
| Appendix A, Appendix B, Appendix C
|
|
29
|
+
|
|
30
|
+
| Preface | Unnumbered
|
|
31
|
+
| (no number)
|
|
32
|
+
|
|
33
|
+
| Glossary | Unnumbered
|
|
34
|
+
| (no number)
|
|
35
|
+
|
|
36
|
+
| Bibliography | Unnumbered
|
|
37
|
+
| (no number)
|
|
38
|
+
|
|
39
|
+
| Index | Unnumbered
|
|
40
|
+
| (no number)
|
|
41
|
+
|===
|
|
42
|
+
|
|
43
|
+
## NumberingBuilder Class
|
|
44
|
+
|
|
45
|
+
The `NumberingBuilder` maintains independent counters for each numbering scope:
|
|
46
|
+
|
|
47
|
+
[source,ruby]
|
|
48
|
+
----
|
|
49
|
+
class NumberingBuilder
|
|
50
|
+
def initialize
|
|
51
|
+
@numbering = {} # id => formatted_number_string
|
|
52
|
+
@part_counter = 0 # Global part counter
|
|
53
|
+
@chapter_counters = {} # part_index => chapter_counter
|
|
54
|
+
@appendix_counter = 0 # Global appendix counter
|
|
55
|
+
@section_counters = {} # scope_id => sequential_counter
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
----
|
|
59
|
+
|
|
60
|
+
### Part Numbering
|
|
61
|
+
|
|
62
|
+
Parts use uppercase Roman numerals. The counter is global across the entire document:
|
|
63
|
+
|
|
64
|
+
[source,ruby]
|
|
65
|
+
----
|
|
66
|
+
def next_part
|
|
67
|
+
@part_counter += 1
|
|
68
|
+
roman_numeral(@part_counter) # "I", "II", "III", "IV", "V", ...
|
|
69
|
+
end
|
|
70
|
+
----
|
|
71
|
+
|
|
72
|
+
Roman numerals are computed using the standard subtractive notation algorithm with mappings for 1000 (M), 900 (CM), 500 (D), 400 (CD), 100 (C), 90 (XC), 50 (L), 40 (XL), 10 (X), 9 (IX), 5 (V), 4 (IV), 1 (I).
|
|
73
|
+
|
|
74
|
+
### Chapter Numbering
|
|
75
|
+
|
|
76
|
+
Chapters use Arabic numerals scoped per Part. Each Part maintains its own chapter counter, which resets when a new Part begins. Chapters outside of Parts use a global counter:
|
|
77
|
+
|
|
78
|
+
[source,ruby]
|
|
79
|
+
----
|
|
80
|
+
def next_chapter(part_index)
|
|
81
|
+
@chapter_counters[part_index] ||= 0
|
|
82
|
+
@chapter_counters[part_index] += 1
|
|
83
|
+
@chapter_counters[part_index].to_s
|
|
84
|
+
end
|
|
85
|
+
----
|
|
86
|
+
|
|
87
|
+
This means:
|
|
88
|
+
|
|
89
|
+
- Part I contains Chapters 1, 2, 3
|
|
90
|
+
- Part II contains Chapters 1, 2 (reset)
|
|
91
|
+
- Chapters without a Part share counter scope 0
|
|
92
|
+
|
|
93
|
+
### Section Numbering
|
|
94
|
+
|
|
95
|
+
Sections use flat sequential numbering per parent scope, combined into hierarchical dot notation:
|
|
96
|
+
|
|
97
|
+
[source,ruby]
|
|
98
|
+
----
|
|
99
|
+
def next_section(scope_id)
|
|
100
|
+
@section_counters[scope_id] ||= 0
|
|
101
|
+
@section_counters[scope_id] += 1
|
|
102
|
+
@section_counters[scope_id].to_s
|
|
103
|
+
end
|
|
104
|
+
----
|
|
105
|
+
|
|
106
|
+
The hierarchical number is built by prefixing with the parent's number:
|
|
107
|
+
|
|
108
|
+
[source,ruby]
|
|
109
|
+
----
|
|
110
|
+
# For a section in Chapter 1:
|
|
111
|
+
# scope_id = "ch01" -> 1
|
|
112
|
+
# Child scope = "ch01_s1" -> 1.1
|
|
113
|
+
# Grandchild scope = "ch01_s1_ss1" -> 1.1.1
|
|
114
|
+
|
|
115
|
+
prefix = parent_number || chapter_num
|
|
116
|
+
full_num = prefix ? "#{prefix}.#{section_num}" : section_num
|
|
117
|
+
----
|
|
118
|
+
|
|
119
|
+
### Appendix Numbering
|
|
120
|
+
|
|
121
|
+
Appendices use uppercase Alpha letters:
|
|
122
|
+
|
|
123
|
+
[source,ruby]
|
|
124
|
+
----
|
|
125
|
+
def next_appendix
|
|
126
|
+
@appendix_counter += 1
|
|
127
|
+
alpha_numeral(@appendix_counter) # "A", "B", "C", ..., "Z", "AA", "AB"
|
|
128
|
+
end
|
|
129
|
+
----
|
|
130
|
+
|
|
131
|
+
The Alpha algorithm supports double-letter notation for more than 26 appendices (A through Z, then AA, AB, etc.).
|
|
132
|
+
|
|
133
|
+
Appendix sections are prefixed with the appendix letter: `A.1`, `A.2`, `B.1`, etc.
|
|
134
|
+
|
|
135
|
+
## Numbering Map
|
|
136
|
+
|
|
137
|
+
The computed numbering is stored as a flat hash mapping section `xml:id` to formatted number string:
|
|
138
|
+
|
|
139
|
+
[source,ruby]
|
|
140
|
+
----
|
|
141
|
+
numbering = {
|
|
142
|
+
"part1" => "I",
|
|
143
|
+
"ch-intro" => "1",
|
|
144
|
+
"ch-intro-s1" => "1.1",
|
|
145
|
+
"ch-intro-s2" => "1.2",
|
|
146
|
+
"ch-intro-s2a" => "1.2.1",
|
|
147
|
+
"part2" => "II",
|
|
148
|
+
"ch-advanced" => "1", # Reset because new Part
|
|
149
|
+
"ch-advanced-s1" => "1.1",
|
|
150
|
+
"app-changes" => "Appendix A",
|
|
151
|
+
"app-changes-s1" => "A.1",
|
|
152
|
+
"app-license" => "Appendix B",
|
|
153
|
+
}
|
|
154
|
+
----
|
|
155
|
+
|
|
156
|
+
This flat map is serialized as an array of `NumberingEntry` objects in the output JSON, and the Vue frontend converts it back to a `Record<string, string>` for O(1) lookup during rendering.
|
|
157
|
+
|
|
158
|
+
## Unnumbered Sections
|
|
159
|
+
|
|
160
|
+
The following section types are explicitly not numbered:
|
|
161
|
+
|
|
162
|
+
- **Preface** -- Front matter that precedes numbered content
|
|
163
|
+
- **Glossary** -- Alphabetical term list
|
|
164
|
+
- **Bibliography** -- Reference list
|
|
165
|
+
- **Index** -- Generated back-of-book index
|
|
166
|
+
|
|
167
|
+
When the numbering traversal encounters these types, it skips numbering assignment but still recurses into their children (which may contain numbered elements in some document structures).
|
|
168
|
+
|
|
169
|
+
## Integration with TOC
|
|
170
|
+
|
|
171
|
+
The numbering map is computed alongside the TOC tree and stored in the `Toc` model:
|
|
172
|
+
|
|
173
|
+
[source,ruby]
|
|
174
|
+
----
|
|
175
|
+
output = DocbookOutput.new(
|
|
176
|
+
title: extract_title,
|
|
177
|
+
toc: Toc.new(
|
|
178
|
+
sections: sections_data,
|
|
179
|
+
numbering: numbering_hash.map { |id, value| NumberingEntry.new(id: id, value: value) }
|
|
180
|
+
),
|
|
181
|
+
# ...
|
|
182
|
+
)
|
|
183
|
+
----
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: TOC Generation
|
|
4
|
+
parent: Features
|
|
5
|
+
nav_order: 4
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# TOC Generation
|
|
9
|
+
|
|
10
|
+
The table of contents is generated as a recursive tree of `SectionData` objects. Each node carries an ID, title, section type, and children. The Vue SPA renders this tree as a collapsible sidebar with type badges and section numbering.
|
|
11
|
+
|
|
12
|
+
## SectionData Tree
|
|
13
|
+
|
|
14
|
+
Each node in the TOC is a `SectionData` with four fields:
|
|
15
|
+
|
|
16
|
+
[cols="1,1,3"]
|
|
17
|
+
|===
|
|
18
|
+
| Field | Type | Description
|
|
19
|
+
|
|
20
|
+
| `id` | `String` | Section's `xml:id` attribute or a generated slug from the title
|
|
21
|
+
| `title` | `String` | Section title (from `info/title` or direct `title` child)
|
|
22
|
+
| `type` | `String` | Section type used for badge rendering and numbering
|
|
23
|
+
| `children` | `SectionData[]` | Nested child sections
|
|
24
|
+
|===
|
|
25
|
+
|
|
26
|
+
### Type Values
|
|
27
|
+
|
|
28
|
+
The `type` field determines how the section is displayed in the sidebar:
|
|
29
|
+
|
|
30
|
+
[cols="1,3,2"]
|
|
31
|
+
|===
|
|
32
|
+
| Type | Badge | Color
|
|
33
|
+
|
|
34
|
+
| `part` | Pt | Purple
|
|
35
|
+
| `chapter` | Ch | Blue
|
|
36
|
+
| `appendix` | App | Green
|
|
37
|
+
| `section` | (none) | Gray
|
|
38
|
+
| `glossary` | Gl | Yellow
|
|
39
|
+
| `bibliography` | Bib | Red
|
|
40
|
+
| `index` | Idx | Indigo
|
|
41
|
+
| `preface` | Pref | Orange
|
|
42
|
+
| `reference` | Ref | Cyan
|
|
43
|
+
|===
|
|
44
|
+
|
|
45
|
+
## Tree Structure by Document Type
|
|
46
|
+
|
|
47
|
+
The TOC tree varies depending on the root document element.
|
|
48
|
+
|
|
49
|
+
### Book
|
|
50
|
+
|
|
51
|
+
For `<book>` documents, the top-level sections include:
|
|
52
|
+
|
|
53
|
+
- **Preface** -- Direct children of the book
|
|
54
|
+
- **Parts** -- Contain chapters, references, appendices, glossary, bibliography, index
|
|
55
|
+
- **Chapters** -- Direct children of the book (when no Parts are used), contain nested sections
|
|
56
|
+
- **Appendices** -- May contain nested sections
|
|
57
|
+
- **Glossary** -- Leaf node
|
|
58
|
+
- **Bibliography** -- Leaf node
|
|
59
|
+
- **Index** -- Leaf node
|
|
60
|
+
|
|
61
|
+
[source,ruby]
|
|
62
|
+
----
|
|
63
|
+
# Book TOC collection
|
|
64
|
+
def collect_sections
|
|
65
|
+
sections = []
|
|
66
|
+
case @document
|
|
67
|
+
when Docbook::Elements::Book
|
|
68
|
+
each_attr(@document, :preface) { |pf| sections << SectionData.new(type: 'preface', ...) }
|
|
69
|
+
each_attr(@document, :part) { |p| sections << SectionData.new(type: 'part', children: collect_part_children(p)) }
|
|
70
|
+
each_attr(@document, :chapter) { |ch| sections << SectionData.new(type: 'chapter', ...) }
|
|
71
|
+
each_attr(@document, :appendix){ |ap| sections << SectionData.new(type: 'appendix', children: collect_appendix_children(ap)) }
|
|
72
|
+
each_attr(@document, :glossary){ |g| sections << SectionData.new(type: 'glossary', ...) }
|
|
73
|
+
each_attr(@document, :bibliography){ |b| sections << SectionData.new(type: 'bibliography', ...) }
|
|
74
|
+
each_attr(@document, :index) { |i| sections << SectionData.new(type: 'index', ...) }
|
|
75
|
+
end
|
|
76
|
+
sections
|
|
77
|
+
end
|
|
78
|
+
----
|
|
79
|
+
|
|
80
|
+
### Article
|
|
81
|
+
|
|
82
|
+
For `<article>` documents, the TOC contains top-level sections and their recursive children:
|
|
83
|
+
|
|
84
|
+
- **Sections** -- Nested recursively via `collect_section_children`
|
|
85
|
+
|
|
86
|
+
### Reference
|
|
87
|
+
|
|
88
|
+
For `<reference>` documents, the TOC contains reference entries:
|
|
89
|
+
|
|
90
|
+
- **RefEntry** -- Each `refentry` becomes a top-level TOC item with type `reference`
|
|
91
|
+
|
|
92
|
+
## Recursive Collection
|
|
93
|
+
|
|
94
|
+
Section children are collected recursively through dedicated methods:
|
|
95
|
+
|
|
96
|
+
- `collect_part_children` -- Collects chapters, references, appendices, prefaces, glossary, bibliography, and index within a Part
|
|
97
|
+
- `collect_section_children` -- Recursively collects nested `section` elements
|
|
98
|
+
- `collect_appendix_children` -- Collects sections within an Appendix
|
|
99
|
+
|
|
100
|
+
The recursion depth is unbounded, supporting arbitrarily nested section hierarchies.
|
|
101
|
+
|
|
102
|
+
## Vue Sidebar Rendering
|
|
103
|
+
|
|
104
|
+
The Vue frontend renders the TOC tree using the recursive `TocTreeItem` component:
|
|
105
|
+
|
|
106
|
+
[source,typescript]
|
|
107
|
+
----
|
|
108
|
+
// TocTreeItem.vue renders each TOC node:
|
|
109
|
+
// - Collapsible toggle for nodes with children
|
|
110
|
+
// - Type badge (Pt, Ch, App, etc.) based on item.type
|
|
111
|
+
// - Section number from the numbering map
|
|
112
|
+
// - Title text
|
|
113
|
+
// - Active state highlighting
|
|
114
|
+
// - Recursive rendering of children with indentation
|
|
115
|
+
----
|
|
116
|
+
----
|
|
117
|
+
|
|
118
|
+
The sidebar features:
|
|
119
|
+
|
|
120
|
+
- **Collapsible tree** -- Items with children show an expand/collapse toggle
|
|
121
|
+
- **Type badges** -- Color-coded labels indicating section type
|
|
122
|
+
- **Section numbers** -- Right-aligned monospace numbers from the numbering map
|
|
123
|
+
- **Active tracking** -- The currently visible section is highlighted via `IntersectionObserver`
|
|
124
|
+
- **Deep linking** -- Clicking a TOC item scrolls to the section via anchor link
|
|
125
|
+
|
|
126
|
+
## Title Resolution
|
|
127
|
+
|
|
128
|
+
Section titles are resolved using a priority chain:
|
|
129
|
+
|
|
130
|
+
. `element.info.title.content` -- Title inside an `info` element
|
|
131
|
+
. `element.title.content` -- Direct title child
|
|
132
|
+
. Fallback string (e.g., "Preface", "Chapter", "Glossary")
|
|
133
|
+
|
|
134
|
+
[source,ruby]
|
|
135
|
+
----
|
|
136
|
+
def best_title(el)
|
|
137
|
+
title_of(el)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def title_of(el)
|
|
141
|
+
case el
|
|
142
|
+
when Elements::Book, Elements::Article
|
|
143
|
+
el.info&.title&.content
|
|
144
|
+
when Elements::Chapter, Elements::Appendix, Elements::Section,
|
|
145
|
+
Elements::Part, Elements::Preface
|
|
146
|
+
el.info&.title&.content || el.title&.content
|
|
147
|
+
# ...
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
----
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: fragid Schemes
|
|
4
|
+
parent: XInclude Resolution
|
|
5
|
+
grand_parent: Features
|
|
6
|
+
nav_order: 3
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# fragid Schemes
|
|
10
|
+
|
|
11
|
+
The `fragid` attribute on text-mode XIncludes enables selective extraction of file content. Three filtering schemes are supported: `search=` for pattern-based extraction, `line=` for line-number ranges, and `char=` for character offsets. This is a key feature for including specific portions of source files in documentation.
|
|
12
|
+
|
|
13
|
+
## Overview
|
|
14
|
+
|
|
15
|
+
The `fragid` attribute is applied to `xi:include` elements with `parse="text"`:
|
|
16
|
+
|
|
17
|
+
[source,xml]
|
|
18
|
+
----
|
|
19
|
+
<programlisting language="ruby">
|
|
20
|
+
<xi:include href="lib/example.rb" parse="text" fragid="line=10,25"/>
|
|
21
|
+
</programlisting>
|
|
22
|
+
----
|
|
23
|
+
|
|
24
|
+
The resolver dispatches to the appropriate filter based on the scheme prefix:
|
|
25
|
+
|
|
26
|
+
[source,ruby]
|
|
27
|
+
----
|
|
28
|
+
def self.apply_fragid(content, fragid)
|
|
29
|
+
if fragid.start_with?("search=")
|
|
30
|
+
apply_search_fragid(content, fragid[7..])
|
|
31
|
+
elsif fragid.start_with?("line=")
|
|
32
|
+
apply_line_fragid(content, fragid[5..])
|
|
33
|
+
elsif fragid.start_with?("char=")
|
|
34
|
+
apply_char_fragid(content, fragid[5..])
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
----
|
|
38
|
+
|
|
39
|
+
## search= Scheme (DocBook xslTNG Extension)
|
|
40
|
+
|
|
41
|
+
The `search=` scheme is an extension inspired by the https://github.com/docbook/xslTNG[DocBook xslTNG stylesheets]. It extracts content by matching patterns, with support for literal and regex matching, directional modifiers, and stop patterns.
|
|
42
|
+
|
|
43
|
+
### Syntax
|
|
44
|
+
|
|
45
|
+
----
|
|
46
|
+
search=DELIMITER pattern DELIMITER [;after|;before] [,DELIMITER stop_pattern DELIMITER]
|
|
47
|
+
----
|
|
48
|
+
|
|
49
|
+
Where `DELIMITER` is `#` (literal match) or `/` (regex match).
|
|
50
|
+
|
|
51
|
+
### Literal Match with `#`
|
|
52
|
+
|
|
53
|
+
The `#` delimiter performs literal string matching:
|
|
54
|
+
|
|
55
|
+
[source,xml]
|
|
56
|
+
----
|
|
57
|
+
<!-- Include lines containing "xmlns:css" up to lines containing "a border" -->
|
|
58
|
+
<programlisting language="xml">
|
|
59
|
+
<xi:include href="stylesheets/main.xsl" parse="text"
|
|
60
|
+
fragid="search=#xmlns:css#,#a border#"/>
|
|
61
|
+
</programlisting>
|
|
62
|
+
----
|
|
63
|
+
|
|
64
|
+
This extracts all lines from the first line containing `xmlns:css` up to (but not including) the first line containing `a border`.
|
|
65
|
+
|
|
66
|
+
### Regex Match with `/`
|
|
67
|
+
|
|
68
|
+
The `/` delimiter performs regular expression matching:
|
|
69
|
+
|
|
70
|
+
[source,xml]
|
|
71
|
+
----
|
|
72
|
+
<!-- Include lines matching a regex pattern -->
|
|
73
|
+
<programlisting language="ruby">
|
|
74
|
+
<xi:include href="lib/docbook.rb" parse="text"
|
|
75
|
+
fragid="search=/def process_image/"/>
|
|
76
|
+
</programlisting>
|
|
77
|
+
----
|
|
78
|
+
|
|
79
|
+
This extracts the first line matching the regex `def process_image`.
|
|
80
|
+
|
|
81
|
+
### Modifiers
|
|
82
|
+
|
|
83
|
+
Two modifiers control which lines around the match are included:
|
|
84
|
+
|
|
85
|
+
**`;after`** -- Extract lines *after* the match (excludes the matching line itself):
|
|
86
|
+
|
|
87
|
+
[source,xml]
|
|
88
|
+
----
|
|
89
|
+
<!-- Include lines after the "class Html" match, up to the "end" match -->
|
|
90
|
+
<programlisting language="ruby">
|
|
91
|
+
<xi:include href="lib/docbook/output/html.rb" parse="text"
|
|
92
|
+
fragid="search=#class Html#;after,#private#"/>
|
|
93
|
+
</programlisting>
|
|
94
|
+
----
|
|
95
|
+
|
|
96
|
+
**`;before`** -- Extract lines *before* the match (excludes the matching line itself):
|
|
97
|
+
|
|
98
|
+
[source,xml]
|
|
99
|
+
----
|
|
100
|
+
<!-- Include all lines before the "Copyright" notice -->
|
|
101
|
+
<programlisting language="ruby">
|
|
102
|
+
<xi:include href="lib/docbook.rb" parse="text"
|
|
103
|
+
fragid="search=#Copyright#;before"/>
|
|
104
|
+
</programlisting>
|
|
105
|
+
----
|
|
106
|
+
|
|
107
|
+
### Stop Patterns
|
|
108
|
+
|
|
109
|
+
A stop pattern terminates extraction at the first matching line (exclusive -- the stop line is not included):
|
|
110
|
+
|
|
111
|
+
[source,xml]
|
|
112
|
+
----
|
|
113
|
+
<!-- Extract from "class Html" to "def initialize" -->
|
|
114
|
+
<programlisting language="ruby">
|
|
115
|
+
<xi:include href="lib/docbook/output/html.rb" parse="text"
|
|
116
|
+
fragid="search=#class Html#,#def initialize#"/>
|
|
117
|
+
</programlisting>
|
|
118
|
+
----
|
|
119
|
+
|
|
120
|
+
[source,xml]
|
|
121
|
+
----
|
|
122
|
+
<!-- Extract from regex match to regex stop -->
|
|
123
|
+
<programlisting language="ruby">
|
|
124
|
+
<xi:include href="lib/docbook/output/html.rb" parse="text"
|
|
125
|
+
fragid="search=/def to_html/,/end$/"/>
|
|
126
|
+
</programlisting>
|
|
127
|
+
----
|
|
128
|
+
|
|
129
|
+
### Complete search= Examples
|
|
130
|
+
|
|
131
|
+
[source,xml]
|
|
132
|
+
----
|
|
133
|
+
<!-- Example 1: Single line literal match -->
|
|
134
|
+
<programlisting>
|
|
135
|
+
<xi:include href="config.yaml" parse="text"
|
|
136
|
+
fragid="search=#database:#"/>
|
|
137
|
+
</programlisting>
|
|
138
|
+
|
|
139
|
+
<!-- Example 2: Range from literal start to literal stop -->
|
|
140
|
+
<programlisting language="ruby">
|
|
141
|
+
<xi:include href="lib/parser.rb" parse="text"
|
|
142
|
+
fragid="search=#class Parser#,#class SyntaxError#"/>
|
|
143
|
+
</programlisting>
|
|
144
|
+
|
|
145
|
+
<!-- Example 3: Lines after a match, with stop -->
|
|
146
|
+
<programlisting language="xml">
|
|
147
|
+
<xi:include href="schema.xsd" parse="text"
|
|
148
|
+
fragid="search=#complexType name="Book"#;after,#</complexType>#"/>
|
|
149
|
+
</programlisting>
|
|
150
|
+
|
|
151
|
+
<!-- Example 4: Regex range -->
|
|
152
|
+
<programlisting language="ruby">
|
|
153
|
+
<xi:include href="spec/example_spec.rb" parse="text"
|
|
154
|
+
fragid="search=/describe.*Html/,/end$/"/>
|
|
155
|
+
</programlisting>
|
|
156
|
+
|
|
157
|
+
<!-- Example 5: Lines before a marker -->
|
|
158
|
+
<programlisting language="yaml">
|
|
159
|
+
<xi:include href="docker-compose.yml" parse="text"
|
|
160
|
+
fragid="search=#volumes:#;before"/>
|
|
161
|
+
</programlisting>
|
|
162
|
+
----
|
|
163
|
+
|
|
164
|
+
## line= Scheme (RFC 5147)
|
|
165
|
+
|
|
166
|
+
The `line=` scheme implements the https://www.rfc-editor.org/rfc/rfc5147[RFC 5147] line-based text range fragment identifier. It extracts a range of lines by 1-based line number.
|
|
167
|
+
|
|
168
|
+
### Syntax
|
|
169
|
+
|
|
170
|
+
----
|
|
171
|
+
line=START,END[;length=N]
|
|
172
|
+
----
|
|
173
|
+
|
|
174
|
+
- `START` -- First line to include (1-based)
|
|
175
|
+
- `END` -- Last line to include (inclusive)
|
|
176
|
+
- `length=N` -- Optional maximum character length for the extracted content
|
|
177
|
+
|
|
178
|
+
### Basic Line Range
|
|
179
|
+
|
|
180
|
+
[source,xml]
|
|
181
|
+
----
|
|
182
|
+
<!-- Include lines 10 through 25 of the file -->
|
|
183
|
+
<programlisting language="ruby">
|
|
184
|
+
<xi:include href="lib/docbook.rb" parse="text" fragid="line=10,25"/>
|
|
185
|
+
</programlisting>
|
|
186
|
+
----
|
|
187
|
+
|
|
188
|
+
This extracts lines 10, 11, 12, ..., 25 from the file (1-based indexing).
|
|
189
|
+
|
|
190
|
+
### Line Range with Length Truncation
|
|
191
|
+
|
|
192
|
+
[source,xml]
|
|
193
|
+
----
|
|
194
|
+
<!-- Include lines 3 through 50, truncated to 889 characters -->
|
|
195
|
+
<programlisting language="ruby">
|
|
196
|
+
<xi:include href="lib/docbook/output/html.rb" parse="text"
|
|
197
|
+
fragid="line=3,50;length=889"/>
|
|
198
|
+
</programlisting>
|
|
199
|
+
----
|
|
200
|
+
|
|
201
|
+
The `length=` modifier truncates the output to at most N characters. This is useful for:
|
|
202
|
+
|
|
203
|
+
- Showing a representative sample of a long file
|
|
204
|
+
- Limiting output size in generated documentation
|
|
205
|
+
- Ensuring consistent example lengths
|
|
206
|
+
|
|
207
|
+
### Examples
|
|
208
|
+
|
|
209
|
+
[source,xml]
|
|
210
|
+
----
|
|
211
|
+
<!-- First 5 lines of a file -->
|
|
212
|
+
<programlisting>
|
|
213
|
+
<xi:include href="README.md" parse="text" fragid="line=1,5"/>
|
|
214
|
+
</programlisting>
|
|
215
|
+
|
|
216
|
+
<!-- Lines 100-120 (a specific function) -->
|
|
217
|
+
<programlisting language="python">
|
|
218
|
+
<xi:include href="app.py" parse="text" fragid="line=100,120"/>
|
|
219
|
+
</programlisting>
|
|
220
|
+
|
|
221
|
+
<!-- A specific range, max 500 characters -->
|
|
222
|
+
<programlisting language="javascript">
|
|
223
|
+
<xi:include href="index.js" parse="text" fragid="line=1,100;length=500"/>
|
|
224
|
+
</programlisting>
|
|
225
|
+
----
|
|
226
|
+
|
|
227
|
+
## char= Scheme (RFC 5147)
|
|
228
|
+
|
|
229
|
+
The `char=` scheme implements the https://www.rfc-editor.org/rfc/rfc5147[RFC 5147] character-based text range fragment identifier. It extracts content by character offset.
|
|
230
|
+
|
|
231
|
+
### Syntax
|
|
232
|
+
|
|
233
|
+
----
|
|
234
|
+
char=START,END
|
|
235
|
+
----
|
|
236
|
+
|
|
237
|
+
- `START` -- Starting character offset (0-based)
|
|
238
|
+
- `END` -- Ending character offset (exclusive)
|
|
239
|
+
|
|
240
|
+
### Usage
|
|
241
|
+
|
|
242
|
+
[source,xml]
|
|
243
|
+
----
|
|
244
|
+
<!-- Extract characters 0 through 500 (first 500 characters) -->
|
|
245
|
+
<programlisting>
|
|
246
|
+
<xi:include href="data/output.txt" parse="text" fragid="char=0,500"/>
|
|
247
|
+
</programlisting>
|
|
248
|
+
----
|
|
249
|
+
|
|
250
|
+
[source,xml]
|
|
251
|
+
----
|
|
252
|
+
<!-- Extract a specific section by character offset -->
|
|
253
|
+
<programlisting>
|
|
254
|
+
<xi:include href="large-file.txt" parse="text" fragid="char=1024,2048"/>
|
|
255
|
+
</programlisting>
|
|
256
|
+
----
|
|
257
|
+
|
|
258
|
+
### Examples
|
|
259
|
+
|
|
260
|
+
[source,xml]
|
|
261
|
+
----
|
|
262
|
+
<!-- First 1000 characters -->
|
|
263
|
+
<programlisting>
|
|
264
|
+
<xi:include href="CHANGELOG.md" parse="text" fragid="char=0,1000"/>
|
|
265
|
+
</programlisting>
|
|
266
|
+
|
|
267
|
+
<!-- Characters from offset 500 to 1000 -->
|
|
268
|
+
<programlisting language="json">
|
|
269
|
+
<xi:include href="package.json" parse="text" fragid="char=500,1000"/>
|
|
270
|
+
</programlisting>
|
|
271
|
+
----
|
|
272
|
+
|
|
273
|
+
## Scheme Comparison
|
|
274
|
+
|
|
275
|
+
[cols="1,1,3,3"]
|
|
276
|
+
|===
|
|
277
|
+
| Scheme | Standard | Best For | Example
|
|
278
|
+
|
|
279
|
+
| `search=` | DocBook xslTNG | Pattern-based extraction, code with markers
|
|
280
|
+
| `search=#def process#;after,#end$#`
|
|
281
|
+
|
|
282
|
+
| `line=` | RFC 5147 | Known line ranges, fixed-position content
|
|
283
|
+
| `line=10,25;length=889`
|
|
284
|
+
|
|
285
|
+
| `char=` | RFC 5147 | Known byte offsets, binary-adjacent content
|
|
286
|
+
| `char=0,500`
|
|
287
|
+
|===
|