coradoc 2.0.4 → 2.0.6

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 (127) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +96 -51
  3. data/coradoc-adoc/lib/coradoc/asciidoc/model/break.rb +7 -0
  4. data/coradoc-adoc/lib/coradoc/asciidoc/model/document.rb +2 -2
  5. data/coradoc-adoc/lib/coradoc/asciidoc/model/include.rb +2 -2
  6. data/coradoc-adoc/lib/coradoc/asciidoc/model.rb +6 -4
  7. data/coradoc-adoc/lib/coradoc/asciidoc/parser/base.rb +1 -0
  8. data/coradoc-adoc/lib/coradoc/asciidoc/parser/block.rb +8 -7
  9. data/coradoc-adoc/lib/coradoc/asciidoc/parser/content.rb +13 -3
  10. data/coradoc-adoc/lib/coradoc/asciidoc/parser/inline.rb +15 -15
  11. data/coradoc-adoc/lib/coradoc/asciidoc/parser/list.rb +2 -2
  12. data/coradoc-adoc/lib/coradoc/asciidoc/parser/section.rb +1 -0
  13. data/coradoc-adoc/lib/coradoc/asciidoc/serializer/serializers/document.rb +4 -1
  14. data/coradoc-adoc/lib/coradoc/asciidoc/transform/from_core_model.rb +84 -13
  15. data/coradoc-adoc/lib/coradoc/asciidoc/transform/to_core_model.rb +106 -27
  16. data/coradoc-adoc/lib/coradoc/asciidoc/transform/to_core_model_registrations.rb +50 -19
  17. data/coradoc-adoc/lib/coradoc/asciidoc/transformer/misc_rules.rb +5 -0
  18. data/coradoc-adoc/lib/coradoc/asciidoc/transformer/structural_rules.rb +9 -8
  19. data/coradoc-adoc/lib/coradoc/asciidoc/transformer.rb +22 -5
  20. data/coradoc-adoc/lib/coradoc/asciidoc.rb +7 -2
  21. data/coradoc-adoc/spec/coradoc/asciidoc/integration_pipeline_spec.rb +342 -0
  22. data/coradoc-adoc/spec/coradoc/asciidoc/list_continuation_spec.rb +1 -1
  23. data/coradoc-adoc/spec/coradoc/asciidoc/round_trip_spec.rb +0 -2
  24. data/coradoc-adoc/spec/coradoc/asciidoc/transform/from_core_model_spec.rb +3 -2
  25. data/coradoc-adoc/spec/coradoc/developer_experience_spec.rb +0 -2
  26. data/coradoc-adoc/spec/transform/from_core_model_spec.rb +3 -3
  27. data/coradoc-adoc/spec/transform/to_core_model_spec.rb +1 -0
  28. data/coradoc-html/lib/coradoc/html/converters/base.rb +41 -17
  29. data/coradoc-html/lib/coradoc/html/converters/example.rb +1 -2
  30. data/coradoc-html/lib/coradoc/html/converters/listing.rb +1 -2
  31. data/coradoc-html/lib/coradoc/html/converters/literal.rb +1 -2
  32. data/coradoc-html/lib/coradoc/html/converters/open.rb +1 -2
  33. data/coradoc-html/lib/coradoc/html/converters/quote.rb +2 -3
  34. data/coradoc-html/lib/coradoc/html/converters/sidebar.rb +1 -2
  35. data/coradoc-html/lib/coradoc/html/converters/source.rb +1 -2
  36. data/coradoc-html/lib/coradoc/html/converters/source_code.rb +1 -2
  37. data/coradoc-html/lib/coradoc/html/converters/term.rb +1 -2
  38. data/coradoc-html/lib/coradoc/html/converters/verse.rb +2 -3
  39. data/coradoc-html/lib/coradoc/html/input/converters/aside.rb +1 -1
  40. data/coradoc-html/lib/coradoc/html/input/converters/audio.rb +1 -1
  41. data/coradoc-html/lib/coradoc/html/input/converters/blockquote.rb +2 -3
  42. data/coradoc-html/lib/coradoc/html/input/converters/div.rb +1 -2
  43. data/coradoc-html/lib/coradoc/html/input/converters/figure.rb +2 -3
  44. data/coradoc-html/lib/coradoc/html/input/converters/hr.rb +1 -1
  45. data/coradoc-html/lib/coradoc/html/input/converters/p.rb +2 -1
  46. data/coradoc-html/lib/coradoc/html/input/converters/pre.rb +14 -10
  47. data/coradoc-html/lib/coradoc/html/input/converters/video.rb +1 -1
  48. data/coradoc-html/lib/coradoc/html/renderer.rb +50 -2
  49. data/coradoc-html/lib/coradoc/html/spa.rb +2 -2
  50. data/coradoc-html/lib/coradoc/html/static.rb +2 -2
  51. data/coradoc-html/lib/coradoc/html/templates/core_model/definition_list.liquid +11 -0
  52. data/coradoc-html/lib/coradoc/html/theme/modern/javascript_generator.rb +2 -2
  53. data/coradoc-html/lib/coradoc/html/theme/modern/serializers/document_serializer.rb +7 -11
  54. data/coradoc-html/lib/coradoc/html/theme/modern_renderer.rb +6 -7
  55. data/coradoc-html/lib/coradoc/html.rb +5 -15
  56. data/coradoc-html/spec/input/converters/converters_spec.rb +6 -8
  57. data/coradoc-markdown/lib/coradoc/markdown/model/attribute_list.rb +0 -3
  58. data/coradoc-markdown/lib/coradoc/markdown/model/definition_list.rb +0 -3
  59. data/coradoc-markdown/lib/coradoc/markdown/model/extension.rb +0 -2
  60. data/coradoc-markdown/lib/coradoc/markdown/model/highlight.rb +0 -1
  61. data/coradoc-markdown/lib/coradoc/markdown/model/math.rb +0 -1
  62. data/coradoc-markdown/lib/coradoc/markdown/model/strikethrough.rb +0 -1
  63. data/coradoc-markdown/lib/coradoc/markdown/parser/ast_processor.rb +2 -2
  64. data/coradoc-markdown/lib/coradoc/markdown/parser/block_parser.rb +2 -2
  65. data/coradoc-markdown/lib/coradoc/markdown/parser/inline_parser.rb +3 -3
  66. data/coradoc-markdown/lib/coradoc/markdown/parser.rb +9 -3
  67. data/coradoc-markdown/lib/coradoc/markdown/toc_generator.rb +6 -4
  68. data/coradoc-markdown/lib/coradoc/markdown/transform/from_core_model.rb +27 -6
  69. data/coradoc-markdown/lib/coradoc/markdown/transform/to_core_model.rb +5 -9
  70. data/coradoc-markdown/lib/coradoc/markdown/transformer.rb +2 -1
  71. data/coradoc-markdown/spec/transform/to_core_model_spec.rb +6 -8
  72. data/exe/coradoc +1 -0
  73. data/lib/coradoc/coradoc.rb +6 -7
  74. data/lib/coradoc/core_model/annotation_block.rb +0 -2
  75. data/lib/coradoc/core_model/block.rb +92 -32
  76. data/lib/coradoc/core_model/builder/block_builder.rb +20 -2
  77. data/lib/coradoc/core_model/builder/detection.rb +0 -8
  78. data/lib/coradoc/core_model/builder.rb +6 -7
  79. data/lib/coradoc/core_model/example_block.rb +12 -0
  80. data/lib/coradoc/core_model/listing_block.rb +15 -0
  81. data/lib/coradoc/core_model/literal_block.rb +12 -0
  82. data/lib/coradoc/core_model/open_block.rb +12 -0
  83. data/lib/coradoc/core_model/pass_block.rb +12 -0
  84. data/lib/coradoc/core_model/quote_block.rb +14 -0
  85. data/lib/coradoc/core_model/reviewer_block.rb +12 -0
  86. data/lib/coradoc/core_model/sidebar_block.rb +12 -0
  87. data/lib/coradoc/core_model/source_block.rb +22 -0
  88. data/lib/coradoc/core_model/structural_element.rb +4 -0
  89. data/lib/coradoc/core_model/verse_block.rb +14 -0
  90. data/lib/coradoc/core_model.rb +10 -0
  91. data/lib/coradoc/document_manipulator.rb +3 -1
  92. data/lib/coradoc/processor_registry.rb +0 -2
  93. data/lib/coradoc/version.rb +1 -1
  94. metadata +13 -34
  95. data/coradoc-markdown/coverage/.last_run.json +0 -5
  96. data/coradoc-markdown/coverage/.resultset.json +0 -6322
  97. data/coradoc-markdown/coverage/.resultset.json.lock +0 -0
  98. data/coradoc-markdown/coverage/assets/0.13.2/DataTables-1.10.20/images/sort_asc.png +0 -0
  99. data/coradoc-markdown/coverage/assets/0.13.2/DataTables-1.10.20/images/sort_asc_disabled.png +0 -0
  100. data/coradoc-markdown/coverage/assets/0.13.2/DataTables-1.10.20/images/sort_both.png +0 -0
  101. data/coradoc-markdown/coverage/assets/0.13.2/DataTables-1.10.20/images/sort_desc.png +0 -0
  102. data/coradoc-markdown/coverage/assets/0.13.2/DataTables-1.10.20/images/sort_desc_disabled.png +0 -0
  103. data/coradoc-markdown/coverage/assets/0.13.2/application.css +0 -1
  104. data/coradoc-markdown/coverage/assets/0.13.2/application.js +0 -7
  105. data/coradoc-markdown/coverage/assets/0.13.2/colorbox/border.png +0 -0
  106. data/coradoc-markdown/coverage/assets/0.13.2/colorbox/controls.png +0 -0
  107. data/coradoc-markdown/coverage/assets/0.13.2/colorbox/loading.gif +0 -0
  108. data/coradoc-markdown/coverage/assets/0.13.2/colorbox/loading_background.png +0 -0
  109. data/coradoc-markdown/coverage/assets/0.13.2/favicon_green.png +0 -0
  110. data/coradoc-markdown/coverage/assets/0.13.2/favicon_red.png +0 -0
  111. data/coradoc-markdown/coverage/assets/0.13.2/favicon_yellow.png +0 -0
  112. data/coradoc-markdown/coverage/assets/0.13.2/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  113. data/coradoc-markdown/coverage/assets/0.13.2/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  114. data/coradoc-markdown/coverage/assets/0.13.2/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  115. data/coradoc-markdown/coverage/assets/0.13.2/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  116. data/coradoc-markdown/coverage/assets/0.13.2/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  117. data/coradoc-markdown/coverage/assets/0.13.2/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  118. data/coradoc-markdown/coverage/assets/0.13.2/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  119. data/coradoc-markdown/coverage/assets/0.13.2/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  120. data/coradoc-markdown/coverage/assets/0.13.2/images/ui-icons_222222_256x240.png +0 -0
  121. data/coradoc-markdown/coverage/assets/0.13.2/images/ui-icons_2e83ff_256x240.png +0 -0
  122. data/coradoc-markdown/coverage/assets/0.13.2/images/ui-icons_454545_256x240.png +0 -0
  123. data/coradoc-markdown/coverage/assets/0.13.2/images/ui-icons_888888_256x240.png +0 -0
  124. data/coradoc-markdown/coverage/assets/0.13.2/images/ui-icons_cd0a0a_256x240.png +0 -0
  125. data/coradoc-markdown/coverage/assets/0.13.2/loading.gif +0 -0
  126. data/coradoc-markdown/coverage/assets/0.13.2/magnify.png +0 -0
  127. data/coradoc-markdown/coverage/index.html +0 -66032
@@ -0,0 +1,342 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe 'Integration pipeline fixes' do
6
+ def parse_to_core(adoc)
7
+ ast = Coradoc::AsciiDoc::Parser::Base.parse(adoc)
8
+ model = Coradoc::AsciiDoc::Transformer.transform(ast)
9
+ Coradoc::AsciiDoc::Transform::ToCoreModel.transform(model)
10
+ end
11
+
12
+ def parse_to_ast(adoc)
13
+ Coradoc::AsciiDoc::Parser::Base.parse(adoc)
14
+ end
15
+
16
+ describe 'Fix 01: Page break parsing' do
17
+ it 'parses <<< as page_break at document level' do
18
+ ast = parse_to_ast("= Title\n\n<<<\n")
19
+ doc_nodes = ast[:document]
20
+ page_breaks = doc_nodes.select { |n| n.is_a?(Hash) && n.key?(:page_break) }
21
+ expect(page_breaks.length).to eq(1)
22
+ end
23
+
24
+ it 'parses <<< as page_break inside sections' do
25
+ ast = parse_to_ast("== Section\n\n<<<\n\nSome text\n")
26
+ doc_nodes = ast[:document]
27
+ section = doc_nodes.find { |n| n.is_a?(Hash) && n.key?(:section) }
28
+ contents = section[:section][:contents]
29
+ page_breaks = contents.select { |n| n.is_a?(Hash) && n.key?(:page_break) }
30
+ expect(page_breaks.length).to eq(1)
31
+ end
32
+
33
+ it 'does not capture <<< as paragraph text' do
34
+ ast = parse_to_ast("= Title\n\n<<<\n")
35
+ doc_nodes = ast[:document]
36
+ paragraphs = doc_nodes.select { |n| n.is_a?(Hash) && n.key?(:paragraph) }
37
+ paragraph_texts = paragraphs.map { |p| p[:paragraph][:lines].map { |l| l[:text].to_s }.join }
38
+ expect(paragraph_texts).not_to include('<<<')
39
+ end
40
+
41
+ it 'transforms page_break through to CoreModel as nil (filtered out)' do
42
+ core = parse_to_core("= Title\n\nHello\n\n<<<\n\n== Section\n")
43
+ expect(core).to be_a(Coradoc::CoreModel::StructuralElement)
44
+ expect(core.children.length).to eq(2) # paragraph + section, no page break
45
+ end
46
+ end
47
+
48
+ describe 'Fix 02+03: Table row grouping and header detection' do
49
+ it 'groups cells into rows by column count' do
50
+ adoc = <<~ADOC
51
+ = Doc
52
+
53
+ [cols="2"]
54
+ |===
55
+ | A | B
56
+ | C | D
57
+ | E | F
58
+ |===
59
+ ADOC
60
+
61
+ core = parse_to_core(adoc)
62
+ table = find_first_table(core)
63
+ expect(table).not_to be_nil
64
+ expect(table.rows.length).to eq(3)
65
+ table.rows.each do |row|
66
+ expect(row.cells.length).to eq(2)
67
+ end
68
+ end
69
+
70
+ it 'marks the first row as header' do
71
+ adoc = <<~ADOC
72
+ = Doc
73
+
74
+ [cols="2"]
75
+ |===
76
+ | Header A | Header B
77
+ | Data 1 | Data 2
78
+ |===
79
+ ADOC
80
+
81
+ core = parse_to_core(adoc)
82
+ table = find_first_table(core)
83
+ expect(table.rows.first.header).to be true
84
+ expect(table.rows.last.header).to be false
85
+ end
86
+
87
+ it 'handles 3-column tables' do
88
+ adoc = <<~ADOC
89
+ = Doc
90
+
91
+ [cols="3"]
92
+ |===
93
+ | A | B | C
94
+ | D | E | F
95
+ |===
96
+ ADOC
97
+
98
+ core = parse_to_core(adoc)
99
+ table = find_first_table(core)
100
+ expect(table.rows.length).to eq(2)
101
+ table.rows.each { |row| expect(row.cells.length).to eq(3) }
102
+ end
103
+ end
104
+
105
+ describe 'Fix 04: LineBreak leak' do
106
+ it 'does not leak LineBreak elements into CoreModel children' do
107
+ adoc = <<~ADOC
108
+ = Title
109
+
110
+ First paragraph.
111
+
112
+ Second paragraph.
113
+ ADOC
114
+
115
+ core = parse_to_core(adoc)
116
+ core.children.each do |child|
117
+ next if child.is_a?(String) && child.strip.empty?
118
+
119
+ expect(child).not_to be_a(Coradoc::AsciiDoc::Model::LineBreak)
120
+ end
121
+ end
122
+
123
+ it 'does not leak PageBreak elements into CoreModel children' do
124
+ core = parse_to_core("= Title\n\n<<<\n\n== Section\n")
125
+ core.children.each do |child|
126
+ expect(child).not_to be_a(Coradoc::AsciiDoc::Model::Break::PageBreak)
127
+ end
128
+ end
129
+ end
130
+
131
+ describe 'Fix 05: Document attributes' do
132
+ it 'preserves document attributes in CoreModel' do
133
+ skip 'Parser does not yet propagate document attributes to CoreModel'
134
+ adoc = <<~ADOC
135
+ = My Document
136
+ :author: John
137
+ :revdate: 2024-01-01
138
+
139
+ Content here.
140
+ ADOC
141
+
142
+ core = parse_to_core(adoc)
143
+ expect(core.attributes).to include('author' => 'John', 'revdate' => '2024-01-01')
144
+ end
145
+
146
+ it 'handles multiple attributes' do
147
+ skip 'Parser does not yet propagate document attributes to CoreModel'
148
+ adoc = <<~ADOC
149
+ = Doc
150
+ :docnumber: 1
151
+ :edition: 2
152
+ :language: zh-Hant
153
+
154
+ Text.
155
+ ADOC
156
+
157
+ core = parse_to_core(adoc)
158
+ expect(core.attributes).to include(
159
+ 'docnumber' => '1',
160
+ 'edition' => '2',
161
+ 'language' => 'zh-Hant'
162
+ )
163
+ end
164
+ end
165
+
166
+ describe 'Fix 07: Cross-references' do
167
+ it 'parses simple cross-reference <<id>>' do
168
+ skip 'Cross-reference parsing not yet implemented'
169
+ adoc = <<~ADOC
170
+ = Doc
171
+
172
+ See <<introduction>> for details.
173
+ ADOC
174
+
175
+ core = parse_to_core(adoc)
176
+ xrefs = find_all_xrefs(core)
177
+ expect(xrefs.length).to be >= 1
178
+ expect(xrefs.first.target).to eq('introduction')
179
+ end
180
+
181
+ it 'parses cross-reference with text <<id,text>>' do
182
+ skip 'Cross-reference parsing not yet implemented'
183
+ adoc = <<~ADOC
184
+ = Doc
185
+
186
+ See <<introduction,Introduction>> for details.
187
+ ADOC
188
+
189
+ core = parse_to_core(adoc)
190
+ xrefs = find_all_xrefs(core)
191
+ expect(xrefs.length).to be >= 1
192
+ expect(xrefs.first.target).to eq('introduction')
193
+ expect(xrefs.first.content).to eq('Introduction')
194
+ end
195
+
196
+ it 'parses multiple cross-references' do
197
+ skip 'Cross-reference parsing not yet implemented'
198
+ adoc = <<~ADOC
199
+ = Doc
200
+
201
+ See <<section-a>> and <<section-b,Section B>>.
202
+ ADOC
203
+
204
+ core = parse_to_core(adoc)
205
+ xrefs = find_all_xrefs(core)
206
+ expect(xrefs.length).to be >= 2
207
+ targets = xrefs.map(&:target)
208
+ expect(targets).to include('section-a', 'section-b')
209
+ end
210
+ end
211
+
212
+ describe 'Fix 06: Section hierarchy — bold in list items' do
213
+ it 'parses ordered list items starting with bold formatting' do
214
+ ast = parse_to_ast(". *First* text\n")
215
+ doc_nodes = ast[:document]
216
+ lists = doc_nodes.select { |n| n.is_a?(Hash) && n.key?(:list) }
217
+ expect(lists.length).to eq(1)
218
+ end
219
+
220
+ it 'parses ordered list items starting with bold inside a section' do
221
+ adoc = <<~ADOC
222
+ == Section
223
+
224
+ . *Bold item* — description
225
+ . Normal item
226
+ ADOC
227
+
228
+ ast = parse_to_ast(adoc)
229
+ section = ast[:document].find { |n| n.is_a?(Hash) && n.key?(:section) }
230
+ contents = section[:section][:contents]
231
+ lists = contents.select { |n| n.is_a?(Hash) && n.key?(:list) }
232
+ expect(lists.length).to eq(1)
233
+ end
234
+
235
+ it 'parses source blocks with YAML delimiters inside' do
236
+ adoc = <<~ADOC
237
+ = Doc
238
+
239
+ [source]
240
+ ----
241
+ ---
242
+ frontmatter
243
+ ---
244
+ ----
245
+ ADOC
246
+
247
+ core = parse_to_core(adoc)
248
+ expect(core).to be_a(Coradoc::CoreModel::StructuralElement)
249
+ end
250
+
251
+ it 'does not let highlight unconstrained match across lines' do
252
+ ast = parse_to_ast("## heading\nSome text\n")
253
+ doc_nodes = ast[:document]
254
+ paragraphs = doc_nodes.select { |n| n.is_a?(Hash) && n.key?(:paragraph) }
255
+ highlight_nodes = paragraphs.select do |p|
256
+ text = p[:paragraph]
257
+ text.to_s.include?('highlight')
258
+ end
259
+ expect(highlight_nodes).to be_empty
260
+ end
261
+ end
262
+
263
+ describe 'Fix 08: List marker_type' do
264
+ it 'sets marker_type to unordered for bullet lists' do
265
+ adoc = <<~ADOC
266
+ = Doc
267
+
268
+ * Item one
269
+ * Item two
270
+ * Item three
271
+ ADOC
272
+
273
+ core = parse_to_core(adoc)
274
+ lists = find_all_lists(core)
275
+ expect(lists).not_to be_empty
276
+ expect(lists.first.marker_type).to eq('unordered')
277
+ end
278
+
279
+ it 'sets marker_type to ordered for numbered lists' do
280
+ adoc = <<~ADOC
281
+ = Doc
282
+
283
+ . First item
284
+ . Second item
285
+ . Third item
286
+ ADOC
287
+
288
+ core = parse_to_core(adoc)
289
+ lists = find_all_lists(core)
290
+ expect(lists).not_to be_empty
291
+ expect(lists.first.marker_type).to eq('ordered')
292
+ end
293
+ end
294
+
295
+ private
296
+
297
+ def find_first_table(el)
298
+ return el if el.is_a?(Coradoc::CoreModel::Table)
299
+ return nil unless el.is_a?(Coradoc::CoreModel::Base)
300
+
301
+ if el.class.attributes.key?(:children) && el.children
302
+ el.children.each do |child|
303
+ result = find_first_table(child)
304
+ return result if result
305
+ end
306
+ end
307
+
308
+ nil
309
+ end
310
+
311
+ def find_all_xrefs(el)
312
+ xrefs = []
313
+ return xrefs unless el
314
+
315
+ xrefs << el if el.is_a?(Coradoc::CoreModel::InlineElement) && el.format_type == 'xref'
316
+
317
+ children = if el.respond_to?(:children) && el.children
318
+ el.children
319
+ elsif el.is_a?(Coradoc::CoreModel::Base) && el.class.attributes.key?(:children)
320
+ el.children
321
+ end
322
+
323
+ children&.each { |c| xrefs.concat(find_all_xrefs(c)) }
324
+
325
+ xrefs
326
+ end
327
+
328
+ def find_all_lists(el)
329
+ lists = []
330
+ return lists unless el
331
+
332
+ lists << el if el.is_a?(Coradoc::CoreModel::ListBlock)
333
+
334
+ if el.is_a?(Coradoc::CoreModel::ListBlock) && el.items
335
+ el.items.each { |c| lists.concat(find_all_lists(c)) }
336
+ elsif el.is_a?(Coradoc::CoreModel::Base) && el.class.attributes.key?(:children) && el.children
337
+ el.children.each { |c| lists.concat(find_all_lists(c)) }
338
+ end
339
+
340
+ lists
341
+ end
342
+ end
@@ -21,7 +21,7 @@ RSpec.describe 'AsciiDoc List Continuation' do
21
21
  expect(result).to be_a(Coradoc::AsciiDoc::Model::Document)
22
22
 
23
23
  # Navigate to the list
24
- contents = result.respond_to?(:contents) ? result.contents : result.sections
24
+ contents = result.is_a?(Coradoc::AsciiDoc::Model::Document) ? result.sections : result.contents
25
25
  list = contents.first
26
26
  expect(list).to be_a(Coradoc::AsciiDoc::Model::List::Unordered)
27
27
 
@@ -2,7 +2,6 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- # rubocop:disable RSpec/DescribeClass
6
5
  RSpec.describe 'Round-Trip Conversion' do
7
6
  # Helper to parse, transform to CoreModel, transform back, and serialize
8
7
  def round_trip(adoc_text)
@@ -258,4 +257,3 @@ RSpec.describe 'Round-Trip Conversion' do
258
257
  end
259
258
  end
260
259
  end
261
- # rubocop:enable RSpec/DescribeClass
@@ -32,15 +32,16 @@ RSpec.describe Coradoc::AsciiDoc::Transform::FromCoreModel do
32
32
  end
33
33
 
34
34
  context 'with Block' do
35
- it 'transforms a CoreModel Block to AsciiDoc Block' do
35
+ it 'transforms a CoreModel Block to AsciiDoc Example Block' do
36
36
  core_block = Coradoc::CoreModel::Block.new(
37
+ block_semantic_type: :example,
37
38
  delimiter_type: '====',
38
39
  content: 'Example content'
39
40
  )
40
41
 
41
42
  result = described_class.transform(core_block)
42
43
 
43
- expect(result).to be_a(Coradoc::AsciiDoc::Model::Block::Core)
44
+ expect(result).to be_a(Coradoc::AsciiDoc::Model::Block::Example)
44
45
  end
45
46
  end
46
47
 
@@ -2,7 +2,6 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- # rubocop:disable RSpec/DescribeClass - This is a feature spec for the API facade
6
5
  RSpec.describe 'Developer Experience API' do
7
6
  # Load coradoc core and asciidoc
8
7
 
@@ -121,4 +120,3 @@ RSpec.describe 'Developer Experience API' do
121
120
  end
122
121
  end
123
122
  end
124
- # rubocop:enable RSpec/DescribeClass
@@ -41,18 +41,18 @@ RSpec.describe Coradoc::AsciiDoc::Transform::FromCoreModel do
41
41
  end
42
42
 
43
43
  context 'when transforming a block' do
44
- it 'transforms a CoreModel block to AsciiDoc' do
44
+ it 'transforms a CoreModel example block to AsciiDoc' do
45
45
  block = Coradoc::CoreModel::Block.new(
46
46
  id: 'example-1',
47
+ block_semantic_type: :example,
47
48
  delimiter_type: '====',
48
49
  content: "Line 1\nLine 2"
49
50
  )
50
51
 
51
52
  result = transformer.transform(block)
52
53
 
53
- expect(result).to be_a(Coradoc::AsciiDoc::Model::Block::Core)
54
+ expect(result).to be_a(Coradoc::AsciiDoc::Model::Block::Example)
54
55
  expect(result.id).to eq('example-1')
55
- expect(result.delimiter).to eq('====')
56
56
  end
57
57
  end
58
58
 
@@ -54,6 +54,7 @@ RSpec.describe Coradoc::AsciiDoc::Transform::ToCoreModel do
54
54
  result = transformer.transform(block)
55
55
 
56
56
  expect(result).to be_a(Coradoc::CoreModel::Block)
57
+ expect(result.block_semantic_type).to eq('example')
57
58
  expect(result.delimiter_type).to eq('====')
58
59
  expect(result.content).to eq("Line 1\nLine 2")
59
60
  end
@@ -169,40 +169,64 @@ module Coradoc
169
169
  # Get renderable content (children if present, otherwise content)
170
170
  renderable = block.renderable_content
171
171
 
172
- # Check element_type first for paragraph handling
173
- case block.element_type
174
- when 'paragraph'
172
+ semantic = resolve_block_semantic_type(block)
173
+
174
+ case semantic
175
+ when :paragraph
175
176
  content = convert_content_to_html(renderable, state)
176
177
  return "<p#{attrs}>#{content}</p>" if content && !content.empty?
177
178
 
178
179
  ''
179
- end
180
-
181
- # Then check delimiter_type for special blocks
182
- case block.delimiter_type
183
- when '----', 'source'
180
+ when :source_code
184
181
  lang = block.language || block.metadata&.dig(:language)
185
182
  lang_attr = lang ? " data-lang=\"#{escape_attribute(lang)}\"" : ''
186
183
  "<pre#{attrs}><code#{lang_attr}>#{escape_html(block.flat_text)}</code></pre>"
187
- when '____'
184
+ when :quote, :verse
188
185
  "<blockquote#{attrs}>#{convert_content_to_html(renderable, state)}</blockquote>"
189
- when '===='
186
+ when :example
190
187
  "<div class=\"example\"#{attrs}>#{convert_content_to_html(renderable, state)}</div>"
191
- when '****'
188
+ when :sidebar
192
189
  "<aside class=\"sidebar\"#{attrs}>#{convert_content_to_html(renderable, state)}</aside>"
193
- when '....'
190
+ when :literal
194
191
  "<pre class=\"literal\"#{attrs}>#{escape_html(block.flat_text)}</pre>"
195
- when '++++'
196
- block.flat_text # Pass through
197
- when 'comment'
198
- '' # Skip comments in output
199
- when '***', '---', '___'
192
+ when :pass
193
+ block.flat_text
194
+ when :listing
195
+ "<pre#{attrs}>#{escape_html(block.flat_text)}</pre>"
196
+ when :open
197
+ "<div#{attrs}>#{convert_content_to_html(renderable, state)}</div>"
198
+ when :verse
199
+ "<blockquote#{attrs}>#{convert_content_to_html(renderable, state)}</blockquote>"
200
+ when :comment, :reviewer
201
+ ''
202
+ when :horizontal_rule
200
203
  "<hr#{attrs}>"
201
204
  else
202
205
  "<div#{attrs}>#{convert_content_to_html(renderable, state)}</div>"
203
206
  end
204
207
  end
205
208
 
209
+ # Resolve the semantic type from a block via polymorphic dispatch.
210
+ # Block#resolve_semantic_type handles class-level semantic_type →
211
+ # block_semantic_type attribute → element_type → delimiter fallback.
212
+ def resolve_block_semantic_type(block)
213
+ block.resolve_semantic_type ||
214
+ resolve_format_specific_semantic(block)
215
+ end
216
+
217
+ # Format-specific semantic mappings not covered by the core model
218
+ def resolve_format_specific_semantic(block)
219
+ delim = block.delimiter_type
220
+ return nil unless delim && !delim.empty?
221
+
222
+ case delim
223
+ when "'''", '---', '___', '***' then :horizontal_rule
224
+ when '[verse]' then :verse
225
+ when 'comment' then :comment
226
+ when 'paragraph' then :paragraph
227
+ end
228
+ end
229
+
206
230
  # Render CoreModel structural element
207
231
  def render_core_structural_element(element, state = {})
208
232
  attrs = build_html_attributes(element.id, nil)
@@ -47,8 +47,7 @@ module Coradoc
47
47
  # Extract ID if present
48
48
  id = element['id']
49
49
 
50
- Coradoc::CoreModel::Block.new(
51
- delimiter_type: '====',
50
+ Coradoc::CoreModel::ExampleBlock.new(
52
51
  content: content,
53
52
  title: title,
54
53
  id: id
@@ -55,8 +55,7 @@ module Coradoc
55
55
  # Extract ID if present
56
56
  id = pre_elem['id'] || element['id']
57
57
 
58
- Coradoc::CoreModel::Block.new(
59
- delimiter_type: '----',
58
+ Coradoc::CoreModel::SourceBlock.new(
60
59
  content: content,
61
60
  title: title,
62
61
  id: id
@@ -58,8 +58,7 @@ module Coradoc
58
58
  # Extract ID if present
59
59
  id = pre_elem['id'] || element['id']
60
60
 
61
- Coradoc::CoreModel::Block.new(
62
- delimiter_type: '....',
61
+ Coradoc::CoreModel::LiteralBlock.new(
63
62
  content: content,
64
63
  title: title,
65
64
  id: id
@@ -39,8 +39,7 @@ module Coradoc
39
39
  # Extract ID if present
40
40
  id = element['id']
41
41
 
42
- Coradoc::CoreModel::Block.new(
43
- delimiter_type: '--',
42
+ Coradoc::CoreModel::OpenBlock.new(
44
43
  content: content,
45
44
  id: id
46
45
  )
@@ -43,11 +43,10 @@ module Coradoc
43
43
  # Extract ID if present
44
44
  id = element['id']
45
45
 
46
- Coradoc::CoreModel::Block.new(
47
- delimiter_type: '____',
46
+ Coradoc::CoreModel::QuoteBlock.new(
48
47
  content: content,
49
48
  id: id,
50
- metadata: attribution ? { attribution: attribution } : {}
49
+ attribution: attribution
51
50
  )
52
51
  end
53
52
 
@@ -46,8 +46,7 @@ module Coradoc
46
46
  # Extract ID if present
47
47
  id = element['id']
48
48
 
49
- Coradoc::CoreModel::Block.new(
50
- delimiter_type: '****',
49
+ Coradoc::CoreModel::SidebarBlock.new(
51
50
  content: content,
52
51
  title: title,
53
52
  id: id
@@ -63,8 +63,7 @@ module Coradoc
63
63
  # Extract ID if present
64
64
  id = code_elem['id'] || element['id']
65
65
 
66
- Coradoc::CoreModel::Block.new(
67
- delimiter_type: '----',
66
+ Coradoc::CoreModel::SourceBlock.new(
68
67
  content: content,
69
68
  title: title,
70
69
  id: id,
@@ -1,10 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'base'
4
-
5
3
  module Coradoc
6
4
  module Html
7
5
  module Converters
6
+ autoload :Base, "#{__dir__}/base"
8
7
  # Converter for SourceCode blocks
9
8
  #
10
9
  # SourceCode models use the `lines` attribute, while Source models use `content`.
@@ -1,10 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'base'
4
-
5
3
  module Coradoc
6
4
  module Html
7
5
  module Converters
6
+ autoload :Base, "#{__dir__}/base"
8
7
  # Converter for CoreModel::InlineElement (term) elements
9
8
  #
10
9
  # Terms are used in definition lists and can have types like "acronym",
@@ -50,12 +50,11 @@ module Coradoc
50
50
  # Extract ID if present
51
51
  id = element['id']
52
52
 
53
- Coradoc::CoreModel::Block.new(
54
- delimiter_type: '[verse]',
53
+ Coradoc::CoreModel::VerseBlock.new(
55
54
  content: content,
56
55
  title: title,
57
56
  id: id,
58
- metadata: attribution ? { attribution: attribution } : {}
57
+ attribution: attribution
59
58
  )
60
59
  end
61
60
 
@@ -10,7 +10,7 @@ module Coradoc
10
10
  # Use AnnotationBlock with annotation_type: "sidebar" for aside elements
11
11
  Coradoc::CoreModel::AnnotationBlock.new(
12
12
  annotation_type: 'sidebar',
13
- delimiter_type: '****',
13
+ block_semantic_type: :sidebar,
14
14
  children: content
15
15
  )
16
16
  end
@@ -16,7 +16,7 @@ module Coradoc
16
16
  # with element_attributes to store audio-specific data
17
17
  Coradoc::CoreModel::Block.new(
18
18
  element_type: 'audio',
19
- delimiter_type: 'audio',
19
+ block_semantic_type: :audio,
20
20
  content: src,
21
21
  title: title,
22
22
  id: id,
@@ -10,11 +10,10 @@ module Coradoc
10
10
  cite = node['cite']
11
11
  content = treat_children_coradoc(node, state)
12
12
 
13
- Coradoc::CoreModel::Block.new(
14
- delimiter_type: '____',
13
+ Coradoc::CoreModel::QuoteBlock.new(
15
14
  content: content,
16
15
  id: id,
17
- metadata: cite ? { attribution: cite } : {}
16
+ attribution: cite
18
17
  )
19
18
  end
20
19
  end
@@ -9,8 +9,7 @@ module Coradoc
9
9
  id = node['id']
10
10
  contents = treat_children_coradoc(node, state)
11
11
 
12
- Coradoc::CoreModel::Block.new(
13
- delimiter_type: '--',
12
+ Coradoc::CoreModel::OpenBlock.new(
14
13
  content: contents,
15
14
  id: id
16
15
  )