coradoc-adoc 2.0.16 → 2.0.17
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/asciidoc/builder/element_builder.rb +12 -1
- data/lib/coradoc/asciidoc/model/section.rb +13 -0
- data/lib/coradoc/asciidoc/parser/attribute_list.rb +1 -1
- data/lib/coradoc/asciidoc/parser/block.rb +36 -8
- data/lib/coradoc/asciidoc/parser/citation.rb +8 -0
- data/lib/coradoc/asciidoc/parser/inline.rb +9 -3
- data/lib/coradoc/asciidoc/parser/list.rb +32 -16
- data/lib/coradoc/asciidoc/parser/paragraph.rb +3 -1
- data/lib/coradoc/asciidoc/transform/attribute_list_to_metadata.rb +33 -0
- data/lib/coradoc/asciidoc/transform/element_transformers/block_transformer.rb +57 -30
- data/lib/coradoc/asciidoc/transform/element_transformers/document_transformer.rb +11 -1
- data/lib/coradoc/asciidoc/transform/to_core_model_registrations.rb +17 -3
- data/lib/coradoc/asciidoc/transform.rb +1 -0
- data/lib/coradoc/asciidoc/transformer/list_rules.rb +7 -3
- data/lib/coradoc/asciidoc/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 99b9b654a8eebde5418dfbee6f7b184e65bbbcd830b24f0211963d61ed6c1201
|
|
4
|
+
data.tar.gz: 93e69befaf29a65358e5b6655b7b6cf63d0fb9baab9a828848d7468290c87e3b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1480db996c2768544ae15657c7f394bcaed954c31c79bdf1bfd3f3493b608aad77dc078ed44058ac0964632626585f096a7c96d6047a256ed07f902291ac204a
|
|
7
|
+
data.tar.gz: b3d0d2044b40702ad13d2f500e06e4e5881e865eda9ac0bc3f01fba94c82175b9b1752e3076b05781d7fd03075af7d276b8af55ef18b4be08576ea5018ea07b0
|
|
@@ -21,10 +21,21 @@ module Coradoc
|
|
|
21
21
|
id: section_ast[:id],
|
|
22
22
|
level: extract_level(section_ast),
|
|
23
23
|
children: build_section_contents(section_ast[:contents]) +
|
|
24
|
-
build_subsections(section_ast[:sections])
|
|
24
|
+
build_subsections(section_ast[:sections]),
|
|
25
|
+
attributes: build_section_metadata(section_ast[:attribute_list])
|
|
25
26
|
)
|
|
26
27
|
end
|
|
27
28
|
|
|
29
|
+
# Coerces the AST attribute_list (hash / string / nil) through the
|
|
30
|
+
# normalizer, then delegates to Transform::AttributeListToMetadata
|
|
31
|
+
# for the typed Model::AttributeList -> CoreModel::Metadata step.
|
|
32
|
+
# Single source of truth lives in the transform layer (DRY/MECE).
|
|
33
|
+
def build_section_metadata(attribute_list)
|
|
34
|
+
list = Coradoc::AsciiDoc::Transformer::AttributeListNormalizer
|
|
35
|
+
.coerce(attribute_list)
|
|
36
|
+
Coradoc::AsciiDoc::Transform::AttributeListToMetadata.call(list)
|
|
37
|
+
end
|
|
38
|
+
|
|
28
39
|
def build_section_contents(contents_ast)
|
|
29
40
|
return [] unless contents_ast
|
|
30
41
|
|
|
@@ -58,8 +58,21 @@ module Coradoc
|
|
|
58
58
|
Coradoc::AsciiDoc::Model::Section,
|
|
59
59
|
collection: true,
|
|
60
60
|
initialize_empty: true
|
|
61
|
+
# Block-header attribute list (e.g. `[appendix]`, `[bibliography]`).
|
|
62
|
+
# Single source of truth for the AsciiDoc `[style]` hint; downstream
|
|
63
|
+
# consumers (coradoc-mirror) read `style` from SectionElement#attributes
|
|
64
|
+
# to dispatch JS section types (annex, references, ...).
|
|
65
|
+
attribute :attribute_list, Coradoc::AsciiDoc::Model::AttributeList
|
|
61
66
|
# attribute :anchor, Coradoc::AsciiDoc::Model::Inline::Anchor
|
|
62
67
|
|
|
68
|
+
# Style hint derived from the first positional in `attribute_list`.
|
|
69
|
+
# Returns nil when no list is attached. Kept here so callers don't
|
|
70
|
+
# need to know the AttributeList shape.
|
|
71
|
+
def style
|
|
72
|
+
first_positional = attribute_list&.positional&.first
|
|
73
|
+
first_positional&.value
|
|
74
|
+
end
|
|
75
|
+
|
|
63
76
|
# Allow setting level directly during initialization
|
|
64
77
|
def initialize(**attributes)
|
|
65
78
|
level_value = attributes.delete(:level)
|
|
@@ -139,29 +139,57 @@ module Coradoc
|
|
|
139
139
|
closing_delimiter >> newline
|
|
140
140
|
end
|
|
141
141
|
|
|
142
|
-
# Block style parser with EXACT delimiter length (for open blocks)
|
|
143
|
-
# Open blocks use exactly 2 dashes
|
|
142
|
+
# Block style parser with EXACT delimiter length (for open blocks).
|
|
143
|
+
# Open blocks use exactly 2 dashes. A `[source]`/`[listing]`/`[literal]`
|
|
144
|
+
# positional attribute casts the body to verbatim — block macros like
|
|
145
|
+
# `image::` must survive byte-for-byte, same as delimited source blocks.
|
|
144
146
|
def block_style_exact(n_deep = 3, delimiter = '-', exact_chars = 2)
|
|
145
147
|
capture_key = :"delimit_#{delimiter}_exact_#{exact_chars}_#{n_deep}"
|
|
148
|
+
attr_capture_key = :"#{capture_key}_attrs"
|
|
146
149
|
current_delimiter = str(delimiter).repeat(exact_chars, exact_chars).capture(capture_key)
|
|
147
150
|
closing_delimiter = dynamic do |_s, c|
|
|
148
151
|
str(c.captures[capture_key].to_s.strip)
|
|
149
152
|
end
|
|
150
153
|
|
|
154
|
+
# Closure so the call bypasses Parslet's method_missing inside the
|
|
155
|
+
# dynamic block. capture() stashes the parsed AST (a nested Hash for
|
|
156
|
+
# a `[source,ruby]` header). Inspect the structure directly so we
|
|
157
|
+
# don't depend on Ruby's Hash#to_s format (it changed in 3.4).
|
|
158
|
+
verbatim_cast = lambda do |raw|
|
|
159
|
+
values = []
|
|
160
|
+
queue = [raw]
|
|
161
|
+
while (node = queue.shift)
|
|
162
|
+
case node
|
|
163
|
+
when Hash then queue.concat(node.values)
|
|
164
|
+
when Array then queue.concat(node)
|
|
165
|
+
else values << node
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
castable = %w[source listing literal]
|
|
169
|
+
values.any? { |v| castable.include?(v.to_s) }
|
|
170
|
+
end
|
|
171
|
+
|
|
151
172
|
block_content_with_closing = dynamic do |_s, c|
|
|
152
173
|
delim_str = c.captures[capture_key].to_s.strip
|
|
174
|
+
raw_header = c.captures[attr_capture_key]
|
|
153
175
|
closing_pattern = str(delim_str) >> newline
|
|
154
176
|
|
|
155
|
-
content =
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
177
|
+
content = if verbatim_cast.call(raw_header)
|
|
178
|
+
text_line(false, unguarded: true, verbatim: true) |
|
|
179
|
+
empty_line.as(:line_break)
|
|
180
|
+
else
|
|
181
|
+
alt = block_image
|
|
182
|
+
alt |= block(n_deep - 1) if n_deep.positive?
|
|
183
|
+
alt |= list
|
|
184
|
+
alt |= text_line(false, unguarded: true)
|
|
185
|
+
alt |= empty_line.as(:line_break)
|
|
186
|
+
alt
|
|
187
|
+
end
|
|
160
188
|
|
|
161
189
|
(closing_pattern.absent? >> content).repeat(1)
|
|
162
190
|
end
|
|
163
191
|
|
|
164
|
-
block_header >>
|
|
192
|
+
block_header.capture(attr_capture_key) >>
|
|
165
193
|
line_start? >>
|
|
166
194
|
current_delimiter.as(:delimiter) >> newline >>
|
|
167
195
|
block_content_with_closing.as(:lines) >>
|
|
@@ -14,6 +14,14 @@ module Coradoc
|
|
|
14
14
|
str('>>')
|
|
15
15
|
).as(:cross_reference)
|
|
16
16
|
end
|
|
17
|
+
|
|
18
|
+
# AsciiDoc escape: `\<<` produces the literal text `<<` without
|
|
19
|
+
# firing the cross-reference rule. Without this, documentation that
|
|
20
|
+
# shows AsciiDoc xref syntax as a literal example gets rewritten
|
|
21
|
+
# into a broken link to a non-existent anchor.
|
|
22
|
+
def escaped_xref
|
|
23
|
+
str('\\') >> str('<<').as(:text)
|
|
24
|
+
end
|
|
17
25
|
end
|
|
18
26
|
end
|
|
19
27
|
end
|
|
@@ -158,7 +158,8 @@ module Coradoc
|
|
|
158
158
|
str('+++').present? |
|
|
159
159
|
term_type.present? |
|
|
160
160
|
str('footnote').present? |
|
|
161
|
-
stem_type.present?
|
|
161
|
+
stem_type.present? |
|
|
162
|
+
str('\\<<').present?
|
|
162
163
|
end
|
|
163
164
|
|
|
164
165
|
def inline
|
|
@@ -175,6 +176,7 @@ module Coradoc
|
|
|
175
176
|
superscript |
|
|
176
177
|
subscript |
|
|
177
178
|
attribute_reference |
|
|
179
|
+
escaped_xref |
|
|
178
180
|
cross_reference |
|
|
179
181
|
term_inline |
|
|
180
182
|
term_inline2 |
|
|
@@ -188,8 +190,12 @@ module Coradoc
|
|
|
188
190
|
end
|
|
189
191
|
|
|
190
192
|
def text_unformatted
|
|
191
|
-
#
|
|
192
|
-
(
|
|
193
|
+
# `str('\\<<').absent?` stops text from consuming the backslash of
|
|
194
|
+
# an escaped xref (`\<<`). Without this guard, the `\` is taken as
|
|
195
|
+
# plain text, the `<<` re-enters cross_reference, and the literal
|
|
196
|
+
# escape is destroyed.
|
|
197
|
+
(str('\\<<').absent? >>
|
|
198
|
+
inline.absent? >>
|
|
193
199
|
match("[^\n]")
|
|
194
200
|
).repeat(1)
|
|
195
201
|
end
|
|
@@ -52,21 +52,19 @@ module Coradoc
|
|
|
52
52
|
|
|
53
53
|
def olist_item(nesting_level = 1)
|
|
54
54
|
item = olist_marker(nesting_level).as(:marker) >>
|
|
55
|
-
match("\n").absent? >> space >>
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
# paragraph #| example_block(n_deep: 1)
|
|
59
|
-
# ).repeat(0).as(:attached)
|
|
55
|
+
match("\n").absent? >> space >>
|
|
56
|
+
(text_line(true, unguarded: true) >>
|
|
57
|
+
list_item_continuation_lines).as(:lines)
|
|
60
58
|
|
|
61
59
|
att = (list_continuation.present? >>
|
|
62
60
|
list_continuation >>
|
|
63
|
-
(admonition_line | paragraph | block)
|
|
61
|
+
(admonition_line | paragraph | block)
|
|
64
62
|
).repeat(0).as(:attached)
|
|
65
63
|
item >>= att.maybe
|
|
66
64
|
|
|
67
65
|
if nesting_level <= 4
|
|
68
66
|
item >>= (list_marker(nesting_level + 1).present? >>
|
|
69
|
-
list(nesting_level + 1)).repeat(0).as(:nested)
|
|
67
|
+
list(nesting_level + 1)).repeat(0).as(:nested)
|
|
70
68
|
end
|
|
71
69
|
olist_marker(nesting_level).present? >> item.as(:list_item)
|
|
72
70
|
end
|
|
@@ -82,21 +80,33 @@ module Coradoc
|
|
|
82
80
|
def ulist_item(nesting_level = 1)
|
|
83
81
|
item = ulist_marker(nesting_level).as(:marker) >>
|
|
84
82
|
str(' [[[').absent? >>
|
|
85
|
-
match("\n").absent? >> space >>
|
|
83
|
+
match("\n").absent? >> space >>
|
|
84
|
+
(text_line(true, unguarded: true) >>
|
|
85
|
+
list_item_continuation_lines).as(:lines)
|
|
86
86
|
|
|
87
87
|
att = (list_continuation.present? >>
|
|
88
88
|
list_continuation >>
|
|
89
|
-
(admonition_line | paragraph | block)
|
|
89
|
+
(admonition_line | paragraph | block)
|
|
90
90
|
).repeat(0).as(:attached)
|
|
91
91
|
item >>= att.maybe
|
|
92
92
|
|
|
93
93
|
if nesting_level <= 4
|
|
94
94
|
item >>= (list_marker(nesting_level + 1).present? >>
|
|
95
|
-
list(nesting_level + 1)).repeat(0).as(:nested)
|
|
95
|
+
list(nesting_level + 1)).repeat(0).as(:nested)
|
|
96
96
|
end
|
|
97
97
|
ulist_marker(nesting_level).present? >> item.as(:list_item)
|
|
98
98
|
end
|
|
99
99
|
|
|
100
|
+
# Continuation lines of a list item's first paragraph. AsciiDoc
|
|
101
|
+
# joins consecutive non-blank lines into one paragraph within the
|
|
102
|
+
# item, but the lines must not start a sibling construct (another
|
|
103
|
+
# list marker, block delimiter, attribute list, section, element
|
|
104
|
+
# id, table boundary, list continuation, or list prefix).
|
|
105
|
+
# `line_not_text?` (from Paragraph) is that exact lookahead.
|
|
106
|
+
def list_item_continuation_lines
|
|
107
|
+
(line_not_text? >> text_line(true, unguarded: true)).repeat(0)
|
|
108
|
+
end
|
|
109
|
+
|
|
100
110
|
def dlist_delimiter
|
|
101
111
|
(
|
|
102
112
|
(str(':::::') >> match(':').absent?) |
|
|
@@ -120,12 +130,18 @@ module Coradoc
|
|
|
120
130
|
end
|
|
121
131
|
|
|
122
132
|
def dlist_item(_delimiter = nil)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
)
|
|
133
|
+
# Both forms below produce the same AST shape:
|
|
134
|
+
# {terms: [<dlist_term>, ...], definition: <text>}
|
|
135
|
+
# so the transformer's definition_list_item rule matches uniformly.
|
|
136
|
+
#
|
|
137
|
+
# Multi-line form: one or more term-lines (`term::` + newline +
|
|
138
|
+
# optional blank lines), then the definition on its own line(s).
|
|
139
|
+
# Single-line form: one term + inline space + definition.
|
|
140
|
+
term_line = dlist_term >> line_ending >> empty_line.repeat(0)
|
|
141
|
+
|
|
142
|
+
((term_line.repeat(1).as(:terms) >> dlist_definition) |
|
|
143
|
+
(dlist_term.repeat(1, 1).as(:terms) >> space >>
|
|
144
|
+
dlist_definition)).as(:definition_list_item)
|
|
129
145
|
end
|
|
130
146
|
end
|
|
131
147
|
end
|
|
@@ -13,7 +13,9 @@ module Coradoc
|
|
|
13
13
|
list_prefix.absent? >>
|
|
14
14
|
list_continuation.absent? >>
|
|
15
15
|
element_id.absent? >>
|
|
16
|
-
section_prefix.absent?
|
|
16
|
+
section_prefix.absent? >>
|
|
17
|
+
comment_line.absent? >>
|
|
18
|
+
tag.absent?
|
|
17
19
|
end
|
|
18
20
|
|
|
19
21
|
# NOTE: many_breaks parameter has three states for different parsing contexts:
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Coradoc
|
|
4
|
+
module AsciiDoc
|
|
5
|
+
module Transform
|
|
6
|
+
# Single source of truth for AsciiDoc Model::AttributeList ->
|
|
7
|
+
# CoreModel::Metadata conversion (DRY/MECE).
|
|
8
|
+
#
|
|
9
|
+
# Promotes the first positional attribute to `style` and the
|
|
10
|
+
# `role=` named attribute to `role`, matching the AsciiDoc
|
|
11
|
+
# block-header semantics that downstream consumers (coradoc-mirror)
|
|
12
|
+
# dispatch on to pick a JS section type (annex, abstract, ...).
|
|
13
|
+
#
|
|
14
|
+
# Returns nil for anything that isn't a typed AttributeList so
|
|
15
|
+
# callers can pass through optional/missing inputs without an
|
|
16
|
+
# extra guard.
|
|
17
|
+
module AttributeListToMetadata
|
|
18
|
+
module_function
|
|
19
|
+
|
|
20
|
+
def call(list)
|
|
21
|
+
return nil unless list.is_a?(Coradoc::AsciiDoc::Model::AttributeList)
|
|
22
|
+
|
|
23
|
+
metadata = Coradoc::CoreModel::Metadata.new
|
|
24
|
+
first_positional = list.positional.first
|
|
25
|
+
metadata['style'] = first_positional.value if first_positional
|
|
26
|
+
named_role = list.named.find { |n| n.name == 'role' }
|
|
27
|
+
metadata['role'] = named_role.value.first if named_role&.value&.any?
|
|
28
|
+
metadata
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -16,7 +16,12 @@ module Coradoc
|
|
|
16
16
|
)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
# Verbatim blocks (source, listing, literal, pass) must round-trip
|
|
20
|
+
# their body byte-for-byte. Each source line becomes one content
|
|
21
|
+
# line; we join with "\n" so whitespace, indentation, and blank
|
|
22
|
+
# lines are preserved. Treating these as paragraphs would collapse
|
|
23
|
+
# whitespace and join consecutive lines into a single flowing text.
|
|
24
|
+
def transform_verbatim_block(block, klass)
|
|
20
25
|
non_break_lines = Array(block.lines).reject do |line|
|
|
21
26
|
line.is_a?(Coradoc::AsciiDoc::Model::LineBreak) ||
|
|
22
27
|
line.is_a?(Coradoc::AsciiDoc::Model::Break::PageBreak)
|
|
@@ -25,16 +30,26 @@ module Coradoc
|
|
|
25
30
|
ToCoreModel.extract_text_content(line)
|
|
26
31
|
end.join("\n")
|
|
27
32
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
Coradoc::CoreModel::SourceBlock.new(
|
|
33
|
+
klass.new(
|
|
31
34
|
id: block.id,
|
|
32
35
|
title: ToCoreModel.extract_title_text(block.title),
|
|
33
36
|
content: content_lines,
|
|
34
|
-
language:
|
|
37
|
+
language: ToCoreModel.extract_block_language(block)
|
|
35
38
|
)
|
|
36
39
|
end
|
|
37
40
|
|
|
41
|
+
def transform_source_block(block)
|
|
42
|
+
transform_verbatim_block(block, Coradoc::CoreModel::SourceBlock)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def transform_literal_block(block)
|
|
46
|
+
transform_verbatim_block(block, Coradoc::CoreModel::LiteralBlock)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def transform_pass_block(block)
|
|
50
|
+
transform_verbatim_block(block, Coradoc::CoreModel::PassBlock)
|
|
51
|
+
end
|
|
52
|
+
|
|
38
53
|
# Open blocks (`--`) are generic containers. AsciiDoc allows
|
|
39
54
|
# casting them to a different block type via positional
|
|
40
55
|
# attributes: verbatim types (`[source]`, `[listing]`,
|
|
@@ -86,22 +101,11 @@ module Coradoc
|
|
|
86
101
|
end
|
|
87
102
|
|
|
88
103
|
def transform_listing_from_open(block)
|
|
89
|
-
|
|
90
|
-
Coradoc::CoreModel::ListingBlock.new(
|
|
91
|
-
id: block.id,
|
|
92
|
-
title: ToCoreModel.extract_title_text(block.title),
|
|
93
|
-
content: content_lines,
|
|
94
|
-
language: ToCoreModel.extract_block_language(block)
|
|
95
|
-
)
|
|
104
|
+
transform_verbatim_block(block, Coradoc::CoreModel::ListingBlock)
|
|
96
105
|
end
|
|
97
106
|
|
|
98
107
|
def transform_literal_from_open(block)
|
|
99
|
-
|
|
100
|
-
Coradoc::CoreModel::LiteralBlock.new(
|
|
101
|
-
id: block.id,
|
|
102
|
-
title: ToCoreModel.extract_title_text(block.title),
|
|
103
|
-
content: content_lines
|
|
104
|
-
)
|
|
108
|
+
transform_verbatim_block(block, Coradoc::CoreModel::LiteralBlock)
|
|
105
109
|
end
|
|
106
110
|
|
|
107
111
|
def transform_block(block, semantic_type_or_delimiter)
|
|
@@ -123,15 +127,14 @@ module Coradoc
|
|
|
123
127
|
end
|
|
124
128
|
|
|
125
129
|
def transform_typed_block(block, klass, extra_attrs = {})
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
line.is_a?(Coradoc::AsciiDoc::Model::Break::PageBreak)
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
has_nested_blocks = lines.any?(Coradoc::AsciiDoc::Model::Block::Core)
|
|
130
|
+
raw_lines = Array(block.lines)
|
|
131
|
+
has_nested_blocks = raw_lines.any?(Coradoc::AsciiDoc::Model::Block::Core)
|
|
132
132
|
|
|
133
133
|
if has_nested_blocks
|
|
134
|
-
children =
|
|
134
|
+
children = raw_lines.reject do |line|
|
|
135
|
+
line.is_a?(Coradoc::AsciiDoc::Model::LineBreak) ||
|
|
136
|
+
line.is_a?(Coradoc::AsciiDoc::Model::Break::PageBreak)
|
|
137
|
+
end.filter_map do |line|
|
|
135
138
|
result = ToCoreModel.transform(line)
|
|
136
139
|
next nil if result.nil?
|
|
137
140
|
next result if result.is_a?(Coradoc::CoreModel::Base)
|
|
@@ -149,15 +152,21 @@ module Coradoc
|
|
|
149
152
|
**extra_attrs
|
|
150
153
|
)
|
|
151
154
|
else
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
155
|
+
paragraph_groups = group_block_lines_into_paragraphs(raw_lines)
|
|
156
|
+
|
|
157
|
+
content_lines = paragraph_groups.map do |group|
|
|
158
|
+
ToCoreModel.extract_text_content(group)
|
|
159
|
+
end.join("\n\n")
|
|
160
|
+
|
|
161
|
+
children = paragraph_groups.map do |group|
|
|
162
|
+
inline = ToCoreModel.transform_inline_content(group)
|
|
163
|
+
inline = [Coradoc::CoreModel::TextContent.new(text: '')] if inline.empty?
|
|
156
164
|
Coradoc::CoreModel::ParagraphBlock.new(
|
|
157
|
-
content: ToCoreModel.extract_text_content(
|
|
165
|
+
content: ToCoreModel.extract_text_content(group),
|
|
158
166
|
children: Array(inline)
|
|
159
167
|
)
|
|
160
168
|
end
|
|
169
|
+
|
|
161
170
|
klass.new(
|
|
162
171
|
id: block.id,
|
|
163
172
|
title: ToCoreModel.extract_title_text(block.title),
|
|
@@ -168,6 +177,24 @@ module Coradoc
|
|
|
168
177
|
)
|
|
169
178
|
end
|
|
170
179
|
end
|
|
180
|
+
|
|
181
|
+
# AsciiDoc joins consecutive non-blank lines into one paragraph;
|
|
182
|
+
# blank lines (parsed as Model::LineBreak) separate paragraphs.
|
|
183
|
+
def group_block_lines_into_paragraphs(lines)
|
|
184
|
+
groups = []
|
|
185
|
+
current = []
|
|
186
|
+
lines.each do |line|
|
|
187
|
+
if line.is_a?(Coradoc::AsciiDoc::Model::LineBreak) ||
|
|
188
|
+
line.is_a?(Coradoc::AsciiDoc::Model::Break::PageBreak)
|
|
189
|
+
groups << current if current.any?
|
|
190
|
+
current = []
|
|
191
|
+
else
|
|
192
|
+
current << line
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
groups << current if current.any?
|
|
196
|
+
groups
|
|
197
|
+
end
|
|
171
198
|
end
|
|
172
199
|
end
|
|
173
200
|
end
|
|
@@ -48,9 +48,19 @@ module Coradoc
|
|
|
48
48
|
id: section_id,
|
|
49
49
|
level: section.level,
|
|
50
50
|
title: title_text,
|
|
51
|
-
children: content_children + nested_sections
|
|
51
|
+
children: content_children + nested_sections,
|
|
52
|
+
attributes: section_metadata_from(section)
|
|
52
53
|
)
|
|
53
54
|
end
|
|
55
|
+
|
|
56
|
+
# Delegates to Transform::AttributeListToMetadata for the typed
|
|
57
|
+
# Model::AttributeList -> CoreModel::Metadata conversion. The
|
|
58
|
+
# helper handles the nil/non-AttributeList guard and is the
|
|
59
|
+
# single source of truth shared with Builder::ElementBuilder.
|
|
60
|
+
def section_metadata_from(section)
|
|
61
|
+
Coradoc::AsciiDoc::Transform::AttributeListToMetadata
|
|
62
|
+
.call(section.attribute_list)
|
|
63
|
+
end
|
|
54
64
|
end
|
|
55
65
|
end
|
|
56
66
|
end
|
|
@@ -50,9 +50,7 @@ module Coradoc
|
|
|
50
50
|
{
|
|
51
51
|
Coradoc::AsciiDoc::Model::Block::Quote => Coradoc::CoreModel::QuoteBlock,
|
|
52
52
|
Coradoc::AsciiDoc::Model::Block::Example => Coradoc::CoreModel::ExampleBlock,
|
|
53
|
-
Coradoc::AsciiDoc::Model::Block::Side => Coradoc::CoreModel::SidebarBlock
|
|
54
|
-
Coradoc::AsciiDoc::Model::Block::Literal => Coradoc::CoreModel::LiteralBlock,
|
|
55
|
-
Coradoc::AsciiDoc::Model::Block::Pass => Coradoc::CoreModel::PassBlock
|
|
53
|
+
Coradoc::AsciiDoc::Model::Block::Side => Coradoc::CoreModel::SidebarBlock
|
|
56
54
|
}.each do |block_class, core_model_class|
|
|
57
55
|
Registry.register_with_priority(
|
|
58
56
|
block_class,
|
|
@@ -61,6 +59,22 @@ module Coradoc
|
|
|
61
59
|
)
|
|
62
60
|
end
|
|
63
61
|
|
|
62
|
+
# Verbatim typed blocks (literal, pass) preserve their body
|
|
63
|
+
# byte-for-byte — same line-joining strategy as source/listing.
|
|
64
|
+
# Routing them through `transform_typed_block` would collapse
|
|
65
|
+
# intra-block whitespace and join consecutive lines as paragraphs.
|
|
66
|
+
Registry.register_with_priority(
|
|
67
|
+
Coradoc::AsciiDoc::Model::Block::Literal,
|
|
68
|
+
->(model) { Blk.transform_literal_block(model) },
|
|
69
|
+
priority: 10
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
Registry.register_with_priority(
|
|
73
|
+
Coradoc::AsciiDoc::Model::Block::Pass,
|
|
74
|
+
->(model) { Blk.transform_pass_block(model) },
|
|
75
|
+
priority: 10
|
|
76
|
+
)
|
|
77
|
+
|
|
64
78
|
Registry.register_with_priority(
|
|
65
79
|
Coradoc::AsciiDoc::Model::Block::Open,
|
|
66
80
|
->(model) { Blk.transform_open_block(model) },
|
|
@@ -14,6 +14,7 @@ module Coradoc
|
|
|
14
14
|
autoload :ElementTransformers, "#{__dir__}/transform/element_transformers"
|
|
15
15
|
autoload :FrontmatterAttributeMap, "#{__dir__}/transform/frontmatter_attribute_map"
|
|
16
16
|
autoload :CalloutMerger, "#{__dir__}/transform/callout_merger"
|
|
17
|
+
autoload :AttributeListToMetadata, "#{__dir__}/transform/attribute_list_to_metadata"
|
|
17
18
|
end
|
|
18
19
|
end
|
|
19
20
|
end
|
|
@@ -47,8 +47,12 @@ module Coradoc
|
|
|
47
47
|
rule(list_item: subtree(:list_item)) do
|
|
48
48
|
marker = list_item[:marker]
|
|
49
49
|
id = list_item[:id]
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
lines = list_item[:lines]
|
|
51
|
+
content = if lines
|
|
52
|
+
Transformer.lines_to_text_elements(lines)
|
|
53
|
+
else
|
|
54
|
+
list_item[:text].to_s
|
|
55
|
+
end
|
|
52
56
|
attached = list_item[:attached]
|
|
53
57
|
nested = list_item[:nested]
|
|
54
58
|
line_break = list_item[:line_break]
|
|
@@ -70,7 +74,7 @@ module Coradoc
|
|
|
70
74
|
end
|
|
71
75
|
|
|
72
76
|
Model::List::Item.new(
|
|
73
|
-
content:
|
|
77
|
+
content: content, id:, marker:, attached:, nested:, line_break:
|
|
74
78
|
)
|
|
75
79
|
end
|
|
76
80
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: coradoc-adoc
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0.
|
|
4
|
+
version: 2.0.17
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -314,6 +314,7 @@ files:
|
|
|
314
314
|
- lib/coradoc/asciidoc/serializer/serializers/video.rb
|
|
315
315
|
- lib/coradoc/asciidoc/serializer/spacing_strategy.rb
|
|
316
316
|
- lib/coradoc/asciidoc/transform.rb
|
|
317
|
+
- lib/coradoc/asciidoc/transform/attribute_list_to_metadata.rb
|
|
317
318
|
- lib/coradoc/asciidoc/transform/callout_merger.rb
|
|
318
319
|
- lib/coradoc/asciidoc/transform/element_transformers.rb
|
|
319
320
|
- lib/coradoc/asciidoc/transform/element_transformers/block_transformer.rb
|