syntax_tree 2.6.0 → 2.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +40 -1
- data/Gemfile.lock +6 -6
- data/README.md +15 -0
- data/lib/syntax_tree/basic_visitor.rb +78 -0
- data/lib/syntax_tree/cli.rb +133 -67
- data/lib/syntax_tree/formatter.rb +23 -4
- data/lib/syntax_tree/language_server/inlay_hints.rb +12 -1
- data/lib/syntax_tree/language_server.rb +4 -9
- data/lib/syntax_tree/node.rb +100 -50
- data/lib/syntax_tree/parser.rb +16 -14
- data/lib/syntax_tree/plugin/single_quotes.rb +1 -2
- data/lib/syntax_tree/plugin/trailing_comma.rb +1 -2
- data/lib/syntax_tree/version.rb +1 -1
- data/lib/syntax_tree/visitor/field_visitor.rb +5 -3
- data/lib/syntax_tree/visitor.rb +4 -66
- data/lib/syntax_tree.rb +3 -0
- metadata +4 -5
- data/lib/syntax_tree/formatter/single_quotes.rb +0 -13
- data/lib/syntax_tree/formatter/trailing_comma.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ad3c35843b6e3148499ac001d05a9c5d613d69debff5fdc411dff89a9adc5ca4
|
4
|
+
data.tar.gz: 570ae99edf8b17b5d205142872621b0551632d29ac4671ee14fed8415cf9cfe4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7ec0e9d8f5f4f828ba0d451d49bd7fa9f5576570f7427c91973503e795fe068e21a61dac4f1880be12dbd551855eb0b8d27f359a9a682b964f3183aaf687e1da
|
7
|
+
data.tar.gz: b8ae5c0132ebc445d060b1d5cef477253c724ea2c7a22a536d4ea78b3fb2d905c560e1f044f8843b92f79e0dbc1a434cda2a38e76b2732b426719150785a59af
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,42 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [2.8.0] - 2022-06-21
|
10
|
+
|
11
|
+
### Added
|
12
|
+
|
13
|
+
- [#95](https://github.com/ruby-syntax-tree/syntax_tree/pull/95) - The `HeredocEnd` node has been added which effectively results in the ability to determine the location of the ending of a heredoc from source.
|
14
|
+
- [#99](https://github.com/ruby-syntax-tree/syntax_tree/pull/99) - The LSP now allows you to pass the same configuration options as the other CLI commands which allows formatting to be modified in the VSCode extension.
|
15
|
+
- [#100](https://github.com/ruby-syntax-tree/syntax_tree/pull/100) - The LSP now explicitly responds to the shutdown request so that VSCode never deadlocks.
|
16
|
+
|
17
|
+
### Changed
|
18
|
+
|
19
|
+
- [#96](https://github.com/ruby-syntax-tree/syntax_tree/pull/96) - The CLI now runs in parallel by default. There is a worker created for each processor on the running machine (as determined by `Etc.nprocessors`).
|
20
|
+
- [#97](https://github.com/ruby-syntax-tree/syntax_tree/pull/97) - Syntax Tree now handles the case where `DidYouMean` is not available for whatever reason, as well as handles the newer `detailed_message` API for errors.
|
21
|
+
|
22
|
+
## [2.7.1] - 2022-05-25
|
23
|
+
|
24
|
+
### Added
|
25
|
+
|
26
|
+
- [#92](https://github.com/ruby-syntax-tree/syntax_tree/pull/92) - (Internal) Drastically increase test coverage, including many more tests for the language server and the CLI.
|
27
|
+
|
28
|
+
### Changed
|
29
|
+
|
30
|
+
- [#87](https://github.com/ruby-syntax-tree/syntax_tree/pull/87) - Don't convert quotes on strings if it would result in more escapes.
|
31
|
+
- [#91](https://github.com/ruby-syntax-tree/syntax_tree/pull/91) - Always use `[]` with array patterns. There are just too many edge cases where you have to use them anyway. This simplifies the look and makes it more consistent.
|
32
|
+
- [#92](https://github.com/ruby-syntax-tree/syntax_tree/pull/92) - Remodel the currently shipped plugins such that they're modifying an options hash instead of overriding methods. This should make it easier for other plugins to reference the already loaded plugins, e.g., the RBS plugin referencing the quotes.
|
33
|
+
- [#92](https://github.com/ruby-syntax-tree/syntax_tree/pull/92) - Fix up the language server inlay hints to continue walking the tree once a pattern is found. This should increase useability.
|
34
|
+
|
35
|
+
## [2.7.0] - 2022-05-19
|
36
|
+
|
37
|
+
### Added
|
38
|
+
|
39
|
+
- [#88](https://github.com/ruby-syntax-tree/syntax_tree/pull/88) - Provide a `SyntaxTree::BasicVisitor` that has no visit methods implemented.
|
40
|
+
|
41
|
+
### Changed
|
42
|
+
|
43
|
+
- [#90](https://github.com/ruby-syntax-tree/syntax_tree/pull/90) - Provide better formatting for `SyntaxTree::AryPtn` when its nested inside a `SyntaxTree::RAssign`.
|
44
|
+
|
9
45
|
## [2.6.0] - 2022-05-16
|
10
46
|
|
11
47
|
### Added
|
@@ -236,7 +272,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
|
|
236
272
|
|
237
273
|
- 🎉 Initial release! 🎉
|
238
274
|
|
239
|
-
[unreleased]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.
|
275
|
+
[unreleased]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.8.0...HEAD
|
276
|
+
[2.8.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.7.1...v2.8.0
|
277
|
+
[2.7.1]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.7.0...v2.7.1
|
278
|
+
[2.7.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.6.0...v2.7.0
|
240
279
|
[2.6.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.5.0...v2.6.0
|
241
280
|
[2.5.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.4.1...v2.5.0
|
242
281
|
[2.4.1]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.4.0...v2.4.1
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
syntax_tree (2.
|
4
|
+
syntax_tree (2.8.0)
|
5
5
|
prettier_print
|
6
6
|
|
7
7
|
GEM
|
@@ -9,25 +9,25 @@ GEM
|
|
9
9
|
specs:
|
10
10
|
ast (2.4.2)
|
11
11
|
docile (1.4.0)
|
12
|
-
minitest (5.
|
12
|
+
minitest (5.16.0)
|
13
13
|
parallel (1.22.1)
|
14
14
|
parser (3.1.2.0)
|
15
15
|
ast (~> 2.4.1)
|
16
16
|
prettier_print (0.1.0)
|
17
17
|
rainbow (3.1.1)
|
18
18
|
rake (13.0.6)
|
19
|
-
regexp_parser (2.
|
19
|
+
regexp_parser (2.5.0)
|
20
20
|
rexml (3.2.5)
|
21
|
-
rubocop (1.
|
21
|
+
rubocop (1.30.1)
|
22
22
|
parallel (~> 1.10)
|
23
23
|
parser (>= 3.1.0.0)
|
24
24
|
rainbow (>= 2.2.2, < 4.0)
|
25
25
|
regexp_parser (>= 1.8, < 3.0)
|
26
26
|
rexml (>= 3.2.5, < 4.0)
|
27
|
-
rubocop-ast (>= 1.
|
27
|
+
rubocop-ast (>= 1.18.0, < 2.0)
|
28
28
|
ruby-progressbar (~> 1.7)
|
29
29
|
unicode-display_width (>= 1.4.0, < 3.0)
|
30
|
-
rubocop-ast (1.
|
30
|
+
rubocop-ast (1.18.0)
|
31
31
|
parser (>= 3.1.1.0)
|
32
32
|
ruby-progressbar (1.11.0)
|
33
33
|
simplecov (0.21.2)
|
data/README.md
CHANGED
@@ -32,6 +32,7 @@ It is built with only standard library dependencies. It additionally ships with
|
|
32
32
|
- [construct_keys](#construct_keys)
|
33
33
|
- [Visitor](#visitor)
|
34
34
|
- [visit_method](#visit_method)
|
35
|
+
- [BasicVisitor](#basicvisitor)
|
35
36
|
- [Language server](#language-server)
|
36
37
|
- [textDocument/formatting](#textdocumentformatting)
|
37
38
|
- [textDocument/inlayHints](#textdocumentinlayhints)
|
@@ -373,6 +374,20 @@ Did you mean? visit_binary
|
|
373
374
|
from bin/console:8:in `<main>'
|
374
375
|
```
|
375
376
|
|
377
|
+
### BasicVisitor
|
378
|
+
|
379
|
+
When you're defining your own visitor, by default it will walk down the tree even if you don't define `visit_*` methods. This is to ensure you can define a subset of the necessary methods in order to only interact with the nodes you're interested in. If you'd like to change this default to instead raise an error if you visit a node you haven't explicitly handled, you can instead inherit from `BasicVisitor`.
|
380
|
+
|
381
|
+
```ruby
|
382
|
+
class MyVisitor < SyntaxTree::BasicVisitor
|
383
|
+
def visit_int(node)
|
384
|
+
# ...
|
385
|
+
end
|
386
|
+
end
|
387
|
+
```
|
388
|
+
|
389
|
+
The visitor defined above will error out unless it's only visiting a `SyntaxTree::Int` node. This is useful in a couple of ways, e.g., if you're trying to define a visitor to handle the whole tree but it's currently a work-in-progress.
|
390
|
+
|
376
391
|
## Language server
|
377
392
|
|
378
393
|
Syntax Tree additionally ships with a language server conforming to the [language server protocol](https://microsoft.github.io/language-server-protocol/). It can be invoked through the CLI by running:
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SyntaxTree
|
4
|
+
# BasicVisitor is the parent class of the Visitor class that provides the
|
5
|
+
# ability to walk down the tree. It does not define any handlers, so you
|
6
|
+
# should extend this class if you want your visitor to raise an error if you
|
7
|
+
# attempt to visit a node that you don't handle.
|
8
|
+
class BasicVisitor
|
9
|
+
# This is raised when you use the Visitor.visit_method method and it fails.
|
10
|
+
# It is correctable to through DidYouMean.
|
11
|
+
class VisitMethodError < StandardError
|
12
|
+
attr_reader :visit_method
|
13
|
+
|
14
|
+
def initialize(visit_method)
|
15
|
+
@visit_method = visit_method
|
16
|
+
super("Invalid visit method: #{visit_method}")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# This class is used by DidYouMean to offer corrections to invalid visit
|
21
|
+
# method names.
|
22
|
+
class VisitMethodChecker
|
23
|
+
attr_reader :visit_method
|
24
|
+
|
25
|
+
def initialize(error)
|
26
|
+
@visit_method = error.visit_method
|
27
|
+
end
|
28
|
+
|
29
|
+
def corrections
|
30
|
+
@corrections ||=
|
31
|
+
DidYouMean::SpellChecker.new(
|
32
|
+
dictionary: Visitor.visit_methods
|
33
|
+
).correct(visit_method)
|
34
|
+
end
|
35
|
+
|
36
|
+
# In some setups with Ruby you can turn off DidYouMean, so we're going to
|
37
|
+
# respect that setting here.
|
38
|
+
if defined?(DidYouMean) && DidYouMean.method_defined?(:correct_error)
|
39
|
+
DidYouMean.correct_error(VisitMethodError, self)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class << self
|
44
|
+
# This method is here to help folks write visitors.
|
45
|
+
#
|
46
|
+
# It's not always easy to ensure you're writing the correct method name in
|
47
|
+
# the visitor since it's perfectly valid to define methods that don't
|
48
|
+
# override these parent methods.
|
49
|
+
#
|
50
|
+
# If you use this method, you can ensure you're writing the correct method
|
51
|
+
# name. It will raise an error if the visit method you're defining isn't
|
52
|
+
# actually a method on the parent visitor.
|
53
|
+
def visit_method(method_name)
|
54
|
+
return if visit_methods.include?(method_name)
|
55
|
+
|
56
|
+
raise VisitMethodError, method_name
|
57
|
+
end
|
58
|
+
|
59
|
+
# This is the list of all of the valid visit methods.
|
60
|
+
def visit_methods
|
61
|
+
@visit_methods ||=
|
62
|
+
Visitor.instance_methods.grep(/^visit_(?!child_nodes)/)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def visit(node)
|
67
|
+
node&.accept(self)
|
68
|
+
end
|
69
|
+
|
70
|
+
def visit_all(nodes)
|
71
|
+
nodes.map { |node| visit(node) }
|
72
|
+
end
|
73
|
+
|
74
|
+
def visit_child_nodes(node)
|
75
|
+
visit_all(node.child_nodes)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/syntax_tree/cli.rb
CHANGED
@@ -34,9 +34,41 @@ module SyntaxTree
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
+
# An item of work that corresponds to a file to be processed.
|
38
|
+
class FileItem
|
39
|
+
attr_reader :filepath
|
40
|
+
|
41
|
+
def initialize(filepath)
|
42
|
+
@filepath = filepath
|
43
|
+
end
|
44
|
+
|
45
|
+
def handler
|
46
|
+
HANDLERS[File.extname(filepath)]
|
47
|
+
end
|
48
|
+
|
49
|
+
def source
|
50
|
+
handler.read(filepath)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# An item of work that corresponds to the stdin content.
|
55
|
+
class STDINItem
|
56
|
+
def handler
|
57
|
+
HANDLERS[".rb"]
|
58
|
+
end
|
59
|
+
|
60
|
+
def filepath
|
61
|
+
:stdin
|
62
|
+
end
|
63
|
+
|
64
|
+
def source
|
65
|
+
$stdin.read
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
37
69
|
# The parent action class for the CLI that implements the basics.
|
38
70
|
class Action
|
39
|
-
def run(
|
71
|
+
def run(item)
|
40
72
|
end
|
41
73
|
|
42
74
|
def success
|
@@ -48,8 +80,8 @@ module SyntaxTree
|
|
48
80
|
|
49
81
|
# An action of the CLI that prints out the AST for the given source.
|
50
82
|
class AST < Action
|
51
|
-
def run(
|
52
|
-
pp handler.parse(source)
|
83
|
+
def run(item)
|
84
|
+
pp item.handler.parse(item.source)
|
53
85
|
end
|
54
86
|
end
|
55
87
|
|
@@ -59,10 +91,11 @@ module SyntaxTree
|
|
59
91
|
class UnformattedError < StandardError
|
60
92
|
end
|
61
93
|
|
62
|
-
def run(
|
63
|
-
|
94
|
+
def run(item)
|
95
|
+
source = item.source
|
96
|
+
raise UnformattedError if source != item.handler.format(source)
|
64
97
|
rescue StandardError
|
65
|
-
warn("[#{Color.yellow("warn")}] #{filepath}")
|
98
|
+
warn("[#{Color.yellow("warn")}] #{item.filepath}")
|
66
99
|
raise
|
67
100
|
end
|
68
101
|
|
@@ -81,9 +114,11 @@ module SyntaxTree
|
|
81
114
|
class NonIdempotentFormatError < StandardError
|
82
115
|
end
|
83
116
|
|
84
|
-
def run(
|
85
|
-
|
86
|
-
|
117
|
+
def run(item)
|
118
|
+
handler = item.handler
|
119
|
+
|
120
|
+
warning = "[#{Color.yellow("warn")}] #{item.filepath}"
|
121
|
+
formatted = handler.format(item.source)
|
87
122
|
|
88
123
|
raise NonIdempotentFormatError if formatted != handler.format(formatted)
|
89
124
|
rescue StandardError
|
@@ -102,25 +137,27 @@ module SyntaxTree
|
|
102
137
|
|
103
138
|
# An action of the CLI that prints out the doc tree IR for the given source.
|
104
139
|
class Doc < Action
|
105
|
-
def run(
|
140
|
+
def run(item)
|
141
|
+
source = item.source
|
142
|
+
|
106
143
|
formatter = Formatter.new(source, [])
|
107
|
-
handler.parse(source).format(formatter)
|
144
|
+
item.handler.parse(source).format(formatter)
|
108
145
|
pp formatter.groups.first
|
109
146
|
end
|
110
147
|
end
|
111
148
|
|
112
149
|
# An action of the CLI that formats the input source and prints it out.
|
113
150
|
class Format < Action
|
114
|
-
def run(
|
115
|
-
puts handler.format(source)
|
151
|
+
def run(item)
|
152
|
+
puts item.handler.format(item.source)
|
116
153
|
end
|
117
154
|
end
|
118
155
|
|
119
156
|
# An action of the CLI that converts the source into its equivalent JSON
|
120
157
|
# representation.
|
121
158
|
class Json < Action
|
122
|
-
def run(
|
123
|
-
object = Visitor::JSONVisitor.new.visit(handler.parse(source))
|
159
|
+
def run(item)
|
160
|
+
object = Visitor::JSONVisitor.new.visit(item.handler.parse(item.source))
|
124
161
|
puts JSON.pretty_generate(object)
|
125
162
|
end
|
126
163
|
end
|
@@ -128,27 +165,28 @@ module SyntaxTree
|
|
128
165
|
# An action of the CLI that outputs a pattern-matching Ruby expression that
|
129
166
|
# would match the input given.
|
130
167
|
class Match < Action
|
131
|
-
def run(
|
132
|
-
puts handler.parse(source).construct_keys
|
168
|
+
def run(item)
|
169
|
+
puts item.handler.parse(item.source).construct_keys
|
133
170
|
end
|
134
171
|
end
|
135
172
|
|
136
173
|
# An action of the CLI that formats the input source and writes the
|
137
174
|
# formatted output back to the file.
|
138
175
|
class Write < Action
|
139
|
-
def run(
|
140
|
-
|
176
|
+
def run(item)
|
177
|
+
filepath = item.filepath
|
141
178
|
start = Time.now
|
142
179
|
|
143
|
-
|
180
|
+
source = item.source
|
181
|
+
formatted = item.handler.format(source)
|
144
182
|
File.write(filepath, formatted) if filepath != :stdin
|
145
183
|
|
146
184
|
color = source == formatted ? Color.gray(filepath) : filepath
|
147
185
|
delta = ((Time.now - start) * 1000).round
|
148
186
|
|
149
|
-
puts "
|
187
|
+
puts "#{color} #{delta}ms"
|
150
188
|
rescue StandardError
|
151
|
-
puts
|
189
|
+
puts filepath
|
152
190
|
raise
|
153
191
|
end
|
154
192
|
end
|
@@ -180,7 +218,7 @@ module SyntaxTree
|
|
180
218
|
#{Color.bold("stree help")}
|
181
219
|
Display this help message
|
182
220
|
|
183
|
-
#{Color.bold("stree lsp")}
|
221
|
+
#{Color.bold("stree lsp [OPTIONS]")}
|
184
222
|
Run syntax tree in language server mode
|
185
223
|
|
186
224
|
#{Color.bold("stree version")}
|
@@ -201,6 +239,20 @@ module SyntaxTree
|
|
201
239
|
def run(argv)
|
202
240
|
name, *arguments = argv
|
203
241
|
|
242
|
+
# If there are any plugins specified on the command line, then load them
|
243
|
+
# by requiring them here. We do this by transforming something like
|
244
|
+
#
|
245
|
+
# stree format --plugins=haml template.haml
|
246
|
+
#
|
247
|
+
# into
|
248
|
+
#
|
249
|
+
# require "syntax_tree/haml"
|
250
|
+
#
|
251
|
+
if arguments.first&.start_with?("--plugins=")
|
252
|
+
plugins = arguments.shift[/^--plugins=(.*)$/, 1]
|
253
|
+
plugins.split(",").each { |plugin| require "syntax_tree/#{plugin}" }
|
254
|
+
end
|
255
|
+
|
204
256
|
case name
|
205
257
|
when "help"
|
206
258
|
puts HELP
|
@@ -244,38 +296,41 @@ module SyntaxTree
|
|
244
296
|
return 1
|
245
297
|
end
|
246
298
|
|
247
|
-
#
|
248
|
-
|
249
|
-
#
|
250
|
-
# stree format --plugins=haml template.haml
|
251
|
-
#
|
252
|
-
# into
|
253
|
-
#
|
254
|
-
# require "syntax_tree/haml"
|
255
|
-
#
|
256
|
-
if arguments.first&.start_with?("--plugins=")
|
257
|
-
plugins = arguments.shift[/^--plugins=(.*)$/, 1]
|
258
|
-
plugins.split(",").each { |plugin| require "syntax_tree/#{plugin}" }
|
259
|
-
end
|
299
|
+
# We're going to build up a queue of items to process.
|
300
|
+
queue = Queue.new
|
260
301
|
|
261
|
-
#
|
262
|
-
#
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
rescue StandardError => error
|
274
|
-
warn(error.message)
|
275
|
-
warn(error.backtrace)
|
276
|
-
errored = true
|
302
|
+
# If we're reading from stdin, then we'll just add the stdin object to
|
303
|
+
# the queue. Otherwise, we'll add each of the filepaths to the queue.
|
304
|
+
if $stdin.tty? || arguments.any?
|
305
|
+
arguments.each do |pattern|
|
306
|
+
Dir
|
307
|
+
.glob(pattern)
|
308
|
+
.each do |filepath|
|
309
|
+
queue << FileItem.new(filepath) if File.file?(filepath)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
else
|
313
|
+
queue << STDINItem.new
|
277
314
|
end
|
278
315
|
|
316
|
+
# At the end, we're going to return whether or not this worker ever
|
317
|
+
# encountered an error.
|
318
|
+
errored =
|
319
|
+
with_workers(queue) do |item|
|
320
|
+
action.run(item)
|
321
|
+
false
|
322
|
+
rescue Parser::ParseError => error
|
323
|
+
warn("Error: #{error.message}")
|
324
|
+
highlight_error(error, item.source)
|
325
|
+
true
|
326
|
+
rescue Check::UnformattedError, Debug::NonIdempotentFormatError
|
327
|
+
true
|
328
|
+
rescue StandardError => error
|
329
|
+
warn(error.message)
|
330
|
+
warn(error.backtrace)
|
331
|
+
true
|
332
|
+
end
|
333
|
+
|
279
334
|
if errored
|
280
335
|
action.failure
|
281
336
|
1
|
@@ -287,22 +342,33 @@ module SyntaxTree
|
|
287
342
|
|
288
343
|
private
|
289
344
|
|
290
|
-
def
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
345
|
+
def with_workers(queue)
|
346
|
+
# If the queue is just 1 item, then we're not going to bother going
|
347
|
+
# through the whole ceremony of parallelizing the work.
|
348
|
+
return yield queue.shift if queue.size == 1
|
349
|
+
|
350
|
+
workers =
|
351
|
+
Etc.nprocessors.times.map do
|
352
|
+
Thread.new do
|
353
|
+
# Propagate errors in the worker threads up to the parent thread.
|
354
|
+
Thread.current.abort_on_exception = true
|
355
|
+
|
356
|
+
# Track whether or not there are any errors from any of the files
|
357
|
+
# that we take action on so that we can properly clean up and
|
358
|
+
# exit.
|
359
|
+
errored = false
|
360
|
+
|
361
|
+
# While there is still work left to do, shift off the queue and
|
362
|
+
# process the item.
|
363
|
+
(errored ||= yield queue.shift) until queue.empty?
|
364
|
+
|
365
|
+
# At the end, we're going to return whether or not this worker
|
366
|
+
# ever encountered an error.
|
367
|
+
errored
|
368
|
+
end
|
302
369
|
end
|
303
|
-
|
304
|
-
|
305
|
-
end
|
370
|
+
|
371
|
+
workers.inject(false) { |accum, thread| accum || thread.value }
|
306
372
|
end
|
307
373
|
|
308
374
|
# Highlights a snippet from a source and parse error.
|
@@ -4,6 +4,18 @@ module SyntaxTree
|
|
4
4
|
# A slightly enhanced PP that knows how to format recursively including
|
5
5
|
# comments.
|
6
6
|
class Formatter < PrettierPrint
|
7
|
+
# We want to minimize as much as possible the number of options that are
|
8
|
+
# available in syntax tree. For the most part, if users want non-default
|
9
|
+
# formatting, they should override the format methods on the specific nodes
|
10
|
+
# themselves. However, because of some history with prettier and the fact
|
11
|
+
# that folks have become entrenched in their ways, we decided to provide a
|
12
|
+
# small amount of configurability.
|
13
|
+
#
|
14
|
+
# Note that we're keeping this in a global-ish hash instead of just
|
15
|
+
# overriding methods on classes so that other plugins can reference this if
|
16
|
+
# necessary. For example, the RBS plugin references the quote style.
|
17
|
+
OPTIONS = { quote: "\"", trailing_comma: false }
|
18
|
+
|
7
19
|
COMMENT_PRIORITY = 1
|
8
20
|
HEREDOC_PRIORITY = 2
|
9
21
|
|
@@ -14,13 +26,20 @@ module SyntaxTree
|
|
14
26
|
attr_reader :quote, :trailing_comma
|
15
27
|
alias trailing_comma? trailing_comma
|
16
28
|
|
17
|
-
def initialize(
|
18
|
-
|
29
|
+
def initialize(
|
30
|
+
source,
|
31
|
+
*args,
|
32
|
+
quote: OPTIONS[:quote],
|
33
|
+
trailing_comma: OPTIONS[:trailing_comma]
|
34
|
+
)
|
35
|
+
super(*args)
|
19
36
|
|
20
37
|
@source = source
|
21
38
|
@stack = []
|
22
|
-
|
23
|
-
|
39
|
+
|
40
|
+
# Memoizing these values per formatter to make access faster.
|
41
|
+
@quote = quote
|
42
|
+
@trailing_comma = trailing_comma
|
24
43
|
end
|
25
44
|
|
26
45
|
def self.format(source, node)
|
@@ -38,6 +38,7 @@ module SyntaxTree
|
|
38
38
|
#
|
39
39
|
def visit_assign(node)
|
40
40
|
parentheses(node.location) if stack[-2].is_a?(Params)
|
41
|
+
super
|
41
42
|
end
|
42
43
|
|
43
44
|
# Adds parentheses around binary expressions to make it clear which
|
@@ -57,6 +58,8 @@ module SyntaxTree
|
|
57
58
|
parentheses(node.location)
|
58
59
|
else
|
59
60
|
end
|
61
|
+
|
62
|
+
super
|
60
63
|
end
|
61
64
|
|
62
65
|
# Adds parentheses around ternary operators contained within certain
|
@@ -70,9 +73,13 @@ module SyntaxTree
|
|
70
73
|
# a ? b : ₍c ? d : e₎
|
71
74
|
#
|
72
75
|
def visit_if_op(node)
|
73
|
-
|
76
|
+
case stack[-2]
|
77
|
+
in Assign | Binary | IfOp | OpAssign
|
74
78
|
parentheses(node.location)
|
79
|
+
else
|
75
80
|
end
|
81
|
+
|
82
|
+
super
|
76
83
|
end
|
77
84
|
|
78
85
|
# Adds the implicitly rescued StandardError into a bare rescue clause. For
|
@@ -92,6 +99,8 @@ module SyntaxTree
|
|
92
99
|
if node.exception.nil?
|
93
100
|
after[node.location.start_char + "rescue".length] << " StandardError"
|
94
101
|
end
|
102
|
+
|
103
|
+
super
|
95
104
|
end
|
96
105
|
|
97
106
|
# Adds parentheses around unary statements using the - operator that are
|
@@ -107,6 +116,8 @@ module SyntaxTree
|
|
107
116
|
if stack[-2].is_a?(Binary) && (node.operator == "-")
|
108
117
|
parentheses(node.location)
|
109
118
|
end
|
119
|
+
|
120
|
+
super
|
110
121
|
end
|
111
122
|
|
112
123
|
def self.find(program)
|
@@ -36,8 +36,9 @@ module SyntaxTree
|
|
36
36
|
write(id: id, result: { capabilities: capabilities })
|
37
37
|
in method: "initialized"
|
38
38
|
# ignored
|
39
|
-
in method: "shutdown"
|
39
|
+
in method: "shutdown" # tolerate missing ID to be a good citizen
|
40
40
|
store.clear
|
41
|
+
write(id: request[:id], result: {})
|
41
42
|
return
|
42
43
|
in {
|
43
44
|
method: "textDocument/didChange",
|
@@ -70,13 +71,11 @@ module SyntaxTree
|
|
70
71
|
id:,
|
71
72
|
params: { textDocument: { uri: } }
|
72
73
|
}
|
73
|
-
|
74
|
-
PP.pp(SyntaxTree.parse(store[uri]), output)
|
75
|
-
write(id: id, result: output.join)
|
74
|
+
write(id: id, result: PP.pp(SyntaxTree.parse(store[uri]), +""))
|
76
75
|
in method: %r{\$/.+}
|
77
76
|
# ignored
|
78
77
|
else
|
79
|
-
raise "Unhandled: #{request}"
|
78
|
+
raise ArgumentError, "Unhandled: #{request}"
|
80
79
|
end
|
81
80
|
end
|
82
81
|
end
|
@@ -109,10 +108,6 @@ module SyntaxTree
|
|
109
108
|
}
|
110
109
|
end
|
111
110
|
|
112
|
-
def log(message)
|
113
|
-
write(method: "window/logMessage", params: { type: 4, message: message })
|
114
|
-
end
|
115
|
-
|
116
111
|
def inlay_hints(source)
|
117
112
|
inlay_hints = InlayHints.find(SyntaxTree.parse(source))
|
118
113
|
serialize = ->(position, text) { { position: position, text: text } }
|
data/lib/syntax_tree/node.rb
CHANGED
@@ -1123,30 +1123,20 @@ module SyntaxTree
|
|
1123
1123
|
end
|
1124
1124
|
|
1125
1125
|
def format(q)
|
1126
|
-
|
1127
|
-
|
1128
|
-
|
1126
|
+
q.group do
|
1127
|
+
q.format(constant) if constant
|
1128
|
+
q.text("[")
|
1129
|
+
q.indent do
|
1130
|
+
q.breakable("")
|
1131
|
+
|
1132
|
+
parts = [*requireds]
|
1133
|
+
parts << RestFormatter.new(rest) if rest
|
1134
|
+
parts += posts
|
1129
1135
|
|
1130
|
-
if constant
|
1131
|
-
q.group do
|
1132
|
-
q.format(constant)
|
1133
|
-
q.text("[")
|
1134
1136
|
q.seplist(parts) { |part| q.format(part) }
|
1135
|
-
q.text("]")
|
1136
1137
|
end
|
1137
|
-
|
1138
|
-
return
|
1139
|
-
end
|
1140
|
-
|
1141
|
-
parent = q.parent
|
1142
|
-
if parts.length == 1 || PATTERNS.include?(parent.class)
|
1143
|
-
q.text("[")
|
1144
|
-
q.seplist(parts) { |part| q.format(part) }
|
1138
|
+
q.breakable("")
|
1145
1139
|
q.text("]")
|
1146
|
-
elsif parts.empty?
|
1147
|
-
q.text("[]")
|
1148
|
-
else
|
1149
|
-
q.group { q.seplist(parts) { |part| q.format(part) } }
|
1150
1140
|
end
|
1151
1141
|
end
|
1152
1142
|
end
|
@@ -2139,11 +2129,13 @@ module SyntaxTree
|
|
2139
2129
|
#
|
2140
2130
|
# break
|
2141
2131
|
#
|
2142
|
-
in [
|
2143
|
-
|
2144
|
-
|
2145
|
-
|
2146
|
-
|
2132
|
+
in [
|
2133
|
+
Paren[
|
2134
|
+
contents: {
|
2135
|
+
body: [ArrayLiteral[contents: { parts: [_, _, *] }] => array]
|
2136
|
+
}
|
2137
|
+
]
|
2138
|
+
]
|
2147
2139
|
# Here we have a single argument that is a set of parentheses wrapping
|
2148
2140
|
# an array literal that has at least 2 elements. We're going to print
|
2149
2141
|
# the contents of the array directly. This would be like if we had:
|
@@ -2777,10 +2769,17 @@ module SyntaxTree
|
|
2777
2769
|
q.format(value)
|
2778
2770
|
q.text(" ")
|
2779
2771
|
q.format(operator)
|
2780
|
-
|
2781
|
-
|
2782
|
-
|
2783
|
-
|
2772
|
+
|
2773
|
+
case pattern
|
2774
|
+
in AryPtn | FndPtn | HshPtn
|
2775
|
+
q.text(" ")
|
2776
|
+
q.format(pattern)
|
2777
|
+
else
|
2778
|
+
q.group do
|
2779
|
+
q.indent do
|
2780
|
+
q.breakable
|
2781
|
+
q.format(pattern)
|
2782
|
+
end
|
2784
2783
|
end
|
2785
2784
|
end
|
2786
2785
|
end
|
@@ -3864,9 +3863,9 @@ module SyntaxTree
|
|
3864
3863
|
# whichever quote the user chose. (If they chose single quotes, then double
|
3865
3864
|
# quoting would activate the escape sequence, and if they chose double
|
3866
3865
|
# quotes, then single quotes would deactivate it.)
|
3867
|
-
def self.locked?(node)
|
3866
|
+
def self.locked?(node, quote)
|
3868
3867
|
node.parts.any? do |part|
|
3869
|
-
!part.is_a?(TStringContent) || part.value.match?(/\\|#[@${]/)
|
3868
|
+
!part.is_a?(TStringContent) || part.value.match?(/\\|#[@${]|#{quote}/)
|
3870
3869
|
end
|
3871
3870
|
end
|
3872
3871
|
|
@@ -3981,12 +3980,12 @@ module SyntaxTree
|
|
3981
3980
|
|
3982
3981
|
if matched
|
3983
3982
|
[quote, matching]
|
3984
|
-
elsif Quotes.locked?(self)
|
3983
|
+
elsif Quotes.locked?(self, q.quote)
|
3985
3984
|
["#{":" unless hash_key}'", "'"]
|
3986
3985
|
else
|
3987
3986
|
["#{":" unless hash_key}#{q.quote}", q.quote]
|
3988
3987
|
end
|
3989
|
-
elsif Quotes.locked?(self)
|
3988
|
+
elsif Quotes.locked?(self, q.quote)
|
3990
3989
|
if quote.start_with?(":")
|
3991
3990
|
[hash_key ? quote[1..] : quote, quote[1..]]
|
3992
3991
|
else
|
@@ -4573,16 +4572,26 @@ module SyntaxTree
|
|
4573
4572
|
|
4574
4573
|
def format(q)
|
4575
4574
|
q.format(constant) if constant
|
4576
|
-
q.group(0, "[", "]") do
|
4577
|
-
q.text("*")
|
4578
|
-
q.format(left)
|
4579
|
-
q.comma_breakable
|
4580
4575
|
|
4581
|
-
|
4582
|
-
q.
|
4576
|
+
q.group do
|
4577
|
+
q.text("[")
|
4583
4578
|
|
4584
|
-
q.
|
4585
|
-
|
4579
|
+
q.indent do
|
4580
|
+
q.breakable("")
|
4581
|
+
|
4582
|
+
q.text("*")
|
4583
|
+
q.format(left)
|
4584
|
+
q.comma_breakable
|
4585
|
+
|
4586
|
+
q.seplist(values) { |value| q.format(value) }
|
4587
|
+
q.comma_breakable
|
4588
|
+
|
4589
|
+
q.text("*")
|
4590
|
+
q.format(right)
|
4591
|
+
end
|
4592
|
+
|
4593
|
+
q.breakable("")
|
4594
|
+
q.text("]")
|
4586
4595
|
end
|
4587
4596
|
end
|
4588
4597
|
end
|
@@ -4804,7 +4813,7 @@ module SyntaxTree
|
|
4804
4813
|
# [HeredocBeg] the opening of the heredoc
|
4805
4814
|
attr_reader :beginning
|
4806
4815
|
|
4807
|
-
# [
|
4816
|
+
# [HeredocEnd] the ending of the heredoc
|
4808
4817
|
attr_reader :ending
|
4809
4818
|
|
4810
4819
|
# [Integer] how far to dedent the heredoc
|
@@ -4838,7 +4847,7 @@ module SyntaxTree
|
|
4838
4847
|
end
|
4839
4848
|
|
4840
4849
|
def child_nodes
|
4841
|
-
[beginning, *parts]
|
4850
|
+
[beginning, *parts, ending]
|
4842
4851
|
end
|
4843
4852
|
|
4844
4853
|
alias deconstruct child_nodes
|
@@ -4874,7 +4883,7 @@ module SyntaxTree
|
|
4874
4883
|
end
|
4875
4884
|
end
|
4876
4885
|
|
4877
|
-
q.
|
4886
|
+
q.format(ending)
|
4878
4887
|
end
|
4879
4888
|
end
|
4880
4889
|
end
|
@@ -4920,6 +4929,45 @@ module SyntaxTree
|
|
4920
4929
|
end
|
4921
4930
|
end
|
4922
4931
|
|
4932
|
+
# HeredocEnd represents the closing declaration of a heredoc.
|
4933
|
+
#
|
4934
|
+
# <<~DOC
|
4935
|
+
# contents
|
4936
|
+
# DOC
|
4937
|
+
#
|
4938
|
+
# In the example above the HeredocEnd node represents the closing DOC.
|
4939
|
+
class HeredocEnd < Node
|
4940
|
+
# [String] the closing declaration of the heredoc
|
4941
|
+
attr_reader :value
|
4942
|
+
|
4943
|
+
# [Array[ Comment | EmbDoc ]] the comments attached to this node
|
4944
|
+
attr_reader :comments
|
4945
|
+
|
4946
|
+
def initialize(value:, location:, comments: [])
|
4947
|
+
@value = value
|
4948
|
+
@location = location
|
4949
|
+
@comments = comments
|
4950
|
+
end
|
4951
|
+
|
4952
|
+
def accept(visitor)
|
4953
|
+
visitor.visit_heredoc_end(self)
|
4954
|
+
end
|
4955
|
+
|
4956
|
+
def child_nodes
|
4957
|
+
[]
|
4958
|
+
end
|
4959
|
+
|
4960
|
+
alias deconstruct child_nodes
|
4961
|
+
|
4962
|
+
def deconstruct_keys(_keys)
|
4963
|
+
{ value: value, location: location, comments: comments }
|
4964
|
+
end
|
4965
|
+
|
4966
|
+
def format(q)
|
4967
|
+
q.text(value)
|
4968
|
+
end
|
4969
|
+
end
|
4970
|
+
|
4923
4971
|
# HshPtn represents matching against a hash pattern using the Ruby 2.7+
|
4924
4972
|
# pattern matching syntax.
|
4925
4973
|
#
|
@@ -5465,12 +5513,14 @@ module SyntaxTree
|
|
5465
5513
|
q.format(predicate)
|
5466
5514
|
q.text(" ?")
|
5467
5515
|
|
5468
|
-
q.
|
5469
|
-
|
5470
|
-
|
5516
|
+
q.indent do
|
5517
|
+
q.breakable
|
5518
|
+
q.format(truthy)
|
5519
|
+
q.text(" :")
|
5471
5520
|
|
5472
|
-
|
5473
|
-
|
5521
|
+
q.breakable
|
5522
|
+
q.format(falsy)
|
5523
|
+
end
|
5474
5524
|
end
|
5475
5525
|
end
|
5476
5526
|
|
@@ -8404,7 +8454,7 @@ module SyntaxTree
|
|
8404
8454
|
end
|
8405
8455
|
|
8406
8456
|
opening_quote, closing_quote =
|
8407
|
-
if !Quotes.locked?(self)
|
8457
|
+
if !Quotes.locked?(self, q.quote)
|
8408
8458
|
[q.quote, q.quote]
|
8409
8459
|
elsif quote.start_with?("%")
|
8410
8460
|
[quote, Quotes.matching(quote[/%[qQ]?(.)/, 1])]
|
data/lib/syntax_tree/parser.rb
CHANGED
@@ -548,13 +548,6 @@ module SyntaxTree
|
|
548
548
|
parts[0].location.to(parts[-1].location)
|
549
549
|
end
|
550
550
|
|
551
|
-
# If there's the optional then keyword, then we'll delete that and use it
|
552
|
-
# as the end bounds of the location.
|
553
|
-
if (token = find_token(Kw, "then", consume: false))
|
554
|
-
tokens.delete(token)
|
555
|
-
location = location.to(token.location)
|
556
|
-
end
|
557
|
-
|
558
551
|
# If there is a plain *, then we're going to fix up the location of it
|
559
552
|
# here because it currently doesn't have anything to use for its precise
|
560
553
|
# location. If we hit a comma, then we've gone too far.
|
@@ -1647,9 +1640,19 @@ module SyntaxTree
|
|
1647
1640
|
def on_heredoc_end(value)
|
1648
1641
|
heredoc = @heredocs[-1]
|
1649
1642
|
|
1643
|
+
location =
|
1644
|
+
Location.token(
|
1645
|
+
line: lineno,
|
1646
|
+
char: char_pos,
|
1647
|
+
column: current_column,
|
1648
|
+
size: value.size + 1
|
1649
|
+
)
|
1650
|
+
|
1651
|
+
heredoc_end = HeredocEnd.new(value: value.chomp, location: location)
|
1652
|
+
|
1650
1653
|
@heredocs[-1] = Heredoc.new(
|
1651
1654
|
beginning: heredoc.beginning,
|
1652
|
-
ending:
|
1655
|
+
ending: heredoc_end,
|
1653
1656
|
dedent: heredoc.dedent,
|
1654
1657
|
parts: heredoc.parts,
|
1655
1658
|
location:
|
@@ -1698,12 +1701,6 @@ module SyntaxTree
|
|
1698
1701
|
end
|
1699
1702
|
end
|
1700
1703
|
|
1701
|
-
# Delete the optional then keyword
|
1702
|
-
if (token = find_token(Kw, "then", consume: false))
|
1703
|
-
parts << token
|
1704
|
-
tokens.delete(token)
|
1705
|
-
end
|
1706
|
-
|
1707
1704
|
HshPtn.new(
|
1708
1705
|
constant: constant,
|
1709
1706
|
keywords: keywords || [],
|
@@ -3013,6 +3010,11 @@ module SyntaxTree
|
|
3013
3010
|
# (StringEmbExpr | StringDVar | TStringContent) part
|
3014
3011
|
# ) -> StringContent
|
3015
3012
|
def on_string_add(string, part)
|
3013
|
+
# Due to some eccentricities in how ripper works, you need this here in
|
3014
|
+
# case you have a syntax error with an embedded expression that doesn't
|
3015
|
+
# finish, as in: "#{"
|
3016
|
+
return string if part.is_a?(String)
|
3017
|
+
|
3016
3018
|
location =
|
3017
3019
|
string.parts.any? ? string.location.to(part.location) : part.location
|
3018
3020
|
|
data/lib/syntax_tree/version.rb
CHANGED
@@ -49,9 +49,7 @@ module SyntaxTree
|
|
49
49
|
# of circumstances, like when visiting the list of optional parameters
|
50
50
|
# defined on a method.
|
51
51
|
#
|
52
|
-
class FieldVisitor <
|
53
|
-
attr_reader :q
|
54
|
-
|
52
|
+
class FieldVisitor < BasicVisitor
|
55
53
|
def visit_aref(node)
|
56
54
|
node(node, "aref") do
|
57
55
|
field("collection", node.collection)
|
@@ -499,6 +497,10 @@ module SyntaxTree
|
|
499
497
|
visit_token(node, "heredoc_beg")
|
500
498
|
end
|
501
499
|
|
500
|
+
def visit_heredoc_end(node)
|
501
|
+
visit_token(node, "heredoc_end")
|
502
|
+
end
|
503
|
+
|
502
504
|
def visit_hshptn(node)
|
503
505
|
node(node, "hshptn") do
|
504
506
|
field("constant", node.constant) if node.constant
|
data/lib/syntax_tree/visitor.rb
CHANGED
@@ -4,72 +4,7 @@ module SyntaxTree
|
|
4
4
|
# Visitor is a parent class that provides the ability to walk down the tree
|
5
5
|
# and handle a subset of nodes. By defining your own subclass, you can
|
6
6
|
# explicitly handle a node type by defining a visit_* method.
|
7
|
-
class Visitor
|
8
|
-
# This is raised when you use the Visitor.visit_method method and it fails.
|
9
|
-
# It is correctable to through DidYouMean.
|
10
|
-
class VisitMethodError < StandardError
|
11
|
-
attr_reader :visit_method
|
12
|
-
|
13
|
-
def initialize(visit_method)
|
14
|
-
@visit_method = visit_method
|
15
|
-
super("Invalid visit method: #{visit_method}")
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
# This class is used by DidYouMean to offer corrections to invalid visit
|
20
|
-
# method names.
|
21
|
-
class VisitMethodChecker
|
22
|
-
attr_reader :visit_method
|
23
|
-
|
24
|
-
def initialize(error)
|
25
|
-
@visit_method = error.visit_method
|
26
|
-
end
|
27
|
-
|
28
|
-
def corrections
|
29
|
-
@corrections ||=
|
30
|
-
DidYouMean::SpellChecker.new(
|
31
|
-
dictionary: Visitor.visit_methods
|
32
|
-
).correct(visit_method)
|
33
|
-
end
|
34
|
-
|
35
|
-
DidYouMean.correct_error(VisitMethodError, self)
|
36
|
-
end
|
37
|
-
|
38
|
-
class << self
|
39
|
-
# This method is here to help folks write visitors.
|
40
|
-
#
|
41
|
-
# It's not always easy to ensure you're writing the correct method name in
|
42
|
-
# the visitor since it's perfectly valid to define methods that don't
|
43
|
-
# override these parent methods.
|
44
|
-
#
|
45
|
-
# If you use this method, you can ensure you're writing the correct method
|
46
|
-
# name. It will raise an error if the visit method you're defining isn't
|
47
|
-
# actually a method on the parent visitor.
|
48
|
-
def visit_method(method_name)
|
49
|
-
return if visit_methods.include?(method_name)
|
50
|
-
|
51
|
-
raise VisitMethodError, method_name
|
52
|
-
end
|
53
|
-
|
54
|
-
# This is the list of all of the valid visit methods.
|
55
|
-
def visit_methods
|
56
|
-
@visit_methods ||=
|
57
|
-
Visitor.instance_methods.grep(/^visit_(?!child_nodes)/)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def visit(node)
|
62
|
-
node&.accept(self)
|
63
|
-
end
|
64
|
-
|
65
|
-
def visit_all(nodes)
|
66
|
-
nodes.map { |node| visit(node) }
|
67
|
-
end
|
68
|
-
|
69
|
-
def visit_child_nodes(node)
|
70
|
-
visit_all(node.child_nodes)
|
71
|
-
end
|
72
|
-
|
7
|
+
class Visitor < BasicVisitor
|
73
8
|
# Visit an ARef node.
|
74
9
|
alias visit_aref visit_child_nodes
|
75
10
|
|
@@ -259,6 +194,9 @@ module SyntaxTree
|
|
259
194
|
# Visit a HeredocBeg node.
|
260
195
|
alias visit_heredoc_beg visit_child_nodes
|
261
196
|
|
197
|
+
# Visit a HeredocEnd node.
|
198
|
+
alias visit_heredoc_end visit_child_nodes
|
199
|
+
|
262
200
|
# Visit a HshPtn node.
|
263
201
|
alias visit_hshptn visit_child_nodes
|
264
202
|
|
data/lib/syntax_tree.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "etc"
|
3
4
|
require "json"
|
4
5
|
require "pp"
|
5
6
|
require "prettier_print"
|
@@ -10,6 +11,8 @@ require_relative "syntax_tree/formatter"
|
|
10
11
|
require_relative "syntax_tree/node"
|
11
12
|
require_relative "syntax_tree/parser"
|
12
13
|
require_relative "syntax_tree/version"
|
14
|
+
|
15
|
+
require_relative "syntax_tree/basic_visitor"
|
13
16
|
require_relative "syntax_tree/visitor"
|
14
17
|
require_relative "syntax_tree/visitor/field_visitor"
|
15
18
|
require_relative "syntax_tree/visitor/json_visitor"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: syntax_tree
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Newton
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-06-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: prettier_print
|
@@ -121,10 +121,9 @@ files:
|
|
121
121
|
- doc/logo.svg
|
122
122
|
- exe/stree
|
123
123
|
- lib/syntax_tree.rb
|
124
|
+
- lib/syntax_tree/basic_visitor.rb
|
124
125
|
- lib/syntax_tree/cli.rb
|
125
126
|
- lib/syntax_tree/formatter.rb
|
126
|
-
- lib/syntax_tree/formatter/single_quotes.rb
|
127
|
-
- lib/syntax_tree/formatter/trailing_comma.rb
|
128
127
|
- lib/syntax_tree/language_server.rb
|
129
128
|
- lib/syntax_tree/language_server/inlay_hints.rb
|
130
129
|
- lib/syntax_tree/node.rb
|
@@ -161,7 +160,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
161
160
|
- !ruby/object:Gem::Version
|
162
161
|
version: '0'
|
163
162
|
requirements: []
|
164
|
-
rubygems_version: 3.
|
163
|
+
rubygems_version: 3.3.3
|
165
164
|
signing_key:
|
166
165
|
specification_version: 4
|
167
166
|
summary: A parser based on ripper
|
@@ -1,13 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module SyntaxTree
|
4
|
-
class Formatter
|
5
|
-
# This module overrides the quote method on the formatter to use single
|
6
|
-
# quotes for everything instead of double quotes.
|
7
|
-
module SingleQuotes
|
8
|
-
def quote
|
9
|
-
"'"
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|