syntax_tree 5.2.0 → 5.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/auto-merge.yml +1 -1
- data/.github/workflows/gh-pages.yml +1 -1
- data/.gitmodules +3 -0
- data/.rubocop.yml +4 -0
- data/CHANGELOG.md +18 -1
- data/Gemfile.lock +6 -6
- data/README.md +1 -0
- data/lib/syntax_tree/cli.rb +3 -2
- data/lib/syntax_tree/formatter.rb +23 -2
- data/lib/syntax_tree/index.rb +374 -0
- data/lib/syntax_tree/node.rb +127 -97
- data/lib/syntax_tree/parser.rb +19 -2
- data/lib/syntax_tree/plugin/disable_ternary.rb +7 -0
- data/lib/syntax_tree/version.rb +1 -1
- data/lib/syntax_tree/yarv/assembler.rb +10 -6
- data/lib/syntax_tree/yarv/compiler.rb +1 -1
- data/lib/syntax_tree/yarv/decompiler.rb +1 -1
- data/lib/syntax_tree/yarv/instruction_sequence.rb +1 -1
- data/lib/syntax_tree/yarv/instructions.rb +800 -0
- data/lib/syntax_tree/yarv/legacy.rb +33 -0
- data/lib/syntax_tree/yarv/vm.rb +4 -0
- data/lib/syntax_tree.rb +15 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3261679bf963ba7263bb3ae21e826ed1fa7a28c6afbf2a2b65b42101be16b7ac
|
4
|
+
data.tar.gz: 653c6c79a34db7686ceeadf83f0d8adb47093d1ab1d946636008ee4c31144258
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9cefe2a22c594efc74aa0fbe300c986a414c890efb7204633481345ceb9b0bd3578d836046ca64e289621ce31ea343f0da40db1f9b3d2d7dbd7f0a11b020c725
|
7
|
+
data.tar.gz: 628ed098064cb9e6dc720666d59b5e9d8653f7e06cb7e0774887dfeb4ed4a5bdb217ea644b93f52a3730e2dfdc14334c0690018d145728a89fa99cbd041e4301
|
data/.gitmodules
CHANGED
data/.rubocop.yml
CHANGED
@@ -8,8 +8,12 @@ AllCops:
|
|
8
8
|
TargetRubyVersion: 2.7
|
9
9
|
Exclude:
|
10
10
|
- '{.git,.github,bin,coverage,pkg,spec,test/fixtures,vendor,tmp}/**/*'
|
11
|
+
- test/ruby-syntax-fixtures/**/*
|
11
12
|
- test.rb
|
12
13
|
|
14
|
+
Gemspec/DevelopmentDependencies:
|
15
|
+
Enabled: false
|
16
|
+
|
13
17
|
Layout/LineLength:
|
14
18
|
Max: 80
|
15
19
|
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,22 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [5.3.0] - 2023-01-26
|
10
|
+
|
11
|
+
### Added
|
12
|
+
|
13
|
+
- `#arity` has been added to `DefNode`, `BlockNode`, and `Params`. The method returns a range where the lower bound is the minimum and the upper bound is the maximum number of arguments that can be used to invoke that block/method definition.
|
14
|
+
- `#arity` has been added to `CallNode`, `Command`, `CommandCall`, and `VCall` nodes. The method returns the number of arguments included in the invocation. For splats, double splats, or argument forwards, this method returns `Float::INFINITY`.
|
15
|
+
- `SyntaxTree::index` and `SyntaxTree::index_file` APIs have been added to collect a list of classes, modules, and methods defined in a given source string or file, respectively. These APIs are experimental and subject to change.
|
16
|
+
- A `plugin/disable_auto_ternary` plugin has been added the disables the formatted that automatically changes permissable `if/else` clauses into ternaries.
|
17
|
+
|
18
|
+
### Changed
|
19
|
+
|
20
|
+
- Files are now only written from the CLI if the content of them changes, which should match watching files less chaotic.
|
21
|
+
- In the case that `rb_iseq_load` cannot be found, `Fiddle::DLError` is now rescued.
|
22
|
+
- Previously if there were invalid UTF-8 byte sequences after the `__END__` keyword the parser could potentially have crashed when parsing comments. This has been fixed.
|
23
|
+
- Previously there was special formatting for array literals that contained only variable references (either locals, method calls, or constants). For consistency, this has been removed and all array literals are now formatted the same way.
|
24
|
+
|
9
25
|
## [5.2.0] - 2023-01-04
|
10
26
|
|
11
27
|
### Added
|
@@ -481,7 +497,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
|
|
481
497
|
|
482
498
|
- 🎉 Initial release! 🎉
|
483
499
|
|
484
|
-
[unreleased]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v5.
|
500
|
+
[unreleased]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v5.3.0...HEAD
|
501
|
+
[5.3.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v5.2.0...v5.3.0
|
485
502
|
[5.2.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v5.1.0...v5.2.0
|
486
503
|
[5.1.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v5.0.1...v5.1.0
|
487
504
|
[5.0.1]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v5.0.0...v5.0.1
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
syntax_tree (5.
|
4
|
+
syntax_tree (5.3.0)
|
5
5
|
prettier_print (>= 1.2.0)
|
6
6
|
|
7
7
|
GEM
|
@@ -17,18 +17,18 @@ GEM
|
|
17
17
|
prettier_print (1.2.0)
|
18
18
|
rainbow (3.1.1)
|
19
19
|
rake (13.0.6)
|
20
|
-
regexp_parser (2.6.
|
20
|
+
regexp_parser (2.6.2)
|
21
21
|
rexml (3.2.5)
|
22
|
-
rubocop (1.
|
22
|
+
rubocop (1.44.1)
|
23
23
|
json (~> 2.3)
|
24
24
|
parallel (~> 1.10)
|
25
|
-
parser (>= 3.
|
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
29
|
rubocop-ast (>= 1.24.1, < 2.0)
|
30
30
|
ruby-progressbar (~> 1.7)
|
31
|
-
unicode-display_width (>=
|
31
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
32
32
|
rubocop-ast (1.24.1)
|
33
33
|
parser (>= 3.1.1.0)
|
34
34
|
ruby-progressbar (1.11.0)
|
@@ -38,7 +38,7 @@ GEM
|
|
38
38
|
simplecov_json_formatter (~> 0.1)
|
39
39
|
simplecov-html (0.12.3)
|
40
40
|
simplecov_json_formatter (0.1.4)
|
41
|
-
unicode-display_width (2.4.
|
41
|
+
unicode-display_width (2.4.2)
|
42
42
|
|
43
43
|
PLATFORMS
|
44
44
|
arm64-darwin-21
|
data/README.md
CHANGED
@@ -658,6 +658,7 @@ To register plugins, define a file somewhere in your load path named `syntax_tre
|
|
658
658
|
|
659
659
|
* `plugin/single_quotes` - This will change all of your string literals to use single quotes instead of the default double quotes.
|
660
660
|
* `plugin/trailing_comma` - This will put trailing commas into multiline array literals, hash literals, and method calls that can support trailing commas.
|
661
|
+
* `plugin/disable_auto_ternary` - This will prevent the automatic conversion of `if ... else` to ternary expressions.
|
661
662
|
|
662
663
|
If you're using Syntax Tree as a library, you can require those files directly or manually pass those options to the formatter initializer through the `SyntaxTree::Formatter::Options` class.
|
663
664
|
|
data/lib/syntax_tree/cli.rb
CHANGED
@@ -303,10 +303,11 @@ module SyntaxTree
|
|
303
303
|
options.print_width,
|
304
304
|
options: options.formatter_options
|
305
305
|
)
|
306
|
+
changed = source != formatted
|
306
307
|
|
307
|
-
File.write(filepath, formatted) if item.writable?
|
308
|
+
File.write(filepath, formatted) if item.writable? && changed
|
308
309
|
|
309
|
-
color =
|
310
|
+
color = changed ? filepath : Color.gray(filepath)
|
310
311
|
delta = ((Time.now - start) * 1000).round
|
311
312
|
|
312
313
|
puts "#{color} #{delta}ms"
|
@@ -21,11 +21,15 @@ module SyntaxTree
|
|
21
21
|
# that folks have become entrenched in their ways, we decided to provide a
|
22
22
|
# small amount of configurability.
|
23
23
|
class Options
|
24
|
-
attr_reader :quote,
|
24
|
+
attr_reader :quote,
|
25
|
+
:trailing_comma,
|
26
|
+
:disable_auto_ternary,
|
27
|
+
:target_ruby_version
|
25
28
|
|
26
29
|
def initialize(
|
27
30
|
quote: :default,
|
28
31
|
trailing_comma: :default,
|
32
|
+
disable_auto_ternary: :default,
|
29
33
|
target_ruby_version: :default
|
30
34
|
)
|
31
35
|
@quote =
|
@@ -50,6 +54,17 @@ module SyntaxTree
|
|
50
54
|
trailing_comma
|
51
55
|
end
|
52
56
|
|
57
|
+
@disable_auto_ternary =
|
58
|
+
if disable_auto_ternary == :default
|
59
|
+
# We ship with a disable ternary plugin that will define this
|
60
|
+
# constant. That constant is responsible for determining the default
|
61
|
+
# disable ternary value. If it's defined, then we default to true.
|
62
|
+
# Otherwise we default to false.
|
63
|
+
defined?(DISABLE_TERNARY)
|
64
|
+
else
|
65
|
+
disable_auto_ternary
|
66
|
+
end
|
67
|
+
|
53
68
|
@target_ruby_version =
|
54
69
|
if target_ruby_version == :default
|
55
70
|
# The default target Ruby version is the current version of Ruby.
|
@@ -69,8 +84,13 @@ module SyntaxTree
|
|
69
84
|
|
70
85
|
# These options are overridden in plugins to we need to make sure they are
|
71
86
|
# available here.
|
72
|
-
attr_reader :quote,
|
87
|
+
attr_reader :quote,
|
88
|
+
:trailing_comma,
|
89
|
+
:disable_auto_ternary,
|
90
|
+
:target_ruby_version
|
91
|
+
|
73
92
|
alias trailing_comma? trailing_comma
|
93
|
+
alias disable_auto_ternary? disable_auto_ternary
|
74
94
|
|
75
95
|
def initialize(source, *args, options: Options.new)
|
76
96
|
super(*args)
|
@@ -81,6 +101,7 @@ module SyntaxTree
|
|
81
101
|
# Memoizing these values to make access faster.
|
82
102
|
@quote = options.quote
|
83
103
|
@trailing_comma = options.trailing_comma
|
104
|
+
@disable_auto_ternary = options.disable_auto_ternary
|
84
105
|
@target_ruby_version = options.target_ruby_version
|
85
106
|
end
|
86
107
|
|
@@ -0,0 +1,374 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SyntaxTree
|
4
|
+
# This class can be used to build an index of the structure of Ruby files. We
|
5
|
+
# define an index as the list of constants and methods defined within a file.
|
6
|
+
#
|
7
|
+
# This index strives to be as fast as possible to better support tools like
|
8
|
+
# IDEs. Because of that, it has different backends depending on what
|
9
|
+
# functionality is available.
|
10
|
+
module Index
|
11
|
+
# This is a location for an index entry.
|
12
|
+
class Location
|
13
|
+
attr_reader :line, :column
|
14
|
+
|
15
|
+
def initialize(line, column)
|
16
|
+
@line = line
|
17
|
+
@column = column
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# This entry represents a class definition using the class keyword.
|
22
|
+
class ClassDefinition
|
23
|
+
attr_reader :nesting, :name, :location, :comments
|
24
|
+
|
25
|
+
def initialize(nesting, name, location, comments)
|
26
|
+
@nesting = nesting
|
27
|
+
@name = name
|
28
|
+
@location = location
|
29
|
+
@comments = comments
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# This entry represents a module definition using the module keyword.
|
34
|
+
class ModuleDefinition
|
35
|
+
attr_reader :nesting, :name, :location, :comments
|
36
|
+
|
37
|
+
def initialize(nesting, name, location, comments)
|
38
|
+
@nesting = nesting
|
39
|
+
@name = name
|
40
|
+
@location = location
|
41
|
+
@comments = comments
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# This entry represents a method definition using the def keyword.
|
46
|
+
class MethodDefinition
|
47
|
+
attr_reader :nesting, :name, :location, :comments
|
48
|
+
|
49
|
+
def initialize(nesting, name, location, comments)
|
50
|
+
@nesting = nesting
|
51
|
+
@name = name
|
52
|
+
@location = location
|
53
|
+
@comments = comments
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# This entry represents a singleton method definition using the def keyword
|
58
|
+
# with a specified target.
|
59
|
+
class SingletonMethodDefinition
|
60
|
+
attr_reader :nesting, :name, :location, :comments
|
61
|
+
|
62
|
+
def initialize(nesting, name, location, comments)
|
63
|
+
@nesting = nesting
|
64
|
+
@name = name
|
65
|
+
@location = location
|
66
|
+
@comments = comments
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# When you're using the instruction sequence backend, this class is used to
|
71
|
+
# lazily parse comments out of the source code.
|
72
|
+
class FileComments
|
73
|
+
# We use the ripper library to pull out source comments.
|
74
|
+
class Parser < Ripper
|
75
|
+
attr_reader :comments
|
76
|
+
|
77
|
+
def initialize(*)
|
78
|
+
super
|
79
|
+
@comments = {}
|
80
|
+
end
|
81
|
+
|
82
|
+
def on_comment(value)
|
83
|
+
comments[lineno] = value.chomp
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# This represents the Ruby source in the form of a file. When it needs to
|
88
|
+
# be read we'll read the file.
|
89
|
+
class FileSource
|
90
|
+
attr_reader :filepath
|
91
|
+
|
92
|
+
def initialize(filepath)
|
93
|
+
@filepath = filepath
|
94
|
+
end
|
95
|
+
|
96
|
+
def source
|
97
|
+
File.read(filepath)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# This represents the Ruby source in the form of a string. When it needs
|
102
|
+
# to be read the string is returned.
|
103
|
+
class StringSource
|
104
|
+
attr_reader :source
|
105
|
+
|
106
|
+
def initialize(source)
|
107
|
+
@source = source
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
attr_reader :source
|
112
|
+
|
113
|
+
def initialize(source)
|
114
|
+
@source = source
|
115
|
+
end
|
116
|
+
|
117
|
+
def comments
|
118
|
+
@comments ||= Parser.new(source.source).tap(&:parse).comments
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# This class handles parsing comments from Ruby source code in the case that
|
123
|
+
# we use the instruction sequence backend. Because the instruction sequence
|
124
|
+
# backend doesn't provide comments (since they are dropped) we provide this
|
125
|
+
# interface to lazily parse them out.
|
126
|
+
class EntryComments
|
127
|
+
include Enumerable
|
128
|
+
attr_reader :file_comments, :location
|
129
|
+
|
130
|
+
def initialize(file_comments, location)
|
131
|
+
@file_comments = file_comments
|
132
|
+
@location = location
|
133
|
+
end
|
134
|
+
|
135
|
+
def each(&block)
|
136
|
+
line = location.line - 1
|
137
|
+
result = []
|
138
|
+
|
139
|
+
while line >= 0 && (comment = file_comments.comments[line])
|
140
|
+
result.unshift(comment)
|
141
|
+
line -= 1
|
142
|
+
end
|
143
|
+
|
144
|
+
result.each(&block)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# This backend creates the index using RubyVM::InstructionSequence, which is
|
149
|
+
# faster than using the Syntax Tree parser, but is not available on all
|
150
|
+
# runtimes.
|
151
|
+
class ISeqBackend
|
152
|
+
VM_DEFINECLASS_TYPE_CLASS = 0x00
|
153
|
+
VM_DEFINECLASS_TYPE_SINGLETON_CLASS = 0x01
|
154
|
+
VM_DEFINECLASS_TYPE_MODULE = 0x02
|
155
|
+
VM_DEFINECLASS_FLAG_SCOPED = 0x08
|
156
|
+
VM_DEFINECLASS_FLAG_HAS_SUPERCLASS = 0x10
|
157
|
+
|
158
|
+
def index(source)
|
159
|
+
index_iseq(
|
160
|
+
RubyVM::InstructionSequence.compile(source).to_a,
|
161
|
+
FileComments.new(FileComments::StringSource.new(source))
|
162
|
+
)
|
163
|
+
end
|
164
|
+
|
165
|
+
def index_file(filepath)
|
166
|
+
index_iseq(
|
167
|
+
RubyVM::InstructionSequence.compile_file(filepath).to_a,
|
168
|
+
FileComments.new(FileComments::FileSource.new(filepath))
|
169
|
+
)
|
170
|
+
end
|
171
|
+
|
172
|
+
private
|
173
|
+
|
174
|
+
def location_for(iseq)
|
175
|
+
code_location = iseq[4][:code_location]
|
176
|
+
Location.new(code_location[0], code_location[1])
|
177
|
+
end
|
178
|
+
|
179
|
+
def index_iseq(iseq, file_comments)
|
180
|
+
results = []
|
181
|
+
queue = [[iseq, []]]
|
182
|
+
|
183
|
+
while (current_iseq, current_nesting = queue.shift)
|
184
|
+
current_iseq[13].each_with_index do |insn, index|
|
185
|
+
next unless insn.is_a?(Array)
|
186
|
+
|
187
|
+
case insn[0]
|
188
|
+
when :defineclass
|
189
|
+
_, name, class_iseq, flags = insn
|
190
|
+
|
191
|
+
if flags == VM_DEFINECLASS_TYPE_SINGLETON_CLASS
|
192
|
+
# At the moment, we don't support singletons that aren't
|
193
|
+
# defined on self. We could, but it would require more
|
194
|
+
# emulation.
|
195
|
+
if current_iseq[13][index - 2] != [:putself]
|
196
|
+
raise NotImplementedError,
|
197
|
+
"singleton class with non-self receiver"
|
198
|
+
end
|
199
|
+
elsif flags & VM_DEFINECLASS_TYPE_MODULE > 0
|
200
|
+
location = location_for(class_iseq)
|
201
|
+
results << ModuleDefinition.new(
|
202
|
+
current_nesting,
|
203
|
+
name,
|
204
|
+
location,
|
205
|
+
EntryComments.new(file_comments, location)
|
206
|
+
)
|
207
|
+
else
|
208
|
+
location = location_for(class_iseq)
|
209
|
+
results << ClassDefinition.new(
|
210
|
+
current_nesting,
|
211
|
+
name,
|
212
|
+
location,
|
213
|
+
EntryComments.new(file_comments, location)
|
214
|
+
)
|
215
|
+
end
|
216
|
+
|
217
|
+
queue << [class_iseq, current_nesting + [name]]
|
218
|
+
when :definemethod
|
219
|
+
location = location_for(insn[2])
|
220
|
+
results << MethodDefinition.new(
|
221
|
+
current_nesting,
|
222
|
+
insn[1],
|
223
|
+
location,
|
224
|
+
EntryComments.new(file_comments, location)
|
225
|
+
)
|
226
|
+
when :definesmethod
|
227
|
+
if current_iseq[13][index - 1] != [:putself]
|
228
|
+
raise NotImplementedError,
|
229
|
+
"singleton method with non-self receiver"
|
230
|
+
end
|
231
|
+
|
232
|
+
location = location_for(insn[2])
|
233
|
+
results << SingletonMethodDefinition.new(
|
234
|
+
current_nesting,
|
235
|
+
insn[1],
|
236
|
+
location,
|
237
|
+
EntryComments.new(file_comments, location)
|
238
|
+
)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
results
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
# This backend creates the index using the Syntax Tree parser and a visitor.
|
248
|
+
# It is not as fast as using the instruction sequences directly, but is
|
249
|
+
# supported on all runtimes.
|
250
|
+
class ParserBackend
|
251
|
+
class IndexVisitor < Visitor
|
252
|
+
attr_reader :results, :nesting, :statements
|
253
|
+
|
254
|
+
def initialize
|
255
|
+
@results = []
|
256
|
+
@nesting = []
|
257
|
+
@statements = nil
|
258
|
+
end
|
259
|
+
|
260
|
+
def visit_class(node)
|
261
|
+
name = visit(node.constant).to_sym
|
262
|
+
location =
|
263
|
+
Location.new(node.location.start_line, node.location.start_column)
|
264
|
+
|
265
|
+
results << ClassDefinition.new(
|
266
|
+
nesting.dup,
|
267
|
+
name,
|
268
|
+
location,
|
269
|
+
comments_for(node)
|
270
|
+
)
|
271
|
+
|
272
|
+
nesting << name
|
273
|
+
super
|
274
|
+
nesting.pop
|
275
|
+
end
|
276
|
+
|
277
|
+
def visit_const_ref(node)
|
278
|
+
node.constant.value
|
279
|
+
end
|
280
|
+
|
281
|
+
def visit_def(node)
|
282
|
+
name = node.name.value.to_sym
|
283
|
+
location =
|
284
|
+
Location.new(node.location.start_line, node.location.start_column)
|
285
|
+
|
286
|
+
results << if node.target.nil?
|
287
|
+
MethodDefinition.new(
|
288
|
+
nesting.dup,
|
289
|
+
name,
|
290
|
+
location,
|
291
|
+
comments_for(node)
|
292
|
+
)
|
293
|
+
else
|
294
|
+
SingletonMethodDefinition.new(
|
295
|
+
nesting.dup,
|
296
|
+
name,
|
297
|
+
location,
|
298
|
+
comments_for(node)
|
299
|
+
)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
def visit_module(node)
|
304
|
+
name = visit(node.constant).to_sym
|
305
|
+
location =
|
306
|
+
Location.new(node.location.start_line, node.location.start_column)
|
307
|
+
|
308
|
+
results << ModuleDefinition.new(
|
309
|
+
nesting.dup,
|
310
|
+
name,
|
311
|
+
location,
|
312
|
+
comments_for(node)
|
313
|
+
)
|
314
|
+
|
315
|
+
nesting << name
|
316
|
+
super
|
317
|
+
nesting.pop
|
318
|
+
end
|
319
|
+
|
320
|
+
def visit_program(node)
|
321
|
+
super
|
322
|
+
results
|
323
|
+
end
|
324
|
+
|
325
|
+
def visit_statements(node)
|
326
|
+
@statements = node
|
327
|
+
super
|
328
|
+
end
|
329
|
+
|
330
|
+
private
|
331
|
+
|
332
|
+
def comments_for(node)
|
333
|
+
comments = []
|
334
|
+
|
335
|
+
body = statements.body
|
336
|
+
line = node.location.start_line - 1
|
337
|
+
index = body.index(node) - 1
|
338
|
+
|
339
|
+
while index >= 0 && body[index].is_a?(Comment) &&
|
340
|
+
(line - body[index].location.start_line < 2)
|
341
|
+
comments.unshift(body[index].value)
|
342
|
+
line = body[index].location.start_line
|
343
|
+
index -= 1
|
344
|
+
end
|
345
|
+
|
346
|
+
comments
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
def index(source)
|
351
|
+
SyntaxTree.parse(source).accept(IndexVisitor.new)
|
352
|
+
end
|
353
|
+
|
354
|
+
def index_file(filepath)
|
355
|
+
index(SyntaxTree.read(filepath))
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
# The class defined here is used to perform the indexing, depending on what
|
360
|
+
# functionality is available from the runtime.
|
361
|
+
INDEX_BACKEND =
|
362
|
+
defined?(RubyVM::InstructionSequence) ? ISeqBackend : ParserBackend
|
363
|
+
|
364
|
+
# This method accepts source code and then indexes it.
|
365
|
+
def self.index(source, backend: INDEX_BACKEND.new)
|
366
|
+
backend.index(source)
|
367
|
+
end
|
368
|
+
|
369
|
+
# This method accepts a filepath and then indexes it.
|
370
|
+
def self.index_file(filepath, backend: INDEX_BACKEND.new)
|
371
|
+
backend.index_file(filepath)
|
372
|
+
end
|
373
|
+
end
|
374
|
+
end
|