metanorma-standoc 1.9.2 → 1.10.1

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 (66) 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/biblio.rng +1 -0
  7. data/lib/asciidoctor/standoc/blocks.rb +1 -1
  8. data/lib/asciidoctor/standoc/cleanup.rb +34 -15
  9. data/lib/asciidoctor/standoc/cleanup_block.rb +0 -1
  10. data/lib/asciidoctor/standoc/cleanup_boilerplate.rb +2 -2
  11. data/lib/asciidoctor/standoc/cleanup_footnotes.rb +0 -1
  12. data/lib/asciidoctor/standoc/cleanup_inline.rb +117 -77
  13. data/lib/asciidoctor/standoc/cleanup_maths.rb +0 -1
  14. data/lib/asciidoctor/standoc/cleanup_ref.rb +7 -0
  15. data/lib/asciidoctor/standoc/cleanup_section.rb +73 -137
  16. data/lib/asciidoctor/standoc/cleanup_section_names.rb +75 -0
  17. data/lib/asciidoctor/standoc/cleanup_terms.rb +19 -18
  18. data/lib/asciidoctor/standoc/converter.rb +1 -0
  19. data/lib/asciidoctor/standoc/front.rb +1 -2
  20. data/lib/asciidoctor/standoc/front_contributor.rb +66 -42
  21. data/lib/asciidoctor/standoc/inline.rb +45 -34
  22. data/lib/asciidoctor/standoc/isodoc.rng +66 -10
  23. data/lib/asciidoctor/standoc/macros.rb +7 -5
  24. data/lib/asciidoctor/standoc/macros_plantuml.rb +21 -23
  25. data/lib/asciidoctor/standoc/macros_terms.rb +60 -23
  26. data/lib/asciidoctor/standoc/section.rb +19 -12
  27. data/lib/asciidoctor/standoc/term_lookup_cleanup.rb +87 -33
  28. data/lib/asciidoctor/standoc/terms.rb +1 -1
  29. data/lib/asciidoctor/standoc/utils.rb +0 -1
  30. data/lib/asciidoctor/standoc/validate.rb +22 -8
  31. data/lib/isodoc/html/html_titlepage.html +81 -0
  32. data/lib/isodoc/html/htmlstyle.css +983 -0
  33. data/lib/isodoc/html/htmlstyle.scss +714 -0
  34. data/lib/isodoc/html/scripts.html +71 -0
  35. data/lib/metanorma/standoc/processor.rb +16 -7
  36. data/lib/metanorma/standoc/version.rb +1 -1
  37. data/metanorma-standoc.gemspec +3 -3
  38. data/spec/asciidoctor/base_spec.rb +697 -557
  39. data/spec/asciidoctor/blocks_spec.rb +6 -8
  40. data/spec/asciidoctor/cleanup_sections_spec.rb +14 -14
  41. data/spec/asciidoctor/cleanup_spec.rb +899 -688
  42. data/spec/asciidoctor/inline_spec.rb +62 -14
  43. data/spec/asciidoctor/isobib_cache_spec.rb +4 -6
  44. data/spec/asciidoctor/lists_spec.rb +149 -137
  45. data/spec/asciidoctor/macros_json2text_spec.rb +1 -1
  46. data/spec/asciidoctor/macros_plantuml_spec.rb +8 -8
  47. data/spec/asciidoctor/macros_spec.rb +646 -169
  48. data/spec/asciidoctor/refs_dl_spec.rb +4 -4
  49. data/spec/asciidoctor/refs_spec.rb +1527 -1532
  50. data/spec/asciidoctor/section_spec.rb +58 -22
  51. data/spec/asciidoctor/table_spec.rb +6 -6
  52. data/spec/asciidoctor/validate_spec.rb +352 -304
  53. data/spec/spec_helper.rb +2 -0
  54. data/spec/vcr_cassettes/dated_iso_ref_joint_iso_iec.yml +49 -49
  55. data/spec/vcr_cassettes/isobib_get_123.yml +12 -12
  56. data/spec/vcr_cassettes/isobib_get_123_1.yml +27 -27
  57. data/spec/vcr_cassettes/isobib_get_123_1_fr.yml +35 -35
  58. data/spec/vcr_cassettes/isobib_get_123_2001.yml +14 -14
  59. data/spec/vcr_cassettes/isobib_get_124.yml +14 -14
  60. data/spec/vcr_cassettes/rfcbib_get_rfc8341.yml +14 -14
  61. data/spec/vcr_cassettes/separates_iev_citations_by_top_level_clause.yml +44 -44
  62. metadata +12 -11
  63. data/lib/liquid/custom_blocks/key_iterator.rb +0 -21
  64. data/lib/liquid/custom_blocks/with_json_nested_context.rb +0 -18
  65. data/lib/liquid/custom_blocks/with_yaml_nested_context.rb +0 -19
  66. data/lib/liquid/custom_filters/values.rb +0 -7
@@ -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
@@ -12,7 +12,7 @@ module Asciidoctor
12
12
  def sectiontype1(node)
13
13
  node&.attr("heading")&.downcase ||
14
14
  node.title.gsub(%r{<index>.*?</index>}m, "").gsub(/<[^>]+>/, "")
15
- .strip.downcase
15
+ .strip.downcase
16
16
  end
17
17
 
18
18
  def sectiontype(node, level = true)
@@ -21,6 +21,7 @@ module Asciidoctor
21
21
  return ret1 if "symbols and abbreviated terms" == ret1
22
22
  return nil unless !level || node.level == 1
23
23
  return nil if @seen_headers.include? ret
24
+
24
25
  @seen_headers << ret
25
26
  ret1
26
27
  end
@@ -48,11 +49,14 @@ module Asciidoctor
48
49
  script: node.attributes["script"],
49
50
  number: node.attributes["number"],
50
51
  type: node.attributes["type"],
51
- annex: ( ((node.attr("style") == "appendix" || node.role == "appendix") &&
52
- node.level == 1) ? true : nil),
53
- preface: (
54
- (node.role == "preface" || node.attr("style") == "preface") ? true : nil) }
52
+ annex: (if (node.attr("style") == "appendix" || node.role == "appendix") &&
53
+ node.level == 1
54
+ true
55
+ end),
56
+ preface: (
57
+ node.role == "preface" || node.attr("style") == "preface" ? true : nil) }
55
58
  return ret unless node.attributes["change"]
59
+
56
60
  ret.merge(change: node.attributes["change"],
57
61
  path: node.attributes["path"],
58
62
  path_end: node.attributes["path_end"],
@@ -75,7 +79,7 @@ module Asciidoctor
75
79
  symbols_parse(symbols_attrs(node, a), xml, node)
76
80
  when "acknowledgements"
77
81
  acknowledgements_parse(a, xml, node)
78
- when "bibliography"
82
+ when "bibliography"
79
83
  bibliography_parse(a, xml, node)
80
84
  else
81
85
  if @term_def then term_def_subclause_parse(a, xml, node)
@@ -88,9 +92,9 @@ module Asciidoctor
88
92
  bibliography_parse(a, xml, node)
89
93
  elsif node.attr("style") == "bibliography"
90
94
  bibliography_parse(a, xml, node)
91
- elsif node.attr("style") == "abstract"
95
+ elsif node.attr("style") == "abstract"
92
96
  abstract_parse(a, xml, node)
93
- elsif node.attr("style") == "index"
97
+ elsif node.attr("style") == "index"
94
98
  indexsect_parse(a, xml, node)
95
99
  elsif node.attr("style") == "appendix" && node.level == 1
96
100
  annex_parse(a, xml, node)
@@ -102,10 +106,13 @@ module Asciidoctor
102
106
  end
103
107
 
104
108
  def set_obligation(attrs, node)
105
- attrs[:obligation] = node.attributes.has_key?("obligation") ?
106
- node.attr("obligation") :
107
- node.parent.attributes.has_key?("obligation") ?
108
- node.parent.attr("obligation") : "normative"
109
+ attrs[:obligation] = if node.attributes.has_key?("obligation")
110
+ node.attr("obligation")
111
+ elsif node.parent.attributes.has_key?("obligation")
112
+ node.parent.attr("obligation")
113
+ else
114
+ "normative"
115
+ end
109
116
  end
110
117
 
111
118
  def preamble(node)
@@ -3,81 +3,135 @@
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
- AUTOMATIC_GENERATED_ID_REGEXP = /\A_/
10
- EXISTING_TERM_REGEXP = /\Aterm-/
7
+ AUTOMATIC_GENERATED_ID_REGEXP = /\A_/.freeze
8
+ EXISTING_TERM_REGEXP = /\Aterm-/.freeze
9
+ EXISTING_SYMBOL_REGEXP = /\Asymbol-/.freeze
11
10
 
12
11
  attr_reader :xmldoc, :termlookup, :log
13
12
 
14
13
  def initialize(xmldoc, log)
15
14
  @xmldoc = xmldoc
16
15
  @log = log
17
- @termlookup = {}
16
+ @termlookup = { term: {}, symbol: {} }
17
+ @idhash = {}
18
18
  end
19
19
 
20
20
  def call
21
+ @idhash = populate_idhash
21
22
  @termlookup = replace_automatic_generated_ids_terms
22
23
  set_termxref_tags_target
24
+ concept_cleanup
23
25
  end
24
26
 
25
27
  private
26
28
 
29
+ def concept_cleanup
30
+ xmldoc.xpath("//concept").each do |n|
31
+ n.delete("type")
32
+ end
33
+ end
34
+
35
+ def populate_idhash
36
+ xmldoc.xpath("//*[@id]").each_with_object({}) do |n, mem|
37
+ next unless /^(term|symbol)-/.match?(n["id"])
38
+
39
+ mem[n["id"]] = true
40
+ end
41
+ end
42
+
27
43
  def set_termxref_tags_target
28
- xmldoc.xpath('//termxref').each do |node|
44
+ xmldoc.xpath("//termxref").each do |node|
29
45
  target = normalize_ref_id(node.text)
30
- if termlookup[target].nil?
46
+ if termlookup[:term][target].nil? && termlookup[:symbol][target].nil?
31
47
  remove_missing_ref(node, target)
32
48
  next
33
49
  end
34
- modify_ref_node(node, target)
50
+ x = node.at("../xrefrender")
51
+ modify_ref_node(x, target)
52
+ node.name = "refterm"
35
53
  end
36
54
  end
37
55
 
38
56
  def remove_missing_ref(node, target)
39
- log.add('AsciiDoc Input', node,
57
+ if node.at("../concept[@type = 'symbol']")
58
+ remove_missing_ref_symbol(node, target)
59
+ else
60
+ remove_missing_ref_term(node, target)
61
+ end
62
+ end
63
+
64
+ def remove_missing_ref_term(node, target)
65
+ log.add("AsciiDoc Input", node,
40
66
  %(Error: Term reference in `term[#{target}]` missing: \
41
67
  "#{target}" is not defined in document))
42
- term_name_node = node.previous.previous
43
- term_name_node.remove
44
- term_name_node.name = "strong"
45
- term_name_node.children.first.content =
46
- %(term "#{term_name_node.text}" not resolved)
47
- node.add_previous_sibling(term_name_node)
48
- node.remove
68
+ node.name = "strong"
69
+ node.at("../xrefrender").remove
70
+ display = node&.at("../renderterm")&.remove&.children
71
+ display = [] if display.nil? || display&.to_xml == node.text
72
+ d = display.empty? ? "" : ", display <tt>#{display.to_xml}</tt>"
73
+ node.children = "term <tt>#{node.text}</tt>#{d} "\
74
+ "not resolved via ID <tt>#{target}</tt>"
75
+ end
76
+
77
+ def remove_missing_ref_symbol(node, target)
78
+ log.add("AsciiDoc Input", node,
79
+ %(Error: Symbol reference in `symbol[#{target}]` missing: \
80
+ "#{target}" is not defined in document))
81
+ node.name = "strong"
82
+ node.at("../xrefrender").remove
83
+ display = node&.at("../renderterm")&.remove&.children
84
+ display = [] if display.nil? || display&.to_xml == node.text
85
+ d = display.empty? ? "" : ", display <tt>#{display.to_xml}</tt>"
86
+ node.children = "symbol <tt>#{node.text}</tt>#{d} "\
87
+ "not resolved via ID <tt>#{target}</tt>"
49
88
  end
50
89
 
51
90
  def modify_ref_node(node, target)
52
- node.name = 'xref'
53
- node['target'] = termlookup[target]
54
- node.children.remove
55
- node.remove_attribute('defaultref')
91
+ node.name = "xref"
92
+ s = termlookup[:symbol][target]
93
+ t = termlookup[:term][target]
94
+ type = node.parent["type"]
95
+ if type == "term" || !type && t
96
+ node["target"] = t
97
+ elsif type == "symbol" || !type && s
98
+ node["target"] = s
99
+ end
56
100
  end
57
101
 
58
102
  def replace_automatic_generated_ids_terms
59
- xmldoc.xpath('//term').each.with_object({}) do |term_node, res|
60
- normalize_id_and_memorize(term_node, res, './preferred')
103
+ r = xmldoc.xpath("//term").each.with_object({}) do |n, res|
104
+ normalize_id_and_memorize(n, res, "./preferred", "term")
61
105
  end
106
+ s = xmldoc.xpath("//definitions//dt").each.with_object({}) do |n, res|
107
+ normalize_id_and_memorize(n, res, ".", "symbol")
108
+ end
109
+ { term: r, symbol: s }
62
110
  end
63
111
 
64
- def normalize_id_and_memorize(term_node, res_table, text_selector)
65
- term_text = normalize_ref_id(term_node.at(text_selector).text)
66
- unless AUTOMATIC_GENERATED_ID_REGEXP.match(term_node['id']).nil?
67
- term_node['id'] = unique_text_id(term_text)
68
- end
69
- res_table[term_text] = term_node['id']
112
+ def normalize_id_and_memorize(node, res_table, text_selector, prefix)
113
+ term_text = normalize_ref_id(node.at(text_selector).text)
114
+ unless AUTOMATIC_GENERATED_ID_REGEXP.match(node["id"]).nil? &&
115
+ !node["id"].nil?
116
+ id = unique_text_id(term_text, prefix)
117
+ node["id"] = id
118
+ @idhash[id] = true
119
+ end
120
+ res_table[term_text] = node["id"]
70
121
  end
71
122
 
72
123
  def normalize_ref_id(text)
73
- text.downcase.gsub(/[[:space:]]/, '-')
124
+ text.downcase.gsub(/[[:space:]]/, "-")
74
125
  end
75
126
 
76
- def unique_text_id(text)
77
- return "term-#{text}" if xmldoc.at("//*[@id = 'term-#{text}']").nil?
127
+ def unique_text_id(text, prefix)
128
+ unless @idhash["#{prefix}-#{text}"]
129
+ return "#{prefix}-#{text}"
130
+ end
131
+
78
132
  (1..Float::INFINITY).lazy.each do |index|
79
- if xmldoc.at("//*[@id = 'term-#{text}-#{index}']").nil?
80
- break("term-#{text}-#{index}")
133
+ unless @idhash["#{prefix}-#{text}-#{index}"]
134
+ break("#{prefix}-#{text}-#{index}")
81
135
  end
82
136
  end
83
137
  end