ruby_tree_sitter 1.6.0-aarch64-linux-gnu
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
 - data/LICENSE +22 -0
 - data/README.md +213 -0
 - data/ext/tree_sitter/encoding.c +29 -0
 - data/ext/tree_sitter/extconf.rb +149 -0
 - data/ext/tree_sitter/input.c +127 -0
 - data/ext/tree_sitter/input_edit.c +42 -0
 - data/ext/tree_sitter/language.c +219 -0
 - data/ext/tree_sitter/logger.c +228 -0
 - data/ext/tree_sitter/macros.h +163 -0
 - data/ext/tree_sitter/node.c +623 -0
 - data/ext/tree_sitter/parser.c +398 -0
 - data/ext/tree_sitter/point.c +26 -0
 - data/ext/tree_sitter/quantifier.c +43 -0
 - data/ext/tree_sitter/query.c +289 -0
 - data/ext/tree_sitter/query_capture.c +28 -0
 - data/ext/tree_sitter/query_cursor.c +231 -0
 - data/ext/tree_sitter/query_error.c +41 -0
 - data/ext/tree_sitter/query_match.c +44 -0
 - data/ext/tree_sitter/query_predicate_step.c +83 -0
 - data/ext/tree_sitter/range.c +35 -0
 - data/ext/tree_sitter/repo.rb +128 -0
 - data/ext/tree_sitter/symbol_type.c +46 -0
 - data/ext/tree_sitter/tree.c +234 -0
 - data/ext/tree_sitter/tree_cursor.c +269 -0
 - data/ext/tree_sitter/tree_sitter.c +44 -0
 - data/ext/tree_sitter/tree_sitter.h +107 -0
 - data/lib/tree_sitter/3.0/tree_sitter.so +0 -0
 - data/lib/tree_sitter/3.1/tree_sitter.so +0 -0
 - data/lib/tree_sitter/3.2/tree_sitter.so +0 -0
 - data/lib/tree_sitter/3.3/tree_sitter.so +0 -0
 - data/lib/tree_sitter/helpers.rb +23 -0
 - data/lib/tree_sitter/mixins/language.rb +167 -0
 - data/lib/tree_sitter/node.rb +167 -0
 - data/lib/tree_sitter/query.rb +191 -0
 - data/lib/tree_sitter/query_captures.rb +30 -0
 - data/lib/tree_sitter/query_cursor.rb +27 -0
 - data/lib/tree_sitter/query_match.rb +100 -0
 - data/lib/tree_sitter/query_matches.rb +39 -0
 - data/lib/tree_sitter/query_predicate.rb +14 -0
 - data/lib/tree_sitter/text_predicate_capture.rb +37 -0
 - data/lib/tree_sitter/version.rb +8 -0
 - data/lib/tree_sitter.rb +34 -0
 - data/lib/tree_stand/ast_modifier.rb +30 -0
 - data/lib/tree_stand/breadth_first_visitor.rb +54 -0
 - data/lib/tree_stand/config.rb +19 -0
 - data/lib/tree_stand/node.rb +351 -0
 - data/lib/tree_stand/parser.rb +87 -0
 - data/lib/tree_stand/range.rb +55 -0
 - data/lib/tree_stand/tree.rb +123 -0
 - data/lib/tree_stand/utils/printer.rb +73 -0
 - data/lib/tree_stand/version.rb +7 -0
 - data/lib/tree_stand/visitor.rb +127 -0
 - data/lib/tree_stand/visitors/tree_walker.rb +37 -0
 - data/lib/tree_stand.rb +48 -0
 - data/tree_sitter.gemspec +34 -0
 - metadata +135 -0
 
| 
         @@ -0,0 +1,167 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module TreeSitter
         
     | 
| 
      
 4 
     | 
    
         
            +
              # Node is a wrapper around a tree-sitter node.
         
     | 
| 
      
 5 
     | 
    
         
            +
              class Node
         
     | 
| 
      
 6 
     | 
    
         
            +
                include Enumerable
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                # @return [Array<Symbol>] the node's named fields
         
     | 
| 
      
 9 
     | 
    
         
            +
                def fields
         
     | 
| 
      
 10 
     | 
    
         
            +
                  return @fields if @fields
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  @fields = Set.new
         
     | 
| 
      
 13 
     | 
    
         
            +
                  child_count.times do |i|
         
     | 
| 
      
 14 
     | 
    
         
            +
                    name = field_name_for_child(i)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    @fields << name.to_sym if name
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  @fields.to_a
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                # @param field [String, Symbol]
         
     | 
| 
      
 22 
     | 
    
         
            +
                def field?(field)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  fields.include?(field.to_sym)
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                # Access node's named children.
         
     | 
| 
      
 27 
     | 
    
         
            +
                #
         
     | 
| 
      
 28 
     | 
    
         
            +
                # It's similar to {#fetch}, but differs in input type, return values, and
         
     | 
| 
      
 29 
     | 
    
         
            +
                # the internal implementation.
         
     | 
| 
      
 30 
     | 
    
         
            +
                #
         
     | 
| 
      
 31 
     | 
    
         
            +
                # Both of these methods exist for separate use cases, but also because
         
     | 
| 
      
 32 
     | 
    
         
            +
                # sometime tree-sitter does some monkey business and having both separate
         
     | 
| 
      
 33 
     | 
    
         
            +
                # implementations can help.
         
     | 
| 
      
 34 
     | 
    
         
            +
                #
         
     | 
| 
      
 35 
     | 
    
         
            +
                # Comparison with {#fetch}:
         
     | 
| 
      
 36 
     | 
    
         
            +
                #
         
     | 
| 
      
 37 
     | 
    
         
            +
                #              []                            | fetch
         
     | 
| 
      
 38 
     | 
    
         
            +
                #              ------------------------------+----------------------
         
     | 
| 
      
 39 
     | 
    
         
            +
                # input types  Integer, String, Symbol       | Array<String, Symbol>
         
     | 
| 
      
 40 
     | 
    
         
            +
                #              Array<Integer, String, Symbol>|
         
     | 
| 
      
 41 
     | 
    
         
            +
                #              ------------------------------+----------------------
         
     | 
| 
      
 42 
     | 
    
         
            +
                # returns      1-to-1 correspondance with    | unique nodes
         
     | 
| 
      
 43 
     | 
    
         
            +
                #              input                         |
         
     | 
| 
      
 44 
     | 
    
         
            +
                #              ------------------------------+----------------------
         
     | 
| 
      
 45 
     | 
    
         
            +
                # uses         named_child                   | field_name_for_child
         
     | 
| 
      
 46 
     | 
    
         
            +
                #              child_by_field_name           |   via each_node
         
     | 
| 
      
 47 
     | 
    
         
            +
                #              ------------------------------+----------------------
         
     | 
| 
      
 48 
     | 
    
         
            +
                #
         
     | 
| 
      
 49 
     | 
    
         
            +
                # @param keys [Integer | String | Symbol | Array<Integer, String, Symbol>, #read]
         
     | 
| 
      
 50 
     | 
    
         
            +
                #
         
     | 
| 
      
 51 
     | 
    
         
            +
                # @return [Node | Array<Node>]
         
     | 
| 
      
 52 
     | 
    
         
            +
                def [](*keys)
         
     | 
| 
      
 53 
     | 
    
         
            +
                  case keys.length
         
     | 
| 
      
 54 
     | 
    
         
            +
                  when 0 then raise ArgumentError, "#{self.class.name}##{__method__} requires a key."
         
     | 
| 
      
 55 
     | 
    
         
            +
                  when 1
         
     | 
| 
      
 56 
     | 
    
         
            +
                    case k = keys.first
         
     | 
| 
      
 57 
     | 
    
         
            +
                    when Integer then named_child(k)
         
     | 
| 
      
 58 
     | 
    
         
            +
                    when String, Symbol
         
     | 
| 
      
 59 
     | 
    
         
            +
                      raise IndexError, "Cannot find field #{k}. Available: #{fields}" unless fields.include?(k.to_sym)
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                      child_by_field_name(k.to_s)
         
     | 
| 
      
 62 
     | 
    
         
            +
                    else raise ArgumentError, <<~ERR
         
     | 
| 
      
 63 
     | 
    
         
            +
                      #{self.class.name}##{__method__} accepts Integer and returns named child at given index,
         
     | 
| 
      
 64 
     | 
    
         
            +
                          or a (String | Symbol) and returns the child by given field name.
         
     | 
| 
      
 65 
     | 
    
         
            +
                    ERR
         
     | 
| 
      
 66 
     | 
    
         
            +
                    end
         
     | 
| 
      
 67 
     | 
    
         
            +
                  else
         
     | 
| 
      
 68 
     | 
    
         
            +
                    keys.map { |key| self[key] }
         
     | 
| 
      
 69 
     | 
    
         
            +
                  end
         
     | 
| 
      
 70 
     | 
    
         
            +
                end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                # @!visibility private
         
     | 
| 
      
 73 
     | 
    
         
            +
                #
         
     | 
| 
      
 74 
     | 
    
         
            +
                # Allows access to child_by_field_name without using [].
         
     | 
| 
      
 75 
     | 
    
         
            +
                def method_missing(method_name, *_args, &_block)
         
     | 
| 
      
 76 
     | 
    
         
            +
                  if fields.include?(method_name)
         
     | 
| 
      
 77 
     | 
    
         
            +
                    child_by_field_name(method_name.to_s)
         
     | 
| 
      
 78 
     | 
    
         
            +
                  else
         
     | 
| 
      
 79 
     | 
    
         
            +
                    super
         
     | 
| 
      
 80 
     | 
    
         
            +
                  end
         
     | 
| 
      
 81 
     | 
    
         
            +
                end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                # @!visibility private
         
     | 
| 
      
 84 
     | 
    
         
            +
                #
         
     | 
| 
      
 85 
     | 
    
         
            +
                def respond_to_missing?(*args)
         
     | 
| 
      
 86 
     | 
    
         
            +
                  args.length == 1 && fields.include?(args[0])
         
     | 
| 
      
 87 
     | 
    
         
            +
                end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                # Iterate over a node's children.
         
     | 
| 
      
 90 
     | 
    
         
            +
                #
         
     | 
| 
      
 91 
     | 
    
         
            +
                # @yieldparam child [Node] the child
         
     | 
| 
      
 92 
     | 
    
         
            +
                def each(&_block)
         
     | 
| 
      
 93 
     | 
    
         
            +
                  return enum_for __method__ if !block_given?
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                  (0...child_count).each do |i|
         
     | 
| 
      
 96 
     | 
    
         
            +
                    yield child(i)
         
     | 
| 
      
 97 
     | 
    
         
            +
                  end
         
     | 
| 
      
 98 
     | 
    
         
            +
                end
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
                # Iterate over a node's children assigned to a field.
         
     | 
| 
      
 101 
     | 
    
         
            +
                #
         
     | 
| 
      
 102 
     | 
    
         
            +
                # @yieldparam name [NilClass | String] field name.
         
     | 
| 
      
 103 
     | 
    
         
            +
                # @yieldparam child [Node] the child.
         
     | 
| 
      
 104 
     | 
    
         
            +
                def each_field
         
     | 
| 
      
 105 
     | 
    
         
            +
                  return enum_for __method__ if !block_given?
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                  each.with_index do |c, i|
         
     | 
| 
      
 108 
     | 
    
         
            +
                    f = field_name_for_child(i)
         
     | 
| 
      
 109 
     | 
    
         
            +
                    next if f.nil? || f.empty?
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                    yield f, c
         
     | 
| 
      
 112 
     | 
    
         
            +
                  end
         
     | 
| 
      
 113 
     | 
    
         
            +
                end
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                # Iterate over a node's named children
         
     | 
| 
      
 116 
     | 
    
         
            +
                #
         
     | 
| 
      
 117 
     | 
    
         
            +
                # @yieldparam child [Node] the child
         
     | 
| 
      
 118 
     | 
    
         
            +
                def each_named
         
     | 
| 
      
 119 
     | 
    
         
            +
                  return enum_for __method__ if !block_given?
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                  (0...(named_child_count)).each do |i|
         
     | 
| 
      
 122 
     | 
    
         
            +
                    yield named_child(i)
         
     | 
| 
      
 123 
     | 
    
         
            +
                  end
         
     | 
| 
      
 124 
     | 
    
         
            +
                end
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
                # @return [Array<TreeSitter::Node>] all the node's children
         
     | 
| 
      
 127 
     | 
    
         
            +
                def to_a
         
     | 
| 
      
 128 
     | 
    
         
            +
                  each.to_a
         
     | 
| 
      
 129 
     | 
    
         
            +
                end
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
      
 131 
     | 
    
         
            +
                # Access node's named children.
         
     | 
| 
      
 132 
     | 
    
         
            +
                #
         
     | 
| 
      
 133 
     | 
    
         
            +
                # It's similar to {#[]}, but differs in input type, return values, and
         
     | 
| 
      
 134 
     | 
    
         
            +
                # the internal implementation.
         
     | 
| 
      
 135 
     | 
    
         
            +
                #
         
     | 
| 
      
 136 
     | 
    
         
            +
                # Both of these methods exist for separate use cases, but also because
         
     | 
| 
      
 137 
     | 
    
         
            +
                # sometime tree-sitter does some monkey business and having both separate
         
     | 
| 
      
 138 
     | 
    
         
            +
                # implementations can help.
         
     | 
| 
      
 139 
     | 
    
         
            +
                #
         
     | 
| 
      
 140 
     | 
    
         
            +
                # Comparison with {#fetch}:
         
     | 
| 
      
 141 
     | 
    
         
            +
                #
         
     | 
| 
      
 142 
     | 
    
         
            +
                #              []                            | fetch
         
     | 
| 
      
 143 
     | 
    
         
            +
                #              ------------------------------+----------------------
         
     | 
| 
      
 144 
     | 
    
         
            +
                # input types  Integer, String, Symbol       | String, Symbol
         
     | 
| 
      
 145 
     | 
    
         
            +
                #              Array<Integer, String, Symbol>| Array<String, Symbol>
         
     | 
| 
      
 146 
     | 
    
         
            +
                #              ------------------------------+----------------------
         
     | 
| 
      
 147 
     | 
    
         
            +
                # returns      1-to-1 correspondance with    | unique nodes
         
     | 
| 
      
 148 
     | 
    
         
            +
                #              input                         |
         
     | 
| 
      
 149 
     | 
    
         
            +
                #              ------------------------------+----------------------
         
     | 
| 
      
 150 
     | 
    
         
            +
                # uses         named_child                   | field_name_for_child
         
     | 
| 
      
 151 
     | 
    
         
            +
                #              child_by_field_name           |   via each_node
         
     | 
| 
      
 152 
     | 
    
         
            +
                #              ------------------------------+----------------------
         
     | 
| 
      
 153 
     | 
    
         
            +
                #
         
     | 
| 
      
 154 
     | 
    
         
            +
                # See {#[]}.
         
     | 
| 
      
 155 
     | 
    
         
            +
                def fetch(*keys)
         
     | 
| 
      
 156 
     | 
    
         
            +
                  keys = keys.map(&:to_s)
         
     | 
| 
      
 157 
     | 
    
         
            +
                  key_set = keys.to_set
         
     | 
| 
      
 158 
     | 
    
         
            +
                  fields = {}
         
     | 
| 
      
 159 
     | 
    
         
            +
                  each_field do |f, _c|
         
     | 
| 
      
 160 
     | 
    
         
            +
                    fields[f] = self[f] if key_set.delete(f)
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
      
 162 
     | 
    
         
            +
                    break if key_set.empty?
         
     | 
| 
      
 163 
     | 
    
         
            +
                  end
         
     | 
| 
      
 164 
     | 
    
         
            +
                  fields.values_at(*keys)
         
     | 
| 
      
 165 
     | 
    
         
            +
                end
         
     | 
| 
      
 166 
     | 
    
         
            +
              end
         
     | 
| 
      
 167 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,191 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative 'helpers'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module TreeSitter
         
     | 
| 
      
 6 
     | 
    
         
            +
              # Query is a wrapper around a tree-sitter query.
         
     | 
| 
      
 7 
     | 
    
         
            +
              class Query
         
     | 
| 
      
 8 
     | 
    
         
            +
                attr_reader :capture_names
         
     | 
| 
      
 9 
     | 
    
         
            +
                attr_reader :capture_quantifiers
         
     | 
| 
      
 10 
     | 
    
         
            +
                attr_reader :text_predicates
         
     | 
| 
      
 11 
     | 
    
         
            +
                attr_reader :property_predicates
         
     | 
| 
      
 12 
     | 
    
         
            +
                attr_reader :property_settings
         
     | 
| 
      
 13 
     | 
    
         
            +
                attr_reader :general_predicates
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                private
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                # Called from query.c on initialize.
         
     | 
| 
      
 18 
     | 
    
         
            +
                #
         
     | 
| 
      
 19 
     | 
    
         
            +
                # Prepares all the predicates so we could process them in places like
         
     | 
| 
      
 20 
     | 
    
         
            +
                # {QueryMatch#satisfies_text_predicate?}.
         
     | 
| 
      
 21 
     | 
    
         
            +
                #
         
     | 
| 
      
 22 
     | 
    
         
            +
                # This is translation from the [rust bindings](https://github.com/tree-sitter/tree-sitter/blob/e553578696fe86071846ed612ee476d0167369c1/lib/binding_rust/lib.rs#L1860)
         
     | 
| 
      
 23 
     | 
    
         
            +
                # Because it's a direct translation, it's way too long and we need to shut up rubocop.
         
     | 
| 
      
 24 
     | 
    
         
            +
                # TODO: refactor + simplify when stable.
         
     | 
| 
      
 25 
     | 
    
         
            +
                def process(source) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
         
     | 
| 
      
 26 
     | 
    
         
            +
                  string_count = self.string_count
         
     | 
| 
      
 27 
     | 
    
         
            +
                  capture_count = self.capture_count
         
     | 
| 
      
 28 
     | 
    
         
            +
                  pattern_count = self.pattern_count
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  # Build a vector of strings to store the capture names.
         
     | 
| 
      
 31 
     | 
    
         
            +
                  capture_names = capture_count.times.map { |i| capture_name_for_id(i) }
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                  # Build a vector to store capture qunatifiers.
         
     | 
| 
      
 34 
     | 
    
         
            +
                  capture_quantifiers =
         
     | 
| 
      
 35 
     | 
    
         
            +
                    pattern_count.times.map do |i|
         
     | 
| 
      
 36 
     | 
    
         
            +
                      capture_count.times.map do |j|
         
     | 
| 
      
 37 
     | 
    
         
            +
                        capture_quantifier_for_id(i, j)
         
     | 
| 
      
 38 
     | 
    
         
            +
                      end
         
     | 
| 
      
 39 
     | 
    
         
            +
                    end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                  # Build a vector of strings to represent literal values used in predicates.
         
     | 
| 
      
 42 
     | 
    
         
            +
                  string_values = string_count.times.map { |i| string_value_for_id(i) }
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  # Build a vector of predicates for each pattern.
         
     | 
| 
      
 45 
     | 
    
         
            +
                  pattern_count.times do |i| # rubocop:disable Metrics/BlockLength
         
     | 
| 
      
 46 
     | 
    
         
            +
                    predicate_steps = predicates_for_pattern(i)
         
     | 
| 
      
 47 
     | 
    
         
            +
                    byte_offset = start_byte_for_pattern(i)
         
     | 
| 
      
 48 
     | 
    
         
            +
                    row =
         
     | 
| 
      
 49 
     | 
    
         
            +
                      source.chars.map.with_index
         
     | 
| 
      
 50 
     | 
    
         
            +
                        .take_while { |_, i| i < byte_offset } # rubocop:disable Lint/ShadowingOuterLocalVariable
         
     | 
| 
      
 51 
     | 
    
         
            +
                        .filter { |c, _| c == "\n" }
         
     | 
| 
      
 52 
     | 
    
         
            +
                        .size
         
     | 
| 
      
 53 
     | 
    
         
            +
                    text_predicates = []
         
     | 
| 
      
 54 
     | 
    
         
            +
                    property_predicates = []
         
     | 
| 
      
 55 
     | 
    
         
            +
                    property_settings = []
         
     | 
| 
      
 56 
     | 
    
         
            +
                    general_predicates = []
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                    array_split_like_rust(predicate_steps) { |s| s.type == QueryPredicateStep::DONE } # rubocop:disable Metrics/BlockLength
         
     | 
| 
      
 59 
     | 
    
         
            +
                      .each do |p|
         
     | 
| 
      
 60 
     | 
    
         
            +
                        next if p.empty?
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                        if p[0] == QueryPredicateStep::STRING
         
     | 
| 
      
 63 
     | 
    
         
            +
                          cap = capture_names[p[0].value_id]
         
     | 
| 
      
 64 
     | 
    
         
            +
                          raise ArgumentError, <<~MSG.chomp
         
     | 
| 
      
 65 
     | 
    
         
            +
                            L#{row}: Expected predicate to start with a function name. Got @#{cap}.
         
     | 
| 
      
 66 
     | 
    
         
            +
                          MSG
         
     | 
| 
      
 67 
     | 
    
         
            +
                        end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                        # Build a predicate for each of the known predicate function names.
         
     | 
| 
      
 70 
     | 
    
         
            +
                        operator_name = string_values[p[0].value_id]
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                        case operator_name
         
     | 
| 
      
 73 
     | 
    
         
            +
                        in 'any-eq?' | 'any-not-eq?' | 'eq?' | 'not-eq?'
         
     | 
| 
      
 74 
     | 
    
         
            +
                          if p.size != 3
         
     | 
| 
      
 75 
     | 
    
         
            +
                            raise ArgumentError, <<~MSG.chomp
         
     | 
| 
      
 76 
     | 
    
         
            +
                              L#{row}: Wrong number of arguments to ##{operator_name} predicate. Expected 2, got #{p.size - 1}.
         
     | 
| 
      
 77 
     | 
    
         
            +
                            MSG
         
     | 
| 
      
 78 
     | 
    
         
            +
                          end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                          if p[1].type != QueryPredicateStep::CAPTURE
         
     | 
| 
      
 81 
     | 
    
         
            +
                            lit = string_values[p[1].value_id]
         
     | 
| 
      
 82 
     | 
    
         
            +
                            raise ArgumentError, <<~MSG.chomp
         
     | 
| 
      
 83 
     | 
    
         
            +
                              L#{row}: First argument to ##{operator_name} predicate must be a capture name. Got literal "#{lit}".
         
     | 
| 
      
 84 
     | 
    
         
            +
                            MSG
         
     | 
| 
      
 85 
     | 
    
         
            +
                          end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                          is_positive = %w[eq? any-eq?].include?(operator_name)
         
     | 
| 
      
 88 
     | 
    
         
            +
                          match_all = %w[eq? not-eq?].include?(operator_name)
         
     | 
| 
      
 89 
     | 
    
         
            +
                          # NOTE: in the rust impl, match_all can hit an unreachable! but I am simplifying
         
     | 
| 
      
 90 
     | 
    
         
            +
                          # for readability. Same applies for the other `in` branches.
         
     | 
| 
      
 91 
     | 
    
         
            +
                          text_predicates <<
         
     | 
| 
      
 92 
     | 
    
         
            +
                            if p[2].type == QueryPredicateStep::CAPTURE
         
     | 
| 
      
 93 
     | 
    
         
            +
                              TextPredicateCapture.eq_capture(p[1].value_id, p[2].value_id, is_positive, match_all)
         
     | 
| 
      
 94 
     | 
    
         
            +
                            else
         
     | 
| 
      
 95 
     | 
    
         
            +
                              TextPredicateCapture.eq_string(p[1].value_id, string_values[p[2].value_id], is_positive, match_all)
         
     | 
| 
      
 96 
     | 
    
         
            +
                            end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                        in 'match?' | 'not-match?' | 'any-match?' | 'any-not-match?'
         
     | 
| 
      
 99 
     | 
    
         
            +
                          if p.size != 3
         
     | 
| 
      
 100 
     | 
    
         
            +
                            raise ArgumentError, <<~MSG.chomp
         
     | 
| 
      
 101 
     | 
    
         
            +
                              L#{row}: Wrong number of arguments to ##{operator_name} predicate. Expected 2, got #{p.size - 1}.
         
     | 
| 
      
 102 
     | 
    
         
            +
                            MSG
         
     | 
| 
      
 103 
     | 
    
         
            +
                          end
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                          if p[1].type != QueryPredicateStep::CAPTURE
         
     | 
| 
      
 106 
     | 
    
         
            +
                            lit = string_values[p[1].value_id]
         
     | 
| 
      
 107 
     | 
    
         
            +
                            raise ArgumentError, <<~MSG.chomp
         
     | 
| 
      
 108 
     | 
    
         
            +
                              L#{row}: First argument to ##{operator_name} predicate must be a capture name. Got literal "#{lit}".
         
     | 
| 
      
 109 
     | 
    
         
            +
                            MSG
         
     | 
| 
      
 110 
     | 
    
         
            +
                          end
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                          if p[2].type == QueryPredicateStep::CAPTURE
         
     | 
| 
      
 113 
     | 
    
         
            +
                            cap = capture_names[p[2].value_id]
         
     | 
| 
      
 114 
     | 
    
         
            +
                            raise ArgumentError, <<~MSG.chomp
         
     | 
| 
      
 115 
     | 
    
         
            +
                              L#{row}: First argument to ##{operator_name} predicate must be a literal. Got capture @#{cap}".
         
     | 
| 
      
 116 
     | 
    
         
            +
                            MSG
         
     | 
| 
      
 117 
     | 
    
         
            +
                          end
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
                          is_positive = %w[match? any-match?].include?(operator_name)
         
     | 
| 
      
 120 
     | 
    
         
            +
                          match_all = %w[match? not-match?].include?(operator_name)
         
     | 
| 
      
 121 
     | 
    
         
            +
                          regex = /#{string_values[p[2].value_id]}/
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
                          text_predicates << TextPredicateCapture.match_string(p[1].value_id, regex, is_positive, match_all)
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                        in 'set!'
         
     | 
| 
      
 126 
     | 
    
         
            +
                          property_settings << 'todo!'
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
                        in 'is?' | 'is-not?'
         
     | 
| 
      
 129 
     | 
    
         
            +
                          property_predicates << 'todo!'
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
      
 131 
     | 
    
         
            +
                        in 'any-of?' | 'not-any-of?'
         
     | 
| 
      
 132 
     | 
    
         
            +
                          if p.size < 2
         
     | 
| 
      
 133 
     | 
    
         
            +
                            raise ArgumentError, <<~MSG.chomp
         
     | 
| 
      
 134 
     | 
    
         
            +
                              L#{row}: Wrong number of arguments to ##{operator_name} predicate. Expected at least 1, got #{p.size - 1}.
         
     | 
| 
      
 135 
     | 
    
         
            +
                            MSG
         
     | 
| 
      
 136 
     | 
    
         
            +
                          end
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
                          if p[1].type != QueryPredicateStep::CAPTURE
         
     | 
| 
      
 139 
     | 
    
         
            +
                            lit = string_values[p[1].value_id]
         
     | 
| 
      
 140 
     | 
    
         
            +
                            raise ArgumentError, <<~MSG.chomp
         
     | 
| 
      
 141 
     | 
    
         
            +
                              L#{row}: First argument to ##{operator_name} predicate must be a capture name. Got literal "#{lit}".
         
     | 
| 
      
 142 
     | 
    
         
            +
                            MSG
         
     | 
| 
      
 143 
     | 
    
         
            +
                          end
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
                          is_positive = operator_name == 'any_of'
         
     | 
| 
      
 146 
     | 
    
         
            +
                          values = []
         
     | 
| 
      
 147 
     | 
    
         
            +
             
     | 
| 
      
 148 
     | 
    
         
            +
                          p[2..].each do |arg|
         
     | 
| 
      
 149 
     | 
    
         
            +
                            if arg.type == QueryPredicateStep::CAPTURE
         
     | 
| 
      
 150 
     | 
    
         
            +
                              lit = string_values[arg.value_id]
         
     | 
| 
      
 151 
     | 
    
         
            +
                              raise ArgumentError, <<~MSG.chomp
         
     | 
| 
      
 152 
     | 
    
         
            +
                                L#{row}: First argument to ##{operator_name} predicate must be a capture name. Got literal "#{lit}".
         
     | 
| 
      
 153 
     | 
    
         
            +
                              MSG
         
     | 
| 
      
 154 
     | 
    
         
            +
                            end
         
     | 
| 
      
 155 
     | 
    
         
            +
                            values << string_values[arg.value_id]
         
     | 
| 
      
 156 
     | 
    
         
            +
                          end
         
     | 
| 
      
 157 
     | 
    
         
            +
             
     | 
| 
      
 158 
     | 
    
         
            +
                          # TODO: is the map to to_s necessary in ruby?
         
     | 
| 
      
 159 
     | 
    
         
            +
                          text_predicates <<
         
     | 
| 
      
 160 
     | 
    
         
            +
                            TextPredicateCapture.any_string(p[1].value_id, values.map(&:to_s), is_positive, match_all)
         
     | 
| 
      
 161 
     | 
    
         
            +
                        else
         
     | 
| 
      
 162 
     | 
    
         
            +
                          general_predicates <<
         
     | 
| 
      
 163 
     | 
    
         
            +
                            QueryPredicate.new(
         
     | 
| 
      
 164 
     | 
    
         
            +
                              operator_name,
         
     | 
| 
      
 165 
     | 
    
         
            +
                              p[1..].map do |a|
         
     | 
| 
      
 166 
     | 
    
         
            +
                                if a.type == QueryPredicateStep::CAPTURE
         
     | 
| 
      
 167 
     | 
    
         
            +
                                  { capture: a.value_id }
         
     | 
| 
      
 168 
     | 
    
         
            +
                                else
         
     | 
| 
      
 169 
     | 
    
         
            +
                                  { string: string_values[a.value_id] }
         
     | 
| 
      
 170 
     | 
    
         
            +
                                end
         
     | 
| 
      
 171 
     | 
    
         
            +
                              end,
         
     | 
| 
      
 172 
     | 
    
         
            +
                            )
         
     | 
| 
      
 173 
     | 
    
         
            +
                        end
         
     | 
| 
      
 174 
     | 
    
         
            +
             
     | 
| 
      
 175 
     | 
    
         
            +
                        @text_predicates << text_predicates
         
     | 
| 
      
 176 
     | 
    
         
            +
                        @property_predicates << property_predicates
         
     | 
| 
      
 177 
     | 
    
         
            +
                        @property_settings << property_settings
         
     | 
| 
      
 178 
     | 
    
         
            +
                        @general_predicates << general_predicates
         
     | 
| 
      
 179 
     | 
    
         
            +
                      end
         
     | 
| 
      
 180 
     | 
    
         
            +
             
     | 
| 
      
 181 
     | 
    
         
            +
                    @capture_names = capture_names
         
     | 
| 
      
 182 
     | 
    
         
            +
                    @capture_quantifiers = capture_quantifiers
         
     | 
| 
      
 183 
     | 
    
         
            +
                  end
         
     | 
| 
      
 184 
     | 
    
         
            +
                end
         
     | 
| 
      
 185 
     | 
    
         
            +
             
     | 
| 
      
 186 
     | 
    
         
            +
                # TODO
         
     | 
| 
      
 187 
     | 
    
         
            +
                def parse_property
         
     | 
| 
      
 188 
     | 
    
         
            +
                  # todo
         
     | 
| 
      
 189 
     | 
    
         
            +
                end
         
     | 
| 
      
 190 
     | 
    
         
            +
              end
         
     | 
| 
      
 191 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,30 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module TreeSitter
         
     | 
| 
      
 4 
     | 
    
         
            +
              # A sequence of {TreeSitter::QueryCapture} associated with a given {TreeSitter::QueryCursor}.
         
     | 
| 
      
 5 
     | 
    
         
            +
              class QueryCaptures
         
     | 
| 
      
 6 
     | 
    
         
            +
                include Enumerable
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                def initialize(cursor, query, src)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @cursor = cursor
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @query = query
         
     | 
| 
      
 11 
     | 
    
         
            +
                  @src = src
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                # Iterator over captures.
         
     | 
| 
      
 15 
     | 
    
         
            +
                #
         
     | 
| 
      
 16 
     | 
    
         
            +
                # @yieldparam match [TreeSitter::QueryMatch]
         
     | 
| 
      
 17 
     | 
    
         
            +
                # @yieldparam capture_index [Integer]
         
     | 
| 
      
 18 
     | 
    
         
            +
                def each(&_block)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  return enum_for __method__ if !block_given?
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  while (capture_index, match = @cursor.next_capture)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    next if !match.is_a?(TreeSitter::QueryMatch)
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                    if match.satisfies_text_predicate?(@query, @src)
         
     | 
| 
      
 25 
     | 
    
         
            +
                      yield [match, capture_index]
         
     | 
| 
      
 26 
     | 
    
         
            +
                    end
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,27 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module TreeSitter
         
     | 
| 
      
 4 
     | 
    
         
            +
              # A Cursor for {Query}.
         
     | 
| 
      
 5 
     | 
    
         
            +
              class QueryCursor
         
     | 
| 
      
 6 
     | 
    
         
            +
                # Iterate over all of the matches in the order that they were found.
         
     | 
| 
      
 7 
     | 
    
         
            +
                #
         
     | 
| 
      
 8 
     | 
    
         
            +
                # Each match contains the index of the pattern that matched, and a list of
         
     | 
| 
      
 9 
     | 
    
         
            +
                # captures. Because multiple patterns can match the same set of nodes,
         
     | 
| 
      
 10 
     | 
    
         
            +
                # one match may contain captures that appear *before* some of the
         
     | 
| 
      
 11 
     | 
    
         
            +
                # captures from a previous match.
         
     | 
| 
      
 12 
     | 
    
         
            +
                def matches(query, node, src)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  self.exec(query, node)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  QueryMatches.new(self, query, src)
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                # Iterate over all of the individual captures in the order that they
         
     | 
| 
      
 18 
     | 
    
         
            +
                # appear.
         
     | 
| 
      
 19 
     | 
    
         
            +
                #
         
     | 
| 
      
 20 
     | 
    
         
            +
                # This is useful if you don't care about which pattern matched, and just
         
     | 
| 
      
 21 
     | 
    
         
            +
                # want a single, ordered sequence of captures.
         
     | 
| 
      
 22 
     | 
    
         
            +
                def captures(query, node, src)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  self.exec(query, node)
         
     | 
| 
      
 24 
     | 
    
         
            +
                  QueryCaptures.new(self, query, src)
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,100 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative 'query_captures'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module TreeSitter
         
     | 
| 
      
 6 
     | 
    
         
            +
              # A match for a {Query}.
         
     | 
| 
      
 7 
     | 
    
         
            +
              class QueryMatch
         
     | 
| 
      
 8 
     | 
    
         
            +
                # All nodes at a given capture index.
         
     | 
| 
      
 9 
     | 
    
         
            +
                #
         
     | 
| 
      
 10 
     | 
    
         
            +
                # @param index [Integer]
         
     | 
| 
      
 11 
     | 
    
         
            +
                #
         
     | 
| 
      
 12 
     | 
    
         
            +
                # @return [TreeSitter::Node]
         
     | 
| 
      
 13 
     | 
    
         
            +
                def nodes_for_capture_index(index) = captures.filter_map { |capture| capture.node if capture.index == index }
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                # Whether the {QueryMatch} satisfies the text predicates in the query.
         
     | 
| 
      
 16 
     | 
    
         
            +
                #
         
     | 
| 
      
 17 
     | 
    
         
            +
                # This is a translation from the [rust bindings](https://github.com/tree-sitter/tree-sitter/blob/e553578696fe86071846ed612ee476d0167369c1/lib/binding_rust/lib.rs#L2502).
         
     | 
| 
      
 18 
     | 
    
         
            +
                # Because it's a direct translation, it's way too long and we need to shut up rubocop.
         
     | 
| 
      
 19 
     | 
    
         
            +
                # TODO: refactor + simplify when satable.
         
     | 
| 
      
 20 
     | 
    
         
            +
                def satisfies_text_predicate?(query, src) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
         
     | 
| 
      
 21 
     | 
    
         
            +
                  return true if query.text_predicates[pattern_index].nil?
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  query # rubocop:disable Metrics/BlockLength
         
     | 
| 
      
 24 
     | 
    
         
            +
                    .text_predicates[pattern_index]
         
     | 
| 
      
 25 
     | 
    
         
            +
                    .all? do |predicate|
         
     | 
| 
      
 26 
     | 
    
         
            +
                      case predicate.type
         
     | 
| 
      
 27 
     | 
    
         
            +
                      in TextPredicateCapture::EQ_CAPTURE
         
     | 
| 
      
 28 
     | 
    
         
            +
                        fst_nodes = nodes_for_capture_index(predicate.fst)
         
     | 
| 
      
 29 
     | 
    
         
            +
                        snd_nodes = nodes_for_capture_index(predicate.snd)
         
     | 
| 
      
 30 
     | 
    
         
            +
                        res = nil
         
     | 
| 
      
 31 
     | 
    
         
            +
                        consumed = 0
         
     | 
| 
      
 32 
     | 
    
         
            +
                        fst_nodes.zip(snd_nodes).each do |node1, node2|
         
     | 
| 
      
 33 
     | 
    
         
            +
                          text1 = node_text(node1, src)
         
     | 
| 
      
 34 
     | 
    
         
            +
                          text2 = node_text(node2, src)
         
     | 
| 
      
 35 
     | 
    
         
            +
                          if (text1 == text2) != predicate.positive? && predicate.match_all?
         
     | 
| 
      
 36 
     | 
    
         
            +
                            res = false
         
     | 
| 
      
 37 
     | 
    
         
            +
                            break
         
     | 
| 
      
 38 
     | 
    
         
            +
                          end
         
     | 
| 
      
 39 
     | 
    
         
            +
                          if (text1 == text2) == predicate.positive? && !predicate.match_all?
         
     | 
| 
      
 40 
     | 
    
         
            +
                            res = true
         
     | 
| 
      
 41 
     | 
    
         
            +
                            break
         
     | 
| 
      
 42 
     | 
    
         
            +
                          end
         
     | 
| 
      
 43 
     | 
    
         
            +
                          consumed += 1
         
     | 
| 
      
 44 
     | 
    
         
            +
                        end
         
     | 
| 
      
 45 
     | 
    
         
            +
                        (res.nil? && consumed == fst_nodes.length && consumed == snd_nodes.length) \
         
     | 
| 
      
 46 
     | 
    
         
            +
                          || res
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                      in TextPredicateCapture::EQ_STRING
         
     | 
| 
      
 49 
     | 
    
         
            +
                        nodes = nodes_for_capture_index(predicate.fst)
         
     | 
| 
      
 50 
     | 
    
         
            +
                        res = true
         
     | 
| 
      
 51 
     | 
    
         
            +
                        nodes.each do |node|
         
     | 
| 
      
 52 
     | 
    
         
            +
                          text = node_text(node, src)
         
     | 
| 
      
 53 
     | 
    
         
            +
                          if (predicate.snd == text) != predicate.positive? && predicate.match_all?
         
     | 
| 
      
 54 
     | 
    
         
            +
                            res = false
         
     | 
| 
      
 55 
     | 
    
         
            +
                            break
         
     | 
| 
      
 56 
     | 
    
         
            +
                          end
         
     | 
| 
      
 57 
     | 
    
         
            +
                          if (predicate.snd == text) == predicate.positive? && !predicate.match_all?
         
     | 
| 
      
 58 
     | 
    
         
            +
                            res = true
         
     | 
| 
      
 59 
     | 
    
         
            +
                            break
         
     | 
| 
      
 60 
     | 
    
         
            +
                          end
         
     | 
| 
      
 61 
     | 
    
         
            +
                        end
         
     | 
| 
      
 62 
     | 
    
         
            +
                        res
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                      in TextPredicateCapture::MATCH_STRING
         
     | 
| 
      
 65 
     | 
    
         
            +
                        nodes = nodes_for_capture_index(predicate.fst)
         
     | 
| 
      
 66 
     | 
    
         
            +
                        res = true
         
     | 
| 
      
 67 
     | 
    
         
            +
                        nodes.each do |node|
         
     | 
| 
      
 68 
     | 
    
         
            +
                          text = node_text(node, src)
         
     | 
| 
      
 69 
     | 
    
         
            +
                          if predicate.snd.match?(text) != predicate.positive? && predicate.match_all?
         
     | 
| 
      
 70 
     | 
    
         
            +
                            res = false
         
     | 
| 
      
 71 
     | 
    
         
            +
                            break
         
     | 
| 
      
 72 
     | 
    
         
            +
                          end
         
     | 
| 
      
 73 
     | 
    
         
            +
                          if predicate.snd.match?(text) == predicate.positive? && !predicate.match_all?
         
     | 
| 
      
 74 
     | 
    
         
            +
                            res = true
         
     | 
| 
      
 75 
     | 
    
         
            +
                            break
         
     | 
| 
      
 76 
     | 
    
         
            +
                          end
         
     | 
| 
      
 77 
     | 
    
         
            +
                        end
         
     | 
| 
      
 78 
     | 
    
         
            +
                        res
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                      in TextPredicateCapture::ANY_STRING
         
     | 
| 
      
 81 
     | 
    
         
            +
                        nodes = nodes_for_capture_index(predicate.fst)
         
     | 
| 
      
 82 
     | 
    
         
            +
                        res = true
         
     | 
| 
      
 83 
     | 
    
         
            +
                        nodes.each do |node|
         
     | 
| 
      
 84 
     | 
    
         
            +
                          text = node_text(node, src)
         
     | 
| 
      
 85 
     | 
    
         
            +
                          if predicate.snd.any? { |v| v == text } != predicate.positive?
         
     | 
| 
      
 86 
     | 
    
         
            +
                            res = false
         
     | 
| 
      
 87 
     | 
    
         
            +
                            break
         
     | 
| 
      
 88 
     | 
    
         
            +
                          end
         
     | 
| 
      
 89 
     | 
    
         
            +
                        end
         
     | 
| 
      
 90 
     | 
    
         
            +
                        res
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                      end
         
     | 
| 
      
 93 
     | 
    
         
            +
                    end
         
     | 
| 
      
 94 
     | 
    
         
            +
                end
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                private
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                def node_text(node, text) = text.byteslice(node.start_byte...node.end_byte)
         
     | 
| 
      
 99 
     | 
    
         
            +
              end
         
     | 
| 
      
 100 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,39 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module TreeSitter
         
     | 
| 
      
 4 
     | 
    
         
            +
              # A sequence of {QueryMatch} associated with a given {QueryCursor}.
         
     | 
| 
      
 5 
     | 
    
         
            +
              class QueryMatches
         
     | 
| 
      
 6 
     | 
    
         
            +
                include Enumerable
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                def initialize(cursor, query, src)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @cursor = cursor
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @query = query
         
     | 
| 
      
 11 
     | 
    
         
            +
                  @src = src
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                # Iterator over matches.
         
     | 
| 
      
 15 
     | 
    
         
            +
                #
         
     | 
| 
      
 16 
     | 
    
         
            +
                # @yieldparam match [TreeSitter::QueryMatch]
         
     | 
| 
      
 17 
     | 
    
         
            +
                def each(&_block)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  return enum_for __method__ if !block_given?
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  while match = @cursor.next_match
         
     | 
| 
      
 21 
     | 
    
         
            +
                    if match.satisfies_text_predicate?(@query, @src)
         
     | 
| 
      
 22 
     | 
    
         
            +
                      yield match
         
     | 
| 
      
 23 
     | 
    
         
            +
                    end
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                # Iterate over all the results presented as hashes of `capture name => node`.
         
     | 
| 
      
 28 
     | 
    
         
            +
                #
         
     | 
| 
      
 29 
     | 
    
         
            +
                # @yieldparam match [Hash<String, TreeSitter::Node>]
         
     | 
| 
      
 30 
     | 
    
         
            +
                def each_capture_hash(&_block)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  # TODO: should we return [Array<Hash<Symbol, TreeSitter::Node]>>] instead?
         
     | 
| 
      
 32 
     | 
    
         
            +
                  return enum_for __method__ if !block_given?
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                  each do |match|
         
     | 
| 
      
 35 
     | 
    
         
            +
                    yield match.captures.to_h { |cap| [@query.capture_name_for_id(cap.index), cap.node] }
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
              end
         
     | 
| 
      
 39 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,14 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module TreeSitter
         
     | 
| 
      
 4 
     | 
    
         
            +
              # A {Query} predicate generic representation.
         
     | 
| 
      
 5 
     | 
    
         
            +
              class QueryPredicate
         
     | 
| 
      
 6 
     | 
    
         
            +
                attr_accessor :operator
         
     | 
| 
      
 7 
     | 
    
         
            +
                attr_accessor :args
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                def initialize(operator, args)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @operator = operator
         
     | 
| 
      
 11 
     | 
    
         
            +
                  @args = args
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
              end
         
     | 
| 
      
 14 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,37 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module TreeSitter
         
     | 
| 
      
 4 
     | 
    
         
            +
              # A representation for text predicates.
         
     | 
| 
      
 5 
     | 
    
         
            +
              class TextPredicateCapture
         
     | 
| 
      
 6 
     | 
    
         
            +
                EQ_CAPTURE = 0   # Equality Capture
         
     | 
| 
      
 7 
     | 
    
         
            +
                EQ_STRING = 1    # Equality String
         
     | 
| 
      
 8 
     | 
    
         
            +
                MATCH_STRING = 2 # Match String
         
     | 
| 
      
 9 
     | 
    
         
            +
                ANY_STRING = 3   # Any String
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                attr_reader :fst
         
     | 
| 
      
 12 
     | 
    
         
            +
                attr_reader :snd
         
     | 
| 
      
 13 
     | 
    
         
            +
                attr_reader :type
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                # Create a TextPredicateCapture for {EQ_CAPTURE}.
         
     | 
| 
      
 16 
     | 
    
         
            +
                def self.eq_capture(...) = new(EQ_CAPTURE, ...)
         
     | 
| 
      
 17 
     | 
    
         
            +
                # Create a TextPredicateCapture for {EQ_STRING}.
         
     | 
| 
      
 18 
     | 
    
         
            +
                def self.eq_string(...) = new(EQ_STRING, ...)
         
     | 
| 
      
 19 
     | 
    
         
            +
                # Create a TextPredicateCapture for {MATCH_STRING}.
         
     | 
| 
      
 20 
     | 
    
         
            +
                def self.match_string(...) = new(MATCH_STRING, ...)
         
     | 
| 
      
 21 
     | 
    
         
            +
                # Create a TextPredicateCapture for {ANY_STRING}.
         
     | 
| 
      
 22 
     | 
    
         
            +
                def self.any_string(...) = new(ANY_STRING, ...)
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def initialize(type, fst, snd, positive, match_all)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  @type = type
         
     | 
| 
      
 26 
     | 
    
         
            +
                  @fst = fst
         
     | 
| 
      
 27 
     | 
    
         
            +
                  @snd = snd
         
     | 
| 
      
 28 
     | 
    
         
            +
                  @positive = positive
         
     | 
| 
      
 29 
     | 
    
         
            +
                  @match_all = match_all
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                # `#eq` is positive, `#not-eq` is not.
         
     | 
| 
      
 33 
     | 
    
         
            +
                def positive? = @positive
         
     | 
| 
      
 34 
     | 
    
         
            +
                # `#any-` means don't match all.
         
     | 
| 
      
 35 
     | 
    
         
            +
                def match_all? = @match_all
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/tree_sitter.rb
    ADDED
    
    | 
         @@ -0,0 +1,34 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'set'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            begin
         
     | 
| 
      
 6 
     | 
    
         
            +
              RUBY_VERSION =~ /(\d+\.\d+)/
         
     | 
| 
      
 7 
     | 
    
         
            +
              require "tree_sitter/#{Regexp.last_match(1)}/tree_sitter"
         
     | 
| 
      
 8 
     | 
    
         
            +
            rescue LoadError
         
     | 
| 
      
 9 
     | 
    
         
            +
              require 'tree_sitter/tree_sitter'
         
     | 
| 
      
 10 
     | 
    
         
            +
            end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            require 'tree_sitter/version'
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            require 'tree_sitter/mixins/language'
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            require 'tree_sitter/node'
         
     | 
| 
      
 17 
     | 
    
         
            +
            require 'tree_sitter/query'
         
     | 
| 
      
 18 
     | 
    
         
            +
            require 'tree_sitter/query_captures'
         
     | 
| 
      
 19 
     | 
    
         
            +
            require 'tree_sitter/query_cursor'
         
     | 
| 
      
 20 
     | 
    
         
            +
            require 'tree_sitter/query_match'
         
     | 
| 
      
 21 
     | 
    
         
            +
            require 'tree_sitter/query_matches'
         
     | 
| 
      
 22 
     | 
    
         
            +
            require 'tree_sitter/query_predicate'
         
     | 
| 
      
 23 
     | 
    
         
            +
            require 'tree_sitter/text_predicate_capture'
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
            # TreeSitter is a Ruby interface to the tree-sitter parsing library.
         
     | 
| 
      
 26 
     | 
    
         
            +
            module TreeSitter
         
     | 
| 
      
 27 
     | 
    
         
            +
              extend Mixins::Language
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
              class << self
         
     | 
| 
      
 30 
     | 
    
         
            +
                alias_method :lang, :language
         
     | 
| 
      
 31 
     | 
    
         
            +
              end
         
     | 
| 
      
 32 
     | 
    
         
            +
            end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
            ObjectSpace.define_finalizer(TreeSitter::Tree.class, proc { TreeSitter::Tree.finalizer })
         
     | 
| 
         @@ -0,0 +1,30 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            # typed: true
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module TreeStand
         
     | 
| 
      
 5 
     | 
    
         
            +
              # An experimental class to modify the AST. It re-runs the query on the
         
     | 
| 
      
 6 
     | 
    
         
            +
              # modified document every loop to ensure that the match is still valid.
         
     | 
| 
      
 7 
     | 
    
         
            +
              # @see TreeStand::Tree
         
     | 
| 
      
 8 
     | 
    
         
            +
              # @api experimental
         
     | 
| 
      
 9 
     | 
    
         
            +
              class AstModifier
         
     | 
| 
      
 10 
     | 
    
         
            +
                extend T::Sig
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                sig { params(tree: TreeStand::Tree).void }
         
     | 
| 
      
 13 
     | 
    
         
            +
                def initialize(tree)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @tree = tree
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                # @param query [String]
         
     | 
| 
      
 18 
     | 
    
         
            +
                # @yieldparam self [self]
         
     | 
| 
      
 19 
     | 
    
         
            +
                # @yieldparam match [TreeStand::Match]
         
     | 
| 
      
 20 
     | 
    
         
            +
                # @return [void]
         
     | 
| 
      
 21 
     | 
    
         
            +
                def on_match(query)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  matches = @tree.query(query)
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  while !matches.empty?
         
     | 
| 
      
 25 
     | 
    
         
            +
                    yield self, matches.first
         
     | 
| 
      
 26 
     | 
    
         
            +
                    matches = @tree.query(query)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
            end
         
     |