coradoc-adoc 2.0.0

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 (217) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/lib/coradoc/asciidoc/model/admonition.rb +37 -0
  4. data/lib/coradoc/asciidoc/model/anchorable.rb +64 -0
  5. data/lib/coradoc/asciidoc/model/attached.rb +26 -0
  6. data/lib/coradoc/asciidoc/model/attribute.rb +22 -0
  7. data/lib/coradoc/asciidoc/model/attribute_list/matchers.rb +45 -0
  8. data/lib/coradoc/asciidoc/model/attribute_list.rb +230 -0
  9. data/lib/coradoc/asciidoc/model/attribute_list_attribute.rb +11 -0
  10. data/lib/coradoc/asciidoc/model/audio.rb +44 -0
  11. data/lib/coradoc/asciidoc/model/author.rb +36 -0
  12. data/lib/coradoc/asciidoc/model/base.rb +141 -0
  13. data/lib/coradoc/asciidoc/model/bibliography.rb +37 -0
  14. data/lib/coradoc/asciidoc/model/bibliography_entry.rb +38 -0
  15. data/lib/coradoc/asciidoc/model/block/core.rb +139 -0
  16. data/lib/coradoc/asciidoc/model/block/example.rb +14 -0
  17. data/lib/coradoc/asciidoc/model/block/listing.rb +14 -0
  18. data/lib/coradoc/asciidoc/model/block/literal.rb +14 -0
  19. data/lib/coradoc/asciidoc/model/block/open.rb +14 -0
  20. data/lib/coradoc/asciidoc/model/block/pass.rb +14 -0
  21. data/lib/coradoc/asciidoc/model/block/quote.rb +14 -0
  22. data/lib/coradoc/asciidoc/model/block/reviewer_comment.rb +14 -0
  23. data/lib/coradoc/asciidoc/model/block/side.rb +14 -0
  24. data/lib/coradoc/asciidoc/model/block/source_code.rb +14 -0
  25. data/lib/coradoc/asciidoc/model/block.rb +21 -0
  26. data/lib/coradoc/asciidoc/model/break.rb +33 -0
  27. data/lib/coradoc/asciidoc/model/comment_block.rb +33 -0
  28. data/lib/coradoc/asciidoc/model/comment_line.rb +30 -0
  29. data/lib/coradoc/asciidoc/model/content_list.rb +334 -0
  30. data/lib/coradoc/asciidoc/model/document.rb +197 -0
  31. data/lib/coradoc/asciidoc/model/document_attributes.rb +43 -0
  32. data/lib/coradoc/asciidoc/model/glossaries.rb +11 -0
  33. data/lib/coradoc/asciidoc/model/header.rb +57 -0
  34. data/lib/coradoc/asciidoc/model/highlight.rb +11 -0
  35. data/lib/coradoc/asciidoc/model/image/block_image/attribute_list.rb +23 -0
  36. data/lib/coradoc/asciidoc/model/image/block_image.rb +25 -0
  37. data/lib/coradoc/asciidoc/model/image/core/attribute_list.rb +43 -0
  38. data/lib/coradoc/asciidoc/model/image/core.rb +72 -0
  39. data/lib/coradoc/asciidoc/model/image/inline_image.rb +17 -0
  40. data/lib/coradoc/asciidoc/model/image.rb +14 -0
  41. data/lib/coradoc/asciidoc/model/include.rb +66 -0
  42. data/lib/coradoc/asciidoc/model/inline/anchor.rb +41 -0
  43. data/lib/coradoc/asciidoc/model/inline/attribute_reference.rb +25 -0
  44. data/lib/coradoc/asciidoc/model/inline/base.rb +15 -0
  45. data/lib/coradoc/asciidoc/model/inline/bold.rb +38 -0
  46. data/lib/coradoc/asciidoc/model/inline/cross_reference.rb +29 -0
  47. data/lib/coradoc/asciidoc/model/inline/cross_reference_arg.rb +15 -0
  48. data/lib/coradoc/asciidoc/model/inline/footnote.rb +34 -0
  49. data/lib/coradoc/asciidoc/model/inline/hard_line_break.rb +24 -0
  50. data/lib/coradoc/asciidoc/model/inline/highlight.rb +36 -0
  51. data/lib/coradoc/asciidoc/model/inline/italic.rb +38 -0
  52. data/lib/coradoc/asciidoc/model/inline/link.rb +46 -0
  53. data/lib/coradoc/asciidoc/model/inline/monospace.rb +39 -0
  54. data/lib/coradoc/asciidoc/model/inline/quotation.rb +25 -0
  55. data/lib/coradoc/asciidoc/model/inline/small.rb +25 -0
  56. data/lib/coradoc/asciidoc/model/inline/span.rb +38 -0
  57. data/lib/coradoc/asciidoc/model/inline/stem.rb +24 -0
  58. data/lib/coradoc/asciidoc/model/inline/strikethrough.rb +39 -0
  59. data/lib/coradoc/asciidoc/model/inline/subscript.rb +33 -0
  60. data/lib/coradoc/asciidoc/model/inline/superscript.rb +33 -0
  61. data/lib/coradoc/asciidoc/model/inline/underline.rb +25 -0
  62. data/lib/coradoc/asciidoc/model/inline.rb +31 -0
  63. data/lib/coradoc/asciidoc/model/line_break.rb +11 -0
  64. data/lib/coradoc/asciidoc/model/list/core.rb +61 -0
  65. data/lib/coradoc/asciidoc/model/list/definition.rb +27 -0
  66. data/lib/coradoc/asciidoc/model/list/definition_item.rb +43 -0
  67. data/lib/coradoc/asciidoc/model/list/item.rb +72 -0
  68. data/lib/coradoc/asciidoc/model/list/nestable.rb +14 -0
  69. data/lib/coradoc/asciidoc/model/list/ordered.rb +34 -0
  70. data/lib/coradoc/asciidoc/model/list/unordered.rb +34 -0
  71. data/lib/coradoc/asciidoc/model/list.rb +29 -0
  72. data/lib/coradoc/asciidoc/model/named_attribute.rb +12 -0
  73. data/lib/coradoc/asciidoc/model/paragraph.rb +59 -0
  74. data/lib/coradoc/asciidoc/model/rejected_positional_attribute.rb +12 -0
  75. data/lib/coradoc/asciidoc/model/resolvable.rb +71 -0
  76. data/lib/coradoc/asciidoc/model/resolver.rb +430 -0
  77. data/lib/coradoc/asciidoc/model/reviewer_note.rb +54 -0
  78. data/lib/coradoc/asciidoc/model/revision.rb +47 -0
  79. data/lib/coradoc/asciidoc/model/section.rb +109 -0
  80. data/lib/coradoc/asciidoc/model/serialization/asciidoc_adapter.rb +28 -0
  81. data/lib/coradoc/asciidoc/model/serialization/asciidoc_mapping.rb +42 -0
  82. data/lib/coradoc/asciidoc/model/serialization/asciidoc_mapping_rule.rb +41 -0
  83. data/lib/coradoc/asciidoc/model/serialization/asciidoc_transform.rb +211 -0
  84. data/lib/coradoc/asciidoc/model/serialization/errors.rb +57 -0
  85. data/lib/coradoc/asciidoc/model/serialization.rb +39 -0
  86. data/lib/coradoc/asciidoc/model/spacing.rb +282 -0
  87. data/lib/coradoc/asciidoc/model/table.rb +44 -0
  88. data/lib/coradoc/asciidoc/model/table_cell.rb +122 -0
  89. data/lib/coradoc/asciidoc/model/table_row.rb +26 -0
  90. data/lib/coradoc/asciidoc/model/tag.rb +36 -0
  91. data/lib/coradoc/asciidoc/model/term.rb +48 -0
  92. data/lib/coradoc/asciidoc/model/text_element.rb +66 -0
  93. data/lib/coradoc/asciidoc/model/title.rb +85 -0
  94. data/lib/coradoc/asciidoc/model/video/attribute_list.rb +43 -0
  95. data/lib/coradoc/asciidoc/model/video.rb +49 -0
  96. data/lib/coradoc/asciidoc/model.rb +75 -0
  97. data/lib/coradoc/asciidoc/parse_error.rb +161 -0
  98. data/lib/coradoc/asciidoc/parser/admonition.rb +26 -0
  99. data/lib/coradoc/asciidoc/parser/attribute_list.rb +110 -0
  100. data/lib/coradoc/asciidoc/parser/base.rb +159 -0
  101. data/lib/coradoc/asciidoc/parser/bibliography.rb +31 -0
  102. data/lib/coradoc/asciidoc/parser/block.rb +186 -0
  103. data/lib/coradoc/asciidoc/parser/block_assembler.rb +183 -0
  104. data/lib/coradoc/asciidoc/parser/cache.rb +155 -0
  105. data/lib/coradoc/asciidoc/parser/citation.rb +32 -0
  106. data/lib/coradoc/asciidoc/parser/content.rb +76 -0
  107. data/lib/coradoc/asciidoc/parser/document_attributes.rb +27 -0
  108. data/lib/coradoc/asciidoc/parser/fix_files.rb +76 -0
  109. data/lib/coradoc/asciidoc/parser/header.rb +31 -0
  110. data/lib/coradoc/asciidoc/parser/inline.rb +199 -0
  111. data/lib/coradoc/asciidoc/parser/list.rb +130 -0
  112. data/lib/coradoc/asciidoc/parser/metadata_detector.rb +164 -0
  113. data/lib/coradoc/asciidoc/parser/paragraph.rb +64 -0
  114. data/lib/coradoc/asciidoc/parser/section.rb +62 -0
  115. data/lib/coradoc/asciidoc/parser/stem.rb +19 -0
  116. data/lib/coradoc/asciidoc/parser/table.rb +166 -0
  117. data/lib/coradoc/asciidoc/parser/term.rb +70 -0
  118. data/lib/coradoc/asciidoc/parser/text.rb +156 -0
  119. data/lib/coradoc/asciidoc/parser.rb +10 -0
  120. data/lib/coradoc/asciidoc/serializer/adoc_serializer.rb +86 -0
  121. data/lib/coradoc/asciidoc/serializer/element_registry.rb +95 -0
  122. data/lib/coradoc/asciidoc/serializer/fallback_serializer.rb +21 -0
  123. data/lib/coradoc/asciidoc/serializer/formatter.rb +144 -0
  124. data/lib/coradoc/asciidoc/serializer/registrations.rb +108 -0
  125. data/lib/coradoc/asciidoc/serializer/serialization_context.rb +238 -0
  126. data/lib/coradoc/asciidoc/serializer/serializers/admonition.rb +19 -0
  127. data/lib/coradoc/asciidoc/serializer/serializers/attribute.rb +23 -0
  128. data/lib/coradoc/asciidoc/serializer/serializers/attribute_list.rb +40 -0
  129. data/lib/coradoc/asciidoc/serializer/serializers/attribute_list_attribute.rb +18 -0
  130. data/lib/coradoc/asciidoc/serializer/serializers/audio.rb +33 -0
  131. data/lib/coradoc/asciidoc/serializer/serializers/author.rb +20 -0
  132. data/lib/coradoc/asciidoc/serializer/serializers/base.rb +152 -0
  133. data/lib/coradoc/asciidoc/serializer/serializers/bibliography.rb +35 -0
  134. data/lib/coradoc/asciidoc/serializer/serializers/bibliography_entry.rb +24 -0
  135. data/lib/coradoc/asciidoc/serializer/serializers/block/core.rb +70 -0
  136. data/lib/coradoc/asciidoc/serializer/serializers/block/example.rb +17 -0
  137. data/lib/coradoc/asciidoc/serializer/serializers/block/listing.rb +22 -0
  138. data/lib/coradoc/asciidoc/serializer/serializers/block/literal.rb +17 -0
  139. data/lib/coradoc/asciidoc/serializer/serializers/block/open.rb +22 -0
  140. data/lib/coradoc/asciidoc/serializer/serializers/block/pass.rb +17 -0
  141. data/lib/coradoc/asciidoc/serializer/serializers/block/quote.rb +17 -0
  142. data/lib/coradoc/asciidoc/serializer/serializers/block/reviewer_comment.rb +17 -0
  143. data/lib/coradoc/asciidoc/serializer/serializers/block/side.rb +22 -0
  144. data/lib/coradoc/asciidoc/serializer/serializers/block/source_code.rb +22 -0
  145. data/lib/coradoc/asciidoc/serializer/serializers/block.rb +23 -0
  146. data/lib/coradoc/asciidoc/serializer/serializers/break.rb +18 -0
  147. data/lib/coradoc/asciidoc/serializer/serializers/comment_block.rb +22 -0
  148. data/lib/coradoc/asciidoc/serializer/serializers/comment_line.rb +22 -0
  149. data/lib/coradoc/asciidoc/serializer/serializers/document.rb +65 -0
  150. data/lib/coradoc/asciidoc/serializer/serializers/document_attributes.rb +21 -0
  151. data/lib/coradoc/asciidoc/serializer/serializers/header.rb +24 -0
  152. data/lib/coradoc/asciidoc/serializer/serializers/highlight.rb +23 -0
  153. data/lib/coradoc/asciidoc/serializer/serializers/image/core.rb +30 -0
  154. data/lib/coradoc/asciidoc/serializer/serializers/image.rb +14 -0
  155. data/lib/coradoc/asciidoc/serializer/serializers/include.rb +19 -0
  156. data/lib/coradoc/asciidoc/serializer/serializers/inline/anchor.rb +20 -0
  157. data/lib/coradoc/asciidoc/serializer/serializers/inline/attribute_reference.rb +20 -0
  158. data/lib/coradoc/asciidoc/serializer/serializers/inline/bold.rb +26 -0
  159. data/lib/coradoc/asciidoc/serializer/serializers/inline/cross_reference.rb +30 -0
  160. data/lib/coradoc/asciidoc/serializer/serializers/inline/cross_reference_arg.rb +20 -0
  161. data/lib/coradoc/asciidoc/serializer/serializers/inline/footnote.rb +24 -0
  162. data/lib/coradoc/asciidoc/serializer/serializers/inline/hard_line_break.rb +20 -0
  163. data/lib/coradoc/asciidoc/serializer/serializers/inline/highlight.rb +26 -0
  164. data/lib/coradoc/asciidoc/serializer/serializers/inline/italic.rb +26 -0
  165. data/lib/coradoc/asciidoc/serializer/serializers/inline/link.rb +38 -0
  166. data/lib/coradoc/asciidoc/serializer/serializers/inline/monospace.rb +26 -0
  167. data/lib/coradoc/asciidoc/serializer/serializers/inline/quotation.rb +21 -0
  168. data/lib/coradoc/asciidoc/serializer/serializers/inline/small.rb +20 -0
  169. data/lib/coradoc/asciidoc/serializer/serializers/inline/span.rb +35 -0
  170. data/lib/coradoc/asciidoc/serializer/serializers/inline/stem.rb +23 -0
  171. data/lib/coradoc/asciidoc/serializer/serializers/inline/strikethrough.rb +29 -0
  172. data/lib/coradoc/asciidoc/serializer/serializers/inline/subscript.rb +29 -0
  173. data/lib/coradoc/asciidoc/serializer/serializers/inline/superscript.rb +26 -0
  174. data/lib/coradoc/asciidoc/serializer/serializers/inline/underline.rb +20 -0
  175. data/lib/coradoc/asciidoc/serializer/serializers/inline.rb +32 -0
  176. data/lib/coradoc/asciidoc/serializer/serializers/line_break.rb +18 -0
  177. data/lib/coradoc/asciidoc/serializer/serializers/list/core.rb +47 -0
  178. data/lib/coradoc/asciidoc/serializer/serializers/list/definition.rb +35 -0
  179. data/lib/coradoc/asciidoc/serializer/serializers/list/definition_item.rb +38 -0
  180. data/lib/coradoc/asciidoc/serializer/serializers/list/item.rb +120 -0
  181. data/lib/coradoc/asciidoc/serializer/serializers/list/ordered.rb +24 -0
  182. data/lib/coradoc/asciidoc/serializer/serializers/list/unordered.rb +29 -0
  183. data/lib/coradoc/asciidoc/serializer/serializers/list.rb +19 -0
  184. data/lib/coradoc/asciidoc/serializer/serializers/named_attribute.rb +22 -0
  185. data/lib/coradoc/asciidoc/serializer/serializers/paragraph.rb +65 -0
  186. data/lib/coradoc/asciidoc/serializer/serializers/reviewer_note.rb +28 -0
  187. data/lib/coradoc/asciidoc/serializer/serializers/revision.rb +26 -0
  188. data/lib/coradoc/asciidoc/serializer/serializers/section.rb +37 -0
  189. data/lib/coradoc/asciidoc/serializer/serializers/table.rb +24 -0
  190. data/lib/coradoc/asciidoc/serializer/serializers/table_cell.rb +75 -0
  191. data/lib/coradoc/asciidoc/serializer/serializers/table_row.rb +24 -0
  192. data/lib/coradoc/asciidoc/serializer/serializers/tag.rb +19 -0
  193. data/lib/coradoc/asciidoc/serializer/serializers/term.rb +20 -0
  194. data/lib/coradoc/asciidoc/serializer/serializers/text_element.rb +23 -0
  195. data/lib/coradoc/asciidoc/serializer/serializers/title.rb +55 -0
  196. data/lib/coradoc/asciidoc/serializer/serializers/video.rb +33 -0
  197. data/lib/coradoc/asciidoc/serializer/spacing_strategy.rb +70 -0
  198. data/lib/coradoc/asciidoc/serializer.rb +75 -0
  199. data/lib/coradoc/asciidoc/transform/from_core_model.rb +502 -0
  200. data/lib/coradoc/asciidoc/transform/from_core_model_registrations.rb +126 -0
  201. data/lib/coradoc/asciidoc/transform/registry.rb +146 -0
  202. data/lib/coradoc/asciidoc/transform/to_core_model.rb +564 -0
  203. data/lib/coradoc/asciidoc/transform/to_core_model_registrations.rb +257 -0
  204. data/lib/coradoc/asciidoc/transform.rb +13 -0
  205. data/lib/coradoc/asciidoc/transformer/block_rules.rb +101 -0
  206. data/lib/coradoc/asciidoc/transformer/header_rules.rb +91 -0
  207. data/lib/coradoc/asciidoc/transformer/inline_rules.rb +179 -0
  208. data/lib/coradoc/asciidoc/transformer/list_rules.rb +131 -0
  209. data/lib/coradoc/asciidoc/transformer/misc_rules.rb +196 -0
  210. data/lib/coradoc/asciidoc/transformer/structural_rules.rb +216 -0
  211. data/lib/coradoc/asciidoc/transformer/text_rules.rb +107 -0
  212. data/lib/coradoc/asciidoc/transformer.rb +406 -0
  213. data/lib/coradoc/asciidoc/version.rb +7 -0
  214. data/lib/coradoc/asciidoc.rb +148 -0
  215. data/lib/coradoc/util/asciidoc.rb +71 -0
  216. data/lib/coradoc/util.rb +8 -0
  217. metadata +343 -0
@@ -0,0 +1,238 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ module AsciiDoc
5
+ module Serializer
6
+ # Context object passed through serialization pipeline
7
+ #
8
+ # Provides a type-safe, extensible way to pass serialization state
9
+ # through the document tree, replacing the opaque options hash.
10
+ #
11
+ # @example Create a context
12
+ # context = SerializationContext.new(
13
+ # format: :adoc,
14
+ # last_element: false,
15
+ # depth: 0
16
+ # )
17
+ #
18
+ # @example Create context for a child
19
+ # child_context = context.for_child(0, 3)
20
+ #
21
+ # @example Add custom option
22
+ # context_with_option = context.with_option(:preserve_whitespace, true)
23
+ #
24
+ class SerializationContext
25
+ # Output format (:adoc, :html, :xml, etc.)
26
+ #
27
+ # @return [Symbol] the output format
28
+ attr_reader :format
29
+
30
+ # Whether this is the last element in its container
31
+ # Used to control trailing newlines and spacing
32
+ #
33
+ # @return [Boolean] true if last element
34
+ attr_reader :last_element
35
+
36
+ # Nesting depth in the document tree
37
+ # Can be used for indentation or formatting decisions
38
+ #
39
+ # @return [Integer] current depth
40
+ attr_reader :depth
41
+
42
+ # Parent context (if any)
43
+ # Allows traversing up the context chain
44
+ #
45
+ # @return [SerializationContext, nil] parent context
46
+ attr_reader :parent
47
+
48
+ # Additional options hash for extension
49
+ # Provides backward compatibility and extensibility
50
+ #
51
+ # @return [Hash] additional options
52
+ attr_reader :options
53
+
54
+ # Create a new serialization context
55
+ #
56
+ # @param format [Symbol] Output format
57
+ # @param last_element [Boolean] Whether this is the last element
58
+ # @param depth [Integer] Nesting depth
59
+ # @param parent [SerializationContext, nil] Parent context
60
+ # @param options [Hash] Additional options
61
+ def initialize(format: :adoc, last_element: false, depth: 0,
62
+ parent: nil, options: {})
63
+ @format = format
64
+ @last_element = last_element
65
+ @depth = depth
66
+ @parent = parent
67
+ @options = options.freeze
68
+ freeze
69
+ end
70
+
71
+ # Create a context for a child element
72
+ #
73
+ # Automatically adjusts depth and calculates last_element
74
+ # based on the child's position.
75
+ #
76
+ # @param child_index [Integer] Index of this child (0-based)
77
+ # @param total_children [Integer] Total number of children
78
+ # @return [SerializationContext] Context for the child
79
+ #
80
+ # @example
81
+ # children.each_with_index do |child, i|
82
+ # serialize(child, context.for_child(i, children.length))
83
+ # end
84
+ def for_child(child_index, total_children)
85
+ SerializationContext.new(
86
+ format: format,
87
+ last_element: child_index == total_children - 1,
88
+ depth: depth + 1,
89
+ parent: self,
90
+ options: options
91
+ )
92
+ end
93
+
94
+ # Create a context with an additional option
95
+ #
96
+ # Returns a new context with the option added to the options hash.
97
+ # Does not mutate the original context.
98
+ #
99
+ # @param key [Symbol] Option key
100
+ # @param value [Object] Option value
101
+ # @return [SerializationContext] New context with the option
102
+ #
103
+ # @example Add a custom option
104
+ # context.with_option(:preserve_whitespace, true)
105
+ def with_option(key, value)
106
+ SerializationContext.new(
107
+ format: format,
108
+ last_element: last_element,
109
+ depth: depth,
110
+ parent: parent,
111
+ options: options.merge(key => value)
112
+ )
113
+ end
114
+
115
+ # Create a context for a different format
116
+ #
117
+ # @param new_format [Symbol] New format
118
+ # @return [SerializationContext] Context with new format
119
+ #
120
+ # @example Switch to HTML format
121
+ # html_context = context.with_format(:html)
122
+ def with_format(new_format)
123
+ SerializationContext.new(
124
+ format: new_format,
125
+ last_element: last_element,
126
+ depth: depth,
127
+ parent: parent,
128
+ options: options
129
+ )
130
+ end
131
+
132
+ # Create a context with last_element set to true
133
+ #
134
+ # @return [SerializationContext] Context as last element
135
+ def as_last_element
136
+ return self if last_element
137
+
138
+ SerializationContext.new(
139
+ format: format,
140
+ last_element: true,
141
+ depth: depth,
142
+ parent: parent,
143
+ options: options
144
+ )
145
+ end
146
+
147
+ # Check if we're inside a table cell
148
+ #
149
+ # Traverses up the parent chain to detect table context.
150
+ #
151
+ # @return [Boolean] true if in a table cell
152
+ #
153
+ # @example
154
+ # if context.in_table_cell?
155
+ # # Don't add block-level spacing
156
+ # end
157
+ def in_table_cell?
158
+ return false unless parent
159
+
160
+ # Check if any ancestor is a table
161
+ current = parent
162
+ while current
163
+ return true if current.options[:in_table] == true
164
+
165
+ current = current.parent
166
+ end
167
+ false
168
+ end
169
+
170
+ # Get an option value
171
+ #
172
+ # @param key [Symbol] Option key
173
+ # @param default [Object] Default value if not found
174
+ # @return [Object] Option value or default
175
+ #
176
+ # @example
177
+ # preserve = context.option(:preserve_whitespace, false)
178
+ def option(key, default = nil)
179
+ options.fetch(key, default)
180
+ end
181
+
182
+ # Check if an option is present
183
+ #
184
+ # @param key [Symbol] Option key
185
+ # @return [Boolean] true if option exists
186
+ def has_option?(key)
187
+ options.key?(key)
188
+ end
189
+
190
+ # Check if this is the root context (no parent)
191
+ #
192
+ # @return [Boolean] true if root
193
+ def root?
194
+ parent.nil?
195
+ end
196
+
197
+ # Create a root context for a new serialization
198
+ #
199
+ # @param format [Symbol] Output format
200
+ # @param options [Hash] Additional options
201
+ # @return [SerializationContext] Root context
202
+ #
203
+ # @example
204
+ # context = SerializationContext.root(format: :adoc)
205
+ def self.root(format: :adoc, options: {})
206
+ new(format: format, last_element: false, depth: 0, parent: nil, options: options)
207
+ end
208
+
209
+ # Backward compatibility: convert options hash to context
210
+ #
211
+ # @param options_or_context [Hash, SerializationContext] Options or context
212
+ # @return [SerializationContext] Serialization context
213
+ #
214
+ # @example Convert options hash
215
+ # context = SerializationContext.from_options(last_element: true)
216
+ def self.from_options(options_or_context)
217
+ return options_or_context if options_or_context.is_a?(SerializationContext)
218
+
219
+ new(
220
+ format: options_or_context.fetch(:format, :adoc),
221
+ last_element: options_or_context.fetch(:last_element, false),
222
+ depth: options_or_context.fetch(:depth, 0),
223
+ parent: nil,
224
+ options: options_or_context
225
+ )
226
+ end
227
+
228
+ # String representation for debugging
229
+ #
230
+ # @return [String] Debug string
231
+ def inspect
232
+ "#<#{self.class.name} format=#{format} last_element=#{last_element} depth=#{depth}>"
233
+ end
234
+ alias to_s inspect
235
+ end
236
+ end
237
+ end
238
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ module AsciiDoc
5
+ module Serializer
6
+ module Serializers
7
+ class Admonition < Base
8
+ def to_adoc(model, _options = {})
9
+ _content = serialize_children(model.content).to_s.strip
10
+ "#{model.type.to_s.upcase}: #{_content}#{model.line_break}"
11
+ end
12
+ end
13
+ end
14
+
15
+ # Self-register this serializer
16
+ ElementRegistry.register(Coradoc::AsciiDoc::Model::Admonition, Serializers::Admonition)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ module AsciiDoc
5
+ module Serializer
6
+ module Serializers
7
+ class Attribute < Base
8
+ # Pre-compiled regex for performance
9
+ QUOTE_REGEX = /^'|'$/
10
+
11
+ def to_adoc(model, _options = {})
12
+ _value = model.value.to_s.gsub(QUOTE_REGEX, '') # Remove surrounding single quotes
13
+ v = _value.empty? ? '' : " #{_value}"
14
+ ":#{model.key}:#{v}#{model.line_break}"
15
+ end
16
+ end
17
+ end
18
+
19
+ # Self-register this serializer (in Adoc module scope)
20
+ ElementRegistry.register(Coradoc::AsciiDoc::Model::Attribute, Serializers::Attribute)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ module AsciiDoc
5
+ module Serializer
6
+ module Serializers
7
+ class AttributeList < Base
8
+ def to_adoc(model, options_or_context = {})
9
+ context = normalize_context(options_or_context)
10
+ show_empty = context.option(:show_empty, true)
11
+
12
+ valid_positional = model.positional.reject.with_index do |_p, i|
13
+ model.rejected_positional.any? { |r| r.position == i }
14
+ end
15
+
16
+ valid_named = model.named.reject do |n|
17
+ model.rejected_named.any? { |r| r.name == n.name }
18
+ end
19
+
20
+ adoc = [valid_positional,
21
+ valid_named].flatten.map { |a| serialize_child(a, context) }.join(',')
22
+
23
+ if adoc.empty? && show_empty
24
+ '[]'
25
+ elsif adoc.empty?
26
+ ''
27
+ else
28
+ "[#{adoc}]"
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ # Self-register this serializer
35
+ ElementRegistry.register(Coradoc::AsciiDoc::Model::AttributeList, Serializers::AttributeList)
36
+ # Also register for Image::Core::AttributeList which inherits from AttributeList
37
+ ElementRegistry.register(Coradoc::AsciiDoc::Model::Image::Core::AttributeList, Serializers::AttributeList)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ module AsciiDoc
5
+ module Serializer
6
+ module Serializers
7
+ class AttributeListAttribute < Base
8
+ def to_adoc(model, _options = {})
9
+ [nil, ''].include?(model.value) ? '""' : model.value
10
+ end
11
+ end
12
+ end
13
+
14
+ # Self-register this serializer
15
+ ElementRegistry.register(Coradoc::AsciiDoc::Model::AttributeListAttribute, Serializers::AttributeListAttribute)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ module AsciiDoc
5
+ module Serializer
6
+ module Serializers
7
+ class Audio < Base
8
+ def to_adoc(model, _options = {})
9
+ @model = model
10
+ _anchor = gen_anchor
11
+ _title = model.title.nil? || model.title.empty? ? '' : ".#{model.title}\n"
12
+ _attrs = model.attributes.empty? ? '[]' : model.attributes.to_adoc
13
+ [_anchor, _title, 'audio::', model.src, _attrs].join + model.line_break
14
+ end
15
+
16
+ private
17
+
18
+ attr_reader :model
19
+
20
+ def gen_anchor
21
+ return '' unless @model.anchor
22
+
23
+ anchor_str = @model.anchor.to_adoc
24
+ anchor_str.empty? ? '' : "#{anchor_str}\n"
25
+ end
26
+ end
27
+ end
28
+
29
+ # Self-register this serializer
30
+ ElementRegistry.register(Coradoc::AsciiDoc::Model::Audio, Serializers::Audio)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ module AsciiDoc
5
+ module Serializer
6
+ module Serializers
7
+ class Author < Base
8
+ def to_adoc(model, _options = {})
9
+ adoc = [model.first_name, model.middle_name, model.last_name].compact.join(' ')
10
+ adoc << " <#{model.email}>\n" if model.email
11
+ adoc
12
+ end
13
+ end
14
+ end
15
+
16
+ # Self-register this serializer
17
+ ElementRegistry.register(Coradoc::AsciiDoc::Model::Author, Serializers::Author)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'coradoc/asciidoc/serializer/serialization_context'
4
+
5
+ module Coradoc
6
+ module AsciiDoc
7
+ module Serializer
8
+ module Serializers
9
+ # Base serializer class for converting Coradoc models to AsciiDoc format.
10
+ # Provides common serialization infrastructure and helpers.
11
+ # Each model type should have its own serializer that inherits from this.
12
+ class Base
13
+ # Serialize a Coradoc model to AsciiDoc string
14
+ # @param model [Coradoc::AsciiDoc::Model::Base] The model to serialize
15
+ # @param options_or_context [Hash, SerializationContext] Options or context
16
+ # @return [String] AsciiDoc representation
17
+ def serialize(model, options_or_context = {})
18
+ return '' if model.nil?
19
+
20
+ context = SerializationContext.from_options(options_or_context)
21
+ to_adoc(model, context)
22
+ end
23
+
24
+ # Abstract method to be implemented by subclasses
25
+ # @param model [Coradoc::AsciiDoc::Model::Base] The model to convert
26
+ # @param options_or_context [Hash, SerializationContext] Options or context
27
+ # @return [String] AsciiDoc representation
28
+ def to_adoc(_model, options_or_context = {})
29
+ SerializationContext.from_options(options_or_context)
30
+ raise NotImplementedError,
31
+ "#{self.class.name} must implement #to_adoc"
32
+ end
33
+
34
+ protected
35
+
36
+ # Normalize options_or_context to SerializationContext
37
+ # @param options_or_context [Hash, SerializationContext] Options or context
38
+ # @return [SerializationContext] Serialization context
39
+ def normalize_context(options_or_context)
40
+ SerializationContext.from_options(options_or_context)
41
+ end
42
+
43
+ # Serialize child elements of a model
44
+ # @param children [Array, Object] Child elements to serialize
45
+ # @param options_or_context [Hash, SerializationContext] Options or context
46
+ # @return [String] Serialized children
47
+ def serialize_children(children, options_or_context = {})
48
+ context = normalize_context(options_or_context)
49
+
50
+ case children
51
+ when Array
52
+ return '' if children.empty?
53
+
54
+ # Mark the last child as the last element for proper spacing
55
+ children.each_with_index.map do |child, index|
56
+ child_context = context.for_child(index, children.length)
57
+ serialize_child(child, child_context)
58
+ end.join
59
+ when nil
60
+ ''
61
+ else
62
+ serialize_child(children, context)
63
+ end
64
+ end
65
+
66
+ # Serialize a single child element
67
+ # @param child [Object] Child element to serialize
68
+ # @param options_or_context [Hash, SerializationContext] Options or context
69
+ # @return [String] Serialized child
70
+ def serialize_child(child, options_or_context = {})
71
+ return '' if child.nil?
72
+
73
+ context = normalize_context(options_or_context)
74
+
75
+ case child
76
+ when String
77
+ child
78
+ when Coradoc::AsciiDoc::Model::Base
79
+ # Use AdocSerializer for all Coradoc models
80
+ AdocSerializer.serialize(child, context)
81
+ when Lutaml::Model::Serializable
82
+ # Handle Lutaml::Model::Serializable objects (like CrossReference, Term)
83
+ # These are model objects that need proper serialization
84
+ AdocSerializer.serialize(child, context)
85
+ when Proc
86
+ child.call
87
+ else
88
+ # This is a programming error - we received an unexpected type
89
+ raise ArgumentError,
90
+ "Cannot serialize child of type #{child.class.name}. " \
91
+ 'Expected String, Coradoc::AsciiDoc::Model::Base, ' \
92
+ 'Lutaml::Model::Serializable, or Proc. ' \
93
+ "Got: #{child.inspect[0..100]}"
94
+ end
95
+ end
96
+
97
+ # Preserve model structure during serialization (recursive)
98
+ # This method handles nested inline models by recursively serializing
99
+ # them, unlike serialize_children which flattens to strings.
100
+ # @param content [Array, String, Object] Content to serialize
101
+ # @return [String] Serialized content with structure preserved
102
+ def serialize_content(content)
103
+ case content
104
+ when Array
105
+ content.map { |elem| serialize_content(elem) }.join
106
+ when String
107
+ content
108
+ when nil
109
+ ''
110
+ else
111
+ # Try AdocSerializer for Lutaml models
112
+ if content.is_a?(Lutaml::Model::Serializable) || content.is_a?(Coradoc::AsciiDoc::Model::Base)
113
+ AdocSerializer.serialize(content)
114
+ else
115
+ raise ArgumentError,
116
+ "Cannot serialize content of type #{content.class.name}. " \
117
+ 'Expected String, nil, Array, or serializable object. ' \
118
+ "Got: #{content.inspect[0..100]}"
119
+ end
120
+ end
121
+ end
122
+
123
+ # Helper to add spacing between elements
124
+ # @param elements [Array] Elements to space
125
+ # @param options [Hash] Spacing options
126
+ # @return [String] Elements with spacing
127
+ def add_spacing(elements, options = {})
128
+ SpacingStrategy.apply(elements, options)
129
+ end
130
+
131
+ # Helper to format attribute list
132
+ # @param attrs [Coradoc::AsciiDoc::Model::AttributeList] Attribute list
133
+ # @return [String] Formatted attribute list
134
+ def format_attribute_list(attrs)
135
+ return '' if attrs.nil?
136
+
137
+ Formatter.attribute_list(attrs)
138
+ end
139
+
140
+ # Helper to format block attributes
141
+ # @param model [Coradoc::AsciiDoc::Model::Base] Model with attributes
142
+ # @return [String] Formatted block attributes
143
+ def format_block_attributes(model)
144
+ return '' unless model.is_a?(Lutaml::Model::Serializable) && model.attributes
145
+
146
+ Formatter.block_attributes(model.attributes)
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ module AsciiDoc
5
+ module Serializer
6
+ module Serializers
7
+ class Bibliography < Base
8
+ def to_adoc(model, _options = {})
9
+ @model = model
10
+ adoc = "#{gen_anchor}\n"
11
+ adoc << '[bibliography]'
12
+ adoc << "== #{model.title}\n\n"
13
+ model.entries&.each do |entry|
14
+ adoc << "#{entry.to_adoc}\n"
15
+ end
16
+ adoc
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :model
22
+
23
+ def gen_anchor
24
+ return '' unless @model.anchor
25
+
26
+ @model.anchor.to_adoc
27
+ end
28
+ end
29
+ end
30
+
31
+ # Self-register this serializer
32
+ ElementRegistry.register(Coradoc::AsciiDoc::Model::Bibliography, Serializers::Bibliography)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ module AsciiDoc
5
+ module Serializer
6
+ module Serializers
7
+ class BibliographyEntry < Base
8
+ def to_adoc(model, _options = {})
9
+ text = serialize_children(model.ref_text) if model.ref_text
10
+ adoc = "* [[[#{model.anchor_name}"
11
+ adoc << ",#{model.document_id}" if model.document_id
12
+ adoc << ']]]'
13
+ adoc << text.to_s if model.ref_text
14
+ adoc << (model.line_break || "\n")
15
+ adoc
16
+ end
17
+ end
18
+ end
19
+
20
+ # Self-register this serializer
21
+ ElementRegistry.register(Coradoc::AsciiDoc::Model::BibliographyEntry, Serializers::BibliographyEntry)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ module AsciiDoc
5
+ module Serializer
6
+ module Serializers
7
+ module Block
8
+ class Core < Base
9
+ def to_adoc(model, _options = {})
10
+ @model = model
11
+ "\n\n#{gen_anchor}#{gen_title}#{gen_attributes}#{gen_delimiter}\n" <<
12
+ gen_lines << "\n#{gen_delimiter}\n\n"
13
+ end
14
+
15
+ private
16
+
17
+ attr_reader :model
18
+
19
+ def gen_title
20
+ t = serialize_children(model.title)
21
+ return '' if t.nil? || t.empty?
22
+
23
+ ".#{t}\n"
24
+ end
25
+
26
+ def gen_attributes
27
+ return '' if model.attributes.nil?
28
+
29
+ attrs = model.attributes.to_adoc(show_empty: false)
30
+ return "#{attrs}\n" unless attrs.empty?
31
+
32
+ ''
33
+ end
34
+
35
+ def gen_delimiter
36
+ return '' if model.delimiter_char.nil? || model.delimiter_len.nil?
37
+
38
+ model.delimiter_char.to_s * model.delimiter_len
39
+ end
40
+
41
+ def gen_lines
42
+ # Handle both String and model object lines
43
+ result = model.lines.map do |line|
44
+ serialized = serialize_child(line)
45
+ # Add newline if line is a plain string without one
46
+ serialized.is_a?(String) && !serialized.end_with?("\n") ? "#{serialized}\n" : serialized
47
+ end.join
48
+ # Remove trailing newline to avoid extra blank line before delimiter
49
+ result.chomp
50
+ end
51
+
52
+ def gen_anchor
53
+ return '' unless @model.anchor
54
+
55
+ anchor_str = if @model.anchor.is_a?(Coradoc::AsciiDoc::Model::Base)
56
+ @model.anchor.to_adoc
57
+ else
58
+ @model.anchor.to_s
59
+ end
60
+ "#{anchor_str}\n"
61
+ end
62
+ end
63
+ end
64
+
65
+ # Self-register this serializer
66
+ ElementRegistry.register(Coradoc::AsciiDoc::Model::Block::Core, Block::Core)
67
+ end
68
+ end
69
+ end
70
+ end