metanorma-standoc 1.4.0 → 1.4.1

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