coradoc-adoc 2.0.9 → 2.0.10
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/model/bibliography_entry.rb +18 -0
- data/lib/coradoc/asciidoc/model/document.rb +9 -0
- data/lib/coradoc/asciidoc/model/glossaries.rb +1 -1
- data/lib/coradoc/asciidoc/model/list/base.rb +41 -0
- data/lib/coradoc/asciidoc/model/list/core.rb +4 -24
- data/lib/coradoc/asciidoc/model/list/definition.rb +7 -0
- data/lib/coradoc/asciidoc/model/list/definition_item.rb +1 -1
- data/lib/coradoc/asciidoc/model/list/item.rb +1 -1
- data/lib/coradoc/asciidoc/model/list/nestable.rb +7 -3
- data/lib/coradoc/asciidoc/model/list.rb +4 -2
- data/lib/coradoc/asciidoc/parser/base.rb +10 -70
- data/lib/coradoc/asciidoc/parser/block.rb +3 -22
- data/lib/coradoc/asciidoc/parser/block_assembler.rb +37 -100
- data/lib/coradoc/asciidoc/parser/block_header.rb +55 -0
- data/lib/coradoc/asciidoc/parser/frontmatter_parser.rb +24 -0
- data/lib/coradoc/asciidoc/parser/paragraph.rb +1 -3
- data/lib/coradoc/asciidoc/parser/rule_dispatcher.rb +158 -0
- data/lib/coradoc/asciidoc/parser/section.rb +1 -3
- data/lib/coradoc/asciidoc/parser/table.rb +1 -4
- data/lib/coradoc/asciidoc/parser/text.rb +1 -3
- data/lib/coradoc/asciidoc/parser.rb +1 -0
- data/lib/coradoc/asciidoc/serializer/serializers/base.rb +1 -1
- data/lib/coradoc/asciidoc/serializer/serializers/document.rb +7 -0
- data/lib/coradoc/asciidoc/serializer/serializers/list/definition.rb +3 -1
- data/lib/coradoc/asciidoc/transform/element_transformers/block_transformer.rb +10 -1
- data/lib/coradoc/asciidoc/transform/element_transformers/document_transformer.rb +15 -1
- data/lib/coradoc/asciidoc/transform/element_transformers/other_transformer.rb +3 -1
- data/lib/coradoc/asciidoc/transform/from_core_model.rb +33 -3
- data/lib/coradoc/asciidoc/transform/from_core_model_registrations.rb +5 -1
- data/lib/coradoc/asciidoc/transform/frontmatter_attribute_map.rb +112 -0
- data/lib/coradoc/asciidoc/transform/text_extract_visitor.rb +33 -1
- data/lib/coradoc/asciidoc/transform/to_core_model.rb +10 -2
- data/lib/coradoc/asciidoc/transform/to_core_model_registrations.rb +15 -10
- data/lib/coradoc/asciidoc/transform.rb +1 -0
- data/lib/coradoc/asciidoc/transformer/attribute_list_normalizer.rb +69 -0
- data/lib/coradoc/asciidoc/transformer/block_rules.rb +4 -42
- data/lib/coradoc/asciidoc/transformer/block_type_classifier.rb +56 -0
- data/lib/coradoc/asciidoc/transformer/header_rules.rb +15 -53
- data/lib/coradoc/asciidoc/transformer/inline_rules.rb +39 -57
- data/lib/coradoc/asciidoc/transformer/misc_rules.rb +1 -24
- data/lib/coradoc/asciidoc/transformer/structural_rules.rb +18 -81
- data/lib/coradoc/asciidoc/transformer/table_cell_builder.rb +161 -0
- data/lib/coradoc/asciidoc/transformer/table_layout.rb +135 -0
- data/lib/coradoc/asciidoc/transformer/text_rules.rb +1 -25
- data/lib/coradoc/asciidoc/transformer.rb +38 -294
- data/lib/coradoc/asciidoc/version.rb +1 -1
- data/lib/coradoc/asciidoc.rb +6 -3
- metadata +10 -1
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Coradoc
|
|
4
|
+
module AsciiDoc
|
|
5
|
+
class Transformer < Parslet::Transform
|
|
6
|
+
# Pure-function module for normalizing raw parser `:attribute_list`
|
|
7
|
+
# values into a single canonical Model::AttributeList.
|
|
8
|
+
#
|
|
9
|
+
# The parser's `block_header` rule captures every consecutive `[...]`
|
|
10
|
+
# block before a structural element as a Parslet sequence under
|
|
11
|
+
# `:attribute_list`. Real-world AsciiDoc often stacks multiple lists
|
|
12
|
+
# before a single delimiter:
|
|
13
|
+
#
|
|
14
|
+
# [role=quote]
|
|
15
|
+
# [source, ruby]
|
|
16
|
+
# ----
|
|
17
|
+
# code
|
|
18
|
+
# ----
|
|
19
|
+
#
|
|
20
|
+
# This module is the single source of truth for converting any of those
|
|
21
|
+
# shapes (nil, single list, array of lists, array of hashes) into one
|
|
22
|
+
# canonical AttributeList that downstream model constructors can use.
|
|
23
|
+
module AttributeListNormalizer
|
|
24
|
+
module_function
|
|
25
|
+
|
|
26
|
+
# @param value [Object, nil] Raw parser value bound to :attribute_list
|
|
27
|
+
# @return [Model::AttributeList, nil]
|
|
28
|
+
def coerce(value)
|
|
29
|
+
case value
|
|
30
|
+
when nil then nil
|
|
31
|
+
when Model::AttributeList then value
|
|
32
|
+
when Array
|
|
33
|
+
lists = value.map { |entry| unwrap(entry) }.compact
|
|
34
|
+
return nil if lists.empty?
|
|
35
|
+
return lists.first if lists.size == 1
|
|
36
|
+
|
|
37
|
+
merge(lists)
|
|
38
|
+
else
|
|
39
|
+
value
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Merge several AttributeLists into one, preserving positional order
|
|
44
|
+
# and concatenating named keys in input order.
|
|
45
|
+
# @param lists [Array<Model::AttributeList>]
|
|
46
|
+
# @return [Model::AttributeList]
|
|
47
|
+
def merge(lists)
|
|
48
|
+
merged = Model::AttributeList.new
|
|
49
|
+
lists.each do |list|
|
|
50
|
+
next unless list.is_a?(Model::AttributeList)
|
|
51
|
+
|
|
52
|
+
list.positional.each { |p| merged.add_positional(p.value) }
|
|
53
|
+
list.named.each { |n| merged.add_named(n.name, n.value) }
|
|
54
|
+
end
|
|
55
|
+
merged
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Unwrap a single entry of the parser's :attribute_list sequence.
|
|
59
|
+
# @param entry [Object]
|
|
60
|
+
# @return [Model::AttributeList, nil]
|
|
61
|
+
def unwrap(entry)
|
|
62
|
+
return entry if entry.is_a?(Model::AttributeList)
|
|
63
|
+
|
|
64
|
+
entry[:attribute_list] if entry.is_a?(Hash) && entry.key?(:attribute_list)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -11,12 +11,11 @@ module Coradoc
|
|
|
11
11
|
rule(block: subtree(:block)) do
|
|
12
12
|
id = block[:id]
|
|
13
13
|
title = block[:title]
|
|
14
|
-
attribute_list = block[:attribute_list]
|
|
14
|
+
attribute_list = AttributeListNormalizer.coerce(block[:attribute_list])
|
|
15
15
|
delimiter = block[:delimiter].to_s
|
|
16
|
-
delimiter_c = delimiter[0]
|
|
17
16
|
lines = block[:lines]
|
|
18
17
|
ordering = block.keys.select do |k|
|
|
19
|
-
%i[id title attribute_list
|
|
18
|
+
%i[id title attribute_list].include?(k)
|
|
20
19
|
end
|
|
21
20
|
|
|
22
21
|
opts = {
|
|
@@ -26,44 +25,7 @@ module Coradoc
|
|
|
26
25
|
lines: lines,
|
|
27
26
|
ordering: ordering
|
|
28
27
|
}
|
|
29
|
-
opts
|
|
30
|
-
delimiter_len = opts[:delimiter_len]
|
|
31
|
-
|
|
32
|
-
if delimiter_c == '*'
|
|
33
|
-
if attribute_list
|
|
34
|
-
if attribute_list.positional == [] &&
|
|
35
|
-
attribute_list.named.first&.name == 'reviewer'
|
|
36
|
-
Model::Block::ReviewerComment.new(
|
|
37
|
-
id:,
|
|
38
|
-
title:,
|
|
39
|
-
lines:,
|
|
40
|
-
delimiter_len:,
|
|
41
|
-
attributes: attribute_list
|
|
42
|
-
)
|
|
43
|
-
else
|
|
44
|
-
Model::Block::Side.new(id:, title:, lines:, delimiter_len:,
|
|
45
|
-
attributes: attribute_list)
|
|
46
|
-
end
|
|
47
|
-
else
|
|
48
|
-
Model::Block::Side.new(id:, title:, lines:, delimiter_len:,
|
|
49
|
-
attributes: attribute_list)
|
|
50
|
-
end
|
|
51
|
-
elsif delimiter_c == '='
|
|
52
|
-
Model::Block::Example.new(id:, title:, lines:, delimiter_len:,
|
|
53
|
-
attributes: attribute_list)
|
|
54
|
-
elsif delimiter_c == '+'
|
|
55
|
-
Model::Block::Pass.new(id:, title:, lines:, delimiter_len:,
|
|
56
|
-
attributes: attribute_list)
|
|
57
|
-
elsif delimiter_c == '-' && delimiter.size == 2
|
|
58
|
-
Model::Block::Open.new(id:, title:, lines:, delimiter_len:,
|
|
59
|
-
attributes: attribute_list)
|
|
60
|
-
elsif delimiter_c == '-' && delimiter.size >= 4
|
|
61
|
-
Model::Block::SourceCode.new(id:, title:, lines:, delimiter_len:,
|
|
62
|
-
attributes: attribute_list)
|
|
63
|
-
elsif delimiter_c == '_'
|
|
64
|
-
Model::Block::Quote.new(id:, title:, lines:, delimiter_len:,
|
|
65
|
-
attributes: attribute_list)
|
|
66
|
-
end
|
|
28
|
+
BlockTypeClassifier.classify(delimiter, opts, attribute_list)
|
|
67
29
|
end
|
|
68
30
|
|
|
69
31
|
# Example
|
|
@@ -84,7 +46,7 @@ module Coradoc
|
|
|
84
46
|
id = block_image[:id]
|
|
85
47
|
title = block_image[:title]
|
|
86
48
|
path = block_image[:path]
|
|
87
|
-
attrs = block_image[:attribute_list]
|
|
49
|
+
attrs = AttributeListNormalizer.coerce(block_image[:attribute_list])
|
|
88
50
|
Model::Image::BlockImage.new(
|
|
89
51
|
title: title,
|
|
90
52
|
id: id,
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Coradoc
|
|
4
|
+
module AsciiDoc
|
|
5
|
+
class Transformer < Parslet::Transform
|
|
6
|
+
# Single source of truth for "which delimiter maps to which block model".
|
|
7
|
+
#
|
|
8
|
+
# The block rule in BlockRules delegates here to convert a parser
|
|
9
|
+
# delimiter string (e.g., `----`, `****`, `--`) into the appropriate
|
|
10
|
+
# Model::Block::* subclass instance. Adding a new block type means
|
|
11
|
+
# appending one entry to DELIMITER_CLASSIFICATIONS — no edits to the
|
|
12
|
+
# block rule itself. (Open/Closed Principle.)
|
|
13
|
+
module BlockTypeClassifier
|
|
14
|
+
# Each entry is [char, min_length, max_length, factory].
|
|
15
|
+
# The factory is a callable taking (opts, attribute_list) and
|
|
16
|
+
# returning a Model::Block::* instance. `max_length` nil means
|
|
17
|
+
# unbounded.
|
|
18
|
+
DELIMITER_CLASSIFICATIONS = [
|
|
19
|
+
['*', 4, nil, ->(opts, attrs) {
|
|
20
|
+
if attrs && attrs.positional == [] && attrs.named.first&.name == 'reviewer'
|
|
21
|
+
Model::Block::ReviewerComment.new(**opts.merge(attributes: attrs))
|
|
22
|
+
else
|
|
23
|
+
Model::Block::Side.new(**opts.merge(attributes: attrs))
|
|
24
|
+
end
|
|
25
|
+
}],
|
|
26
|
+
['=', 4, nil, ->(opts, attrs) { Model::Block::Example.new(**opts.merge(attributes: attrs)) }],
|
|
27
|
+
['+', 4, nil, ->(opts, attrs) { Model::Block::Pass.new(**opts.merge(attributes: attrs)) }],
|
|
28
|
+
['_', 4, nil, ->(opts, attrs) { Model::Block::Quote.new(**opts.merge(attributes: attrs)) }],
|
|
29
|
+
['-', 4, nil, ->(opts, attrs) { Model::Block::SourceCode.new(**opts.merge(attributes: attrs)) }],
|
|
30
|
+
['-', 2, 2, ->(opts, attrs) { Model::Block::Open.new(**opts.merge(attributes: attrs)) }]
|
|
31
|
+
].freeze
|
|
32
|
+
|
|
33
|
+
module_function
|
|
34
|
+
|
|
35
|
+
# @param delimiter [String] e.g., "----", "**", "--"
|
|
36
|
+
# @param opts [Hash] Constructor options (id, title, lines, delimiter_len, ordering)
|
|
37
|
+
# @param attrs [Model::AttributeList, nil]
|
|
38
|
+
# @return [Model::Block::Base, nil]
|
|
39
|
+
def classify(delimiter, opts, attrs)
|
|
40
|
+
char = delimiter[0]
|
|
41
|
+
len = delimiter.size
|
|
42
|
+
entry = DELIMITER_CLASSIFICATIONS.find do |c, min_len, max_len, _|
|
|
43
|
+
next false unless c == char
|
|
44
|
+
next false unless len >= min_len
|
|
45
|
+
next false if max_len && len > max_len
|
|
46
|
+
|
|
47
|
+
true
|
|
48
|
+
end
|
|
49
|
+
return nil unless entry
|
|
50
|
+
|
|
51
|
+
entry.last.call(opts, attrs)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -7,40 +7,25 @@ module Coradoc
|
|
|
7
7
|
module HeaderRules
|
|
8
8
|
def self.apply(transformer_class)
|
|
9
9
|
transformer_class.class_eval do
|
|
10
|
-
# Header
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
end
|
|
10
|
+
# Header — single canonical rule covering all combinations of
|
|
11
|
+
# optional :author and :revision slots. The previous design had
|
|
12
|
+
# four explicit rules (one per combination); this version reads
|
|
13
|
+
# the same data with one `subtree` match.
|
|
14
|
+
rule(header: subtree(:header)) do
|
|
15
|
+
title = header[:title]
|
|
16
|
+
author = header[:author]
|
|
17
|
+
revision = header[:revision]
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
) do
|
|
25
|
-
id = title.is_a?(Model::Title) ? title.id : nil
|
|
26
|
-
Model::Header.new(id:, title:, author:, revision: nil)
|
|
27
|
-
end
|
|
19
|
+
id = header[:id]
|
|
20
|
+
id = title.id if title.is_a?(Model::Title) && title.id && !id
|
|
21
|
+
id = id.to_s unless id.nil?
|
|
22
|
+
id = nil if id && id.empty?
|
|
28
23
|
|
|
29
|
-
|
|
30
|
-
rule(
|
|
31
|
-
title: simple(:title),
|
|
32
|
-
revision: simple(:revision)
|
|
33
|
-
) do
|
|
34
|
-
id = title.is_a?(Model::Title) ? title.id : nil
|
|
35
|
-
Model::Header.new(id:, title:, author: nil, revision:)
|
|
24
|
+
Model::Header.new(id:, title:, author:, revision:)
|
|
36
25
|
end
|
|
37
26
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
title: simple(:title)
|
|
41
|
-
) do
|
|
42
|
-
id = title.is_a?(Model::Title) ? title.id : nil
|
|
43
|
-
Model::Header.new(id:, title:, author: nil, revision: nil)
|
|
27
|
+
rule(header: simple(:header)) do
|
|
28
|
+
header
|
|
44
29
|
end
|
|
45
30
|
|
|
46
31
|
# Author
|
|
@@ -60,29 +45,6 @@ module Coradoc
|
|
|
60
45
|
) do
|
|
61
46
|
Model::Revision.new(number:, date:, remark:)
|
|
62
47
|
end
|
|
63
|
-
|
|
64
|
-
# Unwrap header hash - handles cases where header wasn't transformed yet
|
|
65
|
-
rule(header: subtree(:header)) do
|
|
66
|
-
if header.is_a?(Hash) && header.key?(:title)
|
|
67
|
-
id = header[:id]
|
|
68
|
-
id = id.to_s unless id.nil?
|
|
69
|
-
id = nil if id && id.empty?
|
|
70
|
-
|
|
71
|
-
title = header[:title]
|
|
72
|
-
author = header[:author]
|
|
73
|
-
revision = header[:revision]
|
|
74
|
-
|
|
75
|
-
id = title.id if title.is_a?(Model::Title) && title.id && !id
|
|
76
|
-
|
|
77
|
-
Model::Header.new(id:, title:, author:, revision:)
|
|
78
|
-
else
|
|
79
|
-
header
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
rule(header: simple(:header)) do
|
|
84
|
-
header
|
|
85
|
-
end
|
|
86
48
|
end
|
|
87
49
|
end
|
|
88
50
|
end
|
|
@@ -5,6 +5,17 @@ module Coradoc
|
|
|
5
5
|
class Transformer < Parslet::Transform
|
|
6
6
|
# Module containing inline element transformation rules
|
|
7
7
|
module InlineRules
|
|
8
|
+
# Inline formatting variants that share the same rule shape:
|
|
9
|
+
# constrained and unconstrained forms of the same model class.
|
|
10
|
+
# `span` is excluded because it carries `text:` + `attributes:`
|
|
11
|
+
# rather than `content:`, so it gets its own pair of rules.
|
|
12
|
+
FORMATTING_VARIANTS = [
|
|
13
|
+
%i[bold Bold],
|
|
14
|
+
%i[italic Italic],
|
|
15
|
+
%i[highlight Highlight],
|
|
16
|
+
%i[monospace Monospace]
|
|
17
|
+
].freeze
|
|
18
|
+
|
|
8
19
|
def self.apply(transformer_class)
|
|
9
20
|
transformer_class.class_eval do
|
|
10
21
|
# Link
|
|
@@ -82,64 +93,23 @@ module Coradoc
|
|
|
82
93
|
href_arg.to_s
|
|
83
94
|
end
|
|
84
95
|
|
|
85
|
-
#
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
content = Transformer.extract_inline_content(bold)
|
|
94
|
-
Model::Inline::Bold.new(content: content, unconstrained: true)
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
# Italic (constrained)
|
|
98
|
-
rule(italic_constrained: subtree(:italic)) do
|
|
99
|
-
content = Transformer.extract_inline_content(italic)
|
|
100
|
-
Model::Inline::Italic.new(content: content, unconstrained: false)
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
# Italic (unconstrained)
|
|
104
|
-
rule(italic_unconstrained: subtree(:italic)) do
|
|
105
|
-
content = Transformer.extract_inline_content(italic)
|
|
106
|
-
Model::Inline::Italic.new(content: content, unconstrained: true)
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
# Highlight (constrained)
|
|
110
|
-
rule(highlight_constrained: subtree(:highlight)) do
|
|
111
|
-
content = Transformer.extract_inline_content(highlight)
|
|
112
|
-
Model::Inline::Highlight.new(content: content, unconstrained: false)
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
# Highlight (unconstrained)
|
|
116
|
-
rule(highlight_unconstrained: subtree(:highlight)) do
|
|
117
|
-
content = Transformer.extract_inline_content(highlight)
|
|
118
|
-
Model::Inline::Highlight.new(content: content, unconstrained: true)
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
# Monospace (constrained)
|
|
122
|
-
rule(monospace_constrained: subtree(:monospace)) do
|
|
123
|
-
content = Transformer.extract_inline_content(monospace)
|
|
124
|
-
Model::Inline::Monospace.new(content: content, unconstrained: false)
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
# Monospace (unconstrained)
|
|
128
|
-
rule(monospace_unconstrained: subtree(:monospace)) do
|
|
129
|
-
content = Transformer.extract_inline_content(monospace)
|
|
130
|
-
Model::Inline::Monospace.new(content: content, unconstrained: true)
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
# Superscript
|
|
134
|
-
rule(superscript: subtree(:superscript)) do
|
|
135
|
-
content = Transformer.extract_simple_inline_content(superscript)
|
|
136
|
-
Model::Inline::Superscript.new(content:)
|
|
137
|
-
end
|
|
96
|
+
# Inline formatting rules generated from a single registry.
|
|
97
|
+
# See InlineRules::FORMATTING_VARIANTS. `span` is special
|
|
98
|
+
# because it carries `text:` + `attributes:` rather than
|
|
99
|
+
# `content:`, so it stays inline below.
|
|
100
|
+
InlineRules::FORMATTING_VARIANTS.each do |prefix, class_name|
|
|
101
|
+
klass = Model::Inline.const_get(class_name)
|
|
102
|
+
constrained_key = :"#{prefix}_constrained"
|
|
103
|
+
unconstrained_key = :"#{prefix}_unconstrained"
|
|
138
104
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
105
|
+
rule(constrained_key => subtree(:subtree)) do
|
|
106
|
+
content = Transformer.extract_inline_content(subtree)
|
|
107
|
+
klass.new(content: content, unconstrained: false)
|
|
108
|
+
end
|
|
109
|
+
rule(unconstrained_key => subtree(:subtree)) do
|
|
110
|
+
content = Transformer.extract_inline_content(subtree)
|
|
111
|
+
klass.new(content: content, unconstrained: true)
|
|
112
|
+
end
|
|
143
113
|
end
|
|
144
114
|
|
|
145
115
|
# Span (constrained)
|
|
@@ -160,6 +130,18 @@ module Coradoc
|
|
|
160
130
|
)
|
|
161
131
|
end
|
|
162
132
|
|
|
133
|
+
# Superscript
|
|
134
|
+
rule(superscript: subtree(:superscript)) do
|
|
135
|
+
content = Transformer.extract_simple_inline_content(superscript)
|
|
136
|
+
Model::Inline::Superscript.new(content:)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Subscript
|
|
140
|
+
rule(subscript: subtree(:subscript)) do
|
|
141
|
+
content = Transformer.extract_simple_inline_content(subscript)
|
|
142
|
+
Model::Inline::Subscript.new(content:)
|
|
143
|
+
end
|
|
144
|
+
|
|
163
145
|
# Highlight (simple)
|
|
164
146
|
rule(highlight: simple(:text)) do
|
|
165
147
|
Model::Highlight.new(content: text)
|
|
@@ -157,35 +157,12 @@ module Coradoc
|
|
|
157
157
|
end
|
|
158
158
|
end
|
|
159
159
|
|
|
160
|
-
content = lines.map do |line|
|
|
161
|
-
if line.is_a?(Hash) && line.key?(:text)
|
|
162
|
-
text_content = line[:text]
|
|
163
|
-
line_break = line[:line_break]
|
|
164
|
-
|
|
165
|
-
transformed_text = if text_content.is_a?(Array)
|
|
166
|
-
text_content.map do |item|
|
|
167
|
-
if item.is_a?(Hash)
|
|
168
|
-
Transformer.new.apply(item)
|
|
169
|
-
else
|
|
170
|
-
item
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
|
-
else
|
|
174
|
-
text_content
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
Model::TextElement.new(content: transformed_text, line_break: line_break)
|
|
178
|
-
else
|
|
179
|
-
line
|
|
180
|
-
end
|
|
181
|
-
end
|
|
182
|
-
|
|
183
160
|
Model::ReviewerNote.new(
|
|
184
161
|
reviewer: attrs[:reviewer],
|
|
185
162
|
date: attrs[:date],
|
|
186
163
|
from: attrs[:from],
|
|
187
164
|
to: attrs[:to],
|
|
188
|
-
content:
|
|
165
|
+
content: Transformer.lines_to_text_elements(lines)
|
|
189
166
|
)
|
|
190
167
|
end
|
|
191
168
|
end
|
|
@@ -60,81 +60,19 @@ module Coradoc
|
|
|
60
60
|
table
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
-
# Table
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
) do
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
) do
|
|
77
|
-
Model::Table.new(title: title.to_s, rows: Transformer.regroup_table_rows(rows))
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
# Table with rows and id
|
|
81
|
-
rule(
|
|
82
|
-
id: simple(:id),
|
|
83
|
-
delim_char: simple(:delim_char),
|
|
84
|
-
rows: sequence(:rows)
|
|
85
|
-
) do
|
|
86
|
-
Model::Table.new(id: id.to_s, rows: Transformer.regroup_table_rows(rows))
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
# Table with rows, id, and attributes
|
|
90
|
-
rule(
|
|
91
|
-
id: simple(:id),
|
|
92
|
-
attribute_list: simple(:attrs),
|
|
93
|
-
delim_char: simple(:delim_char),
|
|
94
|
-
rows: sequence(:rows)
|
|
95
|
-
) do
|
|
96
|
-
Model::Table.new(id: id.to_s, rows: Transformer.regroup_table_rows(rows, attrs), attrs: attrs)
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
# Table with rows, title, and attributes
|
|
100
|
-
rule(
|
|
101
|
-
title: simple(:title),
|
|
102
|
-
attribute_list: simple(:attrs),
|
|
103
|
-
delim_char: simple(:delim_char),
|
|
104
|
-
rows: sequence(:rows)
|
|
105
|
-
) do
|
|
106
|
-
Model::Table.new(title: title.to_s, rows: Transformer.regroup_table_rows(rows, attrs), attrs: attrs)
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
# Table with rows and attributes only
|
|
110
|
-
rule(
|
|
111
|
-
attribute_list: simple(:attrs),
|
|
112
|
-
delim_char: simple(:delim_char),
|
|
113
|
-
rows: sequence(:rows)
|
|
114
|
-
) do
|
|
115
|
-
Model::Table.new(rows: Transformer.regroup_table_rows(rows, attrs), attrs: attrs)
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
# Table with rows, id, title, and attributes (full set)
|
|
119
|
-
rule(
|
|
120
|
-
id: simple(:id),
|
|
121
|
-
title: simple(:title),
|
|
122
|
-
attribute_list: simple(:attrs),
|
|
123
|
-
delim_char: simple(:delim_char),
|
|
124
|
-
rows: sequence(:rows)
|
|
125
|
-
) do
|
|
126
|
-
Model::Table.new(id: id.to_s, title: title.to_s, rows: Transformer.regroup_table_rows(rows, attrs),
|
|
127
|
-
attrs: attrs)
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
# Table with id and title (no attributes)
|
|
131
|
-
rule(
|
|
132
|
-
id: simple(:id),
|
|
133
|
-
title: simple(:title),
|
|
134
|
-
delim_char: simple(:delim_char),
|
|
135
|
-
rows: sequence(:rows)
|
|
136
|
-
) do
|
|
137
|
-
Model::Table.new(id: id.to_s, title: title.to_s, rows: Transformer.regroup_table_rows(rows))
|
|
63
|
+
# Unified Table rule. Every variant (with or without title, id,
|
|
64
|
+
# attributes) flows through here. Parser::BlockHeader always
|
|
65
|
+
# captures attribute_lists as a sequence, so we funnel through
|
|
66
|
+
# coerce_attribute_list before constructing the model.
|
|
67
|
+
rule(table: subtree(:table)) do
|
|
68
|
+
id = table[:id]&.to_s
|
|
69
|
+
title = table[:title]&.to_s
|
|
70
|
+
attrs = AttributeListNormalizer.coerce(table[:attribute_list])
|
|
71
|
+
rows = table[:rows]
|
|
72
|
+
opts = { rows: Transformer.regroup_table_rows(rows, attrs), attrs: attrs }
|
|
73
|
+
opts[:id] = id if id
|
|
74
|
+
opts[:title] = title unless title.nil? || title.empty?
|
|
75
|
+
Model::Table.new(**opts)
|
|
138
76
|
end
|
|
139
77
|
|
|
140
78
|
# Title
|
|
@@ -171,7 +109,7 @@ module Coradoc
|
|
|
171
109
|
|
|
172
110
|
id = title.id if title.is_a?(Model::Title) && title.id && !id
|
|
173
111
|
|
|
174
|
-
attribute_list = section[:attribute_list]
|
|
112
|
+
attribute_list = AttributeListNormalizer.coerce(section[:attribute_list])
|
|
175
113
|
contents = section[:contents] || []
|
|
176
114
|
sections = section[:sections]
|
|
177
115
|
Model::Section.new(
|
|
@@ -200,12 +138,11 @@ module Coradoc
|
|
|
200
138
|
|
|
201
139
|
# Bibliography entry
|
|
202
140
|
rule(bibliography_entry: subtree(:bib_entry)) do
|
|
203
|
-
anchor_name = bib_entry[:anchor_name]
|
|
204
|
-
document_id = bib_entry[:document_id]
|
|
205
|
-
ref_text = bib_entry[:ref_text]
|
|
206
|
-
line_break = bib_entry[:line_break]
|
|
207
141
|
Model::BibliographyEntry.new(
|
|
208
|
-
anchor_name
|
|
142
|
+
anchor_name: bib_entry[:anchor_name],
|
|
143
|
+
document_id: bib_entry[:document_id],
|
|
144
|
+
ref_text: Model::BibliographyEntry.coerce_ref_text(bib_entry[:ref_text]),
|
|
145
|
+
line_break: bib_entry[:line_break]
|
|
209
146
|
)
|
|
210
147
|
end
|
|
211
148
|
end
|