metanorma-standoc 1.7.3 → 1.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/lib/asciidoctor/standoc/base.rb +31 -44
  3. data/lib/asciidoctor/standoc/basicdoc.rng +5 -3
  4. data/lib/asciidoctor/standoc/blocks.rb +7 -7
  5. data/lib/asciidoctor/standoc/blocks_notes.rb +2 -2
  6. data/lib/asciidoctor/standoc/cleanup.rb +5 -6
  7. data/lib/asciidoctor/standoc/cleanup_block.rb +3 -3
  8. data/lib/asciidoctor/standoc/cleanup_boilerplate.rb +56 -0
  9. data/lib/asciidoctor/standoc/cleanup_inline.rb +22 -26
  10. data/lib/asciidoctor/standoc/cleanup_ref.rb +0 -85
  11. data/lib/asciidoctor/standoc/cleanup_ref_dl.rb +94 -0
  12. data/lib/asciidoctor/standoc/converter.rb +0 -1
  13. data/lib/asciidoctor/standoc/front.rb +2 -2
  14. data/lib/asciidoctor/standoc/inline.rb +1 -1
  15. data/lib/asciidoctor/standoc/isodoc.rng +3 -0
  16. data/lib/asciidoctor/standoc/lists.rb +2 -2
  17. data/lib/asciidoctor/standoc/macros_plantuml.rb +1 -1
  18. data/lib/asciidoctor/standoc/ref_sect.rb +2 -2
  19. data/lib/asciidoctor/standoc/reqt.rb +6 -1
  20. data/lib/asciidoctor/standoc/section.rb +3 -80
  21. data/lib/asciidoctor/standoc/table.rb +1 -1
  22. data/lib/asciidoctor/standoc/terms.rb +125 -0
  23. data/lib/asciidoctor/standoc/utils.rb +2 -96
  24. data/lib/metanorma/standoc/version.rb +1 -1
  25. data/metanorma-standoc.gemspec +2 -2
  26. data/spec/asciidoctor-standoc/base_spec.rb +30 -1
  27. data/spec/asciidoctor-standoc/cleanup_spec.rb +111 -2
  28. data/spec/asciidoctor-standoc/isobib_cache_spec.rb +4 -4
  29. data/spec/asciidoctor-standoc/refs_dl_spec.rb +13 -1
  30. data/spec/asciidoctor-standoc/refs_spec.rb +12 -12
  31. data/spec/asciidoctor-standoc/section_spec.rb +5 -3
  32. data/spec/vcr_cassettes/dated_iso_ref_joint_iso_iec.yml +47 -47
  33. data/spec/vcr_cassettes/isobib_get_123.yml +11 -11
  34. data/spec/vcr_cassettes/isobib_get_123_1.yml +26 -26
  35. data/spec/vcr_cassettes/isobib_get_123_1_fr.yml +36 -36
  36. data/spec/vcr_cassettes/isobib_get_123_2001.yml +14 -14
  37. data/spec/vcr_cassettes/isobib_get_124.yml +14 -14
  38. data/spec/vcr_cassettes/rfcbib_get_rfc8341.yml +8 -8
  39. data/spec/vcr_cassettes/separates_iev_citations_by_top_level_clause.yml +45 -45
  40. metadata +19 -18
  41. data/lib/asciidoctor/standoc/log.rb +0 -59
@@ -120,91 +120,6 @@ module Asciidoctor
120
120
  end
121
121
  end
122
122
 
123
- def ref_dl_cleanup(xmldoc)
124
- xmldoc.xpath("//clause[@bibitem = 'true']").each do |c|
125
- bib = dl_bib_extract(c) or next
126
- validate_ref_dl(bib, c)
127
- bibitemxml = RelatonBib::BibliographicItem.new(RelatonBib::HashConverter::hash_to_bib(bib)).to_xml or next
128
- bibitem = Nokogiri::XML(bibitemxml)
129
- bibitem.root["id"] = c["id"] if c["id"] && !/^_/.match(c["id"])
130
- c.replace(bibitem.root)
131
- end
132
- end
133
-
134
- def validate_ref_dl(bib, c)
135
- id = bib["id"]
136
- id ||= c["id"] unless /^_/.match(c["id"]) # do not accept implicit id
137
- unless id
138
- @log.add("Anchors", c, "The following reference is missing an anchor:\n" + c.to_xml)
139
- return
140
- end
141
- bib["title"] or @log.add("Bibliography", c, "Reference #{id} is missing a title")
142
- bib["docid"] or @log.add("Bibliography", c, "Reference #{id} is missing a document identifier (docid)")
143
- end
144
-
145
- def extract_from_p(tag, bib, key)
146
- return unless bib[tag]
147
- "<#{key}>#{bib[tag].at('p').children}</#{key}>"
148
- end
149
-
150
- # if the content is a single paragraph, replace it with its children
151
- # single links replaced with uri
152
- def p_unwrap(p)
153
- elems = p.elements
154
- if elems.size == 1 && elems[0].name == "p"
155
- link_unwrap(elems[0]).children.to_xml.strip
156
- else
157
- p.to_xml.strip
158
- end
159
- end
160
-
161
- def link_unwrap(p)
162
- elems = p.elements
163
- if elems.size == 1 && elems[0].name == "link"
164
- p.at("./link").replace(elems[0]["target"].strip)
165
- end
166
- p
167
- end
168
-
169
- def dd_bib_extract(dtd)
170
- return nil if dtd.children.empty?
171
- dtd.at("./dl") and return dl_bib_extract(dtd)
172
- elems = dtd.remove.elements
173
- return p_unwrap(dtd) unless elems.size == 1 && %w(ol ul).include?(elems[0].name)
174
- ret = []
175
- elems[0].xpath("./li").each do |li|
176
- ret << p_unwrap(li)
177
- end
178
- ret
179
- end
180
-
181
- def add_to_hash(bib, key, val)
182
- Utils::set_nested_value(bib, key.split(/\./), val)
183
- end
184
-
185
- # definition list, with at most one level of unordered lists
186
- def dl_bib_extract(c, nested = false)
187
- dl = c.at("./dl") or return
188
- bib = {}
189
- key = ""
190
- dl.xpath("./dt | ./dd").each do |dtd|
191
- dtd.name == "dt" and key = dtd.text.sub(/:+$/, "") or add_to_hash(bib, key, dd_bib_extract(dtd))
192
- end
193
- c.xpath("./clause").each do |c1|
194
- key = c1&.at("./title")&.text&.downcase&.strip
195
- next unless %w(contributor relation series).include? key
196
- add_to_hash(bib, key, dl_bib_extract(c1, true))
197
- end
198
- if !nested and c.at("./title")
199
- title = c.at("./title").remove.children.to_xml
200
- bib["title"] = [bib["title"]] if bib["title"].is_a? Hash
201
- bib["title"] = [bib["title"]] if bib["title"].is_a? String
202
- bib["title"] = [] unless bib["title"]
203
- bib["title"] << title if !title.empty?
204
- end
205
- bib
206
- end
207
-
208
123
  def fetch_termbase(termbase, id)
209
124
  ""
210
125
  end
@@ -0,0 +1,94 @@
1
+ require "set"
2
+ require "relaton_bib"
3
+
4
+ module Asciidoctor
5
+ module Standoc
6
+ module Cleanup
7
+ def ref_dl_cleanup(xmldoc)
8
+ xmldoc.xpath("//clause[@bibitem = 'true']").each do |c|
9
+ bib = dl_bib_extract(c) or next
10
+ validate_ref_dl(bib, c)
11
+ bibitemxml = RelatonBib::BibliographicItem.new(RelatonBib::HashConverter::hash_to_bib(bib)).to_xml or next
12
+ bibitem = Nokogiri::XML(bibitemxml)
13
+ bibitem.root["id"] = c["id"] if c["id"] && !/^_/.match(c["id"])
14
+ c.replace(bibitem.root)
15
+ end
16
+ end
17
+
18
+ def validate_ref_dl(bib, c)
19
+ id = bib["id"]
20
+ id ||= c["id"] unless /^_/.match(c["id"]) # do not accept implicit id
21
+ unless id
22
+ @log.add("Anchors", c, "The following reference is missing an anchor:\n" + c.to_xml)
23
+ return
24
+ end
25
+ @refids << id
26
+ bib["title"] or @log.add("Bibliography", c, "Reference #{id} is missing a title")
27
+ bib["docid"] or @log.add("Bibliography", c, "Reference #{id} is missing a document identifier (docid)")
28
+ end
29
+
30
+ def extract_from_p(tag, bib, key)
31
+ return unless bib[tag]
32
+ "<#{key}>#{bib[tag].at('p').children}</#{key}>"
33
+ end
34
+
35
+ # if the content is a single paragraph, replace it with its children
36
+ # single links replaced with uri
37
+ def p_unwrap(p)
38
+ elems = p.elements
39
+ if elems.size == 1 && elems[0].name == "p"
40
+ link_unwrap(elems[0]).children.to_xml.strip
41
+ else
42
+ p.to_xml.strip
43
+ end
44
+ end
45
+
46
+ def link_unwrap(p)
47
+ elems = p.elements
48
+ if elems.size == 1 && elems[0].name == "link"
49
+ p.at("./link").replace(elems[0]["target"].strip)
50
+ end
51
+ p
52
+ end
53
+
54
+ def dd_bib_extract(dtd)
55
+ return nil if dtd.children.empty?
56
+ dtd.at("./dl") and return dl_bib_extract(dtd)
57
+ elems = dtd.remove.elements
58
+ return p_unwrap(dtd) unless elems.size == 1 && %w(ol ul).include?(elems[0].name)
59
+ ret = []
60
+ elems[0].xpath("./li").each do |li|
61
+ ret << p_unwrap(li)
62
+ end
63
+ ret
64
+ end
65
+
66
+ def add_to_hash(bib, key, val)
67
+ Metanorma::Utils::set_nested_value(bib, key.split(/\./), val)
68
+ end
69
+
70
+ # definition list, with at most one level of unordered lists
71
+ def dl_bib_extract(c, nested = false)
72
+ dl = c.at("./dl") or return
73
+ bib = {}
74
+ key = ""
75
+ dl.xpath("./dt | ./dd").each do |dtd|
76
+ dtd.name == "dt" and key = dtd.text.sub(/:+$/, "") or add_to_hash(bib, key, dd_bib_extract(dtd))
77
+ end
78
+ c.xpath("./clause").each do |c1|
79
+ key = c1&.at("./title")&.text&.downcase&.strip
80
+ next unless %w(contributor relation series).include? key
81
+ add_to_hash(bib, key, dl_bib_extract(c1, true))
82
+ end
83
+ if !nested and c.at("./title")
84
+ title = c.at("./title").remove.children.to_xml
85
+ bib["title"] = [bib["title"]] if bib["title"].is_a? Hash
86
+ bib["title"] = [bib["title"]] if bib["title"].is_a? String
87
+ bib["title"] = [] unless bib["title"]
88
+ bib["title"] << title if !title.empty?
89
+ end
90
+ bib
91
+ end
92
+ end
93
+ end
94
+ end
@@ -14,7 +14,6 @@ require "asciidoctor/standoc/utils"
14
14
  require "asciidoctor/standoc/cleanup"
15
15
  require "asciidoctor/standoc/reqt"
16
16
  require_relative "./macros.rb"
17
- require_relative "./log.rb"
18
17
 
19
18
  module Asciidoctor
20
19
  module Standoc
@@ -93,7 +93,7 @@ module Asciidoctor
93
93
  end
94
94
 
95
95
  def metadata_script(node, xml)
96
- xml.script (node.attr("script") || "Latn")
96
+ xml.script (node.attr("script") || default_script(node.attr("language")))
97
97
  end
98
98
 
99
99
  def relaton_relations
@@ -186,7 +186,7 @@ module Asciidoctor
186
186
  ["en"].each do |lang|
187
187
  at = { language: lang, format: "text/plain" }
188
188
  xml.title **attr_code(at) do |t|
189
- t << (Utils::asciidoc_sub(node.attr("title") ||
189
+ t << (Metanorma::Utils::asciidoc_sub(node.attr("title") ||
190
190
  node.attr("title-en")) || node.title)
191
191
  end
192
192
  end
@@ -194,7 +194,7 @@ module Asciidoctor
194
194
  type = types.first.to_s
195
195
  uri = uri.sub(%r{^data:image/\*;}, "data:#{type};")
196
196
  attr_code(src: uri, #@datauriimage ? datauri(uri) : uri,
197
- id: Utils::anchor_or_uuid,
197
+ id: Metanorma::Utils::anchor_or_uuid,
198
198
  mimetype: type,
199
199
  height: node.attr("height") || "auto",
200
200
  width: node.attr("width") || "auto" ,
@@ -789,6 +789,9 @@
789
789
  <attribute name="width"/>
790
790
  </element>
791
791
  </define>
792
+ <define name="BibItemType" combine="choice">
793
+ <value>internal</value>
794
+ </define>
792
795
  <define name="TextElement" combine="choice">
793
796
  <ref name="concept"/>
794
797
  </define>
@@ -54,7 +54,7 @@ module Asciidoctor
54
54
  end
55
55
 
56
56
  def ol_attrs(node)
57
- attr_code(keep_attrs(node).merge(id: Utils::anchor_or_uuid(node),
57
+ attr_code(keep_attrs(node).merge(id: Metanorma::Utils::anchor_or_uuid(node),
58
58
  type: olist_style(node.style)))
59
59
  end
60
60
 
@@ -88,7 +88,7 @@ module Asciidoctor
88
88
 
89
89
  def dl_attrs(node)
90
90
  attr_code(keep_attrs(node).
91
- merge(id: Utils::anchor_or_uuid(node),
91
+ merge(id: Metanorma::Utils::anchor_or_uuid(node),
92
92
  key: node.option?("key") ? "true" : nil))
93
93
  end
94
94
 
@@ -30,7 +30,7 @@ module Asciidoctor
30
30
  # plantuml process may finish earlier then dot, as result png file
31
31
  # maybe not created yet after plantuml finish
32
32
  def self.generate_file parent, reader
33
- localdir = Utils::localdir(parent.document)
33
+ localdir = Metanorma::Utils::localdir(parent.document)
34
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})!"
@@ -96,9 +96,9 @@ module Asciidoctor
96
96
  xstr = x.to_xml(lang: opts[:lang])
97
97
  xml = Nokogiri::XML(xstr)
98
98
  emend_biblio(xml, code, opts[:title], opts[:usrlbl])
99
- xml.xpath("//date").each { |d| Utils::endash_date(d) }
99
+ xml.xpath("//date").each { |d| Metanorma::Utils::endash_date(d) }
100
100
  xml.traverse do |n|
101
- n.text? and n.replace(Utils::smartformat(n.text))
101
+ n.text? and n.replace(Metanorma::Utils::smartformat(n.text))
102
102
  end
103
103
  xml.to_xml.sub(/<\?[^>]+>/, "")
104
104
  end
@@ -6,6 +6,11 @@ require "base64"
6
6
  module Asciidoctor
7
7
  module Standoc
8
8
  module Blocks
9
+ def reqt_subpart(x)
10
+ %w(specification measurement-target verification import label
11
+ subject inherit classification title).include? x
12
+ end
13
+
9
14
  def reqt_subpart_attrs(node)
10
15
  attr_code(keep_attrs(node).merge(exclude: node.option?("exclude"),
11
16
  type: node.attr("type")))
@@ -41,7 +46,7 @@ module Asciidoctor
41
46
 
42
47
  def reqt_attrs(node)
43
48
  attr_code(keep_attrs(node).merge(id_unnum_attrs(node)).merge(
44
- id: Utils::anchor_or_uuid(node),
49
+ id: Metanorma::Utils::anchor_or_uuid(node),
45
50
  unnumbered: node.option?("unnumbered") ? "true" : nil,
46
51
  number: node.attr("number"),
47
52
  subsequence: node.attr("subsequence"),
@@ -1,6 +1,6 @@
1
- require "htmlentities"
2
1
  require "uri"
3
2
  require_relative "ref_sect"
3
+ require_relative "terms"
4
4
 
5
5
  module Asciidoctor
6
6
  module Standoc
@@ -9,10 +9,6 @@ module Asciidoctor
9
9
  @term_def = false
10
10
  @norm_ref = false
11
11
 
12
- def in_terms?
13
- @term_def
14
- end
15
-
16
12
  def sectiontype1(node)
17
13
  node&.attr("heading")&.downcase || node.title.gsub(/<[^>]+>/, "").downcase
18
14
  end
@@ -45,10 +41,11 @@ module Asciidoctor
45
41
  end
46
42
 
47
43
  def section_attributes(node)
48
- ret = { id: Utils::anchor_or_uuid(node),
44
+ ret = { id: Metanorma::Utils::anchor_or_uuid(node),
49
45
  language: node.attributes["language"],
50
46
  script: node.attributes["script"],
51
47
  number: node.attributes["number"],
48
+ type: node.attributes["type"],
52
49
  annex: ( ((node.attr("style") == "appendix" || node.role == "appendix") &&
53
50
  node.level == 1) ? true : nil),
54
51
  preface: (
@@ -147,80 +144,6 @@ module Asciidoctor
147
144
  end
148
145
  end
149
146
 
150
- def nonterm_symbols_parse(attrs, xml, node)
151
- defs = @definitions
152
- @definitions = false
153
- clause_parse(attrs, xml, node)
154
- @definitions = defs
155
- end
156
-
157
- def symbols_attrs(node, a)
158
- case sectiontype1(node)
159
- when "symbols" then a.merge(type: "symbols")
160
- when "abbreviated terms", "abbreviations" then a.merge(type: "abbreviated_terms")
161
- else
162
- a
163
- end
164
- end
165
-
166
- def symbols_parse(attr, xml, node)
167
- node.role == "nonterm" and return nonterm_symbols_parse(attr, xml, node)
168
- xml.definitions **attr_code(attr) do |xml_section|
169
- xml_section.title { |t| t << node.title }
170
- defs = @definitions
171
- termdefs = @term_def
172
- @definitions = true
173
- @term_def = false
174
- xml_section << node.content
175
- @definitions = defs
176
- @term_def = termdefs
177
- end
178
- end
179
-
180
- def nonterm_term_def_subclause_parse(attrs, xml, node)
181
- defs = @term_def
182
- @term_def = false
183
- clause_parse(attrs, xml, node)
184
- @term_def = defs
185
- end
186
-
187
- def terms_boilerplate_parse(attrs, xml, node)
188
- defs = @term_def
189
- @term_def = false
190
- clause_parse(attrs.merge(type: "boilerplate"), xml, node)
191
- @term_def = defs
192
- end
193
-
194
- # subclause contains subclauses
195
- def term_def_subclause_parse(attrs, xml, node)
196
- node.role == "nonterm" and return nonterm_term_def_subclause_parse(attrs, xml, node)
197
- node.role == "boilerplate" and return terms_boilerplate_parse(attrs, xml, node)
198
- st = sectiontype(node, false)
199
- return symbols_parse(attrs, xml, node) if @definitions
200
- sub = node.find_by(context: :section) { |s| s.level == node.level + 1 }
201
- sub.empty? || (return term_def_parse(attrs, xml, node, false))
202
- st == "symbols and abbreviated terms" and (return symbols_parse(attrs, xml, node))
203
- st == "terms and definitions" and return clause_parse(attrs, xml, node)
204
- term_def_subclause_parse1(attrs, xml, node)
205
- end
206
-
207
- def term_def_subclause_parse1(attrs, xml, node)
208
- xml.term **attr_code(attrs) do |xml_section|
209
- xml_section.preferred { |name| name << node.title }
210
- xml_section << node.content
211
- end
212
- end
213
-
214
- def term_def_parse(attrs, xml, node, toplevel)
215
- xml.terms **attr_code(attrs) do |section|
216
- section.title { |t| t << node.title }
217
- (s = node.attr("source")) && s.split(/,/).each do |s1|
218
- section.termdocsource(nil, **attr_code(bibitemid: s1))
219
- end
220
- section << node.content
221
- end
222
- end
223
-
224
147
  def introduction_parse(attrs, xml, node)
225
148
  xml.introduction **attr_code(attrs) do |xml_section|
226
149
  xml_section.title { |t| t << @i18n.introduction }
@@ -2,7 +2,7 @@ module Asciidoctor
2
2
  module Standoc
3
3
  module Table
4
4
  def table_attrs(node)
5
- keep_attrs(node).merge( id: Utils::anchor_or_uuid(node),
5
+ keep_attrs(node).merge( id: Metanorma::Utils::anchor_or_uuid(node),
6
6
  headerrows: node.attr("headerrows"),
7
7
  unnumbered: node.option?("unnumbered") ? "true" : nil,
8
8
  number: node.attr("number"),
@@ -0,0 +1,125 @@
1
+ module Asciidoctor
2
+ module Standoc
3
+ module Section
4
+ def in_terms?
5
+ @term_def
6
+ end
7
+
8
+ def nonterm_symbols_parse(attrs, xml, node)
9
+ defs = @definitions
10
+ @definitions = false
11
+ clause_parse(attrs, xml, node)
12
+ @definitions = defs
13
+ end
14
+
15
+ def symbols_attrs(node, a)
16
+ case sectiontype1(node)
17
+ when "symbols" then a.merge(type: "symbols")
18
+ when "abbreviated terms", "abbreviations" then a.merge(type: "abbreviated_terms")
19
+ else
20
+ a
21
+ end
22
+ end
23
+
24
+ def symbols_parse(attr, xml, node)
25
+ node.role == "nonterm" and return nonterm_symbols_parse(attr, xml, node)
26
+ xml.definitions **attr_code(attr) do |xml_section|
27
+ xml_section.title { |t| t << node.title }
28
+ defs = @definitions
29
+ termdefs = @term_def
30
+ @definitions = true
31
+ @term_def = false
32
+ xml_section << node.content
33
+ @definitions = defs
34
+ @term_def = termdefs
35
+ end
36
+ end
37
+
38
+ def nonterm_term_def_subclause_parse(attrs, xml, node)
39
+ defs = @term_def
40
+ @term_def = false
41
+ clause_parse(attrs, xml, node)
42
+ @term_def = defs
43
+ end
44
+
45
+ def terms_boilerplate_parse(attrs, xml, node)
46
+ defs = @term_def
47
+ @term_def = false
48
+ clause_parse(attrs.merge(type: "boilerplate"), xml, node)
49
+ @term_def = defs
50
+ end
51
+
52
+ # subclause contains subclauses
53
+ def term_def_subclause_parse(attrs, xml, node)
54
+ node.role == "nonterm" and return nonterm_term_def_subclause_parse(attrs, xml, node)
55
+ node.role == "boilerplate" and return terms_boilerplate_parse(attrs, xml, node)
56
+ st = sectiontype(node, false)
57
+ return symbols_parse(attrs, xml, node) if @definitions
58
+ sub = node.find_by(context: :section) { |s| s.level == node.level + 1 }
59
+ sub.empty? || (return term_def_parse(attrs, xml, node, false))
60
+ st == "symbols and abbreviated terms" and (return symbols_parse(attrs, xml, node))
61
+ st == "terms and definitions" and return clause_parse(attrs, xml, node)
62
+ term_def_subclause_parse1(attrs, xml, node)
63
+ end
64
+
65
+ def term_def_subclause_parse1(attrs, xml, node)
66
+ xml.term **attr_code(attrs) do |xml_section|
67
+ xml_section.preferred { |name| name << node.title }
68
+ xml_section << node.content
69
+ end
70
+ end
71
+
72
+ def term_def_parse(attrs, xml, node, toplevel)
73
+ xml.terms **attr_code(attrs) do |section|
74
+ section.title { |t| t << node.title }
75
+ (s = node.attr("source")) && s.split(/,/).each do |s1|
76
+ section.termdocsource(nil, **attr_code(bibitemid: s1))
77
+ end
78
+ section << node.content
79
+ end
80
+ end
81
+
82
+ def term_source_attrs(seen_xref)
83
+ { bibitemid: seen_xref.children[0]["target"],
84
+ format: seen_xref.children[0]["format"], type: "inline" }
85
+ end
86
+
87
+ def add_term_source(xml_t, seen_xref, m)
88
+ if seen_xref.children[0].name == "concept"
89
+ xml_t.origin { |o| o << seen_xref.children[0].to_xml }
90
+ else
91
+ xml_t.origin seen_xref.children[0].content, **attr_code(term_source_attrs(seen_xref))
92
+ end
93
+ m[:text] && xml_t.modification do |mod|
94
+ mod.p { |p| p << m[:text].sub(/^\s+/, "") }
95
+ end
96
+ end
97
+
98
+ TERM_REFERENCE_RE_STR = <<~REGEXP.freeze
99
+ ^(?<xref><(xref|concept)[^>]+>([^<]*</(xref|concept)>)?)
100
+ (,\s(?<text>.*))?
101
+ $
102
+ REGEXP
103
+ TERM_REFERENCE_RE =
104
+ Regexp.new(TERM_REFERENCE_RE_STR.gsub(/\s/, "").gsub(/_/, "\\s"),
105
+ Regexp::IGNORECASE | Regexp::MULTILINE)
106
+
107
+ def extract_termsource_refs(text, node)
108
+ matched = TERM_REFERENCE_RE.match text
109
+ matched.nil? and @log.add("AsciiDoc Input", node, "term reference not in expected format: #{text}")
110
+ matched
111
+ end
112
+
113
+ def termsource(node)
114
+ matched = extract_termsource_refs(node.content, node) || return
115
+ noko do |xml|
116
+ attrs = { status: matched[:text] ? "modified" : "identical" }
117
+ xml.termsource **attrs do |xml_t|
118
+ seen_xref = Nokogiri::XML.fragment(matched[:xref])
119
+ add_term_source(xml_t, seen_xref, matched)
120
+ end
121
+ end.join("\n")
122
+ end
123
+ end
124
+ end
125
+ end