coradoc-mirror 0.1.9 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0d0dddcebf58649933def45133c70a5ed3caf8f1441d1a789b9ea95d49fa8d24
4
- data.tar.gz: 6275f1315af45cb267762a26862bdcb1d2bb65e29fd4657cd411a98200b6c0f9
3
+ metadata.gz: 4d7f1f75e597c4f6a4afe4b9d290d219ee3563b425dc31583918623f8228d048
4
+ data.tar.gz: 408570af8c07418f349e96c129d07f0f21b8ccb9100f7f3aee6a9bae5727443f
5
5
  SHA512:
6
- metadata.gz: 13c8d02108886f582cf93f501a9e503361c450c224f0105e255583200323bd4d170d3303b2250510922ee4dad0aa92b45a987caf690528098f77d90b18e2672a
7
- data.tar.gz: 3eef4445a831dfce49faaa0e777ad4cd28fb54d9d7b71db640e45585ea557267f87b831031839da32077bf6886ae53f9d085e65200ac6aa2cc939c393f93df1d
6
+ metadata.gz: f2091e8e24d6906ed7b2e40ad53538334d7cabc80a4f480f667b8e343c0e2670866ae50c13550e95bf2b08aa1c79bf629387195503688a16b8ee064b7f3994e3
7
+ data.tar.gz: 7e970c3b58e0a6824e1b9eb967cbb5bf8383ac88f9833334874bbf131771a1a571ab9bda4d38bd70a99f8ab8e3292e2c6a1f0cc6f082187c3e76683b37e90236
@@ -139,6 +139,24 @@ module Coradoc
139
139
  Node::Footnotes.new(content: entries)
140
140
  end
141
141
 
142
+ # Public entrypoint for handlers that need to dispatch a nested
143
+ # CoreModel element through the same registry path as a top-level
144
+ # walk (e.g., `+`-continuation blocks attached to a dlist dd must
145
+ # be transformed by their own registered handler rather than
146
+ # inlined as text). Returns an Array of Mirror nodes (possibly
147
+ # empty). Single source of truth for "transform one element" so
148
+ # handlers don't bypass source-line propagation.
149
+ def transform_element(element)
150
+ result = @registry.handle(element, context: self)
151
+ return [] unless result
152
+
153
+ value, = result
154
+ return [] unless value
155
+
156
+ propagate_source_line(value, element)
157
+ Array(value)
158
+ end
159
+
142
160
  private
143
161
 
144
162
  def element_has_text_content?(element)
@@ -19,19 +19,45 @@ module Coradoc
19
19
  class << self
20
20
  private
21
21
 
22
+ # One `<dt>` per term + one `<dd>` per item. Multi-term `<dt>`'s
23
+ # (AsciiDoc `term1::\nterm2::\ndef`) emit two `<dt>` elements
24
+ # sharing one `<dd>`. Single-term items — the common case —
25
+ # emit one `<dt>` + one `<dd>`.
22
26
  def definition_entry(item, context)
23
- term_node = build_term(item, context)
27
+ term_nodes = build_terms(item, context)
24
28
  desc_node = build_description(item, context)
25
- return nil unless term_node || desc_node
29
+ return nil if term_nodes.empty? && desc_node.nil?
26
30
 
27
- [term_node, desc_node].compact
31
+ term_nodes + [desc_node].compact
28
32
  end
29
33
 
30
- def build_term(item, context)
31
- term_text = item.term
32
- return nil unless term_text && !term_text.to_s.empty?
34
+ # Emit one DefinitionTerm node per term in `item.terms`.
35
+ # Falls back to `[term]` for items populated via paths that
36
+ # only set the singular `term` accessor (backward compat).
37
+ def build_terms(item, context)
38
+ term_list = terms_for(item)
39
+ return [] if term_list.empty?
33
40
 
34
- term_children = item.term_children if item.is_a?(CoreModel::DefinitionItem)
41
+ term_list.map { |term| build_term_node(term, item, context) }
42
+ end
43
+
44
+ # Source of truth for "the terms on this `<dt>`": the `terms`
45
+ # collection when populated, else `[term]` for legacy callers.
46
+ def terms_for(item)
47
+ collection = item.terms if item.is_a?(CoreModel::DefinitionItem)
48
+ return Array(collection) unless collection.nil? || collection.empty?
49
+
50
+ primary = item.term
51
+ primary.to_s.empty? ? [] : [primary.to_s]
52
+ end
53
+
54
+ # Build a single DefinitionTerm node for one term string,
55
+ # carrying inline children when the source had them. Children
56
+ # map 1:1 to the FIRST term only (multi-term `<dt>`'s with
57
+ # inline markup on later terms is rare; the parser doesn't
58
+ # capture per-term children today).
59
+ def build_term_node(term_text, item, context)
60
+ term_children = (item.term_children if (term_text == terms_for(item).first) && item.is_a?(CoreModel::DefinitionItem))
35
61
  if term_children && !term_children.empty?
36
62
  inline_nodes = term_children.flat_map do |child|
37
63
  Handlers::Inline.process_child(child, context)
@@ -44,9 +70,43 @@ module Coradoc
44
70
 
45
71
  def build_description(item, context)
46
72
  desc_nodes = description_nodes(item, context)
47
- return nil if desc_nodes.empty?
73
+ attached_nodes = attached_block_nodes(item, context)
74
+ nested_nodes = nested_dl_nodes(item, context)
75
+ all_nodes = desc_nodes.concat(attached_nodes).concat(nested_nodes)
76
+ return nil if all_nodes.empty?
77
+
78
+ Node::DefinitionDescription.new(content: all_nodes)
79
+ end
48
80
 
49
- Node::DefinitionDescription.new(content: desc_nodes)
81
+ # `+`-continuation blocks attached to this dd in AsciiDoc source
82
+ # become children of the same DefinitionDescription node so they
83
+ # render inside the same <dd> element. Dispatch each through
84
+ # `context.transform_element` — same registry path as a top-level
85
+ # walk, so every registered block handler (paragraph, admonition,
86
+ # source, etc.) applies.
87
+ def attached_block_nodes(item, context)
88
+ return [] unless item.is_a?(CoreModel::DefinitionItem)
89
+ return [] if item.attached_children.empty?
90
+
91
+ item.attached_children.flat_map do |block|
92
+ context.transform_element(block)
93
+ end
94
+ end
95
+
96
+ # Multi-level dlist: AsciiDoc `::`/`:::`/`::::` delimiters
97
+ # express nesting depth. The parser builds the tree
98
+ # (CoreModel::DefinitionItem#nested); the mirror handler must
99
+ # recurse so the nested <dl> renders INSIDE the parent <dd>.
100
+ # Single source of truth: this method recurses through
101
+ # `DefinitionList.call`, so nested items use the same dispatch
102
+ # path as top-level items (DRY).
103
+ def nested_dl_nodes(item, context)
104
+ return [] unless item.is_a?(CoreModel::DefinitionItem)
105
+
106
+ nested = item.nested
107
+ return [] unless nested && nested.items.any?
108
+
109
+ [DefinitionList.call(nested, context: context)].compact
50
110
  end
51
111
 
52
112
  # Emit one text node per source — never both. Rich
@@ -56,7 +116,9 @@ module Coradoc
56
116
  # instances populated by paths that don't build inline children.
57
117
  def description_nodes(item, context)
58
118
  unless item.is_a?(CoreModel::DefinitionItem)
59
- return Array(item.definitions).map { |defn| context.text_node(defn.to_s) }
119
+ return Array(item.definitions).map do |defn|
120
+ context.text_node(defn.to_s)
121
+ end
60
122
  end
61
123
 
62
124
  children = item.definition_children
@@ -123,19 +123,24 @@ module Coradoc
123
123
  end
124
124
 
125
125
  def build_simple_mark(element, context, mark_class)
126
- # When the mark carries parsed children (Bug 16B nested
127
- # inline marks like `**bold \`code\`**`), walk the children
128
- # and apply this mark to each text leaf. Inner mark elements
129
- # get the outer mark added to their marks list, producing
130
- # ProseMirror's flat text-node-with-marks shape.
131
- if element.children&.any?
132
- return flatten_marked_children(element.children, [mark_class.new], context)
126
+ # InlineElement always has children after the dual-shape fix
127
+ # (Bug 16A follow-up). Walk the children and apply this mark
128
+ # to each text leaf. Inner mark elements get the outer mark
129
+ # added to their marks list, producing ProseMirror's flat
130
+ # text-node-with-marks shape.
131
+ #
132
+ # Fallback: programmatically constructed elements (e.g. in
133
+ # specs or other format gems) may not have children. Fall
134
+ # back to the content string in that case.
135
+ children = element.children
136
+ if children.nil? || children.empty?
137
+ text = extract_inline_text(element)
138
+ return nil if text.empty?
139
+
140
+ return context.text_node(text, marks: [mark_class.new])
133
141
  end
134
142
 
135
- text = extract_inline_text(element)
136
- return nil if text.empty?
137
-
138
- context.text_node(text, marks: [mark_class.new])
143
+ flatten_marked_children(children, [mark_class.new], context)
139
144
  end
140
145
 
141
146
  # Walk a list of children, attaching the active set of marks
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Coradoc
4
4
  module Mirror
5
- VERSION = '0.1.9'
5
+ VERSION = '0.1.10'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: coradoc-mirror
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.9
4
+ version: 0.1.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.