Class: Prosereflect::Fragment
- Inherits:
-
Object
- Object
- Prosereflect::Fragment
- Defined in:
- lib/prosereflect/fragment.rb
Overview
Fragment represents a sequence of nodes. Used for document content, slice content, etc.
Instance Attribute Summary collapse
-
#content ⇒ Object
readonly
Returns the value of attribute content.
Class Method Summary collapse
-
.empty ⇒ Object
Create empty fragment.
-
.from(content) ⇒ Object
Create from content.
Instance Method Summary collapse
-
#[](index) ⇒ Object
Access by index.
-
#append(other) ⇒ Object
Append another fragment to this one.
-
#cut(from = 0, to = nil) ⇒ Object
Cut this fragment to a range.
- #cut_nodes(from, to) ⇒ Object
-
#descendants(block, node_start = 0) ⇒ Object
Iterate over all descendant nodes.
- #dispatch_node_callback(node, pos, node_end, from, to, callback, node_start) ⇒ Object
-
#each(&block) ⇒ Object
Iterate.
-
#empty? ⇒ Boolean
Check if fragment is empty.
-
#eq?(other) ⇒ Boolean
(also: #==)
Check equality.
-
#find_diff_end(other) ⇒ Object
Find last position where two fragments differ.
-
#find_diff_start(other) ⇒ Object
Find first position where two fragments differ.
- #full_node_callback(node, _pos, _node_end, _from, _to, callback, node_start) ⇒ Object
-
#hash ⇒ Object
Hash for use in sets/hashes.
- #in_range_before_from?(_pos, node_end, from) ⇒ Boolean
-
#initialize(content = []) ⇒ Fragment
constructor
A new instance of Fragment.
- #inspect ⇒ Object
-
#length ⇒ Object
(also: #count)
Number of items.
- #node_fully_in_range?(pos, node_end, from, to) ⇒ Boolean
- #node_overlaps_from?(pos, node_end, from) ⇒ Boolean
-
#nodes_between(from, to, callback = nil, node_start = 0, &blk) ⇒ Object
Iterate over all nodes between positions.
- #overlaps_range?(pos, node_end, from, to) ⇒ Boolean
- #partial_node_callback(node, pos, _node_end, from, to, callback, node_start) ⇒ Object
- #recurse_into_node(node, start_pos, end_pos, callback, node_start) ⇒ Object
-
#replace_child(index, replacement) ⇒ Object
Replace child at index.
-
#size ⇒ Object
Total size of all nodes in this fragment.
-
#text_between(_from, _to, separator = "", _block_separator = "\n") ⇒ Object
Extract text content between positions.
- #text_node_callback(node, pos, from, node_start, callback) ⇒ Object
-
#to_a ⇒ Object
Convert to array.
- #to_s ⇒ Object
Constructor Details
#initialize(content = []) ⇒ Fragment
Returns a new instance of Fragment.
9 10 11 12 13 14 15 16 17 |
# File 'lib/prosereflect/fragment.rb', line 9 def initialize(content = []) @content = if content.is_a?(Array) content elsif content.respond_to?(:to_a) content.to_a else [content] end end |
Instance Attribute Details
#content ⇒ Object (readonly)
Returns the value of attribute content.
7 8 9 |
# File 'lib/prosereflect/fragment.rb', line 7 def content @content end |
Class Method Details
.empty ⇒ Object
Create empty fragment
228 229 230 |
# File 'lib/prosereflect/fragment.rb', line 228 def self.empty @empty ||= new([]) end |
.from(content) ⇒ Object
Create from content
233 234 235 236 237 238 239 |
# File 'lib/prosereflect/fragment.rb', line 233 def self.from(content) case content when Fragment then content when Array then new(content.flatten) else new([content]) end end |
Instance Method Details
#[](index) ⇒ Object
Access by index
206 207 208 |
# File 'lib/prosereflect/fragment.rb', line 206 def [](index) @content[index] end |
#append(other) ⇒ Object
Append another fragment to this one
30 31 32 33 34 35 36 |
# File 'lib/prosereflect/fragment.rb', line 30 def append(other) if other.is_a?(Fragment) Fragment.new(@content + other.content) else Fragment.new(@content + [other]) end end |
#cut(from = 0, to = nil) ⇒ Object
Cut this fragment to a range
39 40 41 42 43 44 45 |
# File 'lib/prosereflect/fragment.rb', line 39 def cut(from = 0, to = nil) to ||= size return Fragment.new([]) if from >= to cut_nodes(from, to) end |
#cut_nodes(from, to) ⇒ Object
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/prosereflect/fragment.rb', line 47 def cut_nodes(from, to) result = [] pos = 0 @content.each do |node| node_end = pos + node.node_size result << node if in_range_before_from?(pos, node_end, from) result << node if overlaps_range?(pos, node_end, from, to) pos = node_end break if pos >= to end Fragment.new(result) end |
#descendants(block, node_start = 0) ⇒ Object
Iterate over all descendant nodes
134 135 136 |
# File 'lib/prosereflect/fragment.rb', line 134 def descendants(block, node_start = 0) nodes_between(0, size, block, node_start) end |
#dispatch_node_callback(node, pos, node_end, from, to, callback, node_start) ⇒ Object
96 97 98 99 100 101 102 103 104 |
# File 'lib/prosereflect/fragment.rb', line 96 def dispatch_node_callback(node, pos, node_end, from, to, callback, node_start) if node.text? text_node_callback(node, pos, from, node_start, callback) elsif node_fully_in_range?(pos, node_end, from, to) full_node_callback(node, pos, node_end, from, to, callback, node_start) elsif node_overlaps_from?(pos, node_end, from) partial_node_callback(node, pos, node_end, from, to, callback, node_start) end end |
#each(&block) ⇒ Object
Iterate
211 212 213 |
# File 'lib/prosereflect/fragment.rb', line 211 def each(&block) @content.each(&block) end |
#empty? ⇒ Boolean
Check if fragment is empty
25 26 27 |
# File 'lib/prosereflect/fragment.rb', line 25 def empty? @content.empty? end |
#eq?(other) ⇒ Boolean Also known as: ==
Check equality
191 192 193 194 195 196 |
# File 'lib/prosereflect/fragment.rb', line 191 def eq?(other) return false unless other.is_a?(Fragment) @content.length == other.content.length && @content.zip(other.content).all? { |a, b| a.to_h == b.to_h } end |
#find_diff_end(other) ⇒ Object
Find last position where two fragments differ
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/prosereflect/fragment.rb', line 168 def find_diff_end(other) my_nodes = @content.reverse other_nodes = other.content.reverse i = 0 end_pos = size while i < my_nodes.length && i < other_nodes.length my_node = my_nodes[i] other_node = other_nodes[i] unless my_node == other_node return end_pos end end_pos -= my_node.node_size i += 1 end nil end |
#find_diff_start(other) ⇒ Object
Find first position where two fragments differ
152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/prosereflect/fragment.rb', line 152 def find_diff_start(other) min_length = [@content.length, other.content.length].min pos = 0 min_length.times do |i| return pos if @content[i] != other.content[i] pos += @content[i].node_size end return nil if @content.length == other.content.length pos end |
#full_node_callback(node, _pos, _node_end, _from, _to, callback, node_start) ⇒ Object
114 115 116 117 |
# File 'lib/prosereflect/fragment.rb', line 114 def full_node_callback(node, _pos, _node_end, _from, _to, callback, node_start) callback.call(node, node_start) recurse_into_node(node, 0, node.content.size, callback, node_start) end |
#hash ⇒ Object
Hash for use in sets/hashes
201 202 203 |
# File 'lib/prosereflect/fragment.rb', line 201 def hash @content.map(&:to_h).hash end |
#in_range_before_from?(_pos, node_end, from) ⇒ Boolean
64 65 66 |
# File 'lib/prosereflect/fragment.rb', line 64 def in_range_before_from?(_pos, node_end, from) node_end <= from end |
#inspect ⇒ Object
245 246 247 |
# File 'lib/prosereflect/fragment.rb', line 245 def inspect to_s end |
#length ⇒ Object Also known as: count
Number of items
216 217 218 |
# File 'lib/prosereflect/fragment.rb', line 216 def length @content.length end |
#node_fully_in_range?(pos, node_end, from, to) ⇒ Boolean
110 111 112 |
# File 'lib/prosereflect/fragment.rb', line 110 def node_fully_in_range?(pos, node_end, from, to) pos >= from && node_end <= to end |
#node_overlaps_from?(pos, node_end, from) ⇒ Boolean
123 124 125 |
# File 'lib/prosereflect/fragment.rb', line 123 def node_overlaps_from?(pos, node_end, from) pos < from && node_end > from end |
#nodes_between(from, to, callback = nil, node_start = 0, &blk) ⇒ Object
Iterate over all nodes between positions
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/prosereflect/fragment.rb', line 80 def nodes_between(from, to, callback = nil, node_start = 0, &blk) cb = callback || blk return unless cb && to > from pos = 0 @content.each do |node| node_end = pos + node.node_size next unless node_end > from dispatch_node_callback(node, pos, node_end, from, to, cb, node_start) pos = node_end break if pos >= to end end |
#overlaps_range?(pos, node_end, from, to) ⇒ Boolean
68 69 70 |
# File 'lib/prosereflect/fragment.rb', line 68 def overlaps_range?(pos, node_end, from, to) (pos >= from && node_end <= to) || (pos < from && node_end > from) end |
#partial_node_callback(node, pos, _node_end, from, to, callback, node_start) ⇒ Object
119 120 121 |
# File 'lib/prosereflect/fragment.rb', line 119 def partial_node_callback(node, pos, _node_end, from, to, callback, node_start) recurse_into_node(node, from - pos, [to - pos, node.content.size].min, callback, node_start) end |
#recurse_into_node(node, start_pos, end_pos, callback, node_start) ⇒ Object
127 128 129 130 131 |
# File 'lib/prosereflect/fragment.rb', line 127 def recurse_into_node(node, start_pos, end_pos, callback, node_start) return unless node.respond_to?(:nodes_between) node.nodes_between(start_pos, end_pos, callback, node_start) end |
#replace_child(index, replacement) ⇒ Object
Replace child at index
73 74 75 76 77 |
# File 'lib/prosereflect/fragment.rb', line 73 def replace_child(index, replacement) new_content = @content.dup new_content[index] = replacement Fragment.new(new_content) end |
#size ⇒ Object
Total size of all nodes in this fragment
20 21 22 |
# File 'lib/prosereflect/fragment.rb', line 20 def size @content.sum { |n| n.respond_to?(:node_size) ? n.node_size : n.text_content.length + 1 } end |
#text_between(_from, _to, separator = "", _block_separator = "\n") ⇒ Object
Extract text content between positions
139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/prosereflect/fragment.rb', line 139 def text_between(_from, _to, separator = "", _block_separator = "\n") result = [] @content.each do |node| if node.respond_to?(:text) result << node.text elsif node.respond_to?(:text_content) result << node.text_content end end result.join(separator) end |
#text_node_callback(node, pos, from, node_start, callback) ⇒ Object
106 107 108 |
# File 'lib/prosereflect/fragment.rb', line 106 def text_node_callback(node, pos, from, node_start, callback) callback.call(node, node_start + (from - pos).clamp(0, node.node_size - 1)) end |
#to_a ⇒ Object
Convert to array
223 224 225 |
# File 'lib/prosereflect/fragment.rb', line 223 def to_a @content.dup end |
#to_s ⇒ Object
241 242 243 |
# File 'lib/prosereflect/fragment.rb', line 241 def to_s "<Fragment #{@content.length} nodes>" end |