tree_stand 0.1.6 → 0.2.0
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 +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
|