tree_sitter 0.1.0-aarch64-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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 51a6e1af4f766de4482994ad23438a72f9d509a0d983b2f19ebfc6c36867c5ee
4
+ data.tar.gz: 1bd2fcd410e946125955b27d574feee42fc412a2b0792564c33ce6e01e4ca99b
5
+ SHA512:
6
+ metadata.gz: 2bcf5e3da7dbed11638731a8e8d48acc44a7d9b8b0979abc90761ba84062c28b356527d05fe79b89f8900e1805227a766d1824c401987b335ff7ae8093585223
7
+ data.tar.gz: b1823f9f33f7bc065dacd5485e03548dcdbc9edae1495413800d89fd0b3855f5044aa2e5ac291be25f25b6799f747beb5ae7ff5899b05700caf0ce6ff384d59c
data/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # [v0.1.0] - 13-01-2026
2
+ ## What's Changed
3
+ * Bump libloading from 0.8.9 to 0.9.0 by @dependabot[bot] in https://github.com/gjtorikian/tree_sitter/pull/2
4
+ * Bump tree-sitter from 0.25.10 to 0.26.3 by @dependabot[bot] in https://github.com/gjtorikian/tree_sitter/pull/3
5
+ * Bump actions/checkout from 4 to 6 by @dependabot[bot] in https://github.com/gjtorikian/tree_sitter/pull/1
6
+ * Test build bump by @gjtorikian in https://github.com/gjtorikian/tree_sitter/pull/4
7
+
8
+ ## New Contributors
9
+ * @dependabot[bot] made their first contribution in https://github.com/gjtorikian/tree_sitter/pull/2
10
+ * @gjtorikian made their first contribution in https://github.com/gjtorikian/tree_sitter/pull/4
11
+
12
+ **Full Changelog**: https://github.com/gjtorikian/tree_sitter/commits/v0.1.0
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Garen J. Torikian
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/Makefile ADDED
@@ -0,0 +1,116 @@
1
+ # Makefile for building tree-sitter grammars
2
+
3
+ GRAMMAR_DIR := .tree-sitter-grammars
4
+
5
+ # Platform detection
6
+ UNAME_S := $(shell uname -s)
7
+ ifeq ($(UNAME_S),Darwin)
8
+ EXT := dylib
9
+ CC_FLAGS := -shared -fPIC
10
+ else ifeq ($(UNAME_S),Linux)
11
+ EXT := so
12
+ CC_FLAGS := -shared -fPIC
13
+ else ifeq ($(OS),Windows_NT)
14
+ EXT := dll
15
+ CC_FLAGS := -shared
16
+ else
17
+ EXT := so
18
+ CC_FLAGS := -shared -fPIC
19
+ endif
20
+
21
+ GRAMMARS := rust ruby python javascript go php java c_sharp
22
+
23
+ .PHONY: all grammars clean $(GRAMMARS)
24
+
25
+ all: grammars
26
+
27
+ grammars: $(GRAMMARS)
28
+
29
+ rust: $(GRAMMAR_DIR)/rust/libtree-sitter-rust.$(EXT)
30
+ $(GRAMMAR_DIR)/rust/libtree-sitter-rust.$(EXT):
31
+ @mkdir -p $(GRAMMAR_DIR)
32
+ @if [ ! -d "$(GRAMMAR_DIR)/rust" ]; then \
33
+ echo "Cloning tree-sitter-rust..."; \
34
+ git clone --depth 1 https://github.com/tree-sitter/tree-sitter-rust.git $(GRAMMAR_DIR)/rust; \
35
+ fi
36
+ @echo "Building tree-sitter-rust..."
37
+ @cd $(GRAMMAR_DIR)/rust && $(CC) $(CC_FLAGS) -I src src/parser.c src/scanner.c -o libtree-sitter-rust.$(EXT)
38
+
39
+ ruby: $(GRAMMAR_DIR)/ruby/libtree-sitter-ruby.$(EXT)
40
+ $(GRAMMAR_DIR)/ruby/libtree-sitter-ruby.$(EXT):
41
+ @mkdir -p $(GRAMMAR_DIR)
42
+ @if [ ! -d "$(GRAMMAR_DIR)/ruby" ]; then \
43
+ echo "Cloning tree-sitter-ruby..."; \
44
+ git clone --depth 1 https://github.com/tree-sitter/tree-sitter-ruby.git $(GRAMMAR_DIR)/ruby; \
45
+ fi
46
+ @echo "Building tree-sitter-ruby..."
47
+ @cd $(GRAMMAR_DIR)/ruby && $(CC) $(CC_FLAGS) -I src src/parser.c src/scanner.c -o libtree-sitter-ruby.$(EXT)
48
+
49
+ python: $(GRAMMAR_DIR)/python/libtree-sitter-python.$(EXT)
50
+ $(GRAMMAR_DIR)/python/libtree-sitter-python.$(EXT):
51
+ @mkdir -p $(GRAMMAR_DIR)
52
+ @if [ ! -d "$(GRAMMAR_DIR)/python" ]; then \
53
+ echo "Cloning tree-sitter-python..."; \
54
+ git clone --depth 1 https://github.com/tree-sitter/tree-sitter-python.git $(GRAMMAR_DIR)/python; \
55
+ fi
56
+ @echo "Building tree-sitter-python..."
57
+ @cd $(GRAMMAR_DIR)/python && $(CC) $(CC_FLAGS) -I src src/parser.c src/scanner.c -o libtree-sitter-python.$(EXT)
58
+
59
+ javascript: $(GRAMMAR_DIR)/javascript/libtree-sitter-javascript.$(EXT)
60
+ $(GRAMMAR_DIR)/javascript/libtree-sitter-javascript.$(EXT):
61
+ @mkdir -p $(GRAMMAR_DIR)
62
+ @if [ ! -d "$(GRAMMAR_DIR)/javascript" ]; then \
63
+ echo "Cloning tree-sitter-javascript..."; \
64
+ git clone --depth 1 https://github.com/tree-sitter/tree-sitter-javascript.git $(GRAMMAR_DIR)/javascript; \
65
+ fi
66
+ @echo "Building tree-sitter-javascript..."
67
+ @cd $(GRAMMAR_DIR)/javascript && $(CC) $(CC_FLAGS) -I src src/parser.c src/scanner.c -o libtree-sitter-javascript.$(EXT)
68
+
69
+ go: $(GRAMMAR_DIR)/go/libtree-sitter-go.$(EXT)
70
+ $(GRAMMAR_DIR)/go/libtree-sitter-go.$(EXT):
71
+ @mkdir -p $(GRAMMAR_DIR)
72
+ @if [ ! -d "$(GRAMMAR_DIR)/go" ]; then \
73
+ echo "Cloning tree-sitter-go..."; \
74
+ git clone --depth 1 https://github.com/tree-sitter/tree-sitter-go.git $(GRAMMAR_DIR)/go; \
75
+ fi
76
+ @echo "Building tree-sitter-go..."
77
+ @cd $(GRAMMAR_DIR)/go && $(CC) $(CC_FLAGS) -I src src/parser.c -o libtree-sitter-go.$(EXT)
78
+
79
+ php: $(GRAMMAR_DIR)/php/libtree-sitter-php.$(EXT)
80
+ $(GRAMMAR_DIR)/php/libtree-sitter-php.$(EXT):
81
+ @mkdir -p $(GRAMMAR_DIR)
82
+ @if [ ! -d "$(GRAMMAR_DIR)/php" ]; then \
83
+ echo "Cloning tree-sitter-php..."; \
84
+ git clone --depth 1 https://github.com/tree-sitter/tree-sitter-php.git $(GRAMMAR_DIR)/php; \
85
+ fi
86
+ @echo "Building tree-sitter-php..."
87
+ @cd $(GRAMMAR_DIR)/php/php && $(CC) $(CC_FLAGS) -I src src/parser.c src/scanner.c -o ../libtree-sitter-php.$(EXT)
88
+
89
+ java: $(GRAMMAR_DIR)/java/libtree-sitter-java.$(EXT)
90
+ $(GRAMMAR_DIR)/java/libtree-sitter-java.$(EXT):
91
+ @mkdir -p $(GRAMMAR_DIR)
92
+ @if [ ! -d "$(GRAMMAR_DIR)/java" ]; then \
93
+ echo "Cloning tree-sitter-java..."; \
94
+ git clone --depth 1 https://github.com/tree-sitter/tree-sitter-java.git $(GRAMMAR_DIR)/java; \
95
+ fi
96
+ @echo "Building tree-sitter-java..."
97
+ @cd $(GRAMMAR_DIR)/java && $(CC) $(CC_FLAGS) -I src src/parser.c -o libtree-sitter-java.$(EXT)
98
+
99
+ c_sharp: $(GRAMMAR_DIR)/c_sharp/libtree-sitter-c_sharp.$(EXT)
100
+ $(GRAMMAR_DIR)/c_sharp/libtree-sitter-c_sharp.$(EXT):
101
+ @mkdir -p $(GRAMMAR_DIR)
102
+ @if [ ! -d "$(GRAMMAR_DIR)/c_sharp" ]; then \
103
+ echo "Cloning tree-sitter-c-sharp..."; \
104
+ git clone --depth 1 https://github.com/tree-sitter/tree-sitter-c-sharp.git $(GRAMMAR_DIR)/c_sharp; \
105
+ fi
106
+ @echo "Building tree-sitter-c-sharp..."
107
+ @cd $(GRAMMAR_DIR)/c_sharp && $(CC) $(CC_FLAGS) -I src src/parser.c src/scanner.c -o libtree-sitter-c_sharp.$(EXT)
108
+
109
+ clean:
110
+ rm -rf $(GRAMMAR_DIR)
111
+
112
+ # Show which extension will be used
113
+ info:
114
+ @echo "Platform: $(UNAME_S)"
115
+ @echo "Extension: $(EXT)"
116
+ @echo "Grammar directory: $(GRAMMAR_DIR)"
data/README.md ADDED
@@ -0,0 +1,466 @@
1
+ # TreeSitter
2
+
3
+ Ruby bindings for [tree-sitter](https://tree-sitter.github.io/) with code transformation and refactoring capabilities. Parse source code using tree-sitter with a Ruby-friendly API, supporting multiple languages via dynamic grammar loading. Written in Rust, wrapped in Ruby.
4
+
5
+ ## Features
6
+
7
+ - **Multi-language support** - Load any tree-sitter grammar dynamically
8
+ - **Full AST navigation** - Traverse nodes, access children, siblings, parents
9
+ - **Query support** - Pattern-based node searching using S-expressions
10
+ - **Rewriter API** - Programmatic code transformations (replace, remove, insert, wrap)
11
+ - **Query-based editing** - Bulk edits using tree-sitter queries
12
+ - **Transforms** - Move, copy, swap, and reorder nodes
13
+ - **Insertions** - Syntax-aware insertions with automatic indentation
14
+
15
+ The main difference with this gem and others like it is that this focuses on parsing once, transforming, and outputting. It doesn't really work as well for live syntax highlighting or fitting into tools that need sub-millisecond updates (as a user is typing, for instance).
16
+
17
+ ## Installation
18
+
19
+ Add to your Gemfile:
20
+
21
+ ```ruby
22
+ gem "tree_sitter"
23
+ ```
24
+
25
+ Then run:
26
+
27
+ ```bash
28
+ bundle install
29
+ ```
30
+
31
+ **Note:** You need tree-sitter grammar shared libraries (`.so` or `.dylib` files) for the languages you want to parse. See [Grammar Setup](#grammar-setup) below.
32
+
33
+ ## Usage
34
+
35
+ ### Basic Parsing
36
+
37
+ ```ruby
38
+ require "tree_sitter"
39
+
40
+ # Register a language from a shared library
41
+ TreeSitter.register_language("ruby", "path/to/libtree-sitter-ruby.{so,dylib}")
42
+
43
+ # Create a parser and set the language
44
+ parser = TreeSitter::Parser.new
45
+ parser.language = "rust"
46
+
47
+ # Parse source code
48
+ source = "fn add(a: i32, b: i32) -> i32 { a + b }"
49
+ tree = parser.parse(source)
50
+
51
+ # Navigate the AST
52
+ root = tree.root_node
53
+ puts root.kind # => "source_file"
54
+ puts root.child_count # => 1
55
+
56
+ fn_item = root.child(0)
57
+ puts fn_item.kind # => "function_item"
58
+
59
+ fn_name = fn_item.child_by_field_name("name")
60
+ puts fn_name.text # => "add"
61
+
62
+ # Access the original source and language from the tree
63
+ puts tree.source # => "fn add(a: i32, b: i32) -> i32 { a + b }"
64
+ puts tree.language.name # => "rust"
65
+
66
+ # Set a parse timeout (in microseconds) to prevent hanging on large inputs
67
+ parser.timeout_micros = 1_000_000 # 1 second
68
+ tree = parser.parse(very_large_source) # Returns nil if timeout exceeded
69
+ ```
70
+
71
+ ### Multi-Language Support
72
+
73
+ ```ruby
74
+ # Register multiple languages
75
+ TreeSitter.register_language("ruby", ENV["TREE_SITTER_RUBY_PATH"])
76
+ TreeSitter.register_language("python", ENV["TREE_SITTER_PYTHON_PATH"])
77
+
78
+ # Parse Ruby code
79
+ ruby_parser = TreeSitter::Parser.new
80
+ ruby_parser.language = "ruby"
81
+ ruby_tree = ruby_parser.parse("def hello; puts 'hi'; end")
82
+
83
+ # Parse Python code
84
+ python_parser = TreeSitter::Parser.new
85
+ python_parser.language = "python"
86
+ python_tree = python_parser.parse("def hello():\n print('hi')")
87
+
88
+ # List registered languages
89
+ TreeSitter.languages # => ["ruby", "python"]
90
+
91
+ # Language metadata
92
+ lang = TreeSitter.language("ruby")
93
+ lang.name # => "ruby"
94
+ lang.version # => 15 (ABI version)
95
+ lang.node_kind_count # => 200 (number of node types in the grammar)
96
+ ```
97
+
98
+ ### Node Operations
99
+
100
+ Once you have a node from the AST, you can navigate, inspect, and extract information:
101
+
102
+ ```ruby
103
+ # Get nodes to work with
104
+ root = tree.root_node
105
+ fn_item = root.child(0)
106
+ fn_name = fn_item.child_by_field_name("name")
107
+
108
+ # === Navigation ===
109
+ fn_item.parent # => #<TreeSitter::Node kind="source_file" ...>
110
+ fn_item.child(0) # => #<TreeSitter::Node kind="fn" ...>
111
+ fn_item.child_count # => 6
112
+ fn_item.children # => [#<Node>, #<Node>, ...] (array of all children)
113
+ fn_item.named_child(0) # => #<TreeSitter::Node kind="identifier" ...>
114
+ fn_item.named_child_count # => 4
115
+ fn_item.named_children # => [#<Node>, ...] (array of named children only)
116
+ fn_item.child_by_field_name("name") # => #<TreeSitter::Node kind="identifier" ...>
117
+
118
+ # Sibling navigation (using parameters as example)
119
+ params = fn_item.child_by_field_name("parameters")
120
+ first_param = params.named_child(0)
121
+ first_param.next_sibling # => #<TreeSitter::Node kind="," ...>
122
+ first_param.next_named_sibling # => #<TreeSitter::Node kind="parameter" ...>
123
+
124
+ # === Properties ===
125
+ fn_item.kind # => "function_item"
126
+ fn_item.type # => "function_item" (alias for kind)
127
+ fn_item.kind_id # => 188
128
+ fn_item.named? # => true (not anonymous like "{" or ")")
129
+ fn_item.missing? # => false (not inserted by parser for error recovery)
130
+ fn_item.extra? # => false (not extra like comments)
131
+ fn_item.error? # => false (not an ERROR node)
132
+ fn_item.has_error? # => false (no errors in subtree)
133
+ fn_item.has_changes? # => false (not changed in incremental parse)
134
+
135
+ # === Position ===
136
+ fn_name.start_byte # => 3
137
+ fn_name.end_byte # => 6
138
+ fn_name.start_point # => #<TreeSitter::Point row=0 column=3>
139
+ fn_name.end_point # => #<TreeSitter::Point row=0 column=6>
140
+ fn_name.range # => #<TreeSitter::Range start_byte=3 end_byte=6 size=3>
141
+
142
+ # === Text & Display ===
143
+ fn_name.text # => "add"
144
+ fn_name.to_sexp # => "(identifier)"
145
+ fn_name.to_s # => "(identifier)" (alias for to_sexp)
146
+ fn_name.inspect # => "#<TreeSitter::Node kind=\"identifier\" start_byte=3 end_byte=6>"
147
+
148
+ # === Comparison ===
149
+ root == tree.root_node # => true
150
+ root.eql?(tree.root_node) # => true
151
+ ```
152
+
153
+ ### Point and Range
154
+
155
+ Nodes provide position information via `Point` and `Range` objects:
156
+
157
+ ```ruby
158
+ # Point represents a position (row/column)
159
+ point = fn_name.start_point
160
+ point.row # => 0
161
+ point.column # => 3
162
+ point.to_a # => [0, 3]
163
+ point.inspect # => "#<TreeSitter::Point row=0 column=3>"
164
+
165
+ # Create points directly
166
+ point = TreeSitter::Point.new(0, 3)
167
+ point == fn_name.start_point # => true
168
+
169
+ # Range represents a span with byte offsets and points
170
+ range = fn_name.range
171
+ range.start_byte # => 3
172
+ range.end_byte # => 6
173
+ range.size # => 3
174
+ range.start_point # => #<TreeSitter::Point row=0 column=3>
175
+ range.end_point # => #<TreeSitter::Point row=0 column=6>
176
+ range.inspect # => "#<TreeSitter::Range start_byte=3 end_byte=6 size=3>"
177
+ ```
178
+
179
+ ### Query-Based Node Finding
180
+
181
+ Use tree-sitter queries to find nodes matching patterns:
182
+
183
+ ```ruby
184
+ # Create a query with capture names (@fn_name, @fn)
185
+ lang = TreeSitter.language("rust")
186
+ query = TreeSitter::Query.new(lang, "(function_item name: (identifier) @fn_name) @fn")
187
+
188
+ # Query properties
189
+ query.pattern_count # => 1
190
+ query.capture_names # => ["fn_name", "fn"]
191
+
192
+ # Execute query with a cursor
193
+ cursor = TreeSitter::QueryCursor.new
194
+
195
+ # Get all matches (each match contains all captures for one pattern match)
196
+ matches = cursor.matches(query, tree.root_node, source)
197
+ matches.length # => 4 (one per function in file)
198
+
199
+ match = matches.first
200
+ match.pattern_index # => 0
201
+ match.captures # => [#<QueryCapture>, #<QueryCapture>]
202
+ match.captures.length # => 2
203
+
204
+ # Get all captures directly (flattened list)
205
+ cursor = TreeSitter::QueryCursor.new # create new cursor
206
+ captures = cursor.captures(query, tree.root_node, source)
207
+ captures.length # => 8 (2 captures x 4 functions)
208
+
209
+ capture = captures.first
210
+ capture.name # => "fn"
211
+ capture.node # => #<TreeSitter::Node kind="function_item" ...>
212
+ capture.node.text # => "fn add(a: i32, b: i32) -> i32 {\n a + b\n}"
213
+
214
+ # Iterate over captures
215
+ captures.each do |cap|
216
+ puts "#{cap.name}: #{cap.node.text[0..20]}..." if cap.name == "fn_name"
217
+ end
218
+ # => fn_name: add...
219
+ # => fn_name: new...
220
+ # => fn_name: distance...
221
+ # => fn_name: main...
222
+ ```
223
+
224
+ ### Code Rewriting
225
+
226
+ ```ruby
227
+ source = <<~RUST
228
+ fn add(a: i32, b: i32) -> i32 {
229
+ a + b
230
+ }
231
+ RUST
232
+
233
+ tree = parser.parse(source)
234
+ fn_item = tree.root_node.child(0)
235
+ fn_name = fn_item.child_by_field_name("name")
236
+
237
+ # Create a rewriter and apply edits
238
+ new_source = TreeSitter::Rewriter.new(source, tree)
239
+ .replace(fn_name, "sum")
240
+ .insert_before(fn_item, "#[inline]\n")
241
+ .rewrite
242
+
243
+ puts new_source
244
+ # => #[inline]
245
+ # => fn sum(a: i32, b: i32) -> i32 {
246
+ # => a + b
247
+ # => }
248
+ ```
249
+
250
+ #### Rewriter Operations
251
+
252
+ ```ruby
253
+ rewriter = TreeSitter::Rewriter.new(source, tree)
254
+
255
+ # Replace a node's text
256
+ rewriter.replace(node, "new_text")
257
+
258
+ # Remove a node
259
+ rewriter.remove(node)
260
+
261
+ # Insert before/after a node
262
+ rewriter.insert_before(node, "prefix ")
263
+ rewriter.insert_after(node, " suffix")
264
+
265
+ # Wrap a node
266
+ rewriter.wrap(node, "/* ", " */")
267
+
268
+ # All methods return self for chaining
269
+ rewriter
270
+ .replace(name_node, "new_name")
271
+ .insert_before(fn_node, "// Comment\n")
272
+ .rewrite
273
+ ```
274
+
275
+ ### Query-Based Editing
276
+
277
+ Use `QueryRewriter` to find and transform multiple nodes at once:
278
+
279
+ ```ruby
280
+ source = <<~RUST
281
+ fn main() {
282
+ old_func();
283
+ old_func();
284
+ other_func();
285
+ }
286
+ RUST
287
+
288
+ tree = parser.parse(source)
289
+ lang = TreeSitter.language("rust")
290
+
291
+ # Rename all calls to old_func
292
+ new_source = TreeSitter::QueryRewriter.new(source, tree, lang)
293
+ .query('(call_expression function: (identifier) @fn)')
294
+ .where { |m| m.captures.any? { |c| c.node.text == "old_func" } }
295
+ .replace("@fn") { "new_func" }
296
+ .rewrite
297
+
298
+ # Add #[derive(Debug)] to all structs
299
+ new_source = TreeSitter::QueryRewriter.new(source, tree, lang)
300
+ .query('(struct_item) @struct')
301
+ .insert_before("@struct") { "#[derive(Debug)]\n" }
302
+ .rewrite
303
+
304
+ # Remove all comments
305
+ new_source = TreeSitter::QueryRewriter.new(source, tree, lang)
306
+ .query('(line_comment) @comment')
307
+ .remove("@comment")
308
+ .rewrite
309
+ ```
310
+
311
+ ### Transforms
312
+
313
+ Use `Transformer` to move, copy, swap, or reorder nodes:
314
+
315
+ ```ruby
316
+ # Swap two parameters
317
+ transformer = TreeSitter::Transformer.new(source, tree)
318
+ .swap(param_a, param_b)
319
+ .rewrite
320
+
321
+ # Move a function after another
322
+ transformer = TreeSitter::Transformer.new(source, tree)
323
+ .move(first_fn, after: third_fn)
324
+ .rewrite
325
+
326
+ # Copy a node
327
+ transformer = TreeSitter::Transformer.new(source, tree)
328
+ .copy(struct_node, after: fn_node)
329
+ .rewrite
330
+
331
+ # Duplicate with transformation
332
+ transformer = TreeSitter::Transformer.new(source, tree)
333
+ .duplicate(fn_node) { |text| text.gsub("original", "copy") }
334
+ .rewrite
335
+ ```
336
+
337
+ ### Insertions
338
+
339
+ Use `Inserter` for syntax-aware insertions that respect indentation:
340
+
341
+ ```ruby
342
+ # Insert at end of a block with proper indentation
343
+ new_source = TreeSitter::Inserter.new(source, tree)
344
+ .at_end_of(fn_body)
345
+ .insert_statement("println!(\"done\");")
346
+ .rewrite
347
+
348
+ # Insert a sibling function
349
+ new_source = TreeSitter::Inserter.new(source, tree)
350
+ .after(existing_fn)
351
+ .insert_sibling("fn new_func() {\n // body\n}")
352
+ .rewrite
353
+
354
+ # Insert at start of block
355
+ new_source = TreeSitter::Inserter.new(source, tree)
356
+ .at_start_of(fn_body)
357
+ .insert_statement("let start = Instant::now();")
358
+ .rewrite
359
+ ```
360
+
361
+ ### Refactor
362
+
363
+ Use the `Refactor` module for common refactoring operations:
364
+
365
+ ```ruby
366
+ # Rename a symbol (function, variable, type)
367
+ new_source = TreeSitter::Refactor.rename_symbol(
368
+ source, tree, lang,
369
+ from: "old_name",
370
+ to: "new_name",
371
+ kind: :function # or :variable, :type, :identifier
372
+ )
373
+
374
+ # Rename a struct field
375
+ new_source = TreeSitter::Refactor.rename_field(
376
+ source, tree, lang,
377
+ from: "old_field",
378
+ to: "new_field"
379
+ )
380
+
381
+ # Add attributes to matching items
382
+ new_source = TreeSitter::Refactor.add_attribute(
383
+ source, tree, lang,
384
+ query_pattern: "(struct_item) @item",
385
+ attribute: "#[derive(Debug)]"
386
+ )
387
+
388
+ # Remove items matching a pattern
389
+ new_source = TreeSitter::Refactor.remove_matching(
390
+ source, tree, lang,
391
+ query_pattern: "(line_comment) @item"
392
+ )
393
+
394
+ # For regexp matching, use QueryRewriter directly
395
+ new_source = TreeSitter::QueryRewriter.new(source, tree, lang)
396
+ .query('(function_item name: (identifier) @name)')
397
+ .where { |m| m.captures.any? { |c| c.node.text =~ /^test_/ } }
398
+ .replace("@name") { |node| node.text.sub(/^test_/, "spec_") }
399
+ .rewrite
400
+ ```
401
+
402
+ ## Grammar Setup
403
+
404
+ TreeSitter requires grammar shared libraries for each language you want to parse.
405
+
406
+ ### Using a Makefile
407
+
408
+ A sample Makefile is included in the gem which builds all supported grammars locally:
409
+
410
+ ```bash
411
+ make grammars
412
+ ```
413
+
414
+ This clones and compiles grammars into `.tree-sitter-grammars/` with the correct extension for your platform (`.dylib` on macOS, `.so` on Linux, `.dll` on Windows).
415
+
416
+ Build individual grammars:
417
+
418
+ ```bash
419
+ make rust # Just Rust
420
+ make ruby python # Ruby and Python
421
+ ```
422
+
423
+ **You can use this Makefile as a reference for your own project! The gem does NOT ship with any grammars!**
424
+
425
+ ### Custom Grammar Paths
426
+
427
+ You can override grammar locations with environment variables:
428
+
429
+ ```bash
430
+ export TREE_SITTER_RUST_PATH="/path/to/libtree-sitter-rust.dylib"
431
+ export TREE_SITTER_RUBY_PATH="/path/to/libtree-sitter-ruby.so"
432
+ ```
433
+
434
+ Environment variables take precedence over auto-discovered grammars.
435
+
436
+ ## Supported Languages
437
+
438
+ This gem supports any language with a tree-sitter grammar. The test suite validates:
439
+
440
+ - Rust
441
+ - Ruby
442
+ - Python
443
+ - JavaScript
444
+ - Go
445
+ - PHP
446
+ - Java
447
+ - C#
448
+
449
+ ## Development
450
+
451
+ After checking out the repo:
452
+
453
+ ```bash
454
+ bin/setup # Install dependencies
455
+ make grammars # Compile the test grammars
456
+ rake compile # Compile the Rust extension
457
+ rake test # Run tests (requires grammar libraries)
458
+ ```
459
+
460
+ ## Contributing
461
+
462
+ Bug reports and pull requests are welcome on GitHub at https://github.com/gjtorikian/tree_sitter.
463
+
464
+ ## License
465
+
466
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
Binary file
Binary file
Binary file
Binary file