metanorma-standoc 3.4.6 → 3.4.8

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e4f541051cf5c4f2d8d09fa2abd0557875da3503bf5475ca4cf6f346d91afe5d
4
- data.tar.gz: 9d14d6673be1b03aa241d2dfa2c57cf5c8c5fb2e345959b49aea7a713af0b3a4
3
+ metadata.gz: 8cbc11c6b0e55ec1404d12dd309d0f711c1d4918d079375716d0b0406f6bce2f
4
+ data.tar.gz: 016b74cca05a97b06809a7e9af8d48b97b07c39e3d98fe0bd575da362f1bed57
5
5
  SHA512:
6
- metadata.gz: a2789df5307bf398f4ee3f0c87d1ff4295f4964e036d0dfea922362ff6c390a0fb714b623ca58445d9f7cf1ba73edc5b4936555ffe2a1c211af6cbf773ee98b9
7
- data.tar.gz: db9d8f35807bd2dcb2ba96ff93cc6ae2b3434d0d8585a0536f6a4c78c98232c1c7daaad9ed423d21f2f110e80fc300bc54f8e109897a9ad36ad904ce2619de13
6
+ metadata.gz: 7510bd34addc7e4e2f6d123aa19c89dffe555f1a2f06ff572824136b2269e073721bc7ac8df2f4a94ed09180bd724dbcdb2d5f6d29dc076c71650fe6d5f3c597
7
+ data.tar.gz: d575b92f788e95d9754b9d0d2cf6af1f670d82d2f3710d82b165a6e453f8d8ec884fc407926bbf1f86594c61f2992ee3728e9fc3455e0eeb459a2dd4b1f5bcb1
data/.rubocop.yml CHANGED
@@ -3,8 +3,20 @@
3
3
  inherit_from:
4
4
  - https://raw.githubusercontent.com/riboseinc/oss-guides/main/ci/rubocop.yml
5
5
 
6
+ # Rubocop plugins enabled centrally so every metanorma-org gem picks them up
7
+ # on cimas sync — best practice belongs at the shared-template layer, not
8
+ # per-repo. Per ronaldtse feedback on metanorma/ci#332.
9
+ plugins:
10
+ - rubocop-rspec
11
+ - rubocop-performance
12
+ - rubocop-rake
13
+
6
14
  # local repo-specific modifications
7
15
  # ...
8
16
 
9
17
  AllCops:
10
- TargetRubyVersion: 3.4
18
+ # 3.3 matches the org-wide minimum being pushed via #274 (Raise minimum
19
+ # Ruby version to 3.3 due to EOL of 3.2 on 2026-03-31). Was 3.4 before
20
+ # this commit — 3.4 was above the org's stated minimum and would have
21
+ # applied Rubocop rules that fail-close on gems still targeting 3.3.
22
+ TargetRubyVersion: 3.3
@@ -166,13 +166,22 @@ module Metanorma
166
166
  align_callouts_to_annotations(xmldoc)
167
167
  end
168
168
 
169
+ # State threaded through {{{ ... }}} sourcecode-markup processing.
170
+ # open: inside a span; inline: that span opened in the current text node
171
+ # (so its content is convertible, not merely delimiter-stripped);
172
+ # buf: the open span's content so far; out: the rebuilt node content.
173
+ SourcecodeMarkup = Struct.new(:open, :inline, :buf, :out, :node)
174
+
175
+ # {{{ ... }}} injects Asciidoc markup into otherwise-verbatim sourcecode.
176
+ # A span's delimiters can be split across separate text nodes by an
177
+ # element that subs="macros" injected between them (e.g. an inline
178
+ # image), so we walk the sourcecode's text nodes in order and carry the
179
+ # open state across them, rather than processing each node in isolation.
169
180
  def sourcecode_cleanup(xmldoc)
170
181
  xmldoc.xpath("//sourcecode").each do |x|
171
- x.traverse do |n|
172
- n.text? or next
173
- /#{Regexp.escape(@sourcecode_markup_start)}/.match?(n.text) or next
174
- n.replace(sourcecode_markup(n))
175
- end
182
+ open = x.xpath(".//text()")
183
+ .inject(false) { |acc, node| sourcecode_markup(node, acc) }
184
+ open and @log.add("STANDOC_65", x, params: [@sourcecode_markup_start])
176
185
  end
177
186
  end
178
187
 
@@ -183,32 +192,78 @@ module Metanorma
183
192
  )
184
193
  end
185
194
 
186
- def sourcecode_markup(node)
187
- source_markup_prep(node).each_slice(4).map.with_object([]) do |a, acc|
188
- acc << safe_noko(a[0], node.document)
189
- a.size == 4 or next
190
- acc << @conv.isolated_asciidoctor_convert(
191
- "{blank} #{a[2]}", doctype: :inline,
192
- backend: @conv.backend&.to_sym || :standoc
193
- ).strip
194
- end.join
195
+ # @return [Boolean] whether a {{{ span is still open after this node
196
+ def sourcecode_markup(node, open)
197
+ open || sourcecode_markup?(node.text) or return open
198
+ state = SourcecodeMarkup.new(open, false, [], [], node)
199
+ sourcecode_markup_split(node.text)
200
+ .each { |tok| sourcecode_markup_token(tok, state) }
201
+ sourcecode_markup_flush(state)
202
+ state.open
195
203
  end
196
204
 
197
- def source_markup_prep(node)
198
- ret = node.text.split(/(#{Regexp.escape(@sourcecode_markup_start)}|
199
- #{Regexp.escape(@sourcecode_markup_end)})/x)
200
- source_markup_validate(node, ret)
201
- ret
205
+ def sourcecode_markup_flush(state)
206
+ state.open and
207
+ state.out << safe_noko(state.buf.join, state.node.document)
208
+ state.node.replace(state.out.join)
202
209
  end
203
210
 
204
- def source_markup_validate(node, ret)
205
- ret.each_slice(4) do |a|
206
- a.size == 4 or next
207
- a[1] == @sourcecode_markup_start && a[3] == @sourcecode_markup_end or
208
- @log.add("STANDOC_61", node, params: [a.join])
211
+ def sourcecode_markup?(text)
212
+ text.include?(@sourcecode_markup_start) ||
213
+ text.include?(@sourcecode_markup_end)
214
+ end
215
+
216
+ def sourcecode_markup_split(text)
217
+ text.split(/(#{Regexp.escape(@sourcecode_markup_start)}|
218
+ #{Regexp.escape(@sourcecode_markup_end)})/x)
219
+ end
220
+
221
+ def sourcecode_markup_token(tok, state)
222
+ case tok
223
+ when @sourcecode_markup_start then sourcecode_markup_open(state)
224
+ when @sourcecode_markup_end then sourcecode_markup_close(state)
225
+ else sourcecode_markup_content(tok, state)
209
226
  end
210
227
  end
211
228
 
229
+ # a nested {{{ is improper nesting: STANDOC_61 is fatal
230
+ def sourcecode_markup_open(state)
231
+ state.open and
232
+ return @log.add("STANDOC_61", state.node, params: [state.node.text])
233
+ state.open = state.inline = true
234
+ end
235
+
236
+ def sourcecode_markup_close(state)
237
+ state.open or return sourcecode_markup_stray(state)
238
+ state.out << sourcecode_markup_closed(state)
239
+ state.open = state.inline = false
240
+ state.buf = []
241
+ end
242
+
243
+ # a stray }}} with no opener is left as literal text
244
+ def sourcecode_markup_stray(state)
245
+ state.out << safe_noko(@sourcecode_markup_end, state.node.document)
246
+ end
247
+
248
+ # a span closed within one node is converted; one split across nodes
249
+ # (already holding a processed element) is delimiter-stripped only
250
+ def sourcecode_markup_closed(state)
251
+ state.inline and return sourcecode_markup_convert(state.buf.join)
252
+ safe_noko(state.buf.join, state.node.document)
253
+ end
254
+
255
+ def sourcecode_markup_content(tok, state)
256
+ state.open and return state.buf << tok
257
+ state.out << safe_noko(tok, state.node.document)
258
+ end
259
+
260
+ def sourcecode_markup_convert(span)
261
+ @conv.isolated_asciidoctor_convert(
262
+ "{blank} #{span}", doctype: :inline,
263
+ backend: @conv.backend&.to_sym || :standoc
264
+ ).strip
265
+ end
266
+
212
267
  def form_cleanup(xmldoc)
213
268
  xmldoc.xpath("//select").each do |s|
214
269
  while s.next_element&.name == "option"
@@ -116,6 +116,7 @@ module Metanorma
116
116
  .merge(align: node.attr("align"),
117
117
  variant_title: node.role == "variant-title" ? true : nil,
118
118
  key: node.option?("key") ? "true" : nil,
119
+ class: node.attr("class"),
119
120
  type: node.attr("type"))))
120
121
  end
121
122
 
@@ -134,7 +135,7 @@ module Metanorma
134
135
 
135
136
  def quote_attrs(node)
136
137
  attr_code(id_attr(node).merge(keep_attrs(node))
137
- .merge(align: node.attr("align")))
138
+ .merge(align: node.attr("align"), class: node.attr("class")))
138
139
  end
139
140
 
140
141
  def quote_attribution(node, out)
@@ -164,6 +165,7 @@ module Metanorma
164
165
  linenums: linenums ? "true" : nil,
165
166
  unnumbered: node.option?("unnumbered") ? "true" : nil,
166
167
  number: node.attr("number"),
168
+ class: node.attr("class"),
167
169
  filename: node.attr("filename"))))
168
170
  end
169
171
 
@@ -41,7 +41,8 @@ module Metanorma
41
41
  def pseudocode_example(node)
42
42
  node.blocks.each { |b| b.remove_sub(:replacements) }
43
43
  noko do |xml|
44
- xml.figure **example_attrs(node).merge(class: "pseudocode") do |ex|
44
+ klass = (["pseudocode"] + (node.attr("class")&.split || [])).uniq
45
+ xml.figure **example_attrs(node).merge(class: klass.join(" ")) do |ex|
45
46
  block_title(node, ex)
46
47
  wrap_in_para(node, ex)
47
48
  end
@@ -49,7 +50,9 @@ module Metanorma
49
50
  end
50
51
 
51
52
  def example_attrs(node)
52
- attr_code(id_unnum_attrs(node).merge(keep_attrs(node)))
53
+ attr_code(id_unnum_attrs(node).merge(keep_attrs(node))
54
+ .merge(collapsible: node.option?("collapsible") ? "true" : nil,
55
+ class: node.attr("class")))
53
56
  end
54
57
 
55
58
  def example_proper(node)
@@ -12,7 +12,7 @@ module Metanorma
12
12
 
13
13
  def note_attrs(node)
14
14
  attr_code(termnote_attrs(node).merge(admonition_core_attrs(node)
15
- .merge(type: node.attr("type"))))
15
+ .merge(type: node.attr("type"), class: node.attr("class"))))
16
16
  end
17
17
 
18
18
  def sidebar(node)
@@ -86,7 +86,7 @@ module Metanorma
86
86
  def admonition_attrs(node)
87
87
  attr_code(keep_attrs(node).merge(id_attr(node)
88
88
  .merge(admonition_core_attrs(node)
89
- .merge(type: admonition_name(node)))))
89
+ .merge(type: admonition_name(node), class: node.attr("class")))))
90
90
  end
91
91
 
92
92
  def admonition_core_attrs(node)
@@ -40,11 +40,16 @@ module Metanorma
40
40
  preprocessor Metanorma::Standoc::EmbedIncludeProcessor
41
41
  preprocessor Metanorma::Standoc::LinkProtectPreprocessor
42
42
  preprocessor Metanorma::Standoc::PassProtectPreprocessor
43
- preprocessor Metanorma::Standoc::MonospaceProtectPreprocessor
44
43
  preprocessor Metanorma::Plugin::Lutaml::Json2TextPreprocessor
45
44
  preprocessor Metanorma::Plugin::Lutaml::Yaml2TextPreprocessor
46
45
  preprocessor Metanorma::Plugin::Lutaml::Data2TextPreprocessor
47
46
  preprocessor Metanorma::Plugin::Lutaml::LutamlXmiUmlPreprocessor
47
+ # MonospaceProtect must run AFTER the content-injecting preprocessors
48
+ # (json2text/yaml2text/data2text, XMI) so that monospace in their
49
+ # injected content is protected from Asciidoctor character
50
+ # substitution too, consistently with hand-authored adoc.
51
+ # metanorma/iso-10303#208
52
+ preprocessor Metanorma::Standoc::MonospaceProtectPreprocessor
48
53
  preprocessor Metanorma::Plugin::Glossarist::DatasetPreprocessor
49
54
  preprocessor Metanorma::Standoc::NamedEscapePreprocessor
50
55
  inline_macro Metanorma::Standoc::PreferredTermInlineMacro
@@ -54,7 +54,9 @@ module Metanorma
54
54
  part, subpart = node.attr("partnumber")&.split("-")
55
55
  { part:, subpart:, amendment: node.attr("amendment-number"),
56
56
  corrigendum: node.attr("corrigendum-number"),
57
- addendum: node.attr("addendum-number") }
57
+ addendum: node.attr("addendum-number"),
58
+ supplement: node.attr("supplement-number"),
59
+ extract: node.attr("extract-number") }
58
60
  end
59
61
 
60
62
  def title_num_prefix(key, value, xml, lang)
@@ -128,6 +128,7 @@ module Metanorma
128
128
  @tocfigures = node.attr("toc-figures")
129
129
  @toctables = node.attr("toc-tables")
130
130
  @tocrecommendations = node.attr("toc-recommendations")
131
+ @tocexamples = node.attr("toc-examples")
131
132
  end
132
133
 
133
134
  def toc_default
@@ -196,6 +197,15 @@ module Metanorma
196
197
  end
197
198
 
198
199
  def biblio_cutoff(node)
200
+ # An explicit :bibliography-cutoff-date: overrides the date derived from
201
+ # the document's own dates, and a sentinel (none/false/no/off) disables
202
+ # the cutoff entirely. metanorma/metanorma-standoc#941.
203
+ if (override = node.attr("bibliography-cutoff-date"))
204
+ override = override.strip
205
+ return nil if %w(none false no off).include?(override.downcase)
206
+
207
+ return complete_and_compare_dates([override])
208
+ end
199
209
  dates = %w(revdate published-date accessed-date created-date
200
210
  implemented-date confirmed-date updated-date issued-date
201
211
  circulated-date unchanged-date copyright-year)
@@ -24,6 +24,7 @@ module Metanorma
24
24
  def ul_attrs(node)
25
25
  attr_code(id_attr(node).merge(keep_attrs(node)
26
26
  .merge(display: node.attr("display"),
27
+ class: node.attr("class"),
27
28
  "display-directives": node.attr("display-directives"))))
28
29
  end
29
30
 
@@ -63,6 +64,7 @@ module Metanorma
63
64
  .merge(type: olist_style(node.style),
64
65
  start: node.attr("start"),
65
66
  display: node.attr("display"),
67
+ class: node.attr("class"),
66
68
  "display-directives": node.attr("display-directives"),
67
69
  "explicit-type": olist_style(node.attributes[1]))))
68
70
  end
@@ -198,6 +198,9 @@ module Metanorma
198
198
  STANDOC_64: { category: "AsciiDoc Input",
199
199
  error: "Empty index term: %s.",
200
200
  severity: 0 },
201
+ STANDOC_65: { category: "AsciiDoc Input",
202
+ error: "Unbalanced sourcecode markup: unclosed %s",
203
+ severity: 0 },
201
204
  RELATON_1: { category: "Relaton",
202
205
  error: "(Error from Relaton) %s",
203
206
  severity: 0 },
@@ -221,6 +221,8 @@ module Metanorma
221
221
  global = !@no_isobib_cache && !node.attr("local-cache-only")
222
222
  local = node.attr("local-cache") || node.attr("local-cache-only")
223
223
  local = nil if @no_isobib_cache
224
+ flush_bib_caches_force(node) if node.attr("flush-caches") &&
225
+ @no_isobib_cache
224
226
  @bibdb = Relaton::Db.init_bib_caches(
225
227
  local_cache: local,
226
228
  flush_caches: node.attr("flush-caches"),
@@ -228,6 +230,23 @@ module Metanorma
228
230
  )
229
231
  end
230
232
 
233
+ # `:flush-caches:` must clear stale cache state regardless of whether
234
+ # `:no-isobib-cache:` disables caching for *this* compile. When both
235
+ # are set, the `Relaton::Db.init_bib_caches` path no-ops on the flush
236
+ # (because it has no cache paths to flush), leaving any previously
237
+ # cached entries on disk for the next compile that DOES have caching
238
+ # enabled to pick up. Force-flush the conventional cache locations
239
+ # here so the flag does what its name says.
240
+ # Source issue: https://github.com/relaton/relaton-iso/issues/181
241
+ def flush_bib_caches_force(node)
242
+ FileUtils.rm_rf "#{Dir.home}/.relaton/cache"
243
+ lcache_name = node.attr("local-cache") || node.attr("local-cache-only")
244
+ if lcache_name
245
+ base = lcache_name.to_s.empty? ? "relaton" : lcache_name
246
+ FileUtils.rm_rf "#{base}/cache"
247
+ end
248
+ end
249
+
231
250
  # Treat empty strings as falsy (they're set by Asciidoctor for some attributes)
232
251
  def init_iev_caches(node)
233
252
  no_cache = @no_isobib_cache && !@no_isobib_cache.empty?
@@ -41,6 +41,7 @@ module Metanorma
41
41
  tocfigures: @tocfigures,
42
42
  toctables: @toctables,
43
43
  tocrecommendations: @tocrecommendations,
44
+ tocexamples: @tocexamples,
44
45
  fonts: flex_attr_name(node, "fonts"),
45
46
  fontlicenseagreement: flex_attr_name(node, "font-license-agreement"),
46
47
  localizenumber: flex_attr_name(node, "localize-number"),
@@ -92,6 +93,7 @@ module Metanorma
92
93
  tocfigures: @tocfigures,
93
94
  toctables: @toctables,
94
95
  tocrecommendations: @tocrecommendations,
96
+ tocexamples: @tocexamples,
95
97
  fonts: flex_attr_name(node, "fonts"),
96
98
  fontlicenseagreement: flex_attr_name(node, "font-license-agreement"),
97
99
  }
@@ -9,7 +9,11 @@ module Metanorma
9
9
  plain: node.option?("plain") ? "true" : nil,
10
10
  style: node.attr("css-style"),
11
11
  summary: node.attr("summary"),
12
- width: node.attr("width"))
12
+ width: node.attr("width"),
13
+ # Custom CSS class(es) passed through to HTML output, via the
14
+ # `[class="..."]` named attribute (NOT role, which is reserved
15
+ # for block dispatch). metanorma/metanorma-pdfa#33
16
+ class: node.attr("class"))
13
17
  end
14
18
 
15
19
  def table(node)
@@ -19,6 +19,6 @@ module Metanorma
19
19
  end
20
20
 
21
21
  module Standoc
22
- VERSION = "3.4.6".freeze
22
+ VERSION = "3.4.8".freeze
23
23
  end
24
24
  end
@@ -1902,15 +1902,40 @@ or as the string "auto"</a:documentation>
1902
1902
  <a:documentation>Mathematically formatted text</a:documentation>
1903
1903
  <element name="stem">
1904
1904
  <ref name="StemAttributes"/>
1905
- <oneOrMore>
1906
- <choice>
1905
+ <optional>
1906
+ <text>
1907
1907
  <a:documentation>The content of the mathematically formatted text</a:documentation>
1908
- <text/>
1909
- <ref name="AnyElement"/>
1910
- </choice>
1911
- </oneOrMore>
1908
+ </text>
1909
+ </optional>
1910
+ <optional>
1911
+ <ref name="mathml"/>
1912
+ </optional>
1913
+ <optional>
1914
+ <ref name="asciimath"/>
1915
+ </optional>
1916
+ <optional>
1917
+ <ref name="latexmath"/>
1918
+ </optional>
1912
1919
  </element>
1913
1920
  </define>
1921
+ <define name="latexmath">
1922
+ <a:documentation>Encoding of LatexMath</a:documentation>
1923
+ <element name="latexmath">
1924
+ <text/>
1925
+ </element>
1926
+ </define>
1927
+ <define name="asciimath">
1928
+ <a:documentation>Encoding of AsciiMath</a:documentation>
1929
+ <element name="asciimath">
1930
+ <text/>
1931
+ </element>
1932
+ </define>
1933
+ <define name="mathml">
1934
+ <a:documentation>Encoding of MathML: the official W3C MathML 3.0 grammar (namespace
1935
+ http://www.w3.org/1998/Math/MathML), via the metanorma wrapper in
1936
+ grammars/mathml/. See grammars/mathml/README.adoc and basicdoc-models#35.</a:documentation>
1937
+ <externalRef href="metanorma-mathml.rng"/>
1938
+ </define>
1914
1939
  <define name="StemAttributes">
1915
1940
  <attribute name="type">
1916
1941
  <a:documentation>The notation used to mathematically format the text</a:documentation>
@@ -213,6 +213,12 @@ Sources are currently only rendered in metanorma-plateau</a:documentation>
213
213
  <define name="ExampleAttributes">
214
214
  <ref name="NumberingAttributes"/>
215
215
  <ref name="BlockAttributes"/>
216
+ <optional>
217
+ <attribute name="collapsible">
218
+ <a:documentation>Render the example as collapsible (HTML5 details/summary)</a:documentation>
219
+ <data type="boolean"/>
220
+ </attribute>
221
+ </optional>
216
222
  </define>
217
223
  <define name="ExampleBody">
218
224
  <optional>
@@ -846,7 +852,7 @@ titlecase, or lowercase</a:documentation>
846
852
  <a:documentation>Width of the figure block in rendering</a:documentation>
847
853
  </attribute>
848
854
  </optional>
849
- <ref name="BlockAttributes"/>
855
+ <ref name="BlockAttributesCore"/>
850
856
  </define>
851
857
  <define name="SourceAttributes" combine="interleave">
852
858
  <ref name="BlockAttributes"/>
@@ -1277,7 +1283,11 @@ That concept may be defined as a term within the current document, or it may be
1277
1283
  <a:documentation>Class of input form</a:documentation>
1278
1284
  </attribute>
1279
1285
  </optional>
1280
- <ref name="BlockAttributes"/>
1286
+ <ref name="BlockAttributesCore">
1287
+ <a:documentation>form declares its own class above, so it uses BlockAttributesCore to
1288
+ avoid duplicating the custom-class slot in BlockAttributes. Treated like
1289
+ figure. See metanorma/metanorma-standoc#1197</a:documentation>
1290
+ </ref>
1281
1291
  <zeroOrMore>
1282
1292
  <!-- Input form contents -->
1283
1293
  <choice>
@@ -2762,7 +2772,11 @@ links within an SVG file, so that the SVG file can hyperlink to anchors within t
2762
2772
  </oneOrMore>
2763
2773
  </element>
2764
2774
  </define>
2765
- <define name="BlockAttributes">
2775
+ <define name="BlockAttributesCore">
2776
+ <a:documentation>Block attributes excluding the custom CSS class. Blocks that already carry
2777
+ their own `class` attribute (e.g. figure, via basicdoc FigureAttributes)
2778
+ include this directly, to avoid a duplicate-attribute collision with the
2779
+ `class` added in BlockAttributes. See metanorma/metanorma-standoc#1197</a:documentation>
2766
2780
  <optional>
2767
2781
  <attribute name="keep-with-next">
2768
2782
  <a:documentation>Keep this block on the same page as the following block in paged media</a:documentation>
@@ -2794,6 +2808,17 @@ to span across both columns</a:documentation>
2794
2808
  </attribute>
2795
2809
  </optional>
2796
2810
  </define>
2811
+ <define name="BlockAttributes">
2812
+ <a:documentation>Universal block attributes, including a custom CSS class (space-separated,
2813
+ appended additively to the block's built-in HTML class, HTML output only).
2814
+ See metanorma/metanorma-standoc#1197</a:documentation>
2815
+ <ref name="BlockAttributesCore"/>
2816
+ <optional>
2817
+ <attribute name="class">
2818
+ <a:documentation>Custom CSS class(es) for the block in HTML output</a:documentation>
2819
+ </attribute>
2820
+ </optional>
2821
+ </define>
2797
2822
  <define name="DisplayDirective">
2798
2823
  <a:documentation>Directive on how to render a block in Presentation XML </a:documentation>
2799
2824
  <optional>