metanorma-standoc 1.9.4 → 1.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/rake.yml +1 -1
- data/.rubocop.yml +1 -1
- data/lib/asciidoctor/standoc/cleanup_inline.rb +117 -77
- data/lib/asciidoctor/standoc/cleanup_ref.rb +7 -0
- data/lib/asciidoctor/standoc/cleanup_terms.rb +19 -18
- data/lib/asciidoctor/standoc/inline.rb +20 -17
- data/lib/asciidoctor/standoc/isodoc.rng +18 -1
- data/lib/asciidoctor/standoc/macros_plantuml.rb +19 -21
- data/lib/asciidoctor/standoc/macros_terms.rb +33 -23
- data/lib/asciidoctor/standoc/term_lookup_cleanup.rb +10 -12
- data/lib/asciidoctor/standoc/terms.rb +1 -1
- data/lib/asciidoctor/standoc/validate.rb +21 -8
- data/lib/metanorma/standoc/version.rb +1 -1
- data/metanorma-standoc.gemspec +2 -2
- data/spec/asciidoctor/blocks_spec.rb +6 -6
- data/spec/asciidoctor/cleanup_spec.rb +37 -6
- data/spec/asciidoctor/isobib_cache_spec.rb +4 -6
- data/spec/asciidoctor/lists_spec.rb +147 -135
- data/spec/asciidoctor/macros_spec.rb +505 -181
- data/spec/asciidoctor/refs_spec.rb +12 -12
- data/spec/asciidoctor/validate_spec.rb +66 -20
- data/spec/vcr_cassettes/dated_iso_ref_joint_iso_iec.yml +42 -42
- data/spec/vcr_cassettes/isobib_get_123.yml +12 -12
- data/spec/vcr_cassettes/isobib_get_123_1.yml +26 -26
- data/spec/vcr_cassettes/isobib_get_123_1_fr.yml +35 -35
- data/spec/vcr_cassettes/isobib_get_123_2001.yml +13 -13
- data/spec/vcr_cassettes/isobib_get_124.yml +12 -12
- data/spec/vcr_cassettes/rfcbib_get_rfc8341.yml +13 -13
- data/spec/vcr_cassettes/separates_iev_citations_by_top_level_clause.yml +51 -61
- metadata +5 -5
@@ -955,7 +955,24 @@
|
|
955
955
|
<define name="concept">
|
956
956
|
<element name="concept">
|
957
957
|
<optional>
|
958
|
-
<
|
958
|
+
<element name="refterm">
|
959
|
+
<zeroOrMore>
|
960
|
+
<choice>
|
961
|
+
<ref name="PureTextElement"/>
|
962
|
+
<ref name="stem"/>
|
963
|
+
</choice>
|
964
|
+
</zeroOrMore>
|
965
|
+
</element>
|
966
|
+
</optional>
|
967
|
+
<optional>
|
968
|
+
<element name="renderterm">
|
969
|
+
<zeroOrMore>
|
970
|
+
<choice>
|
971
|
+
<ref name="PureTextElement"/>
|
972
|
+
<ref name="stem"/>
|
973
|
+
</choice>
|
974
|
+
</zeroOrMore>
|
975
|
+
</element>
|
959
976
|
</optional>
|
960
977
|
<choice>
|
961
978
|
<ref name="eref"/>
|
@@ -4,8 +4,8 @@ module Asciidoctor
|
|
4
4
|
# https://stackoverflow.com/questions/2108727/which-in-ruby-checking-if-program-exists-in-path-from-ruby
|
5
5
|
def self.plantuml_installed?
|
6
6
|
cmd = "plantuml"
|
7
|
-
exts = ENV[
|
8
|
-
ENV[
|
7
|
+
exts = ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""]
|
8
|
+
ENV["PATH"].split(File::PATH_SEPARATOR).each do |path|
|
9
9
|
exts.each do |ext|
|
10
10
|
exe = File.join(path, "#{cmd}#{ext}")
|
11
11
|
return exe if File.executable?(exe) && !File.directory?(exe)
|
@@ -15,7 +15,7 @@ module Asciidoctor
|
|
15
15
|
nil
|
16
16
|
end
|
17
17
|
|
18
|
-
def self.run
|
18
|
+
def self.run(umlfile, outfile)
|
19
19
|
system "plantuml #{umlfile.path}" or (warn $? and return false)
|
20
20
|
i = 0
|
21
21
|
until !Gem.win_platform? || File.exist?(outfile) || i == 15
|
@@ -29,9 +29,9 @@ module Asciidoctor
|
|
29
29
|
# sleep need for windows because dot works in separate process and
|
30
30
|
# plantuml process may finish earlier then dot, as result png file
|
31
31
|
# maybe not created yet after plantuml finish
|
32
|
-
def self.generate_file
|
32
|
+
def self.generate_file(parent, reader)
|
33
33
|
localdir = Metanorma::Utils::localdir(parent.document)
|
34
|
-
imagesdir = parent.document.attr(
|
34
|
+
imagesdir = parent.document.attr("imagesdir")
|
35
35
|
umlfile, outfile = save_plantuml parent, reader, localdir
|
36
36
|
run(umlfile, outfile) or raise "No image output from PlantUML (#{umlfile}, #{outfile})!"
|
37
37
|
umlfile.unlink
|
@@ -40,7 +40,7 @@ module Asciidoctor
|
|
40
40
|
File.writable?(localdir) or raise "Destination path #{path} not writable for PlantUML!"
|
41
41
|
path.mkpath
|
42
42
|
File.writable?(path) or raise "Destination path #{path} not writable for PlantUML!"
|
43
|
-
#File.exist?(path) or raise "Destination path #{path} already exists for PlantUML!"
|
43
|
+
# File.exist?(path) or raise "Destination path #{path} already exists for PlantUML!"
|
44
44
|
|
45
45
|
# Warning: metanorma/metanorma-standoc#187
|
46
46
|
# Windows Ruby 2.4 will crash if a Tempfile is "mv"ed.
|
@@ -51,21 +51,21 @@ module Asciidoctor
|
|
51
51
|
imagesdir ? filename : File.join(path, filename)
|
52
52
|
end
|
53
53
|
|
54
|
-
def self.save_plantuml
|
54
|
+
def self.save_plantuml(_parent, reader, _localdir)
|
55
55
|
src = reader.source
|
56
56
|
reader.lines.first.sub(/\s+$/, "").match /^@startuml($| )/ or
|
57
57
|
src = "@startuml\n#{src}\n@enduml\n"
|
58
58
|
/^@startuml (?<fn>[^\n]+)\n/ =~ src
|
59
|
-
Tempfile.open(["plantuml", ".pml"], :
|
59
|
+
Tempfile.open(["plantuml", ".pml"], encoding: "utf-8") do |f|
|
60
60
|
f.write(src)
|
61
61
|
[f, File.join(File.dirname(f.path),
|
62
62
|
(fn || File.basename(f.path, ".pml")) + ".png")]
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
66
|
-
def self.generate_attrs
|
67
|
-
|
68
|
-
inject({}) do |memo, key|
|
66
|
+
def self.generate_attrs(attrs)
|
67
|
+
%w(id align float title role width height alt)
|
68
|
+
.inject({}) do |memo, key|
|
69
69
|
memo[key] = attrs[key] if attrs.has_key? key
|
70
70
|
memo
|
71
71
|
end
|
@@ -81,19 +81,17 @@ module Asciidoctor
|
|
81
81
|
def abort(parent, reader, attrs, msg)
|
82
82
|
warn msg
|
83
83
|
attrs["language"] = "plantuml"
|
84
|
-
create_listing_block parent, reader.source, attrs.reject { |k,
|
84
|
+
create_listing_block parent, reader.source, attrs.reject { |k, _v| k == 1 }
|
85
85
|
end
|
86
86
|
|
87
87
|
def process(parent, reader, attrs)
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
abort(parent, reader, attrs, e.message)
|
96
|
-
end
|
88
|
+
PlantUMLBlockMacroBackend.plantuml_installed?
|
89
|
+
filename = PlantUMLBlockMacroBackend.generate_file(parent, reader)
|
90
|
+
through_attrs = PlantUMLBlockMacroBackend.generate_attrs attrs
|
91
|
+
through_attrs["target"] = filename
|
92
|
+
create_image_block parent, through_attrs
|
93
|
+
rescue StandardError => e
|
94
|
+
abort(parent, reader, attrs, e.message)
|
97
95
|
end
|
98
96
|
end
|
99
97
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require "csv"
|
2
|
+
|
1
3
|
module Asciidoctor
|
2
4
|
module Standoc
|
3
5
|
class AltTermInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
@@ -36,46 +38,54 @@ module Asciidoctor
|
|
36
38
|
end
|
37
39
|
end
|
38
40
|
|
39
|
-
# Macro to transform `term[X,Y]` into em, termxref xml
|
40
41
|
class TermRefInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
41
42
|
use_dsl
|
42
43
|
named :term
|
43
|
-
name_positional_attributes
|
44
|
+
name_positional_attributes "name", "termxref"
|
44
45
|
using_format :short
|
45
46
|
|
46
47
|
def process(_parent, _target, attrs)
|
47
|
-
termref = attrs[
|
48
|
-
"<
|
48
|
+
termref = attrs["termxref"] || attrs["name"]
|
49
|
+
"<concept><termxref>#{attrs['name']}</termxref>"\
|
50
|
+
"<renderterm>#{termref}</renderterm><xrefrender/></concept>"
|
49
51
|
end
|
50
52
|
end
|
51
53
|
|
54
|
+
# Possibilities:
|
55
|
+
# {{<<id>>, term}}
|
56
|
+
# {{<<id>>, term, text}}
|
57
|
+
# {{<<termbase:id>>, term}}
|
58
|
+
# {{<<termbase:id>>, term, text}}
|
59
|
+
# {{term}} equivalent to term:[term]
|
60
|
+
# {{term, text}} equivalent to term:[term, text]
|
61
|
+
# text may optionally be followed by crossreference-rendering, options=""
|
52
62
|
class ConceptInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
53
63
|
use_dsl
|
54
64
|
named :concept
|
55
|
-
name_positional_attributes "id", "word", "term"
|
56
|
-
# match %r{concept:(?<target>[^\[]*)\[(?<content>|.*?[^\\])\]$}
|
57
65
|
match /\{\{(?<content>|.*?[^\\])\}\}/
|
58
66
|
using_format :short
|
59
67
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
attrs[
|
66
|
-
attrs[
|
67
|
-
|
68
|
+
def preprocess_attrs(target)
|
69
|
+
m = /^(?<id><<.+?>>)?(?<rest>.*)$/.match(target)
|
70
|
+
ret = { id: m[:id]&.sub(/^<</, "")&.sub(/>>$/, "") }
|
71
|
+
m2 = /^(?<rest>.*)(?<opt>,option=.+)?$/.match(m[:rest].sub(/^,/, ""))
|
72
|
+
ret[:opt] = m2[:opt]&.sub(/^,option=/, "")
|
73
|
+
attrs = CSV.parse_line(m2[:rest]) || []
|
74
|
+
ret.merge(term: attrs[0], word: attrs[1] || attrs[0],
|
75
|
+
xrefrender: attrs[2])
|
68
76
|
end
|
69
77
|
|
70
|
-
def process(parent,
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
78
|
+
def process(parent, target, _attrs)
|
79
|
+
attrs = preprocess_attrs(target)
|
80
|
+
termout = Asciidoctor::Inline.new(parent, :quoted, attrs[:term]).convert
|
81
|
+
wordout = Asciidoctor::Inline.new(parent, :quoted, attrs[:word]).convert
|
82
|
+
xrefout = Asciidoctor::Inline.new(parent, :quoted,
|
83
|
+
attrs[:xrefrender]).convert
|
84
|
+
attrs[:id] and return "<concept key='#{attrs[:id]}'><refterm>"\
|
85
|
+
"#{termout}</refterm><renderterm>#{wordout}</renderterm>"\
|
86
|
+
"<xrefrender>#{xrefout}</xrefrender></concept>"
|
87
|
+
"<concept><termxref>#{termout}</termxref><renderterm>#{wordout}"\
|
88
|
+
"</renderterm><xrefrender>#{xrefout}</xrefrender></concept>"
|
79
89
|
end
|
80
90
|
end
|
81
91
|
end
|
@@ -3,8 +3,6 @@
|
|
3
3
|
module Asciidoctor
|
4
4
|
module Standoc
|
5
5
|
# Intelligent term lookup xml modifier
|
6
|
-
# Lookup all `term` and `calause` tags and replace `termxref` tags with
|
7
|
-
# `xref`:target tag
|
8
6
|
class TermLookupCleanup
|
9
7
|
AUTOMATIC_GENERATED_ID_REGEXP = /\A_/.freeze
|
10
8
|
EXISTING_TERM_REGEXP = /\Aterm-/.freeze
|
@@ -41,7 +39,9 @@ module Asciidoctor
|
|
41
39
|
remove_missing_ref(node, target)
|
42
40
|
next
|
43
41
|
end
|
44
|
-
|
42
|
+
x = node.at("../xrefrender")
|
43
|
+
modify_ref_node(x, target)
|
44
|
+
node.name = "refterm"
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
@@ -49,20 +49,18 @@ module Asciidoctor
|
|
49
49
|
log.add("AsciiDoc Input", node,
|
50
50
|
%(Error: Term reference in `term[#{target}]` missing: \
|
51
51
|
"#{target}" is not defined in document))
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
node.
|
58
|
-
|
52
|
+
node.name = "strong"
|
53
|
+
node.at("../xrefrender").remove
|
54
|
+
display = node&.at("../renderterm")&.remove&.children
|
55
|
+
display = [] if display.nil? || display&.to_xml == node.text
|
56
|
+
d = display.empty? ? "" : ", display <tt>#{display.to_xml}</tt>"
|
57
|
+
node.children = "term <tt>#{node.text}</tt>#{d} "\
|
58
|
+
"not resolved via ID <tt>#{target}</tt>"
|
59
59
|
end
|
60
60
|
|
61
61
|
def modify_ref_node(node, target)
|
62
62
|
node.name = "xref"
|
63
63
|
node["target"] = termlookup[target]
|
64
|
-
node.children.remove
|
65
|
-
node.remove_attribute("defaultref")
|
66
64
|
end
|
67
65
|
|
68
66
|
def replace_automatic_generated_ids_terms
|
@@ -19,10 +19,10 @@ module Asciidoctor
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def iev_validate(xmldoc)
|
22
|
+
@iev = init_iev or return
|
22
23
|
xmldoc.xpath("//term").each do |t|
|
23
24
|
/^IEC 60050-/.match(t&.at("./termsource/origin/@citeas")&.text) &&
|
24
25
|
loc = t.xpath(SOURCELOCALITY)&.text or next
|
25
|
-
@iev = init_iev or return
|
26
26
|
iev = @iev.fetch(loc, xmldoc&.at("//language")&.text || "en") or next
|
27
27
|
pref = t.xpath("./preferred").inject([]) do |m, x|
|
28
28
|
m << x&.text&.downcase
|
@@ -38,6 +38,7 @@ module Asciidoctor
|
|
38
38
|
norm_ref_validate(doc)
|
39
39
|
repeat_id_validate(doc.root)
|
40
40
|
iev_validate(doc.root)
|
41
|
+
concept_validate(doc)
|
41
42
|
end
|
42
43
|
|
43
44
|
def norm_ref_validate(doc)
|
@@ -54,6 +55,20 @@ module Asciidoctor
|
|
54
55
|
clean_abort("Numeric reference in normative references", doc.to_xml)
|
55
56
|
end
|
56
57
|
|
58
|
+
def concept_validate(doc)
|
59
|
+
found = false
|
60
|
+
doc.xpath("//concept/xref").each do |x|
|
61
|
+
next if doc.at("//term[@id = '#{x['target']}']")
|
62
|
+
|
63
|
+
ref = x&.at("../refterm")&.text
|
64
|
+
@log.add("Anchors", x, "Concept #{ref} is pointing to "\
|
65
|
+
"#{x['target']}, which is not a term")
|
66
|
+
found = true
|
67
|
+
end
|
68
|
+
found and
|
69
|
+
clean_abort("Concept not cross-referencing term", doc.to_xml)
|
70
|
+
end
|
71
|
+
|
57
72
|
def repeat_id_validate1(ids, elem)
|
58
73
|
if ids[elem["id"]]
|
59
74
|
@log.add("Anchors", elem, "Anchor #{elem['id']} has already been "\
|
@@ -78,13 +93,11 @@ module Asciidoctor
|
|
78
93
|
|
79
94
|
def schema_validate(doc, schema)
|
80
95
|
Tempfile.open(["tmp", ".xml"], encoding: "UTF-8") do |f|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
f.close!
|
87
|
-
end
|
96
|
+
schema_validate1(f, doc, schema)
|
97
|
+
rescue Jing::Error => e
|
98
|
+
clean_abort("Jing failed with error: #{e}", doc.to_xml)
|
99
|
+
ensure
|
100
|
+
f.close!
|
88
101
|
end
|
89
102
|
end
|
90
103
|
|
data/metanorma-standoc.gemspec
CHANGED
@@ -24,11 +24,11 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.require_paths = ["lib"]
|
25
25
|
spec.files = `git ls-files`.split("\n")
|
26
26
|
spec.test_files = `git ls-files -- {spec}/*`.split("\n")
|
27
|
-
spec.required_ruby_version = Gem::Requirement.new(">= 2.
|
27
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
|
28
28
|
|
29
29
|
spec.add_dependency "asciidoctor", "~> 2.0.0"
|
30
30
|
spec.add_dependency "iev", "~> 0.2.1"
|
31
|
-
spec.add_dependency "isodoc", "~> 1.
|
31
|
+
spec.add_dependency "isodoc", "~> 1.7.0"
|
32
32
|
spec.add_dependency "metanorma-plugin-datastruct"
|
33
33
|
spec.add_dependency "metanorma-plugin-lutaml"
|
34
34
|
spec.add_dependency "ruby-jing"
|
@@ -1051,13 +1051,13 @@ RSpec.describe Asciidoctor::Standoc do
|
|
1051
1051
|
Definition
|
1052
1052
|
|
1053
1053
|
[.source]
|
1054
|
-
{{IEV:xyz}}
|
1054
|
+
{{<<IEV:xyz>>}}
|
1055
1055
|
|
1056
1056
|
[.source]
|
1057
|
-
{{IEV:xyz
|
1057
|
+
{{<<IEV:xyz>>,t1}}
|
1058
1058
|
|
1059
1059
|
[.source]
|
1060
|
-
{{IEV:xyz
|
1060
|
+
{{<<IEV:xyz>>,t1,t2}}
|
1061
1061
|
INPUT
|
1062
1062
|
output = <<~OUTPUT
|
1063
1063
|
#{BLANK_HDR}
|
@@ -1094,12 +1094,12 @@ RSpec.describe Asciidoctor::Standoc do
|
|
1094
1094
|
</termsource>
|
1095
1095
|
<termsource status='identical'>
|
1096
1096
|
<origin citeas=''>
|
1097
|
-
<termref base='IEV' target='xyz'
|
1097
|
+
<termref base='IEV' target='xyz'/>
|
1098
1098
|
</origin>
|
1099
1099
|
</termsource>
|
1100
1100
|
<termsource status='identical'>
|
1101
1101
|
<origin citeas=''>
|
1102
|
-
<termref base='IEV' target='xyz'
|
1102
|
+
<termref base='IEV' target='xyz'/>
|
1103
1103
|
</origin>
|
1104
1104
|
</termsource>
|
1105
1105
|
</term>
|
@@ -1126,7 +1126,7 @@ RSpec.describe Asciidoctor::Standoc do
|
|
1126
1126
|
Definition
|
1127
1127
|
|
1128
1128
|
[.source]
|
1129
|
-
{{IEV:xyz}}, with adjustments
|
1129
|
+
{{<<IEV:xyz>>}}, with adjustments
|
1130
1130
|
INPUT
|
1131
1131
|
output = <<~OUTPUT
|
1132
1132
|
#{BLANK_HDR}
|
@@ -1618,11 +1618,42 @@ RSpec.describe Asciidoctor::Standoc do
|
|
1618
1618
|
</standard-document>
|
1619
1619
|
INPUT
|
1620
1620
|
output = <<~OUTPUT
|
1621
|
-
|
1622
|
-
|
1623
|
-
|
1621
|
+
#{BLANK_HDR}
|
1622
|
+
<sections>
|
1623
|
+
<stem type="MathML"><math xmlns="http://www.w3.org/1998/Math/MathML"><mfrac><mn>1</mn><mi>r</mi></mfrac></math></stem>
|
1624
1624
|
</sections>
|
1625
|
-
|
1625
|
+
</standard-document>
|
1626
|
+
OUTPUT
|
1627
|
+
expect(Asciidoctor::Standoc::Converter.new(nil, *OPTIONS)
|
1628
|
+
.cleanup(Nokogiri::XML(input)).to_xml)
|
1629
|
+
.to be_equivalent_to xmlpp(output)
|
1630
|
+
end
|
1631
|
+
|
1632
|
+
it "removes nested bibitem IDs" do
|
1633
|
+
input = <<~INPUT
|
1634
|
+
#{BLANK_HDR}
|
1635
|
+
<bibliography>
|
1636
|
+
<references normative="true"><title>Normative</title>
|
1637
|
+
<bibitem id="A">
|
1638
|
+
<relation type="includes">
|
1639
|
+
<bibitem id="B"/>
|
1640
|
+
</relation>
|
1641
|
+
</bibitem>
|
1642
|
+
</bibliography>
|
1643
|
+
</standard-document>
|
1644
|
+
INPUT
|
1645
|
+
output = <<~OUTPUT
|
1646
|
+
#{BLANK_HDR}
|
1647
|
+
<bibliography>
|
1648
|
+
<references normative="true"><title>Normative</title>
|
1649
|
+
<bibitem id="A">
|
1650
|
+
<relation type="includes">
|
1651
|
+
<bibitem id="B"/>
|
1652
|
+
</relation>
|
1653
|
+
</bibitem>
|
1654
|
+
</references>
|
1655
|
+
</bibliography>
|
1656
|
+
</standard-document>
|
1626
1657
|
OUTPUT
|
1627
1658
|
expect(Asciidoctor::Standoc::Converter.new(nil, *OPTIONS)
|
1628
1659
|
.cleanup(Nokogiri::XML(input)).to_xml)
|
@@ -1879,7 +1910,7 @@ RSpec.describe Asciidoctor::Standoc do
|
|
1879
1910
|
.to be_equivalent_to xmlpp(output)
|
1880
1911
|
end
|
1881
1912
|
|
1882
|
-
it "sorts symbols lists" do
|
1913
|
+
it "sorts symbols lists #1" do
|
1883
1914
|
input = <<~INPUT
|
1884
1915
|
#{ASCIIDOC_BLANK_HDR}
|
1885
1916
|
|
@@ -1943,7 +1974,7 @@ RSpec.describe Asciidoctor::Standoc do
|
|
1943
1974
|
.to be_equivalent_to xmlpp(output)
|
1944
1975
|
end
|
1945
1976
|
|
1946
|
-
it "sorts symbols lists" do
|
1977
|
+
it "sorts symbols lists #2" do
|
1947
1978
|
input = <<~INPUT
|
1948
1979
|
#{ASCIIDOC_BLANK_HDR}
|
1949
1980
|
|