@@ -20,46 +20,128 @@ def initialize(line, column)
20
20
21
21
# This entry represents a class definition using the class keyword.
22
22
class ClassDefinition
23
- attr_reader :nesting , :name , :location
23
+ attr_reader :nesting , :name , :location , :comments
24
24
25
- def initialize ( nesting , name , location )
25
+ def initialize ( nesting , name , location , comments )
26
26
@nesting = nesting
27
27
@name = name
28
28
@location = location
29
+ @comments = comments
29
30
end
30
31
end
31
32
32
33
# This entry represents a module definition using the module keyword.
33
34
class ModuleDefinition
34
- attr_reader :nesting , :name , :location
35
+ attr_reader :nesting , :name , :location , :comments
35
36
36
- def initialize ( nesting , name , location )
37
+ def initialize ( nesting , name , location , comments )
37
38
@nesting = nesting
38
39
@name = name
39
40
@location = location
41
+ @comments = comments
40
42
end
41
43
end
42
44
43
45
# This entry represents a method definition using the def keyword.
44
46
class MethodDefinition
45
- attr_reader :nesting , :name , :location
47
+ attr_reader :nesting , :name , :location , :comments
46
48
47
- def initialize ( nesting , name , location )
49
+ def initialize ( nesting , name , location , comments )
48
50
@nesting = nesting
49
51
@name = name
50
52
@location = location
53
+ @comments = comments
51
54
end
52
55
end
53
56
54
57
# This entry represents a singleton method definition using the def keyword
55
58
# with a specified target.
56
59
class SingletonMethodDefinition
57
- attr_reader :nesting , :name , :location
60
+ attr_reader :nesting , :name , :location , :comments
58
61
59
- def initialize ( nesting , name , location )
62
+ def initialize ( nesting , name , location , comments )
60
63
@nesting = nesting
61
64
@name = name
62
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 )
63
145
end
64
146
end
65
147
@@ -74,16 +156,22 @@ class ISeqBackend
74
156
VM_DEFINECLASS_FLAG_HAS_SUPERCLASS = 0x10
75
157
76
158
def index ( source )
77
- index_iseq ( RubyVM ::InstructionSequence . compile ( source ) . to_a )
159
+ index_iseq (
160
+ RubyVM ::InstructionSequence . compile ( source ) . to_a ,
161
+ FileComments . new ( FileComments ::StringSource . new ( source ) )
162
+ )
78
163
end
79
164
80
165
def index_file ( filepath )
81
- index_iseq ( RubyVM ::InstructionSequence . compile_file ( filepath ) . to_a )
166
+ index_iseq (
167
+ RubyVM ::InstructionSequence . compile_file ( filepath ) . to_a ,
168
+ FileComments . new ( FileComments ::FileSource . new ( filepath ) )
169
+ )
82
170
end
83
171
84
172
private
85
173
86
- def index_iseq ( iseq )
174
+ def index_iseq ( iseq , file_comments )
87
175
results = [ ]
88
176
queue = [ [ iseq , [ ] ] ]
89
177
@@ -106,11 +194,23 @@ def index_iseq(iseq)
106
194
elsif flags & VM_DEFINECLASS_TYPE_MODULE > 0
107
195
code_location = class_iseq [ 4 ] [ :code_location ]
108
196
location = Location . new ( code_location [ 0 ] , code_location [ 1 ] )
109
- results << ModuleDefinition . new ( current_nesting , name , location )
197
+
198
+ results << ModuleDefinition . new (
199
+ current_nesting ,
200
+ name ,
201
+ location ,
202
+ EntryComments . new ( file_comments , location )
203
+ )
110
204
else
111
205
code_location = class_iseq [ 4 ] [ :code_location ]
112
206
location = Location . new ( code_location [ 0 ] , code_location [ 1 ] )
113
- results << ClassDefinition . new ( current_nesting , name , location )
207
+
208
+ results << ClassDefinition . new (
209
+ current_nesting ,
210
+ name ,
211
+ location ,
212
+ EntryComments . new ( file_comments , location )
213
+ )
114
214
end
115
215
116
216
queue << [ class_iseq , current_nesting + [ name ] ]
@@ -122,14 +222,21 @@ def index_iseq(iseq)
122
222
results << SingletonMethodDefinition . new (
123
223
current_nesting ,
124
224
name ,
125
- location
225
+ location ,
226
+ EntryComments . new ( file_comments , location )
126
227
)
127
228
when :definesmethod
128
229
_ , name , method_iseq = insn
129
230
130
231
code_location = method_iseq [ 4 ] [ :code_location ]
131
232
location = Location . new ( code_location [ 0 ] , code_location [ 1 ] )
132
- results << MethodDefinition . new ( current_nesting , name , location )
233
+
234
+ results << MethodDefinition . new (
235
+ current_nesting ,
236
+ name ,
237
+ location ,
238
+ EntryComments . new ( file_comments , location )
239
+ )
133
240
end
134
241
end
135
242
end
@@ -143,21 +250,27 @@ def index_iseq(iseq)
143
250
# supported on all runtimes.
144
251
class ParserBackend
145
252
class IndexVisitor < Visitor
146
- attr_reader :results , :nesting
253
+ attr_reader :results , :nesting , :statements
147
254
148
255
def initialize
149
256
@results = [ ]
150
257
@nesting = [ ]
258
+ @statements = nil
151
259
end
152
260
153
261
def visit_class ( node )
154
262
name = visit ( node . constant ) . to_sym
155
263
location =
156
264
Location . new ( node . location . start_line , node . location . start_column )
157
265
158
- results << ClassDefinition . new ( nesting . dup , name , location )
159
- nesting << name
266
+ results << ClassDefinition . new (
267
+ nesting . dup ,
268
+ name ,
269
+ location ,
270
+ comments_for ( node )
271
+ )
160
272
273
+ nesting << name
161
274
super
162
275
nesting . pop
163
276
end
@@ -172,9 +285,19 @@ def visit_def(node)
172
285
Location . new ( node . location . start_line , node . location . start_column )
173
286
174
287
results << if node . target . nil?
175
- MethodDefinition . new ( nesting . dup , name , location )
288
+ MethodDefinition . new (
289
+ nesting . dup ,
290
+ name ,
291
+ location ,
292
+ comments_for ( node )
293
+ )
176
294
else
177
- SingletonMethodDefinition . new ( nesting . dup , name , location )
295
+ SingletonMethodDefinition . new (
296
+ nesting . dup ,
297
+ name ,
298
+ location ,
299
+ comments_for ( node )
300
+ )
178
301
end
179
302
end
180
303
@@ -183,9 +306,14 @@ def visit_module(node)
183
306
location =
184
307
Location . new ( node . location . start_line , node . location . start_column )
185
308
186
- results << ModuleDefinition . new ( nesting . dup , name , location )
187
- nesting << name
309
+ results << ModuleDefinition . new (
310
+ nesting . dup ,
311
+ name ,
312
+ location ,
313
+ comments_for ( node )
314
+ )
188
315
316
+ nesting << name
189
317
super
190
318
nesting . pop
191
319
end
@@ -194,6 +322,30 @@ def visit_program(node)
194
322
super
195
323
results
196
324
end
325
+
326
+ def visit_statements ( node )
327
+ @statements = node
328
+ super
329
+ end
330
+
331
+ private
332
+
333
+ def comments_for ( node )
334
+ comments = [ ]
335
+
336
+ body = statements . body
337
+ line = node . location . start_line - 1
338
+ index = body . index ( node ) - 1
339
+
340
+ while index >= 0 && body [ index ] . is_a? ( Comment ) &&
341
+ ( line - body [ index ] . location . start_line < 2 )
342
+ comments . unshift ( body [ index ] . value )
343
+ line = body [ index ] . location . start_line
344
+ index -= 1
345
+ end
346
+
347
+ comments
348
+ end
197
349
end
198
350
199
351
def index ( source )
0 commit comments