coradoc-mirror 0.1.7 → 0.1.9
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 +4 -4
- data/lib/coradoc/mirror/core_model_to_mirror.rb +17 -0
- data/lib/coradoc/mirror/handlers/inline.rb +49 -1
- data/lib/coradoc/mirror/mirror_to_core_model.rb +21 -1
- data/lib/coradoc/mirror/node.rb +7 -0
- data/lib/coradoc/mirror/reverse_builder/abstract.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/admonition.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/biblio_entry.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/bibliography.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/blockquote.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/bullet_list.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/caption.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/code_block.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/definition_list.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/document.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/example.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/figure.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/footnote_entry.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/footnote_marker.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/footnotes.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/frontmatter.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/generic_block.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/hard_break.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/header.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/horizontal_rule.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/image.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/include.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/inline_text.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/list_item.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/literal_block.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/open_block.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/ordered_list.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/paragraph.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/partintro.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/pass_block.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/preamble.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/raw_inline.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/section.rb +0 -6
- data/lib/coradoc/mirror/reverse_builder/sections.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/sidebar.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/soft_break.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/stem_block.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/table.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/table_body.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/table_cell.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/table_head.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/table_row.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/text.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/toc.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/toc_entry.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder/verse.rb +0 -4
- data/lib/coradoc/mirror/reverse_builder.rb +122 -75
- data/lib/coradoc/mirror/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0d0dddcebf58649933def45133c70a5ed3caf8f1441d1a789b9ea95d49fa8d24
|
|
4
|
+
data.tar.gz: 6275f1315af45cb267762a26862bdcb1d2bb65e29fd4657cd411a98200b6c0f9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 13c8d02108886f582cf93f501a9e503361c450c224f0105e255583200323bd4d170d3303b2250510922ee4dad0aa92b45a987caf690528098f77d90b18e2672a
|
|
7
|
+
data.tar.gz: 3eef4445a831dfce49faaa0e777ad4cd28fb54d9d7b71db640e45585ea557267f87b831031839da32077bf6886ae53f9d085e65200ac6aa2cc939c393f93df1d
|
|
@@ -164,9 +164,26 @@ module Coradoc
|
|
|
164
164
|
value, concat = result
|
|
165
165
|
return unless value
|
|
166
166
|
|
|
167
|
+
propagate_source_line(value, element)
|
|
167
168
|
concat ? content.concat(Array(value)) : content << value
|
|
168
169
|
end
|
|
169
170
|
|
|
171
|
+
# Copy parser-attached source_line from the CoreModel element onto
|
|
172
|
+
# every Mirror node the handler produced. Single touchpoint for the
|
|
173
|
+
# entire CoreModel → Mirror direction — handlers stay focused on
|
|
174
|
+
# per-type mapping and don't repeat this concern (DRY).
|
|
175
|
+
def propagate_source_line(value, element)
|
|
176
|
+
line = element.source_line
|
|
177
|
+
return unless line
|
|
178
|
+
|
|
179
|
+
Array(value).each do |node|
|
|
180
|
+
next unless node.is_a?(Node)
|
|
181
|
+
next if node.source_line
|
|
182
|
+
|
|
183
|
+
node.source_line = line
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
170
187
|
def build_document_attrs(document)
|
|
171
188
|
attrs = {}
|
|
172
189
|
attrs[:title] = document.title if document.title
|
|
@@ -123,12 +123,56 @@ module Coradoc
|
|
|
123
123
|
end
|
|
124
124
|
|
|
125
125
|
def build_simple_mark(element, context, mark_class)
|
|
126
|
+
# When the mark carries parsed children (Bug 16B — nested
|
|
127
|
+
# inline marks like `**bold \`code\`**`), walk the children
|
|
128
|
+
# and apply this mark to each text leaf. Inner mark elements
|
|
129
|
+
# get the outer mark added to their marks list, producing
|
|
130
|
+
# ProseMirror's flat text-node-with-marks shape.
|
|
131
|
+
if element.children&.any?
|
|
132
|
+
return flatten_marked_children(element.children, [mark_class.new], context)
|
|
133
|
+
end
|
|
134
|
+
|
|
126
135
|
text = extract_inline_text(element)
|
|
127
136
|
return nil if text.empty?
|
|
128
137
|
|
|
129
138
|
context.text_node(text, marks: [mark_class.new])
|
|
130
139
|
end
|
|
131
140
|
|
|
141
|
+
# Walk a list of children, attaching the active set of marks
|
|
142
|
+
# to each text leaf. When a child is itself an InlineElement
|
|
143
|
+
# with its own mark (e.g. CodeElement inside BoldElement),
|
|
144
|
+
# the inner mark is added to the active set and recursion
|
|
145
|
+
# proceeds into its children. Produces the conjunction of
|
|
146
|
+
# marks for each text run that ProseMirror expects:
|
|
147
|
+
# **a `b` c** → text("a ", [bold]), text("b", [bold, code]), text(" c", [bold])
|
|
148
|
+
def flatten_marked_children(children, active_marks, context)
|
|
149
|
+
result = []
|
|
150
|
+
children.each do |child|
|
|
151
|
+
case child
|
|
152
|
+
when CoreModel::TextContent
|
|
153
|
+
next if child.text.nil? || child.text.empty?
|
|
154
|
+
|
|
155
|
+
result << context.text_node(child.text, marks: active_marks.dup)
|
|
156
|
+
when CoreModel::InlineElement
|
|
157
|
+
inner_mark_class = SIMPLE_MARK_TYPES[child.class]
|
|
158
|
+
if inner_mark_class && child.children&.any?
|
|
159
|
+
combined = active_marks + [inner_mark_class.new]
|
|
160
|
+
result.concat(flatten_marked_children(child.children, combined, context))
|
|
161
|
+
elsif inner_mark_class
|
|
162
|
+
text = extract_inline_text(child)
|
|
163
|
+
next if text.empty?
|
|
164
|
+
|
|
165
|
+
combined = active_marks + [inner_mark_class.new]
|
|
166
|
+
result << context.text_node(text, marks: combined)
|
|
167
|
+
else
|
|
168
|
+
nested = dispatch_inline(child, context)
|
|
169
|
+
result << nested if nested
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
result
|
|
174
|
+
end
|
|
175
|
+
|
|
132
176
|
def build_link_mark(element, context)
|
|
133
177
|
text = extract_inline_text(element)
|
|
134
178
|
text = element.target.to_s if text.empty? && element.target
|
|
@@ -208,7 +252,11 @@ module Coradoc
|
|
|
208
252
|
def extract_inline_text(element)
|
|
209
253
|
return element.content.to_s if element.content && !element.content.to_s.empty?
|
|
210
254
|
|
|
211
|
-
|
|
255
|
+
if element.is_a?(CoreModel::InlineElement) && element.nested_elements
|
|
256
|
+
return element.nested_elements.map do |nested|
|
|
257
|
+
extract_inline_text(nested)
|
|
258
|
+
end.join
|
|
259
|
+
end
|
|
212
260
|
|
|
213
261
|
if (element.is_a?(CoreModel::InlineElement) || element.is_a?(CoreModel::Block)) && element.children && !element.children.empty?
|
|
214
262
|
return element.children.map do |child|
|
|
@@ -19,7 +19,27 @@ module Coradoc
|
|
|
19
19
|
builder_class = ReverseBuilder.lookup(node.type)
|
|
20
20
|
raise Error, "Unknown mirror node type: #{node.type}" unless builder_class
|
|
21
21
|
|
|
22
|
-
builder_class.new(self).build(node)
|
|
22
|
+
result = builder_class.new(self).build(node)
|
|
23
|
+
propagate_source_line(result, node)
|
|
24
|
+
result
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Copy parser-attached source_line from the Mirror node onto every
|
|
28
|
+
# CoreModel node the builder produced. Single touchpoint for the
|
|
29
|
+
# entire Mirror → CoreModel direction — builders stay focused on
|
|
30
|
+
# per-type mapping and don't repeat this concern (DRY).
|
|
31
|
+
def propagate_source_line(result, node)
|
|
32
|
+
return unless node.is_a?(Node)
|
|
33
|
+
|
|
34
|
+
line = node.source_line
|
|
35
|
+
return unless line
|
|
36
|
+
|
|
37
|
+
Array(result).each do |built|
|
|
38
|
+
next unless built.is_a?(CoreModel::Base)
|
|
39
|
+
next if built.source_line
|
|
40
|
+
|
|
41
|
+
built.source_line = line
|
|
42
|
+
end
|
|
23
43
|
end
|
|
24
44
|
|
|
25
45
|
# ── Shared helpers (single source of truth — used by every
|
data/lib/coradoc/mirror/node.rb
CHANGED
|
@@ -29,6 +29,13 @@ module Coradoc
|
|
|
29
29
|
attribute :type, :string, default: -> { self.class::PM_TYPE }
|
|
30
30
|
attribute :content, Node, collection: true
|
|
31
31
|
attribute :marks, Mark, collection: true
|
|
32
|
+
# Parser metadata, single source of truth on the wire side too.
|
|
33
|
+
# Carried in memory so CoreModel → Mirror → CoreModel round-trips
|
|
34
|
+
# preserve the source location. Intentionally absent from every
|
|
35
|
+
# subclass `key_value` block: source_line is not part of the
|
|
36
|
+
# ProseMirror JSON contract — editor consumers don't need it, and
|
|
37
|
+
# serializing it would leak parser internals onto the wire.
|
|
38
|
+
attribute :source_line, :integer
|
|
32
39
|
|
|
33
40
|
key_value do
|
|
34
41
|
map 'type', to: :type, render_default: true
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'base'
|
|
4
|
-
|
|
5
3
|
module Coradoc
|
|
6
4
|
module Mirror
|
|
7
5
|
module ReverseBuilder
|
|
8
6
|
class Admonition < Base
|
|
9
|
-
registers 'admonition'
|
|
10
|
-
|
|
11
7
|
def build(node)
|
|
12
8
|
CoreModel::AnnotationBlock.new(
|
|
13
9
|
annotation_type: node.attrs&.admonition_type,
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'base'
|
|
4
|
-
|
|
5
3
|
module Coradoc
|
|
6
4
|
module Mirror
|
|
7
5
|
module ReverseBuilder
|
|
8
6
|
class Bibliography < Base
|
|
9
|
-
registers 'bibliography'
|
|
10
|
-
|
|
11
7
|
def build(node)
|
|
12
8
|
entries = build_content(node).select { |c| c.is_a?(CoreModel::BibliographyEntry) }
|
|
13
9
|
CoreModel::Bibliography.new(title: node.attrs&.title, entries: entries)
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'base'
|
|
4
|
-
|
|
5
3
|
module Coradoc
|
|
6
4
|
module Mirror
|
|
7
5
|
module ReverseBuilder
|
|
8
6
|
class Blockquote < Base
|
|
9
|
-
registers 'quote'
|
|
10
|
-
|
|
11
7
|
def build(node)
|
|
12
8
|
CoreModel::QuoteBlock.new(
|
|
13
9
|
attribution: node.attrs&.attribution,
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'base'
|
|
4
|
-
|
|
5
3
|
module Coradoc
|
|
6
4
|
module Mirror
|
|
7
5
|
module ReverseBuilder
|
|
8
6
|
class BulletList < Base
|
|
9
|
-
registers 'bullet_list'
|
|
10
|
-
|
|
11
7
|
def build(node)
|
|
12
8
|
items = build_content(node).select { |c| c.is_a?(CoreModel::ListItem) }
|
|
13
9
|
CoreModel::ListBlock.new(marker_type: 'unordered', items: items)
|
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'base'
|
|
4
|
-
|
|
5
3
|
module Coradoc
|
|
6
4
|
module Mirror
|
|
7
5
|
module ReverseBuilder
|
|
8
6
|
# Caption only appears as a Figure child. If encountered standalone,
|
|
9
7
|
# extract its text as an inline element so it isn't lost.
|
|
10
8
|
class Caption < Base
|
|
11
|
-
registers 'caption'
|
|
12
|
-
|
|
13
9
|
def build(node)
|
|
14
10
|
CoreModel::InlineElement.new(content: extract_text(node))
|
|
15
11
|
end
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'base'
|
|
4
|
-
|
|
5
3
|
module Coradoc
|
|
6
4
|
module Mirror
|
|
7
5
|
module ReverseBuilder
|
|
@@ -9,8 +7,6 @@ module Coradoc
|
|
|
9
7
|
# caption. Reverse: collapse back to a single CoreModel::Image,
|
|
10
8
|
# promoting the caption child to `caption:` if present.
|
|
11
9
|
class Figure < Base
|
|
12
|
-
registers 'figure'
|
|
13
|
-
|
|
14
10
|
def build(node)
|
|
15
11
|
image_child = node.content&.find { |c| c.is_a?(Node) && c.type == 'image' }
|
|
16
12
|
return nil unless image_child
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'base'
|
|
4
|
-
|
|
5
3
|
module Coradoc
|
|
6
4
|
module Mirror
|
|
7
5
|
module ReverseBuilder
|
|
8
6
|
class FootnoteEntry < Base
|
|
9
|
-
registers 'footnote_entry'
|
|
10
|
-
|
|
11
7
|
def build(node)
|
|
12
8
|
attrs = node.attrs
|
|
13
9
|
CoreModel::Footnote.new(id: attrs&.id, content: extract_text(node))
|
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'base'
|
|
4
|
-
|
|
5
3
|
module Coradoc
|
|
6
4
|
module Mirror
|
|
7
5
|
module ReverseBuilder
|
|
8
6
|
# Inline footnote marker (JS `footnote_marker`). The CoreModel
|
|
9
7
|
# FootnoteReference holds the same id/ref/number triple.
|
|
10
8
|
class FootnoteMarker < Base
|
|
11
|
-
registers 'footnote_marker'
|
|
12
|
-
|
|
13
9
|
def build(node)
|
|
14
10
|
attrs = node.attrs
|
|
15
11
|
CoreModel::FootnoteReference.new(
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'base'
|
|
4
|
-
|
|
5
3
|
module Coradoc
|
|
6
4
|
module Mirror
|
|
7
5
|
module ReverseBuilder
|
|
@@ -9,8 +7,6 @@ module Coradoc
|
|
|
9
7
|
# no CoreModel equivalent (each entry is built separately). Returns
|
|
10
8
|
# nil so build_content filters it out.
|
|
11
9
|
class Footnotes < Base
|
|
12
|
-
registers 'footnotes'
|
|
13
|
-
|
|
14
10
|
def build(_node)
|
|
15
11
|
nil
|
|
16
12
|
end
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'base'
|
|
4
|
-
|
|
5
3
|
module Coradoc
|
|
6
4
|
module Mirror
|
|
7
5
|
module ReverseBuilder
|
|
@@ -9,8 +7,6 @@ module Coradoc
|
|
|
9
7
|
# direction (`Node::GenericBlock`). Preserves the semantic_type so
|
|
10
8
|
# downstream consumers can dispatch on it.
|
|
11
9
|
class GenericBlock < Base
|
|
12
|
-
registers 'generic_block'
|
|
13
|
-
|
|
14
10
|
def build(node)
|
|
15
11
|
attrs = node.attrs
|
|
16
12
|
CoreModel::Block.new(
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'base'
|
|
4
|
-
|
|
5
3
|
module Coradoc
|
|
6
4
|
module Mirror
|
|
7
5
|
module ReverseBuilder
|
|
8
6
|
class Header < Base
|
|
9
|
-
registers 'floating_title', 'heading'
|
|
10
|
-
|
|
11
7
|
def build(node)
|
|
12
8
|
attrs = node.attrs
|
|
13
9
|
CoreModel::HeaderElement.new(
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'base'
|
|
4
|
-
|
|
5
3
|
module Coradoc
|
|
6
4
|
module Mirror
|
|
7
5
|
module ReverseBuilder
|
|
8
6
|
class HorizontalRule < Base
|
|
9
|
-
registers 'horizontal_rule', 'thematic_break'
|
|
10
|
-
|
|
11
7
|
def build(_node)
|
|
12
8
|
CoreModel::HorizontalRuleBlock.new
|
|
13
9
|
end
|
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'base'
|
|
4
|
-
|
|
5
3
|
module Coradoc
|
|
6
4
|
module Mirror
|
|
7
5
|
module ReverseBuilder
|
|
8
6
|
# Include directive: round-trips back to a CoreModel::Include link
|
|
9
7
|
# node. The text graph is preserved through mirror_json → core.
|
|
10
8
|
class Include < Base
|
|
11
|
-
registers 'include'
|
|
12
|
-
|
|
13
9
|
def build(node)
|
|
14
10
|
attrs = node.attrs
|
|
15
11
|
CoreModel::Include.new(
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'base'
|
|
4
|
-
|
|
5
3
|
module Coradoc
|
|
6
4
|
module Mirror
|
|
7
5
|
module ReverseBuilder
|
|
8
6
|
class InlineText < Base
|
|
9
|
-
registers 'dt', 'dd'
|
|
10
|
-
|
|
11
7
|
def build(node)
|
|
12
8
|
children = build_inline_children(node)
|
|
13
9
|
text = children.map { |c| c.is_a?(CoreModel::TextContent) ? c.text : '' }.join
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'base'
|
|
4
|
-
|
|
5
3
|
module Coradoc
|
|
6
4
|
module Mirror
|
|
7
5
|
module ReverseBuilder
|
|
8
6
|
class OrderedList < Base
|
|
9
|
-
registers 'ordered_list'
|
|
10
|
-
|
|
11
7
|
def build(node)
|
|
12
8
|
items = build_content(node).select { |c| c.is_a?(CoreModel::ListItem) }
|
|
13
9
|
CoreModel::ListBlock.new(marker_type: 'ordered', items: items)
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'base'
|
|
4
|
-
|
|
5
3
|
module Coradoc
|
|
6
4
|
module Mirror
|
|
7
5
|
module ReverseBuilder
|
|
8
6
|
class Paragraph < Base
|
|
9
|
-
registers 'paragraph'
|
|
10
|
-
|
|
11
7
|
def build(node)
|
|
12
8
|
CoreModel::ParagraphBlock.new(children: build_inline_children(node))
|
|
13
9
|
end
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'base'
|
|
4
|
-
|
|
5
3
|
module Coradoc
|
|
6
4
|
module Mirror
|
|
7
5
|
module ReverseBuilder
|
|
8
6
|
class Partintro < Base
|
|
9
|
-
registers 'partintro_block'
|
|
10
|
-
|
|
11
7
|
def build(node)
|
|
12
8
|
CoreModel::PartintroBlock.new(
|
|
13
9
|
title: node.attrs&.title,
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'base'
|
|
4
|
-
|
|
5
3
|
module Coradoc
|
|
6
4
|
module Mirror
|
|
7
5
|
module ReverseBuilder
|
|
8
6
|
class Preamble < Base
|
|
9
|
-
registers 'preface'
|
|
10
|
-
|
|
11
7
|
def build(node)
|
|
12
8
|
CoreModel::PreambleElement.new(children: build_content(node))
|
|
13
9
|
end
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'base'
|
|
4
|
-
|
|
5
3
|
module Coradoc
|
|
6
4
|
module Mirror
|
|
7
5
|
module ReverseBuilder
|
|
8
6
|
class RawInline < Base
|
|
9
|
-
registers 'raw_inline'
|
|
10
|
-
|
|
11
7
|
def build(node)
|
|
12
8
|
CoreModel::RawInlineElement.new(content: node.text.to_s)
|
|
13
9
|
end
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'base'
|
|
4
|
-
|
|
5
3
|
module Coradoc
|
|
6
4
|
module Mirror
|
|
7
5
|
module ReverseBuilder
|
|
@@ -9,10 +7,6 @@ module Coradoc
|
|
|
9
7
|
# information lives in the original AsciiDoc and is preserved only
|
|
10
8
|
# on the forward side; the reverse side collapses them.
|
|
11
9
|
class Section < Base
|
|
12
|
-
registers 'section', 'clause', 'annex', 'content_section',
|
|
13
|
-
'abstract', 'foreword', 'introduction',
|
|
14
|
-
'acknowledgements', 'terms', 'definitions', 'references'
|
|
15
|
-
|
|
16
10
|
def build(node)
|
|
17
11
|
attrs = node.attrs
|
|
18
12
|
CoreModel::SectionElement.new(
|
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'base'
|
|
4
|
-
|
|
5
3
|
module Coradoc
|
|
6
4
|
module Mirror
|
|
7
5
|
module ReverseBuilder
|
|
8
6
|
# `sections` is a structural container only — unwrap directly into
|
|
9
7
|
# an array. MirrorToCoreModel#build_content flattens arrays.
|
|
10
8
|
class Sections < Base
|
|
11
|
-
registers 'sections'
|
|
12
|
-
|
|
13
9
|
def build(node)
|
|
14
10
|
build_content(node)
|
|
15
11
|
end
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'base'
|
|
4
|
-
|
|
5
3
|
module Coradoc
|
|
6
4
|
module Mirror
|
|
7
5
|
module ReverseBuilder
|
|
8
6
|
class TableRow < Base
|
|
9
|
-
registers 'table_row'
|
|
10
|
-
|
|
11
7
|
def build(node)
|
|
12
8
|
cells = build_content(node).select { |c| c.is_a?(CoreModel::TableCell) }
|
|
13
9
|
CoreModel::TableRow.new(cells: cells)
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'base'
|
|
4
|
-
|
|
5
3
|
module Coradoc
|
|
6
4
|
module Mirror
|
|
7
5
|
module ReverseBuilder
|
|
8
6
|
class TocEntry < Base
|
|
9
|
-
registers 'toc_entry'
|
|
10
|
-
|
|
11
7
|
def build(node)
|
|
12
8
|
attrs = node.attrs
|
|
13
9
|
CoreModel::TocEntry.new(id: attrs&.id, title: attrs&.title)
|
|
@@ -2,34 +2,117 @@
|
|
|
2
2
|
|
|
3
3
|
module Coradoc
|
|
4
4
|
module Mirror
|
|
5
|
-
# OCP-compliant registry for Mirror node
|
|
5
|
+
# OCP-compliant registry for Mirror node → CoreModel transformation.
|
|
6
6
|
#
|
|
7
|
-
#
|
|
7
|
+
# Single source of truth: the TYPE_TO_FILE table below maps every
|
|
8
|
+
# Mirror wire type string (and its aliases) to the file that
|
|
9
|
+
# implements its builder. Two things derive from this one table:
|
|
8
10
|
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# class Figure < Base
|
|
13
|
-
# registers 'figure'
|
|
14
|
-
# def build(node) = CoreModel::Image.new(...)
|
|
15
|
-
# end
|
|
16
|
-
# end
|
|
11
|
+
# 1. autoload declarations — one per unique file, so builders
|
|
12
|
+
# load lazily on first lookup (no 47-file eager load at boot).
|
|
13
|
+
# 2. lookup() — type → file → const_get, which triggers autoload.
|
|
17
14
|
#
|
|
18
|
-
#
|
|
19
|
-
# to MirrorToCoreModel or any other existing class — the registry is
|
|
20
|
-
# the single source of truth for "which type string maps to which
|
|
21
|
-
# builder" (MECE).
|
|
15
|
+
# Adding a new built-in builder is purely additive:
|
|
22
16
|
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
17
|
+
# 1. Add `'<wire_type>' => '<file_basename>'` to TYPE_TO_FILE.
|
|
18
|
+
# 2. Create `reverse_builder/<file_basename>.rb` defining
|
|
19
|
+
# `class <CamelizedName> < Base; def build(node); ...; end; end`.
|
|
20
|
+
#
|
|
21
|
+
# No edits to this file beyond the table — autoload and lookup
|
|
22
|
+
# adapt automatically. Mirror-level mark dispatch lives in
|
|
23
|
+
# MarkReverseBuilder (mark_reverse_builder.rb).
|
|
24
|
+
#
|
|
25
|
+
# Third-party / runtime builders can still register via
|
|
26
|
+
# `ReverseBuilder.register(type, klass)`; they take precedence over
|
|
27
|
+
# built-in autoload entries.
|
|
29
28
|
module ReverseBuilder
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
#
|
|
29
|
+
autoload :Base, "#{__dir__}/reverse_builder/base"
|
|
30
|
+
|
|
31
|
+
# Wire type string → file basename under reverse_builder/.
|
|
32
|
+
# Aliases (e.g. 'clause' and 'annex' both route to section) are
|
|
33
|
+
# expressed by mapping multiple type strings to the same file.
|
|
34
|
+
TYPE_TO_FILE = {
|
|
35
|
+
'doc' => 'document',
|
|
36
|
+
'section' => 'section',
|
|
37
|
+
'clause' => 'section',
|
|
38
|
+
'annex' => 'section',
|
|
39
|
+
'content_section' => 'section',
|
|
40
|
+
'abstract' => 'section',
|
|
41
|
+
'foreword' => 'section',
|
|
42
|
+
'introduction' => 'section',
|
|
43
|
+
'acknowledgements' => 'section',
|
|
44
|
+
'terms' => 'section',
|
|
45
|
+
'definitions' => 'section',
|
|
46
|
+
'references' => 'section',
|
|
47
|
+
'sections' => 'sections',
|
|
48
|
+
'preface' => 'preamble',
|
|
49
|
+
'floating_title' => 'header',
|
|
50
|
+
'heading' => 'header',
|
|
51
|
+
'paragraph' => 'paragraph',
|
|
52
|
+
'sourcecode' => 'code_block',
|
|
53
|
+
'literal' => 'literal_block',
|
|
54
|
+
'pass' => 'pass_block',
|
|
55
|
+
'stem' => 'stem_block',
|
|
56
|
+
'quote' => 'blockquote',
|
|
57
|
+
'example' => 'example',
|
|
58
|
+
'sidebar' => 'sidebar',
|
|
59
|
+
'abstract_block' => 'abstract',
|
|
60
|
+
'partintro_block' => 'partintro',
|
|
61
|
+
'open_block' => 'open_block',
|
|
62
|
+
'verse' => 'verse',
|
|
63
|
+
'horizontal_rule' => 'horizontal_rule',
|
|
64
|
+
'thematic_break' => 'horizontal_rule',
|
|
65
|
+
'soft_break' => 'soft_break',
|
|
66
|
+
'hard_break' => 'hard_break',
|
|
67
|
+
'admonition' => 'admonition',
|
|
68
|
+
'bullet_list' => 'bullet_list',
|
|
69
|
+
'ordered_list' => 'ordered_list',
|
|
70
|
+
'list_item' => 'list_item',
|
|
71
|
+
'dl' => 'definition_list',
|
|
72
|
+
'dt' => 'inline_text',
|
|
73
|
+
'dd' => 'inline_text',
|
|
74
|
+
'image' => 'image',
|
|
75
|
+
'figure' => 'figure',
|
|
76
|
+
'caption' => 'caption',
|
|
77
|
+
'include' => 'include',
|
|
78
|
+
'table' => 'table',
|
|
79
|
+
'table_head' => 'table_head',
|
|
80
|
+
'table_body' => 'table_body',
|
|
81
|
+
'table_row' => 'table_row',
|
|
82
|
+
'table_cell' => 'table_cell',
|
|
83
|
+
'bibliography' => 'bibliography',
|
|
84
|
+
'biblio_entry' => 'biblio_entry',
|
|
85
|
+
'footnotes' => 'footnotes',
|
|
86
|
+
'footnote_entry' => 'footnote_entry',
|
|
87
|
+
'footnote_marker' => 'footnote_marker',
|
|
88
|
+
'toc' => 'toc',
|
|
89
|
+
'toc_entry' => 'toc_entry',
|
|
90
|
+
'text' => 'text',
|
|
91
|
+
'raw_inline' => 'raw_inline',
|
|
92
|
+
'frontmatter' => 'frontmatter',
|
|
93
|
+
'generic_block' => 'generic_block'
|
|
94
|
+
}.freeze
|
|
95
|
+
|
|
96
|
+
# File basename → Ruby constant name. Derived from the file name
|
|
97
|
+
# using the project-wide snake_case → CamelCase convention
|
|
98
|
+
# (every existing builder follows it). Declared once here so
|
|
99
|
+
# adding a builder that follows convention requires no extra
|
|
100
|
+
# wiring.
|
|
101
|
+
FILE_TO_CLASS = TYPE_TO_FILE.each_value.each_with_object({}) do |file, acc|
|
|
102
|
+
next if acc.key?(file)
|
|
103
|
+
|
|
104
|
+
acc[file] = file.split('_').map(&:capitalize).join
|
|
105
|
+
end.freeze
|
|
106
|
+
|
|
107
|
+
# Lazy-load each builder. Lookup triggers autoload via const_get.
|
|
108
|
+
FILE_TO_CLASS.each do |file, const_name|
|
|
109
|
+
autoload const_name.to_sym, "#{__dir__}/reverse_builder/#{file}"
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Runtime registrations from third-party / external builders.
|
|
113
|
+
# Takes precedence over built-in autoload entries so external
|
|
114
|
+
# code can override built-in behaviour (e.g. a custom Paragraph).
|
|
115
|
+
# Not frozen: register() writes here at runtime.
|
|
33
116
|
REGISTRY = {}
|
|
34
117
|
|
|
35
118
|
module_function
|
|
@@ -38,63 +121,27 @@ module Coradoc
|
|
|
38
121
|
REGISTRY[type] = builder_class
|
|
39
122
|
end
|
|
40
123
|
|
|
124
|
+
# DSL for subclasses to self-register at load time. Kept for
|
|
125
|
+
# backward compatibility with the existing OCP test pattern that
|
|
126
|
+
# creates synthetic builders via `Class.new(Base) { registers(...) }`.
|
|
127
|
+
# Built-in builders no longer call this — their dispatch is
|
|
128
|
+
# derived from TYPE_TO_FILE.
|
|
129
|
+
def registers(*types)
|
|
130
|
+
types.each { |t| register(t, self) }
|
|
131
|
+
end
|
|
132
|
+
|
|
41
133
|
def lookup(type)
|
|
42
|
-
REGISTRY[type]
|
|
134
|
+
return REGISTRY[type] if REGISTRY.key?(type)
|
|
135
|
+
|
|
136
|
+
file = TYPE_TO_FILE[type]
|
|
137
|
+
return nil unless file
|
|
138
|
+
|
|
139
|
+
const_get(FILE_TO_CLASS[file])
|
|
43
140
|
end
|
|
44
141
|
|
|
45
142
|
def registered_types
|
|
46
|
-
REGISTRY.keys
|
|
143
|
+
(REGISTRY.keys + TYPE_TO_FILE.keys).uniq
|
|
47
144
|
end
|
|
48
|
-
|
|
49
|
-
# Base must load first — every subclass inherits from it. Then
|
|
50
|
-
# eager-load each Builder so its `registers` call runs.
|
|
51
|
-
require_relative 'reverse_builder/base'
|
|
52
|
-
require_relative 'reverse_builder/document'
|
|
53
|
-
require_relative 'reverse_builder/section'
|
|
54
|
-
require_relative 'reverse_builder/header'
|
|
55
|
-
require_relative 'reverse_builder/preamble'
|
|
56
|
-
require_relative 'reverse_builder/sections'
|
|
57
|
-
require_relative 'reverse_builder/paragraph'
|
|
58
|
-
require_relative 'reverse_builder/code_block'
|
|
59
|
-
require_relative 'reverse_builder/literal_block'
|
|
60
|
-
require_relative 'reverse_builder/pass_block'
|
|
61
|
-
require_relative 'reverse_builder/stem_block'
|
|
62
|
-
require_relative 'reverse_builder/blockquote'
|
|
63
|
-
require_relative 'reverse_builder/example'
|
|
64
|
-
require_relative 'reverse_builder/sidebar'
|
|
65
|
-
require_relative 'reverse_builder/abstract'
|
|
66
|
-
require_relative 'reverse_builder/partintro'
|
|
67
|
-
require_relative 'reverse_builder/open_block'
|
|
68
|
-
require_relative 'reverse_builder/verse'
|
|
69
|
-
require_relative 'reverse_builder/horizontal_rule'
|
|
70
|
-
require_relative 'reverse_builder/frontmatter'
|
|
71
|
-
require_relative 'reverse_builder/admonition'
|
|
72
|
-
require_relative 'reverse_builder/bullet_list'
|
|
73
|
-
require_relative 'reverse_builder/ordered_list'
|
|
74
|
-
require_relative 'reverse_builder/list_item'
|
|
75
|
-
require_relative 'reverse_builder/definition_list'
|
|
76
|
-
require_relative 'reverse_builder/inline_text'
|
|
77
|
-
require_relative 'reverse_builder/image'
|
|
78
|
-
require_relative 'reverse_builder/figure'
|
|
79
|
-
require_relative 'reverse_builder/caption'
|
|
80
|
-
require_relative 'reverse_builder/include'
|
|
81
|
-
require_relative 'reverse_builder/table'
|
|
82
|
-
require_relative 'reverse_builder/table_head'
|
|
83
|
-
require_relative 'reverse_builder/table_body'
|
|
84
|
-
require_relative 'reverse_builder/table_row'
|
|
85
|
-
require_relative 'reverse_builder/table_cell'
|
|
86
|
-
require_relative 'reverse_builder/bibliography'
|
|
87
|
-
require_relative 'reverse_builder/biblio_entry'
|
|
88
|
-
require_relative 'reverse_builder/footnotes'
|
|
89
|
-
require_relative 'reverse_builder/footnote_entry'
|
|
90
|
-
require_relative 'reverse_builder/footnote_marker'
|
|
91
|
-
require_relative 'reverse_builder/toc'
|
|
92
|
-
require_relative 'reverse_builder/toc_entry'
|
|
93
|
-
require_relative 'reverse_builder/text'
|
|
94
|
-
require_relative 'reverse_builder/raw_inline'
|
|
95
|
-
require_relative 'reverse_builder/soft_break'
|
|
96
|
-
require_relative 'reverse_builder/hard_break'
|
|
97
|
-
require_relative 'reverse_builder/generic_block'
|
|
98
145
|
end
|
|
99
146
|
end
|
|
100
147
|
end
|