ruby_tree_sitter 0.20.8.3-x86_64-linux → 1.1.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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +2 -1
  3. data/README.md +32 -18
  4. data/ext/tree_sitter/extconf.rb +1 -1
  5. data/ext/tree_sitter/input.c +1 -0
  6. data/ext/tree_sitter/language.c +131 -46
  7. data/ext/tree_sitter/logger.c +28 -12
  8. data/ext/tree_sitter/node.c +438 -130
  9. data/ext/tree_sitter/parser.c +232 -37
  10. data/ext/tree_sitter/query.c +197 -72
  11. data/ext/tree_sitter/query_cursor.c +140 -28
  12. data/ext/tree_sitter/repo.rb +1 -1
  13. data/ext/tree_sitter/tree.c +118 -34
  14. data/ext/tree_sitter/tree_cursor.c +205 -33
  15. data/ext/tree_sitter/tree_sitter.c +12 -0
  16. data/lib/tree_sitter/node.rb +23 -6
  17. data/lib/tree_sitter/tree_sitter.so +0 -0
  18. data/lib/tree_sitter/version.rb +4 -2
  19. data/lib/tree_sitter.rb +1 -0
  20. data/lib/tree_stand/ast_modifier.rb +30 -0
  21. data/lib/tree_stand/breadth_first_visitor.rb +54 -0
  22. data/lib/tree_stand/config.rb +13 -0
  23. data/lib/tree_stand/node.rb +224 -0
  24. data/lib/tree_stand/parser.rb +67 -0
  25. data/lib/tree_stand/range.rb +55 -0
  26. data/lib/tree_stand/tree.rb +123 -0
  27. data/lib/tree_stand/utils/printer.rb +73 -0
  28. data/lib/tree_stand/version.rb +7 -0
  29. data/lib/tree_stand/visitor.rb +127 -0
  30. data/lib/tree_stand/visitors/tree_walker.rb +37 -0
  31. data/lib/tree_stand.rb +48 -0
  32. data/tree_sitter.gemspec +14 -11
  33. metadata +36 -107
  34. data/test/README.md +0 -15
  35. data/test/test_helper.rb +0 -9
  36. data/test/tree_sitter/js_test.rb +0 -48
  37. data/test/tree_sitter/language_test.rb +0 -73
  38. data/test/tree_sitter/logger_test.rb +0 -70
  39. data/test/tree_sitter/node_test.rb +0 -411
  40. data/test/tree_sitter/parser_test.rb +0 -140
  41. data/test/tree_sitter/query_test.rb +0 -153
  42. data/test/tree_sitter/tree_cursor_test.rb +0 -83
  43. data/test/tree_sitter/tree_test.rb +0 -51
@@ -17,58 +17,220 @@ DATA_UNWRAP(tree_cursor)
17
17
  DATA_NEW(cTreeCursor, TSTreeCursor, tree_cursor)
18
18
  DATA_FROM_VALUE(TSTreeCursor, tree_cursor)
19
19
 
20
- static VALUE tree_cursor_initialize(VALUE self, VALUE node) {
21
- TSNode n = value_to_node(node);
22
- tree_cursor_t *ptr = unwrap(self);
23
- ptr->data = ts_tree_cursor_new(n);
24
- return self;
25
- }
26
-
27
- static VALUE tree_cursor_reset(VALUE self, VALUE node) {
28
- ts_tree_cursor_reset(&SELF, value_to_node(node));
29
- return Qnil;
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
30
  }
31
31
 
32
- static VALUE tree_cursor_current_node(VALUE self) {
33
- TSNode node = ts_tree_cursor_current_node(&SELF);
34
- return new_node(&node);
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));
35
40
  }
36
41
 
37
- static VALUE tree_cursor_current_field_name(VALUE self) {
38
- return safe_str(ts_tree_cursor_current_field_name(&SELF));
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));
39
50
  }
40
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
+ */
41
62
  static VALUE tree_cursor_current_field_id(VALUE self) {
42
63
  return UINT2NUM(ts_tree_cursor_current_field_id(&SELF));
43
64
  }
44
65
 
45
- static VALUE tree_cursor_goto_parent(VALUE self) {
46
- return ts_tree_cursor_goto_parent(&SELF) ? Qtrue : Qfalse;
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));
47
77
  }
48
78
 
49
- static VALUE tree_cursor_goto_next_sibling(VALUE self) {
50
- return ts_tree_cursor_goto_next_sibling(&SELF) ? Qtrue : Qfalse;
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;
51
100
  }
52
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
+ */
53
110
  static VALUE tree_cursor_goto_first_child(VALUE self) {
54
111
  return ts_tree_cursor_goto_first_child(&SELF) ? Qtrue : Qfalse;
55
112
  }
56
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
+ */
57
123
  static VALUE tree_cursor_goto_first_child_for_byte(VALUE self, VALUE byte) {
58
124
  return LL2NUM(
59
125
  ts_tree_cursor_goto_first_child_for_byte(&SELF, NUM2UINT(byte)));
60
126
  }
61
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
+ */
62
137
  static VALUE tree_cursor_goto_first_child_for_point(VALUE self, VALUE point) {
63
138
  return LL2NUM(
64
139
  ts_tree_cursor_goto_first_child_for_point(&SELF, value_to_point(point)));
65
140
  }
66
141
 
67
- static VALUE tree_cursor_copy(VALUE self) {
68
- VALUE res = tree_cursor_allocate(cTreeCursor);
69
- tree_cursor_t *ptr = unwrap(res);
70
- ptr->data = ts_tree_cursor_copy(&SELF);
71
- return res;
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;
72
234
  }
73
235
 
74
236
  void init_tree_cursor(void) {
@@ -77,21 +239,31 @@ void init_tree_cursor(void) {
77
239
  rb_define_alloc_func(cTreeCursor, tree_cursor_allocate);
78
240
 
79
241
  /* Class methods */
80
- rb_define_method(cTreeCursor, "initialize", tree_cursor_initialize, 1);
81
- rb_define_method(cTreeCursor, "reset", tree_cursor_reset, 1);
82
- rb_define_method(cTreeCursor, "current_node", tree_cursor_current_node, 0);
83
- rb_define_method(cTreeCursor, "current_field_name",
84
- tree_cursor_current_field_name, 0);
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);
85
246
  rb_define_method(cTreeCursor, "current_field_id",
86
247
  tree_cursor_current_field_id, 0);
87
- rb_define_method(cTreeCursor, "goto_parent", tree_cursor_goto_parent, 0);
88
- rb_define_method(cTreeCursor, "goto_next_sibling",
89
- tree_cursor_goto_next_sibling, 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);
90
253
  rb_define_method(cTreeCursor, "goto_first_child",
91
254
  tree_cursor_goto_first_child, 0);
92
255
  rb_define_method(cTreeCursor, "goto_first_child_for_byte",
93
256
  tree_cursor_goto_first_child_for_byte, 1);
94
257
  rb_define_method(cTreeCursor, "goto_first_child_for_point",
95
258
  tree_cursor_goto_first_child_for_point, 1);
96
- rb_define_method(cTreeCursor, "copy", tree_cursor_copy, 0);
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);
97
269
  }
@@ -5,8 +5,20 @@ VALUE mTreeSitter;
5
5
  void Init_tree_sitter() {
6
6
  mTreeSitter = rb_define_module("TreeSitter");
7
7
 
8
+ /**
9
+ * The latest ABI version that is supported by the current version of the
10
+ * library. When Languages are generated by the Tree-sitter CLI, they are
11
+ * assigned an ABI version number that corresponds to the current CLI version.
12
+ * The Tree-sitter library is generally backwards-compatible with languages
13
+ * generated using older CLI versions, but is not forwards-compatible.
14
+ */
8
15
  rb_define_const(mTreeSitter, "LANGUAGE_VERSION",
9
16
  INT2NUM(TREE_SITTER_LANGUAGE_VERSION));
17
+
18
+ /**
19
+ * The earliest ABI version that is supported by the current version of the
20
+ * library.
21
+ */
10
22
  rb_define_const(mTreeSitter, "MIN_COMPATIBLE_LANGUAGE_VERSION",
11
23
  TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION);
12
24
 
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TreeSitter
4
+ # Node is a wrapper around a tree-sitter node.
4
5
  class Node
6
+ # @return [Array<Symbol>] the node's named fields
5
7
  def fields
6
8
  return @fields if @fields
7
9
 
@@ -18,6 +20,18 @@ module TreeSitter
18
20
  fields.include?(field)
19
21
  end
20
22
 
23
+ # FIXME: These APIs (`[]` and `fetch`) need absolute fixing.
24
+ # 1. The documentation with the table doesn't work.
25
+ # 1. The APIs are very confusing! Make them act similarly to Hash's
26
+ # `fetch` and `[]`.
27
+ # 1. `[]` should take a single input and return nil if nothing found
28
+ # (no exceptions).
29
+ # 1. `fetch` should should accept a single argument, potentially a
30
+ # default, and raise exception if no default was provided.
31
+ # Also allow for the `all:` kwarg.
32
+ # 1. `values_at` takes many arguments.
33
+ # And I don't think we can move to 1.0 without adressing them.
34
+ #
21
35
  # Access node's named children.
22
36
  #
23
37
  # It's similar to {#fetch}, but differes in input type, return values, and
@@ -49,13 +63,11 @@ module TreeSitter
49
63
  when 0 then raise "#{self.class.name}##{__method__} requires a key."
50
64
  when 1
51
65
  case k = keys.first
52
- when Numeric then named_child(k)
66
+ when Numeric then named_child(k)
53
67
  when String, Symbol
54
- if fields.include?(k.to_sym)
55
- child_by_field_name(k.to_s)
56
- else
57
- raise "Cannot find field #{k}"
58
- end
68
+ raise "Cannot find field #{k}" unless fields.include?(k.to_sym)
69
+
70
+ child_by_field_name(k.to_s)
59
71
  else raise <<~ERR
60
72
  #{self.class.name}##{__method__} accepts Integer and returns named child at given index,
61
73
  or a (String | Symbol) and returns the child by given field name.
@@ -66,6 +78,8 @@ module TreeSitter
66
78
  end
67
79
  end
68
80
 
81
+ # @!visibility private
82
+ #
69
83
  # Allows access to child_by_field_name without using [].
70
84
  def method_missing(method_name, *_args, &_block)
71
85
  if fields.include?(method_name)
@@ -75,6 +89,8 @@ module TreeSitter
75
89
  end
76
90
  end
77
91
 
92
+ # @!visibility private
93
+ #
78
94
  def respond_to_missing?(*args)
79
95
  args.length == 1 && fields.include?(args[0])
80
96
  end
@@ -116,6 +132,7 @@ module TreeSitter
116
132
  end
117
133
  end
118
134
 
135
+ # @return [Array<TreeSitter::Node>] all the node's children
119
136
  def to_a
120
137
  each.to_a
121
138
  end
Binary file
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TreeSitter
4
- TREESITTER_VERSION = '0.20.8'
5
- VERSION = "#{TREESITTER_VERSION}.3".freeze
4
+ # The version of the tree-sitter library.
5
+ TREESITTER_VERSION = '0.22.6'
6
+ # The current version of the gem.
7
+ VERSION = '1.1.0'
6
8
  end
data/lib/tree_sitter.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # TreeSitter is a Ruby interface to the tree-sitter parsing library.
3
4
  module TreeSitter
4
5
  end
5
6
 
@@ -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
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ module TreeStand
5
+ # Breadth-first traversal through the tree, calling hooks at each stop.
6
+ class BreadthFirstVisitor
7
+ extend T::Sig
8
+
9
+ sig { params(node: TreeStand::Node).void }
10
+ def initialize(node)
11
+ @node = node
12
+ end
13
+
14
+ # Run the visitor on the document and return self. Allows chaining create and visit.
15
+ # @example
16
+ # visitor = CountingVisitor.new(node, :predicate).visit
17
+ sig { returns(T.self_type) }
18
+ def visit
19
+ queue = [@node]
20
+ visit_node(queue) while queue.any?
21
+ self
22
+ end
23
+
24
+ # @abstract The default implementation does nothing.
25
+ sig { overridable.params(node: TreeStand::Node).void }
26
+ def on(node) = nil
27
+
28
+ # @abstract The default implementation yields to visit all children.
29
+ sig { overridable.params(node: TreeStand::Node, block: T.proc.void).void }
30
+ def around(node, &block) = yield
31
+
32
+ private
33
+
34
+ def visit_node(queue)
35
+ node = queue.shift
36
+
37
+ if respond_to?("on_#{node.type}")
38
+ public_send("on_#{node.type}", node)
39
+ else
40
+ on(node)
41
+ end
42
+
43
+ if respond_to?("around_#{node.type}")
44
+ public_send("around_#{node.type}", node) do
45
+ node.each { |child| queue << child }
46
+ end
47
+ else
48
+ around(node) do
49
+ node.each { |child| queue << child }
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ module TreeStand
5
+ # Global configuration for the gem.
6
+ # @api private
7
+ class Config
8
+ extend T::Sig
9
+
10
+ sig { returns(String) }
11
+ attr_accessor :parser_path
12
+ end
13
+ end