ruby_tree_sitter 0.20.8.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) 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/version.rb +4 -2
  18. data/lib/tree_sitter.rb +1 -0
  19. data/lib/tree_stand/ast_modifier.rb +30 -0
  20. data/lib/tree_stand/breadth_first_visitor.rb +54 -0
  21. data/lib/tree_stand/config.rb +13 -0
  22. data/lib/tree_stand/node.rb +224 -0
  23. data/lib/tree_stand/parser.rb +67 -0
  24. data/lib/tree_stand/range.rb +55 -0
  25. data/lib/tree_stand/tree.rb +123 -0
  26. data/lib/tree_stand/utils/printer.rb +73 -0
  27. data/lib/tree_stand/version.rb +7 -0
  28. data/lib/tree_stand/visitor.rb +127 -0
  29. data/lib/tree_stand/visitors/tree_walker.rb +37 -0
  30. data/lib/tree_stand.rb +48 -0
  31. data/tree_sitter.gemspec +14 -11
  32. metadata +37 -108
  33. data/test/README.md +0 -15
  34. data/test/test_helper.rb +0 -9
  35. data/test/tree_sitter/js_test.rb +0 -48
  36. data/test/tree_sitter/language_test.rb +0 -73
  37. data/test/tree_sitter/logger_test.rb +0 -70
  38. data/test/tree_sitter/node_test.rb +0 -411
  39. data/test/tree_sitter/parser_test.rb +0 -140
  40. data/test/tree_sitter/query_test.rb +0 -153
  41. data/test/tree_sitter/tree_cursor_test.rb +0 -83
  42. 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
@@ -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.20.9'
6
+ # The current version of the gem.
7
+ VERSION = '1.0.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