metanorma-standoc 1.4.0 → 1.4.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 (30) hide show
  1. checksums.yaml +4 -4
  2. data/lib/asciidoctor/standoc/base.rb +2 -2
  3. data/lib/asciidoctor/standoc/biblio.rng +1 -1
  4. data/lib/asciidoctor/standoc/blocks.rb +25 -95
  5. data/lib/asciidoctor/standoc/blocks_notes.rb +89 -0
  6. data/lib/asciidoctor/standoc/cleanup.rb +10 -6
  7. data/lib/asciidoctor/standoc/cleanup_inline.rb +1 -1
  8. data/lib/asciidoctor/standoc/cleanup_ref.rb +45 -0
  9. data/lib/asciidoctor/standoc/isodoc.rng +427 -0
  10. data/lib/asciidoctor/standoc/lists.rb +12 -12
  11. data/lib/asciidoctor/standoc/macros_yaml2text.rb +32 -15
  12. data/lib/asciidoctor/standoc/ref.rb +71 -29
  13. data/lib/asciidoctor/standoc/reqt.rb +11 -6
  14. data/lib/asciidoctor/standoc/reqt.rng +23 -0
  15. data/lib/asciidoctor/standoc/table.rb +3 -2
  16. data/lib/asciidoctor/standoc/views/datamodel/model_representation.adoc.erb +1 -1
  17. data/lib/metanorma/standoc/version.rb +1 -1
  18. data/spec/asciidoctor-standoc/blocks_spec.rb +55 -26
  19. data/spec/asciidoctor-standoc/cleanup_spec.rb +1 -1
  20. data/spec/asciidoctor-standoc/datamodel/attributes_table_preprocessor_spec.rb +35 -0
  21. data/spec/asciidoctor-standoc/lists_spec.rb +7 -5
  22. data/spec/asciidoctor-standoc/macros_spec.rb +3 -2
  23. data/spec/asciidoctor-standoc/refs_spec.rb +250 -5
  24. data/spec/asciidoctor-standoc/table_spec.rb +2 -2
  25. data/spec/asciidoctor-standoc/validate_spec.rb +56 -0
  26. data/spec/assets/iso123.rxl +107 -0
  27. data/spec/examples/datamodel/blank_definition_profile.adoc +4 -0
  28. data/spec/examples/datamodel/models/models/{Signature copy.yml → SignatureBlankDefinition.yml} +2 -2
  29. data/spec/fixtures/macros_datamodel/blank_definition_profile.xml +51 -0
  30. metadata +7 -3
@@ -13,7 +13,7 @@ module Asciidoctor
13
13
  end
14
14
 
15
15
  def ul_li(xml_ul, item)
16
- xml_ul.li **ul_li_attr(item) do |xml_li|
16
+ xml_ul.li **ul_li_attrs(item) do |xml_li|
17
17
  if item.blocks?
18
18
  xml_li.p(**id_attr(item)) { |t| t << item.text }
19
19
  xml_li << item.content
@@ -23,11 +23,11 @@ module Asciidoctor
23
23
  end
24
24
  end
25
25
 
26
- def ul_attr(node)
27
- attr_code(id_attr(node))
26
+ def ul_attrs(node)
27
+ attr_code(id_attr(node).merge(keep_attrs(node)))
28
28
  end
29
29
 
30
- def ul_li_attr(node)
30
+ def ul_li_attrs(node)
31
31
  attr_code(
32
32
  uncheckedcheckbox: node.attr?("checkbox") ? !node.attr?("checked") : nil,
33
33
  checkedcheckbox: node.attr?("checkbox") ? node.attr?("checked") : nil,
@@ -37,7 +37,7 @@ module Asciidoctor
37
37
  def ulist(node)
38
38
  return reference(node) if in_norm_ref? || in_biblio?
39
39
  noko do |xml|
40
- xml.ul **ul_attr(node) do |xml_ul|
40
+ xml.ul **ul_attrs(node) do |xml_ul|
41
41
  node.items.each do |item|
42
42
  ul_li(xml_ul, item)
43
43
  end
@@ -53,14 +53,14 @@ module Asciidoctor
53
53
  style
54
54
  end
55
55
 
56
- def ol_attr(node)
57
- attr_code(id: Utils::anchor_or_uuid(node),
58
- type: olist_style(node.style))
56
+ def ol_attrs(node)
57
+ attr_code(keep_attrs(node).merge(id: Utils::anchor_or_uuid(node),
58
+ type: olist_style(node.style)))
59
59
  end
60
60
 
61
61
  def olist(node)
62
62
  noko do |xml|
63
- xml.ol **ol_attr(node) do |xml_ol|
63
+ xml.ol **ol_attrs(node) do |xml_ol|
64
64
  node.items.each { |item| li(xml_ol, item) }
65
65
  end
66
66
  end.join("\n")
@@ -86,13 +86,13 @@ module Asciidoctor
86
86
  end
87
87
  end
88
88
 
89
- def dl_attr(node)
90
- attr_code(id_attr(node))
89
+ def dl_attrs(node)
90
+ attr_code(id_attr(node).merge(keep_attrs(node)))
91
91
  end
92
92
 
93
93
  def dlist(node)
94
94
  noko do |xml|
95
- xml.dl **dl_attr(node) do |xml_dl|
95
+ xml.dl **dl_attrs(node) do |xml_dl|
96
96
  node.items.each do |terms, dd|
97
97
  dt(terms, xml_dl)
98
98
  dd(dd, xml_dl)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'ostruct'
2
4
 
3
5
  module Asciidoctor
@@ -61,7 +63,7 @@ module Asciidoctor
61
63
  # - name: spaghetti
62
64
  # desc: wheat noodles of 9mm diameter
63
65
  # symbol: SPAG
64
- # symbol_def: the situation is message like spaghetti at a kid's meal
66
+ # symbol_def: the situation is message like spaghetti at a kid's
65
67
  #
66
68
  # will produce:
67
69
  # === spaghetti
@@ -77,19 +79,20 @@ module Asciidoctor
77
79
 
78
80
  def processed_lines(document, input_lines)
79
81
  result = []
82
+ current_macro_line_num = 0
80
83
  loop do
81
84
  line = input_lines.next
85
+ current_macro_line_num += 1
82
86
  if yaml_block_match = line.match(/^\[yaml2text,(.+?),(.+?)\]/)
83
87
  mark = input_lines.next
84
88
  current_yaml_block = []
85
89
  while (yaml_block_line = input_lines.next) != mark
86
90
  current_yaml_block.push(yaml_block_line)
87
91
  end
88
- content = nested_open_struct_from_yaml(yaml_block_match[1], document)
89
- result.push(*
90
- parse_blocks_recursively(lines: current_yaml_block,
91
- attributes: content,
92
- context_name: yaml_block_match[2]))
92
+ result.push(*read_yaml_and_parse_template(current_yaml_block,
93
+ document,
94
+ yaml_block_match,
95
+ current_macro_line_num))
93
96
  else
94
97
  result.push(line)
95
98
  end
@@ -97,9 +100,23 @@ module Asciidoctor
97
100
  result
98
101
  end
99
102
 
103
+ def read_yaml_and_parse_template(current_yaml_block, document, yaml_block_match, current_macro_line_num)
104
+ content = nested_open_struct_from_yaml(yaml_block_match[1], document)
105
+ parse_blocks_recursively(lines: current_yaml_block,
106
+ attributes: content,
107
+ context_name: yaml_block_match[2])
108
+ rescue StandardError => exception
109
+ document
110
+ .logger
111
+ .warn("Failed to parse yaml2text block on line #{current_macro_line_num}: #{exception.message}")
112
+ []
113
+ end
114
+
100
115
  def nested_open_struct_from_yaml(file_path, document)
101
116
  docfile_directory = File.dirname(document.attributes['docfile'] || '.')
102
- yaml_file_path = document.path_resolver.system_path(file_path, docfile_directory)
117
+ yaml_file_path = document
118
+ .path_resolver
119
+ .system_path(file_path, docfile_directory)
103
120
  content = YAML.safe_load(File.read(yaml_file_path))
104
121
  # Load content as json, then parse with JSON as nested open_struct
105
122
  JSON.parse(content.to_json, object_class: YamlBlockStruct)
@@ -112,19 +129,19 @@ module Asciidoctor
112
129
  result = []
113
130
  loop do
114
131
  line = lines.next
115
- if line.match(BLOCK_START_REGEXP)
132
+ if line.match?(BLOCK_START_REGEXP)
116
133
  line.gsub!(BLOCK_START_REGEXP,
117
134
  '<% \1.each&.with_index do |\2,index| %>')
118
135
  end
119
136
 
120
- if line.strip.match(BLOCK_END_REGEXP)
137
+ if line.strip.match?(BLOCK_END_REGEXP)
121
138
  line.gsub!(BLOCK_END_REGEXP, '<% end %>')
122
139
  end
123
140
  line.gsub!(/{\s*if\s*([^}]+)}/, '<% if \1 %>')
124
141
  line.gsub!(/{\s*?end\s*?}/, '<% end %>')
125
142
  line = line
126
- .gsub(/{(.+?[^}]*)}/, '<%= \1 %>')
127
- .gsub(/[a-z\.]+\#/, 'index')
143
+ .gsub(/{(.+?[^}]*)}/, '<%= \1 %>')
144
+ .gsub(/[a-z\.]+\#/, 'index')
128
145
  result.push(line)
129
146
  end
130
147
  result = parse_context_block(context_lines: result,
@@ -137,10 +154,10 @@ module Asciidoctor
137
154
  context_items:,
138
155
  context_name:)
139
156
  renderer = YamlContextRenderer
140
- .new(
141
- context_object: context_items,
142
- context_name: context_name
143
- )
157
+ .new(
158
+ context_object: context_items,
159
+ context_name: context_name
160
+ )
144
161
  renderer.render(context_lines.join("\n")).split("\n")
145
162
  end
146
163
  end
@@ -37,8 +37,7 @@ module Asciidoctor
37
37
  end
38
38
 
39
39
  def id_and_year(id, year)
40
- return id unless year
41
- "#{id}:#{year}"
40
+ year ? "#{id}:#{year}" : id
42
41
  end
43
42
 
44
43
  def docid(t, code)
@@ -58,8 +57,14 @@ module Asciidoctor
58
57
  end
59
58
 
60
59
  def norm_year(yr)
61
- return "--" if /^\&\#821[12];$/.match yr
62
- yr
60
+ /^\&\#821[12];$/.match(yr) ? "--" : yr
61
+ end
62
+
63
+ def isorefrender1(t, m, yr, allp = "")
64
+ t.title(**plaintxt) { |i| i << ref_normalise(m[:text]) }
65
+ docid(t, m[:usrlbl]) if m[:usrlbl]
66
+ docid(t, id_and_year(m[:code], yr) + allp)
67
+ docnumber(t, m[:code])
63
68
  end
64
69
 
65
70
  def isorefmatches(xml, m)
@@ -67,10 +72,7 @@ module Asciidoctor
67
72
  ref = fetch_ref xml, m[:code], yr, title: m[:text], usrlbl: m[:usrlbl]
68
73
  return use_my_anchor(ref, m[:anchor]) if ref
69
74
  xml.bibitem **attr_code(ref_attributes(m)) do |t|
70
- t.title(**plaintxt) { |i| i << ref_normalise(m[:text]) }
71
- docid(t, m[:usrlbl]) if m[:usrlbl]
72
- docid(t, id_and_year(m[:code], yr))
73
- docnumber(t, m[:code])
75
+ isorefrender1(t, m, yr)
74
76
  yr and t.date **{ type: "published" } do |d|
75
77
  set_date_range(d, yr)
76
78
  end
@@ -82,27 +84,23 @@ module Asciidoctor
82
84
  ref = fetch_ref xml, m[:code], nil, no_year: true, note: m[:fn],
83
85
  title: m[:text], usrlbl: m[:usrlbl]
84
86
  return use_my_anchor(ref, m[:anchor]) if ref
85
-
86
87
  xml.bibitem **attr_code(ref_attributes(m)) do |t|
87
- t.title(**plaintxt) { |i| i << ref_normalise(m[:text]) }
88
- docid(t, m[:usrlbl]) if m[:usrlbl]
89
- docid(t, id_and_year(m[:code], "--"))
90
- docnumber(t, m[:code])
88
+ isorefrender1(t, m, "--")
91
89
  t.date **{ type: "published" } do |d|
92
90
  d.on "--"
93
91
  end
94
92
  iso_publisher(t, m[:code])
95
- m[:fn].nil? or t.note(**plaintxt) { |p| p << "ISO DATE: #{m[:fn]}" }
93
+ m[:fn].nil? or t.note(**plaintxt.merge(type: "ISO DATE")) do |p|
94
+ p << "#{m[:fn]}"
95
+ end
96
96
  end
97
97
  end
98
98
 
99
99
  def conditional_date(t, m, noyr)
100
100
  m.names.include?("year") and !m[:year].nil? and
101
101
  t.date(**{ type: "published" }) do |d|
102
- if noyr then d.on "--"
103
- else
102
+ noyr and d.on "--" or
104
103
  set_date_range(d, norm_year(m[:year]))
105
- end
106
104
  end
107
105
  end
108
106
 
@@ -115,28 +113,35 @@ module Asciidoctor
115
113
  return use_my_anchor(ref, m[:anchor]) if ref
116
114
 
117
115
  xml.bibitem(**attr_code(ref_attributes(m))) do |t|
118
- t.title(**plaintxt) { |i| i << ref_normalise(m[:text]) }
119
- docid(t, m[:usrlbl]) if m[:usrlbl]
120
- docid(t, id_and_year(m[:code], yr) + " (all parts)")
121
- docnumber(t, m[:code])
116
+ isorefrender1(t, m, yr, " (all parts)")
122
117
  conditional_date(t, m, noyr)
123
118
  iso_publisher(t, m[:code])
124
119
  m.names.include?("fn") && m[:fn] and
125
- t.note(**plaintxt) { |p| p << "ISO DATE: #{m[:fn]}" }
120
+ t.note(**plaintxt.merge(type: "ISO DATE")) { |p| p << "#{m[:fn]}" }
126
121
  t.extent **{ type: 'part' } do |e|
127
122
  e.referenceFrom "all"
128
123
  end
129
124
  end
130
125
  end
131
126
 
132
- def refitem_render(xml, m)
127
+ def refitem_render1(m, code, t)
128
+ if code[:type] == "path"
129
+ t.uri code[:key].sub(/\.[a-zA-Z0-9]+$/, ""), **{ type: "URI" }
130
+ t.uri code[:key].sub(/\.[a-zA-Z0-9]+$/, ""), **{ type: "citation" }
131
+ end
132
+ docid(t, m[:usrlbl]) if m[:usrlbl]
133
+ docid(t, /^\d+$/.match(code[:id]) ? "[#{code[:id]}]" : code[:id])
134
+ code[:type] == "repo" and
135
+ t.docidentifier code[:key], **{ type: "repository" }
136
+ end
137
+
138
+ def refitem_render(xml, m, code)
133
139
  xml.bibitem **attr_code(id: m[:anchor]) do |t|
134
140
  t.formattedref **{ format: "application/x-isodoc+xml" } do |i|
135
141
  i << ref_normalise_no_format(m[:text])
136
142
  end
137
- docid(t, m[:usrlbl]) if m[:usrlbl]
138
- docid(t, /^\d+$/.match(m[:code]) ? "[#{m[:code]}]" : m[:code])
139
- docnumber(t, m[:code]) unless /^\d+$|^\(.+\)$/.match(m[:code])
143
+ refitem_render1(m, code, t)
144
+ docnumber(t, code[:id]) unless /^\d+$|^\(.+\)$/.match(code[:id])
140
145
  end
141
146
  end
142
147
 
@@ -144,19 +149,56 @@ module Asciidoctor
144
149
  "https://www.metanorma.com/author/topics/document-format/bibliography/ , "\
145
150
  "https://www.metanorma.com/author/iso/topics/markup/#bibliographies".freeze
146
151
 
152
+ def analyse_ref_nofetch(ret)
153
+ if m = /^nofetch\((?<id>.+)\)$/.match(ret[:id])
154
+ ret[:id] = m[:id]
155
+ ret[:nofetch] = true
156
+ end
157
+ ret
158
+ end
159
+
160
+ def analyse_ref_repo_path(ret)
161
+ if m = /^(?<type>repo|path):\((?<key>[^,]+),(?<id>.+)\)$/.match(ret[:id])
162
+ ret[:id] = m[:id]
163
+ ret[:type] = m[:type]
164
+ ret[:key] = m[:key]
165
+ ret[:nofetch] = true
166
+ end
167
+ ret
168
+ end
169
+
170
+ def analyse_ref_numeric(ret)
171
+ if /^\d+$/.match(ret[:id])
172
+ ret[:numeric] = true
173
+ end
174
+ ret
175
+ end
176
+
177
+ # ref id = (usrlbl)code[:-]year
178
+ # code = nofetch(code) | (repo|path):(key,code) | \[? number \]? | identifier
179
+ def analyse_ref_code(code)
180
+ ret = {id: code}
181
+ return ret if code.nil? || code.empty?
182
+ ret = analyse_ref_nofetch(ret)
183
+ ret = analyse_ref_repo_path(ret)
184
+ ret = analyse_ref_numeric(ret)
185
+ ret
186
+ end
187
+
147
188
  # TODO: alternative where only title is available
148
189
  def refitem(xml, item, node)
149
190
  unless m = NON_ISO_REF.match(item)
150
191
  @log.add("AsciiDoc Input", node, "#{MALFORMED_REF}: #{item}")
151
192
  return
152
193
  end
153
- unless m[:code] && /^\d+$/.match(m[:code])
154
- ref = fetch_ref xml, m[:code],
194
+ code = analyse_ref_code(m[:code])
195
+ unless code[:id] && code[:numeric] || code[:nofetch]
196
+ ref = fetch_ref xml, code[:id],
155
197
  m.names.include?("year") ? m[:year] : nil, title: m[:text],
156
198
  usrlbl: m[:usrlbl]
157
199
  return use_my_anchor(ref, m[:anchor]) if ref
158
200
  end
159
- refitem_render(xml, m)
201
+ refitem_render(xml, m, code)
160
202
  end
161
203
 
162
204
  def ref_normalise(ref)
@@ -6,11 +6,15 @@ require "base64"
6
6
  module Asciidoctor
7
7
  module Standoc
8
8
  module Blocks
9
+ def reqt_subpart_attrs(node)
10
+ attr_code(keep_attrs(node).merge(exclude: node.option?("exclude"),
11
+ type: node.attr("type")))
12
+ end
13
+
9
14
  def requirement_subpart(node)
10
15
  name = node.role || node.attr("style")
11
16
  noko do |xml|
12
- xml.send name, **attr_code(exclude: node.option?("exclude"),
13
- type: node.attr("type")) do |o|
17
+ xml.send name, **reqt_subpart_attrs(node) do |o|
14
18
  o << node.content
15
19
  end
16
20
  end
@@ -35,22 +39,23 @@ module Asciidoctor
35
39
  end
36
40
  end
37
41
 
38
- def reqt_attributes(node)
39
- {
42
+ def reqt_attrs(node)
43
+ attr_code(keep_attrs(node).merge(id_unnum_attrs(node)).merge(
40
44
  id: Utils::anchor_or_uuid(node),
41
45
  unnumbered: node.option?("unnumbered") ? "true" : nil,
46
+ number: node.attr("number"),
42
47
  subsequence: node.attr("subsequence"),
43
48
  obligation: node.attr("obligation"),
44
49
  filename: node.attr("filename"),
45
50
  type: node.attr("type"),
46
51
  model: node.attr("model"),
47
- }
52
+ ))
48
53
  end
49
54
 
50
55
  def requirement(node, obligation)
51
56
  classif = node.attr("classification")
52
57
  noko do |xml|
53
- xml.send obligation, **attr_code(reqt_attributes(node)) do |ex|
58
+ xml.send obligation, **reqt_attrs(node) do |ex|
54
59
  node.title and ex.title { |t| t << node.title }
55
60
  node.attr("label") and ex.label { |l| l << node.attr("label") }
56
61
  node.attr("subject") and ex.subject { |s| s << node.attr("subject") }
@@ -30,9 +30,22 @@
30
30
  <data type="boolean"/>
31
31
  </attribute>
32
32
  </optional>
33
+ <optional>
34
+ <attribute name="number"/>
35
+ </optional>
33
36
  <optional>
34
37
  <attribute name="subsequence"/>
35
38
  </optional>
39
+ <optional>
40
+ <attribute name="keep-with-next">
41
+ <data type="boolean"/>
42
+ </attribute>
43
+ </optional>
44
+ <optional>
45
+ <attribute name="keep-lines-together">
46
+ <data type="boolean"/>
47
+ </attribute>
48
+ </optional>
36
49
  <attribute name="id">
37
50
  <data type="ID"/>
38
51
  </attribute>
@@ -141,6 +154,16 @@
141
154
  <data type="boolean"/>
142
155
  </attribute>
143
156
  </optional>
157
+ <optional>
158
+ <attribute name="keep-with-next">
159
+ <data type="boolean"/>
160
+ </attribute>
161
+ </optional>
162
+ <optional>
163
+ <attribute name="keep-lines-together">
164
+ <data type="boolean"/>
165
+ </attribute>
166
+ </optional>
144
167
  <oneOrMore>
145
168
  <ref name="BasicBlock"/>
146
169
  </oneOrMore>
@@ -2,14 +2,15 @@ module Asciidoctor
2
2
  module Standoc
3
3
  module Table
4
4
  def table_attrs(node)
5
- { id: Utils::anchor_or_uuid(node),
5
+ keep_attrs(node).merge( id: Utils::anchor_or_uuid(node),
6
6
  headerrows: node.attr("headerrows"),
7
7
  unnumbered: node.option?("unnumbered") ? "true" : nil,
8
+ number: node.attr("number"),
8
9
  subsequence: node.attr("subsequence"),
9
10
  alt: node.attr("alt"),
10
11
  summary: node.attr("summary"),
11
12
  width: node.attr("width"),
12
- }
13
+ )
13
14
  end
14
15
 
15
16
  def table(node)
@@ -11,7 +11,7 @@
11
11
  |Name |Definition |Mandatory/ Optional/ Conditional |Max Occur |Data Type
12
12
 
13
13
  {definition.attributes&.*,key,EOK}
14
- |{key} |{definition.attributes[key].definition || "TODO: enum " + key + "'s definition"} |{definition.attributes[key]&.cardinality&.min == 0 ? "O" : "M"} |{definition.attributes[key]&.cardinality&.max == "*" ? "N" : "1"} |{definition.attributes[key].origin ? "<<" + definition.attributes[key].origin + ">>" : ""}`{definition.attributes[key].type}`
14
+ |{key} |{definition.attributes[key].definition || "TODO: enum " + key.to_s + "'s definition"} |{definition.attributes[key]&.cardinality&.min == 0 ? "O" : "M"} |{definition.attributes[key]&.cardinality&.max == "*" ? "N" : "1"} |{definition.attributes[key].origin ? "<<" + definition.attributes[key].origin.to_s + ">>" : ""}`{definition.attributes[key].type}`
15
15
  {EOK}
16
16
  |===
17
17
  {end}