metanorma-standoc 1.9.3 → 1.10.2

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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rake.yml +3 -13
  3. data/.hound.yml +3 -1
  4. data/.rubocop.yml +4 -6
  5. data/lib/asciidoctor/standoc/base.rb +3 -1
  6. data/lib/asciidoctor/standoc/blocks.rb +1 -1
  7. data/lib/asciidoctor/standoc/cleanup.rb +1 -2
  8. data/lib/asciidoctor/standoc/cleanup_block.rb +0 -1
  9. data/lib/asciidoctor/standoc/cleanup_boilerplate.rb +2 -2
  10. data/lib/asciidoctor/standoc/cleanup_footnotes.rb +0 -1
  11. data/lib/asciidoctor/standoc/cleanup_inline.rb +117 -77
  12. data/lib/asciidoctor/standoc/cleanup_maths.rb +0 -1
  13. data/lib/asciidoctor/standoc/cleanup_ref.rb +7 -0
  14. data/lib/asciidoctor/standoc/cleanup_section.rb +13 -81
  15. data/lib/asciidoctor/standoc/cleanup_section_names.rb +75 -0
  16. data/lib/asciidoctor/standoc/cleanup_terms.rb +19 -18
  17. data/lib/asciidoctor/standoc/converter.rb +1 -0
  18. data/lib/asciidoctor/standoc/front.rb +0 -1
  19. data/lib/asciidoctor/standoc/front_contributor.rb +66 -42
  20. data/lib/asciidoctor/standoc/inline.rb +45 -34
  21. data/lib/asciidoctor/standoc/isodoc.rng +65 -7
  22. data/lib/asciidoctor/standoc/macros.rb +7 -5
  23. data/lib/asciidoctor/standoc/macros_plantuml.rb +21 -23
  24. data/lib/asciidoctor/standoc/macros_terms.rb +60 -23
  25. data/lib/asciidoctor/standoc/section.rb +19 -12
  26. data/lib/asciidoctor/standoc/term_lookup_cleanup.rb +69 -30
  27. data/lib/asciidoctor/standoc/terms.rb +1 -1
  28. data/lib/asciidoctor/standoc/utils.rb +0 -1
  29. data/lib/asciidoctor/standoc/validate.rb +22 -8
  30. data/lib/isodoc/html/html_titlepage.html +81 -0
  31. data/lib/isodoc/html/htmlstyle.css +983 -0
  32. data/lib/isodoc/html/htmlstyle.scss +714 -0
  33. data/lib/isodoc/html/scripts.html +71 -0
  34. data/lib/metanorma/standoc/processor.rb +16 -7
  35. data/lib/metanorma/standoc/version.rb +1 -1
  36. data/metanorma-standoc.gemspec +2 -2
  37. data/spec/asciidoctor/base_spec.rb +693 -553
  38. data/spec/asciidoctor/blocks_spec.rb +6 -6
  39. data/spec/asciidoctor/cleanup_spec.rb +899 -688
  40. data/spec/asciidoctor/inline_spec.rb +62 -14
  41. data/spec/asciidoctor/isobib_cache_spec.rb +4 -6
  42. data/spec/asciidoctor/lists_spec.rb +147 -135
  43. data/spec/asciidoctor/macros_json2text_spec.rb +1 -1
  44. data/spec/asciidoctor/macros_spec.rb +714 -168
  45. data/spec/asciidoctor/refs_spec.rb +1527 -1532
  46. data/spec/asciidoctor/validate_spec.rb +352 -304
  47. data/spec/vcr_cassettes/dated_iso_ref_joint_iso_iec.yml +51 -51
  48. data/spec/vcr_cassettes/isobib_get_123.yml +14 -14
  49. data/spec/vcr_cassettes/isobib_get_123_1.yml +27 -27
  50. data/spec/vcr_cassettes/isobib_get_123_1_fr.yml +36 -36
  51. data/spec/vcr_cassettes/isobib_get_123_2001.yml +12 -12
  52. data/spec/vcr_cassettes/isobib_get_124.yml +13 -13
  53. data/spec/vcr_cassettes/rfcbib_get_rfc8341.yml +14 -14
  54. data/spec/vcr_cassettes/separates_iev_citations_by_top_level_clause.yml +46 -46
  55. metadata +10 -5
@@ -9,8 +9,8 @@ require "latexmath"
9
9
  module Asciidoctor
10
10
  module Standoc
11
11
  module Inline
12
- def refid?(x)
13
- @refids.include? x
12
+ def refid?(ref)
13
+ @refids.include? ref
14
14
  end
15
15
 
16
16
  def inline_anchor(node)
@@ -46,19 +46,20 @@ module Asciidoctor
46
46
  def inline_anchor_xref_attrs(node)
47
47
  m = /^(?<drop>droploc%)?(?<case>capital%|lowercase%)?(?<drop2>droploc%)?
48
48
  (?<fn>fn:?\s*)?(?<text>.*)$/x.match node.text
49
- casing = m.nil? ? nil : m[:case]&.sub(/%$/, "")
50
- droploc = m.nil? ? nil : ((m[:drop].nil? && m[:drop2].nil?) ? nil: true)
51
- f = (m.nil? || m[:fn].nil?) ? "inline" : "footnote"
52
- c = (!m.nil? && (%i[case fn drop drop2].any? { |x| !m[x].nil? })) ?
53
- m[:text] : node.text
54
49
  t = node.target.gsub(/^#/, "").gsub(%r{(\.xml|\.adoc)(#.*$)}, "\\2")
55
- { target: t, type: f, case: casing, droploc: droploc, text: c }
50
+ m.nil? and return { target: t, type: "inline", text: node.text }
51
+ droploc = m[:drop].nil? && m[:drop2].nil? ? nil : true
52
+ f = m[:fn].nil? ? "inline" : "footnote"
53
+ c = %i[case fn drop drop2].any? { |x| !m[x].nil? } ? m[:text] : node.text
54
+ { target: t, type: f, case: m[:case]&.sub(/%$/, ""), droploc: droploc,
55
+ text: c }
56
56
  end
57
57
 
58
58
  def inline_anchor_link(node)
59
59
  contents = node.text
60
60
  contents = "" if node.target.gsub(%r{^mailto:}, "") == node.text
61
- attributes = { "target": node.target, "alt": node.attr("title") }
61
+ attributes = { "target": node.target, "alt": node.attr("title"),
62
+ "updatetype": node.attr("updatetype") }
62
63
  noko do |xml|
63
64
  xml.link **attr_code(attributes) do |l|
64
65
  l << contents
@@ -67,8 +68,8 @@ module Asciidoctor
67
68
  end
68
69
 
69
70
  def inline_anchor_bibref(node)
70
- eref_contents = (node.text || node.target || node.id)&.
71
- sub(/^\[?([^\[\]]+?)\]?$/, "[\\1]")
71
+ eref_contents = (node.text || node.target || node.id)
72
+ &.sub(/^\[?([^\[\]]+?)\]?$/, "[\\1]")
72
73
  eref_attributes = { id: node.target || node.id }
73
74
  @refids << (node.target || node.id)
74
75
  noko do |xml|
@@ -105,7 +106,7 @@ module Asciidoctor
105
106
  attrs = {}
106
107
  node.option?("landscape") and attrs[:orientation] = "landscape"
107
108
  node.option?("portrait") and attrs[:orientation] = "portrait"
108
- noko { |xml| xml.pagebreak **attr_code(attrs)}.join
109
+ noko { |xml| xml.pagebreak **attr_code(attrs) }.join
109
110
  end
110
111
 
111
112
  def thematic_break(_node)
@@ -113,41 +114,45 @@ module Asciidoctor
113
114
  end
114
115
 
115
116
  def xml_encode(text)
116
- HTMLEntities.new.encode(text, :basic, :hexadecimal).
117
- gsub(/&amp;gt;/, ">").gsub(/\&amp;lt;/, "<").gsub(/&amp;amp;/, "&").
118
- gsub(/&gt;/, ">").gsub(/&lt;/, "<").gsub(/&amp;/, "&").
119
- gsub(/&quot;/, '"').gsub(/&#xa;/, "\n").gsub(/&amp;#/, "&#")
117
+ HTMLEntities.new.encode(text, :basic, :hexadecimal)
118
+ .gsub(/&amp;gt;/, ">").gsub(/&amp;lt;/, "<").gsub(/&amp;amp;/, "&")
119
+ .gsub(/&gt;/, ">").gsub(/&lt;/, "<").gsub(/&amp;/, "&")
120
+ .gsub(/&quot;/, '"').gsub(/&#xa;/, "\n").gsub(/&amp;#/, "&#")
120
121
  end
121
122
 
122
- def latex_parse(text)
123
+ def latex_parse1(text)
123
124
  lxm_input = Unicode2LaTeX.unicode2latex(HTMLEntities.new.decode(text))
124
125
  results = Latexmath.parse(lxm_input).to_mathml
125
126
  results.nil? and
126
- @log.add('Math', nil,
127
+ @log.add("Math", nil,
127
128
  "latexmlmath failed to process equation:\n#{lxm_input}")
128
129
  results&.sub(%r{<math ([^>]+ )?display="block"}, "<math \\1")
129
130
  end
130
131
 
131
132
  def stem_parse(text, xml, style)
132
133
  if /&lt;([^:>&]+:)?math(\s+[^>&]+)?&gt; |
133
- <([^:>&]+:)?math(\s+[^>&]+)?>/x.match text
134
+ <([^:>&]+:)?math(\s+[^>&]+)?>/x.match? text
134
135
  math = xml_encode(text)
135
136
  xml.stem math, **{ type: "MathML" }
136
- elsif style == :latexmath
137
- latex = latex_parse(text) or return xml.stem **{ type: "MathML" }
138
- xml.stem **{ type: "MathML" } do |s|
139
- math = Nokogiri::XML.fragment(latex.sub(/<\?[^>]+>/, "")).elements[0]
140
- math.delete("alttext")
141
- s.parent.children = math
142
- end
137
+ elsif style == :latexmath then latex_parse(text, xml)
143
138
  else
144
- xml.stem text&.gsub(/\&amp;#/, "&#"), **{ type: "AsciiMath" }
139
+ xml.stem text&.gsub(/&amp;#/, "&#"), **{ type: "AsciiMath" }
145
140
  end
146
141
  end
147
142
 
148
- def highlight_parse(text, xml)
149
- xml << text
143
+ def latex_parse(text, xml)
144
+ latex = latex_parse1(text) or return xml.stem **{ type: "MathML" }
145
+ xml.stem **{ type: "MathML" } do |s|
146
+ math = Nokogiri::XML.fragment(latex.sub(/<\?[^>]+>/, ""))
147
+ .elements[0]
148
+ math.delete("alttext")
149
+ s.parent.children = math
150
150
  end
151
+ end
152
+
153
+ def highlight_parse(text, xml)
154
+ xml << text
155
+ end
151
156
 
152
157
  def inline_quoted(node)
153
158
  noko do |xml|
@@ -182,14 +187,20 @@ module Asciidoctor
182
187
 
183
188
  def image_attributes(node)
184
189
  uri = node.image_uri (node.attr("target") || node.target)
185
- types = /^data:/.match(uri) ? Metanorma::Utils::datauri2mime(uri) : MIME::Types.type_for(uri)
190
+ types = if /^data:/.match?(uri) then Metanorma::Utils::datauri2mime(uri)
191
+ else MIME::Types.type_for(uri)
192
+ end
186
193
  type = types.first.to_s
187
194
  uri = uri.sub(%r{^data:image/\*;}, "data:#{type};")
188
- attr_code(src: uri,
195
+ image_attributes1(node, uri, type)
196
+ end
197
+
198
+ def image_attributes1(node, uri, type)
199
+ attr_code(src: uri,
189
200
  id: Metanorma::Utils::anchor_or_uuid,
190
201
  mimetype: type,
191
202
  height: node.attr("height") || "auto",
192
- width: node.attr("width") || "auto" ,
203
+ width: node.attr("width") || "auto",
193
204
  filename: node.attr("filename"),
194
205
  title: node.attr("titleattr"),
195
206
  alt: node.alt == node.attr("default-alt") ? nil : node.alt)
@@ -197,14 +208,14 @@ module Asciidoctor
197
208
 
198
209
  def inline_image(node)
199
210
  noko do |xml|
200
- xml.image **(image_attributes(node))
211
+ xml.image **image_attributes(node)
201
212
  end.join("")
202
213
  end
203
214
 
204
215
  def inline_indexterm(node)
205
216
  noko do |xml|
206
217
  node.type == :visible and xml << node.text
207
- terms = (node.attr("terms") || [node.text]).map { |x| xml_encode(x) }
218
+ terms = (node.attr("terms") || [node.text]).map { |x| xml_encode(x) }
208
219
  xml.index do |i|
209
220
  i.primary { |x| x << terms[0] }
210
221
  a = terms.dig(1) and i.secondary { |x| x << a }
@@ -45,6 +45,11 @@
45
45
  <optional>
46
46
  <attribute name="alt"/>
47
47
  </optional>
48
+ <optional>
49
+ <attribute name="updatetype">
50
+ <data type="boolean"/>
51
+ </attribute>
52
+ </optional>
48
53
  <text/>
49
54
  </element>
50
55
  </define>
@@ -199,6 +204,18 @@
199
204
  </zeroOrMore>
200
205
  </element>
201
206
  </define>
207
+ <define name="dt">
208
+ <element name="dt">
209
+ <optional>
210
+ <attribute name="id">
211
+ <data type="ID"/>
212
+ </attribute>
213
+ </optional>
214
+ <zeroOrMore>
215
+ <ref name="TextElement"/>
216
+ </zeroOrMore>
217
+ </element>
218
+ </define>
202
219
  <define name="example">
203
220
  <element name="example">
204
221
  <attribute name="id">
@@ -543,6 +560,9 @@
543
560
  </define>
544
561
  <define name="BibDataExtensionType">
545
562
  <ref name="doctype"/>
563
+ <optional>
564
+ <ref name="docsubtype"/>
565
+ </optional>
546
566
  <optional>
547
567
  <ref name="editorialgroup"/>
548
568
  </optional>
@@ -890,6 +910,14 @@
890
910
  </define>
891
911
  </include>
892
912
  <!-- end overrides -->
913
+ <define name="docsubtype">
914
+ <element name="subdoctype">
915
+ <ref name="DocumentSubtype"/>
916
+ </element>
917
+ </define>
918
+ <define name="DocumentSubtype">
919
+ <text/>
920
+ </define>
893
921
  <define name="colgroup">
894
922
  <element name="colgroup">
895
923
  <oneOrMore>
@@ -939,7 +967,34 @@
939
967
  <define name="concept">
940
968
  <element name="concept">
941
969
  <optional>
942
- <attribute name="term"/>
970
+ <attribute name="ital">
971
+ <data type="boolean"/>
972
+ </attribute>
973
+ </optional>
974
+ <optional>
975
+ <attribute name="ref">
976
+ <data type="boolean"/>
977
+ </attribute>
978
+ </optional>
979
+ <optional>
980
+ <element name="refterm">
981
+ <zeroOrMore>
982
+ <choice>
983
+ <ref name="PureTextElement"/>
984
+ <ref name="stem"/>
985
+ </choice>
986
+ </zeroOrMore>
987
+ </element>
988
+ </optional>
989
+ <optional>
990
+ <element name="renderterm">
991
+ <zeroOrMore>
992
+ <choice>
993
+ <ref name="PureTextElement"/>
994
+ <ref name="stem"/>
995
+ </choice>
996
+ </zeroOrMore>
997
+ </element>
943
998
  </optional>
944
999
  <choice>
945
1000
  <ref name="eref"/>
@@ -965,6 +1020,9 @@
965
1020
  </attribute>
966
1021
  <attribute name="name"/>
967
1022
  <attribute name="action"/>
1023
+ <optional>
1024
+ <attribute name="class"/>
1025
+ </optional>
968
1026
  <zeroOrMore>
969
1027
  <choice>
970
1028
  <ref name="TextElement"/>
@@ -1457,26 +1515,26 @@
1457
1515
  <optional>
1458
1516
  <ref name="section-title"/>
1459
1517
  </optional>
1460
- <group>
1518
+ <choice>
1461
1519
  <choice>
1462
1520
  <group>
1463
- <zeroOrMore>
1521
+ <oneOrMore>
1464
1522
  <ref name="BasicBlock"/>
1465
- </zeroOrMore>
1523
+ </oneOrMore>
1466
1524
  <zeroOrMore>
1467
1525
  <ref name="note"/>
1468
1526
  </zeroOrMore>
1469
1527
  </group>
1470
1528
  <ref name="amend"/>
1471
1529
  </choice>
1472
- <zeroOrMore>
1530
+ <oneOrMore>
1473
1531
  <choice>
1474
1532
  <ref name="clause-subsection"/>
1475
1533
  <ref name="terms"/>
1476
1534
  <ref name="definitions"/>
1477
1535
  </choice>
1478
- </zeroOrMore>
1479
- </group>
1536
+ </oneOrMore>
1537
+ </choice>
1480
1538
  </define>
1481
1539
  <define name="Annex-Section">
1482
1540
  <optional>
@@ -75,7 +75,7 @@ module Asciidoctor
75
75
  def supply_br(lines)
76
76
  ignore = false
77
77
  lines.each_with_index do |l, i|
78
- /^(--+|====+|\|===|\.\.\.\.+|\*\*\*\*+|\+\+\+\++|\`\`\`\`+|____\+)$/
78
+ /^(--+|====+|\|===|\.\.\.\.+|\*\*\*\*+|\+\+\+\++|````+|____\+)$/
79
79
  .match(l) && (ignore = !ignore)
80
80
  next if l.empty? || l.match(/ \+$/) || /^\[.*\]$/.match?(l) || ignore
81
81
  next if i == lines.size - 1 ||
@@ -107,7 +107,7 @@ module Asciidoctor
107
107
  if (attributes.size == 1) && attributes.key?("text")
108
108
  rt = attributes["text"]
109
109
  elsif (attributes.size == 2) && attributes.key?(1) &&
110
- attributes.key?("rpbegin")
110
+ attributes.key?("rpbegin")
111
111
  rt = attributes[1] || ""
112
112
  else
113
113
  rpbegin = attributes["rpbegin"]
@@ -143,7 +143,7 @@ module Asciidoctor
143
143
  para.set_attr("caption", "TODO")
144
144
  para.lines[0].sub!(/^TODO: /, "")
145
145
  todo = Block.new(parent, :admonition, attributes: para.attributes,
146
- source: para.lines, content_model: :compound)
146
+ source: para.lines, content_model: :compound)
147
147
  parent.blocks[parent.blocks.index(para)] = todo
148
148
  end
149
149
  end
@@ -168,9 +168,11 @@ module Asciidoctor
168
168
  def process(parent, target, attrs)
169
169
  /^(?<lang>[^-]*)(-(?<script>.*))?$/ =~ target
170
170
  out = Asciidoctor::Inline.new(parent, :quoted, attrs["text"]).convert
171
- script ?
172
- %{<variant lang=#{lang} script=#{script}>#{out}</variant>} :
171
+ if script
172
+ %{<variant lang=#{lang} script=#{script}>#{out}</variant>}
173
+ else
173
174
  %{<variant lang=#{lang}>#{out}</variant>}
175
+ end
174
176
  end
175
177
  end
176
178
 
@@ -4,18 +4,17 @@ 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['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
8
- ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
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)
12
12
  end
13
13
  end
14
14
  raise "PlantUML not installed"
15
- nil
16
15
  end
17
16
 
18
- def self.run umlfile, outfile
17
+ def self.run(umlfile, outfile)
19
18
  system "plantuml #{umlfile.path}" or (warn $? and return false)
20
19
  i = 0
21
20
  until !Gem.win_platform? || File.exist?(outfile) || i == 15
@@ -29,9 +28,9 @@ module Asciidoctor
29
28
  # sleep need for windows because dot works in separate process and
30
29
  # plantuml process may finish earlier then dot, as result png file
31
30
  # maybe not created yet after plantuml finish
32
- def self.generate_file parent, reader
31
+ def self.generate_file(parent, reader)
33
32
  localdir = Metanorma::Utils::localdir(parent.document)
34
- imagesdir = parent.document.attr('imagesdir')
33
+ imagesdir = parent.document.attr("imagesdir")
35
34
  umlfile, outfile = save_plantuml parent, reader, localdir
36
35
  run(umlfile, outfile) or raise "No image output from PlantUML (#{umlfile}, #{outfile})!"
37
36
  umlfile.unlink
@@ -40,7 +39,7 @@ module Asciidoctor
40
39
  File.writable?(localdir) or raise "Destination path #{path} not writable for PlantUML!"
41
40
  path.mkpath
42
41
  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!"
42
+ # File.exist?(path) or raise "Destination path #{path} already exists for PlantUML!"
44
43
 
45
44
  # Warning: metanorma/metanorma-standoc#187
46
45
  # Windows Ruby 2.4 will crash if a Tempfile is "mv"ed.
@@ -51,21 +50,21 @@ module Asciidoctor
51
50
  imagesdir ? filename : File.join(path, filename)
52
51
  end
53
52
 
54
- def self.save_plantuml parent, reader, localdir
53
+ def self.save_plantuml(_parent, reader, _localdir)
55
54
  src = reader.source
56
55
  reader.lines.first.sub(/\s+$/, "").match /^@startuml($| )/ or
57
56
  src = "@startuml\n#{src}\n@enduml\n"
58
57
  /^@startuml (?<fn>[^\n]+)\n/ =~ src
59
- Tempfile.open(["plantuml", ".pml"], :encoding => "utf-8") do |f|
58
+ Tempfile.open(["plantuml", ".pml"], encoding: "utf-8") do |f|
60
59
  f.write(src)
61
60
  [f, File.join(File.dirname(f.path),
62
- (fn || File.basename(f.path, ".pml")) + ".png")]
61
+ "#{fn || File.basename(f.path, '.pml')}.png")]
63
62
  end
64
63
  end
65
64
 
66
- def self.generate_attrs attrs
67
- through_attrs = %w(id align float title role width height alt).
68
- inject({}) do |memo, key|
65
+ def self.generate_attrs(attrs)
66
+ %w(id align float title role width height alt)
67
+ .inject({}) do |memo, key|
69
68
  memo[key] = attrs[key] if attrs.has_key? key
70
69
  memo
71
70
  end
@@ -81,19 +80,18 @@ module Asciidoctor
81
80
  def abort(parent, reader, attrs, msg)
82
81
  warn msg
83
82
  attrs["language"] = "plantuml"
84
- create_listing_block parent, reader.source, attrs.reject { |k, v| k == 1 }
83
+ create_listing_block parent, reader.source,
84
+ (attrs.reject { |k, _v| k == 1 })
85
85
  end
86
86
 
87
87
  def process(parent, reader, attrs)
88
- begin
89
- PlantUMLBlockMacroBackend.plantuml_installed?
90
- filename = PlantUMLBlockMacroBackend.generate_file(parent, reader)
91
- through_attrs = PlantUMLBlockMacroBackend.generate_attrs attrs
92
- through_attrs["target"] = filename
93
- create_image_block parent, through_attrs
94
- rescue StandardError => e
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,81 @@ 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 'name', 'termxref'
44
+ name_positional_attributes "name", "termxref"
45
+ using_format :short
46
+
47
+ def process(_parent, _target, attrs)
48
+ termref = attrs["termxref"] || attrs["name"]
49
+ "<concept type='term'><termxref>#{attrs['name']}</termxref>"\
50
+ "<renderterm>#{termref}</renderterm><xrefrender/></concept>"
51
+ end
52
+ end
53
+
54
+ class SymbolRefInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
55
+ use_dsl
56
+ named :symbol
57
+ name_positional_attributes "name", "termxref"
44
58
  using_format :short
45
59
 
46
60
  def process(_parent, _target, attrs)
47
- termref = attrs['termxref'] || attrs['name']
48
- "<em>#{attrs['name']}</em> (<termxref>#{termref}</termxref>)"
61
+ termref = attrs["termxref"] || attrs["name"]
62
+ "<concept type='symbol'><termxref>#{attrs['name']}</termxref>"\
63
+ "<renderterm>#{termref}</renderterm><xrefrender/></concept>"
49
64
  end
50
65
  end
51
66
 
67
+ # Possibilities:
68
+ # {{<<id>>, term}}
69
+ # {{<<id>>, term, text}}
70
+ # {{<<termbase:id>>, term}}
71
+ # {{<<termbase:id>>, term, text}}
72
+ # {{term}} equivalent to term:[term]
73
+ # {{term, text}} equivalent to term:[term, text]
74
+ # text may optionally be followed by crossreference-rendering, options=""
52
75
  class ConceptInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
53
76
  use_dsl
54
77
  named :concept
55
- name_positional_attributes "id", "word", "term"
56
- # match %r{concept:(?<target>[^\[]*)\[(?<content>|.*?[^\\])\]$}
57
78
  match /\{\{(?<content>|.*?[^\\])\}\}/
58
79
  using_format :short
59
80
 
60
- # deal with locality attrs and their disruption of positional attrs
61
- def preprocess_attrs(attrs)
62
- attrs.delete("term") if attrs["term"] && !attrs["word"]
63
- attrs.delete(3) if attrs[3] == attrs["term"]
64
- a = attrs.keys.reject { |k| k.is_a?(String) || [1, 2].include?(k) }
65
- attrs["word"] ||= attrs[a[0]] if !a.empty?
66
- attrs["term"] ||= attrs[a[1]] if a.length > 1
67
- attrs
81
+ def preprocess_attrs(target)
82
+ m = /^(?<id>&lt;&lt;.+?&gt;&gt;)?(?<rest>.*)$/.match(target)
83
+ ret = { id: m[:id]&.sub(/^&lt;&lt;/, "")&.sub(/&gt;&gt;$/, "") }
84
+ if m2 = /^(?<rest>.*?)(?<opt>,option=.+)$/.match(m[:rest].sub(/^,/, ""))
85
+ ret[:opt] = CSV.parse_line(m2[:opt].sub(/^,option=/, "")
86
+ .sub(/^"(.+)"$/, "\\1").sub(/^'(.+)'$/, "\\1"))
87
+ attrs = CSV.parse_line(m2[:rest]) || []
88
+ else
89
+ attrs = CSV.parse_line(m[:rest].sub(/^,/, "")) || []
90
+ end
91
+ ret.merge(term: attrs[0], word: attrs[1] || attrs[0],
92
+ xrefrender: attrs[2])
93
+ end
94
+
95
+ def generate_attrs(opts)
96
+ ret = ""
97
+ opts.include?("noital") and ret += " ital='false'"
98
+ opts.include?("noref") and ret += " ref='false'"
99
+ opts.include?("ital") and ret += " ital='true'"
100
+ opts.include?("ref") and ret += " ref='true'"
101
+ ret
68
102
  end
69
103
 
70
- def process(parent, _target, attr)
71
- attr = preprocess_attrs(attr)
72
- localities = attr.keys.reject { |k| %w(id word term).include? k }.
73
- reject { |k| k.is_a? Numeric }.
74
- map { |k| "#{k}=#{attr[k]}" }.join(",")
75
- text = [localities, attr["word"]].reject{ |k| k.nil? || k.empty? }.
76
- join(",")
77
- out = Asciidoctor::Inline.new(parent, :quoted, text).convert
78
- %{<concept key="#{attr['id']}" term="#{attr['term']}">#{out}</concept>}
104
+ def process(parent, target, _attrs)
105
+ attrs = preprocess_attrs(target)
106
+ termout = Asciidoctor::Inline.new(parent, :quoted, attrs[:term]).convert
107
+ wordout = Asciidoctor::Inline.new(parent, :quoted, attrs[:word]).convert
108
+ xrefout = Asciidoctor::Inline.new(parent, :quoted,
109
+ attrs[:xrefrender]).convert
110
+ optout = generate_attrs(attrs[:opt] || [])
111
+ attrs[:id] and return "<concept#{optout} key='#{attrs[:id]}'><refterm>"\
112
+ "#{termout}</refterm><renderterm>#{wordout}</renderterm>"\
113
+ "<xrefrender>#{xrefout}</xrefrender></concept>"
114
+ "<concept#{optout}><termxref>#{termout}</termxref><renderterm>"\
115
+ "#{wordout}</renderterm><xrefrender>#{xrefout}</xrefrender></concept>"
79
116
  end
80
117
  end
81
118
  end