ruby_tree_sitter 1.2.0-x86_64-linux → 1.4.0-x86_64-linux
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 +4 -4
- data/README.md +5 -1
- data/ext/tree_sitter/query.c +7 -0
- data/ext/tree_sitter/query_cursor.c +18 -2
- data/lib/tree_sitter/helpers.rb +23 -0
- data/lib/tree_sitter/mixins/language.rb +161 -0
- data/lib/tree_sitter/node.rb +4 -2
- 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/tree_sitter.so +0 -0
- data/lib/tree_sitter/version.rb +1 -1
- data/lib/tree_sitter.rb +19 -5
- data/lib/tree_stand/node.rb +5 -13
- data/lib/tree_stand/parser.rb +14 -150
- metadata +11 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 701e16d45de25378b1f6e0b3e21b8c589cadb9f2f4e8af420a130d55637cd984
|
4
|
+
data.tar.gz: b1899ead2950b9ab9806076a0708c084221d3a8b59bbeb6a0e6848b57cbecf93
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e07d00239ccab6b2ddabff6d90076bd6c42c48b9f1a5b98f8cc8fc07a4688e5d36c357c2373c2d5629676b46a3b34fc694f3ab1e97fdd5614c9e6350c2ade28c
|
7
|
+
data.tar.gz: 0dbbac2900d41056e4ab37b8d298351b819c9ca42296b33962f6f087a773859afc7e7de666d3d0734f883702872f90d59bf66f7546bc43510f003b47c91e11fe
|
data/README.md
CHANGED
@@ -200,4 +200,8 @@ To See a full list of the ruby-specific APIs, see [here](lib/README.md).
|
|
200
200
|
|
201
201
|
## Sponsors
|
202
202
|
|
203
|
-
<
|
203
|
+
- <a href="https://faveod.com">https://faveod.com</a>
|
204
|
+
|
205
|
+
<a href="https://faveod.com">
|
206
|
+
<img src="img/faveod.jpg" width="66%" />
|
207
|
+
</a>
|
data/ext/tree_sitter/query.c
CHANGED
@@ -140,6 +140,13 @@ static VALUE query_initialize(VALUE self, VALUE language, VALUE source) {
|
|
140
140
|
SELF = res;
|
141
141
|
}
|
142
142
|
|
143
|
+
rb_iv_set(self, "@text_predicates", rb_ary_new());
|
144
|
+
rb_iv_set(self, "@property_predicates", rb_ary_new());
|
145
|
+
rb_iv_set(self, "@property_settings", rb_ary_new());
|
146
|
+
rb_iv_set(self, "@general_predicates", rb_ary_new());
|
147
|
+
|
148
|
+
rb_funcall(self, rb_intern("process"), 1, source);
|
149
|
+
|
143
150
|
return self;
|
144
151
|
}
|
145
152
|
|
@@ -49,7 +49,7 @@ DATA_FROM_VALUE(TSQueryCursor *, query_cursor)
|
|
49
49
|
*
|
50
50
|
* @return [QueryCursor]
|
51
51
|
*/
|
52
|
-
static VALUE
|
52
|
+
static VALUE query_cursor_exec_static(VALUE self, VALUE query, VALUE node) {
|
53
53
|
VALUE res = query_cursor_allocate(cQueryCursor);
|
54
54
|
query_cursor_t *query_cursor = unwrap(res);
|
55
55
|
ts_query_cursor_exec(query_cursor->data, value_to_query(query),
|
@@ -57,6 +57,21 @@ static VALUE query_cursor_exec(VALUE self, VALUE query, VALUE node) {
|
|
57
57
|
return res;
|
58
58
|
}
|
59
59
|
|
60
|
+
/**
|
61
|
+
* Start running a given query on a given node.
|
62
|
+
*
|
63
|
+
* @param query [Query]
|
64
|
+
* @param node [Node]
|
65
|
+
*
|
66
|
+
* @return [QueryCursor]
|
67
|
+
*/
|
68
|
+
static VALUE query_cursor_exec(VALUE self, VALUE query, VALUE node) {
|
69
|
+
query_cursor_t *query_cursor = unwrap(self);
|
70
|
+
ts_query_cursor_exec(query_cursor->data, value_to_query(query),
|
71
|
+
value_to_node(node));
|
72
|
+
return self;
|
73
|
+
}
|
74
|
+
|
60
75
|
/**
|
61
76
|
* Manage the maximum number of in-progress matches allowed by this query
|
62
77
|
* cursor.
|
@@ -190,13 +205,14 @@ void init_query_cursor(void) {
|
|
190
205
|
rb_define_alloc_func(cQueryCursor, query_cursor_allocate);
|
191
206
|
|
192
207
|
/* Module methods */
|
193
|
-
rb_define_module_function(cQueryCursor, "exec",
|
208
|
+
rb_define_module_function(cQueryCursor, "exec", query_cursor_exec_static, 2);
|
194
209
|
|
195
210
|
/* Class methods */
|
196
211
|
// Accessors
|
197
212
|
DECLARE_ACCESSOR(cQueryCursor, query_cursor, match_limit)
|
198
213
|
|
199
214
|
// Other
|
215
|
+
rb_define_method(cQueryCursor, "exec", query_cursor_exec, 2);
|
200
216
|
rb_define_method(cQueryCursor, "exceed_match_limit?",
|
201
217
|
query_cursor_did_exceed_match_limit, 0);
|
202
218
|
rb_define_method(cQueryCursor, "match_limit", query_cursor_get_match_limit,
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# splits an array like [rust](https://doc.rust-lang.org/std/primitive.slice.html#method.split)
|
4
|
+
def array_split_like_rust(array, &block)
|
5
|
+
return enum_for(__method__, array) if !block_given?
|
6
|
+
|
7
|
+
return [] if array.empty?
|
8
|
+
|
9
|
+
result = []
|
10
|
+
current_slice = []
|
11
|
+
|
12
|
+
array.each do |element|
|
13
|
+
if yield(element)
|
14
|
+
result << current_slice
|
15
|
+
current_slice = []
|
16
|
+
else
|
17
|
+
current_slice << element
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
result << current_slice
|
22
|
+
result
|
23
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TreeSitter
|
4
|
+
# A colon-separated list of paths pointing to directories that can contain parsers.
|
5
|
+
# Order matters.
|
6
|
+
# Takes precedence over default lookup paths.
|
7
|
+
ENV_PARSERS =
|
8
|
+
ENV['TREE_SITTER_PARSERS']
|
9
|
+
&.split(':')
|
10
|
+
&.map { |v| Pathname(v) }
|
11
|
+
.freeze
|
12
|
+
|
13
|
+
# The default paths we use to lookup parsers.
|
14
|
+
# Order matters.
|
15
|
+
LIBDIRS = [
|
16
|
+
'.vendor/parsers',
|
17
|
+
'.vendor/tree-sitter-parsers',
|
18
|
+
'vendor/parsers',
|
19
|
+
'vendor/tree-sitter-parsers',
|
20
|
+
'parsers',
|
21
|
+
'tree-sitter-parsers',
|
22
|
+
'/opt/local/lib',
|
23
|
+
'/opt/lib',
|
24
|
+
'/usr/local/lib',
|
25
|
+
'/usr/lib',
|
26
|
+
].map { |p| Pathname(p) }.freeze
|
27
|
+
|
28
|
+
# Mixins.
|
29
|
+
module Mixins
|
30
|
+
# Language Mixin.
|
31
|
+
module Language
|
32
|
+
# Load a language from configuration or default lookup paths.
|
33
|
+
#
|
34
|
+
# @example Load java from default paths
|
35
|
+
# # This will look for:
|
36
|
+
# #
|
37
|
+
# # .vendor/tree-sitter-parsers/(java/)?(libtree-sitter-)?java.{ext}
|
38
|
+
# # .vendor/parsers/(java/)?(libtree-sitter-)?java.{ext}
|
39
|
+
# # vendor/tree-sitter-parsers/(java/)?(libtree-sitter-)?java.{ext}
|
40
|
+
# # vendor/parsers/(java/)?(libtree-sitter-)?java.{ext}
|
41
|
+
# # parsers/(java/)?(libtree-sitter-)?java.{ext}
|
42
|
+
# # tree-sitter-parsers/(java/)?(libtree-sitter-)?java.{ext}
|
43
|
+
# # /opt/local/lib/(java/)?(libtree-sitter-)?java.{ext}
|
44
|
+
# # /opt/lib/(java/)?(libtree-sitter-)?java.{ext}
|
45
|
+
# # /usr/local/lib/(java/)?(libtree-sitter-)?java.{ext}
|
46
|
+
# # /usr/lib/(java/)?(libtree-sitter-)?java.{ext}
|
47
|
+
# #
|
48
|
+
# java = TreeSitter.language('java')
|
49
|
+
#
|
50
|
+
# @example (TreeStand) Load java from a configured path
|
51
|
+
# # This will look for:
|
52
|
+
# #
|
53
|
+
# # /my/path/(java/)?(libtree-sitter-)?java.{ext}
|
54
|
+
# #
|
55
|
+
# TreeStand.config.parser_path = '/my/path'
|
56
|
+
# java = TreeStand::Parser.language('java')
|
57
|
+
#
|
58
|
+
# @example (TreeStand) Load java from environment variables
|
59
|
+
# # This will look for:
|
60
|
+
# #
|
61
|
+
# # /my/forced/env/path/(java/)?(libtree-sitter-)?java.{ext}
|
62
|
+
# # /my/path/(java/)?(libtree-sitter-)?java.{ext}
|
63
|
+
# #
|
64
|
+
# # … and the same works for the default paths if `TreeStand.config.parser_path`
|
65
|
+
# # was `nil`
|
66
|
+
# ENV['TREE_SITTER_PARSERS'] = '/my/forced/env/path'
|
67
|
+
# TreeStand.config.parser_path = '/my/path'
|
68
|
+
# java = TreeStand::Parser.language('java')
|
69
|
+
#
|
70
|
+
# @param name [String] the name of the parser.
|
71
|
+
# This name is used to load the symbol from the compiled parser, replacing `-` with `_`.
|
72
|
+
#
|
73
|
+
# @return [TreeSitter:language] a language object to use in your parsers.
|
74
|
+
#
|
75
|
+
# @raise [RuntimeError] if the parser was not found.
|
76
|
+
#
|
77
|
+
# @see search_for_lib
|
78
|
+
def language(name)
|
79
|
+
lib = search_for_lib(name)
|
80
|
+
|
81
|
+
if lib.nil?
|
82
|
+
raise <<~MSG.chomp
|
83
|
+
Failed to load a parser for #{name}.
|
84
|
+
|
85
|
+
#{search_lib_message}
|
86
|
+
MSG
|
87
|
+
end
|
88
|
+
|
89
|
+
# We know that the bindings will accept `lib`, but I don't know how to tell sorbet
|
90
|
+
# the types in ext/tree_sitter where `load` is defined.
|
91
|
+
TreeSitter::Language.load(name.tr('-', '_'), T.unsafe(lib))
|
92
|
+
end
|
93
|
+
|
94
|
+
# The platform-specific extension of the parser.
|
95
|
+
# @return [String] `dylib` or `so` for mac or linux.
|
96
|
+
def ext
|
97
|
+
case Gem::Platform.local.os
|
98
|
+
in /darwin/ then 'dylib'
|
99
|
+
else 'so'
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
# The library directories we need to look into.
|
106
|
+
#
|
107
|
+
# @return [Array<Pathname>] the list of candidate places to use when searching for parsers.
|
108
|
+
#
|
109
|
+
# @see ENV_PARSERS
|
110
|
+
# @see LIBDIRS
|
111
|
+
def lib_dirs = [*TreeSitter::ENV_PARSERS, *TreeSitter::LIBDIRS]
|
112
|
+
|
113
|
+
# Lookup a parser by name.
|
114
|
+
#
|
115
|
+
# Precedence:
|
116
|
+
# 1. `Env['TREE_SITTER_PARSERS]`.
|
117
|
+
# 2. {TreeStand::Config#parser_path} if using {TreeStand}.
|
118
|
+
# 3. {LIBDIRS}.
|
119
|
+
#
|
120
|
+
# If a {TreeStand::Config#parser_path} is `nil`, {LIBDIRS} is used.
|
121
|
+
# If a {TreeStand::Config#parser_path} is a {::Pathname}, {LIBDIRS} is ignored.
|
122
|
+
def search_for_lib(name)
|
123
|
+
files = [
|
124
|
+
name,
|
125
|
+
"tree-sitter-#{name}",
|
126
|
+
"libtree-sitter-#{name}",
|
127
|
+
].map { |v| "#{v}.#{ext}" }
|
128
|
+
|
129
|
+
lib_dirs
|
130
|
+
.product(files)
|
131
|
+
.find do |dir, so|
|
132
|
+
path = dir / so
|
133
|
+
path = dir / name / so if !path.exist?
|
134
|
+
break path.expand_path if path.exist?
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Generates a string message on where parser lookup happens.
|
139
|
+
#
|
140
|
+
# @return [String] A pretty message.
|
141
|
+
def search_lib_message
|
142
|
+
indent = 2
|
143
|
+
pretty = ->(arr) {
|
144
|
+
if arr
|
145
|
+
arr
|
146
|
+
.compact
|
147
|
+
.map { |v| "#{' ' * indent}#{v.expand_path}" }
|
148
|
+
.join("\n")
|
149
|
+
end
|
150
|
+
}
|
151
|
+
<<~MSG.chomp
|
152
|
+
From ENV['TREE_SITTER_PARSERS']:
|
153
|
+
#{pretty.call(ENV_PARSERS)}
|
154
|
+
|
155
|
+
From Defaults:
|
156
|
+
#{pretty.call(lib_dirs)}
|
157
|
+
MSG
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
data/lib/tree_sitter/node.rb
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
module TreeSitter
|
4
4
|
# Node is a wrapper around a tree-sitter node.
|
5
5
|
class Node
|
6
|
+
include Enumerable
|
7
|
+
|
6
8
|
# @return [Array<Symbol>] the node's named fields
|
7
9
|
def fields
|
8
10
|
return @fields if @fields
|
@@ -87,10 +89,10 @@ module TreeSitter
|
|
87
89
|
# Iterate over a node's children.
|
88
90
|
#
|
89
91
|
# @yieldparam child [Node] the child
|
90
|
-
def each
|
92
|
+
def each(&_block)
|
91
93
|
return enum_for __method__ if !block_given?
|
92
94
|
|
93
|
-
(0...
|
95
|
+
(0...child_count).each do |i|
|
94
96
|
yield child(i)
|
95
97
|
end
|
96
98
|
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
|
Binary file
|
data/lib/tree_sitter/version.rb
CHANGED
data/lib/tree_sitter.rb
CHANGED
@@ -1,14 +1,28 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# TreeSitter is a Ruby interface to the tree-sitter parsing library.
|
4
|
-
module TreeSitter
|
5
|
-
end
|
6
|
-
|
7
3
|
require 'set'
|
8
4
|
|
5
|
+
require 'tree_sitter/tree_sitter'
|
9
6
|
require 'tree_sitter/version'
|
10
7
|
|
11
|
-
require 'tree_sitter/
|
8
|
+
require 'tree_sitter/mixins/language'
|
9
|
+
|
12
10
|
require 'tree_sitter/node'
|
11
|
+
require 'tree_sitter/query'
|
12
|
+
require 'tree_sitter/query_captures'
|
13
|
+
require 'tree_sitter/query_cursor'
|
14
|
+
require 'tree_sitter/query_match'
|
15
|
+
require 'tree_sitter/query_matches'
|
16
|
+
require 'tree_sitter/query_predicate'
|
17
|
+
require 'tree_sitter/text_predicate_capture'
|
18
|
+
|
19
|
+
# TreeSitter is a Ruby interface to the tree-sitter parsing library.
|
20
|
+
module TreeSitter
|
21
|
+
extend Mixins::Language
|
22
|
+
|
23
|
+
class << self
|
24
|
+
alias_method :lang, :language
|
25
|
+
end
|
26
|
+
end
|
13
27
|
|
14
28
|
ObjectSpace.define_finalizer(TreeSitter::Tree.class, proc { TreeSitter::Tree.finalizer })
|
data/lib/tree_stand/node.rb
CHANGED
@@ -97,19 +97,11 @@ module TreeStand
|
|
97
97
|
sig { params(query_string: String).returns(T::Array[T::Hash[String, TreeStand::Node]]) }
|
98
98
|
def query(query_string)
|
99
99
|
ts_query = TreeSitter::Query.new(@tree.parser.ts_language, query_string)
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
ts_match.captures.each do |ts_capture|
|
106
|
-
capture_name = ts_query.capture_name_for_id(ts_capture.index)
|
107
|
-
captures[capture_name] = TreeStand::Node.new(@tree, ts_capture.node)
|
108
|
-
end
|
109
|
-
|
110
|
-
matches << captures
|
111
|
-
end
|
112
|
-
matches
|
100
|
+
TreeSitter::QueryCursor
|
101
|
+
.new
|
102
|
+
.matches(ts_query, @tree.ts_tree.root_node, @tree.document)
|
103
|
+
.each_capture_hash
|
104
|
+
.map { |h| h.transform_values! { |n| TreeStand::Node.new(@tree, n) } }
|
113
105
|
end
|
114
106
|
|
115
107
|
# Returns the first captured node that matches the query string or nil if
|
data/lib/tree_stand/parser.rb
CHANGED
@@ -26,6 +26,7 @@ module TreeStand
|
|
26
26
|
# @see LIBDIRS
|
27
27
|
class Parser
|
28
28
|
extend T::Sig
|
29
|
+
extend TreeSitter::Mixins::Language
|
29
30
|
|
30
31
|
sig { returns(TreeSitter::Language) }
|
31
32
|
attr_reader :ts_language
|
@@ -33,156 +34,6 @@ module TreeStand
|
|
33
34
|
sig { returns(TreeSitter::Parser) }
|
34
35
|
attr_reader :ts_parser
|
35
36
|
|
36
|
-
# A colon-seprated list of paths pointing to directories that can contain parsers.
|
37
|
-
# Order matters.
|
38
|
-
# Takes precedence over default lookup paths.
|
39
|
-
ENV_PARSERS =
|
40
|
-
ENV['TREE_SITTER_PARSERS']
|
41
|
-
&.split(':')
|
42
|
-
&.map { |v| Pathname(v) }
|
43
|
-
.freeze
|
44
|
-
|
45
|
-
# The default paths we use to lookup parsers when no specific
|
46
|
-
# {TreeStand::Config#parser_path} is {nil}.
|
47
|
-
# Order matters.
|
48
|
-
LIBDIRS = [
|
49
|
-
'tree-sitter-parsers',
|
50
|
-
'/opt/local/lib',
|
51
|
-
'/opt/lib',
|
52
|
-
'/usr/local/lib',
|
53
|
-
'/usr/lib',
|
54
|
-
].map { |p| Pathname(p) }.freeze
|
55
|
-
|
56
|
-
# The library directories we need to look into.
|
57
|
-
#
|
58
|
-
# @return [Array<Pathname>] the list of candidate places to use when searching for parsers.
|
59
|
-
#
|
60
|
-
# @see ENV_PARSERS
|
61
|
-
# @see LIBDIRS
|
62
|
-
sig { returns(T::Array[Pathname]) }
|
63
|
-
def self.lib_dirs = [
|
64
|
-
*ENV_PARSERS,
|
65
|
-
*(TreeStand.config.parser_path ? [TreeStand.config.parser_path] : LIBDIRS),
|
66
|
-
].compact
|
67
|
-
|
68
|
-
# The platform-specific extension of the parser.
|
69
|
-
# @return [String] `dylib` or `so` for mac or linux.
|
70
|
-
sig { returns(String) }
|
71
|
-
def self.ext
|
72
|
-
case Gem::Platform.local.os
|
73
|
-
in /darwin/ then 'dylib'
|
74
|
-
else 'so'
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
# Lookup a parser by name.
|
79
|
-
#
|
80
|
-
# Precedence:
|
81
|
-
# 1. `Env['TREE_SITTER_PARSERS]`
|
82
|
-
# 2. {TreeStand::Config#parser_path}
|
83
|
-
# 3. {LIBDIRS}
|
84
|
-
#
|
85
|
-
# If a {TreeStand::Config#parser_path} is `nil`, {LIBDIRS} is used.
|
86
|
-
# If a {TreeStand::Config#parser_path} is a {::Pathname}, {LIBDIRS} is ignored.
|
87
|
-
sig { params(name: String).returns(T.nilable(Pathname)) }
|
88
|
-
def self.search_for_lib(name)
|
89
|
-
files = [
|
90
|
-
name,
|
91
|
-
"tree-sitter-#{name}",
|
92
|
-
"libtree-sitter-#{name}",
|
93
|
-
].map { |v| "#{v}.#{ext}" }
|
94
|
-
|
95
|
-
res = lib_dirs
|
96
|
-
.product(files)
|
97
|
-
.find do |dir, so|
|
98
|
-
path = dir / so
|
99
|
-
path = dir / name / so if !path.exist?
|
100
|
-
break path.expand_path if path.exist?
|
101
|
-
end
|
102
|
-
res.is_a?(Array) ? nil : res
|
103
|
-
end
|
104
|
-
|
105
|
-
# Generates a string message on where parser lookup happens.
|
106
|
-
#
|
107
|
-
# @return [String] A pretty message.
|
108
|
-
sig { returns(String) }
|
109
|
-
def self.search_lib_message
|
110
|
-
indent = 2
|
111
|
-
pretty = ->(arr) {
|
112
|
-
if arr
|
113
|
-
arr
|
114
|
-
.compact
|
115
|
-
.map { |v| "#{' ' * indent}#{v.expand_path}" }
|
116
|
-
.join("\n")
|
117
|
-
end
|
118
|
-
}
|
119
|
-
<<~MSG
|
120
|
-
From ENV['TREE_SITTER_PARSERS']:
|
121
|
-
#{pretty.call(ENV_PARSERS)}
|
122
|
-
|
123
|
-
From TreeStand.config.parser_path:
|
124
|
-
#{pretty.call([TreeStand.config.parser_path])}
|
125
|
-
|
126
|
-
From Defaults:
|
127
|
-
#{pretty.call(LIBDIRS)}
|
128
|
-
MSG
|
129
|
-
end
|
130
|
-
|
131
|
-
# Load a language from configuration or default lookup paths.
|
132
|
-
#
|
133
|
-
# @example Load java from default paths
|
134
|
-
# # This will look for:
|
135
|
-
# #
|
136
|
-
# # tree-sitter-parsers/(java/)?(libtree-sitter-)?java.{ext}
|
137
|
-
# # /opt/local/lib/(java/)?(libtree-sitter-)?java.{ext}
|
138
|
-
# # /opt/lib/(java/)?(libtree-sitter-)?java.{ext}
|
139
|
-
# # /usr/local/lib/(java/)?(libtree-sitter-)?java.{ext}
|
140
|
-
# # /usr/lib/(java/)?(libtree-sitter-)?java.{ext}
|
141
|
-
# #
|
142
|
-
# java = TreeStand::Parser.language('java')
|
143
|
-
#
|
144
|
-
# @example Load java from a configured path
|
145
|
-
# # This will look for:
|
146
|
-
# #
|
147
|
-
# # /my/path/(java/)?(libtree-sitter-)?java.{ext}
|
148
|
-
# #
|
149
|
-
# TreeStand.config.parser_path = '/my/path'
|
150
|
-
# java = TreeStand::Parser.language('java')
|
151
|
-
#
|
152
|
-
# @example Load java from environment variables
|
153
|
-
# # This will look for:
|
154
|
-
# #
|
155
|
-
# # /my/forced/env/path/(java/)?(libtree-sitter-)?java.{ext}
|
156
|
-
# # /my/path/(java/)?(libtree-sitter-)?java.{ext}
|
157
|
-
# #
|
158
|
-
# # … and the same works for the default paths if `TreeStand.config.parser_path`
|
159
|
-
# # was `nil`
|
160
|
-
# ENV['TREE_SITTER_PARSERS'] = '/my/forced/env/path'
|
161
|
-
# TreeStand.config.parser_path = '/my/path'
|
162
|
-
# java = TreeStand::Parser.language('java')
|
163
|
-
#
|
164
|
-
# @param name [String] the name of the parser.
|
165
|
-
# This name is used to load the symbol from the compiled parser, replacing `-` with `_`.
|
166
|
-
# @return [TreeSitter:language] a language object to use in your parsers.
|
167
|
-
# @raise [RuntimeError] if the parser was not found.
|
168
|
-
# @see search_for_lib
|
169
|
-
sig { params(name: String).returns(TreeSitter::Language) }
|
170
|
-
def self.language(name)
|
171
|
-
lib = search_for_lib(name)
|
172
|
-
|
173
|
-
if lib.nil?
|
174
|
-
raise <<~MSG
|
175
|
-
Failed to load a parser for #{name}.
|
176
|
-
|
177
|
-
#{search_lib_message}
|
178
|
-
MSG
|
179
|
-
end
|
180
|
-
|
181
|
-
# We know that the bindings will accept `lib`, but I don't know how to tell sorbet
|
182
|
-
# the types in ext/tree_sitter where `load` is defined.
|
183
|
-
TreeSitter::Language.load(name.gsub(/-/, '_'), T.unsafe(lib))
|
184
|
-
end
|
185
|
-
|
186
37
|
# @param language [String]
|
187
38
|
sig { params(language: String).void }
|
188
39
|
def initialize(language)
|
@@ -192,6 +43,19 @@ module TreeStand
|
|
192
43
|
end
|
193
44
|
end
|
194
45
|
|
46
|
+
# The library directories we need to look into.
|
47
|
+
#
|
48
|
+
# @return [Array<Pathname>] the list of candidate places to use when searching for parsers.
|
49
|
+
#
|
50
|
+
# @see ENV_PARSERS
|
51
|
+
# @see LIBDIRS
|
52
|
+
def self.lib_dirs
|
53
|
+
[
|
54
|
+
*TreeSitter::ENV_PARSERS,
|
55
|
+
*(TreeStand.config.parser_path ? [TreeStand.config.parser_path] : TreeSitter::LIBDIRS),
|
56
|
+
]
|
57
|
+
end
|
58
|
+
|
195
59
|
# Parse the provided document with the TreeSitter parser.
|
196
60
|
# @param tree [TreeStand::Tree, nil] providing the old tree will allow the
|
197
61
|
# parser to take advantage of incremental parsing and improve performance
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_tree_sitter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: x86_64-linux
|
6
6
|
authors:
|
7
7
|
- Firas al-Khalil
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2024-
|
12
|
+
date: 2024-07-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sorbet-runtime
|
@@ -74,7 +74,16 @@ files:
|
|
74
74
|
- ext/tree_sitter/tree_sitter.c
|
75
75
|
- ext/tree_sitter/tree_sitter.h
|
76
76
|
- lib/tree_sitter.rb
|
77
|
+
- lib/tree_sitter/helpers.rb
|
78
|
+
- lib/tree_sitter/mixins/language.rb
|
77
79
|
- lib/tree_sitter/node.rb
|
80
|
+
- lib/tree_sitter/query.rb
|
81
|
+
- lib/tree_sitter/query_captures.rb
|
82
|
+
- lib/tree_sitter/query_cursor.rb
|
83
|
+
- lib/tree_sitter/query_match.rb
|
84
|
+
- lib/tree_sitter/query_matches.rb
|
85
|
+
- lib/tree_sitter/query_predicate.rb
|
86
|
+
- lib/tree_sitter/text_predicate_capture.rb
|
78
87
|
- lib/tree_sitter/tree_sitter.so
|
79
88
|
- lib/tree_sitter/version.rb
|
80
89
|
- lib/tree_stand.rb
|