ruby_tree_sitter 1.10.0-arm-linux-gnu → 1.11.0-arm-linux-gnu
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +14 -0
- data/exe/print_matches +20 -0
- data/exe/rbts +83 -0
- data/lib/tree_sitter/3.0/tree_sitter.so +0 -0
- data/lib/tree_sitter/3.1/tree_sitter.so +0 -0
- data/lib/tree_sitter/3.2/tree_sitter.so +0 -0
- data/lib/tree_sitter/{3.3 → 3.4}/tree_sitter.so +0 -0
- data/lib/tree_sitter/node.rb +90 -1
- data/lib/tree_sitter/version.rb +2 -2
- data/lib/tree_sitter.rb +2 -0
- data/lib/tree_stand/cli/options.rb +70 -0
- data/lib/tree_stand/cli.rb +16 -0
- data/lib/tree_stand/node.rb +7 -7
- data/lib/tree_stand.rb +2 -0
- data/tree_sitter.gemspec +4 -1
- metadata +26 -7
- data/lib/tree_stand/utils/printer.rb +0 -73
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5ef654309b2863745574ac6c7333fd5248585cd9d33a1845eb1613cd27e843c2
|
4
|
+
data.tar.gz: f8acb3d2dd3fbdd447c7a535af7110a594f807ea2fd65cee4d92871ce0951702
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ee6dc554f2147f10f188ed09dd0d322a990c6fad879443aecaa11e4dc4b5ff39dca494a18da87752db0eecc56acb342406b87cc97095afca7d3b94ef879624e2
|
7
|
+
data.tar.gz: 3c010398647f848c70e4dcc4ef82f586cc2d1fc29eb4af38b387bfe500e9a24b883f5dbb7b020c36a75a5ba46b800310eb9001e45ae24fae6947bc0033d0ea8f
|
data/README.md
CHANGED
@@ -175,6 +175,20 @@ You will have to install parsers yourself, either by:
|
|
175
175
|
[Faveod/tree-sitter-parsers](https://github.com/Faveod/tree-sitter-parsers)
|
176
176
|
which supports numerous architectures.
|
177
177
|
|
178
|
+
### Utilities
|
179
|
+
|
180
|
+
`ruby_tree_sitter` ships with some useful utility programs to help work with parsers & queries.
|
181
|
+
|
182
|
+
#### `rbts`
|
183
|
+
|
184
|
+
```sh
|
185
|
+
$ rbts --source SOURCE --query QUERY --parser PARSER
|
186
|
+
```
|
187
|
+
|
188
|
+
Watches a source and a query file and prints the matches when one of the files are updated. Uses [entr](https://github.com/eradman/entr), if available, otherwise [watch(1)](https://man7.org/linux/man-pages/man1/watch.1.html) is used by default or if --watch is specified.
|
189
|
+
|
190
|
+
See `rbts --help` for more information.
|
191
|
+
|
178
192
|
## Examples
|
179
193
|
|
180
194
|
See `examples` directory.
|
data/exe/print_matches
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'tree_stand/cli'
|
6
|
+
|
7
|
+
program = TreeStand::Cli::Options.new
|
8
|
+
OptionParser.new do |parser|
|
9
|
+
program.define_options(parser)
|
10
|
+
parser.parse!(ARGV)
|
11
|
+
end
|
12
|
+
program.check!
|
13
|
+
|
14
|
+
def main(program)
|
15
|
+
program.tree.query(program.query).each do |match|
|
16
|
+
pp match.transform_values(&:text)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
main(program)
|
data/exe/rbts
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'tree_stand/cli'
|
6
|
+
|
7
|
+
BANNER = <<~BANNER
|
8
|
+
Usage: rbts --source SOURCE --query QUERY --parser PARSER
|
9
|
+
|
10
|
+
Watches a source and a query file and prints the matches when one of the
|
11
|
+
files are updated. Uses entr, if available, otherwise watch(1) is used by
|
12
|
+
default or if --watch is specified.
|
13
|
+
|
14
|
+
Check out the tree-sitter repository & documentation for information on how
|
15
|
+
to write S-expression queries.
|
16
|
+
|
17
|
+
Repo: https://github.com/tree-sitter/tree-sitter
|
18
|
+
Docs: https://tree-sitter.github.io/tree-sitter/using-parsers/queries
|
19
|
+
|
20
|
+
Example:
|
21
|
+
|
22
|
+
rbts --source tmp/rbts/code.rb \\
|
23
|
+
--query tmp/rbts/query.scm \\
|
24
|
+
--parser tmp/rbts/ruby.so
|
25
|
+
BANNER
|
26
|
+
|
27
|
+
program = TreeStand::Cli::Options.new
|
28
|
+
OptionParser.new do |parser|
|
29
|
+
parser.banner = BANNER
|
30
|
+
parser.separator('')
|
31
|
+
program.define_options(parser)
|
32
|
+
parser.separator('')
|
33
|
+
parser.on_tail(
|
34
|
+
'-w',
|
35
|
+
'--watch',
|
36
|
+
"use watch(1) to continuously print matches, by default entr is used if it's available",
|
37
|
+
TrueClass,
|
38
|
+
) do |watch|
|
39
|
+
program.watch = watch
|
40
|
+
end
|
41
|
+
|
42
|
+
parser.parse!(ARGV)
|
43
|
+
end
|
44
|
+
program.check!
|
45
|
+
|
46
|
+
def print_matches = File.join(__dir__, 'print_matches')
|
47
|
+
|
48
|
+
# ls -1 SOURCE QUERY |
|
49
|
+
# entr -c exe/print_matches
|
50
|
+
# --source SOURCE
|
51
|
+
# --query QUERY
|
52
|
+
# --parser PARSER
|
53
|
+
def entr_cmd(source, query, parser)
|
54
|
+
[
|
55
|
+
'ls', '-1', source, query, '|',
|
56
|
+
'entr', '-c',
|
57
|
+
print_matches,
|
58
|
+
'--source', source,
|
59
|
+
'--query', query,
|
60
|
+
'--parser', parser
|
61
|
+
].join(' ')
|
62
|
+
end
|
63
|
+
|
64
|
+
# watch 'exe/print_matches --source SOURCE --query QUERY --parser PARSER'
|
65
|
+
def watch_cmd(source, query, parser)
|
66
|
+
[
|
67
|
+
'watch',
|
68
|
+
"'#{print_matches} --source #{source} --query #{query} --parser #{parser}'",
|
69
|
+
].join(' ')
|
70
|
+
end
|
71
|
+
|
72
|
+
def main(program)
|
73
|
+
cmd = if !program.watch && system('which', 'entr')
|
74
|
+
entr_cmd(program.source_file, program.query_file, program.parser_file)
|
75
|
+
else
|
76
|
+
watch_cmd(program.source_file, program.query_file, program.parser_file)
|
77
|
+
end
|
78
|
+
|
79
|
+
program.logger.debug("Running `#{cmd}`")
|
80
|
+
exec(cmd)
|
81
|
+
end
|
82
|
+
|
83
|
+
main(program)
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/lib/tree_sitter/node.rb
CHANGED
@@ -56,7 +56,7 @@ module TreeSitter
|
|
56
56
|
case k = keys.first
|
57
57
|
when Integer then named_child(k)
|
58
58
|
when String, Symbol
|
59
|
-
raise IndexError, "Cannot find field #{k}. Available: #{fields}" unless fields.include?(k.to_sym)
|
59
|
+
raise IndexError, "Cannot find field #{k.to_sym}. Available: #{fields.to_a}" unless fields.include?(k.to_sym)
|
60
60
|
|
61
61
|
child_by_field_name(k.to_s)
|
62
62
|
else raise ArgumentError, <<~ERR
|
@@ -163,5 +163,94 @@ module TreeSitter
|
|
163
163
|
end
|
164
164
|
fields.values_at(*keys)
|
165
165
|
end
|
166
|
+
|
167
|
+
# Regex for line annotation extraction from sexpr with source.
|
168
|
+
#
|
169
|
+
# @!visibility private
|
170
|
+
LINE_ANNOTATION = /\0\{(.*?)\0\}/
|
171
|
+
|
172
|
+
# Pretty-prints the node's sexp.
|
173
|
+
#
|
174
|
+
# The default call to {to_s} or {to_string} calls tree-sitter's
|
175
|
+
# `ts_node_string`. It's displayed on a single line, so reading a rich node
|
176
|
+
# becomes tiresome.
|
177
|
+
#
|
178
|
+
# This provides a better sexpr where you can control the "screen" width to
|
179
|
+
# decide when to break.
|
180
|
+
#
|
181
|
+
# @param indent [Integer]
|
182
|
+
# indentation for nested nodes.
|
183
|
+
# @param width [Integer]
|
184
|
+
# the screen's width.
|
185
|
+
# @param source [Nil|String]
|
186
|
+
# display source on the margin if not `nil`.
|
187
|
+
# @param vertical [Nil|Boolean]
|
188
|
+
# fit as much sexpr on a single line if `false`, else, go vertical.
|
189
|
+
# This is always `true` if `source` is not `nil`.
|
190
|
+
#
|
191
|
+
# @return [String] the pretty-printed sexpr.
|
192
|
+
def sexpr(indent: 2, width: 120, source: nil, vertical: nil)
|
193
|
+
res =
|
194
|
+
sexpr_recur(
|
195
|
+
indent: indent,
|
196
|
+
width: width,
|
197
|
+
source: source,
|
198
|
+
vertical: !source.nil? || !!vertical,
|
199
|
+
).output
|
200
|
+
return res if source.nil?
|
201
|
+
|
202
|
+
max_width = 0
|
203
|
+
res
|
204
|
+
.lines
|
205
|
+
.map { |line|
|
206
|
+
extracted = line.scan(LINE_ANNOTATION).flatten.first || ''
|
207
|
+
base = line.gsub(LINE_ANNOTATION, '').rstrip
|
208
|
+
max_width = [max_width, base.length].max
|
209
|
+
[base, extracted]
|
210
|
+
}
|
211
|
+
.map { |base, extracted|
|
212
|
+
("%-#{max_width}s | %s" % [base, extracted]).rstrip
|
213
|
+
}
|
214
|
+
.join("\n")
|
215
|
+
end
|
216
|
+
|
217
|
+
# Helper function for {sexpr}.
|
218
|
+
#
|
219
|
+
# @!visibility private
|
220
|
+
def sexpr_recur(indent: 2, width: 120, out: nil, source: nil, vertical: false)
|
221
|
+
out ||= Oppen::Wadler.new(width: width)
|
222
|
+
out.group(indent) {
|
223
|
+
out.text "(#{type}"
|
224
|
+
if source.is_a?(String) && child_count.zero?
|
225
|
+
out.text "\0{#{source.byteslice(start_byte...end_byte)}\0}", width: 0
|
226
|
+
end
|
227
|
+
brk(out, vertical) if child_count.positive?
|
228
|
+
each.with_index do |child, index|
|
229
|
+
if field_name = field_name_for_child(index)
|
230
|
+
out.text "#{field_name}:"
|
231
|
+
out.group(indent) {
|
232
|
+
brk(out, vertical)
|
233
|
+
child.sexpr_recur(indent: indent, width: width, out: out, vertical: vertical, source: source)
|
234
|
+
}
|
235
|
+
else
|
236
|
+
child.sexpr_recur(indent: indent, width: width, out: out, vertical: vertical, source: source)
|
237
|
+
end
|
238
|
+
brk(out, vertical) if index < child_count - 1
|
239
|
+
end
|
240
|
+
out.text ')'
|
241
|
+
}
|
242
|
+
out
|
243
|
+
end
|
244
|
+
|
245
|
+
# Break helper
|
246
|
+
#
|
247
|
+
# !@visibility private
|
248
|
+
def brk(out, vertical)
|
249
|
+
if vertical
|
250
|
+
out.break
|
251
|
+
else
|
252
|
+
out.breakable
|
253
|
+
end
|
254
|
+
end
|
166
255
|
end
|
167
256
|
end
|
data/lib/tree_sitter/version.rb
CHANGED
data/lib/tree_sitter.rb
CHANGED
@@ -23,6 +23,8 @@ require 'tree_sitter/query_matches'
|
|
23
23
|
require 'tree_sitter/query_predicate'
|
24
24
|
require 'tree_sitter/text_predicate_capture'
|
25
25
|
|
26
|
+
require 'oppen'
|
27
|
+
|
26
28
|
# TreeSitter is a Ruby interface to the tree-sitter parsing library.
|
27
29
|
module TreeSitter
|
28
30
|
extend Mixins::Language
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module TreeStand
|
5
|
+
module Cli
|
6
|
+
# @!visibility private
|
7
|
+
class Options
|
8
|
+
attr_accessor :source_file, :query_file, :parser_file, :watch
|
9
|
+
|
10
|
+
attr_reader :logger, :progname
|
11
|
+
|
12
|
+
def initialize(progname = File.basename($PROGRAM_NAME))
|
13
|
+
@logger = Logger.new($stderr, level: T.unsafe(ENV.fetch('LOG_LEVEL', Logger::INFO)), progname: progname)
|
14
|
+
@progname = progname
|
15
|
+
end
|
16
|
+
|
17
|
+
# @!visibility private
|
18
|
+
def define_options(parser)
|
19
|
+
parser.on_tail('-v', '--verbose', 'Enable verbose logging') { logger.level -= 1 }
|
20
|
+
|
21
|
+
parser.separator('Required options:')
|
22
|
+
parser.on('-s', '--source SOURCE', 'The filepath to the source code to be parsed') do |filepath|
|
23
|
+
self.source_file = error_no_file(File.expand_path(filepath), "Source file not found: #{filepath}", 1)
|
24
|
+
end
|
25
|
+
parser.on('-q', '--query QUERY', 'The filepath to the query to be run against the source code') do |filepath|
|
26
|
+
self.query_file = error_no_file(File.expand_path(filepath), "Query file not found: #{filepath}", 2)
|
27
|
+
end
|
28
|
+
parser.on('-p', '--parser PARSER', 'The parser to use to parse the source code') do |filepath|
|
29
|
+
self.parser_file = error_no_file(File.expand_path(filepath), "Parser file not found: #{filepath}", 3)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# @!visibility private
|
34
|
+
def check!
|
35
|
+
error!('No source file provided, specify with --source', 4) unless source_file
|
36
|
+
error!('No query file provided, specify with --query', 5) unless query_file
|
37
|
+
error!('No parser file provided, specify with --parser', 6) unless parser_file
|
38
|
+
|
39
|
+
parser_path = File.dirname(parser_file)
|
40
|
+
config = TreeStand.config
|
41
|
+
|
42
|
+
TreeStand.configure do
|
43
|
+
config.parser_path = parser_path
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# @!visibility private
|
48
|
+
def source = @source ||= File.read(source_file)
|
49
|
+
# @!visibility private
|
50
|
+
def query = @query ||= File.read(query_file)
|
51
|
+
# @!visibility private
|
52
|
+
def parser = @parser ||= TreeStand::Parser.new(File.extname(source_file).delete_prefix('.'))
|
53
|
+
# @!visibility private
|
54
|
+
def tree = @tree ||= parser.parse_string(source)
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def error_no_file(filepath, message, code)
|
59
|
+
return filepath if File.exist?(filepath)
|
60
|
+
|
61
|
+
error!(message, code)
|
62
|
+
end
|
63
|
+
|
64
|
+
def error!(message, code = 1)
|
65
|
+
logger.error(message)
|
66
|
+
exit(code)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
require 'logger'
|
5
|
+
require 'optparse'
|
6
|
+
require 'tree_stand'
|
7
|
+
|
8
|
+
require 'tree_stand/cli/options'
|
9
|
+
|
10
|
+
$stdout.sync = $stderr.sync = true
|
11
|
+
|
12
|
+
module TreeStand
|
13
|
+
# @!visibility private
|
14
|
+
module Cli
|
15
|
+
end
|
16
|
+
end
|
data/lib/tree_stand/node.rb
CHANGED
@@ -15,6 +15,8 @@ module TreeStand
|
|
15
15
|
# @return [Boolean] true if a syntax node has been edited.
|
16
16
|
# @!method child_count
|
17
17
|
# @return [Integer] the number of child nodes.
|
18
|
+
# @!method error?
|
19
|
+
# @return [bool] true if the node is an error node.
|
18
20
|
# @!method extra?
|
19
21
|
# @return [Boolean] true if the node is *extra* (e.g. comments).
|
20
22
|
# @!method has_error?
|
@@ -25,10 +27,10 @@ module TreeStand
|
|
25
27
|
# @return [Boolean] true if the node is not a literal in the grammar.
|
26
28
|
# @!method named_child_count
|
27
29
|
# @return [Integer] the number of *named* children.
|
30
|
+
# @!method sexpr
|
31
|
+
# @return [String] a pretty-printed sexpr.
|
28
32
|
# @!method type
|
29
33
|
# @return [Symbol] the type of the node in the tree-sitter grammar.
|
30
|
-
# @!method error?
|
31
|
-
# @return [bool] true if the node is an error node.
|
32
34
|
def_delegators(
|
33
35
|
:@ts_node,
|
34
36
|
:changed?,
|
@@ -39,6 +41,7 @@ module TreeStand
|
|
39
41
|
:missing?,
|
40
42
|
:named?,
|
41
43
|
:named_child_count,
|
44
|
+
:sexpr,
|
42
45
|
:type,
|
43
46
|
)
|
44
47
|
|
@@ -310,13 +313,10 @@ module TreeStand
|
|
310
313
|
T.must(range == other.range && type == other.type && text == other.text)
|
311
314
|
end
|
312
315
|
|
313
|
-
#
|
314
|
-
# Backed by {TreeStand::Utils::Printer}.
|
315
|
-
#
|
316
|
-
# @see TreeStand::Utils::Printer
|
316
|
+
# @see TreeSitter:Node::sexpr
|
317
317
|
sig { params(pp: PP).void }
|
318
318
|
def pretty_print(pp)
|
319
|
-
|
319
|
+
pp.output << sexpr(source: text)
|
320
320
|
end
|
321
321
|
|
322
322
|
private
|
data/lib/tree_stand.rb
CHANGED
@@ -10,6 +10,8 @@ require 'zeitwerk'
|
|
10
10
|
loader = Zeitwerk::Loader.for_gem
|
11
11
|
loader.ignore("#{__dir__}/tree_sitter")
|
12
12
|
loader.ignore("#{__dir__}/tree_sitter.rb")
|
13
|
+
loader.ignore("#{__dir__}/tree_stand/cli")
|
14
|
+
loader.ignore("#{__dir__}/tree_stand/cli.rb")
|
13
15
|
loader.setup
|
14
16
|
|
15
17
|
# TreeStand is a high-level Ruby wrapper for {https://tree-sitter.github.io/tree-sitter tree-sitter} bindings. It makes
|
data/tree_sitter.gemspec
CHANGED
@@ -6,7 +6,7 @@ $LOAD_PATH.unshift(lib) if !$LOAD_PATH.include?(lib)
|
|
6
6
|
require 'tree_sitter/version'
|
7
7
|
|
8
8
|
Gem::Specification.new do |spec|
|
9
|
-
spec.required_ruby_version = '>= 3.0'
|
9
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 3.0.0')
|
10
10
|
|
11
11
|
spec.authors = ['Firas al-Khalil', 'Derek Stride']
|
12
12
|
spec.email = ['firasalkhalil@gmail.com', 'derek@stride.host']
|
@@ -27,8 +27,11 @@ Gem::Specification.new do |spec|
|
|
27
27
|
spec.files = %w[LICENSE README.md tree_sitter.gemspec]
|
28
28
|
spec.files += Dir.glob('ext/**/*.{c,h,rb}')
|
29
29
|
spec.files += Dir.glob('lib/**/*.rb')
|
30
|
+
spec.bindir = 'exe'
|
31
|
+
spec.executables << 'rbts' << 'print_matches'
|
30
32
|
spec.require_paths = ['lib']
|
31
33
|
|
34
|
+
spec.add_dependency 'oppen', '0.9.8'
|
32
35
|
spec.add_dependency 'sorbet-runtime'
|
33
36
|
spec.add_dependency 'zeitwerk'
|
34
37
|
end
|
metadata
CHANGED
@@ -1,16 +1,30 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_tree_sitter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.11.0
|
5
5
|
platform: arm-linux-gnu
|
6
6
|
authors:
|
7
7
|
- Firas al-Khalil
|
8
8
|
- Derek Stride
|
9
9
|
autorequire:
|
10
|
-
bindir:
|
10
|
+
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2025-01-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: oppen
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - '='
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 0.9.8
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - '='
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 0.9.8
|
14
28
|
- !ruby/object:Gem::Dependency
|
15
29
|
name: sorbet-runtime
|
16
30
|
requirement: !ruby/object:Gem::Requirement
|
@@ -43,12 +57,16 @@ description:
|
|
43
57
|
email:
|
44
58
|
- firasalkhalil@gmail.com
|
45
59
|
- derek@stride.host
|
46
|
-
executables:
|
60
|
+
executables:
|
61
|
+
- rbts
|
62
|
+
- print_matches
|
47
63
|
extensions: []
|
48
64
|
extra_rdoc_files: []
|
49
65
|
files:
|
50
66
|
- LICENSE
|
51
67
|
- README.md
|
68
|
+
- exe/print_matches
|
69
|
+
- exe/rbts
|
52
70
|
- ext/tree_sitter/encoding.c
|
53
71
|
- ext/tree_sitter/extconf.rb
|
54
72
|
- ext/tree_sitter/input.c
|
@@ -77,7 +95,7 @@ files:
|
|
77
95
|
- lib/tree_sitter/3.0/tree_sitter.so
|
78
96
|
- lib/tree_sitter/3.1/tree_sitter.so
|
79
97
|
- lib/tree_sitter/3.2/tree_sitter.so
|
80
|
-
- lib/tree_sitter/3.
|
98
|
+
- lib/tree_sitter/3.4/tree_sitter.so
|
81
99
|
- lib/tree_sitter/error.rb
|
82
100
|
- lib/tree_sitter/helpers.rb
|
83
101
|
- lib/tree_sitter/mixins/language.rb
|
@@ -93,12 +111,13 @@ files:
|
|
93
111
|
- lib/tree_stand.rb
|
94
112
|
- lib/tree_stand/ast_modifier.rb
|
95
113
|
- lib/tree_stand/breadth_first_visitor.rb
|
114
|
+
- lib/tree_stand/cli.rb
|
115
|
+
- lib/tree_stand/cli/options.rb
|
96
116
|
- lib/tree_stand/config.rb
|
97
117
|
- lib/tree_stand/node.rb
|
98
118
|
- lib/tree_stand/parser.rb
|
99
119
|
- lib/tree_stand/range.rb
|
100
120
|
- lib/tree_stand/tree.rb
|
101
|
-
- lib/tree_stand/utils/printer.rb
|
102
121
|
- lib/tree_stand/version.rb
|
103
122
|
- lib/tree_stand/visitor.rb
|
104
123
|
- lib/tree_stand/visitors/tree_walker.rb
|
@@ -122,7 +141,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
122
141
|
version: '3.0'
|
123
142
|
- - "<"
|
124
143
|
- !ruby/object:Gem::Version
|
125
|
-
version: 3.
|
144
|
+
version: 3.5.dev
|
126
145
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
127
146
|
requirements:
|
128
147
|
- - ">="
|
@@ -1,73 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
# typed: true
|
3
|
-
|
4
|
-
module TreeStand
|
5
|
-
# A collection of useful methods for working with syntax trees.
|
6
|
-
module Utils
|
7
|
-
# Used to {TreeStand::Node#pretty_print pretty-print} the node.
|
8
|
-
#
|
9
|
-
# @example
|
10
|
-
# pp node
|
11
|
-
# # (expression
|
12
|
-
# # (sum
|
13
|
-
# # left: (number) | 1
|
14
|
-
# # ("+") | +
|
15
|
-
# # right: (variable))) | x
|
16
|
-
class Printer
|
17
|
-
extend T::Sig
|
18
|
-
|
19
|
-
# @param ralign the right alignment for the text column.
|
20
|
-
sig { params(ralign: Integer).void }
|
21
|
-
def initialize(ralign:)
|
22
|
-
@ralign = ralign
|
23
|
-
end
|
24
|
-
|
25
|
-
# (see TreeStand::Utils::Printer)
|
26
|
-
sig { params(node: TreeStand::Node, io: T.any(IO, StringIO, String)).returns(T.any(IO, StringIO, String)) }
|
27
|
-
def print(node, io: StringIO.new)
|
28
|
-
lines = pretty_output_lines(node)
|
29
|
-
|
30
|
-
lines.each do |line|
|
31
|
-
if line.text.empty?
|
32
|
-
io << line.sexpr << "\n"
|
33
|
-
next
|
34
|
-
end
|
35
|
-
|
36
|
-
io << "#{line.sexpr}#{' ' * [(@ralign - line.sexpr.size), 0].max}| #{line.text}\n"
|
37
|
-
end
|
38
|
-
|
39
|
-
io
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
Line = Struct.new(:sexpr, :text)
|
45
|
-
private_constant :Line
|
46
|
-
|
47
|
-
def pretty_output_lines(node, prefix: '', depth: 0)
|
48
|
-
indent = ' ' * depth
|
49
|
-
ts_node = node.ts_node
|
50
|
-
if indent.size + prefix.size + ts_node.to_s.size < @ralign || ts_node.child_count.zero?
|
51
|
-
return [Line.new("#{indent}#{prefix}#{ts_node}", node.text)]
|
52
|
-
end
|
53
|
-
|
54
|
-
lines = T.let([Line.new("#{indent}#{prefix}(#{ts_node.type}", '')], T::Array[Line])
|
55
|
-
|
56
|
-
node.each.with_index do |child, index|
|
57
|
-
lines += if field_name = ts_node.field_name_for_child(index)
|
58
|
-
pretty_output_lines(
|
59
|
-
child,
|
60
|
-
prefix: "#{field_name}: ",
|
61
|
-
depth: depth + 1,
|
62
|
-
)
|
63
|
-
else
|
64
|
-
pretty_output_lines(child, depth: depth + 1)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
T.must(lines.last).sexpr << ')'
|
69
|
-
lines
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|