ruby_tree_sitter 1.1.0-arm64-darwin-22
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 +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
|
+
}
|