Class: MDOM::Node

Inherits:
Object
  • Object
show all
Defined in:
lib/mdom/node.rb

Overview

Base class for every node in the Markdown tree.

A node carries a type (a Symbol), an ordered list of children, and optional attributes (a Hash). The parent of a node is maintained automatically whenever a child is appended or removed.

Direct Known Subclasses

BlockNode, InlineNode

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(type, attributes: {}, children: []) ⇒ Node

-- construction -------------------------------------------------------



15
16
17
18
19
20
21
# File 'lib/mdom/node.rb', line 15

def initialize(type, attributes: {}, children: [])
  @type = type
  @attributes = attributes
  @children = []
  @parent = nil
  children.each { |child| append(child) }
end

Instance Attribute Details

#attributesObject (readonly)

Returns the value of attribute attributes.



10
11
12
# File 'lib/mdom/node.rb', line 10

def attributes
  @attributes
end

#childrenObject (readonly)

Returns the value of attribute children.



10
11
12
# File 'lib/mdom/node.rb', line 10

def children
  @children
end

#parentObject

Returns the value of attribute parent.



11
12
13
# File 'lib/mdom/node.rb', line 11

def parent
  @parent
end

#typeObject (readonly)

Returns the value of attribute type.



10
11
12
# File 'lib/mdom/node.rb', line 10

def type
  @type
end

Instance Method Details

#[](*types) ⇒ Object

Query by one or more types (Symbol or array of Symbols). Returns an array of matching nodes (including self if it matches) in document order.



129
130
131
132
# File 'lib/mdom/node.rb', line 129

def [](*types)
  types = types.flatten
  find_all.select { |n| types.include?(n.type) }
end

#ancestorsObject

The chain of ancestors, closest first. Returns [] for a root node.



74
75
76
77
78
79
80
81
82
# File 'lib/mdom/node.rb', line 74

def ancestors
  list = []
  node = @parent
  while node
    list << node
    node = node.parent
  end
  list
end

#append(child) ⇒ Object Also known as: <<

Append child to the end of this node's children. Reparents child away from any previous parent. Returns self so it can be chained.



27
28
29
30
31
32
33
34
# File 'lib/mdom/node.rb', line 27

def append(child)
  return self if child.nil?

  child.remove if child.parent
  child.parent = self
  @children << child
  self
end

#children?Boolean



46
47
48
# File 'lib/mdom/node.rb', line 46

def children?
  !@children.empty?
end

#depthObject

Depth in the tree: 0 at the root, 1 for its direct children, etc.



67
68
69
70
71
# File 'lib/mdom/node.rb', line 67

def depth
  return 0 if @parent.nil?

  @parent.depth + 1
end

#each_child(&block) ⇒ Object Also known as: each

Yield each child in order. Returns an Enumerator when no block is given.



38
39
40
41
42
43
# File 'lib/mdom/node.rb', line 38

def each_child(&block)
  return enum_for(:each_child) unless block

  @children.each(&block)
  self
end

#empty?Boolean



194
195
196
# File 'lib/mdom/node.rb', line 194

def empty?
  @children.empty?
end

#find(type = nil, &block) ⇒ Object

The first matching descendant (or self); type may be a Symbol or a callable, or a block may be supplied.



112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/mdom/node.rb', line 112

def find(type = nil, &block)
  predicate =
    if block
      block
    elsif type.is_a?(Symbol)
      ->(n) { n.type == type }
    elsif type.respond_to?(:call)
      type
    else
      nil
    end
  walk { |node| return node if predicate && predicate.call(node) }
  nil
end

#find_all(type = nil) ⇒ Object

Yield each descendant (including self) whose type matches type, or all nodes when no type is given. With a block given, also yields when the block returns truthy.



99
100
101
102
103
104
105
106
107
108
# File 'lib/mdom/node.rb', line 99

def find_all(type = nil)
  results = []
  walk do |node|
    next unless type.nil? || node.type == type
    next if block_given? && !yield(node)

    results << node
  end
  results
end

#insert_after(node) ⇒ Object

Insert node as the sibling immediately after this node.



149
150
151
152
153
154
155
156
157
158
# File 'lib/mdom/node.rb', line 149

def insert_after(node)
  parent = @parent
  raise "cannot insert after a root node" if parent.nil?

  node.remove if node.parent
  idx = parent.children.index(self)
  parent.children.insert(idx + 1, node)
  node.parent = parent
  self
end

#insert_before(node) ⇒ Object

Insert node as the sibling immediately before this node.



137
138
139
140
141
142
143
144
145
146
# File 'lib/mdom/node.rb', line 137

def insert_before(node)
  parent = @parent
  raise "cannot insert before a root node" if parent.nil?

  node.remove if node.parent
  idx = parent.children.index(self)
  parent.children.insert(idx, node)
  node.parent = parent
  self
end

#inspectObject

A human-readable tree rendering of this node and its descendants.



205
206
207
# File 'lib/mdom/node.rb', line 205

def inspect
  pretty_print
end

#plain_textObject

Concatenate the literal string content of this subtree. Leaves that carry text expose it via to_s (Text, Code); container nodes recurse.



181
182
183
184
185
186
# File 'lib/mdom/node.rb', line 181

def plain_text
  return "\n" if @type == :softbreak || @type == :hardbreak
  return to_s.to_s if @children.empty?

  @children.map(&:plain_text).join
end

#pretty_print(level = 0) ⇒ Object

Render this node plus its subtree as an indented, printable tree.



210
211
212
213
214
# File 'lib/mdom/node.rb', line 210

def pretty_print(level = 0)
  indent = "  " * level
  children = @children.empty? ? "" : "\n" + @children.map { |c| c.pretty_print(level + 1) }.join("\n")
  "#{indent}#<#{short_class}#{detail}>#{children}"
end

#removeObject

Remove this node from its current parent, if any. Returns self.



51
52
53
54
55
# File 'lib/mdom/node.rb', line 51

def remove
  @parent&.children&.delete(self)
  @parent = nil
  self
end

#replace(node) ⇒ Object

Replace this node in its parent with node. Returns node.



161
162
163
164
165
166
167
168
169
170
171
# File 'lib/mdom/node.rb', line 161

def replace(node)
  parent = @parent
  raise "cannot replace a root node" if parent.nil?

  node.remove if node.parent
  idx = parent.children.index(self)
  parent.children[idx] = node
  node.parent = parent
  @parent = nil
  node
end

#rootObject

The topmost ancestor (a node whose parent is nil).



60
61
62
63
64
# File 'lib/mdom/node.rb', line 60

def root
  node = self
  node = node.parent while node.parent
  node
end

#text?Boolean

-- misc ---------------------------------------------------------------



175
176
177
# File 'lib/mdom/node.rb', line 175

def text?
  @type == :text
end

#to_markdownObject Also known as: markdown

Serialize this node (and its subtree) back to Markdown.



189
190
191
# File 'lib/mdom/node.rb', line 189

def to_markdown
  MDOM::Serializer.serialize(self)
end

#to_sObject

A compact, single-line summary of this node (its own type/attributes only, without recursing into children).



200
201
202
# File 'lib/mdom/node.rb', line 200

def to_s
  "#<#{short_class}#{detail}>"
end

#walk(&block) ⇒ Object

Depth-first (preorder) traversal that yields this node then its descendants, depth-first. Returns an Enumerator when no block is given.



88
89
90
91
92
93
94
# File 'lib/mdom/node.rb', line 88

def walk(&block)
  return enum_for(:walk) unless block

  block.call(self)
  @children.each { |child| child.walk(&block) }
  self
end