coradoc-adoc 2.0.19 → 2.0.20
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/parser/block.rb +16 -14
- data/lib/coradoc/asciidoc/transform/element_transformers/admonition_styles.rb +50 -0
- data/lib/coradoc/asciidoc/transform/element_transformers/block_transformer.rb +71 -14
- data/lib/coradoc/asciidoc/transform/element_transformers/document_transformer.rb +26 -0
- data/lib/coradoc/asciidoc/transform/element_transformers.rb +1 -0
- data/lib/coradoc/asciidoc/transform/from_core_model.rb +29 -2
- data/lib/coradoc/asciidoc/transform/to_core_model_registrations.rb +10 -5
- 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: fad1b11f8028b7157066b7a2d4474b2d20e3cab1e526e0347ca36015cdf3ad90
|
|
4
|
+
data.tar.gz: d143bb70ec7ab2e3a1656a5e9ed72d52bb0b2f1341b3566efa02ef44343a2044
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7848b8f80f104e960265ed5dbd295d4dfc7ad4cecb401b81765ae21ba6d16a31e02024ebfd7c65c1c86e0fd433512788a92ab4600b348293d895980bf2b5b446
|
|
7
|
+
data.tar.gz: c6a55d7b4abecd086fcbde8015fd54b217eb3a442f86699bd1ab110affc1f1dc20f9ff54ef24668dcc77fdcb1cf609b09651308bc4122ea47a05535e18585879
|
|
@@ -105,12 +105,24 @@ module Coradoc
|
|
|
105
105
|
end
|
|
106
106
|
|
|
107
107
|
def block_content(n_deep = 3)
|
|
108
|
-
|
|
108
|
+
block_container_content(n_deep).repeat(1)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Single source of truth for "what can appear inside a non-verbatim
|
|
112
|
+
# block container". Used by block_content, block_style (non-verbatim
|
|
113
|
+
# branch), and block_style_exact (non-verbatim branch).
|
|
114
|
+
#
|
|
115
|
+
# `table` is listed before `text_line` so a leading `|===` is parsed
|
|
116
|
+
# as a table rather than mis-bracketed as text. The recursive `block`
|
|
117
|
+
# alternative handles every other delimited block.
|
|
118
|
+
def block_container_content(n_deep)
|
|
119
|
+
c = table.as(:table)
|
|
120
|
+
c |= block_image
|
|
109
121
|
c |= block(n_deep - 1) if n_deep.positive?
|
|
110
122
|
c |= list
|
|
111
123
|
c |= text_line(false, unguarded: true)
|
|
112
124
|
c |= empty_line.as(:line_break)
|
|
113
|
-
c
|
|
125
|
+
c
|
|
114
126
|
end
|
|
115
127
|
|
|
116
128
|
# Block delimiter: 4+ identical characters, or 2 dashes for open
|
|
@@ -155,12 +167,7 @@ module Coradoc
|
|
|
155
167
|
text_line(false, unguarded: true, verbatim: true) |
|
|
156
168
|
empty_line.as(:line_break)
|
|
157
169
|
else
|
|
158
|
-
|
|
159
|
-
c |= block(n_deep - 1) if n_deep.positive?
|
|
160
|
-
c |= list
|
|
161
|
-
c |= text_line(false, unguarded: true)
|
|
162
|
-
c |= empty_line.as(:line_break)
|
|
163
|
-
c
|
|
170
|
+
block_container_content(n_deep)
|
|
164
171
|
end
|
|
165
172
|
|
|
166
173
|
(closing_pattern.absent? >> content).repeat(1)
|
|
@@ -213,12 +220,7 @@ module Coradoc
|
|
|
213
220
|
text_line(false, unguarded: true, verbatim: true) |
|
|
214
221
|
empty_line.as(:line_break)
|
|
215
222
|
else
|
|
216
|
-
|
|
217
|
-
alt |= block(n_deep - 1) if n_deep.positive?
|
|
218
|
-
alt |= list
|
|
219
|
-
alt |= text_line(false, unguarded: true)
|
|
220
|
-
alt |= empty_line.as(:line_break)
|
|
221
|
-
alt
|
|
223
|
+
block_container_content(n_deep)
|
|
222
224
|
end
|
|
223
225
|
|
|
224
226
|
(closing_pattern.absent? >> content).repeat(1)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Coradoc
|
|
4
|
+
module AsciiDoc
|
|
5
|
+
module Transform
|
|
6
|
+
module ElementTransformers
|
|
7
|
+
# Admonition style registry. Single source of truth for "which
|
|
8
|
+
# positional attribute names map to an admonition block."
|
|
9
|
+
#
|
|
10
|
+
# Built-in AsciiDoc admonition styles are always recognized. Callers
|
|
11
|
+
# can register additional styles (e.g. +DANGER+, +SAFETY+) without
|
|
12
|
+
# modifying dispatch code — the registry is consulted by every
|
|
13
|
+
# block-form transformer that needs to decide between an admonition
|
|
14
|
+
# and the block's native type.
|
|
15
|
+
module AdmonitionStyles
|
|
16
|
+
BUILTIN = %w[note tip warning caution important editor todo].freeze
|
|
17
|
+
|
|
18
|
+
@custom = []
|
|
19
|
+
|
|
20
|
+
class << self
|
|
21
|
+
# True if +style+ (case-insensitive) is a known admonition style.
|
|
22
|
+
def admonition?(style)
|
|
23
|
+
return false if style.nil?
|
|
24
|
+
|
|
25
|
+
name = style.to_s.downcase
|
|
26
|
+
BUILTIN.include?(name) || custom.include?(name)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Register an additional admonition style. Open for extension.
|
|
30
|
+
def register(style)
|
|
31
|
+
name = style.to_s.downcase
|
|
32
|
+
@custom << name unless @custom.include?(name) || BUILTIN.include?(name)
|
|
33
|
+
self
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Reset custom registrations. Intended for specs.
|
|
37
|
+
def reset!
|
|
38
|
+
@custom = []
|
|
39
|
+
self
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def custom
|
|
43
|
+
@custom.dup
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -21,7 +21,7 @@ module Coradoc
|
|
|
21
21
|
# line; we join with "\n" so whitespace, indentation, and blank
|
|
22
22
|
# lines are preserved. Treating these as paragraphs would collapse
|
|
23
23
|
# whitespace and join consecutive lines into a single flowing text.
|
|
24
|
-
def transform_verbatim_block(block, klass)
|
|
24
|
+
def transform_verbatim_block(block, klass, language_override: nil)
|
|
25
25
|
non_break_lines = Array(block.lines).reject do |line|
|
|
26
26
|
line.is_a?(Coradoc::AsciiDoc::Model::LineBreak) ||
|
|
27
27
|
line.is_a?(Coradoc::AsciiDoc::Model::Break::PageBreak)
|
|
@@ -34,7 +34,7 @@ module Coradoc
|
|
|
34
34
|
id: block.id,
|
|
35
35
|
title: ToCoreModel.extract_title_text(block.title),
|
|
36
36
|
content: content_lines,
|
|
37
|
-
language: ToCoreModel.extract_block_language(block)
|
|
37
|
+
language: language_override || ToCoreModel.extract_block_language(block)
|
|
38
38
|
)
|
|
39
39
|
end
|
|
40
40
|
|
|
@@ -47,9 +47,54 @@ module Coradoc
|
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def transform_pass_block(block)
|
|
50
|
+
style = first_positional_attr(block)
|
|
51
|
+
return transform_stem_block(block, style) if STEM_STYLES.key?(style.to_s.downcase)
|
|
52
|
+
|
|
50
53
|
transform_verbatim_block(block, Coradoc::CoreModel::PassBlock)
|
|
51
54
|
end
|
|
52
55
|
|
|
56
|
+
# AsciiDoc recognizes three STEM block styles. The default
|
|
57
|
+
# `[stem]` resolves to LaTeX; the other two name their
|
|
58
|
+
# interpreter explicitly. Single source of truth for the
|
|
59
|
+
# style→language mapping.
|
|
60
|
+
STEM_STYLES = {
|
|
61
|
+
'stem' => 'latex',
|
|
62
|
+
'latexmath' => 'latex',
|
|
63
|
+
'asciimath' => 'asciimath'
|
|
64
|
+
}.freeze
|
|
65
|
+
|
|
66
|
+
def transform_stem_block(block, style)
|
|
67
|
+
language = STEM_STYLES.fetch(style.to_s.downcase, 'latex')
|
|
68
|
+
transform_verbatim_block(block, Coradoc::CoreModel::StemBlock,
|
|
69
|
+
language_override: language)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Dispatch entry point for any block whose AsciiDoc model carries
|
|
73
|
+
# an attribute list (delimited blocks + open blocks). Per the
|
|
74
|
+
# AsciiDoc spec, every delimited block accepts admonition styles
|
|
75
|
+
# in its attribute line. Admonition style wins for example,
|
|
76
|
+
# sidebar, quote, and open blocks; verbatim blocks (source,
|
|
77
|
+
# listing) intentionally ignore it because their verbatim
|
|
78
|
+
# semantics are stronger than the annotation label.
|
|
79
|
+
def transform_with_admonition_check(block, native_class)
|
|
80
|
+
style = first_positional_attr(block)
|
|
81
|
+
return transform_admonition_block(block, style) if AdmonitionStyles.admonition?(style)
|
|
82
|
+
|
|
83
|
+
transform_typed_block(block, native_class)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def transform_example_block(block)
|
|
87
|
+
transform_with_admonition_check(block, Coradoc::CoreModel::ExampleBlock)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def transform_sidebar_block(block)
|
|
91
|
+
transform_with_admonition_check(block, Coradoc::CoreModel::SidebarBlock)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def transform_quote_block(block)
|
|
95
|
+
transform_with_admonition_check(block, Coradoc::CoreModel::QuoteBlock)
|
|
96
|
+
end
|
|
97
|
+
|
|
53
98
|
# Open blocks (`--`) are generic containers. AsciiDoc allows
|
|
54
99
|
# casting them to a different block type via positional
|
|
55
100
|
# attributes: verbatim types (`[source]`, `[listing]`,
|
|
@@ -60,41 +105,53 @@ module Coradoc
|
|
|
60
105
|
def transform_open_block(block)
|
|
61
106
|
semantic = open_block_semantic(block)
|
|
62
107
|
case semantic
|
|
63
|
-
when :
|
|
108
|
+
when :source
|
|
64
109
|
transform_source_block(block)
|
|
65
110
|
when :listing
|
|
66
111
|
transform_listing_from_open(block)
|
|
67
112
|
when :literal
|
|
68
113
|
transform_literal_from_open(block)
|
|
69
114
|
when :admonition
|
|
70
|
-
|
|
115
|
+
transform_admonition_block(block, first_positional_attr(block))
|
|
71
116
|
else
|
|
72
117
|
transform_typed_block(block, Coradoc::CoreModel::OpenBlock)
|
|
73
118
|
end
|
|
74
119
|
end
|
|
75
120
|
|
|
76
|
-
|
|
121
|
+
VERBATILE_CAST_STYLES = %w[source listing literal].freeze
|
|
77
122
|
|
|
78
123
|
def open_block_semantic(block)
|
|
124
|
+
style = first_positional_attr(block)
|
|
125
|
+
return nil unless style
|
|
126
|
+
|
|
127
|
+
case style.to_s.downcase
|
|
128
|
+
when *VERBATILE_CAST_STYLES then :"#{style.downcase}"
|
|
129
|
+
else :admonition if AdmonitionStyles.admonition?(style)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Returns the first positional attribute on the block's
|
|
134
|
+
# attribute list as a String, or nil if absent. Centralizes the
|
|
135
|
+
# shape-walking that was previously inlined in
|
|
136
|
+
# `transform_admonition_from_open` and `open_block_semantic`.
|
|
137
|
+
def first_positional_attr(block)
|
|
79
138
|
attrs = block.attributes
|
|
80
139
|
return nil unless attrs.is_a?(Coradoc::AsciiDoc::Model::AttributeList)
|
|
81
140
|
|
|
82
141
|
first = attrs.positional&.first
|
|
83
142
|
return nil unless first.is_a?(Coradoc::AsciiDoc::Model::AttributeListAttribute)
|
|
84
143
|
|
|
85
|
-
|
|
86
|
-
when 'source' then :source_code
|
|
87
|
-
when 'listing' then :listing
|
|
88
|
-
when 'literal' then :literal
|
|
89
|
-
when *ADMONITION_TYPES then :admonition
|
|
90
|
-
end
|
|
144
|
+
first.value.to_s
|
|
91
145
|
end
|
|
92
146
|
|
|
93
|
-
|
|
94
|
-
|
|
147
|
+
# Single admonition dispatch used by every block-form path
|
|
148
|
+
# (example / sidebar / quote / pass / open). Builds an
|
|
149
|
+
# AnnotationBlock with annotation_type set from the style and
|
|
150
|
+
# the block's body lines joined into a single content string.
|
|
151
|
+
def transform_admonition_block(block, type)
|
|
95
152
|
content_lines = Array(block.lines).map { |line| ToCoreModel.extract_text_content(line) }.join("\n")
|
|
96
153
|
Coradoc::CoreModel::AnnotationBlock.new(
|
|
97
|
-
annotation_type: type,
|
|
154
|
+
annotation_type: type.to_s.downcase,
|
|
98
155
|
content: content_lines,
|
|
99
156
|
title: ToCoreModel.extract_title_text(block.title)
|
|
100
157
|
)
|
|
@@ -12,6 +12,7 @@ module Coradoc
|
|
|
12
12
|
children = ToCoreModel.transform(doc.sections || doc.contents || [])
|
|
13
13
|
children = CalloutMerger.call(children)
|
|
14
14
|
children = prepend_frontmatter(children, doc.frontmatter)
|
|
15
|
+
children = insert_title_heading_after_frontmatter(children, title_text)
|
|
15
16
|
|
|
16
17
|
Coradoc::CoreModel::DocumentElement.new(
|
|
17
18
|
id: doc.id,
|
|
@@ -32,6 +33,31 @@ module Coradoc
|
|
|
32
33
|
[block, *children]
|
|
33
34
|
end
|
|
34
35
|
|
|
36
|
+
# Per the AsciiDoc spec, the document title (`= Title`) is the
|
|
37
|
+
# document's level-0 heading. Asciidoctor renders it as an
|
|
38
|
+
# <h1> at the top of the body by default. Emit a HeaderElement
|
|
39
|
+
# at level 0 so consumers that walk the body's children see the
|
|
40
|
+
# title (the standard ProseMirror/HTML rendering pattern)
|
|
41
|
+
# instead of having to read Document.title separately.
|
|
42
|
+
#
|
|
43
|
+
# The title attribute on DocumentElement is preserved for
|
|
44
|
+
# consumers that read it directly. The title heading is placed
|
|
45
|
+
# after any FrontmatterBlock (frontmatter is metadata that
|
|
46
|
+
# precedes the body).
|
|
47
|
+
def insert_title_heading_after_frontmatter(children, title_text)
|
|
48
|
+
return children if title_text.nil? || title_text.strip.empty?
|
|
49
|
+
|
|
50
|
+
title_heading = Coradoc::CoreModel::HeaderElement.new(
|
|
51
|
+
level: 0,
|
|
52
|
+
title: title_text,
|
|
53
|
+
content: title_text
|
|
54
|
+
)
|
|
55
|
+
frontmatter_count = children.count do |child|
|
|
56
|
+
child.is_a?(Coradoc::CoreModel::FrontmatterBlock)
|
|
57
|
+
end
|
|
58
|
+
children.insert(frontmatter_count, title_heading)
|
|
59
|
+
end
|
|
60
|
+
|
|
35
61
|
def transform_section(section, parent_id: nil)
|
|
36
62
|
title_text = ToCoreModel.extract_title_text(section.title)
|
|
37
63
|
section_id = section.id || Coradoc::CoreModel::IdGenerator.generate_from_title(
|
|
@@ -11,6 +11,7 @@ module Coradoc
|
|
|
11
11
|
autoload :TableTransformer, "#{__dir__}/element_transformers/table_transformer"
|
|
12
12
|
autoload :OtherTransformer, "#{__dir__}/element_transformers/other_transformer"
|
|
13
13
|
autoload :IncludeTransformer, "#{__dir__}/element_transformers/include_transformer"
|
|
14
|
+
autoload :AdmonitionStyles, "#{__dir__}/element_transformers/admonition_styles"
|
|
14
15
|
end
|
|
15
16
|
end
|
|
16
17
|
end
|
|
@@ -40,12 +40,18 @@ module Coradoc
|
|
|
40
40
|
Coradoc::AsciiDoc::Model::Header.new(title: '')
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
# Pull FrontmatterBlock out first so strip_title_heading sees
|
|
44
|
+
# the body children in their natural order (frontmatter →
|
|
45
|
+
# title heading → body). The reverse-direction strip then
|
|
46
|
+
# matches on a level-0 HeaderElement wherever it sits among
|
|
47
|
+
# the remaining children.
|
|
48
|
+
without_frontmatter, frontmatter = extract_frontmatter(Array(element.children))
|
|
49
|
+
without_title = strip_title_heading(without_frontmatter, element.title)
|
|
44
50
|
|
|
45
51
|
Coradoc::AsciiDoc::Model::Document.new(
|
|
46
52
|
id: element.id,
|
|
47
53
|
header: header,
|
|
48
|
-
sections: flatten_children(
|
|
54
|
+
sections: flatten_children(without_title),
|
|
49
55
|
frontmatter: frontmatter
|
|
50
56
|
)
|
|
51
57
|
when CoreModel::SectionElement
|
|
@@ -64,6 +70,27 @@ module Coradoc
|
|
|
64
70
|
end
|
|
65
71
|
end
|
|
66
72
|
|
|
73
|
+
# The forward direction (DocumentTransformer#insert_title_heading_after_frontmatter)
|
|
74
|
+
# emits a level-0 HeaderElement after any FrontmatterBlock so
|
|
75
|
+
# consumers that walk the children see the title. On the reverse
|
|
76
|
+
# path, that HeaderElement would round-trip back as a separate
|
|
77
|
+
# body element and the title would be emitted twice (once via
|
|
78
|
+
# the document header, once via the heading child). Drop it
|
|
79
|
+
# before serialization when it carries the same text as the
|
|
80
|
+
# document title.
|
|
81
|
+
def strip_title_heading(children, document_title)
|
|
82
|
+
return children unless document_title && !document_title.strip.empty?
|
|
83
|
+
|
|
84
|
+
index = children.find_index do |child|
|
|
85
|
+
child.is_a?(CoreModel::HeaderElement) &&
|
|
86
|
+
child.level.to_i.zero? &&
|
|
87
|
+
child.title.to_s == document_title.to_s
|
|
88
|
+
end
|
|
89
|
+
return children unless index
|
|
90
|
+
|
|
91
|
+
children.reject.with_index { |_, i| i == index }
|
|
92
|
+
end
|
|
93
|
+
|
|
67
94
|
# Transforms each CoreModel child and flattens one level so a
|
|
68
95
|
# transform that returns multiple siblings (e.g. a source block
|
|
69
96
|
# followed by its re-expanded callout paragraphs) stays in
|
|
@@ -48,14 +48,19 @@ module Coradoc
|
|
|
48
48
|
priority: 10
|
|
49
49
|
)
|
|
50
50
|
|
|
51
|
+
# Quote / Example / Sidebar blocks all share the same dispatch:
|
|
52
|
+
# check the attribute style for an admonition label first
|
|
53
|
+
# (`[NOTE]\n====` → AnnotationBlock), fall back to the block's
|
|
54
|
+
# native CoreModel type. Single source of truth lives in
|
|
55
|
+
# BlockTransformer#transform_with_admonition_check.
|
|
51
56
|
{
|
|
52
|
-
Coradoc::AsciiDoc::Model::Block::Quote =>
|
|
53
|
-
Coradoc::AsciiDoc::Model::Block::Example =>
|
|
54
|
-
Coradoc::AsciiDoc::Model::Block::Side =>
|
|
55
|
-
}.each do |block_class,
|
|
57
|
+
Coradoc::AsciiDoc::Model::Block::Quote => :transform_quote_block,
|
|
58
|
+
Coradoc::AsciiDoc::Model::Block::Example => :transform_example_block,
|
|
59
|
+
Coradoc::AsciiDoc::Model::Block::Side => :transform_sidebar_block
|
|
60
|
+
}.each do |block_class, method|
|
|
56
61
|
Registry.register_with_priority(
|
|
57
62
|
block_class,
|
|
58
|
-
->(model) { Blk.
|
|
63
|
+
->(model) { Blk.public_send(method, model) },
|
|
59
64
|
priority: 10
|
|
60
65
|
)
|
|
61
66
|
end
|
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.20
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -317,6 +317,7 @@ files:
|
|
|
317
317
|
- lib/coradoc/asciidoc/transform/attribute_list_to_metadata.rb
|
|
318
318
|
- lib/coradoc/asciidoc/transform/callout_merger.rb
|
|
319
319
|
- lib/coradoc/asciidoc/transform/element_transformers.rb
|
|
320
|
+
- lib/coradoc/asciidoc/transform/element_transformers/admonition_styles.rb
|
|
320
321
|
- lib/coradoc/asciidoc/transform/element_transformers/block_transformer.rb
|
|
321
322
|
- lib/coradoc/asciidoc/transform/element_transformers/document_transformer.rb
|
|
322
323
|
- lib/coradoc/asciidoc/transform/element_transformers/include_transformer.rb
|