metanorma-standoc 1.5.3 → 1.6.4

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rake.yml +66 -0
  3. data/README.adoc +1 -3
  4. data/lib/asciidoctor/standoc/base.rb +6 -1
  5. data/lib/asciidoctor/standoc/basicdoc.rng +4 -11
  6. data/lib/asciidoctor/standoc/cleanup.rb +78 -12
  7. data/lib/asciidoctor/standoc/cleanup_block.rb +41 -4
  8. data/lib/asciidoctor/standoc/cleanup_boilerplate.rb +14 -0
  9. data/lib/asciidoctor/standoc/cleanup_footnotes.rb +25 -0
  10. data/lib/asciidoctor/standoc/cleanup_inline.rb +6 -2
  11. data/lib/asciidoctor/standoc/cleanup_ref.rb +3 -4
  12. data/lib/asciidoctor/standoc/converter.rb +58 -3
  13. data/lib/asciidoctor/standoc/front.rb +9 -3
  14. data/lib/asciidoctor/standoc/front_contributor.rb +43 -11
  15. data/lib/asciidoctor/standoc/inline.rb +18 -40
  16. data/lib/asciidoctor/standoc/isodoc.rng +27 -50
  17. data/lib/asciidoctor/standoc/lists.rb +4 -2
  18. data/lib/asciidoctor/standoc/macros.rb +28 -2
  19. data/lib/asciidoctor/standoc/ref.rb +46 -48
  20. data/lib/asciidoctor/standoc/ref_sect.rb +16 -8
  21. data/lib/asciidoctor/standoc/section.rb +5 -9
  22. data/lib/liquid/custom_blocks/with_json_nested_context.rb +18 -0
  23. data/lib/liquid/custom_blocks/with_yaml_nested_context.rb +19 -0
  24. data/lib/metanorma/standoc.rb +0 -5
  25. data/lib/metanorma/standoc/version.rb +20 -1
  26. data/metanorma-standoc.gemspec +7 -3
  27. data/spec/asciidoctor-standoc/base_spec.rb +246 -9
  28. data/spec/asciidoctor-standoc/blocks_spec.rb +1 -1
  29. data/spec/asciidoctor-standoc/cleanup_sections_spec.rb +1514 -0
  30. data/spec/asciidoctor-standoc/cleanup_spec.rb +384 -1547
  31. data/spec/asciidoctor-standoc/inline_spec.rb +128 -4
  32. data/spec/asciidoctor-standoc/isobib_cache_spec.rb +15 -22
  33. data/spec/asciidoctor-standoc/lists_spec.rb +10 -1
  34. data/spec/asciidoctor-standoc/macros_json2text_spec.rb +1 -1
  35. data/spec/asciidoctor-standoc/macros_lutaml_spec.rb +80 -0
  36. data/spec/asciidoctor-standoc/macros_spec.rb +258 -0
  37. data/spec/asciidoctor-standoc/macros_yaml2text_spec.rb +1 -1
  38. data/spec/asciidoctor-standoc/refs_dl_spec.rb +8 -8
  39. data/spec/asciidoctor-standoc/refs_spec.rb +362 -104
  40. data/spec/asciidoctor-standoc/validate_spec.rb +26 -0
  41. data/spec/fixtures/diagram_definitions.lutaml +22 -0
  42. data/spec/fixtures/test.exp +121 -0
  43. data/spec/spec_helper.rb +34 -1
  44. data/spec/support/shared_examples/structured_data_2_text_preprocessor.rb +201 -3
  45. data/spec/vcr_cassettes/dated_iso_ref_joint_iso_iec.yml +47 -231
  46. data/spec/vcr_cassettes/isobib_get_123.yml +14 -60
  47. data/spec/vcr_cassettes/isobib_get_123_1.yml +24 -116
  48. data/spec/vcr_cassettes/isobib_get_123_1_fr.yml +361 -0
  49. data/spec/vcr_cassettes/isobib_get_123_2001.yml +14 -60
  50. data/spec/vcr_cassettes/isobib_get_124.yml +12 -58
  51. data/spec/vcr_cassettes/rfcbib_get_rfc8341.yml +8 -8
  52. data/spec/vcr_cassettes/separates_iev_citations_by_top_level_clause.yml +292 -162
  53. metadata +59 -17
  54. data/.github/workflows/macos.yml +0 -46
  55. data/.github/workflows/ubuntu.yml +0 -49
  56. data/.github/workflows/windows.yml +0 -53
  57. data/lib/asciidoctor/standoc/base_structured_text_preprocessor.rb +0 -123
  58. data/lib/asciidoctor/standoc/json2_text_preprocessor.rb +0 -43
  59. data/lib/asciidoctor/standoc/yaml2_text_preprocessor.rb +0 -43
  60. data/lib/metanorma/standoc/latexml_requirement.rb +0 -62
  61. data/lib/metanorma/standoc/requirement.rb +0 -13
@@ -1,4 +1,7 @@
1
1
  require "asciidoctor"
2
+ require "fontist"
3
+ require "fontist/manifest/install"
4
+ require "metanorma/util"
2
5
  require "metanorma/standoc/version"
3
6
  require "asciidoctor/standoc/base"
4
7
  require "asciidoctor/standoc/front"
@@ -23,8 +26,10 @@ module Asciidoctor
23
26
  Asciidoctor::Extensions.register do
24
27
  preprocessor Asciidoctor::Standoc::Datamodel::AttributesTablePreprocessor
25
28
  preprocessor Asciidoctor::Standoc::Datamodel::DiagramPreprocessor
26
- preprocessor Asciidoctor::Standoc::Yaml2TextPreprocessor
27
- preprocessor Asciidoctor::Standoc::Json2TextPreprocessor
29
+ preprocessor Metanorma::Plugin::Datastruct::Json2TextPreprocessor
30
+ preprocessor Metanorma::Plugin::Datastruct::Yaml2TextPreprocessor
31
+ preprocessor Metanorma::Plugin::Lutaml::LutamlPreprocessor
32
+ preprocessor Metanorma::Plugin::Lutaml::LutamlUmlAttributesTablePreprocessor
28
33
  inline_macro Asciidoctor::Standoc::AltTermInlineMacro
29
34
  inline_macro Asciidoctor::Standoc::DeprecatedTermInlineMacro
30
35
  inline_macro Asciidoctor::Standoc::DomainTermInlineMacro
@@ -32,9 +37,12 @@ module Asciidoctor
32
37
  inline_macro Asciidoctor::Standoc::HTML5RubyMacro
33
38
  inline_macro Asciidoctor::Standoc::ConceptInlineMacro
34
39
  inline_macro Asciidoctor::Standoc::AutonumberInlineMacro
40
+ inline_macro Asciidoctor::Standoc::VariantInlineMacro
41
+ inline_macro Asciidoctor::Standoc::FootnoteBlockInlineMacro
35
42
  block Asciidoctor::Standoc::ToDoAdmonitionBlock
36
43
  treeprocessor Asciidoctor::Standoc::ToDoInlineAdmonitionBlock
37
44
  block Asciidoctor::Standoc::PlantUMLBlockMacro
45
+ block Metanorma::Plugin::Lutaml::LutamlDiagramBlock
38
46
  block Asciidoctor::Standoc::PseudocodeBlockMacro
39
47
  end
40
48
 
@@ -61,6 +69,8 @@ module Asciidoctor
61
69
  basebackend "html"
62
70
  outfilesuffix ".xml"
63
71
  @libdir = File.dirname(self.class::_file || __FILE__)
72
+
73
+ install_fonts(opts)
64
74
  end
65
75
 
66
76
  class << self
@@ -73,7 +83,52 @@ module Asciidoctor
73
83
 
74
84
  # path to isodoc assets in child gems
75
85
  def html_doc_path(file)
76
- File.join(@libdir, File.join("../../isodoc/html", file))
86
+ File.join(@libdir, "../../isodoc/html", file)
87
+ end
88
+
89
+ def flavor_name
90
+ self.class.name.split("::")&.[](-2).downcase
91
+ end
92
+
93
+ def fonts_manifest
94
+ File.expand_path(File.join(@libdir, "../../metanorma/", flavor_name, "fonts_manifest.yaml"))
95
+ end
96
+
97
+ def install_fonts(options={})
98
+ if options[:no_install_fonts]
99
+ Metanorma::Util.log("[fontist] Skip font installation because" \
100
+ " --no-install-fonts argument passed", :debug)
101
+ return
102
+ end
103
+
104
+ if fonts_manifest.nil? || !File.exist?(fonts_manifest)
105
+ Metanorma::Util.log("[fontist] Skip font installation because" \
106
+ " font manifest file doesn't exists/defined", :debug)
107
+ return
108
+ end
109
+
110
+ begin
111
+ Fontist::Manifest::Install.call(
112
+ fonts_manifest,
113
+ confirmation: options[:agree_to_terms] ? "yes" : "no"
114
+ )
115
+ rescue Fontist::Errors::LicensingError
116
+ if !options[:agree_to_terms]
117
+ Metanorma::Util.log("[fontist] --agree-to-terms option missing." \
118
+ " You must accept font licenses to install fonts.", :debug)
119
+ elsif options[:continue_without_fonts]
120
+ Metanorma::Util.log("[fontist] Processing will continue without" \
121
+ " fonts installed", :debug)
122
+ else
123
+ Metanorma::Util.log("[fontist] Aborting without proper fonts" \
124
+ " installed", :fatal)
125
+ end
126
+ rescue Fontist::Errors::NonSupportedFontError
127
+ flavor = flavor_name || "cli"
128
+ Metanorma::Util.log("[fontist] '#{font}' font is not supported. " \
129
+ "Please go to github.com/metanorma/metanorma-#{flavor}/issues" \
130
+ " to report this issue.", :info)
131
+ end
77
132
  end
78
133
 
79
134
  alias_method :embedded, :content
@@ -17,6 +17,11 @@ module Asciidoctor
17
17
  xml.docnumber node.attr("docnumber")
18
18
  end
19
19
 
20
+ def metadata_other_id(node, xml)
21
+ a = node.attr("isbn") and xml.docidentifier a, type: "ISBN"
22
+ a = node.attr("isbn10") and xml.docidentifier a, type: "ISBN10"
23
+ end
24
+
20
25
  def metadata_version(node, xml)
21
26
  xml.edition node.attr("edition") if node.attr("edition")
22
27
  xml.version do |v|
@@ -27,7 +32,7 @@ module Asciidoctor
27
32
 
28
33
  def metadata_status(node, xml)
29
34
  xml.status do |s|
30
- s.stage ( node.attr("status") || node.attr("docstage") || "published" )
35
+ s.stage (node.attr("status") || node.attr("docstage") || "published")
31
36
  node.attr("docsubstage") and s.substage node.attr("docsubstage")
32
37
  node.attr("iteration") and s.iteration node.attr("iteration")
33
38
  end
@@ -138,6 +143,7 @@ module Asciidoctor
138
143
  title node, xml
139
144
  metadata_source(node, xml)
140
145
  metadata_id(node, xml)
146
+ metadata_other_id(node, xml)
141
147
  metadata_date(node, xml)
142
148
  metadata_author(node, xml)
143
149
  metadata_publisher(node, xml)
@@ -180,8 +186,8 @@ module Asciidoctor
180
186
  ["en"].each do |lang|
181
187
  at = { language: lang, format: "text/plain" }
182
188
  xml.title **attr_code(at) do |t|
183
- t << (Utils::asciidoc_sub(node.attr("title") || node.attr("title-en")) ||
184
- node.title)
189
+ t << (Utils::asciidoc_sub(node.attr("title") ||
190
+ node.attr("title-en")) || node.title)
185
191
  end
186
192
  end
187
193
  end
@@ -21,22 +21,45 @@ module Asciidoctor
21
21
  end
22
22
  end
23
23
 
24
- def organization(org, orgname)
24
+ def organization(org, orgname, node = nil, default_org = nil)
25
+ abbrevs = org_abbrev
26
+ n = abbrevs.invert[orgname] and orgname = n
25
27
  org.name orgname
28
+ default_org and a = node.attr("subdivision") and org.subdivision a
29
+ abbr = org_abbrev[orgname]
30
+ default_org && b = node.attr("subdivision-abbr") and abbr = b
31
+ abbr and org.abbreviation abbr
32
+ default_org and org_address(node, org)
33
+ end
34
+
35
+ def org_address(node, p)
36
+ node.attr("pub-address") and p.address do |ad|
37
+ ad.formattedAddress do |f|
38
+ f << node.attr("pub-address").gsub(/ \+\n/, "<br/>")
39
+ end
40
+ end
41
+ node.attr("pub-phone") and p.phone node.attr("pub-phone")
42
+ node.attr("pub-fax") and p.phone node.attr("pub-fax"), **{type: "fax"}
43
+ node.attr("pub-email") and p.email node.attr("pub-email")
44
+ node.attr("pub-uri") and p.uri node.attr("pub-uri")
26
45
  end
27
46
 
28
47
  # , " => ," : CSV definition does not deal with space followed by quote
29
48
  # at start of field
30
49
  def csv_split(s, delim = ",")
31
- CSV.parse_line(s&.gsub(/, "(?!")/, ',"'), liberal_parsing: true,
32
- col_sep: delim)&.map { |x| x.strip }
50
+ CSV.parse_line(s&.gsub(/, "(?!")/, ',"'),
51
+ liberal_parsing: true,
52
+ col_sep: delim)&.compact&.map { |x| x.strip }
33
53
  end
34
54
 
35
55
  def metadata_author(node, xml)
36
- csv_split(node.attr("publisher") || default_publisher || "")&.each do |p|
56
+ csv_split(node.attr("publisher") || default_publisher || "")&.
57
+ each do |p|
37
58
  xml.contributor do |c|
38
59
  c.role **{ type: "author" }
39
- c.organization { |a| organization(a, p) }
60
+ c.organization do |a|
61
+ organization(a, p, node, !node.attr("publisher"))
62
+ end
40
63
  end
41
64
  end
42
65
  personal_author(node, xml)
@@ -95,7 +118,9 @@ module Asciidoctor
95
118
  abbr = node.attr("affiliation_abbrev#{suffix}") and
96
119
  o.abbreviation abbr
97
120
  node.attr("address#{suffix}") and o.address do |ad|
98
- ad.formattedAddress node.attr("address#{suffix}")
121
+ ad.formattedAddress do |f|
122
+ f << node.attr("address#{suffix}").gsub(/ \+\n/, "<br/>")
123
+ end
99
124
  end
100
125
  end
101
126
  end
@@ -105,24 +130,31 @@ module Asciidoctor
105
130
  nil
106
131
  end
107
132
 
133
+ def org_abbrev
134
+ { }
135
+ end
136
+
108
137
  def metadata_publisher(node, xml)
109
138
  publishers = node.attr("publisher") || default_publisher || return
110
139
  csv_split(publishers)&.each do |p|
111
140
  xml.contributor do |c|
112
141
  c.role **{ type: "publisher" }
113
- c.organization { |a| organization(a, p) }
142
+ c.organization do |a|
143
+ organization(a, p, node, !node.attr("publisher"))
144
+ end
114
145
  end
115
146
  end
116
147
  end
117
148
 
118
149
  def metadata_copyright(node, xml)
119
- publishers = node.attr("copyright-holder") || node.attr("publisher") ||
120
- default_publisher || "-"
121
- csv_split(publishers)&.each do |p|
150
+ pub = node.attr("copyright-holder") || node.attr("publisher")
151
+ csv_split(pub || default_publisher || "-")&.each do |p|
122
152
  xml.copyright do |c|
123
153
  c.from (node.attr("copyright-year") || Date.today.year)
124
154
  p.match(/[A-Za-z]/).nil? or c.owner do |owner|
125
- owner.organization { |o| organization(o, p) }
155
+ owner.organization do |a|
156
+ organization(a, p, node, !pub)
157
+ end
126
158
  end
127
159
  end
128
160
  end
@@ -33,18 +33,28 @@ module Asciidoctor
33
33
  end
34
34
 
35
35
  def inline_anchor_xref(node)
36
- m = /^(?<case>capital%|lowercase%)?(?<fn>fn(:\s*(?<text>.*))?)?$/.match node.text
37
- casing = m.nil? ? nil : m[:case]&.sub(/%$/, "")
38
- f = (m.nil? || m[:fn].nil?) ? "inline" : "footnote"
39
- c = (!m.nil? && (!m[:fn].nil? || !m[:case].nil?)) ? m[:text] : node.text
40
- t = node.target.gsub(/^#/, "").gsub(%r{(\.xml|\.adoc)(#.*$)}, "\\2")
41
36
  noko do |xml|
42
- xml.xref **attr_code(target: t, type: f, case: casing) do |x|
37
+ attrs = inline_anchor_xref_attrs(node)
38
+ c = attrs[:text]
39
+ attrs.delete(:text) unless c.nil?
40
+ xml.xref **attr_code(attrs) do |x|
43
41
  x << c
44
42
  end
45
43
  end.join
46
44
  end
47
45
 
46
+ def inline_anchor_xref_attrs(node)
47
+ m = /^(?<drop>droploc%)?(?<case>capital%|lowercase%)?(?<drop2>droploc%)?
48
+ (?<fn>fn(:\s*(?<text>.*))?)?$/x.match node.text
49
+ casing = m.nil? ? nil : m[:case]&.sub(/%$/, "")
50
+ droploc = m.nil? ? nil : ((m[:drop].nil? && m[:drop2].nil?) ? nil: true)
51
+ f = (m.nil? || m[:fn].nil?) ? "inline" : "footnote"
52
+ c = (!m.nil? && (%i[case fn drop drop2].any? { |x| !m[x].nil? })) ?
53
+ m[:text] : node.text
54
+ t = node.target.gsub(/^#/, "").gsub(%r{(\.xml|\.adoc)(#.*$)}, "\\2")
55
+ { target: t, type: f, case: casing, droploc: droploc, text: c }
56
+ end
57
+
48
58
  def inline_anchor_link(node)
49
59
  contents = node.text
50
60
  contents = "" if node.target.gsub(%r{^mailto:}, "") == node.text
@@ -106,40 +116,8 @@ module Asciidoctor
106
116
  HTMLEntities.new.encode(text, :basic, :hexadecimal).
107
117
  gsub(/&amp;gt;/, ">").gsub(/\&amp;lt;/, "<").gsub(/&amp;amp;/, "&").
108
118
  gsub(/&gt;/, ">").gsub(/&lt;/, "<").gsub(/&amp;/, "&").
109
- gsub(/&quot;/, '"').gsub(/&#xa;/, "\n")
110
- end
111
-
112
- =begin
113
- def latex_run1(lxm_input, cmd)
114
- IO.popen(cmd, "r+", external_encoding: "UTF-8") do |io|
115
- io.write(lxm_input)
116
- io.close_write
117
- io.read
118
- end
119
- end
120
-
121
- def latex_run(lxm_input)
122
- results = nil
123
- Metanorma::Standoc::Requirements[:latexml].cmd.each_with_index do |cmd, i|
124
- warn "Retrying with #{cmd}" if i > 0
125
- results = latex_run1(lxm_input, cmd)
126
- if $CHILD_STATUS.to_i.zero?
127
- warn "Success!" if i > 0
128
- break
129
- end
130
- end
131
- $CHILD_STATUS.to_i.zero? ? results : nil
132
- end
133
-
134
- def latex_parse(text)
135
- lxm_input = Unicode2LaTeX.unicode2latex(HTMLEntities.new.decode(text))
136
- results = latex_run(lxm_input)
137
- results.nil? and
138
- @log.add('Math', nil,
139
- "latexmlmath failed to process equation:\n#{lxm_input}")
140
- results&.sub(%r{<math ([^>]+ )?display="block"}, "<math \\1")
119
+ gsub(/&quot;/, '"').gsub(/&#xa;/, "\n").gsub(/&amp;#/, "&#")
141
120
  end
142
- =end
143
121
 
144
122
  def latex_parse(text)
145
123
  lxm_input = Unicode2LaTeX.unicode2latex(HTMLEntities.new.decode(text))
@@ -163,7 +141,7 @@ module Asciidoctor
163
141
  s.parent.children = math
164
142
  end
165
143
  else
166
- xml.stem text, **{ type: "AsciiMath" }
144
+ xml.stem text&.gsub(/\&amp;#/, "&#"), **{ type: "AsciiMath" }
167
145
  end
168
146
  end
169
147
 
@@ -24,6 +24,14 @@
24
24
  <start>
25
25
  <ref name="standard-document"/>
26
26
  </start>
27
+ <define name="doctype">
28
+ <element name="doctype">
29
+ <optional>
30
+ <attribute name="abbreviation"/>
31
+ </optional>
32
+ <ref name="DocumentType"/>
33
+ </element>
34
+ </define>
27
35
  <define name="hyperlink">
28
36
  <element name="link">
29
37
  <attribute name="target">
@@ -42,7 +50,6 @@
42
50
  </define>
43
51
  <define name="xref">
44
52
  <element name="xref">
45
- <!-- attribute target { xsd:IDREF }, -->
46
53
  <attribute name="target">
47
54
  <data type="string">
48
55
  <param name="pattern">\i\c*|\c+#\c+</param>
@@ -64,6 +71,11 @@
64
71
  </choice>
65
72
  </attribute>
66
73
  </optional>
74
+ <optional>
75
+ <attribute name="droploc">
76
+ <data type="boolean"/>
77
+ </attribute>
78
+ </optional>
67
79
  <text/>
68
80
  </element>
69
81
  </define>
@@ -137,6 +149,11 @@
137
149
  <data type="boolean"/>
138
150
  </attribute>
139
151
  </optional>
152
+ <optional>
153
+ <attribute name="key">
154
+ <data type="boolean"/>
155
+ </attribute>
156
+ </optional>
140
157
  <oneOrMore>
141
158
  <ref name="dt"/>
142
159
  <ref name="dd"/>
@@ -859,6 +876,13 @@
859
876
  </define>
860
877
  <define name="standard-document">
861
878
  <element name="standard-document">
879
+ <attribute name="version"/>
880
+ <attribute name="type">
881
+ <choice>
882
+ <value>semantic</value>
883
+ <value>presentation</value>
884
+ </choice>
885
+ </attribute>
862
886
  <ref name="bibdata"/>
863
887
  <optional>
864
888
  <ref name="boilerplate"/>
@@ -880,7 +904,7 @@
880
904
  <oneOrMore>
881
905
  <choice>
882
906
  <ref name="content"/>
883
- <ref name="preface_abstract"/>
907
+ <ref name="abstract"/>
884
908
  <ref name="foreword"/>
885
909
  <ref name="introduction"/>
886
910
  <ref name="acknowledgements"/>
@@ -1153,49 +1177,7 @@
1153
1177
  </define>
1154
1178
  <define name="annex">
1155
1179
  <element name="annex">
1156
- <optional>
1157
- <attribute name="id">
1158
- <data type="ID"/>
1159
- </attribute>
1160
- </optional>
1161
- <optional>
1162
- <attribute name="language"/>
1163
- </optional>
1164
- <optional>
1165
- <attribute name="script"/>
1166
- </optional>
1167
- <optional>
1168
- <attribute name="inline-header">
1169
- <data type="boolean"/>
1170
- </attribute>
1171
- </optional>
1172
- <attribute name="obligation">
1173
- <choice>
1174
- <value>normative</value>
1175
- <value>informative</value>
1176
- </choice>
1177
- </attribute>
1178
- <optional>
1179
- <ref name="section-title"/>
1180
- </optional>
1181
- <group>
1182
- <group>
1183
- <zeroOrMore>
1184
- <ref name="BasicBlock"/>
1185
- </zeroOrMore>
1186
- <zeroOrMore>
1187
- <ref name="note"/>
1188
- </zeroOrMore>
1189
- </group>
1190
- <zeroOrMore>
1191
- <choice>
1192
- <ref name="annex-subsection"/>
1193
- <ref name="terms"/>
1194
- <ref name="definitions"/>
1195
- <ref name="references"/>
1196
- </choice>
1197
- </zeroOrMore>
1198
- </group>
1180
+ <ref name="Annex-Section"/>
1199
1181
  </element>
1200
1182
  </define>
1201
1183
  <define name="terms">
@@ -1481,11 +1463,6 @@
1481
1463
  </optional>
1482
1464
  </element>
1483
1465
  </define>
1484
- <define name="preface_abstract">
1485
- <element name="abstract">
1486
- <ref name="Basic-Section"/>
1487
- </element>
1488
- </define>
1489
1466
  <define name="term-clause">
1490
1467
  <element name="clause">
1491
1468
  <optional>
@@ -55,7 +55,7 @@ module Asciidoctor
55
55
 
56
56
  def ol_attrs(node)
57
57
  attr_code(keep_attrs(node).merge(id: Utils::anchor_or_uuid(node),
58
- type: olist_style(node.style)))
58
+ type: olist_style(node.style)))
59
59
  end
60
60
 
61
61
  def olist(node)
@@ -87,7 +87,9 @@ module Asciidoctor
87
87
  end
88
88
 
89
89
  def dl_attrs(node)
90
- attr_code(id_attr(node).merge(keep_attrs(node)))
90
+ attr_code(keep_attrs(node).
91
+ merge(id: Utils::anchor_or_uuid(node),
92
+ key: node.option?("key") ? "true" : nil))
91
93
  end
92
94
 
93
95
  def dlist(node)