coradoc-mirror 0.1.1
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/lib/coradoc/mirror/core_model_to_mirror.rb +181 -0
- data/lib/coradoc/mirror/handler_registry.rb +105 -0
- data/lib/coradoc/mirror/handlers/admonition.rb +29 -0
- data/lib/coradoc/mirror/handlers/bibliography.rb +43 -0
- data/lib/coradoc/mirror/handlers/blockquote.rb +19 -0
- data/lib/coradoc/mirror/handlers/code_block.rb +69 -0
- data/lib/coradoc/mirror/handlers/comment.rb +14 -0
- data/lib/coradoc/mirror/handlers/definition_list.rb +69 -0
- data/lib/coradoc/mirror/handlers/example.rb +19 -0
- data/lib/coradoc/mirror/handlers/footnote.rb +18 -0
- data/lib/coradoc/mirror/handlers/frontmatter.rb +71 -0
- data/lib/coradoc/mirror/handlers/generic_block.rb +24 -0
- data/lib/coradoc/mirror/handlers/horizontal_rule.rb +14 -0
- data/lib/coradoc/mirror/handlers/image.rb +58 -0
- data/lib/coradoc/mirror/handlers/inline.rb +213 -0
- data/lib/coradoc/mirror/handlers/list.rb +80 -0
- data/lib/coradoc/mirror/handlers/open_block.rb +16 -0
- data/lib/coradoc/mirror/handlers/paragraph.rb +16 -0
- data/lib/coradoc/mirror/handlers/reviewer.rb +14 -0
- data/lib/coradoc/mirror/handlers/sidebar.rb +19 -0
- data/lib/coradoc/mirror/handlers/structural.rb +84 -0
- data/lib/coradoc/mirror/handlers/table.rb +82 -0
- data/lib/coradoc/mirror/handlers/toc.rb +48 -0
- data/lib/coradoc/mirror/handlers/verse.rb +22 -0
- data/lib/coradoc/mirror/handlers.rb +38 -0
- data/lib/coradoc/mirror/mark.rb +181 -0
- data/lib/coradoc/mirror/mark_reverse_builder.rb +142 -0
- data/lib/coradoc/mirror/mirror_json_format.rb +42 -0
- data/lib/coradoc/mirror/mirror_to_core_model.rb +73 -0
- data/lib/coradoc/mirror/mirror_yaml_format.rb +41 -0
- data/lib/coradoc/mirror/node.rb +856 -0
- data/lib/coradoc/mirror/output.rb +62 -0
- data/lib/coradoc/mirror/partitioner.rb +62 -0
- data/lib/coradoc/mirror/reverse_builder.rb +600 -0
- data/lib/coradoc/mirror/transformer.rb +41 -0
- data/lib/coradoc/mirror/version.rb +7 -0
- data/lib/coradoc/mirror.rb +161 -0
- data/lib/coradoc-mirror.rb +14 -0
- metadata +140 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Coradoc
|
|
4
|
+
module Mirror
|
|
5
|
+
# Handler modules for transforming CoreModel types to Mirror nodes.
|
|
6
|
+
#
|
|
7
|
+
# Each handler is a module/class that responds to +call(element, context:)+,
|
|
8
|
+
# where +element+ is a CoreModel instance and +context+ is the
|
|
9
|
+
# CoreModelToMirror transformer providing shared helpers.
|
|
10
|
+
#
|
|
11
|
+
# New handlers are added by creating a new module file and registering
|
|
12
|
+
# it in +Coradoc::Mirror.default_registry+ — no existing code changes (OCP).
|
|
13
|
+
module Handlers
|
|
14
|
+
autoload :Structural, "#{__dir__}/handlers/structural"
|
|
15
|
+
autoload :Paragraph, "#{__dir__}/handlers/paragraph"
|
|
16
|
+
autoload :CodeBlock, "#{__dir__}/handlers/code_block"
|
|
17
|
+
autoload :Blockquote, "#{__dir__}/handlers/blockquote"
|
|
18
|
+
autoload :Example, "#{__dir__}/handlers/example"
|
|
19
|
+
autoload :Sidebar, "#{__dir__}/handlers/sidebar"
|
|
20
|
+
autoload :OpenBlock, "#{__dir__}/handlers/open_block"
|
|
21
|
+
autoload :Verse, "#{__dir__}/handlers/verse"
|
|
22
|
+
autoload :Comment, "#{__dir__}/handlers/comment"
|
|
23
|
+
autoload :HorizontalRule, "#{__dir__}/handlers/horizontal_rule"
|
|
24
|
+
autoload :Reviewer, "#{__dir__}/handlers/reviewer"
|
|
25
|
+
autoload :Admonition, "#{__dir__}/handlers/admonition"
|
|
26
|
+
autoload :List, "#{__dir__}/handlers/list"
|
|
27
|
+
autoload :DefinitionList, "#{__dir__}/handlers/definition_list"
|
|
28
|
+
autoload :Table, "#{__dir__}/handlers/table"
|
|
29
|
+
autoload :Image, "#{__dir__}/handlers/image"
|
|
30
|
+
autoload :Inline, "#{__dir__}/handlers/inline"
|
|
31
|
+
autoload :Bibliography, "#{__dir__}/handlers/bibliography"
|
|
32
|
+
autoload :Footnote, "#{__dir__}/handlers/footnote"
|
|
33
|
+
autoload :Toc, "#{__dir__}/handlers/toc"
|
|
34
|
+
autoload :Frontmatter, "#{__dir__}/handlers/frontmatter"
|
|
35
|
+
autoload :GenericBlock, "#{__dir__}/handlers/generic_block"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'lutaml/model'
|
|
4
|
+
|
|
5
|
+
module Coradoc
|
|
6
|
+
module Mirror
|
|
7
|
+
# ProseMirror-compatible inline mark (formatting annotation).
|
|
8
|
+
#
|
|
9
|
+
# Marks decorate inline text nodes with formatting semantics like
|
|
10
|
+
# bold, italic, link, etc. Wire format:
|
|
11
|
+
#
|
|
12
|
+
# { "type": "strong" }
|
|
13
|
+
# { "type": "link", "attrs": { "href": "..." } }
|
|
14
|
+
#
|
|
15
|
+
# All built-in Mark subclasses live below in this file so the
|
|
16
|
+
# TYPE_TO_CLASS registry at the bottom can see every PM_TYPE at
|
|
17
|
+
# load time. Adding a new mark type = adding one subclass + letting
|
|
18
|
+
# the registry walker pick it up (OCP).
|
|
19
|
+
class Mark < Lutaml::Model::Serializable
|
|
20
|
+
PM_TYPE = 'mark'
|
|
21
|
+
|
|
22
|
+
attribute :type, :string, default: -> { self.class::PM_TYPE }
|
|
23
|
+
|
|
24
|
+
key_value do
|
|
25
|
+
map 'type', to: :type, render_default: true
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def text_content
|
|
29
|
+
''
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
module Coradoc
|
|
36
|
+
module Mirror
|
|
37
|
+
class Mark
|
|
38
|
+
# ── Marks without attrs ──
|
|
39
|
+
|
|
40
|
+
class Bold < Mark
|
|
41
|
+
PM_TYPE = 'strong'
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class Italic < Mark
|
|
45
|
+
PM_TYPE = 'emphasis'
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
class Monospace < Mark
|
|
49
|
+
PM_TYPE = 'code'
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
class Underline < Mark
|
|
53
|
+
PM_TYPE = 'underline'
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
class Strikethrough < Mark
|
|
57
|
+
PM_TYPE = 'strike'
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
class Subscript < Mark
|
|
61
|
+
PM_TYPE = 'subscript'
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
class Superscript < Mark
|
|
65
|
+
PM_TYPE = 'superscript'
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
class Highlight < Mark
|
|
69
|
+
PM_TYPE = 'highlight'
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# ── Marks with attrs ──
|
|
73
|
+
|
|
74
|
+
class Link < Mark
|
|
75
|
+
PM_TYPE = 'link'
|
|
76
|
+
|
|
77
|
+
class Attrs < Lutaml::Model::Serializable
|
|
78
|
+
attribute :href, :string
|
|
79
|
+
|
|
80
|
+
key_value do
|
|
81
|
+
map 'href', to: :href
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
attribute :attrs, Attrs
|
|
86
|
+
|
|
87
|
+
key_value do
|
|
88
|
+
map 'type', to: :type, render_default: true
|
|
89
|
+
map 'attrs', to: :attrs
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
class CrossReference < Mark
|
|
94
|
+
PM_TYPE = 'xref'
|
|
95
|
+
|
|
96
|
+
class Attrs < Lutaml::Model::Serializable
|
|
97
|
+
attribute :target, :string
|
|
98
|
+
attribute :resolved, :string
|
|
99
|
+
|
|
100
|
+
key_value do
|
|
101
|
+
map 'target', to: :target
|
|
102
|
+
map 'resolved', to: :resolved
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
attribute :attrs, Attrs
|
|
107
|
+
|
|
108
|
+
key_value do
|
|
109
|
+
map 'type', to: :type, render_default: true
|
|
110
|
+
map 'attrs', to: :attrs
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
class Stem < Mark
|
|
115
|
+
PM_TYPE = 'stem'
|
|
116
|
+
|
|
117
|
+
class Attrs < Lutaml::Model::Serializable
|
|
118
|
+
attribute :stem_type, :string
|
|
119
|
+
|
|
120
|
+
key_value do
|
|
121
|
+
map 'stem_type', to: :stem_type
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
attribute :attrs, Attrs
|
|
126
|
+
|
|
127
|
+
key_value do
|
|
128
|
+
map 'type', to: :type, render_default: true
|
|
129
|
+
map 'attrs', to: :attrs
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
class Span < Mark
|
|
134
|
+
PM_TYPE = 'span'
|
|
135
|
+
|
|
136
|
+
class Attrs < Lutaml::Model::Serializable
|
|
137
|
+
attribute :role, :string
|
|
138
|
+
|
|
139
|
+
key_value do
|
|
140
|
+
map 'role', to: :role
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
attribute :attrs, Attrs
|
|
145
|
+
|
|
146
|
+
key_value do
|
|
147
|
+
map 'type', to: :type, render_default: true
|
|
148
|
+
map 'attrs', to: :attrs
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
module Coradoc
|
|
156
|
+
module Mirror
|
|
157
|
+
class Mark
|
|
158
|
+
# Polymorphic class map — flat hash from PM_TYPE wire string to
|
|
159
|
+
# fully-qualified Ruby class name. Used by Node and Mark mappings
|
|
160
|
+
# to dispatch polymorphic deserialization. Populated once after
|
|
161
|
+
# all subclasses are defined above.
|
|
162
|
+
TYPE_TO_CLASS = begin
|
|
163
|
+
result = {}
|
|
164
|
+
Mark.constants.each do |name|
|
|
165
|
+
k = Mark.const_get(name)
|
|
166
|
+
next unless k.is_a?(Class) && k < Mark && k::PM_TYPE != 'mark'
|
|
167
|
+
|
|
168
|
+
result[k::PM_TYPE] = k.name
|
|
169
|
+
end
|
|
170
|
+
result.freeze
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Frozen polymorphic option block. Referenced verbatim by every
|
|
174
|
+
# Node mapping that has a `marks` collection.
|
|
175
|
+
POLYMORPHIC = {
|
|
176
|
+
attribute: 'type',
|
|
177
|
+
class_map: TYPE_TO_CLASS
|
|
178
|
+
}.freeze
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Coradoc
|
|
4
|
+
module Mirror
|
|
5
|
+
# OCP-compliant registry for Mirror mark -> CoreModel transformation.
|
|
6
|
+
#
|
|
7
|
+
# This is the mark-level counterpart to ReverseBuilder (which handles
|
|
8
|
+
# node-level dispatch). Adding support for a new mark type is purely
|
|
9
|
+
# additive:
|
|
10
|
+
#
|
|
11
|
+
# module MarkReverseBuilder
|
|
12
|
+
# class Concept < Base
|
|
13
|
+
# registers 'concept'
|
|
14
|
+
#
|
|
15
|
+
# def build(inner, _mark)
|
|
16
|
+
# CoreModel::TermElement.new(children: Array(inner))
|
|
17
|
+
# end
|
|
18
|
+
# end
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# No edits to MirrorToCoreModel#apply_mark or any other existing class.
|
|
22
|
+
module MarkReverseBuilder
|
|
23
|
+
REGISTRY = {}
|
|
24
|
+
|
|
25
|
+
module_function
|
|
26
|
+
|
|
27
|
+
def register(type, builder_class)
|
|
28
|
+
REGISTRY[type] = builder_class
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def lookup(type)
|
|
32
|
+
REGISTRY[type]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def registered_types
|
|
36
|
+
REGISTRY.keys
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Base class for all mark reverse builders. Subclasses register one
|
|
40
|
+
# mark type string via `registers` and implement `#build(inner, mark)`.
|
|
41
|
+
# `inner` is the already-built CoreModel inline content this mark
|
|
42
|
+
# wraps; `mark` is the source Mirror::Mark (for marks that carry
|
|
43
|
+
# attrs, like `link` reading `mark.href`).
|
|
44
|
+
class Base
|
|
45
|
+
def self.registers(*types)
|
|
46
|
+
types.each { |t| MarkReverseBuilder.register(t, self) }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def build(_inner, _mark)
|
|
50
|
+
raise NotImplementedError,
|
|
51
|
+
"#{self.class} must implement #build(inner, mark)"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# ── Simple wraps: typed InlineElement subclass, no attrs ──
|
|
56
|
+
|
|
57
|
+
class Bold < Base
|
|
58
|
+
registers 'strong'
|
|
59
|
+
|
|
60
|
+
def build(inner, _mark)
|
|
61
|
+
CoreModel::BoldElement.new(children: Array(inner))
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
class Italic < Base
|
|
66
|
+
registers 'emphasis'
|
|
67
|
+
|
|
68
|
+
def build(inner, _mark)
|
|
69
|
+
CoreModel::ItalicElement.new(children: Array(inner))
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
class Monospace < Base
|
|
74
|
+
registers 'code'
|
|
75
|
+
|
|
76
|
+
def build(inner, _mark)
|
|
77
|
+
CoreModel::MonospaceElement.new(children: Array(inner))
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
class Underline < Base
|
|
82
|
+
registers 'underline'
|
|
83
|
+
|
|
84
|
+
def build(inner, _mark)
|
|
85
|
+
CoreModel::UnderlineElement.new(children: Array(inner))
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
class Strikethrough < Base
|
|
90
|
+
registers 'strike'
|
|
91
|
+
|
|
92
|
+
def build(inner, _mark)
|
|
93
|
+
CoreModel::StrikethroughElement.new(children: Array(inner))
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
class Subscript < Base
|
|
98
|
+
registers 'subscript'
|
|
99
|
+
|
|
100
|
+
def build(inner, _mark)
|
|
101
|
+
CoreModel::SubscriptElement.new(children: Array(inner))
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
class Superscript < Base
|
|
106
|
+
registers 'superscript'
|
|
107
|
+
|
|
108
|
+
def build(inner, _mark)
|
|
109
|
+
CoreModel::SuperscriptElement.new(children: Array(inner))
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
class Highlight < Base
|
|
114
|
+
registers 'highlight'
|
|
115
|
+
|
|
116
|
+
def build(inner, _mark)
|
|
117
|
+
CoreModel::HighlightElement.new(children: Array(inner))
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# ── Marks with attrs ──
|
|
122
|
+
|
|
123
|
+
class Link < Base
|
|
124
|
+
registers 'link'
|
|
125
|
+
|
|
126
|
+
def build(inner, mark)
|
|
127
|
+
CoreModel::LinkElement.new(target: mark.attrs&.href, children: Array(inner))
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
class CrossReference < Base
|
|
132
|
+
registers 'xref'
|
|
133
|
+
|
|
134
|
+
def build(inner, mark)
|
|
135
|
+
CoreModel::CrossReferenceElement.new(
|
|
136
|
+
target: mark.attrs&.target, children: Array(inner)
|
|
137
|
+
)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
5
|
+
module Coradoc
|
|
6
|
+
module Mirror
|
|
7
|
+
# Format module for mirror JSON output.
|
|
8
|
+
#
|
|
9
|
+
# Registers with Coradoc so the CLI can discover it:
|
|
10
|
+
# Coradoc.convert(text, from: :asciidoc, to: :mirror_json)
|
|
11
|
+
# coradoc convert doc.adoc -t mirror_json
|
|
12
|
+
module MirrorJsonFormat
|
|
13
|
+
class << self
|
|
14
|
+
# Output-only format — parsing from mirror JSON is not supported via
|
|
15
|
+
# the format registry. Use Mirror::Node.from_hash directly.
|
|
16
|
+
def parse_to_core(_input, _options = {})
|
|
17
|
+
raise Coradoc::UnsupportedFormatError,
|
|
18
|
+
'Parsing from mirror JSON is not supported via the format registry. ' \
|
|
19
|
+
'Use Coradoc::Mirror::Node.from_hash(JSON.parse(input)) directly.'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Accept CoreModel, serialize to Mirror JSON.
|
|
23
|
+
def serialize(document, options = {})
|
|
24
|
+
pretty = options[:pretty] != false
|
|
25
|
+
node = Coradoc::Mirror.transform(document)
|
|
26
|
+
pretty ? JSON.pretty_generate(node.to_hash) : JSON.generate(node.to_hash)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def serialize?
|
|
30
|
+
true
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def handles_model?(_model)
|
|
34
|
+
false
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
Coradoc.register_format(:mirror_json, Coradoc::Mirror::MirrorJsonFormat,
|
|
42
|
+
extensions: %w[.mirror.json])
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Coradoc
|
|
4
|
+
module Mirror
|
|
5
|
+
# Transforms ProseMirror-compatible Mirror nodes back into CoreModel.
|
|
6
|
+
#
|
|
7
|
+
# Dispatch is delegated to ReverseBuilder (node-level) and
|
|
8
|
+
# MarkReverseBuilder (mark-level) — adding a new node or mark type is
|
|
9
|
+
# done by registering a new Builder class, with no edit to this file
|
|
10
|
+
# (OCP). ReverseBuilder and MarkReverseBuilder are autoloaded from
|
|
11
|
+
# coradoc/mirror.rb; referencing them here triggers load of the
|
|
12
|
+
# registry files, which is where the built-in builders self-register.
|
|
13
|
+
class MirrorToCoreModel
|
|
14
|
+
def call(mirror_node)
|
|
15
|
+
build_node(mirror_node)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def build_node(node)
|
|
19
|
+
builder_class = ReverseBuilder.lookup(node.type)
|
|
20
|
+
raise Error, "Unknown mirror node type: #{node.type}" unless builder_class
|
|
21
|
+
|
|
22
|
+
builder_class.new(self).build(node)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# ── Shared helpers (single source of truth — used by every
|
|
26
|
+
# ReverseBuilder::Base subclass via delegation) ──
|
|
27
|
+
|
|
28
|
+
def build_content(node)
|
|
29
|
+
return [] unless node.content
|
|
30
|
+
|
|
31
|
+
node.content.flat_map do |child|
|
|
32
|
+
result = build_node(child)
|
|
33
|
+
next [] if result.nil?
|
|
34
|
+
|
|
35
|
+
result.is_a?(Array) ? result : [result]
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Mark dispatch goes through MarkReverseBuilder so adding a new
|
|
40
|
+
# mark type is purely additive (OCP parity with node dispatch).
|
|
41
|
+
# Unknown marks pass `inner` through unchanged.
|
|
42
|
+
def apply_mark(inner, mark)
|
|
43
|
+
builder_class = MarkReverseBuilder.lookup(mark.type)
|
|
44
|
+
return inner unless builder_class
|
|
45
|
+
|
|
46
|
+
builder_class.new.build(inner, mark)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def build_inline_children(node)
|
|
50
|
+
return [] unless node.content
|
|
51
|
+
|
|
52
|
+
node.content.filter_map do |child|
|
|
53
|
+
next unless child.is_a?(Node)
|
|
54
|
+
|
|
55
|
+
build_node(child)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def extract_text(node)
|
|
60
|
+
return node.text.to_s if node.is_a?(Node::Text)
|
|
61
|
+
return '' unless node.content
|
|
62
|
+
|
|
63
|
+
node.content.filter_map do |child|
|
|
64
|
+
child.is_a?(Node) ? extract_text(child) : ''
|
|
65
|
+
end.join
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def inline_content(element)
|
|
69
|
+
element.is_a?(CoreModel::InlineElement) ? element.content : element.to_s
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'yaml'
|
|
4
|
+
|
|
5
|
+
module Coradoc
|
|
6
|
+
module Mirror
|
|
7
|
+
# Format module for mirror YAML output.
|
|
8
|
+
#
|
|
9
|
+
# Registers with Coradoc so the CLI can discover it:
|
|
10
|
+
# Coradoc.convert(text, from: :asciidoc, to: :mirror_yaml)
|
|
11
|
+
# coradoc convert doc.adoc -t mirror_yaml
|
|
12
|
+
module MirrorYamlFormat
|
|
13
|
+
class << self
|
|
14
|
+
# Output-only format — parsing from mirror YAML is not supported via
|
|
15
|
+
# the format registry. Use Mirror::Node.from_hash directly.
|
|
16
|
+
def parse_to_core(_input, _options = {})
|
|
17
|
+
raise Coradoc::UnsupportedFormatError,
|
|
18
|
+
'Parsing from mirror YAML is not supported via the format registry. ' \
|
|
19
|
+
'Use Coradoc::Mirror::Node.from_hash(YAML.safe_load(input)) directly.'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Accept CoreModel, serialize to Mirror YAML.
|
|
23
|
+
def serialize(document, _options = {})
|
|
24
|
+
node = Coradoc::Mirror.transform(document)
|
|
25
|
+
YAML.dump(node.to_hash)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def serialize?
|
|
29
|
+
true
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def handles_model?(_model)
|
|
33
|
+
false
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
Coradoc.register_format(:mirror_yaml, Coradoc::Mirror::MirrorYamlFormat,
|
|
41
|
+
extensions: %w[.mirror.yaml .mirror.yml])
|