Class: Prosereflect::Schema::ContentMatch
- Inherits:
-
Object
- Object
- Prosereflect::Schema::ContentMatch
- Defined in:
- lib/prosereflect/schema/content_match.rb
Overview
Represents a match state for node content expressions Parses expressions like “block+”, “inline*”, “(paragraph | heading)2,4”
Defined Under Namespace
Classes: TokenStream
Instance Attribute Summary collapse
-
#next_edges ⇒ Object
readonly
Returns the value of attribute next_edges.
-
#valid_end ⇒ Object
readonly
Returns the value of attribute valid_end.
-
#wrap_cache ⇒ Object
readonly
Returns the value of attribute wrap_cache.
Class Method Summary collapse
- .empty ⇒ Object
-
.parse(expression, node_types) ⇒ Object
Parse content expression and return ContentMatch.
Instance Method Summary collapse
-
#compatible?(other) ⇒ Boolean
Check if this content expression is compatible with another.
-
#default_type ⇒ Object
Get the default type for this match (first non-text type without required attrs).
-
#edge(n) ⇒ Object
Get edge at index n.
-
#edge_count ⇒ Object
Number of edges.
-
#fill_before(after:, to_end: false, start_index: 0) ⇒ Object
Fill in content before the given fragment Returns a Fragment if successful, nil otherwise.
-
#find_wrapping(target_node_type) ⇒ Object
Find wrapping nodes to reach the target type.
-
#initialize(valid_end:, next_edges: []) ⇒ ContentMatch
constructor
A new instance of ContentMatch.
-
#inline_content? ⇒ Boolean
Check if this match has inline content.
-
#make_fragment(types) ⇒ Object
For creating test fragments.
-
#match_fragment(fragment, start: 0, end_index: nil) ⇒ Object
Match a fragment and return the next match state.
-
#match_type(node_type) ⇒ Object
Match a node type and return the next match state.
Constructor Details
#initialize(valid_end:, next_edges: []) ⇒ ContentMatch
Returns a new instance of ContentMatch.
20 21 22 23 24 |
# File 'lib/prosereflect/schema/content_match.rb', line 20 def initialize(valid_end:, next_edges: []) @valid_end = valid_end @next_edges = next_edges @wrap_cache = [] # [[target_node_type, computed_wrapping]] end |
Instance Attribute Details
#next_edges ⇒ Object (readonly)
Returns the value of attribute next_edges.
18 19 20 |
# File 'lib/prosereflect/schema/content_match.rb', line 18 def next_edges @next_edges end |
#valid_end ⇒ Object (readonly)
Returns the value of attribute valid_end.
18 19 20 |
# File 'lib/prosereflect/schema/content_match.rb', line 18 def valid_end @valid_end end |
#wrap_cache ⇒ Object (readonly)
Returns the value of attribute wrap_cache.
18 19 20 |
# File 'lib/prosereflect/schema/content_match.rb', line 18 def wrap_cache @wrap_cache end |
Class Method Details
.empty ⇒ Object
26 27 28 |
# File 'lib/prosereflect/schema/content_match.rb', line 26 def self.empty @empty ||= new(valid_end: true, next_edges: []) end |
.parse(expression, node_types) ⇒ Object
Parse content expression and return ContentMatch
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/prosereflect/schema/content_match.rb', line 124 def self.parse(expression, node_types) return empty if expression.nil? || expression.empty? stream = TokenStream.new(expression, node_types) return empty if stream.peek.nil? expr = parse_expression(stream) unless stream.peek.nil? stream.error("Unexpected trailing text") end nfa_result = to_nfa(expr) dfa_result = to_dfa(nfa_result) check_for_dead_ends(dfa_result, stream) dfa_result end |
Instance Method Details
#compatible?(other) ⇒ Boolean
Check if this content expression is compatible with another
52 53 54 55 56 |
# File 'lib/prosereflect/schema/content_match.rb', line 52 def compatible?(other) @next_edges.any? do |i| other.next_edges.any? { |j| i.type == j.type } end end |
#default_type ⇒ Object
Get the default type for this match (first non-text type without required attrs)
41 42 43 44 45 46 47 48 49 |
# File 'lib/prosereflect/schema/content_match.rb', line 41 def default_type @next_edges.each do |edge| type = edge.type if !type.text? && !type.has_required_attrs? return type end end nil end |
#edge(n) ⇒ Object
Get edge at index n
100 101 102 103 104 105 106 107 |
# File 'lib/prosereflect/schema/content_match.rb', line 100 def edge(n) if n >= edge_count raise Prosereflect::SchemaErrors::ContentMatchError, "There's no #{n}th edge in this content match" end @next_edges[n] end |
#edge_count ⇒ Object
Number of edges
95 96 97 |
# File 'lib/prosereflect/schema/content_match.rb', line 95 def edge_count @next_edges.length end |
#fill_before(after:, to_end: false, start_index: 0) ⇒ Object
Fill in content before the given fragment Returns a Fragment if successful, nil otherwise
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/prosereflect/schema/content_match.rb', line 60 def fill_before(after:, to_end: false, start_index: 0) seen = [self] search = ->(match, types) do finished = match_fragment(after, start_index) if finished && (!to_end || finished.valid_end) return make_fragment(types) end match.next_edges.each do |edge| type = edge.type next_match = edge.next_match if !type.text? && !type.has_required_attrs? && !seen.include?(next_match) seen << next_match result = search.call(next_match, types + [type]) return result if result end end nil end search.call(self, []) end |
#find_wrapping(target_node_type) ⇒ Object
Find wrapping nodes to reach the target type
85 86 87 88 89 90 91 92 |
# File 'lib/prosereflect/schema/content_match.rb', line 85 def find_wrapping(target_node_type) cached = @wrap_cache.find { |entry| entry[0] == target_node_type } return cached[1] if cached computed = compute_wrapping(target_node_type) @wrap_cache << [target_node_type, computed] computed end |
#inline_content? ⇒ Boolean
Check if this match has inline content
36 37 38 |
# File 'lib/prosereflect/schema/content_match.rb', line 36 def inline_content? @next_edges.any? && @next_edges.first.type.is_a?(NodeType) && @next_edges.first.type.inline? end |
#make_fragment(types) ⇒ Object
For creating test fragments
587 588 589 590 591 592 593 594 |
# File 'lib/prosereflect/schema/content_match.rb', line 587 def make_fragment(types) return nil if types.empty? nodes = types.map(&:create_and_fill) return nil if nodes.any?(&:nil?) Fragment.new(nodes) end |
#match_fragment(fragment, start: 0, end_index: nil) ⇒ Object
Match a fragment and return the next match state
110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/prosereflect/schema/content_match.rb', line 110 def match_fragment(fragment, start: 0, end_index: nil) end_index ||= fragment.content.size current = self i = start while current && i < end_index child = fragment[i] current = current.match_type(child.type) i += 1 end current end |
#match_type(node_type) ⇒ Object
Match a node type and return the next match state
31 32 33 |
# File 'lib/prosereflect/schema/content_match.rb', line 31 def match_type(node_type) @next_edges.find { |edge| edge.type == node_type }&.next_match end |