tree_stand 0.1.6 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitattributes +1 -0
- data/.github/workflows/ci.yml +3 -11
- data/.yardopts +6 -0
- data/CONTRIBUTING.md +6 -0
- data/Gemfile +13 -1
- data/README.md +4 -2
- data/bin/console +3 -3
- data/bin/setup +0 -1
- data/bin/tapioca +27 -0
- data/lib/tree_stand/ast_modifier.rb +7 -2
- data/lib/tree_stand/config.rb +6 -1
- data/lib/tree_stand/node.rb +45 -39
- data/lib/tree_stand/parser.rb +10 -4
- data/lib/tree_stand/range.rb +18 -6
- data/lib/tree_stand/tree.rb +14 -9
- data/lib/tree_stand/utils/printer.rb +12 -9
- data/lib/tree_stand/version.rb +4 -1
- data/lib/tree_stand/visitor.rb +79 -11
- data/lib/tree_stand/visitors/tree_walker.rb +13 -6
- data/lib/tree_stand.rb +10 -2
- data/sorbet/config +5 -0
- data/sorbet/rbi/gems/ruby_tree_sitter@0.20.6.3.rbi +391 -0
- data/sorbet/rbi/gems/zeitwerk@2.6.6.rbi +950 -0
- data/sorbet/tapioca/config.yml +29 -0
- data/sorbet/tapioca/require.rb +4 -0
- data/tree_stand.gemspec +2 -8
- metadata +16 -78
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 957ff5fd108366dc0b3c0ec05c821a8d207930a4ccf291e1092d26e373a0f4da
|
4
|
+
data.tar.gz: fce8cce29e6d558ee447f2afcdf9c83dd276521c0261ec99be93dea8c456b9b1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0f87824f3b009cb8d6993e25cd9b44c2ba645bcf253ef88771b12307a37190a8cb3e405836baea363344763c71cfd3be1bf5c53dd10c69bdfbd66fad1dfdd62d
|
7
|
+
data.tar.gz: 25b6b852ff81d122a61d8c74fa94e2e4cde20918ca1b0323b7dbda2e30666d534c8f4acaf736c5298b1de09a6491a819f44badeab3d2ec0a13c56a5e9e46c7ef
|
data/.gitattributes
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
sorbet/rbi/** linguist-generated
|
data/.github/workflows/ci.yml
CHANGED
@@ -15,15 +15,11 @@ jobs:
|
|
15
15
|
entry:
|
16
16
|
- { ruby: 3.0 }
|
17
17
|
- { ruby: 3.1 }
|
18
|
+
- { ruby: 3.2 }
|
18
19
|
name: test (${{ matrix.entry.ruby }})
|
19
20
|
steps:
|
20
21
|
- uses: actions/checkout@v3
|
21
22
|
|
22
|
-
- uses: actions/checkout@v3
|
23
|
-
with:
|
24
|
-
repository: DerekStride/tree-sitter-sql
|
25
|
-
path: tmp/tree-sitter-sql
|
26
|
-
|
27
23
|
- uses: ruby/setup-ruby@v1
|
28
24
|
with:
|
29
25
|
ruby-version: ${{ matrix.entry.ruby }}
|
@@ -33,12 +29,8 @@ jobs:
|
|
33
29
|
key: ${{ runner.os }}-gems-${{ hashFiles('Gemfile') }}
|
34
30
|
restore-keys: ${{ runner.os }}-gems-
|
35
31
|
|
36
|
-
-
|
37
|
-
with:
|
38
|
-
node-version: 16
|
39
|
-
- run: npm install tree-sitter-cli
|
40
|
-
|
41
|
-
- run: sudo apt-get install -y libtree-sitter-dev make gcc
|
32
|
+
- run: sudo apt-get install -y libtree-sitter-dev
|
42
33
|
- run: bundle install --jobs=3 --retry=3 --path=vendor/bundle
|
43
34
|
- run: bin/setup
|
35
|
+
- run: bundle exec srb tc
|
44
36
|
- run: bundle exec rake
|
data/.yardopts
ADDED
data/CONTRIBUTING.md
CHANGED
data/Gemfile
CHANGED
@@ -2,4 +2,16 @@ source "https://rubygems.org"
|
|
2
2
|
|
3
3
|
gemspec
|
4
4
|
|
5
|
-
|
5
|
+
group :development, :test do
|
6
|
+
gem "bundler", "~> 2.3"
|
7
|
+
gem "debug", "~> 1.7", require: false
|
8
|
+
gem "rake", "~> 13.0"
|
9
|
+
gem 'tapioca', "~> 0.11.1", require: false
|
10
|
+
gem "yard", "~> 0.9.28"
|
11
|
+
end
|
12
|
+
|
13
|
+
group :test do
|
14
|
+
gem "minitest", "~> 5.17"
|
15
|
+
gem "minitest-reporters", "~> 1.5"
|
16
|
+
gem "minitest-focus", "~> 1.3"
|
17
|
+
end
|
data/README.md
CHANGED
@@ -20,7 +20,7 @@ See the [documentation](https://shopify.github.io/tree_stand) for supported feat
|
|
20
20
|
|
21
21
|
### Setting Up a Parser
|
22
22
|
|
23
|
-
TreeStand
|
23
|
+
TreeStand does not help with compiling individual parsers. However, once you compile a parser and generate a shared
|
24
24
|
object (`.so`) or a dynamic library (`.dylib`) you can tell TreeStand where to find them and pass the parser filename
|
25
25
|
to `TreeStand::Parser::new`.
|
26
26
|
|
@@ -48,10 +48,12 @@ The underlying objects are accessible via a `ts_` prefixed attribute, e.g. `ts_p
|
|
48
48
|
|
49
49
|
Bug reports and pull requests are welcome on GitHub at https://github.com/Shopify/tree_stand. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
50
50
|
|
51
|
+
See [CONTRIBUTING.md](https://github.com/Shopify/tree_stand/blob/main/CONTRIBUTING.md) for documentation on how to set up the project for development.
|
52
|
+
|
51
53
|
## License
|
52
54
|
|
53
55
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
54
56
|
|
55
57
|
## Code of Conduct
|
56
58
|
|
57
|
-
Everyone interacting in the TreeStand project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/Shopify/tree_stand/blob/
|
59
|
+
Everyone interacting in the TreeStand project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/Shopify/tree_stand/blob/main/CODE_OF_CONDUCT.md).
|
data/bin/console
CHANGED
@@ -11,12 +11,12 @@ end
|
|
11
11
|
|
12
12
|
ivars_to_add = <<~RUBY
|
13
13
|
@parser = TreeStand::Parser.new("math")
|
14
|
-
@tree = @parser.parse_string(
|
14
|
+
@tree = @parser.parse_string("1 + x")
|
15
15
|
RUBY
|
16
16
|
|
17
17
|
eval(ivars_to_add)
|
18
18
|
puts("available ivars:")
|
19
19
|
puts(ivars_to_add)
|
20
20
|
|
21
|
-
require "
|
22
|
-
|
21
|
+
require "irb"
|
22
|
+
IRB.start(__FILE__)
|
data/bin/setup
CHANGED
data/bin/tapioca
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'tapioca' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
12
|
+
|
13
|
+
bundle_binstub = File.expand_path("bundle", __dir__)
|
14
|
+
|
15
|
+
if File.file?(bundle_binstub)
|
16
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
17
|
+
load(bundle_binstub)
|
18
|
+
else
|
19
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
20
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
require "rubygems"
|
25
|
+
require "bundler/setup"
|
26
|
+
|
27
|
+
load Gem.bin_path("tapioca", "tapioca")
|
@@ -1,10 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
1
4
|
module TreeStand
|
2
|
-
# An experimental class to modify the AST.
|
5
|
+
# An experimental class to modify the AST. It re-runs the query on the
|
3
6
|
# modified document every loop to ensure that the match is still valid.
|
4
7
|
# @see TreeStand::Tree
|
5
8
|
# @api experimental
|
6
9
|
class AstModifier
|
7
|
-
|
10
|
+
extend T::Sig
|
11
|
+
|
12
|
+
sig { params(tree: TreeStand::Tree).void }
|
8
13
|
def initialize(tree)
|
9
14
|
@tree = tree
|
10
15
|
end
|
data/lib/tree_stand/config.rb
CHANGED
data/lib/tree_stand/node.rb
CHANGED
@@ -1,9 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
1
4
|
module TreeStand
|
2
5
|
# Wrapper around a TreeSitter node and provides convient
|
3
6
|
# methods that are missing on the original node. This class
|
4
7
|
# overrides the `method_missing` method to delegate to a nodes
|
5
8
|
# named children.
|
6
9
|
class Node
|
10
|
+
extend T::Sig
|
7
11
|
extend Forwardable
|
8
12
|
include Enumerable
|
9
13
|
|
@@ -17,20 +21,13 @@ module TreeStand
|
|
17
21
|
:error?,
|
18
22
|
)
|
19
23
|
|
20
|
-
|
24
|
+
sig { returns(TreeStand::Tree) }
|
21
25
|
attr_reader :tree
|
22
|
-
|
26
|
+
sig { returns(TreeSitter::Node) }
|
23
27
|
attr_reader :ts_node
|
24
28
|
|
25
|
-
# @!method to_a
|
26
|
-
# @example
|
27
|
-
# node.text # => "3 * 4"
|
28
|
-
# node.to_a.map(&:text) # => ["3", "*", "4"]
|
29
|
-
# node.children.map(&:text) # => ["3", "*", "4"]
|
30
|
-
# @return [Array<TreeStand::Node>]
|
31
|
-
alias_method :children, :to_a
|
32
|
-
|
33
29
|
# @api private
|
30
|
+
sig { params(tree: TreeStand::Tree, ts_node: TreeSitter::Node).void }
|
34
31
|
def initialize(tree, ts_node)
|
35
32
|
@tree = tree
|
36
33
|
@ts_node = ts_node
|
@@ -53,9 +50,7 @@ module TreeStand
|
|
53
50
|
# tree.root_node.query(<<~QUERY)
|
54
51
|
# (identifier) @identifier
|
55
52
|
# QUERY
|
56
|
-
|
57
|
-
# @param query_string [String]
|
58
|
-
# @return [Array<Hash<String, TreeStand::Node>>]
|
53
|
+
sig { params(query_string: String).returns(T::Array[T::Hash[String, TreeStand::Node]]) }
|
59
54
|
def query(query_string)
|
60
55
|
ts_query = TreeSitter::Query.new(@tree.parser.ts_language, query_string)
|
61
56
|
ts_cursor = TreeSitter::QueryCursor.exec(ts_query, ts_node)
|
@@ -81,8 +76,7 @@ module TreeStand
|
|
81
76
|
#
|
82
77
|
# @see #find_node!
|
83
78
|
# @see #query
|
84
|
-
|
85
|
-
# @return [TreeStand::Node, nil]
|
79
|
+
sig { params(query_string: String).returns(T.nilable(TreeStand::Node)) }
|
86
80
|
def find_node(query_string)
|
87
81
|
query(query_string).first&.values&.first
|
88
82
|
end
|
@@ -91,14 +85,13 @@ module TreeStand
|
|
91
85
|
# {TreeStand::NodeNotFound} error.
|
92
86
|
#
|
93
87
|
# @see #find_node
|
94
|
-
# @param query_string [String]
|
95
|
-
# @return [TreeStand::Node]
|
96
88
|
# @raise [TreeStand::NodeNotFound]
|
89
|
+
sig { params(query_string: String).returns(TreeStand::Node) }
|
97
90
|
def find_node!(query_string)
|
98
91
|
find_node(query_string) || raise(TreeStand::NodeNotFound)
|
99
92
|
end
|
100
93
|
|
101
|
-
|
94
|
+
sig { returns(TreeStand::Range) }
|
102
95
|
def range
|
103
96
|
TreeStand::Range.new(
|
104
97
|
start_byte: @ts_node.start_byte,
|
@@ -122,13 +115,19 @@ module TreeStand
|
|
122
115
|
# node.map(&:text) # => ["3", "*", "4"]
|
123
116
|
#
|
124
117
|
# @yieldparam child [TreeStand::Node]
|
125
|
-
|
118
|
+
sig do
|
119
|
+
override
|
120
|
+
.params(block: T.nilable(T.proc.params(node: TreeStand::Node).returns(BasicObject)))
|
121
|
+
.returns(T::Enumerator[TreeStand::Node])
|
122
|
+
end
|
126
123
|
def each(&block)
|
127
|
-
Enumerator.new do |yielder|
|
124
|
+
enumerator = Enumerator.new do |yielder|
|
128
125
|
@ts_node.each do |child|
|
129
126
|
yielder << TreeStand::Node.new(@tree, child)
|
130
127
|
end
|
131
|
-
end
|
128
|
+
end
|
129
|
+
enumerator.each(&block) if block_given?
|
130
|
+
enumerator
|
132
131
|
end
|
133
132
|
|
134
133
|
# (see TreeStand::Visitors::TreeWalker)
|
@@ -137,32 +136,44 @@ module TreeStand
|
|
137
136
|
# @example Check the subtree for error nodes
|
138
137
|
# node.walk.any? { |node| node.type == :error }
|
139
138
|
#
|
140
|
-
# @yieldparam node [TreeStand::Node]
|
141
|
-
# @return [Enumerator]
|
142
|
-
#
|
143
139
|
# @see TreeStand::Visitors::TreeWalker
|
140
|
+
#
|
141
|
+
# @yieldparam node [TreeStand::Node]
|
142
|
+
sig do
|
143
|
+
params(block: T.nilable(T.proc.params(node: TreeStand::Node).returns(BasicObject)))
|
144
|
+
.returns(T::Enumerator[TreeStand::Node])
|
145
|
+
end
|
144
146
|
def walk(&block)
|
145
|
-
Enumerator.new do |yielder|
|
147
|
+
enumerator = Enumerator.new do |yielder|
|
146
148
|
Visitors::TreeWalker.new(self) do |child|
|
147
149
|
yielder << child
|
148
150
|
end.visit
|
149
|
-
end
|
151
|
+
end
|
152
|
+
enumerator.each(&block) if block_given?
|
153
|
+
enumerator
|
150
154
|
end
|
151
155
|
|
152
156
|
# @example
|
153
157
|
# node.text # => "4"
|
154
158
|
# node.parent.text # => "3 * 4"
|
155
159
|
# node.parent.parent.text # => "1 + 3 * 4"
|
156
|
-
|
160
|
+
sig { returns(TreeStand::Node) }
|
157
161
|
def parent
|
158
162
|
TreeStand::Node.new(@tree, @ts_node.parent)
|
159
163
|
end
|
160
164
|
|
161
|
-
#
|
165
|
+
# @example
|
166
|
+
# node.text # => "3 * 4"
|
167
|
+
# node.to_a.map(&:text) # => ["3", "*", "4"]
|
168
|
+
# node.children.map(&:text) # => ["3", "*", "4"]
|
169
|
+
sig { returns(T::Array[TreeStand::Node]) }
|
170
|
+
def children = to_a
|
171
|
+
|
172
|
+
# A convenience method for getting the text of the node. Each {TreeStand::Node}
|
162
173
|
# wraps the parent {TreeStand::Tree #tree} and has access to the source document.
|
163
|
-
|
174
|
+
sig { returns(String) }
|
164
175
|
def text
|
165
|
-
@tree.document[@ts_node.start_byte...@ts_node.end_byte]
|
176
|
+
T.must(@tree.document[@ts_node.start_byte...@ts_node.end_byte])
|
166
177
|
end
|
167
178
|
|
168
179
|
# This class overrides the `method_missing` method to delegate to the
|
@@ -183,26 +194,21 @@ module TreeStand
|
|
183
194
|
# @raise [NoMethodError]
|
184
195
|
def method_missing(method, *args, &block)
|
185
196
|
return super unless @fields.include?(method.to_s)
|
186
|
-
TreeStand::Node.new(@tree, @ts_node.public_send(method, *args, &block))
|
197
|
+
TreeStand::Node.new(@tree, T.unsafe(@ts_node).public_send(method, *args, &block))
|
187
198
|
end
|
188
199
|
|
189
|
-
|
190
|
-
# @return [bool]
|
200
|
+
sig { params(other: Object).returns(T::Boolean) }
|
191
201
|
def ==(other)
|
192
202
|
return false unless other.is_a?(TreeStand::Node)
|
193
203
|
|
194
|
-
range == other.range &&
|
195
|
-
type == other.type &&
|
196
|
-
text == other.text
|
204
|
+
T.must(range == other.range && type == other.type && text == other.text)
|
197
205
|
end
|
198
206
|
|
199
207
|
# (see TreeStand::Utils::Printer)
|
200
208
|
# Backed by {TreeStand::Utils::Printer}.
|
201
209
|
#
|
202
|
-
# @param pp [PP]
|
203
|
-
# @return [void]
|
204
|
-
#
|
205
210
|
# @see TreeStand::Utils::Printer
|
211
|
+
sig { params(pp: PP).void }
|
206
212
|
def pretty_print(pp)
|
207
213
|
Utils::Printer.new(ralign: 80).print(self, io: pp.output)
|
208
214
|
end
|
data/lib/tree_stand/parser.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
1
4
|
module TreeStand
|
2
5
|
# Wrapper around the TreeSitter parser. It looks up the parser by filename in
|
3
6
|
# the configured parsers directory.
|
@@ -12,12 +15,15 @@ module TreeStand
|
|
12
15
|
# # Looks for a parser in `path/to/parser/folder/ruby.{so,dylib}`
|
13
16
|
# ruby_parser = TreeStand::Parser.new("ruby")
|
14
17
|
class Parser
|
15
|
-
|
18
|
+
extend T::Sig
|
19
|
+
|
20
|
+
sig { returns(TreeSitter::Language) }
|
16
21
|
attr_reader :ts_language
|
17
|
-
|
22
|
+
sig { returns(TreeSitter::Parser) }
|
18
23
|
attr_reader :ts_parser
|
19
24
|
|
20
25
|
# @param language [String]
|
26
|
+
sig { params(language: String).void }
|
21
27
|
def initialize(language)
|
22
28
|
@language_string = language
|
23
29
|
@ts_language = TreeSitter::Language.load(
|
@@ -33,8 +39,7 @@ module TreeStand
|
|
33
39
|
# @param tree [TreeStand::Tree, nil] providing the old tree will allow the
|
34
40
|
# parser to take advantage of incremental parsing and improve performance
|
35
41
|
# by re-useing nodes from the old tree.
|
36
|
-
|
37
|
-
# @return [TreeStand::Tree]
|
42
|
+
sig { params(document: String, tree: T.nilable(TreeStand::Tree)).returns(TreeStand::Tree) }
|
38
43
|
def parse_string(document, tree: nil)
|
39
44
|
# @todo There's a bug with passing a non-nil tree
|
40
45
|
ts_tree = @ts_parser.parse_string(nil, document)
|
@@ -47,6 +52,7 @@ module TreeStand
|
|
47
52
|
#
|
48
53
|
# @see #parse_string
|
49
54
|
# @raise [TreeStand::InvalidDocument]
|
55
|
+
sig { params(document: String, tree: T.nilable(TreeStand::Tree)).returns(TreeStand::Tree) }
|
50
56
|
def parse_string!(document, tree: nil)
|
51
57
|
tree = parse_string(document, tree: tree)
|
52
58
|
return tree unless tree.any?(&:error?)
|
data/lib/tree_stand/range.rb
CHANGED
@@ -1,6 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
1
4
|
module TreeStand
|
2
5
|
# Wrapper around a TreeSitter range. This is mainly used to compare ranges.
|
3
6
|
class Range
|
7
|
+
extend T::Sig
|
8
|
+
|
4
9
|
# Point is a Struct containing the row and column from a TreeSitter point.
|
5
10
|
# TreeStand uses this to compare points.
|
6
11
|
# @!attribute [rw] row
|
@@ -9,16 +14,24 @@ module TreeStand
|
|
9
14
|
# @return [Integer]
|
10
15
|
Point = Struct.new(:row, :column)
|
11
16
|
|
12
|
-
|
17
|
+
sig { returns(Integer) }
|
13
18
|
attr_reader :start_byte
|
14
|
-
|
19
|
+
sig { returns(Integer) }
|
15
20
|
attr_reader :end_byte
|
16
|
-
|
21
|
+
sig { returns(TreeStand::Range::Point) }
|
17
22
|
attr_reader :start_point
|
18
|
-
|
23
|
+
sig { returns(TreeStand::Range::Point) }
|
19
24
|
attr_reader :end_point
|
20
25
|
|
21
26
|
# @api private
|
27
|
+
sig do
|
28
|
+
params(
|
29
|
+
start_byte: Integer,
|
30
|
+
end_byte: Integer,
|
31
|
+
start_point: T.any(TreeStand::Range::Point, TreeSitter::Point),
|
32
|
+
end_point: T.any(TreeStand::Range::Point, TreeSitter::Point),
|
33
|
+
).void
|
34
|
+
end
|
22
35
|
def initialize(start_byte:, end_byte:, start_point:, end_point:)
|
23
36
|
@start_byte = start_byte
|
24
37
|
@end_byte = end_byte
|
@@ -26,8 +39,7 @@ module TreeStand
|
|
26
39
|
@end_point = Point.new(end_point.row, end_point.column)
|
27
40
|
end
|
28
41
|
|
29
|
-
|
30
|
-
# @return [bool]
|
42
|
+
sig { params(other: Object).returns(T::Boolean) }
|
31
43
|
def ==(other)
|
32
44
|
return false unless other.is_a?(TreeStand::Range)
|
33
45
|
|
data/lib/tree_stand/tree.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
1
4
|
module TreeStand
|
2
5
|
# Wrapper around a TreeSitter tree.
|
3
6
|
#
|
@@ -22,25 +25,29 @@ module TreeStand
|
|
22
25
|
# This is not always possible and depends on the edits you make, beware that
|
23
26
|
# the tree will be different after each edit and this approach may cause bugs.
|
24
27
|
class Tree
|
28
|
+
extend T::Sig
|
25
29
|
extend Forwardable
|
26
30
|
include Enumerable
|
27
31
|
|
28
|
-
|
32
|
+
sig { returns(String) }
|
29
33
|
attr_reader :document
|
30
|
-
|
34
|
+
sig { returns(TreeSitter::Tree) }
|
31
35
|
attr_reader :ts_tree
|
32
|
-
|
36
|
+
sig { returns(TreeStand::Parser) }
|
33
37
|
attr_reader :parser
|
34
38
|
|
35
39
|
# @!method query(query_string)
|
40
|
+
# (see TreeStand::Node#query)
|
36
41
|
# @note This is a convenience method that calls {TreeStand::Node#query} on
|
37
42
|
# {#root_node}.
|
38
43
|
#
|
39
44
|
# @!method find_node(query_string)
|
45
|
+
# (see TreeStand::Node#find_node)
|
40
46
|
# @note This is a convenience method that calls {TreeStand::Node#find_node} on
|
41
47
|
# {#root_node}.
|
42
48
|
#
|
43
49
|
# @!method find_node!(query_string)
|
50
|
+
# (see TreeStand::Node#find_node!)
|
44
51
|
# @note This is a convenience method that calls {TreeStand::Node#find_node!} on
|
45
52
|
# {#root_node}.
|
46
53
|
#
|
@@ -69,13 +76,14 @@ module TreeStand
|
|
69
76
|
alias_method :each, :walk
|
70
77
|
|
71
78
|
# @api private
|
79
|
+
sig { params(parser: TreeStand::Parser, tree: TreeSitter::Tree, document: String).void }
|
72
80
|
def initialize(parser, tree, document)
|
73
81
|
@parser = parser
|
74
82
|
@ts_tree = tree
|
75
83
|
@document = document
|
76
84
|
end
|
77
85
|
|
78
|
-
|
86
|
+
sig { returns(TreeStand::Node) }
|
79
87
|
def root_node
|
80
88
|
TreeStand::Node.new(self, @ts_tree.root_node)
|
81
89
|
end
|
@@ -83,9 +91,7 @@ module TreeStand
|
|
83
91
|
# This method replaces the section of the document specified by range and
|
84
92
|
# replaces it with the provided text. Then it will reparse the document and
|
85
93
|
# update the tree!
|
86
|
-
|
87
|
-
# @param replacement [String]
|
88
|
-
# @return [void]
|
94
|
+
sig { params(range: TreeStand::Range, replacement: String).void }
|
89
95
|
def edit!(range, replacement)
|
90
96
|
new_document = +""
|
91
97
|
new_document << @document[0...range.start_byte]
|
@@ -96,8 +102,7 @@ module TreeStand
|
|
96
102
|
|
97
103
|
# This method deletes the section of the document specified by range. Then
|
98
104
|
# it will reparse the document and update the tree!
|
99
|
-
|
100
|
-
# @return [void]
|
105
|
+
sig { params(range: TreeStand::Range).void }
|
101
106
|
def delete!(range)
|
102
107
|
new_document = +""
|
103
108
|
new_document << @document[0...range.start_byte]
|
@@ -1,3 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
1
4
|
module TreeStand
|
2
5
|
# A collection of useful methods for working with syntax trees.
|
3
6
|
module Utils
|
@@ -11,26 +14,26 @@ module TreeStand
|
|
11
14
|
# # ("+") | +
|
12
15
|
# # right: (variable))) | x
|
13
16
|
class Printer
|
14
|
-
|
17
|
+
extend T::Sig
|
18
|
+
|
19
|
+
# @param ralign the right alignment for the text column.
|
20
|
+
sig { params(ralign: Integer).void }
|
15
21
|
def initialize(ralign:)
|
16
22
|
@ralign = ralign
|
17
23
|
end
|
18
24
|
|
19
25
|
# (see TreeStand::Utils::Printer)
|
20
|
-
|
21
|
-
# @param node [TreeStand::Node]
|
22
|
-
# @param io [IO]
|
23
|
-
# @return [IO]
|
26
|
+
sig { params(node: TreeStand::Node, io: T.any(IO, StringIO, String)).returns(T.any(IO, StringIO, String)) }
|
24
27
|
def print(node, io: StringIO.new)
|
25
28
|
lines = pretty_output_lines(node)
|
26
29
|
|
27
30
|
lines.each do |line|
|
28
31
|
if line.text.empty?
|
29
|
-
io
|
32
|
+
io << line.sexpr << "\n"
|
30
33
|
next
|
31
34
|
end
|
32
35
|
|
33
|
-
io
|
36
|
+
io << "#{line.sexpr}#{" " * (@ralign - line.sexpr.size)}| #{line.text}\n"
|
34
37
|
end
|
35
38
|
|
36
39
|
io
|
@@ -48,7 +51,7 @@ module TreeStand
|
|
48
51
|
return [Line.new("#{indent}#{prefix}#{ts_node}", node.text)]
|
49
52
|
end
|
50
53
|
|
51
|
-
lines = [Line.new("#{indent}#{prefix}(#{ts_node.type}", "")]
|
54
|
+
lines = T.let([Line.new("#{indent}#{prefix}(#{ts_node.type}", "")], T::Array[Line])
|
52
55
|
|
53
56
|
node.each.with_index do |child, index|
|
54
57
|
lines += if field_name = ts_node.field_name_for_child(index)
|
@@ -62,7 +65,7 @@ module TreeStand
|
|
62
65
|
end
|
63
66
|
end
|
64
67
|
|
65
|
-
lines.last.sexpr << ")"
|
68
|
+
T.must(lines.last).sexpr << ")"
|
66
69
|
lines
|
67
70
|
end
|
68
71
|
end
|