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.
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 |