coradoc-adoc 2.0.10 → 2.0.11
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 -5
- data/lib/coradoc/asciidoc/parser/content.rb +10 -1
- data/lib/coradoc/asciidoc/transform/callout_merger.rb +94 -0
- data/lib/coradoc/asciidoc/transform/element_transformers/document_transformer.rb +2 -0
- data/lib/coradoc/asciidoc/transform/from_core_model.rb +38 -3
- data/lib/coradoc/asciidoc/transform.rb +1 -0
- 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: 791dca3b794e199e6d856458c3399946545f54a26a98cc4e8aee07de8b7aa21f
|
|
4
|
+
data.tar.gz: 6fd4f6457098075c4ad3f98238ab56c4eb5359271f2024e3b75b0caee3791372
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d8c7eb7672609651c3152ce468727f638cd5e9b5bd73b25b1334697710b8083391db13e9d6d030f97a288a7fe28e33567a2dd934b6a455c0e5279fe4fe183455
|
|
7
|
+
data.tar.gz: 003e62344acf12212216306ad211330464866862dc5876d2caf7a4812c3c120a041b1da11d7ae83668b15e4a0c96a80e08984b9b7a3686de451fa548c444b0e0
|
|
@@ -104,11 +104,22 @@ module Coradoc
|
|
|
104
104
|
delim_str = c.captures[capture_key].to_s.strip
|
|
105
105
|
closing_pattern = str(delim_str) >> newline
|
|
106
106
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
107
|
+
# Verbatim blocks (source/listing) treat their body as literal
|
|
108
|
+
# text per the AsciiDoc spec — no substitutions, no nested
|
|
109
|
+
# blocks. Allowing nested block parsing here would consume
|
|
110
|
+
# shorter inner delimiters (e.g. `----` inside `------`) and
|
|
111
|
+
# strip the original structure when serializing back.
|
|
112
|
+
content = if verbatim
|
|
113
|
+
text_line(false, unguarded: true, verbatim: true) |
|
|
114
|
+
empty_line.as(:line_break)
|
|
115
|
+
else
|
|
116
|
+
c = block_image
|
|
117
|
+
c |= block(n_deep - 1) if n_deep.positive?
|
|
118
|
+
c |= list
|
|
119
|
+
c |= text_line(false, unguarded: true)
|
|
120
|
+
c |= empty_line.as(:line_break)
|
|
121
|
+
c
|
|
122
|
+
end
|
|
112
123
|
|
|
113
124
|
(closing_pattern.absent? >> content).repeat(1)
|
|
114
125
|
end
|
|
@@ -24,7 +24,7 @@ module Coradoc
|
|
|
24
24
|
# :zero :one :many
|
|
25
25
|
def text_line(many_breaks = false, unguarded: false, verbatim: false)
|
|
26
26
|
tl = if verbatim
|
|
27
|
-
|
|
27
|
+
raw_verbatim_line.as(:text)
|
|
28
28
|
elsif unguarded
|
|
29
29
|
literal_space? >> text_any.as(:text)
|
|
30
30
|
else
|
|
@@ -38,6 +38,15 @@ module Coradoc
|
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
+
# A verbatim source/listing line: capture every character up to the
|
|
42
|
+
# next newline without applying any inline substitutions. Source and
|
|
43
|
+
# listing blocks must round-trip their text untouched — otherwise
|
|
44
|
+
# sequences like Liquid `{{ var }}` collide with AsciiDoc attribute
|
|
45
|
+
# references and get rewritten.
|
|
46
|
+
def raw_verbatim_line
|
|
47
|
+
(line_ending.absent? >> any).repeat(1)
|
|
48
|
+
end
|
|
49
|
+
|
|
41
50
|
def asciidoc_char
|
|
42
51
|
line_start? >> match['*_:+=\-']
|
|
43
52
|
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Coradoc
|
|
4
|
+
module AsciiDoc
|
|
5
|
+
module Transform
|
|
6
|
+
# Post-processing pass that merges AsciiDoc callout annotation
|
|
7
|
+
# paragraphs into the verbatim block they annotate.
|
|
8
|
+
#
|
|
9
|
+
# AsciiDoc callouts look like:
|
|
10
|
+
#
|
|
11
|
+
# [source,ruby]
|
|
12
|
+
# ----
|
|
13
|
+
# get '/hi' do <1>
|
|
14
|
+
# ----
|
|
15
|
+
# <1> Returns hello world
|
|
16
|
+
#
|
|
17
|
+
# The parser emits the source block and the annotation as two
|
|
18
|
+
# independent children. The CoreModel representation should attach
|
|
19
|
+
# the annotation to the block as a typed Callout, so downstream
|
|
20
|
+
# serializers can render them appropriately for each format.
|
|
21
|
+
#
|
|
22
|
+
# Single responsibility: take a flat list of transformed CoreModel
|
|
23
|
+
# children, return a flat list with `<N>` paragraphs adjacent to a
|
|
24
|
+
# SourceBlock / ListingBlock folded into that block's `callouts`.
|
|
25
|
+
# Anything else is passed through untouched.
|
|
26
|
+
class CalloutMerger
|
|
27
|
+
ANNOTATION_LINE = /<(\d+)>\s*(.*?)\s*\z/
|
|
28
|
+
ANNOTATION_SPLIT = /(?=<\d+>)/
|
|
29
|
+
|
|
30
|
+
class << self
|
|
31
|
+
def call(children)
|
|
32
|
+
new.merge(Array(children))
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Walks the input children left-to-right. When a paragraph whose
|
|
37
|
+
# content is composed entirely of `<N> text` lines follows a
|
|
38
|
+
# verbatim block (SourceBlock or ListingBlock), each `<N>` line
|
|
39
|
+
# becomes a Callout attached to that block instead of a separate
|
|
40
|
+
# paragraph.
|
|
41
|
+
#
|
|
42
|
+
# Annotations that do not follow a verbatim block are preserved
|
|
43
|
+
# verbatim — they may be legitimate prose.
|
|
44
|
+
def merge(children)
|
|
45
|
+
children.each.with_object([]) do |child, result|
|
|
46
|
+
annotations = extract_annotations(child)
|
|
47
|
+
target = annotations && preceding_verbatim_block(result)
|
|
48
|
+
if target
|
|
49
|
+
target.callouts.concat(annotations)
|
|
50
|
+
else
|
|
51
|
+
result << child
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
# Returns an Array of Callout if the paragraph is entirely
|
|
59
|
+
# callout annotations, otherwise nil.
|
|
60
|
+
def extract_annotations(child)
|
|
61
|
+
return nil unless child.is_a?(Coradoc::CoreModel::ParagraphBlock)
|
|
62
|
+
|
|
63
|
+
lines = split_annotation_lines(child.flat_text)
|
|
64
|
+
return nil if lines.empty?
|
|
65
|
+
|
|
66
|
+
callouts = lines.map do |line|
|
|
67
|
+
match = line.match(ANNOTATION_LINE)
|
|
68
|
+
match ? Coradoc::CoreModel::Callout.new(index: match[1].to_i, content: match[2]) : nil
|
|
69
|
+
end
|
|
70
|
+
return nil if callouts.any?(&:nil?)
|
|
71
|
+
|
|
72
|
+
callouts
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Splits a paragraph into candidate annotation lines. Returns []
|
|
76
|
+
# if any non-blank segment fails to look like an annotation.
|
|
77
|
+
def split_annotation_lines(text)
|
|
78
|
+
return [] if text.nil? || text.strip.empty?
|
|
79
|
+
|
|
80
|
+
chunks = text.strip.split(ANNOTATION_SPLIT)
|
|
81
|
+
chunks.map(&:strip).reject(&:empty?)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def preceding_verbatim_block(result)
|
|
85
|
+
last = result.last
|
|
86
|
+
return nil unless last.is_a?(Coradoc::CoreModel::SourceBlock) ||
|
|
87
|
+
last.is_a?(Coradoc::CoreModel::ListingBlock)
|
|
88
|
+
|
|
89
|
+
last
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -10,6 +10,7 @@ module Coradoc
|
|
|
10
10
|
title_text = ToCoreModel.extract_title_text(doc.header&.title)
|
|
11
11
|
attributes = ToCoreModel.extract_document_attributes(doc)
|
|
12
12
|
children = ToCoreModel.transform(doc.sections || doc.contents || [])
|
|
13
|
+
children = CalloutMerger.call(children)
|
|
13
14
|
children = prepend_frontmatter(children, doc.frontmatter)
|
|
14
15
|
|
|
15
16
|
Coradoc::CoreModel::DocumentElement.new(
|
|
@@ -38,6 +39,7 @@ module Coradoc
|
|
|
38
39
|
)
|
|
39
40
|
|
|
40
41
|
content_children = ToCoreModel.transform(section.contents || [])
|
|
42
|
+
content_children = CalloutMerger.call(content_children)
|
|
41
43
|
nested_sections = (section.sections || []).map do |child|
|
|
42
44
|
transform_section(child, parent_id: section_id)
|
|
43
45
|
end
|
|
@@ -45,7 +45,7 @@ module Coradoc
|
|
|
45
45
|
Coradoc::AsciiDoc::Model::Document.new(
|
|
46
46
|
id: element.id,
|
|
47
47
|
header: header,
|
|
48
|
-
sections:
|
|
48
|
+
sections: flatten_children(sections),
|
|
49
49
|
frontmatter: frontmatter
|
|
50
50
|
)
|
|
51
51
|
when CoreModel::SectionElement
|
|
@@ -53,17 +53,29 @@ module Coradoc
|
|
|
53
53
|
id: element.id,
|
|
54
54
|
level: element.level,
|
|
55
55
|
title: create_title(element.title, element.level),
|
|
56
|
-
contents:
|
|
56
|
+
contents: flatten_children(element.children)
|
|
57
57
|
)
|
|
58
58
|
else
|
|
59
59
|
Coradoc::AsciiDoc::Model::Section.new(
|
|
60
60
|
id: element.id,
|
|
61
61
|
title: create_title(element.title, 1),
|
|
62
|
-
contents:
|
|
62
|
+
contents: flatten_children(element.children)
|
|
63
63
|
)
|
|
64
64
|
end
|
|
65
65
|
end
|
|
66
66
|
|
|
67
|
+
# Transforms each CoreModel child and flattens one level so a
|
|
68
|
+
# transform that returns multiple siblings (e.g. a source block
|
|
69
|
+
# followed by its re-expanded callout paragraphs) stays in
|
|
70
|
+
# document order.
|
|
71
|
+
def flatten_children(children)
|
|
72
|
+
Array(children).flat_map { |child| flatten_one(transform(child)) }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def flatten_one(result)
|
|
76
|
+
result.is_a?(Array) ? result : [result]
|
|
77
|
+
end
|
|
78
|
+
|
|
67
79
|
def transform_block(block)
|
|
68
80
|
content = block.renderable_content
|
|
69
81
|
|
|
@@ -82,7 +94,13 @@ module Coradoc
|
|
|
82
94
|
end
|
|
83
95
|
|
|
84
96
|
content_text = safe_content_to_string(content)
|
|
97
|
+
result = build_verbatim_block(semantic, block, content_text)
|
|
98
|
+
return result unless verbatim_with_callouts?(semantic, block)
|
|
85
99
|
|
|
100
|
+
[result, *build_callout_paragraphs(block.callouts)]
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def build_verbatim_block(semantic, block, content_text)
|
|
86
104
|
case semantic
|
|
87
105
|
when :source_code
|
|
88
106
|
Coradoc::AsciiDoc::Model::Block::SourceCode.new(
|
|
@@ -163,6 +181,23 @@ module Coradoc
|
|
|
163
181
|
end
|
|
164
182
|
end
|
|
165
183
|
|
|
184
|
+
def verbatim_with_callouts?(semantic, block)
|
|
185
|
+
return false unless %i[source_code listing].include?(semantic)
|
|
186
|
+
return false if block.callouts.nil? || block.callouts.empty?
|
|
187
|
+
|
|
188
|
+
true
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Re-expands typed Callouts back into the AsciiDoc `<N> text`
|
|
192
|
+
# paragraph form so the round-trip is faithful.
|
|
193
|
+
def build_callout_paragraphs(callouts)
|
|
194
|
+
callouts.sort_by { |c| c.index || Float::INFINITY }.map do |callout|
|
|
195
|
+
Coradoc::AsciiDoc::Model::Paragraph.new(
|
|
196
|
+
content: create_text_elements("<#{callout.index}> #{callout.content}")
|
|
197
|
+
)
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
166
201
|
def transform_table(table)
|
|
167
202
|
rows = Array(table.rows).map do |row|
|
|
168
203
|
columns = Array(row.cells).map do |cell|
|
|
@@ -13,6 +13,7 @@ module Coradoc
|
|
|
13
13
|
autoload :InlineTransformVisitor, "#{__dir__}/transform/inline_transform_visitor"
|
|
14
14
|
autoload :ElementTransformers, "#{__dir__}/transform/element_transformers"
|
|
15
15
|
autoload :FrontmatterAttributeMap, "#{__dir__}/transform/frontmatter_attribute_map"
|
|
16
|
+
autoload :CalloutMerger, "#{__dir__}/transform/callout_merger"
|
|
16
17
|
end
|
|
17
18
|
end
|
|
18
19
|
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.11
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -313,6 +313,7 @@ files:
|
|
|
313
313
|
- lib/coradoc/asciidoc/serializer/serializers/video.rb
|
|
314
314
|
- lib/coradoc/asciidoc/serializer/spacing_strategy.rb
|
|
315
315
|
- lib/coradoc/asciidoc/transform.rb
|
|
316
|
+
- lib/coradoc/asciidoc/transform/callout_merger.rb
|
|
316
317
|
- lib/coradoc/asciidoc/transform/element_transformers.rb
|
|
317
318
|
- lib/coradoc/asciidoc/transform/element_transformers/block_transformer.rb
|
|
318
319
|
- lib/coradoc/asciidoc/transform/element_transformers/document_transformer.rb
|