coradoc-mirror 0.1.4 → 0.1.5
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/handler_registry.rb +19 -30
- data/lib/coradoc/mirror/handlers/code_block.rb +17 -24
- data/lib/coradoc/mirror/handlers/inline.rb +14 -0
- data/lib/coradoc/mirror/node.rb +39 -0
- data/lib/coradoc/mirror/reverse_builder/admonition.rb +20 -0
- data/lib/coradoc/mirror/reverse_builder/base.rb +42 -0
- data/lib/coradoc/mirror/reverse_builder/biblio_entry.rb +22 -0
- data/lib/coradoc/mirror/reverse_builder/bibliography.rb +18 -0
- data/lib/coradoc/mirror/reverse_builder/blockquote.rb +20 -0
- data/lib/coradoc/mirror/reverse_builder/bullet_list.rb +18 -0
- data/lib/coradoc/mirror/reverse_builder/caption.rb +19 -0
- data/lib/coradoc/mirror/reverse_builder/code_block.rb +22 -0
- data/lib/coradoc/mirror/reverse_builder/definition_list.rb +35 -0
- data/lib/coradoc/mirror/reverse_builder/document.rb +22 -0
- data/lib/coradoc/mirror/reverse_builder/example.rb +20 -0
- data/lib/coradoc/mirror/reverse_builder/figure.rb +35 -0
- data/lib/coradoc/mirror/reverse_builder/footnote_entry.rb +18 -0
- data/lib/coradoc/mirror/reverse_builder/footnote_marker.rb +24 -0
- data/lib/coradoc/mirror/reverse_builder/footnotes.rb +20 -0
- data/lib/coradoc/mirror/reverse_builder/frontmatter.rb +21 -0
- data/lib/coradoc/mirror/reverse_builder/generic_block.rb +26 -0
- data/lib/coradoc/mirror/reverse_builder/header.rb +22 -0
- data/lib/coradoc/mirror/reverse_builder/horizontal_rule.rb +17 -0
- data/lib/coradoc/mirror/reverse_builder/image.rb +25 -0
- data/lib/coradoc/mirror/reverse_builder/include.rb +46 -0
- data/lib/coradoc/mirror/reverse_builder/inline_text.rb +19 -0
- data/lib/coradoc/mirror/reverse_builder/list_item.rb +37 -0
- data/lib/coradoc/mirror/reverse_builder/literal_block.rb +22 -0
- data/lib/coradoc/mirror/reverse_builder/open_block.rb +17 -0
- data/lib/coradoc/mirror/reverse_builder/ordered_list.rb +18 -0
- data/lib/coradoc/mirror/reverse_builder/paragraph.rb +17 -0
- data/lib/coradoc/mirror/reverse_builder/pass_block.rb +22 -0
- data/lib/coradoc/mirror/reverse_builder/preamble.rb +17 -0
- data/lib/coradoc/mirror/reverse_builder/raw_inline.rb +17 -0
- data/lib/coradoc/mirror/reverse_builder/section.rb +28 -0
- data/lib/coradoc/mirror/reverse_builder/sections.rb +19 -0
- data/lib/coradoc/mirror/reverse_builder/sidebar.rb +20 -0
- data/lib/coradoc/mirror/reverse_builder/soft_break.rb +17 -0
- data/lib/coradoc/mirror/reverse_builder/stem_block.rb +22 -0
- data/lib/coradoc/mirror/reverse_builder/table.rb +27 -0
- data/lib/coradoc/mirror/reverse_builder/table_body.rb +17 -0
- data/lib/coradoc/mirror/reverse_builder/table_cell.rb +24 -0
- data/lib/coradoc/mirror/reverse_builder/table_head.rb +17 -0
- data/lib/coradoc/mirror/reverse_builder/table_row.rb +18 -0
- data/lib/coradoc/mirror/reverse_builder/text.rb +24 -0
- data/lib/coradoc/mirror/reverse_builder/toc.rb +17 -0
- data/lib/coradoc/mirror/reverse_builder/toc_entry.rb +18 -0
- data/lib/coradoc/mirror/reverse_builder/verse.rb +20 -0
- data/lib/coradoc/mirror/reverse_builder.rb +59 -574
- data/lib/coradoc/mirror/version.rb +1 -1
- data/lib/coradoc/mirror.rb +2 -0
- data/lib/coradoc-mirror.rb +0 -2
- metadata +45 -2
- data/lib/coradoc/mirror/output.rb +0 -62
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ec671d9e6bbd3654635451d5a9ae2284f7e778f313b56e4e8d87e237e853b15e
|
|
4
|
+
data.tar.gz: 51f63185d93bda861e6f545fdd4bceb0e07a9516db1d8d51011fda4e08236b35
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 40d4b935ed85cdfb2a0d4845807df32367541498c4921879d5875194f8c1572402ad7bd809055cdb285b9ea2b7457c1847c8039c861c88db9c5094ea42d6edfa
|
|
7
|
+
data.tar.gz: 91b105b79f7eef2230869c1cc386d0ec105b08346a36e66f4e18e95b6931a66382ae786910c9d6bc72c04e287dd9f856a38cc569dadfe3a9f2304f710186e0f7
|
|
@@ -8,6 +8,10 @@ module Coradoc
|
|
|
8
8
|
# Third-party gems can register additional handlers without modifying
|
|
9
9
|
# core classes (OCP).
|
|
10
10
|
#
|
|
11
|
+
# Lookup (including ancestor walking) is delegated to
|
|
12
|
+
# `Coradoc::Dispatch.hierarchical`. This class keeps the Entry shape and
|
|
13
|
+
# the handler-invocation semantics — those are mirror-specific.
|
|
14
|
+
#
|
|
11
15
|
# @example Registering a handler
|
|
12
16
|
# registry = Coradoc::Mirror.default_registry
|
|
13
17
|
# registry.register(MyCustomBlock, MyHandler)
|
|
@@ -17,12 +21,11 @@ module Coradoc
|
|
|
17
21
|
# registry.register(Coradoc::CoreModel::ParagraphBlock, MyParagraphHandler)
|
|
18
22
|
#
|
|
19
23
|
class HandlerRegistry
|
|
20
|
-
# Structured entry for a registered handler.
|
|
21
24
|
Entry = Struct.new(:handler, :method_name, :concat, :extra_kwargs,
|
|
22
25
|
keyword_init: true)
|
|
23
26
|
|
|
24
27
|
def initialize
|
|
25
|
-
@
|
|
28
|
+
@dispatch = Coradoc::Dispatch.hierarchical
|
|
26
29
|
end
|
|
27
30
|
|
|
28
31
|
# Register a handler for a CoreModel class.
|
|
@@ -38,46 +41,32 @@ module Coradoc
|
|
|
38
41
|
# to the handler
|
|
39
42
|
def register(model_class, handler, method_name: :call, concat: false,
|
|
40
43
|
extra_kwargs: {})
|
|
41
|
-
@
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
@dispatch.register(
|
|
45
|
+
model_class,
|
|
46
|
+
Entry.new(
|
|
47
|
+
handler: handler,
|
|
48
|
+
method_name: method_name,
|
|
49
|
+
concat: concat,
|
|
50
|
+
extra_kwargs: extra_kwargs
|
|
51
|
+
)
|
|
46
52
|
)
|
|
47
53
|
end
|
|
48
54
|
|
|
49
55
|
# Find the handler entry for a given CoreModel element.
|
|
50
56
|
#
|
|
51
|
-
# Walks the element's class ancestors
|
|
52
|
-
# registered handler. This allows registering
|
|
53
|
-
# base class (e.g., Block) that applies to all
|
|
54
|
-
# while also registering specific handlers for subclasses.
|
|
57
|
+
# Walks the element's class ancestors (via Dispatch.hierarchical) to
|
|
58
|
+
# find the most specific registered handler. This allows registering
|
|
59
|
+
# a handler for a base class (e.g., Block) that applies to all
|
|
60
|
+
# subclasses, while also registering specific handlers for subclasses.
|
|
55
61
|
#
|
|
56
62
|
# @param element [CoreModel::Base] element to find handler for
|
|
57
63
|
# @return [Entry, nil]
|
|
58
|
-
TERMINAL_ANCESTORS = [Object, BasicObject].freeze
|
|
59
|
-
|
|
60
64
|
def entry_for(element)
|
|
61
|
-
|
|
62
|
-
return entry if entry
|
|
63
|
-
|
|
64
|
-
element.class.ancestors.each do |ancestor|
|
|
65
|
-
next if ancestor == element.class
|
|
66
|
-
break if TERMINAL_ANCESTORS.include?(ancestor)
|
|
67
|
-
|
|
68
|
-
entry = @handlers[ancestor]
|
|
69
|
-
return entry if entry
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
nil
|
|
65
|
+
@dispatch.lookup(element.class)
|
|
73
66
|
end
|
|
74
67
|
|
|
75
|
-
# Check if a handler is registered for a CoreModel class.
|
|
76
|
-
#
|
|
77
|
-
# @param model_class [Class]
|
|
78
|
-
# @return [Boolean]
|
|
79
68
|
def registered?(model_class)
|
|
80
|
-
@
|
|
69
|
+
@dispatch.registered?(model_class)
|
|
81
70
|
end
|
|
82
71
|
|
|
83
72
|
# Invoke the handler for a given element.
|
|
@@ -5,49 +5,42 @@ module Coradoc
|
|
|
5
5
|
module Handlers
|
|
6
6
|
module CodeBlock
|
|
7
7
|
def self.source(element, context:)
|
|
8
|
-
build_code_block(element, context)
|
|
8
|
+
build_code_block(element, context, node_class: Node::CodeBlock)
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def self.listing(element, context:)
|
|
12
|
-
build_code_block(element, context)
|
|
12
|
+
build_code_block(element, context, node_class: Node::CodeBlock)
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def self.literal(element, context:)
|
|
16
|
-
build_code_block(element, context)
|
|
16
|
+
build_code_block(element, context, node_class: Node::LiteralBlock)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def self.pass(element, context:)
|
|
20
|
-
build_code_block(element, context, passthrough: true)
|
|
20
|
+
build_code_block(element, context, node_class: Node::PassBlock, passthrough: true)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.stem(element, context:)
|
|
24
|
+
build_code_block(element, context, node_class: Node::StemBlock)
|
|
21
25
|
end
|
|
22
26
|
|
|
23
27
|
class << self
|
|
24
28
|
private
|
|
25
29
|
|
|
26
|
-
def build_code_block(element, context, passthrough: false)
|
|
30
|
+
def build_code_block(element, context, node_class:, passthrough: false)
|
|
27
31
|
text = extract_text(element)
|
|
28
32
|
js_mode = context.partition_structural
|
|
33
|
+
attrs = Node::CodeBlock::Attrs.new(
|
|
34
|
+
title: element.title,
|
|
35
|
+
language: element.language,
|
|
36
|
+
passthrough: passthrough || nil,
|
|
37
|
+
text: js_mode ? text : nil
|
|
38
|
+
)
|
|
29
39
|
|
|
30
40
|
if js_mode
|
|
31
|
-
|
|
32
|
-
# no children. Pre-formatted text rendered via <pre><code>.
|
|
33
|
-
Node::CodeBlock.new(
|
|
34
|
-
attrs: Node::CodeBlock::Attrs.new(
|
|
35
|
-
title: element.title,
|
|
36
|
-
language: element.language,
|
|
37
|
-
passthrough: passthrough || nil,
|
|
38
|
-
text: text
|
|
39
|
-
),
|
|
40
|
-
content: []
|
|
41
|
-
)
|
|
41
|
+
node_class.new(attrs: attrs, content: [])
|
|
42
42
|
else
|
|
43
|
-
|
|
44
|
-
attrs: Node::CodeBlock::Attrs.new(
|
|
45
|
-
title: element.title,
|
|
46
|
-
language: element.language,
|
|
47
|
-
passthrough: passthrough || nil
|
|
48
|
-
),
|
|
49
|
-
content: [context.text_node(text)]
|
|
50
|
-
)
|
|
43
|
+
node_class.new(attrs: attrs, content: [context.text_node(text)])
|
|
51
44
|
end
|
|
52
45
|
end
|
|
53
46
|
|
|
@@ -106,6 +106,8 @@ module Coradoc
|
|
|
106
106
|
Node::SoftBreak.new
|
|
107
107
|
when CoreModel::TextElement
|
|
108
108
|
build_text_only(element, context)
|
|
109
|
+
when CoreModel::RawInlineElement
|
|
110
|
+
build_raw_inline(element, context)
|
|
109
111
|
when CoreModel::InlineElement
|
|
110
112
|
handle_generic_inline(element, context)
|
|
111
113
|
end
|
|
@@ -189,6 +191,18 @@ module Coradoc
|
|
|
189
191
|
context.text_node(text)
|
|
190
192
|
end
|
|
191
193
|
|
|
194
|
+
# Passthrough content carries its own raw output-format markup
|
|
195
|
+
# (HTML, XML, etc.). Emit a typed RawInline node so renderers
|
|
196
|
+
# can skip escaping unambiguously — never a plain text node,
|
|
197
|
+
# which would force every downstream consumer to choose between
|
|
198
|
+
# XSS risk and visible literal markup.
|
|
199
|
+
def build_raw_inline(element, _context)
|
|
200
|
+
text = element.content.to_s
|
|
201
|
+
return nil if text.empty?
|
|
202
|
+
|
|
203
|
+
Node::RawInline.new(text: text)
|
|
204
|
+
end
|
|
205
|
+
|
|
192
206
|
def extract_inline_text(element)
|
|
193
207
|
return element.content.to_s if element.content && !element.content.to_s.empty?
|
|
194
208
|
|
data/lib/coradoc/mirror/node.rb
CHANGED
|
@@ -162,6 +162,25 @@ module Coradoc
|
|
|
162
162
|
end
|
|
163
163
|
end
|
|
164
164
|
|
|
165
|
+
# Raw inline passthrough — content the source format marked as "do
|
|
166
|
+
# not process, emit verbatim." Distinct from `text` so renderers can
|
|
167
|
+
# skip escaping without sniffing content for `<...>`. AsciiDoc's
|
|
168
|
+
# `+++raw+++` is the canonical producer.
|
|
169
|
+
class RawInline < Node
|
|
170
|
+
PM_TYPE = 'raw_inline'
|
|
171
|
+
|
|
172
|
+
attribute :text, :string
|
|
173
|
+
|
|
174
|
+
key_value do
|
|
175
|
+
map 'type', to: :type, render_default: true
|
|
176
|
+
map 'text', to: :text
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def text_content
|
|
180
|
+
text.to_s
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
165
184
|
# ── Paragraph / block text ──
|
|
166
185
|
|
|
167
186
|
class Paragraph < Node
|
|
@@ -200,6 +219,26 @@ module Coradoc
|
|
|
200
219
|
end
|
|
201
220
|
end
|
|
202
221
|
|
|
222
|
+
# Literal block — preformatted text (`....` delimiter). Same shape
|
|
223
|
+
# as CodeBlock but distinguished on the wire so consumers can apply
|
|
224
|
+
# literal-vs-source rendering/styling.
|
|
225
|
+
class LiteralBlock < CodeBlock
|
|
226
|
+
PM_TYPE = 'literal'
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Pass block — raw passthrough content (`++++` delimiter). Same
|
|
230
|
+
# shape as CodeBlock; flagged via attrs.passthrough = true.
|
|
231
|
+
class PassBlock < CodeBlock
|
|
232
|
+
PM_TYPE = 'pass'
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# STEM block — mathematical/scientific markup (`[stem|latexmath|
|
|
236
|
+
# asciimath]\n++++`). Carries a +language+ attr ('latex' default,
|
|
237
|
+
# 'asciimath' alternative).
|
|
238
|
+
class StemBlock < CodeBlock
|
|
239
|
+
PM_TYPE = 'stem'
|
|
240
|
+
end
|
|
241
|
+
|
|
203
242
|
class Blockquote < Node
|
|
204
243
|
PM_TYPE = 'quote'
|
|
205
244
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
|
|
5
|
+
module Coradoc
|
|
6
|
+
module Mirror
|
|
7
|
+
module ReverseBuilder
|
|
8
|
+
class Admonition < Base
|
|
9
|
+
registers 'admonition'
|
|
10
|
+
|
|
11
|
+
def build(node)
|
|
12
|
+
CoreModel::AnnotationBlock.new(
|
|
13
|
+
annotation_type: node.attrs&.admonition_type,
|
|
14
|
+
content: extract_text(node)
|
|
15
|
+
)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Coradoc
|
|
4
|
+
module Mirror
|
|
5
|
+
module ReverseBuilder
|
|
6
|
+
# Base class for all reverse builders. Subclasses register one or
|
|
7
|
+
# more Mirror type strings via `registers` and implement `#build`.
|
|
8
|
+
# Shared helpers (build_content, extract_text, apply_mark, ...) are
|
|
9
|
+
# delegated to the context (a MirrorToCoreModel instance), keeping
|
|
10
|
+
# each builder focused on the per-type mapping only (DRY).
|
|
11
|
+
class Base
|
|
12
|
+
attr_reader :context
|
|
13
|
+
|
|
14
|
+
def initialize(context)
|
|
15
|
+
@context = context
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def build(_node)
|
|
19
|
+
raise NotImplementedError,
|
|
20
|
+
"#{self.class} must implement #build(node)"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Shared helpers — all delegate to the context (DRY).
|
|
24
|
+
def build_content(node) = context.build_content(node)
|
|
25
|
+
def build_inline_children(node) = context.build_inline_children(node)
|
|
26
|
+
def build_node(node) = context.build_node(node)
|
|
27
|
+
def extract_text(node) = context.extract_text(node)
|
|
28
|
+
def apply_mark(inner, mark) = context.apply_mark(inner, mark)
|
|
29
|
+
def inline_content(element) = context.inline_content(element)
|
|
30
|
+
|
|
31
|
+
class << self
|
|
32
|
+
# DSL: declare which Mirror type strings this builder handles.
|
|
33
|
+
# Multiple strings per builder are allowed (e.g. all JS
|
|
34
|
+
# SECTION_TYPES route to the same SectionElement builder).
|
|
35
|
+
def registers(*types)
|
|
36
|
+
types.each { |t| ReverseBuilder.register(t, self) }
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
|
|
5
|
+
module Coradoc
|
|
6
|
+
module Mirror
|
|
7
|
+
module ReverseBuilder
|
|
8
|
+
class BiblioEntry < Base
|
|
9
|
+
registers 'biblio_entry'
|
|
10
|
+
|
|
11
|
+
def build(node)
|
|
12
|
+
attrs = node.attrs
|
|
13
|
+
CoreModel::BibliographyEntry.new(
|
|
14
|
+
anchor_name: attrs&.anchor_name,
|
|
15
|
+
document_id: attrs&.document_id,
|
|
16
|
+
ref_text: extract_text(node)
|
|
17
|
+
)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
|
|
5
|
+
module Coradoc
|
|
6
|
+
module Mirror
|
|
7
|
+
module ReverseBuilder
|
|
8
|
+
class Bibliography < Base
|
|
9
|
+
registers 'bibliography'
|
|
10
|
+
|
|
11
|
+
def build(node)
|
|
12
|
+
entries = build_content(node).select { |c| c.is_a?(CoreModel::BibliographyEntry) }
|
|
13
|
+
CoreModel::Bibliography.new(title: node.attrs&.title, entries: entries)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
|
|
5
|
+
module Coradoc
|
|
6
|
+
module Mirror
|
|
7
|
+
module ReverseBuilder
|
|
8
|
+
class Blockquote < Base
|
|
9
|
+
registers 'quote'
|
|
10
|
+
|
|
11
|
+
def build(node)
|
|
12
|
+
CoreModel::QuoteBlock.new(
|
|
13
|
+
attribution: node.attrs&.attribution,
|
|
14
|
+
children: build_content(node)
|
|
15
|
+
)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
|
|
5
|
+
module Coradoc
|
|
6
|
+
module Mirror
|
|
7
|
+
module ReverseBuilder
|
|
8
|
+
class BulletList < Base
|
|
9
|
+
registers 'bullet_list'
|
|
10
|
+
|
|
11
|
+
def build(node)
|
|
12
|
+
items = build_content(node).select { |c| c.is_a?(CoreModel::ListItem) }
|
|
13
|
+
CoreModel::ListBlock.new(marker_type: 'unordered', items: items)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
|
|
5
|
+
module Coradoc
|
|
6
|
+
module Mirror
|
|
7
|
+
module ReverseBuilder
|
|
8
|
+
# Caption only appears as a Figure child. If encountered standalone,
|
|
9
|
+
# extract its text as an inline element so it isn't lost.
|
|
10
|
+
class Caption < Base
|
|
11
|
+
registers 'caption'
|
|
12
|
+
|
|
13
|
+
def build(node)
|
|
14
|
+
CoreModel::InlineElement.new(content: extract_text(node))
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
|
|
5
|
+
module Coradoc
|
|
6
|
+
module Mirror
|
|
7
|
+
module ReverseBuilder
|
|
8
|
+
class CodeBlock < Base
|
|
9
|
+
registers 'sourcecode'
|
|
10
|
+
|
|
11
|
+
def build(node)
|
|
12
|
+
attrs = node.attrs
|
|
13
|
+
CoreModel::SourceBlock.new(
|
|
14
|
+
content: attrs&.text || extract_text(node),
|
|
15
|
+
language: attrs&.language,
|
|
16
|
+
title: attrs&.title
|
|
17
|
+
)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
|
|
5
|
+
module Coradoc
|
|
6
|
+
module Mirror
|
|
7
|
+
module ReverseBuilder
|
|
8
|
+
class DefinitionList < Base
|
|
9
|
+
registers 'dl'
|
|
10
|
+
|
|
11
|
+
def build(node)
|
|
12
|
+
terms = []
|
|
13
|
+
descriptions = []
|
|
14
|
+
node.content&.each do |child|
|
|
15
|
+
next unless child.is_a?(Node)
|
|
16
|
+
|
|
17
|
+
case child.type
|
|
18
|
+
when 'dt' then terms << build_node(child)
|
|
19
|
+
when 'dd' then descriptions << build_node(child)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
items = terms.zip(descriptions).map do |term, desc|
|
|
24
|
+
CoreModel::DefinitionItem.new(
|
|
25
|
+
term: inline_content(term),
|
|
26
|
+
definitions: [inline_content(desc)]
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
CoreModel::DefinitionList.new(items: items)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
|
|
5
|
+
module Coradoc
|
|
6
|
+
module Mirror
|
|
7
|
+
module ReverseBuilder
|
|
8
|
+
class Document < Base
|
|
9
|
+
registers 'doc'
|
|
10
|
+
|
|
11
|
+
def build(node)
|
|
12
|
+
attrs = node.attrs
|
|
13
|
+
CoreModel::DocumentElement.new(
|
|
14
|
+
title: attrs&.title,
|
|
15
|
+
id: attrs&.id,
|
|
16
|
+
children: build_content(node)
|
|
17
|
+
)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
|
|
5
|
+
module Coradoc
|
|
6
|
+
module Mirror
|
|
7
|
+
module ReverseBuilder
|
|
8
|
+
class Example < Base
|
|
9
|
+
registers 'example'
|
|
10
|
+
|
|
11
|
+
def build(node)
|
|
12
|
+
CoreModel::ExampleBlock.new(
|
|
13
|
+
title: node.attrs&.title,
|
|
14
|
+
children: build_content(node)
|
|
15
|
+
)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
|
|
5
|
+
module Coradoc
|
|
6
|
+
module Mirror
|
|
7
|
+
module ReverseBuilder
|
|
8
|
+
# JS @metanorma/mirror `figure` wraps an image plus an optional
|
|
9
|
+
# caption. Reverse: collapse back to a single CoreModel::Image,
|
|
10
|
+
# promoting the caption child to `caption:` if present.
|
|
11
|
+
class Figure < Base
|
|
12
|
+
registers 'figure'
|
|
13
|
+
|
|
14
|
+
def build(node)
|
|
15
|
+
image_child = node.content&.find { |c| c.is_a?(Node) && c.type == 'image' }
|
|
16
|
+
return nil unless image_child
|
|
17
|
+
|
|
18
|
+
image = build_node(image_child)
|
|
19
|
+
caption = extract_caption(node)
|
|
20
|
+
image.caption = caption if caption && !image.caption
|
|
21
|
+
image
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def extract_caption(node)
|
|
27
|
+
caption_node = node.content&.find { |c| c.is_a?(Node) && c.type == 'caption' }
|
|
28
|
+
return nil unless caption_node
|
|
29
|
+
|
|
30
|
+
extract_text(caption_node)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
|
|
5
|
+
module Coradoc
|
|
6
|
+
module Mirror
|
|
7
|
+
module ReverseBuilder
|
|
8
|
+
class FootnoteEntry < Base
|
|
9
|
+
registers 'footnote_entry'
|
|
10
|
+
|
|
11
|
+
def build(node)
|
|
12
|
+
attrs = node.attrs
|
|
13
|
+
CoreModel::Footnote.new(id: attrs&.id, content: extract_text(node))
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
|
|
5
|
+
module Coradoc
|
|
6
|
+
module Mirror
|
|
7
|
+
module ReverseBuilder
|
|
8
|
+
# Inline footnote marker (JS `footnote_marker`). The CoreModel
|
|
9
|
+
# FootnoteReference holds the same id/ref/number triple.
|
|
10
|
+
class FootnoteMarker < Base
|
|
11
|
+
registers 'footnote_marker'
|
|
12
|
+
|
|
13
|
+
def build(node)
|
|
14
|
+
attrs = node.attrs
|
|
15
|
+
CoreModel::FootnoteReference.new(
|
|
16
|
+
id: attrs&.id,
|
|
17
|
+
reference: attrs&.ref_id,
|
|
18
|
+
number: attrs&.number
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
|
|
5
|
+
module Coradoc
|
|
6
|
+
module Mirror
|
|
7
|
+
module ReverseBuilder
|
|
8
|
+
# The `footnotes` block is a structural trailing container; it has
|
|
9
|
+
# no CoreModel equivalent (each entry is built separately). Returns
|
|
10
|
+
# nil so build_content filters it out.
|
|
11
|
+
class Footnotes < Base
|
|
12
|
+
registers 'footnotes'
|
|
13
|
+
|
|
14
|
+
def build(_node)
|
|
15
|
+
nil
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
|
|
5
|
+
module Coradoc
|
|
6
|
+
module Mirror
|
|
7
|
+
module ReverseBuilder
|
|
8
|
+
class Frontmatter < Base
|
|
9
|
+
registers 'frontmatter'
|
|
10
|
+
|
|
11
|
+
def build(node)
|
|
12
|
+
attrs = node.attrs
|
|
13
|
+
CoreModel::FrontmatterBlock.new(
|
|
14
|
+
schema: attrs&.schema,
|
|
15
|
+
data: FrontmatterTreeToHash.to_hash(attrs&.entries || [])
|
|
16
|
+
)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
|
|
5
|
+
module Coradoc
|
|
6
|
+
module Mirror
|
|
7
|
+
module ReverseBuilder
|
|
8
|
+
# Catch-all for unrecognized block types emitted by the forward
|
|
9
|
+
# direction (`Node::GenericBlock`). Preserves the semantic_type so
|
|
10
|
+
# downstream consumers can dispatch on it.
|
|
11
|
+
class GenericBlock < Base
|
|
12
|
+
registers 'generic_block'
|
|
13
|
+
|
|
14
|
+
def build(node)
|
|
15
|
+
attrs = node.attrs
|
|
16
|
+
CoreModel::Block.new(
|
|
17
|
+
block_semantic_type: attrs&.semantic_type || 'generic',
|
|
18
|
+
title: attrs&.title,
|
|
19
|
+
id: attrs&.id,
|
|
20
|
+
content: extract_text(node)
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|