syntax_tree 6.0.0 → 6.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -1
- data/Gemfile.lock +7 -7
- data/README.md +5 -0
- data/lib/syntax_tree/index.rb +112 -15
- data/lib/syntax_tree/node.rb +47 -16
- data/lib/syntax_tree/parser.rb +10 -1
- data/lib/syntax_tree/translation/parser.rb +176 -88
- data/lib/syntax_tree/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b1e39c546fb96aea864e8190b8a8ff338543afd644d2334edad5b98663d418f2
|
4
|
+
data.tar.gz: 7633b68e7b42fcd782fc1c89395d93679e72828fe1a9214e741ad00718543c6b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a0990a769cbc448e549d14542610ccfbf09536ada139cc10701871a506a1b160a8ce751712a94a8f003d3b2c1828a6b5d4bd5384efe63f0f82a21c0762b689d5
|
7
|
+
data.tar.gz: a08b4057370c230bfd713ff9a717ea4f2a02be4f8610af51b18143ff9b2b9e542541b77c049488c500033751b50adc279d5e3a8beb0568f5e2bb165793c612d4
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,19 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [6.0.1] - 2023-02-26
|
10
|
+
|
11
|
+
### Added
|
12
|
+
|
13
|
+
- The class declarations returned as the result of the indexing operation now have their superclass as a field. It is returned as an array of constants. If the superclass is anything other than a constant lookup, then it raises an error.
|
14
|
+
|
15
|
+
### Changed
|
16
|
+
|
17
|
+
- The `nesting` field on the results of the indexing operation is no longer a single flat array. Instead it is an array of arrays, where each array is a single nesting level. This more accurately reflects the nesting of the nodes in the tree. For example, `class Foo::Bar::Baz; end` would result in `[Foo, Bar, Baz]`, but that incorrectly implies that you can see constants at each of those levels. Now this would result in `[[Foo, Bar, Baz]]` to indicate that it can see either the top level or constants within the scope of `Foo::Bar::Baz` only.
|
18
|
+
- When formatting hashes that have omitted values and mixed hash rockets with labels, the formatting now maintains whichever delimiter was used in the source. This is because forcing the use of hash rockets with omitted values results in a syntax error.
|
19
|
+
- Handle the case where a bare hash is used after the `break`, `next`, or `return` keywords. Previously this would result in hash labels which is not valid syntax. Now it maintains the delimiters used in the source.
|
20
|
+
- The `<<` operator will now break on chained `<<` expressions. Previously it would always stay flat.
|
21
|
+
|
9
22
|
## [6.0.0] - 2023-02-10
|
10
23
|
|
11
24
|
### Added
|
@@ -559,7 +572,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
|
|
559
572
|
|
560
573
|
- 🎉 Initial release! 🎉
|
561
574
|
|
562
|
-
[unreleased]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v6.0.
|
575
|
+
[unreleased]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v6.0.1...HEAD
|
576
|
+
[6.0.1]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v6.0.0...v6.0.1
|
563
577
|
[6.0.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v5.3.0...v6.0.0
|
564
578
|
[5.3.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v5.2.0...v5.3.0
|
565
579
|
[5.2.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v5.1.0...v5.2.0
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
syntax_tree (6.0.
|
4
|
+
syntax_tree (6.0.1)
|
5
5
|
prettier_print (>= 1.2.0)
|
6
6
|
|
7
7
|
GEM
|
@@ -12,25 +12,25 @@ GEM
|
|
12
12
|
json (2.6.3)
|
13
13
|
minitest (5.17.0)
|
14
14
|
parallel (1.22.1)
|
15
|
-
parser (3.2.
|
15
|
+
parser (3.2.1.0)
|
16
16
|
ast (~> 2.4.1)
|
17
17
|
prettier_print (1.2.0)
|
18
18
|
rainbow (3.1.1)
|
19
19
|
rake (13.0.6)
|
20
|
-
regexp_parser (2.
|
20
|
+
regexp_parser (2.7.0)
|
21
21
|
rexml (3.2.5)
|
22
|
-
rubocop (1.
|
22
|
+
rubocop (1.46.0)
|
23
23
|
json (~> 2.3)
|
24
24
|
parallel (~> 1.10)
|
25
25
|
parser (>= 3.2.0.0)
|
26
26
|
rainbow (>= 2.2.2, < 4.0)
|
27
27
|
regexp_parser (>= 1.8, < 3.0)
|
28
28
|
rexml (>= 3.2.5, < 4.0)
|
29
|
-
rubocop-ast (>= 1.
|
29
|
+
rubocop-ast (>= 1.26.0, < 2.0)
|
30
30
|
ruby-progressbar (~> 1.7)
|
31
31
|
unicode-display_width (>= 2.4.0, < 3.0)
|
32
|
-
rubocop-ast (1.
|
33
|
-
parser (>= 3.
|
32
|
+
rubocop-ast (1.26.0)
|
33
|
+
parser (>= 3.2.1.0)
|
34
34
|
ruby-progressbar (1.11.0)
|
35
35
|
simplecov (0.22.0)
|
36
36
|
docile (~> 1.1)
|
data/README.md
CHANGED
@@ -29,6 +29,7 @@ It is built with only standard library dependencies. It additionally ships with
|
|
29
29
|
- [SyntaxTree.format(source)](#syntaxtreeformatsource)
|
30
30
|
- [SyntaxTree.mutation(&block)](#syntaxtreemutationblock)
|
31
31
|
- [SyntaxTree.search(source, query, &block)](#syntaxtreesearchsource-query-block)
|
32
|
+
- [SyntaxTree.index(source)](#syntaxtreeindexsource)
|
32
33
|
- [Nodes](#nodes)
|
33
34
|
- [child_nodes](#child_nodes)
|
34
35
|
- [copy(**attrs)](#copyattrs)
|
@@ -347,6 +348,10 @@ This function yields a new mutation visitor to the block, and then returns the i
|
|
347
348
|
|
348
349
|
This function takes an input string containing Ruby code, an input string containing a valid Ruby `in` clause expression that can be used to match against nodes in the tree (can be generated using `stree expr`, `stree match`, or `Node#construct_keys`), and a block. Each node that matches the given query will be yielded to the block. The block will receive the node as its only argument.
|
349
350
|
|
351
|
+
### SyntaxTree.index(source)
|
352
|
+
|
353
|
+
This function takes an input string containing Ruby code and returns a list of all of the class declarations, module declarations, and method definitions within a file. Each of the entries also has access to its associated comments. This is useful for generating documentation or index information for a file to support something like go-to-definition.
|
354
|
+
|
350
355
|
## Nodes
|
351
356
|
|
352
357
|
There are many different node types in the syntax tree. They are meant to be treated as immutable structs containing links to child nodes with minimal logic contained within their implementation. However, for the most part they all respond to a certain set of APIs, listed below.
|
data/lib/syntax_tree/index.rb
CHANGED
@@ -20,11 +20,12 @@ module SyntaxTree
|
|
20
20
|
|
21
21
|
# This entry represents a class definition using the class keyword.
|
22
22
|
class ClassDefinition
|
23
|
-
attr_reader :nesting, :name, :location, :comments
|
23
|
+
attr_reader :nesting, :name, :superclass, :location, :comments
|
24
24
|
|
25
|
-
def initialize(nesting, name, location, comments)
|
25
|
+
def initialize(nesting, name, superclass, location, comments)
|
26
26
|
@nesting = nesting
|
27
27
|
@name = name
|
28
|
+
@superclass = superclass
|
28
29
|
@location = location
|
29
30
|
@comments = comments
|
30
31
|
end
|
@@ -176,30 +177,101 @@ module SyntaxTree
|
|
176
177
|
Location.new(code_location[0], code_location[1])
|
177
178
|
end
|
178
179
|
|
180
|
+
def find_constant_path(insns, index)
|
181
|
+
index -= 1 while insns[index].is_a?(Integer)
|
182
|
+
insn = insns[index]
|
183
|
+
|
184
|
+
if insn.is_a?(Array) && insn[0] == :opt_getconstant_path
|
185
|
+
# In this case we're on Ruby 3.2+ and we have an opt_getconstant_path
|
186
|
+
# instruction, so we already know all of the symbols in the nesting.
|
187
|
+
[index - 1, insn[1]]
|
188
|
+
elsif insn.is_a?(Symbol) && insn.match?(/\Alabel_\d+/)
|
189
|
+
# Otherwise, if we have a label then this is very likely the
|
190
|
+
# destination of an opt_getinlinecache instruction, in which case
|
191
|
+
# we'll walk backwards to grab up all of the constants.
|
192
|
+
names = []
|
193
|
+
|
194
|
+
index -= 1
|
195
|
+
until insns[index].is_a?(Array) &&
|
196
|
+
insns[index][0] == :opt_getinlinecache
|
197
|
+
if insns[index].is_a?(Array) && insns[index][0] == :getconstant
|
198
|
+
names.unshift(insns[index][1])
|
199
|
+
end
|
200
|
+
|
201
|
+
index -= 1
|
202
|
+
end
|
203
|
+
|
204
|
+
[index - 1, names]
|
205
|
+
else
|
206
|
+
[index, []]
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
179
210
|
def index_iseq(iseq, file_comments)
|
180
211
|
results = []
|
181
212
|
queue = [[iseq, []]]
|
182
213
|
|
183
214
|
while (current_iseq, current_nesting = queue.shift)
|
184
|
-
current_iseq[
|
185
|
-
|
215
|
+
line = current_iseq[8]
|
216
|
+
insns = current_iseq[13]
|
217
|
+
|
218
|
+
insns.each_with_index do |insn, index|
|
219
|
+
case insn
|
220
|
+
when Integer
|
221
|
+
line = insn
|
222
|
+
next
|
223
|
+
when Array
|
224
|
+
# continue on
|
225
|
+
else
|
226
|
+
# skip everything else
|
227
|
+
next
|
228
|
+
end
|
186
229
|
|
187
230
|
case insn[0]
|
188
231
|
when :defineclass
|
189
232
|
_, name, class_iseq, flags = insn
|
233
|
+
next_nesting = current_nesting.dup
|
234
|
+
|
235
|
+
# This is the index we're going to search for the nested constant
|
236
|
+
# path within the declaration name.
|
237
|
+
constant_index = index - 2
|
238
|
+
|
239
|
+
# This is the superclass of the class being defined.
|
240
|
+
superclass = []
|
241
|
+
|
242
|
+
# If there is a superclass, then we're going to find it here and
|
243
|
+
# then update the constant_index as necessary.
|
244
|
+
if flags & VM_DEFINECLASS_FLAG_HAS_SUPERCLASS > 0
|
245
|
+
constant_index, superclass =
|
246
|
+
find_constant_path(insns, index - 1)
|
247
|
+
|
248
|
+
if superclass.empty?
|
249
|
+
raise NotImplementedError,
|
250
|
+
"superclass with non constant path on line #{line}"
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
if (_, nesting = find_constant_path(insns, constant_index))
|
255
|
+
# If there is a constant path in the class name, then we need to
|
256
|
+
# handle that by updating the nesting.
|
257
|
+
next_nesting << (nesting << name)
|
258
|
+
else
|
259
|
+
# Otherwise we'll add the class name to the nesting.
|
260
|
+
next_nesting << [name]
|
261
|
+
end
|
190
262
|
|
191
263
|
if flags == VM_DEFINECLASS_TYPE_SINGLETON_CLASS
|
192
264
|
# At the moment, we don't support singletons that aren't
|
193
265
|
# defined on self. We could, but it would require more
|
194
266
|
# emulation.
|
195
|
-
if
|
267
|
+
if insns[index - 2] != [:putself]
|
196
268
|
raise NotImplementedError,
|
197
269
|
"singleton class with non-self receiver"
|
198
270
|
end
|
199
271
|
elsif flags & VM_DEFINECLASS_TYPE_MODULE > 0
|
200
272
|
location = location_for(class_iseq)
|
201
273
|
results << ModuleDefinition.new(
|
202
|
-
|
274
|
+
next_nesting,
|
203
275
|
name,
|
204
276
|
location,
|
205
277
|
EntryComments.new(file_comments, location)
|
@@ -207,14 +279,15 @@ module SyntaxTree
|
|
207
279
|
else
|
208
280
|
location = location_for(class_iseq)
|
209
281
|
results << ClassDefinition.new(
|
210
|
-
|
282
|
+
next_nesting,
|
211
283
|
name,
|
284
|
+
superclass,
|
212
285
|
location,
|
213
286
|
EntryComments.new(file_comments, location)
|
214
287
|
)
|
215
288
|
end
|
216
289
|
|
217
|
-
queue << [class_iseq,
|
290
|
+
queue << [class_iseq, next_nesting]
|
218
291
|
when :definemethod
|
219
292
|
location = location_for(insn[2])
|
220
293
|
results << MethodDefinition.new(
|
@@ -259,24 +332,43 @@ module SyntaxTree
|
|
259
332
|
|
260
333
|
visit_methods do
|
261
334
|
def visit_class(node)
|
262
|
-
|
335
|
+
names = visit(node.constant)
|
336
|
+
nesting << names
|
337
|
+
|
263
338
|
location =
|
264
339
|
Location.new(node.location.start_line, node.location.start_column)
|
265
340
|
|
341
|
+
superclass =
|
342
|
+
if node.superclass
|
343
|
+
visited = visit(node.superclass)
|
344
|
+
|
345
|
+
if visited == [[]]
|
346
|
+
raise NotImplementedError, "superclass with non constant path"
|
347
|
+
end
|
348
|
+
|
349
|
+
visited
|
350
|
+
else
|
351
|
+
[]
|
352
|
+
end
|
353
|
+
|
266
354
|
results << ClassDefinition.new(
|
267
355
|
nesting.dup,
|
268
|
-
|
356
|
+
names.last,
|
357
|
+
superclass,
|
269
358
|
location,
|
270
359
|
comments_for(node)
|
271
360
|
)
|
272
361
|
|
273
|
-
nesting << name
|
274
362
|
super
|
275
363
|
nesting.pop
|
276
364
|
end
|
277
365
|
|
278
366
|
def visit_const_ref(node)
|
279
|
-
node.constant.value
|
367
|
+
[node.constant.value.to_sym]
|
368
|
+
end
|
369
|
+
|
370
|
+
def visit_const_path_ref(node)
|
371
|
+
visit(node.parent) << node.constant.value.to_sym
|
280
372
|
end
|
281
373
|
|
282
374
|
def visit_def(node)
|
@@ -302,18 +394,19 @@ module SyntaxTree
|
|
302
394
|
end
|
303
395
|
|
304
396
|
def visit_module(node)
|
305
|
-
|
397
|
+
names = visit(node.constant)
|
398
|
+
nesting << names
|
399
|
+
|
306
400
|
location =
|
307
401
|
Location.new(node.location.start_line, node.location.start_column)
|
308
402
|
|
309
403
|
results << ModuleDefinition.new(
|
310
404
|
nesting.dup,
|
311
|
-
|
405
|
+
names.last,
|
312
406
|
location,
|
313
407
|
comments_for(node)
|
314
408
|
)
|
315
409
|
|
316
|
-
nesting << name
|
317
410
|
super
|
318
411
|
nesting.pop
|
319
412
|
end
|
@@ -327,6 +420,10 @@ module SyntaxTree
|
|
327
420
|
@statements = node
|
328
421
|
super
|
329
422
|
end
|
423
|
+
|
424
|
+
def visit_var_ref(node)
|
425
|
+
[node.value.value.to_sym]
|
426
|
+
end
|
330
427
|
end
|
331
428
|
|
332
429
|
private
|
data/lib/syntax_tree/node.rb
CHANGED
@@ -1780,13 +1780,25 @@ module SyntaxTree
|
|
1780
1780
|
end
|
1781
1781
|
|
1782
1782
|
def self.for(container)
|
1783
|
-
|
1784
|
-
|
1785
|
-
|
1786
|
-
|
1783
|
+
container.assocs.each do |assoc|
|
1784
|
+
if assoc.is_a?(AssocSplat)
|
1785
|
+
# Splat nodes do not impact the formatting choice.
|
1786
|
+
elsif assoc.value.nil?
|
1787
|
+
# If the value is nil, then it has been omitted. In this case we have
|
1788
|
+
# to match the existing formatting because standardizing would
|
1789
|
+
# potentially break the code. For example:
|
1790
|
+
#
|
1791
|
+
# { first:, "second" => "value" }
|
1792
|
+
#
|
1793
|
+
return Identity.new
|
1794
|
+
else
|
1795
|
+
# Otherwise, we need to check the type of the key. If it's a label or
|
1796
|
+
# dynamic symbol, we can use labels. If it's a symbol literal then it
|
1797
|
+
# needs to match a certain pattern to be used as a label. If it's
|
1798
|
+
# anything else, then we need to use hash rockets.
|
1787
1799
|
case assoc.key
|
1788
|
-
when Label
|
1789
|
-
|
1800
|
+
when Label, DynaSymbol
|
1801
|
+
# Here labels can be used.
|
1790
1802
|
when SymbolLiteral
|
1791
1803
|
# When attempting to convert a hash rocket into a hash label,
|
1792
1804
|
# you need to take care because only certain patterns are
|
@@ -1794,15 +1806,18 @@ module SyntaxTree
|
|
1794
1806
|
# arguments to methods, but don't specify what that is. After
|
1795
1807
|
# some experimentation, it looks like it's:
|
1796
1808
|
value = assoc.key.value.value
|
1797
|
-
|
1798
|
-
|
1799
|
-
|
1809
|
+
|
1810
|
+
if !value.match?(/^[_A-Za-z]/) || value.end_with?("=")
|
1811
|
+
return Rockets.new
|
1812
|
+
end
|
1800
1813
|
else
|
1801
|
-
|
1814
|
+
# If the value is anything else, we have to use hash rockets.
|
1815
|
+
return Rockets.new
|
1802
1816
|
end
|
1803
1817
|
end
|
1818
|
+
end
|
1804
1819
|
|
1805
|
-
|
1820
|
+
Labels.new
|
1806
1821
|
end
|
1807
1822
|
end
|
1808
1823
|
|
@@ -1859,7 +1874,15 @@ module SyntaxTree
|
|
1859
1874
|
end
|
1860
1875
|
|
1861
1876
|
def format_key(q, key)
|
1862
|
-
|
1877
|
+
@key_formatter ||=
|
1878
|
+
case q.parents.take(3).last
|
1879
|
+
when Break, Next, ReturnNode
|
1880
|
+
HashKeyFormatter::Identity.new
|
1881
|
+
else
|
1882
|
+
HashKeyFormatter.for(self)
|
1883
|
+
end
|
1884
|
+
|
1885
|
+
@key_formatter.format_key(q, key)
|
1863
1886
|
end
|
1864
1887
|
end
|
1865
1888
|
|
@@ -2074,10 +2097,15 @@ module SyntaxTree
|
|
2074
2097
|
q.group { q.format(left) }
|
2075
2098
|
q.text(" ") unless power
|
2076
2099
|
|
2077
|
-
if operator
|
2078
|
-
q.
|
2079
|
-
|
2080
|
-
|
2100
|
+
if operator != :<<
|
2101
|
+
q.group do
|
2102
|
+
q.text(operator.name)
|
2103
|
+
q.indent do
|
2104
|
+
power ? q.breakable_empty : q.breakable_space
|
2105
|
+
q.format(right)
|
2106
|
+
end
|
2107
|
+
end
|
2108
|
+
elsif left.is_a?(Binary) && left.operator == :<<
|
2081
2109
|
q.group do
|
2082
2110
|
q.text(operator.name)
|
2083
2111
|
q.indent do
|
@@ -2085,6 +2113,9 @@ module SyntaxTree
|
|
2085
2113
|
q.format(right)
|
2086
2114
|
end
|
2087
2115
|
end
|
2116
|
+
else
|
2117
|
+
q.text("<< ")
|
2118
|
+
q.format(right)
|
2088
2119
|
end
|
2089
2120
|
end
|
2090
2121
|
end
|
data/lib/syntax_tree/parser.rb
CHANGED
@@ -1559,7 +1559,14 @@ module SyntaxTree
|
|
1559
1559
|
beginning = consume_keyword(:elsif)
|
1560
1560
|
ending = consequent || consume_keyword(:end)
|
1561
1561
|
|
1562
|
-
|
1562
|
+
delimiter =
|
1563
|
+
find_keyword_between(:then, predicate, statements) ||
|
1564
|
+
find_token_between(Semicolon, predicate, statements)
|
1565
|
+
|
1566
|
+
tokens.delete(delimiter) if delimiter
|
1567
|
+
start_char =
|
1568
|
+
find_next_statement_start((delimiter || predicate).location.end_char)
|
1569
|
+
|
1563
1570
|
statements.bind(
|
1564
1571
|
self,
|
1565
1572
|
start_char,
|
@@ -2045,6 +2052,7 @@ module SyntaxTree
|
|
2045
2052
|
|
2046
2053
|
start_char =
|
2047
2054
|
find_next_statement_start((keyword || predicate).location.end_char)
|
2055
|
+
|
2048
2056
|
statements.bind(
|
2049
2057
|
self,
|
2050
2058
|
start_char,
|
@@ -3805,6 +3813,7 @@ module SyntaxTree
|
|
3805
3813
|
|
3806
3814
|
start_char =
|
3807
3815
|
find_next_statement_start((keyword || predicate).location.end_char)
|
3816
|
+
|
3808
3817
|
statements.bind(
|
3809
3818
|
self,
|
3810
3819
|
start_char,
|
@@ -336,8 +336,8 @@ module SyntaxTree
|
|
336
336
|
# Visit an Assoc node.
|
337
337
|
def visit_assoc(node)
|
338
338
|
if node.value.nil?
|
339
|
+
# { foo: }
|
339
340
|
expression = srange(node.start_char, node.end_char - 1)
|
340
|
-
|
341
341
|
type, location =
|
342
342
|
if node.key.value.start_with?(/[A-Z]/)
|
343
343
|
[:const, smap_constant(nil, expression, expression)]
|
@@ -356,13 +356,38 @@ module SyntaxTree
|
|
356
356
|
srange_node(node)
|
357
357
|
)
|
358
358
|
)
|
359
|
-
|
359
|
+
elsif node.key.is_a?(Label)
|
360
|
+
# { foo: 1 }
|
360
361
|
s(
|
361
362
|
:pair,
|
362
363
|
[visit(node.key), visit(node.value)],
|
363
364
|
smap_operator(
|
364
|
-
|
365
|
-
|
365
|
+
srange_length(node.key.end_char, -1),
|
366
|
+
srange_node(node)
|
367
|
+
)
|
368
|
+
)
|
369
|
+
elsif (operator = srange_search_between(node.key, node.value, "=>"))
|
370
|
+
# { :foo => 1 }
|
371
|
+
s(
|
372
|
+
:pair,
|
373
|
+
[visit(node.key), visit(node.value)],
|
374
|
+
smap_operator(operator, srange_node(node))
|
375
|
+
)
|
376
|
+
else
|
377
|
+
# { "foo": 1 }
|
378
|
+
key = visit(node.key)
|
379
|
+
key_location =
|
380
|
+
smap_collection(
|
381
|
+
key.location.begin,
|
382
|
+
srange_length(node.key.end_char - 2, 1),
|
383
|
+
srange(node.key.start_char, node.key.end_char - 1)
|
384
|
+
)
|
385
|
+
|
386
|
+
s(
|
387
|
+
:pair,
|
388
|
+
[s(key.type, key.children, key_location), visit(node.value)],
|
389
|
+
smap_operator(
|
390
|
+
srange_length(node.key.end_char, -1),
|
366
391
|
srange_node(node)
|
367
392
|
)
|
368
393
|
)
|
@@ -769,7 +794,11 @@ module SyntaxTree
|
|
769
794
|
|
770
795
|
srange(node.start_char, end_char)
|
771
796
|
elsif node.block
|
772
|
-
|
797
|
+
if node.receiver
|
798
|
+
srange(node.receiver.start_char, node.message.end_char)
|
799
|
+
else
|
800
|
+
srange_node(node.message)
|
801
|
+
end
|
773
802
|
else
|
774
803
|
srange_node(node)
|
775
804
|
end
|
@@ -1010,6 +1039,21 @@ module SyntaxTree
|
|
1010
1039
|
|
1011
1040
|
# Visit an Elsif node.
|
1012
1041
|
def visit_elsif(node)
|
1042
|
+
begin_start = node.predicate.end_char
|
1043
|
+
begin_end =
|
1044
|
+
if node.statements.empty?
|
1045
|
+
node.statements.end_char
|
1046
|
+
else
|
1047
|
+
node.statements.body.first.start_char
|
1048
|
+
end
|
1049
|
+
|
1050
|
+
begin_token =
|
1051
|
+
if buffer.source[begin_start...begin_end].include?("then")
|
1052
|
+
srange_find(begin_start, begin_end, "then")
|
1053
|
+
elsif buffer.source[begin_start...begin_end].include?(";")
|
1054
|
+
srange_find(begin_start, begin_end, ";")
|
1055
|
+
end
|
1056
|
+
|
1013
1057
|
else_token =
|
1014
1058
|
case node.consequent
|
1015
1059
|
when Elsif
|
@@ -1029,7 +1073,7 @@ module SyntaxTree
|
|
1029
1073
|
],
|
1030
1074
|
smap_condition(
|
1031
1075
|
srange_length(node.start_char, 5),
|
1032
|
-
|
1076
|
+
begin_token,
|
1033
1077
|
else_token,
|
1034
1078
|
nil,
|
1035
1079
|
expression
|
@@ -1287,35 +1331,13 @@ module SyntaxTree
|
|
1287
1331
|
|
1288
1332
|
# Visit an IfNode node.
|
1289
1333
|
def visit_if(node)
|
1290
|
-
predicate =
|
1291
|
-
case node.predicate
|
1292
|
-
when RangeNode
|
1293
|
-
type =
|
1294
|
-
node.predicate.operator.value == ".." ? :iflipflop : :eflipflop
|
1295
|
-
s(type, visit(node.predicate).children, nil)
|
1296
|
-
when RegexpLiteral
|
1297
|
-
s(:match_current_line, [visit(node.predicate)], nil)
|
1298
|
-
when Unary
|
1299
|
-
if node.predicate.operator.value == "!" &&
|
1300
|
-
node.predicate.statement.is_a?(RegexpLiteral)
|
1301
|
-
s(
|
1302
|
-
:send,
|
1303
|
-
[
|
1304
|
-
s(:match_current_line, [visit(node.predicate.statement)]),
|
1305
|
-
:!
|
1306
|
-
],
|
1307
|
-
nil
|
1308
|
-
)
|
1309
|
-
else
|
1310
|
-
visit(node.predicate)
|
1311
|
-
end
|
1312
|
-
else
|
1313
|
-
visit(node.predicate)
|
1314
|
-
end
|
1315
|
-
|
1316
1334
|
s(
|
1317
1335
|
:if,
|
1318
|
-
[
|
1336
|
+
[
|
1337
|
+
visit_predicate(node.predicate),
|
1338
|
+
visit(node.statements),
|
1339
|
+
visit(node.consequent)
|
1340
|
+
],
|
1319
1341
|
if node.modifier?
|
1320
1342
|
smap_keyword_bare(
|
1321
1343
|
srange_find_between(node.statements, node.predicate, "if"),
|
@@ -1551,12 +1573,14 @@ module SyntaxTree
|
|
1551
1573
|
location =
|
1552
1574
|
if node.start_char == node.end_char
|
1553
1575
|
smap_collection_bare(nil)
|
1554
|
-
|
1576
|
+
elsif buffer.source[node.start_char - 1] == "("
|
1555
1577
|
smap_collection(
|
1556
1578
|
srange_length(node.start_char, 1),
|
1557
1579
|
srange_length(node.end_char, -1),
|
1558
1580
|
srange_node(node)
|
1559
1581
|
)
|
1582
|
+
else
|
1583
|
+
smap_collection_bare(srange_node(node))
|
1560
1584
|
end
|
1561
1585
|
|
1562
1586
|
s(:args, visit(node.params).children + shadowargs, location)
|
@@ -1577,28 +1601,20 @@ module SyntaxTree
|
|
1577
1601
|
# Visit a MethodAddBlock node.
|
1578
1602
|
def visit_method_add_block(node)
|
1579
1603
|
case node.call
|
1580
|
-
when Break, Next, ReturnNode
|
1581
|
-
type, arguments = block_children(node.block)
|
1582
|
-
call = visit(node.call)
|
1583
|
-
|
1584
|
-
s(
|
1585
|
-
call.type,
|
1586
|
-
[
|
1587
|
-
s(
|
1588
|
-
type,
|
1589
|
-
[*call.children, arguments, visit(node.block.bodystmt)],
|
1590
|
-
nil
|
1591
|
-
)
|
1592
|
-
],
|
1593
|
-
nil
|
1594
|
-
)
|
1595
1604
|
when ARef, Super, ZSuper
|
1596
1605
|
type, arguments = block_children(node.block)
|
1597
1606
|
|
1598
1607
|
s(
|
1599
1608
|
type,
|
1600
1609
|
[visit(node.call), arguments, visit(node.block.bodystmt)],
|
1601
|
-
|
1610
|
+
smap_collection(
|
1611
|
+
srange_node(node.block.opening),
|
1612
|
+
srange_length(
|
1613
|
+
node.block.end_char,
|
1614
|
+
node.block.keywords? ? -3 : -1
|
1615
|
+
),
|
1616
|
+
srange_node(node)
|
1617
|
+
)
|
1602
1618
|
)
|
1603
1619
|
else
|
1604
1620
|
visit_command_call(
|
@@ -2274,7 +2290,16 @@ module SyntaxTree
|
|
2274
2290
|
)
|
2275
2291
|
)
|
2276
2292
|
when ArgsForward
|
2277
|
-
s(
|
2293
|
+
s(
|
2294
|
+
:super,
|
2295
|
+
[visit(node.arguments.arguments)],
|
2296
|
+
smap_keyword(
|
2297
|
+
srange_length(node.start_char, 5),
|
2298
|
+
srange_find(node.start_char + 5, node.end_char, "("),
|
2299
|
+
srange_length(node.end_char, -1),
|
2300
|
+
srange_node(node)
|
2301
|
+
)
|
2302
|
+
)
|
2278
2303
|
else
|
2279
2304
|
s(
|
2280
2305
|
:super,
|
@@ -2376,22 +2401,58 @@ module SyntaxTree
|
|
2376
2401
|
# Visit a Unary node.
|
2377
2402
|
def visit_unary(node)
|
2378
2403
|
# Special handling here for flipflops
|
2379
|
-
if node.statement.is_a?(Paren) &&
|
2380
|
-
|
2381
|
-
|
2382
|
-
(range =
|
2404
|
+
if (paren = node.statement).is_a?(Paren) &&
|
2405
|
+
paren.contents.is_a?(Statements) &&
|
2406
|
+
paren.contents.body.length == 1 &&
|
2407
|
+
(range = paren.contents.body.first).is_a?(RangeNode) &&
|
2383
2408
|
node.operator == "!"
|
2384
|
-
|
2385
|
-
|
2386
|
-
|
2387
|
-
|
2388
|
-
|
2389
|
-
|
2409
|
+
s(
|
2410
|
+
:send,
|
2411
|
+
[
|
2412
|
+
s(
|
2413
|
+
:begin,
|
2414
|
+
[
|
2415
|
+
s(
|
2416
|
+
range.operator.value == ".." ? :iflipflop : :eflipflop,
|
2417
|
+
visit(range).children,
|
2418
|
+
smap_operator(
|
2419
|
+
srange_node(range.operator),
|
2420
|
+
srange_node(range)
|
2421
|
+
)
|
2422
|
+
)
|
2423
|
+
],
|
2424
|
+
smap_collection(
|
2425
|
+
srange_length(paren.start_char, 1),
|
2426
|
+
srange_length(paren.end_char, -1),
|
2427
|
+
srange_node(paren)
|
2428
|
+
)
|
2429
|
+
),
|
2430
|
+
:!
|
2431
|
+
],
|
2432
|
+
smap_send_bare(
|
2433
|
+
srange_length(node.start_char, 1),
|
2434
|
+
srange_node(node)
|
2435
|
+
)
|
2436
|
+
)
|
2437
|
+
elsif node.operator == "!" && node.statement.is_a?(RegexpLiteral)
|
2438
|
+
s(
|
2439
|
+
:send,
|
2440
|
+
[
|
2441
|
+
s(
|
2442
|
+
:match_current_line,
|
2443
|
+
[visit(node.statement)],
|
2444
|
+
smap(srange_node(node.statement))
|
2445
|
+
),
|
2446
|
+
:!
|
2447
|
+
],
|
2448
|
+
smap_send_bare(
|
2449
|
+
srange_length(node.start_char, 1),
|
2450
|
+
srange_node(node)
|
2390
2451
|
)
|
2391
2452
|
)
|
2453
|
+
else
|
2454
|
+
visit(canonical_unary(node))
|
2392
2455
|
end
|
2393
|
-
|
2394
|
-
visit(canonical_unary(node))
|
2395
2456
|
end
|
2396
2457
|
|
2397
2458
|
# Visit an Undef node.
|
@@ -2408,41 +2469,43 @@ module SyntaxTree
|
|
2408
2469
|
|
2409
2470
|
# Visit an UnlessNode node.
|
2410
2471
|
def visit_unless(node)
|
2411
|
-
predicate =
|
2412
|
-
case node.predicate
|
2413
|
-
when RegexpLiteral
|
2414
|
-
s(:match_current_line, [visit(node.predicate)], nil)
|
2415
|
-
when Unary
|
2416
|
-
if node.predicate.operator.value == "!" &&
|
2417
|
-
node.predicate.statement.is_a?(RegexpLiteral)
|
2418
|
-
s(
|
2419
|
-
:send,
|
2420
|
-
[
|
2421
|
-
s(:match_current_line, [visit(node.predicate.statement)]),
|
2422
|
-
:!
|
2423
|
-
],
|
2424
|
-
nil
|
2425
|
-
)
|
2426
|
-
else
|
2427
|
-
visit(node.predicate)
|
2428
|
-
end
|
2429
|
-
else
|
2430
|
-
visit(node.predicate)
|
2431
|
-
end
|
2432
|
-
|
2433
2472
|
s(
|
2434
2473
|
:if,
|
2435
|
-
[
|
2474
|
+
[
|
2475
|
+
visit_predicate(node.predicate),
|
2476
|
+
visit(node.consequent),
|
2477
|
+
visit(node.statements)
|
2478
|
+
],
|
2436
2479
|
if node.modifier?
|
2437
2480
|
smap_keyword_bare(
|
2438
2481
|
srange_find_between(node.statements, node.predicate, "unless"),
|
2439
2482
|
srange_node(node)
|
2440
2483
|
)
|
2441
2484
|
else
|
2485
|
+
begin_start = node.predicate.end_char
|
2486
|
+
begin_end =
|
2487
|
+
if node.statements.empty?
|
2488
|
+
node.statements.end_char
|
2489
|
+
else
|
2490
|
+
node.statements.body.first.start_char
|
2491
|
+
end
|
2492
|
+
|
2493
|
+
begin_token =
|
2494
|
+
if buffer.source[begin_start...begin_end].include?("then")
|
2495
|
+
srange_find(begin_start, begin_end, "then")
|
2496
|
+
elsif buffer.source[begin_start...begin_end].include?(";")
|
2497
|
+
srange_find(begin_start, begin_end, ";")
|
2498
|
+
end
|
2499
|
+
|
2500
|
+
else_token =
|
2501
|
+
if node.consequent
|
2502
|
+
srange_length(node.consequent.start_char, 4)
|
2503
|
+
end
|
2504
|
+
|
2442
2505
|
smap_condition(
|
2443
2506
|
srange_length(node.start_char, 6),
|
2444
|
-
|
2445
|
-
|
2507
|
+
begin_token,
|
2508
|
+
else_token,
|
2446
2509
|
srange_length(node.end_char, -3),
|
2447
2510
|
srange_node(node)
|
2448
2511
|
)
|
@@ -3014,6 +3077,31 @@ module SyntaxTree
|
|
3014
3077
|
location = node.location
|
3015
3078
|
srange(location.start_char, location.end_char)
|
3016
3079
|
end
|
3080
|
+
|
3081
|
+
def visit_predicate(node)
|
3082
|
+
case node
|
3083
|
+
when RangeNode
|
3084
|
+
s(
|
3085
|
+
node.operator.value == ".." ? :iflipflop : :eflipflop,
|
3086
|
+
visit(node).children,
|
3087
|
+
smap_operator(srange_node(node.operator), srange_node(node))
|
3088
|
+
)
|
3089
|
+
when RegexpLiteral
|
3090
|
+
s(:match_current_line, [visit(node)], smap(srange_node(node)))
|
3091
|
+
when Unary
|
3092
|
+
if node.operator.value == "!" && node.statement.is_a?(RegexpLiteral)
|
3093
|
+
s(
|
3094
|
+
:send,
|
3095
|
+
[s(:match_current_line, [visit(node.statement)]), :!],
|
3096
|
+
smap_send_bare(srange_node(node.operator), srange_node(node))
|
3097
|
+
)
|
3098
|
+
else
|
3099
|
+
visit(node)
|
3100
|
+
end
|
3101
|
+
else
|
3102
|
+
visit(node)
|
3103
|
+
end
|
3104
|
+
end
|
3017
3105
|
end
|
3018
3106
|
end
|
3019
3107
|
end
|
data/lib/syntax_tree/version.rb
CHANGED
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: 6.0.
|
4
|
+
version: 6.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Newton
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-02-
|
11
|
+
date: 2023-02-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: prettier_print
|