metanorma-standoc 1.3.24 → 1.3.29
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/.github/workflows/macos.yml +10 -1
- data/.github/workflows/ubuntu.yml +13 -3
- data/.github/workflows/windows.yml +8 -1
- data/lib/asciidoctor/standoc/base.rb +29 -11
- data/lib/asciidoctor/standoc/biblio.rng +75 -28
- data/lib/asciidoctor/standoc/blocks.rb +12 -5
- data/lib/asciidoctor/standoc/cleanup.rb +17 -8
- data/lib/asciidoctor/standoc/cleanup_block.rb +3 -0
- data/lib/asciidoctor/standoc/cleanup_boilerplate.rb +1 -3
- data/lib/asciidoctor/standoc/cleanup_inline.rb +3 -0
- data/lib/asciidoctor/standoc/cleanup_ref.rb +15 -6
- data/lib/asciidoctor/standoc/cleanup_section.rb +36 -8
- data/lib/asciidoctor/standoc/front.rb +5 -3
- data/lib/asciidoctor/standoc/inline.rb +43 -18
- data/lib/asciidoctor/standoc/isodoc.rng +31 -1
- data/lib/asciidoctor/standoc/macros.rb +2 -1
- data/lib/asciidoctor/standoc/macros_yaml2text.rb +142 -0
- data/lib/asciidoctor/standoc/ref.rb +6 -4
- data/lib/asciidoctor/standoc/section.rb +41 -9
- data/lib/asciidoctor/standoc/utils.rb +2 -11
- data/lib/asciidoctor/standoc/validate.rb +8 -2
- data/lib/asciidoctor/standoc/validate_section.rb +1 -3
- data/lib/metanorma/standoc/latexml_requirement.rb +14 -12
- data/lib/metanorma/standoc/version.rb +1 -1
- data/metanorma-standoc.gemspec +2 -2
- data/spec/asciidoctor-standoc/base_spec.rb +8 -0
- data/spec/asciidoctor-standoc/blocks_spec.rb +74 -2
- data/spec/asciidoctor-standoc/cleanup_spec.rb +75 -28
- data/spec/asciidoctor-standoc/inline_spec.rb +4 -3
- data/spec/asciidoctor-standoc/macros_spec.rb +9 -8
- data/spec/asciidoctor-standoc/macros_yaml2text_spec.rb +564 -0
- data/spec/asciidoctor-standoc/refs_dl_spec.rb +91 -3
- data/spec/asciidoctor-standoc/refs_spec.rb +272 -166
- data/spec/asciidoctor-standoc/section_spec.rb +197 -6
- data/spec/asciidoctor-standoc/validate_spec.rb +67 -2
- data/spec/assets/codes.yml +695 -0
- data/spec/assets/xref_error.adoc +7 -0
- data/spec/examples/codes_table.html +3174 -0
- data/spec/metanorma/processor_spec.rb +2 -2
- data/spec/spec_helper.rb +1 -0
- data/spec/vcr_cassettes/dated_iso_ref_joint_iso_iec.yml +77 -271
- data/spec/vcr_cassettes/isobib_get_123.yml +36 -82
- data/spec/vcr_cassettes/isobib_get_123_2001.yml +17 -40
- data/spec/vcr_cassettes/isobib_get_124.yml +19 -101
- data/spec/vcr_cassettes/rfcbib_get_rfc8341.yml +8 -8
- data/spec/vcr_cassettes/separates_iev_citations_by_top_level_clause.yml +34 -34
- metadata +11 -6
@@ -131,10 +131,13 @@ module Asciidoctor
|
|
131
131
|
def note_cleanup(xmldoc)
|
132
132
|
q = "//note[following-sibling::*[not(local-name() = 'note')]]"
|
133
133
|
xmldoc.xpath(q).each do |n|
|
134
|
+
next if n["keep-separate"] == "true"
|
134
135
|
next unless n.ancestors("table").empty?
|
135
136
|
prev = n.previous_element || next
|
136
137
|
n.parent = prev if ELEMS_ALLOW_NOTES.include? prev.name
|
137
138
|
end
|
139
|
+
xmldoc.xpath("//note[@keep-separate]").each { |n| n.delete("keep-separate") }
|
140
|
+
xmldoc.xpath("//termnote[@keep-separate]").each { |n| n.delete("keep-separate") }
|
138
141
|
end
|
139
142
|
|
140
143
|
def requirement_cleanup(x)
|
@@ -18,7 +18,6 @@ module Asciidoctor
|
|
18
18
|
source.each do |s|
|
19
19
|
@anchors[s["bibitemid"]] or
|
20
20
|
@log.add("Crossreferences", nil, "term source #{s['bibitemid']} not referenced")
|
21
|
-
#warn "term source #{s['bibitemid']} not referenced"
|
22
21
|
end
|
23
22
|
if source.empty? && term.nil?
|
24
23
|
div.next = @no_terms_boilerplate
|
@@ -49,8 +48,7 @@ module Asciidoctor
|
|
49
48
|
TERM_CLAUSE = "//sections/terms | "\
|
50
49
|
"//sections/clause[descendant::terms]".freeze
|
51
50
|
|
52
|
-
NORM_REF = "//bibliography/references[
|
53
|
-
"title = 'Normative references']".freeze
|
51
|
+
NORM_REF = "//bibliography/references[@normative = 'true']".freeze
|
54
52
|
|
55
53
|
def boilerplate_isodoc(xmldoc)
|
56
54
|
x = xmldoc.dup
|
@@ -90,6 +90,9 @@ module Asciidoctor
|
|
90
90
|
end
|
91
91
|
|
92
92
|
def origin_cleanup(xmldoc)
|
93
|
+
xmldoc.xpath("//origin/concept[termref]").each do |x|
|
94
|
+
x.replace(x.children)
|
95
|
+
end
|
93
96
|
xmldoc.xpath("//origin").each do |x|
|
94
97
|
x["citeas"] = @anchors&.dig(x["bibitemid"], :xref) ||
|
95
98
|
@log.add("Crossreferences", x,
|
@@ -5,7 +5,7 @@ module Asciidoctor
|
|
5
5
|
module Standoc
|
6
6
|
module Cleanup
|
7
7
|
def biblio_reorder(xmldoc)
|
8
|
-
xmldoc.xpath("//references[
|
8
|
+
xmldoc.xpath("//references[@normative = 'false']").each do |r|
|
9
9
|
biblio_reorder1(r)
|
10
10
|
end
|
11
11
|
end
|
@@ -49,8 +49,8 @@ module Asciidoctor
|
|
49
49
|
# consecutively, but that standards codes are preserved as is:
|
50
50
|
# only numeric references are renumbered
|
51
51
|
def biblio_renumber(xmldoc)
|
52
|
-
r = xmldoc.at("//references[
|
53
|
-
"//clause[
|
52
|
+
r = xmldoc.at("//references[@normative = 'false'] | "\
|
53
|
+
"//clause[.//references[@normative = 'false']]") or return
|
54
54
|
r.xpath(".//bibitem[not(ancestor::bibitem)]").each_with_index do |b, i|
|
55
55
|
next unless docid = b.at("./docidentifier[@type = 'metanorma']")
|
56
56
|
next unless /^\[\d+\]$/.match(docid.text)
|
@@ -67,18 +67,23 @@ module Asciidoctor
|
|
67
67
|
end
|
68
68
|
|
69
69
|
def normref_cleanup(xmldoc)
|
70
|
-
r = xmldoc.at(NORM_REF) || return
|
71
|
-
|
72
|
-
preface = r.xpath("./title/following-sibling::*") &
|
70
|
+
r = xmldoc.at(self.class::NORM_REF) || return
|
71
|
+
preface = r.xpath("./title/following-sibling::*") & # intersection
|
73
72
|
r.xpath("./bibitem[1]/preceding-sibling::*")
|
74
73
|
preface.each { |n| n.remove }
|
75
74
|
end
|
76
75
|
|
77
76
|
def biblio_cleanup(xmldoc)
|
78
77
|
biblio_reorder(xmldoc)
|
78
|
+
biblio_nested(xmldoc)
|
79
79
|
biblio_renumber(xmldoc)
|
80
|
+
end
|
81
|
+
|
82
|
+
def biblio_nested(xmldoc)
|
80
83
|
xmldoc.xpath("//references[references]").each do |t|
|
81
84
|
t.name = "clause"
|
85
|
+
t.xpath("./references").each { |r| r["normative"] = t["normative"] }
|
86
|
+
t.delete("normative")
|
82
87
|
end
|
83
88
|
end
|
84
89
|
|
@@ -187,6 +192,10 @@ module Asciidoctor
|
|
187
192
|
end
|
188
193
|
bib
|
189
194
|
end
|
195
|
+
|
196
|
+
def fetch_termbase(termbase, id)
|
197
|
+
""
|
198
|
+
end
|
190
199
|
end
|
191
200
|
end
|
192
201
|
end
|
@@ -11,7 +11,7 @@ module Asciidoctor
|
|
11
11
|
module Cleanup
|
12
12
|
def make_preface(x, s)
|
13
13
|
if x.at("//foreword | //introduction | //acknowledgements | "\
|
14
|
-
"
|
14
|
+
"//*[@preface]")
|
15
15
|
preface = s.add_previous_sibling("<preface/>").first
|
16
16
|
f = x.at("//foreword") and preface.add_child f.remove
|
17
17
|
f = x.at("//introduction") and preface.add_child f.remove
|
@@ -22,7 +22,7 @@ module Asciidoctor
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def move_clauses_into_preface(x, preface)
|
25
|
-
x.xpath("
|
25
|
+
x.xpath("//*[@preface]").each do |c|
|
26
26
|
c.delete("preface")
|
27
27
|
preface.add_child c.remove
|
28
28
|
end
|
@@ -66,9 +66,22 @@ module Asciidoctor
|
|
66
66
|
def sections_order_cleanup(x)
|
67
67
|
s = x.at("//sections")
|
68
68
|
make_preface(x, s)
|
69
|
+
make_annexes(x)
|
69
70
|
make_bibliography(x, s)
|
70
71
|
x.xpath("//sections/annex").reverse_each { |r| s.next = r.remove }
|
71
72
|
end
|
73
|
+
|
74
|
+
def make_annexes(x)
|
75
|
+
x.xpath("//*[@annex]").each do |y|
|
76
|
+
y.delete("annex")
|
77
|
+
next if y.name == "annex" || !y.ancestors("annex").empty?
|
78
|
+
y.wrap("<annex/>")
|
79
|
+
y.parent["id"] = "_#{UUIDTools::UUID.random_create}"
|
80
|
+
y.parent["obligation"] = y["obligation"]
|
81
|
+
y.parent["language"] = y["language"]
|
82
|
+
y.parent["script"] = y["script"]
|
83
|
+
end
|
84
|
+
end
|
72
85
|
|
73
86
|
def maxlevel(x)
|
74
87
|
max = 5
|
@@ -117,7 +130,7 @@ module Asciidoctor
|
|
117
130
|
end
|
118
131
|
|
119
132
|
def obligations_cleanup_inherit(x)
|
120
|
-
x.xpath("//annex | //clause").each do |r|
|
133
|
+
x.xpath("//annex | //clause[not(ancestor::boilerplate)]").each do |r|
|
121
134
|
r["obligation"] = "normative" unless r["obligation"]
|
122
135
|
end
|
123
136
|
x.xpath(Utils::SUBCLAUSE_XPATH).each do |r|
|
@@ -192,7 +205,16 @@ module Asciidoctor
|
|
192
205
|
end
|
193
206
|
end
|
194
207
|
|
208
|
+
def termdef_from_termbase(xmldoc)
|
209
|
+
xmldoc.xpath("//term").each do |x|
|
210
|
+
if c = x.at("./origin/termref") and !x.at("./definition")
|
211
|
+
x.at("./origin").previous = fetch_termbase(c["base"], c.text)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
195
216
|
def termdef_cleanup(xmldoc)
|
217
|
+
termdef_from_termbase(xmldoc)
|
196
218
|
termdef_unnest_cleanup(xmldoc)
|
197
219
|
termdef_stem_cleanup(xmldoc)
|
198
220
|
termdomain_cleanup(xmldoc)
|
@@ -209,15 +231,21 @@ module Asciidoctor
|
|
209
231
|
# Numbers sort *after* letters; we use thorn to force that sort order.
|
210
232
|
def symbol_key(x)
|
211
233
|
key = x.dup
|
212
|
-
key.
|
213
|
-
|
234
|
+
key.traverse do |n|
|
235
|
+
next unless n.name == "math"
|
236
|
+
n.replace(grkletters(MathML2AsciiMath.m2a(n.to_xml)))
|
214
237
|
end
|
215
|
-
ret = Nokogiri::XML(
|
216
|
-
HTMLEntities.new.decode(ret.text).
|
238
|
+
ret = Nokogiri::XML(key.to_xml)
|
239
|
+
HTMLEntities.new.decode(ret.text).
|
240
|
+
gsub(/[\[\]\{\}<>\(\)]/, "").strip.
|
217
241
|
gsub(/[[:punct]]|[_^]/, ":\\0").gsub(/`/, "").
|
218
242
|
gsub(/[0-9]+/, "þ\\0")
|
219
243
|
end
|
220
244
|
|
245
|
+
def grkletters(x)
|
246
|
+
x.gsub(/\b(alpha|beta|gamma|delta|epsilon|zeta|eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi|rho|sigma|tau|upsilon|phi|chi|psi|omega)\b/i, "&\\1;")
|
247
|
+
end
|
248
|
+
|
221
249
|
def extract_symbols_list(dl)
|
222
250
|
dl_out = []
|
223
251
|
dl.xpath("./dt | ./dd").each do |dtd|
|
@@ -233,7 +261,7 @@ module Asciidoctor
|
|
233
261
|
def symbols_cleanup(docxml)
|
234
262
|
docxml.xpath("//definitions/dl").each do |dl|
|
235
263
|
dl_out = extract_symbols_list(dl)
|
236
|
-
dl_out.sort! { |a, b| a[:key] <=> b[:key] }
|
264
|
+
dl_out.sort! { |a, b| a[:key] <=> b[:key] || a[:dt] <=> b[:dt] }
|
237
265
|
dl.children = dl_out.map { |d| d[:dt].to_s + d[:dd].to_s }.join("\n")
|
238
266
|
end
|
239
267
|
docxml
|
@@ -78,7 +78,9 @@ module Asciidoctor
|
|
78
78
|
|
79
79
|
def datetypes
|
80
80
|
%w{ published accessed created implemented obsoleted
|
81
|
-
confirmed updated issued circulated unchanged received
|
81
|
+
confirmed updated issued circulated unchanged received
|
82
|
+
vote-started vote-ended
|
83
|
+
}
|
82
84
|
end
|
83
85
|
|
84
86
|
def metadata_date(node, xml)
|
@@ -190,8 +192,8 @@ module Asciidoctor
|
|
190
192
|
["en"].each do |lang|
|
191
193
|
at = { language: lang, format: "text/plain" }
|
192
194
|
xml.title **attr_code(at) do |t|
|
193
|
-
t << Utils::asciidoc_sub(node.attr("title") || node.attr("title-en") ||
|
194
|
-
|
195
|
+
t << (Utils::asciidoc_sub(node.attr("title") || node.attr("title-en")) ||
|
196
|
+
node.title)
|
195
197
|
end
|
196
198
|
end
|
197
199
|
end
|
@@ -3,6 +3,7 @@ require "htmlentities"
|
|
3
3
|
require "unicode2latex"
|
4
4
|
require "mime/types"
|
5
5
|
require "base64"
|
6
|
+
require 'English'
|
6
7
|
|
7
8
|
module Asciidoctor
|
8
9
|
module Standoc
|
@@ -106,23 +107,47 @@ module Asciidoctor
|
|
106
107
|
gsub(/"/, '"').gsub(/
/, "\n")
|
107
108
|
end
|
108
109
|
|
110
|
+
def latex_run1(lxm_input, cmd)
|
111
|
+
IO.popen(cmd, "r+", external_encoding: "UTF-8") do |io|
|
112
|
+
io.write(lxm_input)
|
113
|
+
io.close_write
|
114
|
+
io.read
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def latex_run(lxm_input)
|
119
|
+
results = nil
|
120
|
+
Metanorma::Standoc::Requirements[:latexml].cmd.each_with_index do |cmd, i|
|
121
|
+
warn "Retrying with #{cmd}" if i > 0
|
122
|
+
results = latex_run1(lxm_input, cmd)
|
123
|
+
if $CHILD_STATUS.to_i.zero?
|
124
|
+
warn "Success!" if i > 0
|
125
|
+
break
|
126
|
+
end
|
127
|
+
end
|
128
|
+
$CHILD_STATUS.to_i.zero? ? results : nil
|
129
|
+
end
|
130
|
+
|
131
|
+
def latex_parse(text)
|
132
|
+
lxm_input = Unicode2LaTeX.unicode2latex(HTMLEntities.new.decode(text))
|
133
|
+
results = latex_run(lxm_input)
|
134
|
+
results.nil? and
|
135
|
+
@log.add('Math', nil,
|
136
|
+
"latexmlmath failed to process equation:\n#{lxm_input}")
|
137
|
+
results
|
138
|
+
end
|
139
|
+
|
109
140
|
def stem_parse(text, xml, style)
|
110
141
|
if /<([^:>&]+:)?math(\s+[^>&]+)?> |
|
111
142
|
<([^:>&]+:)?math(\s+[^>&]+)?>/x.match text
|
112
143
|
math = xml_encode(text)
|
113
144
|
xml.stem math, **{ type: "MathML" }
|
114
145
|
elsif style == :latexmath
|
115
|
-
|
116
|
-
latexmlmath_input =
|
117
|
-
Unicode2LaTeX::unicode2latex(HTMLEntities.new.decode(text)).
|
118
|
-
gsub(/'/, '\\').gsub(/\n/, " ")
|
119
|
-
latex = IO.popen(latex_cmd, "r+", external_encoding: "UTF-8") do |io|
|
120
|
-
io.write(latexmlmath_input)
|
121
|
-
io.close_write
|
122
|
-
io.read
|
123
|
-
end
|
146
|
+
latex = latex_parse(text) or return xml.stem **{ type: "MathML" }
|
124
147
|
xml.stem **{ type: "MathML" } do |s|
|
125
|
-
|
148
|
+
math = Nokogiri::XML.fragment(latex.sub(/<\?[^>]+>/, "")).elements[0]
|
149
|
+
math.delete("alttext")
|
150
|
+
s.parent.children = math
|
126
151
|
end
|
127
152
|
else
|
128
153
|
xml.stem text, **{ type: "AsciiMath" }
|
@@ -172,14 +197,14 @@ module Asciidoctor
|
|
172
197
|
types = /^data:/.match(uri) ? datauri2mime(uri) : MIME::Types.type_for(uri)
|
173
198
|
type = types.first.to_s
|
174
199
|
uri = uri.sub(%r{^data:image/\*;}, "data:#{type};")
|
175
|
-
attr_code(src:
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
200
|
+
attr_code(src: uri, #@datauriimage ? datauri(uri) : uri,
|
201
|
+
id: Utils::anchor_or_uuid,
|
202
|
+
mimetype: type,
|
203
|
+
height: node.attr("height") || "auto",
|
204
|
+
width: node.attr("width") || "auto" ,
|
205
|
+
filename: node.attr("filename"),
|
206
|
+
title: node.attr("titleattr"),
|
207
|
+
alt: node.alt == node.attr("default-alt") ? nil : node.alt)
|
183
208
|
end
|
184
209
|
|
185
210
|
def inline_image(node)
|
@@ -129,6 +129,9 @@
|
|
129
129
|
</choice>
|
130
130
|
</attribute>
|
131
131
|
</optional>
|
132
|
+
<attribute name="normative">
|
133
|
+
<data type="boolean"/>
|
134
|
+
</attribute>
|
132
135
|
<optional>
|
133
136
|
<ref name="section-title"/>
|
134
137
|
</optional>
|
@@ -305,6 +308,21 @@
|
|
305
308
|
</define>
|
306
309
|
</include>
|
307
310
|
<!-- end overrides -->
|
311
|
+
<define name="TextElement" combine="choice">
|
312
|
+
<ref name="concept"/>
|
313
|
+
</define>
|
314
|
+
<define name="concept">
|
315
|
+
<element name="concept">
|
316
|
+
<optional>
|
317
|
+
<attribute name="term"/>
|
318
|
+
</optional>
|
319
|
+
<choice>
|
320
|
+
<ref name="eref"/>
|
321
|
+
<ref name="xref"/>
|
322
|
+
<ref name="termref"/>
|
323
|
+
</choice>
|
324
|
+
</element>
|
325
|
+
</define>
|
308
326
|
<define name="BasicBlock" combine="choice">
|
309
327
|
<choice>
|
310
328
|
<ref name="requirement"/>
|
@@ -911,7 +929,10 @@
|
|
911
929
|
</define>
|
912
930
|
<define name="origin">
|
913
931
|
<element name="origin">
|
914
|
-
<
|
932
|
+
<choice>
|
933
|
+
<ref name="erefType"/>
|
934
|
+
<ref name="termref"/>
|
935
|
+
</choice>
|
915
936
|
</element>
|
916
937
|
</define>
|
917
938
|
<define name="modification">
|
@@ -919,6 +940,15 @@
|
|
919
940
|
<ref name="paragraph"/>
|
920
941
|
</element>
|
921
942
|
</define>
|
943
|
+
<define name="termref">
|
944
|
+
<element name="termref">
|
945
|
+
<attribute name="base"/>
|
946
|
+
<attribute name="target"/>
|
947
|
+
<optional>
|
948
|
+
<text/>
|
949
|
+
</optional>
|
950
|
+
</element>
|
951
|
+
</define>
|
922
952
|
<define name="structuredidentifier">
|
923
953
|
<element name="structuredidentifier">
|
924
954
|
<optional>
|
@@ -2,6 +2,7 @@ require "asciidoctor/extensions"
|
|
2
2
|
require "fileutils"
|
3
3
|
require "uuidtools"
|
4
4
|
require_relative "./macros_plantuml.rb"
|
5
|
+
require_relative "./macros_yaml2text.rb"
|
5
6
|
|
6
7
|
module Asciidoctor
|
7
8
|
module Standoc
|
@@ -58,7 +59,7 @@ module Asciidoctor
|
|
58
59
|
named :concept
|
59
60
|
name_positional_attributes "id", "word", "term"
|
60
61
|
#match %r{concept:(?<target>[^\[]*)\[(?<content>|.*?[^\\])\]$}
|
61
|
-
match /\{\{(?<content>|.*?[^\\])\}\}
|
62
|
+
match /\{\{(?<content>|.*?[^\\])\}\}/
|
62
63
|
using_format :short
|
63
64
|
|
64
65
|
# deal with locality attrs and their disruption of positional attrs
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module Asciidoctor
|
4
|
+
module Standoc
|
5
|
+
class YamlBlockStruct < OpenStruct
|
6
|
+
def to_a
|
7
|
+
@table.to_h.keys
|
8
|
+
end
|
9
|
+
|
10
|
+
def values
|
11
|
+
@table.to_h.values
|
12
|
+
end
|
13
|
+
|
14
|
+
def each
|
15
|
+
return to_a.each unless block_given?
|
16
|
+
|
17
|
+
to_a.each do |key|
|
18
|
+
yield(key)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class YamlContextRenderer
|
24
|
+
attr_reader :context_object, :context_name
|
25
|
+
|
26
|
+
def initialize(context_object:, context_name:)
|
27
|
+
@context_object = context_object
|
28
|
+
@context_name = context_name
|
29
|
+
end
|
30
|
+
|
31
|
+
def respond_to_missing?(name)
|
32
|
+
respond_to?(name)
|
33
|
+
end
|
34
|
+
|
35
|
+
def method_missing(name, *_args)
|
36
|
+
return context_object if name.to_s == context_name
|
37
|
+
|
38
|
+
super
|
39
|
+
end
|
40
|
+
|
41
|
+
def render(template)
|
42
|
+
ERB.new(template).result(binding)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class Yaml2TextPreprocessor < Asciidoctor::Extensions::Preprocessor
|
47
|
+
BLOCK_START_REGEXP = /\{(.+?)\.\*,(.+),(.+)\}/.freeze
|
48
|
+
BLOCK_END_REGEXP = /\A\{[A-Z]+\}\z/.freeze
|
49
|
+
# search document for block `yaml2text`
|
50
|
+
# after that take template from block and read file into this template
|
51
|
+
# example:
|
52
|
+
# [yaml2text,foobar.yaml]
|
53
|
+
# ----
|
54
|
+
# === {item.name}
|
55
|
+
# {item.desc}
|
56
|
+
#
|
57
|
+
# {item.symbol}:: {item.symbol_def}
|
58
|
+
# ----
|
59
|
+
#
|
60
|
+
# with content of `foobar.yaml` file equal to:
|
61
|
+
# - name: spaghetti
|
62
|
+
# desc: wheat noodles of 9mm diameter
|
63
|
+
# symbol: SPAG
|
64
|
+
# symbol_def: the situation is message like spaghetti at a kid's meal
|
65
|
+
#
|
66
|
+
# will produce:
|
67
|
+
# === spaghetti
|
68
|
+
# wheat noodles of 9mm diameter
|
69
|
+
#
|
70
|
+
# SPAG:: the situation is message like spaghetti at a kid's meal
|
71
|
+
def process(document, reader)
|
72
|
+
input_lines = reader.readlines.to_enum
|
73
|
+
Reader.new(processed_lines(document, input_lines))
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def processed_lines(document, input_lines)
|
79
|
+
result = []
|
80
|
+
loop do
|
81
|
+
line = input_lines.next
|
82
|
+
if yaml_block_match = line.match(/^\[yaml2text,(.+?),(.+?)\]/)
|
83
|
+
mark = input_lines.next
|
84
|
+
current_yaml_block = []
|
85
|
+
while (yaml_block_line = input_lines.next) != mark
|
86
|
+
current_yaml_block.push(yaml_block_line)
|
87
|
+
end
|
88
|
+
content = nested_open_struct_from_yaml(yaml_block_match[1], document)
|
89
|
+
result.push(*
|
90
|
+
parse_blocks_recursively(lines: current_yaml_block,
|
91
|
+
attributes: content,
|
92
|
+
context_name: yaml_block_match[2]))
|
93
|
+
else
|
94
|
+
result.push(line)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
result
|
98
|
+
end
|
99
|
+
|
100
|
+
def nested_open_struct_from_yaml(file_path, document)
|
101
|
+
docfile_directory = File.dirname(document.attributes['docfile'] || '.')
|
102
|
+
yaml_file_path = document.path_resolver.system_path(file_path, docfile_directory)
|
103
|
+
content = YAML.safe_load(File.read(yaml_file_path))
|
104
|
+
# Load content as json, then parse with JSON as nested open_struct
|
105
|
+
JSON.parse(content.to_json, object_class: YamlBlockStruct)
|
106
|
+
end
|
107
|
+
|
108
|
+
def parse_blocks_recursively(lines:,
|
109
|
+
attributes:,
|
110
|
+
context_name:,
|
111
|
+
parent_context: nil)
|
112
|
+
lines = lines.to_enum
|
113
|
+
result = []
|
114
|
+
loop do
|
115
|
+
line = lines.next
|
116
|
+
if line.match(BLOCK_START_REGEXP)
|
117
|
+
line.gsub!(BLOCK_START_REGEXP, '<% \1.each.with_index do |\2,index| %>')
|
118
|
+
end
|
119
|
+
|
120
|
+
if line.match(BLOCK_END_REGEXP)
|
121
|
+
line.gsub!(BLOCK_END_REGEXP, '<% end %>')
|
122
|
+
end
|
123
|
+
line = line.gsub(/{(.+?[^}]*)}/, '<%= \1 %>').gsub(/[a-z\.]+\#/, 'index')
|
124
|
+
result.push(line)
|
125
|
+
end
|
126
|
+
result = parse_context_block(context_lines: result,
|
127
|
+
context_items: attributes,
|
128
|
+
context_name: context_name,
|
129
|
+
parent_context: parent_context)
|
130
|
+
result
|
131
|
+
end
|
132
|
+
|
133
|
+
def parse_context_block(context_lines:,
|
134
|
+
context_items:,
|
135
|
+
context_name:,
|
136
|
+
parent_context: nil)
|
137
|
+
renderer = YamlContextRenderer.new(context_object: context_items, context_name: context_name)
|
138
|
+
renderer.render(context_lines.join('\n')).split('\n')
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|