metanorma-standoc 1.9.3 → 1.10.2

Sign up to get free protection for your applications and to get access to all the features.
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