ruby_tree_sitter 1.1.0-arm64-darwin-22
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +22 -0
- data/README.md +199 -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 +618 -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 +282 -0
- data/ext/tree_sitter/query_capture.c +28 -0
- data/ext/tree_sitter/query_cursor.c +215 -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 +121 -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/node.rb +197 -0
- data/lib/tree_sitter/tree_sitter.bundle +0 -0
- data/lib/tree_sitter/version.rb +8 -0
- data/lib/tree_sitter.rb +14 -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 +13 -0
- data/lib/tree_stand/node.rb +224 -0
- data/lib/tree_stand/parser.rb +67 -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 +35 -0
- metadata +124 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
#include "tree_sitter.h"
|
2
|
+
|
3
|
+
extern VALUE mTreeSitter;
|
4
|
+
|
5
|
+
VALUE cRange;
|
6
|
+
|
7
|
+
DATA_WRAP(Range, range)
|
8
|
+
DATA_DEFINE_ACCESSOR(range, start_point, new_point_by_val, value_to_point)
|
9
|
+
DATA_DEFINE_ACCESSOR(range, end_point, new_point_by_val, value_to_point)
|
10
|
+
DATA_DEFINE_ACCESSOR(range, start_byte, UINT2NUM, NUM2UINT)
|
11
|
+
DATA_DEFINE_ACCESSOR(range, end_byte, UINT2NUM, NUM2UINT)
|
12
|
+
|
13
|
+
static VALUE range_inspect(VALUE self) {
|
14
|
+
range_t *range = unwrap(self);
|
15
|
+
return rb_sprintf("{start_point= %+" PRIsVALUE ", end_point=%+" PRIsVALUE
|
16
|
+
", start_byte=%i, end_byte=%i}",
|
17
|
+
new_point_by_val(range->data.start_point),
|
18
|
+
new_point_by_val(range->data.end_point),
|
19
|
+
range->data.start_byte, range->data.end_byte);
|
20
|
+
}
|
21
|
+
|
22
|
+
void init_range(void) {
|
23
|
+
cRange = rb_define_class_under(mTreeSitter, "Range", rb_cObject);
|
24
|
+
|
25
|
+
rb_define_alloc_func(cRange, range_allocate);
|
26
|
+
|
27
|
+
/* Class methods */
|
28
|
+
DECLARE_ACCESSOR(cRange, range, start_point)
|
29
|
+
DECLARE_ACCESSOR(cRange, range, end_point)
|
30
|
+
DECLARE_ACCESSOR(cRange, range, start_byte)
|
31
|
+
DECLARE_ACCESSOR(cRange, range, end_byte)
|
32
|
+
|
33
|
+
rb_define_method(cRange, "inspect", range_inspect, 0);
|
34
|
+
rb_define_method(cRange, "to_s", range_inspect, 0);
|
35
|
+
}
|
@@ -0,0 +1,121 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../lib/tree_sitter/version'
|
4
|
+
|
5
|
+
module TreeSitter
|
6
|
+
# Fetches tree-sitter sources.
|
7
|
+
class Repo
|
8
|
+
attr_reader :exe, :src, :url, :version
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@version = TREESITTER_VERSION
|
12
|
+
|
13
|
+
# `tree-sitter-@version` is the name produced by tagged releases of sources
|
14
|
+
# by git, so we use it everywhere, including when cloning from git.
|
15
|
+
@src = Pathname.pwd / "tree-sitter-#{@version}"
|
16
|
+
|
17
|
+
@url = {
|
18
|
+
git: 'https://github.com/tree-sitter/tree-sitter',
|
19
|
+
tar: "https://github.com/tree-sitter/tree-sitter/archive/refs/tags/v#{@version}.tar.gz",
|
20
|
+
zip: "https://github.com/tree-sitter/tree-sitter/archive/refs/tags/v#{@version}.zip",
|
21
|
+
}
|
22
|
+
|
23
|
+
@exe = {}
|
24
|
+
%i[curl git tar wget zip].each do |cmd|
|
25
|
+
@exe[cmd] = find_executable(cmd.to_s)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def compile
|
30
|
+
# We need to clean because the same folder is used over and over
|
31
|
+
# by rake-compiler-dock
|
32
|
+
sh "cd #{src} && make clean && make"
|
33
|
+
end
|
34
|
+
|
35
|
+
def exe?(name)
|
36
|
+
@exe[name]
|
37
|
+
end
|
38
|
+
|
39
|
+
def extract?
|
40
|
+
!exe.filter { |k, v| %i[tar zip].include?(k) && v }.empty?
|
41
|
+
end
|
42
|
+
|
43
|
+
def download
|
44
|
+
# TODO: should we force re-download? Maybe with a flag?
|
45
|
+
return true if Dir.exist? src
|
46
|
+
|
47
|
+
res = false
|
48
|
+
%w[git curl wget].each do |cmd|
|
49
|
+
res =
|
50
|
+
if find_executable(cmd)
|
51
|
+
send("sources_from_#{cmd}")
|
52
|
+
else
|
53
|
+
false
|
54
|
+
end
|
55
|
+
break if res
|
56
|
+
end
|
57
|
+
|
58
|
+
res
|
59
|
+
end
|
60
|
+
|
61
|
+
def include_and_lib_dirs
|
62
|
+
[[src / 'lib' / 'include'], [src.to_s]]
|
63
|
+
end
|
64
|
+
|
65
|
+
def keep_static_lib
|
66
|
+
src
|
67
|
+
.children
|
68
|
+
.filter { |f| /\.(dylib|so)/ =~ f.basename.to_s }
|
69
|
+
.each(&:unlink)
|
70
|
+
end
|
71
|
+
|
72
|
+
def sh(cmd)
|
73
|
+
return if system(cmd)
|
74
|
+
|
75
|
+
abort <<~MSG
|
76
|
+
|
77
|
+
Failed to run: #{cmd}
|
78
|
+
|
79
|
+
exiting …
|
80
|
+
|
81
|
+
MSG
|
82
|
+
end
|
83
|
+
|
84
|
+
def sources_from_curl
|
85
|
+
return false if !exe?(:curl) || !extract?
|
86
|
+
|
87
|
+
if exe?(:tar)
|
88
|
+
sh "curl -L #{url[:tar]} -o tree-sitter-v#{version}.tar.gz"
|
89
|
+
sh "tar -xf tree-sitter-v#{version}.tar.gz"
|
90
|
+
elsif exe?(:zip)
|
91
|
+
sh "curl -L #{url[:zip]} -o tree-sitter-v#{version}.zip"
|
92
|
+
sh "unzip -q tree-sitter-v#{version}.zip"
|
93
|
+
end
|
94
|
+
|
95
|
+
true
|
96
|
+
end
|
97
|
+
|
98
|
+
def sources_from_git
|
99
|
+
return false if !exe?(:git)
|
100
|
+
|
101
|
+
sh "git clone #{url[:git]} #{src}"
|
102
|
+
sh "cd #{src} && git checkout tags/v#{version}"
|
103
|
+
|
104
|
+
true
|
105
|
+
end
|
106
|
+
|
107
|
+
def sources_from_wget
|
108
|
+
return false if !exe?(:wget) || !extract?
|
109
|
+
|
110
|
+
if exe?(:tar)
|
111
|
+
sh "wget #{url[:tar]} -O tree-sitter-v#{version}.tar.gz"
|
112
|
+
sh "tar -xf tree-sitter-v#{version}.tar.gz"
|
113
|
+
elsif exe?(:zip)
|
114
|
+
sh "wget #{url[:zip]} -O tree-sitter-v#{version}.zip"
|
115
|
+
sh "unzip -q tree-sitter-v#{version}.zip"
|
116
|
+
end
|
117
|
+
|
118
|
+
true
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
#include "tree_sitter.h"
|
2
|
+
|
3
|
+
extern VALUE mTreeSitter;
|
4
|
+
|
5
|
+
VALUE mSymbolType;
|
6
|
+
|
7
|
+
const char *regular = "regular";
|
8
|
+
const char *anonymous = "anonymous";
|
9
|
+
const char *auxiliary = "auxiliary";
|
10
|
+
|
11
|
+
TSSymbolType value_to_symbol_type(VALUE symbol_type) {
|
12
|
+
VALUE sym = SYM2ID(symbol_type);
|
13
|
+
VALUE anon = rb_const_get_at(mSymbolType, rb_intern(anonymous));
|
14
|
+
VALUE aux = rb_const_get_at(mSymbolType, rb_intern(auxiliary));
|
15
|
+
|
16
|
+
// NOTE: should we emit a warning instead of defaulting to regular?
|
17
|
+
if (sym == anon) {
|
18
|
+
return TSSymbolTypeAnonymous;
|
19
|
+
} else if (sym == aux) {
|
20
|
+
return TSSymbolTypeAuxiliary;
|
21
|
+
} else {
|
22
|
+
return TSSymbolTypeRegular;
|
23
|
+
}
|
24
|
+
}
|
25
|
+
|
26
|
+
VALUE new_symbol_type(TSSymbolType symbol_type) {
|
27
|
+
switch (symbol_type) {
|
28
|
+
case TSSymbolTypeRegular:
|
29
|
+
return ID2SYM(rb_intern(regular));
|
30
|
+
case TSSymbolTypeAnonymous:
|
31
|
+
return ID2SYM(rb_intern(anonymous));
|
32
|
+
case TSSymbolTypeAuxiliary:
|
33
|
+
return ID2SYM(rb_intern(auxiliary));
|
34
|
+
default:
|
35
|
+
return ID2SYM(rb_intern("this_should_never_get_reached"));
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
void init_symbol_type(void) {
|
40
|
+
mSymbolType = rb_define_module_under(mTreeSitter, "SymbolType");
|
41
|
+
|
42
|
+
/* Constants */
|
43
|
+
rb_define_const(mSymbolType, "REGULAR", ID2SYM(rb_intern(regular)));
|
44
|
+
rb_define_const(mSymbolType, "ANONYMOUS", ID2SYM(rb_intern(anonymous)));
|
45
|
+
rb_define_const(mSymbolType, "AUXILIARY", ID2SYM(rb_intern(auxiliary)));
|
46
|
+
}
|
@@ -0,0 +1,234 @@
|
|
1
|
+
#include "tree_sitter.h"
|
2
|
+
|
3
|
+
extern VALUE mTreeSitter;
|
4
|
+
|
5
|
+
VALUE cTree;
|
6
|
+
|
7
|
+
int tree_rc_free(const TSTree *tree) {
|
8
|
+
VALUE ptr = ULONG2NUM((uintptr_t)tree);
|
9
|
+
VALUE rc = rb_cv_get(cTree, "@@rc");
|
10
|
+
VALUE val = rb_hash_lookup(rc, ptr);
|
11
|
+
|
12
|
+
if (!NIL_P(val)) {
|
13
|
+
unsigned int count = NUM2UINT(val);
|
14
|
+
--count;
|
15
|
+
if (count < 1) {
|
16
|
+
rb_hash_delete(rc, ptr);
|
17
|
+
ts_tree_delete((TSTree *)tree);
|
18
|
+
return 1;
|
19
|
+
} else {
|
20
|
+
rb_hash_aset(rc, ptr, ULONG2NUM(count));
|
21
|
+
return 0;
|
22
|
+
}
|
23
|
+
} else {
|
24
|
+
return 1;
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
void tree_rc_new(const TSTree *tree) {
|
29
|
+
VALUE ptr = ULONG2NUM((uintptr_t)tree);
|
30
|
+
VALUE rc = rb_cv_get(cTree, "@@rc");
|
31
|
+
VALUE val = rb_hash_lookup(rc, ptr);
|
32
|
+
|
33
|
+
if (NIL_P(val)) {
|
34
|
+
rb_hash_aset(rc, ptr, UINT2NUM(1));
|
35
|
+
} else {
|
36
|
+
rb_hash_aset(rc, ptr, UINT2NUM(NUM2UINT(val) + 1));
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
DATA_TYPE(TSTree *, tree)
|
41
|
+
static void tree_free(void *ptr) {
|
42
|
+
tree_t *type = (tree_t *)ptr;
|
43
|
+
if (tree_rc_free(type->data)) {
|
44
|
+
xfree(ptr);
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
DATA_MEMSIZE(tree)
|
49
|
+
DATA_DECLARE_DATA_TYPE(tree)
|
50
|
+
DATA_ALLOCATE(tree)
|
51
|
+
DATA_UNWRAP(tree)
|
52
|
+
|
53
|
+
VALUE new_tree(TSTree *ptr) {
|
54
|
+
if (ptr == NULL) {
|
55
|
+
return Qnil;
|
56
|
+
}
|
57
|
+
VALUE res = tree_allocate(cTree);
|
58
|
+
tree_t *type = unwrap(res);
|
59
|
+
type->data = ptr;
|
60
|
+
tree_rc_new(ptr);
|
61
|
+
return res;
|
62
|
+
}
|
63
|
+
|
64
|
+
DATA_FROM_VALUE(TSTree *, tree)
|
65
|
+
|
66
|
+
/**
|
67
|
+
* Compare an old edited syntax tree to a new syntax tree representing the same
|
68
|
+
* document, returning an array of ranges whose syntactic structure has changed.
|
69
|
+
*
|
70
|
+
* For this to work correctly, the old syntax tree must have been edited such
|
71
|
+
* that its ranges match up to the new tree. Generally, you'll want to call
|
72
|
+
* this function right after calling one of the {Parser#parse} functions.
|
73
|
+
* You need to pass the old tree that was passed to parse, as well as the new
|
74
|
+
* tree that was returned from that function.
|
75
|
+
*
|
76
|
+
* @param old_tree [Tree]
|
77
|
+
* @param new_tree [Tree]
|
78
|
+
*
|
79
|
+
* @return [Array<Range>]
|
80
|
+
*/
|
81
|
+
static VALUE tree_changed_ranges(VALUE _self, VALUE old_tree, VALUE new_tree) {
|
82
|
+
TSTree *old = unwrap(old_tree)->data;
|
83
|
+
TSTree *new = unwrap(new_tree)->data;
|
84
|
+
uint32_t length;
|
85
|
+
TSRange *ranges = ts_tree_get_changed_ranges(old, new, &length);
|
86
|
+
VALUE res = rb_ary_new_capa(length);
|
87
|
+
|
88
|
+
for (uint32_t i = 0; i < length; i++) {
|
89
|
+
rb_ary_push(res, new_range(&ranges[i]));
|
90
|
+
}
|
91
|
+
|
92
|
+
if (ranges) {
|
93
|
+
free(ranges);
|
94
|
+
}
|
95
|
+
|
96
|
+
return res;
|
97
|
+
}
|
98
|
+
|
99
|
+
static VALUE tree_finalizer(VALUE _self) {
|
100
|
+
VALUE rc = rb_cv_get(cTree, "@@rc");
|
101
|
+
VALUE keys = rb_funcall(rc, rb_intern("keys"), 0);
|
102
|
+
long len = RARRAY_LEN(keys);
|
103
|
+
|
104
|
+
for (long i = 0; i < len; ++i) {
|
105
|
+
VALUE curr = RARRAY_AREF(keys, i);
|
106
|
+
unsigned int val = NUM2UINT(rb_hash_lookup(rc, curr));
|
107
|
+
if (val > 0) {
|
108
|
+
ts_tree_delete((TSTree *)NUM2ULONG(curr));
|
109
|
+
}
|
110
|
+
|
111
|
+
rb_hash_delete(rc, curr);
|
112
|
+
}
|
113
|
+
|
114
|
+
return Qnil;
|
115
|
+
}
|
116
|
+
|
117
|
+
/**
|
118
|
+
* Create a shallow copy of the syntax tree. This is very fast.
|
119
|
+
*
|
120
|
+
* You need to copy a syntax tree in order to use it on more than one thread at
|
121
|
+
* a time, as syntax trees are not thread safe.
|
122
|
+
*
|
123
|
+
* @return [Tree]
|
124
|
+
*/
|
125
|
+
static VALUE tree_copy(VALUE self) { return new_tree(ts_tree_copy(SELF)); }
|
126
|
+
|
127
|
+
/**
|
128
|
+
* Edit the syntax tree to keep it in sync with source code that has been
|
129
|
+
* edited.
|
130
|
+
*
|
131
|
+
* You must describe the edit both in terms of byte offsets and in terms of
|
132
|
+
* (row, column) coordinates.
|
133
|
+
*
|
134
|
+
* @param edit [InputEdit]
|
135
|
+
*
|
136
|
+
* @return [nil]
|
137
|
+
*/
|
138
|
+
static VALUE tree_edit(VALUE self, VALUE edit) {
|
139
|
+
TSInputEdit in = value_to_input_edit(edit);
|
140
|
+
ts_tree_edit(SELF, &in);
|
141
|
+
return Qnil;
|
142
|
+
}
|
143
|
+
|
144
|
+
/**
|
145
|
+
* Get the array of included ranges that was used to parse the syntax tree.
|
146
|
+
*
|
147
|
+
* @return [Array<Range>]
|
148
|
+
*/
|
149
|
+
static VALUE included_ranges(VALUE self) {
|
150
|
+
uint32_t length;
|
151
|
+
const TSRange *ranges = ts_tree_included_ranges(SELF, &length);
|
152
|
+
VALUE res = rb_ary_new_capa(length);
|
153
|
+
for (uint32_t i = 0; i < length; i++) {
|
154
|
+
rb_ary_push(res, new_range(&ranges[i]));
|
155
|
+
}
|
156
|
+
return res;
|
157
|
+
}
|
158
|
+
|
159
|
+
/**
|
160
|
+
* Get the language that was used to parse the syntax tree.
|
161
|
+
*
|
162
|
+
* @return [Language]
|
163
|
+
*/
|
164
|
+
static VALUE tree_language(VALUE self) {
|
165
|
+
return new_language(ts_tree_language(SELF));
|
166
|
+
}
|
167
|
+
|
168
|
+
/**
|
169
|
+
* Write a DOT graph describing the syntax tree to the given file.
|
170
|
+
*
|
171
|
+
* @param file [String]
|
172
|
+
*
|
173
|
+
* @return [nil]
|
174
|
+
*/
|
175
|
+
static VALUE tree_print_dot_graph(VALUE self, VALUE file) {
|
176
|
+
Check_Type(file, T_STRING);
|
177
|
+
char *path = StringValueCStr(file);
|
178
|
+
int fd = open(path, O_CREAT | O_WRONLY | O_TRUNC,
|
179
|
+
S_IWUSR | S_IRUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
180
|
+
if (fd < 0) {
|
181
|
+
rb_raise(rb_eRuntimeError, "Could not open file `%s'.\nReason:\n%s", path,
|
182
|
+
strerror(fd));
|
183
|
+
return Qnil;
|
184
|
+
}
|
185
|
+
ts_tree_print_dot_graph(SELF, fd);
|
186
|
+
close(fd);
|
187
|
+
return Qnil;
|
188
|
+
}
|
189
|
+
|
190
|
+
/**
|
191
|
+
* Get the root node of the syntax tree.
|
192
|
+
*
|
193
|
+
* @return [Node]
|
194
|
+
*/
|
195
|
+
static VALUE tree_root_node(VALUE self) {
|
196
|
+
return new_node_by_val(ts_tree_root_node(SELF));
|
197
|
+
}
|
198
|
+
|
199
|
+
/**
|
200
|
+
* Get the root node of the syntax tree, but with its position
|
201
|
+
* shifted forward by the given offset.
|
202
|
+
*
|
203
|
+
* @return [Node]
|
204
|
+
*/
|
205
|
+
static VALUE tree_root_node_with_offset(VALUE self, VALUE offset_bytes,
|
206
|
+
VALUE offset_extent) {
|
207
|
+
uint32_t bytes = NUM2UINT(offset_bytes);
|
208
|
+
TSPoint extent = value_to_point(offset_extent);
|
209
|
+
return new_node_by_val(ts_tree_root_node_with_offset(SELF, bytes, extent));
|
210
|
+
}
|
211
|
+
|
212
|
+
void init_tree(void) {
|
213
|
+
cTree = rb_define_class_under(mTreeSitter, "Tree", rb_cObject);
|
214
|
+
|
215
|
+
rb_undef_alloc_func(cTree);
|
216
|
+
|
217
|
+
/* Module methods */
|
218
|
+
rb_define_module_function(cTree, "changed_ranges", tree_changed_ranges, 2);
|
219
|
+
rb_define_module_function(cTree, "finalizer", tree_finalizer, 0);
|
220
|
+
|
221
|
+
/* Class methods */
|
222
|
+
rb_define_method(cTree, "copy", tree_copy, 0);
|
223
|
+
rb_define_method(cTree, "edit", tree_edit, 1);
|
224
|
+
rb_define_method(cTree, "included_ranges", included_ranges, 0);
|
225
|
+
rb_define_method(cTree, "language", tree_language, 0);
|
226
|
+
rb_define_method(cTree, "print_dot_graph", tree_print_dot_graph, 1);
|
227
|
+
rb_define_method(cTree, "root_node", tree_root_node, 0);
|
228
|
+
rb_define_method(cTree, "root_node_with_offset", tree_root_node_with_offset,
|
229
|
+
2);
|
230
|
+
|
231
|
+
// Reference-count created trees
|
232
|
+
VALUE rc = rb_hash_new();
|
233
|
+
rb_cv_set(cTree, "@@rc", rc);
|
234
|
+
}
|
@@ -0,0 +1,269 @@
|
|
1
|
+
#include "tree_sitter.h"
|
2
|
+
|
3
|
+
extern VALUE mTreeSitter;
|
4
|
+
|
5
|
+
VALUE cTreeCursor;
|
6
|
+
|
7
|
+
DATA_TYPE(TSTreeCursor, tree_cursor)
|
8
|
+
static void tree_cursor_free(void *ptr) {
|
9
|
+
tree_cursor_t *type = (tree_cursor_t *)ptr;
|
10
|
+
ts_tree_cursor_delete(&type->data);
|
11
|
+
xfree(ptr);
|
12
|
+
}
|
13
|
+
DATA_MEMSIZE(tree_cursor)
|
14
|
+
DATA_DECLARE_DATA_TYPE(tree_cursor)
|
15
|
+
DATA_ALLOCATE(tree_cursor)
|
16
|
+
DATA_UNWRAP(tree_cursor)
|
17
|
+
DATA_NEW(cTreeCursor, TSTreeCursor, tree_cursor)
|
18
|
+
DATA_FROM_VALUE(TSTreeCursor, tree_cursor)
|
19
|
+
|
20
|
+
/**
|
21
|
+
* Safely copy a tree cursor.
|
22
|
+
*
|
23
|
+
* @return [TreeCursor]
|
24
|
+
*/
|
25
|
+
static VALUE tree_cursor_copy(VALUE self) {
|
26
|
+
VALUE res = tree_cursor_allocate(cTreeCursor);
|
27
|
+
tree_cursor_t *ptr = unwrap(res);
|
28
|
+
ptr->data = ts_tree_cursor_copy(&SELF);
|
29
|
+
return res;
|
30
|
+
}
|
31
|
+
|
32
|
+
/**
|
33
|
+
* Get the depth of the cursor's current node relative to the original
|
34
|
+
* node that the cursor was constructed with.
|
35
|
+
*
|
36
|
+
* @return [Integer]
|
37
|
+
*/
|
38
|
+
static VALUE tree_cursor_current_depth(VALUE self) {
|
39
|
+
return UINT2NUM(ts_tree_cursor_current_depth(&SELF));
|
40
|
+
}
|
41
|
+
|
42
|
+
/**
|
43
|
+
* Get the index of the cursor's current node out of all of the
|
44
|
+
* descendants of the original node that the cursor was constructed with.
|
45
|
+
*
|
46
|
+
* @return [Integer]
|
47
|
+
*/
|
48
|
+
static VALUE tree_cursor_current_descendant_index(VALUE self) {
|
49
|
+
return UINT2NUM(ts_tree_cursor_current_descendant_index(&SELF));
|
50
|
+
}
|
51
|
+
|
52
|
+
/**
|
53
|
+
* Get the field id of the tree cursor's current node.
|
54
|
+
*
|
55
|
+
* This returns zero if the current node doesn't have a field.
|
56
|
+
*
|
57
|
+
* @see Node#child_by_field_id
|
58
|
+
* @see Node#field_id_for_name
|
59
|
+
*
|
60
|
+
* @return [Integer]
|
61
|
+
*/
|
62
|
+
static VALUE tree_cursor_current_field_id(VALUE self) {
|
63
|
+
return UINT2NUM(ts_tree_cursor_current_field_id(&SELF));
|
64
|
+
}
|
65
|
+
|
66
|
+
/**
|
67
|
+
* Get the field name of the tree cursor's current node.
|
68
|
+
*
|
69
|
+
* This returns +nil+ if the current node doesn't have a field.
|
70
|
+
*
|
71
|
+
* @see Node#child_by_field_name
|
72
|
+
*
|
73
|
+
* @return [String]
|
74
|
+
*/
|
75
|
+
static VALUE tree_cursor_current_field_name(VALUE self) {
|
76
|
+
return safe_str(ts_tree_cursor_current_field_name(&SELF));
|
77
|
+
}
|
78
|
+
|
79
|
+
/**
|
80
|
+
* Get the tree cursor's current node.
|
81
|
+
*
|
82
|
+
* @return [Node]
|
83
|
+
*/
|
84
|
+
static VALUE tree_cursor_current_node(VALUE self) {
|
85
|
+
TSNode node = ts_tree_cursor_current_node(&SELF);
|
86
|
+
return new_node(&node);
|
87
|
+
}
|
88
|
+
|
89
|
+
/**
|
90
|
+
* Move the cursor to the node that is the nth descendant of
|
91
|
+
* the original node that the cursor was constructed with, where
|
92
|
+
* zero represents the original node itself.
|
93
|
+
*
|
94
|
+
* @return [nil]
|
95
|
+
*/
|
96
|
+
static VALUE tree_cursor_goto_descendant(VALUE self, VALUE descendant_idx) {
|
97
|
+
uint32_t idx = NUM2UINT(descendant_idx);
|
98
|
+
ts_tree_cursor_goto_descendant(&SELF, idx);
|
99
|
+
return Qnil;
|
100
|
+
}
|
101
|
+
|
102
|
+
/**
|
103
|
+
* Move the cursor to the first child of its current node.
|
104
|
+
*
|
105
|
+
* This returns +true+ if the cursor successfully moved, and returns +false+
|
106
|
+
* if there were no children.
|
107
|
+
*
|
108
|
+
* @return [Boolean]
|
109
|
+
*/
|
110
|
+
static VALUE tree_cursor_goto_first_child(VALUE self) {
|
111
|
+
return ts_tree_cursor_goto_first_child(&SELF) ? Qtrue : Qfalse;
|
112
|
+
}
|
113
|
+
|
114
|
+
/**
|
115
|
+
* Move the cursor to the first child of its current node that extends beyond
|
116
|
+
* the given byte offset.
|
117
|
+
*
|
118
|
+
* This returns the index of the child node if one was found, and returns -1
|
119
|
+
* if no such child was found.
|
120
|
+
*
|
121
|
+
* @return [Integer]
|
122
|
+
*/
|
123
|
+
static VALUE tree_cursor_goto_first_child_for_byte(VALUE self, VALUE byte) {
|
124
|
+
return LL2NUM(
|
125
|
+
ts_tree_cursor_goto_first_child_for_byte(&SELF, NUM2UINT(byte)));
|
126
|
+
}
|
127
|
+
|
128
|
+
/**
|
129
|
+
* Move the cursor to the first child of its current node that extends beyond
|
130
|
+
* the given or point.
|
131
|
+
*
|
132
|
+
* This returns the index of the child node if one was found, and returns -1
|
133
|
+
* if no such child was found.
|
134
|
+
*
|
135
|
+
* @return [Integer]
|
136
|
+
*/
|
137
|
+
static VALUE tree_cursor_goto_first_child_for_point(VALUE self, VALUE point) {
|
138
|
+
return LL2NUM(
|
139
|
+
ts_tree_cursor_goto_first_child_for_point(&SELF, value_to_point(point)));
|
140
|
+
}
|
141
|
+
|
142
|
+
/**
|
143
|
+
* Move the cursor to the last child of its current node.
|
144
|
+
*
|
145
|
+
* This returns +true+ if the cursor successfully moved, and returns +false+ if
|
146
|
+
* there were no children.
|
147
|
+
*
|
148
|
+
* Note that this function may be slower than {#goto_first_child}
|
149
|
+
* because it needs to iterate through all the children to compute the child's
|
150
|
+
* position.
|
151
|
+
*/
|
152
|
+
static VALUE tree_cursor_goto_last_child(VALUE self) {
|
153
|
+
return ts_tree_cursor_goto_last_child(&SELF) ? Qtrue : Qfalse;
|
154
|
+
}
|
155
|
+
|
156
|
+
/**
|
157
|
+
* Move the cursor to the next sibling of its current node.
|
158
|
+
*
|
159
|
+
* This returns +true+ if the cursor successfully moved, and returns +false+
|
160
|
+
* if there was no next sibling node.
|
161
|
+
*
|
162
|
+
* @return Boolean
|
163
|
+
*/
|
164
|
+
static VALUE tree_cursor_goto_next_sibling(VALUE self) {
|
165
|
+
return ts_tree_cursor_goto_next_sibling(&SELF) ? Qtrue : Qfalse;
|
166
|
+
}
|
167
|
+
|
168
|
+
/**
|
169
|
+
* Move the cursor to the parent of its current node.
|
170
|
+
*
|
171
|
+
* This returns +true+ if the cursor successfully moved, and returns +false+
|
172
|
+
* if there was no parent node (the cursor was already on the root node).
|
173
|
+
*
|
174
|
+
* @return [Boolean]
|
175
|
+
*/
|
176
|
+
static VALUE tree_cursor_goto_parent(VALUE self) {
|
177
|
+
return ts_tree_cursor_goto_parent(&SELF) ? Qtrue : Qfalse;
|
178
|
+
}
|
179
|
+
|
180
|
+
/**
|
181
|
+
* Move the cursor to the previous sibling of its current node.
|
182
|
+
*
|
183
|
+
* This returns +true+ if the cursor successfully moved, and returns +false+ if
|
184
|
+
* there was no previous sibling node.
|
185
|
+
*
|
186
|
+
* Note, that this function may be slower than
|
187
|
+
* {#goto_next_sibling} due to how node positions are stored. In
|
188
|
+
* the worst case, this will need to iterate through all the children upto the
|
189
|
+
* previous sibling node to recalculate its position.
|
190
|
+
*
|
191
|
+
* @return [Boolean]
|
192
|
+
*/
|
193
|
+
static VALUE tree_cursor_goto_previous_sibling(VALUE self) {
|
194
|
+
return ts_tree_cursor_goto_previous_sibling(&SELF) ? Qtrue : Qfalse;
|
195
|
+
}
|
196
|
+
|
197
|
+
/**
|
198
|
+
* Create a new tree cursor starting from the given node.
|
199
|
+
*
|
200
|
+
* A tree cursor allows you to walk a syntax tree more efficiently than is
|
201
|
+
* possible using the {Node} functions. It is a mutable object that is always
|
202
|
+
* on a certain syntax node, and can be moved imperatively to different nodes.
|
203
|
+
*
|
204
|
+
* @return [TreeCursor]
|
205
|
+
*/
|
206
|
+
static VALUE tree_cursor_initialize(VALUE self, VALUE node) {
|
207
|
+
TSNode n = value_to_node(node);
|
208
|
+
tree_cursor_t *ptr = unwrap(self);
|
209
|
+
ptr->data = ts_tree_cursor_new(n);
|
210
|
+
return self;
|
211
|
+
}
|
212
|
+
|
213
|
+
/**
|
214
|
+
* Re-initialize a tree cursor to start at a different node.
|
215
|
+
*
|
216
|
+
* @return [nil]
|
217
|
+
*/
|
218
|
+
static VALUE tree_cursor_reset(VALUE self, VALUE node) {
|
219
|
+
ts_tree_cursor_reset(&SELF, value_to_node(node));
|
220
|
+
return Qnil;
|
221
|
+
}
|
222
|
+
|
223
|
+
/**
|
224
|
+
* Re-initialize a tree cursor to the same position as another cursor.
|
225
|
+
*
|
226
|
+
* Unlike {#reset}, this will not lose parent information and allows reusing
|
227
|
+
* already created cursors.
|
228
|
+
*
|
229
|
+
* @return [nil]
|
230
|
+
*/
|
231
|
+
VALUE tree_cursor_reset_to(VALUE self, VALUE src) {
|
232
|
+
ts_tree_cursor_reset_to(&SELF, &unwrap(src)->data);
|
233
|
+
return Qnil;
|
234
|
+
}
|
235
|
+
|
236
|
+
void init_tree_cursor(void) {
|
237
|
+
cTreeCursor = rb_define_class_under(mTreeSitter, "TreeCursor", rb_cObject);
|
238
|
+
|
239
|
+
rb_define_alloc_func(cTreeCursor, tree_cursor_allocate);
|
240
|
+
|
241
|
+
/* Class methods */
|
242
|
+
rb_define_method(cTreeCursor, "copy", tree_cursor_copy, 0);
|
243
|
+
rb_define_method(cTreeCursor, "current_depth", tree_cursor_current_depth, 0);
|
244
|
+
rb_define_method(cTreeCursor, "current_descendant_index",
|
245
|
+
tree_cursor_current_descendant_index, 0);
|
246
|
+
rb_define_method(cTreeCursor, "current_field_id",
|
247
|
+
tree_cursor_current_field_id, 0);
|
248
|
+
rb_define_method(cTreeCursor, "current_field_name",
|
249
|
+
tree_cursor_current_field_name, 0);
|
250
|
+
rb_define_method(cTreeCursor, "current_node", tree_cursor_current_node, 0);
|
251
|
+
rb_define_method(cTreeCursor, "goto_descendant", tree_cursor_goto_descendant,
|
252
|
+
1);
|
253
|
+
rb_define_method(cTreeCursor, "goto_first_child",
|
254
|
+
tree_cursor_goto_first_child, 0);
|
255
|
+
rb_define_method(cTreeCursor, "goto_first_child_for_byte",
|
256
|
+
tree_cursor_goto_first_child_for_byte, 1);
|
257
|
+
rb_define_method(cTreeCursor, "goto_first_child_for_point",
|
258
|
+
tree_cursor_goto_first_child_for_point, 1);
|
259
|
+
rb_define_method(cTreeCursor, "goto_last_child", tree_cursor_goto_last_child,
|
260
|
+
0);
|
261
|
+
rb_define_method(cTreeCursor, "goto_next_sibling",
|
262
|
+
tree_cursor_goto_next_sibling, 0);
|
263
|
+
rb_define_method(cTreeCursor, "goto_parent", tree_cursor_goto_parent, 0);
|
264
|
+
rb_define_method(cTreeCursor, "goto_previous_sibling",
|
265
|
+
tree_cursor_goto_previous_sibling, 0);
|
266
|
+
rb_define_method(cTreeCursor, "initialize", tree_cursor_initialize, 1);
|
267
|
+
rb_define_method(cTreeCursor, "reset", tree_cursor_reset, 1);
|
268
|
+
rb_define_method(cTreeCursor, "reset_to", tree_cursor_reset_to, 1);
|
269
|
+
}
|