Class: Markbridge::AST::Element
- Defined in:
- lib/markbridge/ast/element.rb
Overview
Base class for all AST elements that can contain children. Elements form the structural nodes of the AST tree, while Text nodes are leaves.
Direct Known Subclasses
Align, Bold, Code, Color, Details, Document, Email, Heading, Image, Italic, List, ListItem, Paragraph, Quote, Size, Spoiler, Strikethrough, Subscript, Superscript, Table, TableCell, TableRow, Underline, Url
Instance Attribute Summary collapse
-
#children ⇒ Array<Node>
readonly
The child nodes of this element.
Instance Method Summary collapse
-
#<<(child) ⇒ Element
Add a child node to this element.
-
#descendants(klass = nil) ⇒ Array<Node>
Array of descendant nodes, optionally filtered by class.
-
#each_descendant {|node| ... } ⇒ Enumerator, Element
Depth-first pre-order traversal yielding every descendant node.
-
#initialize ⇒ Element
constructor
A new instance of Element.
-
#replace_child(old_child, new_child) ⇒ Element
Replace a direct child of this Element with a different Node.
-
#replace_children(new_children) ⇒ Element
Replace this element's entire child list in one shot.
Constructor Details
#initialize ⇒ Element
Returns a new instance of Element.
17 18 19 |
# File 'lib/markbridge/ast/element.rb', line 17 def initialize @children = [] end |
Instance Attribute Details
#children ⇒ Array<Node> (readonly)
Returns the child nodes of this element.
15 16 17 |
# File 'lib/markbridge/ast/element.rb', line 15 def children @children end |
Instance Method Details
#<<(child) ⇒ Element
Add a child node to this element. Consecutive Text nodes are automatically merged for optimization.
31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/markbridge/ast/element.rb', line 31 def <<(child) unless child.is_a?(Node) actual = child.nil? ? "nil" : child.class raise TypeError, "<< on #{self.class} expected a #{Node}, got #{actual}" end if child.instance_of?(Text) && children.last.instance_of?(Text) @children.last.merge(child) else @children << child end self end |
#descendants(klass = nil) ⇒ Array<Node>
79 80 81 82 83 84 |
# File 'lib/markbridge/ast/element.rb', line 79 def descendants(klass = nil) result = each_descendant.to_a return result if klass.nil? result.select { |node| node.is_a?(klass) } end |
#each_descendant {|node| ... } ⇒ Enumerator, Element
Depth-first pre-order traversal yielding every descendant node.
Returns an Enumerator when called without a block so it
composes through Enumerable:
document.each_descendant.select { |n| n.is_a?(AST::Url) }
Iteration semantics: each Element snapshots its own children
array at the moment iteration enters it, so replacing a child
via #replace_child mid-walk is safe — descent uses the
pre-replacement reference. Adding or removing siblings on an
Element you are currently descending into is not guaranteed
to be visible to the current walk.
61 62 63 64 65 66 67 68 69 |
# File 'lib/markbridge/ast/element.rb', line 61 def each_descendant(&block) return enum_for(:each_descendant) unless block_given? @children.dup.each do |child| yield child child.each_descendant(&block) if child.is_a?(Element) end self end |
#replace_child(old_child, new_child) ⇒ Element
Replace a direct child of this Element with a different Node.
Preserves the child's index — useful for AST-mutation passes
that need to swap one Element type for another in place
(e.g. wrapping trailing paragraphs in a Details block).
96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/markbridge/ast/element.rb', line 96 def replace_child(old_child, new_child) index = @children.index(old_child) raise ArgumentError, "child not found in #{self.class}" if index.nil? unless new_child.is_a?(Node) actual = new_child.nil? ? "nil" : new_child.class raise TypeError, "replace_child on #{self.class} expected a #{Node}, got #{actual}" end @children[index] = new_child self end |
#replace_children(new_children) ⇒ Element
Replace this element's entire child list in one shot.
A plain validated setter for tree-rewriting passes (e.g. the Normalizer) that rebuild an element's children out of band and need to commit the result without re-running the per-append logic of #<<. Every entry must be a Node.
NOTE: unlike #<<, this does not merge adjacent Text nodes — the auto-merge invariant is a property of #<< only. Callers that build the array themselves own that coalescing (the Normalizer's walker merges adjacent text as it assembles the list, so it never hands a state #<< would not have produced).
125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/markbridge/ast/element.rb', line 125 def replace_children(new_children) new_children.each do |child| next if child.is_a?(Node) actual = child.nil? ? "nil" : child.class raise TypeError, "replace_children on #{self.class} expected #{Node}s, got #{actual}" end @children = new_children self end |