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
@@ -103,14 +103,15 @@ module Coradoc
103
103
  def transform_block(block)
104
104
  content = block.renderable_content
105
105
 
106
- if block.element_type == 'paragraph'
106
+ semantic = resolve_semantic_type(block)
107
+
108
+ case semantic
109
+ when :paragraph
107
110
  return Coradoc::AsciiDoc::Model::Paragraph.new(
108
111
  id: block.id,
109
112
  content: create_text_elements(content)
110
113
  )
111
- end
112
-
113
- if block.element_type == 'comment'
114
+ when :comment
114
115
  return Coradoc::AsciiDoc::Model::CommentBlock.new(
115
116
  text: safe_content_to_string(content)
116
117
  )
@@ -118,42 +119,67 @@ module Coradoc
118
119
 
119
120
  content_text = safe_content_to_string(content)
120
121
 
121
- case block.delimiter_type
122
- when 'source'
122
+ case semantic
123
+ when :source_code
123
124
  Coradoc::AsciiDoc::Model::Block::SourceCode.new(
124
125
  id: block.id,
125
126
  title: block.title,
126
127
  lines: content_text.split("\n"),
127
128
  attributes: build_attributes(block)
128
129
  )
129
- when 'quote'
130
+ when :quote
130
131
  Coradoc::AsciiDoc::Model::Block::Quote.new(
131
132
  id: block.id,
132
133
  title: block.title,
133
134
  lines: content_text.split("\n")
134
135
  )
135
- when 'example'
136
+ when :example
136
137
  Coradoc::AsciiDoc::Model::Block::Example.new(
137
138
  id: block.id,
138
139
  title: block.title,
139
140
  lines: content_text.split("\n")
140
141
  )
141
- when 'sidebar'
142
+ when :sidebar
142
143
  Coradoc::AsciiDoc::Model::Block::Side.new(
143
144
  id: block.id,
144
145
  title: block.title,
145
146
  lines: content_text.split("\n")
146
147
  )
147
- when 'literal'
148
+ when :literal
148
149
  Coradoc::AsciiDoc::Model::Block::Literal.new(
149
150
  id: block.id,
150
151
  title: block.title,
151
152
  lines: content_text.split("\n")
152
153
  )
153
- when 'paragraph'
154
- Coradoc::AsciiDoc::Model::Paragraph.new(
154
+ when :open
155
+ Coradoc::AsciiDoc::Model::Block::Open.new(
155
156
  id: block.id,
156
- content: create_text_elements(content)
157
+ title: block.title,
158
+ lines: content_text.split("\n")
159
+ )
160
+ when :pass
161
+ Coradoc::AsciiDoc::Model::Block::Pass.new(
162
+ id: block.id,
163
+ title: block.title,
164
+ lines: content_text.split("\n")
165
+ )
166
+ when :listing
167
+ Coradoc::AsciiDoc::Model::Block::Listing.new(
168
+ id: block.id,
169
+ title: block.title,
170
+ lines: content_text.split("\n")
171
+ )
172
+ when :verse
173
+ Coradoc::AsciiDoc::Model::Block::Quote.new(
174
+ id: block.id,
175
+ title: block.title,
176
+ lines: content_text.split("\n")
177
+ )
178
+ when :reviewer
179
+ Coradoc::AsciiDoc::Model::Block::ReviewerComment.new(
180
+ id: block.id,
181
+ title: block.title,
182
+ lines: content_text.split("\n")
157
183
  )
158
184
  else
159
185
  delim = block.delimiter_type.to_s
@@ -350,6 +376,51 @@ module Coradoc
350
376
 
351
377
  private
352
378
 
379
+ # Reverse mapping: semantic type → AsciiDoc delimiter generation
380
+ SEMANTIC_TO_ADOC_BLOCK = {
381
+ source_code: :source_code,
382
+ quote: :quote,
383
+ example: :example,
384
+ sidebar: :sidebar,
385
+ literal: :literal,
386
+ pass: :pass,
387
+ open: :open,
388
+ verse: :open,
389
+ paragraph: :paragraph,
390
+ comment: :comment
391
+ }.freeze
392
+
393
+ # Map raw delimiter_type string → semantic type (backward compat)
394
+ DELIMITER_CHAR_TO_SEMANTIC = {
395
+ '-' => :source_code,
396
+ '=' => :example,
397
+ '_' => :quote,
398
+ '*' => :sidebar,
399
+ '.' => :literal,
400
+ '+' => :pass
401
+ }.freeze
402
+
403
+ def resolve_semantic_type(block)
404
+ # Polymorphic dispatch: typed classes override semantic_type
405
+ semantic = block.resolve_semantic_type
406
+ return semantic if semantic
407
+
408
+ # Format-specific fallback from delimiter_type
409
+ delim = block.delimiter_type
410
+ return nil unless delim && !delim.empty?
411
+
412
+ case delim
413
+ when 'comment' then :comment
414
+ when 'paragraph' then :paragraph
415
+ when '[verse]' then :verse
416
+ when '>' then :quote
417
+ when "'''", '---', '___', '***' then :horizontal_rule
418
+ else
419
+ char = delim[0]
420
+ DELIMITER_CHAR_TO_SEMANTIC[char] || nil
421
+ end
422
+ end
423
+
353
424
  def safe_content_to_string(content)
354
425
  case content
355
426
  when String
@@ -13,7 +13,7 @@ module Coradoc
13
13
 
14
14
  class << self
15
15
  def transform(model)
16
- return model.map { |item| transform(item) } if model.is_a?(Array)
16
+ return model.map { |item| transform(item) }.compact if model.is_a?(Array)
17
17
  return model unless model.is_a?(Coradoc::AsciiDoc::Model::Base)
18
18
 
19
19
  transformer = Registry.lookup(model.class)
@@ -31,21 +31,23 @@ module Coradoc
31
31
  when Coradoc::AsciiDoc::Model::Paragraph
32
32
  transform_paragraph(model)
33
33
  when Coradoc::AsciiDoc::Model::Block::SourceCode
34
- transform_block(model, 'source')
34
+ transform_source_block(model)
35
35
  when Coradoc::AsciiDoc::Model::Block::Quote
36
- transform_block(model, 'quote')
36
+ transform_typed_block(model, Coradoc::CoreModel::QuoteBlock)
37
37
  when Coradoc::AsciiDoc::Model::Block::Example
38
- transform_block(model, 'example')
38
+ transform_typed_block(model, Coradoc::CoreModel::ExampleBlock)
39
39
  when Coradoc::AsciiDoc::Model::Block::Side
40
- transform_block(model, 'sidebar')
40
+ transform_typed_block(model, Coradoc::CoreModel::SidebarBlock)
41
41
  when Coradoc::AsciiDoc::Model::Block::Literal
42
- transform_block(model, 'literal')
42
+ transform_typed_block(model, Coradoc::CoreModel::LiteralBlock)
43
43
  when Coradoc::AsciiDoc::Model::Block::Open
44
- transform_block(model, 'open')
44
+ transform_typed_block(model, Coradoc::CoreModel::OpenBlock)
45
45
  when Coradoc::AsciiDoc::Model::Block::Pass
46
- transform_block(model, 'pass')
46
+ transform_typed_block(model, Coradoc::CoreModel::PassBlock)
47
+ when Coradoc::AsciiDoc::Model::Block::Listing
48
+ transform_typed_block(model, Coradoc::CoreModel::ListingBlock)
47
49
  when Coradoc::AsciiDoc::Model::Block::Core
48
- transform_block(model, model.delimiter)
50
+ transform_block(model, model.delimiter.to_s)
49
51
  when Coradoc::AsciiDoc::Model::Table
50
52
  transform_table(model)
51
53
  when Coradoc::AsciiDoc::Model::TableRow
@@ -96,10 +98,12 @@ module Coradoc
96
98
 
97
99
  def transform_document(doc)
98
100
  title_text = extract_title_text(doc.header&.title)
101
+ attributes = extract_document_attributes(doc)
99
102
  Coradoc::CoreModel::StructuralElement.new(
100
103
  element_type: 'document',
101
104
  id: doc.id,
102
105
  title: title_text,
106
+ attributes: attributes,
103
107
  children: transform(doc.sections || doc.contents || [])
104
108
  )
105
109
  end
@@ -123,33 +127,25 @@ module Coradoc
123
127
 
124
128
  Coradoc::CoreModel::Block.new(
125
129
  element_type: 'paragraph',
130
+ block_semantic_type: :paragraph,
126
131
  id: para.id,
127
132
  content: extract_text_content(para.content),
128
133
  children: children
129
134
  )
130
135
  end
131
136
 
132
- def transform_block(block, delimiter_type)
133
- content_lines = Array(block.lines).map do |line|
134
- case line
135
- when Coradoc::AsciiDoc::Model::Base
136
- transformed = transform(line)
137
- if transformed.is_a?(Coradoc::CoreModel::Base)
138
- extract_core_model_text(transformed)
139
- else
140
- transformed.to_s
141
- end
142
- else
143
- line.to_s
144
- end
137
+ def transform_source_block(block)
138
+ content_lines = Array(block.lines).reject do |line|
139
+ line.is_a?(Coradoc::AsciiDoc::Model::LineBreak) ||
140
+ line.is_a?(Coradoc::AsciiDoc::Model::Break::PageBreak)
141
+ end.map do |line|
142
+ extract_text_content(line)
145
143
  end.join("\n")
146
144
 
147
- language = block.lang || block.attributes&.[]('language') ||
148
- block.attributes&.positional&.first
145
+ language = extract_block_language(block)
149
146
 
150
- Coradoc::CoreModel::Block.new(
147
+ Coradoc::CoreModel::SourceBlock.new(
151
148
  element_type: 'block',
152
- delimiter_type: delimiter_type,
153
149
  id: block.id,
154
150
  title: extract_title_text(block.title),
155
151
  content: content_lines,
@@ -157,6 +153,47 @@ module Coradoc
157
153
  )
158
154
  end
159
155
 
156
+ def transform_block(block, semantic_type_or_delimiter)
157
+ content_lines = extract_block_lines(block)
158
+ semantic_type = if semantic_type_or_delimiter.is_a?(Symbol)
159
+ semantic_type_or_delimiter
160
+ else
161
+ asciidoc_delimiter_to_semantic(semantic_type_or_delimiter)
162
+ end
163
+
164
+ Coradoc::CoreModel::Block.new(
165
+ element_type: 'block',
166
+ block_semantic_type: semantic_type,
167
+ delimiter_type: semantic_type_or_delimiter.is_a?(String) ? semantic_type_or_delimiter : nil,
168
+ id: block.id,
169
+ title: extract_title_text(block.title),
170
+ content: content_lines,
171
+ language: extract_block_language(block)
172
+ )
173
+ end
174
+
175
+ def transform_typed_block(block, klass, extra_attrs = {})
176
+ content_lines = extract_block_lines(block)
177
+
178
+ klass.new(
179
+ element_type: 'block',
180
+ id: block.id,
181
+ title: extract_title_text(block.title),
182
+ content: content_lines,
183
+ language: extract_block_language(block),
184
+ **extra_attrs
185
+ )
186
+ end
187
+
188
+ def extract_block_lines(block)
189
+ Array(block.lines).reject do |line|
190
+ line.is_a?(Coradoc::AsciiDoc::Model::LineBreak) ||
191
+ line.is_a?(Coradoc::AsciiDoc::Model::Break::PageBreak)
192
+ end.map do |line|
193
+ extract_text_content(line)
194
+ end.join("\n")
195
+ end
196
+
160
197
  def transform_table(table)
161
198
  rows = Array(table.rows).map do |row|
162
199
  transform_table_row(row)
@@ -173,7 +210,10 @@ module Coradoc
173
210
  cells = Array(row.columns).map do |cell|
174
211
  transform_table_cell(cell)
175
212
  end
176
- Coradoc::CoreModel::TableRow.new(cells: cells)
213
+ Coradoc::CoreModel::TableRow.new(
214
+ cells: cells,
215
+ header: row.header
216
+ )
177
217
  end
178
218
 
179
219
  def transform_table_cell(cell)
@@ -317,6 +357,45 @@ module Coradoc
317
357
 
318
358
  private
319
359
 
360
+ # AsciiDoc delimiters are any length of the same character (4+ for most, 2+ for open).
361
+ # We map by the first character to handle all lengths correctly.
362
+ ADOC_DELIMITER_CHAR_TO_SEMANTIC = {
363
+ '-' => :source_code,
364
+ '=' => :example,
365
+ '_' => :quote,
366
+ '*' => :sidebar,
367
+ '.' => :literal,
368
+ '+' => :pass
369
+ }.freeze
370
+
371
+ def asciidoc_delimiter_to_semantic(delimiter)
372
+ return :open if delimiter && delimiter.length < 4
373
+
374
+ char = delimiter&.[](0)
375
+ ADOC_DELIMITER_CHAR_TO_SEMANTIC[char] || :open
376
+ end
377
+
378
+ def extract_document_attributes(doc)
379
+ return {} unless doc.document_attributes
380
+
381
+ doc.document_attributes.to_hash
382
+ end
383
+
384
+ def extract_block_language(block)
385
+ lang = block.lang
386
+ return lang if lang.is_a?(String) && !lang.empty?
387
+
388
+ attrs = block.attributes
389
+ return nil unless attrs.is_a?(Coradoc::AsciiDoc::Model::AttributeList)
390
+
391
+ named_lang = attrs['language']
392
+ return named_lang.to_s if named_lang
393
+
394
+ # For [source,yaml], the language is the second positional attribute
395
+ positional = attrs.positional
396
+ positional[1]&.value&.to_s if positional.length > 1
397
+ end
398
+
320
399
  def transform_inline_content(content)
321
400
  return [] if content.nil?
322
401
 
@@ -35,25 +35,30 @@ module Coradoc
35
35
  end
36
36
 
37
37
  def register_block_transformers!
38
- [
39
- [Coradoc::AsciiDoc::Model::Block::SourceCode, 'source'],
40
- [Coradoc::AsciiDoc::Model::Block::Quote, 'quote'],
41
- [Coradoc::AsciiDoc::Model::Block::Example, 'example'],
42
- [Coradoc::AsciiDoc::Model::Block::Side, 'sidebar'],
43
- [Coradoc::AsciiDoc::Model::Block::Literal, 'literal'],
44
- [Coradoc::AsciiDoc::Model::Block::Open, 'open'],
45
- [Coradoc::AsciiDoc::Model::Block::Pass, 'pass']
46
- ].each do |block_class, delimiter_type|
38
+ Registry.register_with_priority(
39
+ Coradoc::AsciiDoc::Model::Block::SourceCode,
40
+ METHOD_DISPATCH[:transform_source_block],
41
+ priority: 10
42
+ )
43
+
44
+ {
45
+ Coradoc::AsciiDoc::Model::Block::Quote => Coradoc::CoreModel::QuoteBlock,
46
+ Coradoc::AsciiDoc::Model::Block::Example => Coradoc::CoreModel::ExampleBlock,
47
+ Coradoc::AsciiDoc::Model::Block::Side => Coradoc::CoreModel::SidebarBlock,
48
+ Coradoc::AsciiDoc::Model::Block::Literal => Coradoc::CoreModel::LiteralBlock,
49
+ Coradoc::AsciiDoc::Model::Block::Open => Coradoc::CoreModel::OpenBlock,
50
+ Coradoc::AsciiDoc::Model::Block::Pass => Coradoc::CoreModel::PassBlock
51
+ }.each do |block_class, core_model_class|
47
52
  Registry.register_with_priority(
48
53
  block_class,
49
- block_wrapper(delimiter_type),
54
+ typed_block_wrapper(core_model_class),
50
55
  priority: 10
51
56
  )
52
57
  end
53
58
 
54
59
  Registry.register(
55
60
  Coradoc::AsciiDoc::Model::Block::Core,
56
- block_wrapper(nil) # uses model.delimiter at call time
61
+ block_model_wrapper
57
62
  )
58
63
 
59
64
  Registry.register(
@@ -182,7 +187,6 @@ module Coradoc
182
187
  # Passthrough types (no CoreModel equivalent)
183
188
  [
184
189
  Coradoc::AsciiDoc::Model::TextElement,
185
- Coradoc::AsciiDoc::Model::LineBreak,
186
190
  Coradoc::AsciiDoc::Model::Include,
187
191
  Coradoc::AsciiDoc::Model::Audio,
188
192
  Coradoc::AsciiDoc::Model::Video,
@@ -191,18 +195,45 @@ module Coradoc
191
195
  ].each do |klass|
192
196
  Registry.register(klass, ->(model) { model })
193
197
  end
198
+
199
+ # Filtered types (layout-only, no CoreModel representation)
200
+ [
201
+ Coradoc::AsciiDoc::Model::LineBreak,
202
+ Coradoc::AsciiDoc::Model::Break::PageBreak
203
+ ].each do |klass|
204
+ Registry.register(klass, ->(_model) { nil })
205
+ end
194
206
  end
195
207
 
208
+ METHOD_DISPATCH = {
209
+ transform_document: ->(model) { ToCoreModel.transform_document(model) },
210
+ transform_section: ->(model) { ToCoreModel.transform_section(model) },
211
+ transform_paragraph: ->(model) { ToCoreModel.transform_paragraph(model) },
212
+ transform_source_block: ->(model) { ToCoreModel.transform_source_block(model) },
213
+ transform_link: ->(model) { ToCoreModel.transform_link(model) },
214
+ transform_cross_reference: ->(model) { ToCoreModel.transform_cross_reference(model) },
215
+ transform_inline_footnote: ->(model) { ToCoreModel.transform_inline_footnote(model) },
216
+ transform_stem: ->(model) { ToCoreModel.transform_stem(model) },
217
+ transform_table: ->(model) { ToCoreModel.transform_table(model) },
218
+ transform_table_row: ->(model) { ToCoreModel.transform_table_row(model) },
219
+ transform_table_cell: ->(model) { ToCoreModel.transform_table_cell(model) },
220
+ transform_term: ->(model) { ToCoreModel.transform_term(model) },
221
+ transform_admonition: ->(model) { ToCoreModel.transform_admonition(model) },
222
+ transform_image: ->(model) { ToCoreModel.transform_image(model) },
223
+ transform_bibliography: ->(model) { ToCoreModel.transform_bibliography(model) },
224
+ transform_bibliography_entry: ->(model) { ToCoreModel.transform_bibliography_entry(model) }
225
+ }.freeze
226
+
196
227
  def method_wrapper(method_name)
197
- ->(model) { ToCoreModel.public_send(method_name, model) }
228
+ METHOD_DISPATCH.fetch(method_name)
198
229
  end
199
230
 
200
- def block_wrapper(delimiter_type)
201
- if delimiter_type
202
- ->(model) { ToCoreModel.transform_block(model, delimiter_type) }
203
- else
204
- ->(model) { ToCoreModel.transform_block(model, model.delimiter) }
205
- end
231
+ def typed_block_wrapper(klass)
232
+ ->(model) { ToCoreModel.transform_typed_block(model, klass) }
233
+ end
234
+
235
+ def block_model_wrapper
236
+ ->(model) { ToCoreModel.transform_block(model, model.delimiter.to_s) }
206
237
  end
207
238
 
208
239
  def list_wrapper(marker_type)
@@ -19,6 +19,11 @@ module Coradoc
19
19
  Model::CommentBlock.new(text: comment_text)
20
20
  end
21
21
 
22
+ # Page break
23
+ rule(page_break: simple(:page_break)) do
24
+ Model::Break::PageBreak.new
25
+ end
26
+
22
27
  # Tag
23
28
  rule(tag: subtree(:tag)) do
24
29
  Model::Tag.new(
@@ -65,7 +65,7 @@ module Coradoc
65
65
  delim_char: simple(:delim_char),
66
66
  rows: sequence(:rows)
67
67
  ) do
68
- Model::Table.new(rows: rows)
68
+ Model::Table.new(rows: Transformer.regroup_table_rows(rows))
69
69
  end
70
70
 
71
71
  # Table with rows and title
@@ -74,7 +74,7 @@ module Coradoc
74
74
  delim_char: simple(:delim_char),
75
75
  rows: sequence(:rows)
76
76
  ) do
77
- Model::Table.new(title: title.to_s, rows: rows)
77
+ Model::Table.new(title: title.to_s, rows: Transformer.regroup_table_rows(rows))
78
78
  end
79
79
 
80
80
  # Table with rows and id
@@ -83,7 +83,7 @@ module Coradoc
83
83
  delim_char: simple(:delim_char),
84
84
  rows: sequence(:rows)
85
85
  ) do
86
- Model::Table.new(id: id.to_s, rows: rows)
86
+ Model::Table.new(id: id.to_s, rows: Transformer.regroup_table_rows(rows))
87
87
  end
88
88
 
89
89
  # Table with rows, id, and attributes
@@ -93,7 +93,7 @@ module Coradoc
93
93
  delim_char: simple(:delim_char),
94
94
  rows: sequence(:rows)
95
95
  ) do
96
- Model::Table.new(id: id.to_s, rows: rows, attrs: attrs)
96
+ Model::Table.new(id: id.to_s, rows: Transformer.regroup_table_rows(rows, attrs), attrs: attrs)
97
97
  end
98
98
 
99
99
  # Table with rows, title, and attributes
@@ -103,7 +103,7 @@ module Coradoc
103
103
  delim_char: simple(:delim_char),
104
104
  rows: sequence(:rows)
105
105
  ) do
106
- Model::Table.new(title: title.to_s, rows: rows, attrs: attrs)
106
+ Model::Table.new(title: title.to_s, rows: Transformer.regroup_table_rows(rows, attrs), attrs: attrs)
107
107
  end
108
108
 
109
109
  # Table with rows and attributes only
@@ -112,7 +112,7 @@ module Coradoc
112
112
  delim_char: simple(:delim_char),
113
113
  rows: sequence(:rows)
114
114
  ) do
115
- Model::Table.new(rows: rows, attrs: attrs)
115
+ Model::Table.new(rows: Transformer.regroup_table_rows(rows, attrs), attrs: attrs)
116
116
  end
117
117
 
118
118
  # Table with rows, id, title, and attributes (full set)
@@ -123,7 +123,8 @@ module Coradoc
123
123
  delim_char: simple(:delim_char),
124
124
  rows: sequence(:rows)
125
125
  ) do
126
- Model::Table.new(id: id.to_s, title: title.to_s, rows: rows, attrs: attrs)
126
+ Model::Table.new(id: id.to_s, title: title.to_s, rows: Transformer.regroup_table_rows(rows, attrs),
127
+ attrs: attrs)
127
128
  end
128
129
 
129
130
  # Table with id and title (no attributes)
@@ -133,7 +134,7 @@ module Coradoc
133
134
  delim_char: simple(:delim_char),
134
135
  rows: sequence(:rows)
135
136
  ) do
136
- Model::Table.new(id: id.to_s, title: title.to_s, rows: rows)
137
+ Model::Table.new(id: id.to_s, title: title.to_s, rows: Transformer.regroup_table_rows(rows))
137
138
  end
138
139
 
139
140
  # Title
@@ -333,11 +333,9 @@ module Coradoc
333
333
 
334
334
  # Infer column count from cells
335
335
  # Look for patterns where rows have consistent cell counts
336
- # Prefers LARGER valid column counts (more likely to be correct)
337
336
  def self.infer_column_count(cells)
338
337
  return nil if cells.nil? || cells.empty?
339
338
 
340
- # Count column slots for each cell
341
339
  col_slots = cells.map do |cell|
342
340
  cell.is_a?(Model::TableCell) && cell.colspan ? cell.colspan : 1
343
341
  end
@@ -349,7 +347,6 @@ module Coradoc
349
347
  next false if candidate > total_cells
350
348
  next false if total_cells % candidate != 0
351
349
 
352
- # Verify that the cells distribute evenly
353
350
  slots_used = 0
354
351
  valid = true
355
352
 
@@ -366,11 +363,31 @@ module Coradoc
366
363
  valid && slots_used.zero?
367
364
  end
368
365
 
369
- # Return the largest valid column count
370
- # (more likely to represent actual table structure)
371
366
  possible_cols.max || col_slots.first || 1
372
367
  end
373
368
 
369
+ # Regroup parser-level rows into proper AsciiDoc rows.
370
+ # The parser produces one "row" per line; this flattens all cells
371
+ # and regroups by the cols attribute, then marks the first row as header.
372
+ #
373
+ # @param rows [Array<Model::TableRow>] Parser-level rows
374
+ # @param attrs [Model::AttributeList, nil] Table attributes containing cols
375
+ # @return [Array<Model::TableRow>] Properly grouped rows with header flag
376
+ def self.regroup_table_rows(rows, attrs = nil)
377
+ return rows if rows.nil? || rows.empty?
378
+
379
+ col_count = parse_cols_attribute(attrs)
380
+ all_cells = rows.flat_map do |r|
381
+ r.is_a?(Model::TableRow) ? r.columns : []
382
+ end
383
+
384
+ return rows if all_cells.empty?
385
+
386
+ grouped = group_cells_into_rows(all_cells, col_count)
387
+ grouped.first.header = true unless grouped.empty?
388
+ grouped
389
+ end
390
+
374
391
  # Transform a syntax tree using this transformer's rules
375
392
  #
376
393
  # @param syntax_tree [Hash, Array] The AST from the parser
@@ -15,9 +15,14 @@ module Coradoc
15
15
  end
16
16
  end
17
17
 
18
- # Load version and parse_error (small files, needed immediately)
18
+ # Autoload version and parse error
19
19
  require_relative 'asciidoc/version'
20
- require_relative 'asciidoc/parse_error'
20
+
21
+ module Coradoc
22
+ module AsciiDoc
23
+ autoload :ParseError, "#{__dir__}/asciidoc/parse_error"
24
+ end
25
+ end
21
26
 
22
27
  # Autoload main components (lazy loading)
23
28
  module Coradoc