solargraph 0.53.2 → 0.54.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +36 -0
- data/lib/solargraph/api_map/cache.rb +2 -12
- data/lib/solargraph/api_map/source_to_yard.rb +17 -10
- data/lib/solargraph/api_map/store.rb +11 -6
- data/lib/solargraph/api_map.rb +80 -38
- data/lib/solargraph/complex_type/type_methods.rb +62 -30
- data/lib/solargraph/complex_type/unique_type.rb +149 -75
- data/lib/solargraph/complex_type.rb +41 -25
- data/lib/solargraph/doc_map.rb +19 -3
- data/lib/solargraph/gem_pins.rb +10 -2
- data/lib/solargraph/language_server/host/dispatch.rb +8 -1
- data/lib/solargraph/language_server/host/sources.rb +1 -61
- data/lib/solargraph/language_server/host.rb +39 -68
- data/lib/solargraph/language_server/message/base.rb +1 -1
- data/lib/solargraph/language_server/message/initialize.rb +14 -0
- data/lib/solargraph/language_server/message/text_document/formatting.rb +1 -0
- data/lib/solargraph/language_server/progress.rb +118 -0
- data/lib/solargraph/language_server.rb +1 -0
- data/lib/solargraph/library.rb +149 -97
- data/lib/solargraph/page.rb +6 -0
- data/lib/solargraph/parser/node_processor/base.rb +3 -2
- data/lib/solargraph/parser/node_processor.rb +1 -0
- data/lib/solargraph/parser/parser_gem/class_methods.rb +3 -7
- data/lib/solargraph/parser/parser_gem/node_methods.rb +0 -4
- data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +6 -19
- data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +47 -0
- data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +5 -3
- data/lib/solargraph/parser/parser_gem/node_processors.rb +2 -0
- data/lib/solargraph/pin/base_variable.rb +35 -6
- data/lib/solargraph/pin/block.rb +70 -32
- data/lib/solargraph/pin/delegated_method.rb +5 -1
- data/lib/solargraph/pin/documenting.rb +2 -0
- data/lib/solargraph/pin/method.rb +4 -2
- data/lib/solargraph/pin/parameter.rb +5 -28
- data/lib/solargraph/rbs_map/conversions.rb +13 -44
- data/lib/solargraph/rbs_map/core_fills.rb +12 -6
- data/lib/solargraph/rbs_map/core_map.rb +2 -13
- data/lib/solargraph/rbs_map.rb +17 -7
- data/lib/solargraph/shell.rb +18 -13
- data/lib/solargraph/source/chain.rb +20 -0
- data/lib/solargraph/source/updater.rb +1 -0
- data/lib/solargraph/source.rb +0 -44
- data/lib/solargraph/source_map/clip.rb +3 -2
- data/lib/solargraph/source_map/mapper.rb +3 -2
- data/lib/solargraph/source_map.rb +8 -6
- data/lib/solargraph/type_checker.rb +57 -13
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/workspace/config.rb +2 -1
- data/lib/solargraph/workspace.rb +13 -0
- data/lib/solargraph/yard_map/mapper/to_method.rb +5 -2
- metadata +4 -4
- data/lib/solargraph/language_server/host/cataloger.rb +0 -57
- data/lib/solargraph/rbs_map/core_signs.rb +0 -35
data/lib/solargraph/library.rb
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'pathname'
|
|
4
|
+
require 'observer'
|
|
4
5
|
|
|
5
6
|
module Solargraph
|
|
6
7
|
# A Library handles coordination between a Workspace and an ApiMap.
|
|
7
8
|
#
|
|
8
9
|
class Library
|
|
9
10
|
include Logging
|
|
11
|
+
include Observable
|
|
10
12
|
|
|
11
13
|
# @return [Solargraph::Workspace]
|
|
12
14
|
attr_reader :workspace
|
|
@@ -17,12 +19,19 @@ module Solargraph
|
|
|
17
19
|
# @return [Source, nil]
|
|
18
20
|
attr_reader :current
|
|
19
21
|
|
|
22
|
+
# @return [LanguageServer::Progress, nil]
|
|
23
|
+
attr_reader :cache_progress
|
|
24
|
+
|
|
20
25
|
# @param workspace [Solargraph::Workspace]
|
|
21
26
|
# @param name [String, nil]
|
|
22
27
|
def initialize workspace = Solargraph::Workspace.new, name = nil
|
|
23
28
|
@workspace = workspace
|
|
24
29
|
@name = name
|
|
25
|
-
@
|
|
30
|
+
@threads = []
|
|
31
|
+
# @type [Integer, nil]
|
|
32
|
+
@total = nil
|
|
33
|
+
# @type [Source, nil]
|
|
34
|
+
@current = nil
|
|
26
35
|
end
|
|
27
36
|
|
|
28
37
|
def inspect
|
|
@@ -35,7 +44,7 @@ module Solargraph
|
|
|
35
44
|
#
|
|
36
45
|
# @return [Boolean]
|
|
37
46
|
def synchronized?
|
|
38
|
-
|
|
47
|
+
!mutex.locked?
|
|
39
48
|
end
|
|
40
49
|
|
|
41
50
|
# Attach a source to the library.
|
|
@@ -47,17 +56,15 @@ module Solargraph
|
|
|
47
56
|
# @param source [Source, nil]
|
|
48
57
|
# @return [void]
|
|
49
58
|
def attach source
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
@external_requires = nil
|
|
55
|
-
@synchronized = false
|
|
56
|
-
end
|
|
57
|
-
@current = source
|
|
58
|
-
maybe_map @current
|
|
59
|
-
catalog_inlock
|
|
59
|
+
if @current && (!source || @current.filename != source.filename) && source_map_hash.key?(@current.filename) && !workspace.has_file?(@current.filename)
|
|
60
|
+
source_map_hash.delete @current.filename
|
|
61
|
+
source_map_external_require_hash.delete @current.filename
|
|
62
|
+
@external_requires = nil
|
|
60
63
|
end
|
|
64
|
+
changed = source && @current != source
|
|
65
|
+
@current = source
|
|
66
|
+
maybe_map @current
|
|
67
|
+
catalog if changed
|
|
61
68
|
end
|
|
62
69
|
|
|
63
70
|
# True if the specified file is currently attached.
|
|
@@ -95,15 +102,10 @@ module Solargraph
|
|
|
95
102
|
# @param text [String] The contents of the file
|
|
96
103
|
# @return [Boolean] True if the file was added to the workspace.
|
|
97
104
|
def create filename, text
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
source = Solargraph::Source.load_string(text, filename)
|
|
103
|
-
workspace.merge(source)
|
|
104
|
-
result = true
|
|
105
|
-
end
|
|
106
|
-
result
|
|
105
|
+
return false unless contain?(filename) || open?(filename)
|
|
106
|
+
source = Solargraph::Source.load_string(text, filename)
|
|
107
|
+
workspace.merge(source)
|
|
108
|
+
true
|
|
107
109
|
end
|
|
108
110
|
|
|
109
111
|
# Create file sources from files on disk. A file is ignored if it is
|
|
@@ -112,14 +114,11 @@ module Solargraph
|
|
|
112
114
|
# @param filenames [Array<String>]
|
|
113
115
|
# @return [Boolean] True if at least one file was added to the workspace.
|
|
114
116
|
def create_from_disk *filenames
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
result = workspace.merge(*sources)
|
|
121
|
-
sources.each { |source| maybe_map source }
|
|
122
|
-
end
|
|
117
|
+
sources = filenames
|
|
118
|
+
.reject { |filename| File.directory?(filename) || !File.exist?(filename) }
|
|
119
|
+
.map { |filename| Solargraph::Source.load_string(File.read(filename), filename) }
|
|
120
|
+
result = workspace.merge(*sources)
|
|
121
|
+
sources.each { |source| maybe_map source }
|
|
123
122
|
result
|
|
124
123
|
end
|
|
125
124
|
|
|
@@ -133,10 +132,7 @@ module Solargraph
|
|
|
133
132
|
result = false
|
|
134
133
|
filenames.each do |filename|
|
|
135
134
|
detach filename
|
|
136
|
-
|
|
137
|
-
result ||= workspace.remove(filename)
|
|
138
|
-
@synchronized = !result if synchronized?
|
|
139
|
-
end
|
|
135
|
+
result ||= workspace.remove(filename)
|
|
140
136
|
end
|
|
141
137
|
result
|
|
142
138
|
end
|
|
@@ -147,11 +143,10 @@ module Solargraph
|
|
|
147
143
|
# @param filename [String]
|
|
148
144
|
# @return [void]
|
|
149
145
|
def close filename
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
end
|
|
146
|
+
return unless @current&.filename == filename
|
|
147
|
+
|
|
148
|
+
@current = nil
|
|
149
|
+
catalog unless workspace.has_file?(filename)
|
|
155
150
|
end
|
|
156
151
|
|
|
157
152
|
# Get completion suggestions at the specified file and location.
|
|
@@ -162,9 +157,10 @@ module Solargraph
|
|
|
162
157
|
# @return [SourceMap::Completion, nil]
|
|
163
158
|
# @todo Take a Location instead of filename/line/column
|
|
164
159
|
def completions_at filename, line, column
|
|
160
|
+
sync_catalog
|
|
165
161
|
position = Position.new(line, column)
|
|
166
162
|
cursor = Source::Cursor.new(read(filename), position)
|
|
167
|
-
api_map.clip(cursor).complete
|
|
163
|
+
mutex.synchronize { api_map.clip(cursor).complete }
|
|
168
164
|
rescue FileNotFoundError => e
|
|
169
165
|
handle_file_not_found filename, e
|
|
170
166
|
end
|
|
@@ -180,6 +176,7 @@ module Solargraph
|
|
|
180
176
|
def definitions_at filename, line, column
|
|
181
177
|
position = Position.new(line, column)
|
|
182
178
|
cursor = Source::Cursor.new(read(filename), position)
|
|
179
|
+
sync_catalog
|
|
183
180
|
if cursor.comment?
|
|
184
181
|
source = read(filename)
|
|
185
182
|
offset = Solargraph::Position.to_offset(source.code, Solargraph::Position.new(line, column))
|
|
@@ -187,13 +184,13 @@ module Solargraph
|
|
|
187
184
|
rgt = source.code[offset..-1].match(/^([a-z0-9_]*)(:[a-z0-9_:]*)?[\]>, ]/i)
|
|
188
185
|
if lft && rgt
|
|
189
186
|
tag = (lft[1] + rgt[1]).sub(/:+$/, '')
|
|
190
|
-
clip = api_map.clip(cursor)
|
|
187
|
+
clip = mutex.synchronize { api_map.clip(cursor) }
|
|
191
188
|
clip.translate tag
|
|
192
189
|
else
|
|
193
190
|
[]
|
|
194
191
|
end
|
|
195
192
|
else
|
|
196
|
-
api_map.clip(cursor).define.map { |pin| pin.realize(api_map) }
|
|
193
|
+
mutex.synchronize { api_map.clip(cursor).define.map { |pin| pin.realize(api_map) } }
|
|
197
194
|
end
|
|
198
195
|
rescue FileNotFoundError => e
|
|
199
196
|
handle_file_not_found(filename, e)
|
|
@@ -210,7 +207,8 @@ module Solargraph
|
|
|
210
207
|
def type_definitions_at filename, line, column
|
|
211
208
|
position = Position.new(line, column)
|
|
212
209
|
cursor = Source::Cursor.new(read(filename), position)
|
|
213
|
-
|
|
210
|
+
sync_catalog
|
|
211
|
+
mutex.synchronize { api_map.clip(cursor).types }
|
|
214
212
|
rescue FileNotFoundError => e
|
|
215
213
|
handle_file_not_found filename, e
|
|
216
214
|
end
|
|
@@ -226,7 +224,8 @@ module Solargraph
|
|
|
226
224
|
def signatures_at filename, line, column
|
|
227
225
|
position = Position.new(line, column)
|
|
228
226
|
cursor = Source::Cursor.new(read(filename), position)
|
|
229
|
-
|
|
227
|
+
sync_catalog
|
|
228
|
+
mutex.synchronize { api_map.clip(cursor).signify }
|
|
230
229
|
end
|
|
231
230
|
|
|
232
231
|
# @param filename [String]
|
|
@@ -237,8 +236,9 @@ module Solargraph
|
|
|
237
236
|
# @return [Array<Solargraph::Range>]
|
|
238
237
|
# @todo Take a Location instead of filename/line/column
|
|
239
238
|
def references_from filename, line, column, strip: false, only: false
|
|
240
|
-
|
|
241
|
-
|
|
239
|
+
sync_catalog
|
|
240
|
+
cursor = Source::Cursor.new(read(filename), [line, column])
|
|
241
|
+
clip = mutex.synchronize { api_map.clip(cursor) }
|
|
242
242
|
pin = clip.define.first
|
|
243
243
|
return [] unless pin
|
|
244
244
|
result = []
|
|
@@ -251,7 +251,19 @@ module Solargraph
|
|
|
251
251
|
found = source.references(pin.name)
|
|
252
252
|
found.select! do |loc|
|
|
253
253
|
referenced = definitions_at(loc.filename, loc.range.ending.line, loc.range.ending.character).first
|
|
254
|
-
referenced
|
|
254
|
+
referenced&.path == pin.path
|
|
255
|
+
end
|
|
256
|
+
if pin.path == 'Class#new'
|
|
257
|
+
caller = cursor.chain.base.infer(api_map, clip.send(:block), clip.locals).first
|
|
258
|
+
if caller.defined?
|
|
259
|
+
found.select! do |loc|
|
|
260
|
+
clip = api_map.clip_at(loc.filename, loc.range.start)
|
|
261
|
+
other = clip.send(:cursor).chain.base.infer(api_map, clip.send(:block), clip.locals).first
|
|
262
|
+
caller == other
|
|
263
|
+
end
|
|
264
|
+
else
|
|
265
|
+
found.clear
|
|
266
|
+
end
|
|
255
267
|
end
|
|
256
268
|
# HACK: for language clients that exclude special characters from the start of variable names
|
|
257
269
|
if strip && match = cursor.word.match(/^[^a-z0-9_]+/i)
|
|
@@ -271,7 +283,8 @@ module Solargraph
|
|
|
271
283
|
# @param location [Location]
|
|
272
284
|
# @return [Array<Solargraph::Pin::Base>]
|
|
273
285
|
def locate_pins location
|
|
274
|
-
|
|
286
|
+
sync_catalog
|
|
287
|
+
mutex.synchronize { api_map.locate_pins(location).map { |pin| pin.realize(api_map) } }
|
|
275
288
|
end
|
|
276
289
|
|
|
277
290
|
# Match a require reference to a file.
|
|
@@ -304,19 +317,22 @@ module Solargraph
|
|
|
304
317
|
# @param path [String]
|
|
305
318
|
# @return [Enumerable<Solargraph::Pin::Base>]
|
|
306
319
|
def get_path_pins path
|
|
307
|
-
|
|
320
|
+
sync_catalog
|
|
321
|
+
mutex.synchronize { api_map.get_path_suggestions(path) }
|
|
308
322
|
end
|
|
309
323
|
|
|
310
324
|
# @param query [String]
|
|
311
325
|
# @return [Enumerable<YARD::CodeObjects::Base>]
|
|
312
326
|
def document query
|
|
313
|
-
|
|
327
|
+
sync_catalog
|
|
328
|
+
mutex.synchronize { api_map.document query }
|
|
314
329
|
end
|
|
315
330
|
|
|
316
331
|
# @param query [String]
|
|
317
332
|
# @return [Array<String>]
|
|
318
333
|
def search query
|
|
319
|
-
|
|
334
|
+
sync_catalog
|
|
335
|
+
mutex.synchronize { api_map.search query }
|
|
320
336
|
end
|
|
321
337
|
|
|
322
338
|
# Get an array of all symbols in the workspace that match the query.
|
|
@@ -324,7 +340,8 @@ module Solargraph
|
|
|
324
340
|
# @param query [String]
|
|
325
341
|
# @return [Array<Pin::Base>]
|
|
326
342
|
def query_symbols query
|
|
327
|
-
|
|
343
|
+
sync_catalog
|
|
344
|
+
mutex.synchronize { api_map.query_symbols query }
|
|
328
345
|
end
|
|
329
346
|
|
|
330
347
|
# Get an array of document symbols.
|
|
@@ -336,13 +353,15 @@ module Solargraph
|
|
|
336
353
|
# @param filename [String]
|
|
337
354
|
# @return [Array<Solargraph::Pin::Base>]
|
|
338
355
|
def document_symbols filename
|
|
339
|
-
|
|
356
|
+
sync_catalog
|
|
357
|
+
mutex.synchronize { api_map.document_symbols(filename) }
|
|
340
358
|
end
|
|
341
359
|
|
|
342
360
|
# @param path [String]
|
|
343
361
|
# @return [Enumerable<Solargraph::Pin::Base>]
|
|
344
362
|
def path_pins path
|
|
345
|
-
|
|
363
|
+
sync_catalog
|
|
364
|
+
mutex.synchronize { api_map.get_path_suggestions(path) }
|
|
346
365
|
end
|
|
347
366
|
|
|
348
367
|
# @return [Array<SourceMap>]
|
|
@@ -368,10 +387,10 @@ module Solargraph
|
|
|
368
387
|
# everything in the workspace should get diagnosed, or if there should
|
|
369
388
|
# be an option to do so.
|
|
370
389
|
#
|
|
390
|
+
sync_catalog
|
|
371
391
|
return [] unless open?(filename)
|
|
372
392
|
result = []
|
|
373
393
|
source = read(filename)
|
|
374
|
-
catalog
|
|
375
394
|
repargs = {}
|
|
376
395
|
workspace.config.reporters.each do |line|
|
|
377
396
|
if line == 'all!'
|
|
@@ -397,21 +416,20 @@ module Solargraph
|
|
|
397
416
|
#
|
|
398
417
|
# @return [void]
|
|
399
418
|
def catalog
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
419
|
+
@threads.delete_if(&:stop?)
|
|
420
|
+
@threads.push(Thread.new do
|
|
421
|
+
sleep 0.05 if RUBY_PLATFORM =~ /mingw/
|
|
422
|
+
next unless @threads.last == Thread.current
|
|
404
423
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
cache_next_gemspec
|
|
424
|
+
mutex.synchronize do
|
|
425
|
+
logger.info "Cataloging #{workspace.directory.empty? ? 'generic workspace' : workspace.directory}"
|
|
426
|
+
api_map.catalog bench
|
|
427
|
+
logger.info "Catalog complete (#{api_map.source_maps.length} files, #{api_map.pins.length} pins)"
|
|
428
|
+
logger.info "#{api_map.uncached_gemspecs.length} uncached gemspecs"
|
|
429
|
+
cache_next_gemspec
|
|
430
|
+
end
|
|
431
|
+
end)
|
|
432
|
+
@threads.last.run if RUBY_PLATFORM =~ /mingw/
|
|
415
433
|
end
|
|
416
434
|
|
|
417
435
|
# @return [Bench]
|
|
@@ -450,12 +468,8 @@ module Solargraph
|
|
|
450
468
|
# @return [Boolean] True if the source was merged into the workspace.
|
|
451
469
|
def merge source
|
|
452
470
|
Logging.logger.debug "Merging source: #{source.filename}"
|
|
453
|
-
result =
|
|
454
|
-
|
|
455
|
-
result = workspace.merge(source)
|
|
456
|
-
maybe_map source
|
|
457
|
-
end
|
|
458
|
-
# catalog
|
|
471
|
+
result = workspace.merge(source)
|
|
472
|
+
maybe_map source
|
|
459
473
|
result
|
|
460
474
|
end
|
|
461
475
|
|
|
@@ -471,17 +485,14 @@ module Solargraph
|
|
|
471
485
|
# @return [SourceMap, Boolean]
|
|
472
486
|
def next_map
|
|
473
487
|
return false if mapped?
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
else
|
|
483
|
-
false
|
|
484
|
-
end
|
|
488
|
+
src = workspace.sources.find { |s| !source_map_hash.key?(s.filename) }
|
|
489
|
+
if src
|
|
490
|
+
Logging.logger.debug "Mapping #{src.filename}"
|
|
491
|
+
source_map_hash[src.filename] = Solargraph::SourceMap.map(src)
|
|
492
|
+
find_external_requires(source_map_hash[src.filename])
|
|
493
|
+
source_map_hash[src.filename]
|
|
494
|
+
else
|
|
495
|
+
false
|
|
485
496
|
end
|
|
486
497
|
end
|
|
487
498
|
|
|
@@ -563,7 +574,7 @@ module Solargraph
|
|
|
563
574
|
end
|
|
564
575
|
end
|
|
565
576
|
|
|
566
|
-
# @param source [Source]
|
|
577
|
+
# @param source [Source, nil]
|
|
567
578
|
# @return [void]
|
|
568
579
|
def maybe_map source
|
|
569
580
|
return unless source
|
|
@@ -577,7 +588,6 @@ module Solargraph
|
|
|
577
588
|
unless source_map_hash[source.filename].try_merge!(new_map)
|
|
578
589
|
source_map_hash[source.filename] = new_map
|
|
579
590
|
find_external_requires(source_map_hash[source.filename])
|
|
580
|
-
@synchronized = false
|
|
581
591
|
end
|
|
582
592
|
else
|
|
583
593
|
# @todo Smelly instance variable access
|
|
@@ -586,7 +596,6 @@ module Solargraph
|
|
|
586
596
|
else
|
|
587
597
|
source_map_hash[source.filename] = Solargraph::SourceMap.map(source)
|
|
588
598
|
find_external_requires(source_map_hash[source.filename])
|
|
589
|
-
@synchronized = false
|
|
590
599
|
end
|
|
591
600
|
end
|
|
592
601
|
|
|
@@ -597,25 +606,68 @@ module Solargraph
|
|
|
597
606
|
|
|
598
607
|
# @return [void]
|
|
599
608
|
def cache_next_gemspec
|
|
600
|
-
return if @
|
|
601
|
-
spec = api_map.uncached_gemspecs.find { |spec| !cache_errors.include?(spec)}
|
|
602
|
-
return unless spec
|
|
609
|
+
return if @cache_progress
|
|
610
|
+
spec = api_map.uncached_gemspecs.find { |spec| !cache_errors.include?(spec) }
|
|
611
|
+
return end_cache_progress unless spec
|
|
603
612
|
|
|
613
|
+
pending = api_map.uncached_gemspecs.length - cache_errors.length - 1
|
|
604
614
|
logger.info "Caching #{spec.name} #{spec.version}"
|
|
605
615
|
Thread.new do
|
|
606
|
-
|
|
607
|
-
|
|
616
|
+
cache_pid = Process.spawn(workspace.command_path, 'cache', spec.name, spec.version.to_s)
|
|
617
|
+
report_cache_progress spec.name, pending
|
|
618
|
+
Process.wait(cache_pid)
|
|
608
619
|
logger.info "Cached #{spec.name} #{spec.version}"
|
|
609
|
-
|
|
610
|
-
rescue Errno::EINVAL => e
|
|
620
|
+
rescue Errno::EINVAL => _e
|
|
611
621
|
logger.info "Cached #{spec.name} #{spec.version} with EINVAL"
|
|
612
|
-
@synchronized = false
|
|
613
622
|
rescue StandardError => e
|
|
614
623
|
cache_errors.add spec
|
|
615
624
|
Solargraph.logger.warn "Error caching gemspec #{spec.name} #{spec.version}: [#{e.class}] #{e.message}"
|
|
616
625
|
ensure
|
|
617
|
-
|
|
626
|
+
end_cache_progress
|
|
627
|
+
catalog
|
|
618
628
|
end
|
|
619
629
|
end
|
|
630
|
+
|
|
631
|
+
# @param gem_name [String]
|
|
632
|
+
# @param pending [Integer]
|
|
633
|
+
# @return [void]
|
|
634
|
+
def report_cache_progress gem_name, pending
|
|
635
|
+
@total ||= pending
|
|
636
|
+
@total = pending if pending > @total
|
|
637
|
+
finished = @total - pending
|
|
638
|
+
pct = if @total.zero?
|
|
639
|
+
0
|
|
640
|
+
else
|
|
641
|
+
((finished.to_f / @total.to_f) * 100).to_i
|
|
642
|
+
end
|
|
643
|
+
message = "#{gem_name}#{pending > 0 ? " (+#{pending})" : ''}"
|
|
644
|
+
# "
|
|
645
|
+
if @cache_progress
|
|
646
|
+
@cache_progress.report(message, pct)
|
|
647
|
+
else
|
|
648
|
+
@cache_progress = LanguageServer::Progress.new('Caching gem')
|
|
649
|
+
# If we don't send both a begin and a report, the progress notification
|
|
650
|
+
# might get stuck in the status bar forever
|
|
651
|
+
@cache_progress.begin(message, pct)
|
|
652
|
+
changed
|
|
653
|
+
notify_observers @cache_progress
|
|
654
|
+
@cache_progress.report(message, pct)
|
|
655
|
+
end
|
|
656
|
+
changed
|
|
657
|
+
notify_observers @cache_progress
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
# @return [void]
|
|
661
|
+
def end_cache_progress
|
|
662
|
+
changed if @cache_progress&.finish('done')
|
|
663
|
+
notify_observers @cache_progress
|
|
664
|
+
@cache_progress = nil
|
|
665
|
+
@total = nil
|
|
666
|
+
end
|
|
667
|
+
|
|
668
|
+
def sync_catalog
|
|
669
|
+
@threads.delete_if(&:stop?)
|
|
670
|
+
.last&.join
|
|
671
|
+
end
|
|
620
672
|
end
|
|
621
673
|
end
|
data/lib/solargraph/page.rb
CHANGED
|
@@ -7,6 +7,12 @@ require 'cgi'
|
|
|
7
7
|
|
|
8
8
|
module Solargraph
|
|
9
9
|
class Page
|
|
10
|
+
# @todo This method directive is necessary because OpenStruct.new confuses
|
|
11
|
+
# the typechecker.
|
|
12
|
+
# @!method self.new(locals, render_method)
|
|
13
|
+
# @param locals[Hash]
|
|
14
|
+
# @param render_method [Proc]
|
|
15
|
+
# @return [Binder]
|
|
10
16
|
class Binder < OpenStruct
|
|
11
17
|
# @param locals [Hash]
|
|
12
18
|
# @param render_method [Proc]
|
|
@@ -13,7 +13,7 @@ module Solargraph
|
|
|
13
13
|
# @return [Array<Pin::Base>]
|
|
14
14
|
attr_reader :pins
|
|
15
15
|
|
|
16
|
-
# @return [Array<Pin::
|
|
16
|
+
# @return [Array<Pin::BaseVariable>]
|
|
17
17
|
attr_reader :locals
|
|
18
18
|
|
|
19
19
|
# @param node [Parser::AST::Node]
|
|
@@ -62,7 +62,7 @@ module Solargraph
|
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
# @param position [Solargraph::Position]
|
|
65
|
-
# @return [Pin::
|
|
65
|
+
# @return [Pin::Closure, nil]
|
|
66
66
|
def named_path_pin position
|
|
67
67
|
pins.select{|pin| pin.is_a?(Pin::Closure) && pin.path && !pin.path.empty? && pin.location.range.contain?(position)}.last
|
|
68
68
|
end
|
|
@@ -71,6 +71,7 @@ module Solargraph
|
|
|
71
71
|
# @param position [Solargraph::Position]
|
|
72
72
|
# @return [Pin::Closure, nil]
|
|
73
73
|
def block_pin position
|
|
74
|
+
# @todo determine if this can return a Pin::Block
|
|
74
75
|
pins.select{|pin| pin.is_a?(Pin::Closure) && pin.location.range.contain?(position)}.last
|
|
75
76
|
end
|
|
76
77
|
|
|
@@ -24,6 +24,7 @@ module Solargraph
|
|
|
24
24
|
# @param node [Parser::AST::Node]
|
|
25
25
|
# @param region [Region]
|
|
26
26
|
# @param pins [Array<Pin::Base>]
|
|
27
|
+
# @param locals [Array<Pin::BaseVariable>]
|
|
27
28
|
# @return [Array(Array<Pin::Base>, Array<Pin::Base>)]
|
|
28
29
|
def self.process node, region = Region.new, pins = [], locals = []
|
|
29
30
|
if pins.empty?
|
|
@@ -48,12 +48,6 @@ module Solargraph
|
|
|
48
48
|
NodeProcessor.process(source.node, Region.new(source: source))
|
|
49
49
|
end
|
|
50
50
|
|
|
51
|
-
# @param node [Parser::AST::Node]
|
|
52
|
-
# @return [Array<Parser::AST::Node>]
|
|
53
|
-
def returns_from node
|
|
54
|
-
NodeMethods.returns_from(node)
|
|
55
|
-
end
|
|
56
|
-
|
|
57
51
|
# @param source [Source]
|
|
58
52
|
# @param name [String]
|
|
59
53
|
# @return [Array<Location>]
|
|
@@ -62,10 +56,12 @@ module Solargraph
|
|
|
62
56
|
reg = /#{Regexp.escape name[0..-2]}\s*=/
|
|
63
57
|
# @param code [String]
|
|
64
58
|
# @param offset [Integer]
|
|
59
|
+
# @return [Array(Integer, Integer), Array(nil, nil)]
|
|
65
60
|
extract_offset = ->(code, offset) { reg.match(code, offset).offset(0) }
|
|
66
61
|
else
|
|
67
62
|
# @param code [String]
|
|
68
63
|
# @param offset [Integer]
|
|
64
|
+
# @return [Array(Integer, Integer), Array(nil, nil)]
|
|
69
65
|
extract_offset = ->(code, offset) { [soff = code.index(name, offset), soff + name.length] }
|
|
70
66
|
end
|
|
71
67
|
inner_node_references(name, source.node).map do |n|
|
|
@@ -115,7 +111,7 @@ module Solargraph
|
|
|
115
111
|
NodeMethods.infer_literal_node_type node
|
|
116
112
|
end
|
|
117
113
|
|
|
118
|
-
# @return [
|
|
114
|
+
# @return [Integer]
|
|
119
115
|
def version
|
|
120
116
|
parser.version
|
|
121
117
|
end
|
|
@@ -181,10 +181,6 @@ module Solargraph
|
|
|
181
181
|
elsif [:super, :zsuper].include?(node.type)
|
|
182
182
|
result.push node
|
|
183
183
|
node.children.each { |child| result.concat call_nodes_from(child) }
|
|
184
|
-
elsif node.type == :masgn
|
|
185
|
-
# @todo We're treating a mass assignment as a call node, but the
|
|
186
|
-
# type checker still needs the logic to handle it.
|
|
187
|
-
result.push node
|
|
188
184
|
else
|
|
189
185
|
node.children.each { |child| result.concat call_nodes_from(child) }
|
|
190
186
|
end
|
|
@@ -6,31 +6,18 @@ module Solargraph
|
|
|
6
6
|
module NodeProcessors
|
|
7
7
|
class DefNode < Parser::NodeProcessor::Base
|
|
8
8
|
def process
|
|
9
|
+
name = node.children[0].to_s
|
|
10
|
+
scope = region.scope || (region.closure.is_a?(Pin::Singleton) ? :class : :instance)
|
|
9
11
|
methpin = Solargraph::Pin::Method.new(
|
|
10
12
|
location: get_node_location(node),
|
|
11
13
|
closure: region.closure,
|
|
12
|
-
name:
|
|
14
|
+
name: name,
|
|
13
15
|
comments: comments_for(node),
|
|
14
|
-
scope:
|
|
15
|
-
visibility: region.visibility,
|
|
16
|
+
scope: scope,
|
|
17
|
+
visibility: scope == :instance && name == 'initialize' ? :private : region.visibility,
|
|
16
18
|
node: node
|
|
17
19
|
)
|
|
18
|
-
if
|
|
19
|
-
pins.push Solargraph::Pin::Method.new(
|
|
20
|
-
location: methpin.location,
|
|
21
|
-
closure: methpin.closure,
|
|
22
|
-
name: 'new',
|
|
23
|
-
comments: methpin.comments,
|
|
24
|
-
scope: :class,
|
|
25
|
-
parameters: methpin.parameters
|
|
26
|
-
)
|
|
27
|
-
# @todo Smelly instance variable access.
|
|
28
|
-
pins.last.instance_variable_set(:@return_type, ComplexType::SELF)
|
|
29
|
-
pins.push methpin
|
|
30
|
-
# @todo Smelly instance variable access.
|
|
31
|
-
methpin.instance_variable_set(:@visibility, :private)
|
|
32
|
-
methpin.instance_variable_set(:@return_type, ComplexType::VOID)
|
|
33
|
-
elsif region.visibility == :module_function
|
|
20
|
+
if region.visibility == :module_function
|
|
34
21
|
pins.push Solargraph::Pin::Method.new(
|
|
35
22
|
location: methpin.location,
|
|
36
23
|
closure: methpin.closure,
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solargraph
|
|
4
|
+
module Parser
|
|
5
|
+
module ParserGem
|
|
6
|
+
module NodeProcessors
|
|
7
|
+
class MasgnNode < Parser::NodeProcessor::Base
|
|
8
|
+
include ParserGem::NodeMethods
|
|
9
|
+
|
|
10
|
+
def process
|
|
11
|
+
# Example:
|
|
12
|
+
#
|
|
13
|
+
# s(:masgn,
|
|
14
|
+
# s(:mlhs,
|
|
15
|
+
# s(:send,
|
|
16
|
+
# s(:send, nil, :a), :b=),
|
|
17
|
+
# s(:lvasgn, :b),
|
|
18
|
+
# s(:ivasgn, :@c)),
|
|
19
|
+
# s(:array,
|
|
20
|
+
# s(:int, 1),
|
|
21
|
+
# s(:int, 2),
|
|
22
|
+
# s(:int, 3)))
|
|
23
|
+
masgn = node
|
|
24
|
+
mlhs = masgn.children.fetch(0)
|
|
25
|
+
lhs_arr = mlhs.children
|
|
26
|
+
mass_rhs = node.children.fetch(1)
|
|
27
|
+
|
|
28
|
+
# Get pins created for the mlhs node
|
|
29
|
+
process_children
|
|
30
|
+
|
|
31
|
+
lhs_arr.each_with_index do |lhs, i|
|
|
32
|
+
location = get_node_location(lhs)
|
|
33
|
+
# @todo in line below, nothing in typechecking alerts
|
|
34
|
+
# when a non-existant method is called on 'l'
|
|
35
|
+
pin = locals.find { |l| l.location == location }
|
|
36
|
+
if pin.nil?
|
|
37
|
+
Solargraph.logger.debug "Could not find pin in location #{location}"
|
|
38
|
+
next
|
|
39
|
+
end
|
|
40
|
+
pin.mass_assignment = [mass_rhs, i]
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -80,7 +80,7 @@ module Solargraph
|
|
|
80
80
|
)
|
|
81
81
|
end
|
|
82
82
|
if node.children[1] == :attr_writer || node.children[1] == :attr_accessor
|
|
83
|
-
|
|
83
|
+
method_pin = Solargraph::Pin::Method.new(
|
|
84
84
|
location: loc,
|
|
85
85
|
closure: clos,
|
|
86
86
|
name: "#{a.children[0]}=",
|
|
@@ -89,8 +89,9 @@ module Solargraph
|
|
|
89
89
|
visibility: region.visibility,
|
|
90
90
|
attribute: true
|
|
91
91
|
)
|
|
92
|
-
pins.
|
|
93
|
-
|
|
92
|
+
pins.push method_pin
|
|
93
|
+
method_pin.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last)
|
|
94
|
+
if method_pin.return_type.defined?
|
|
94
95
|
pins.last.docstring.add_tag YARD::Tags::Tag.new(:param, '', pins.last.return_type.to_s.split(', '), 'value')
|
|
95
96
|
end
|
|
96
97
|
end
|
|
@@ -112,6 +113,7 @@ module Solargraph
|
|
|
112
113
|
end
|
|
113
114
|
end
|
|
114
115
|
|
|
116
|
+
# @return [void]
|
|
115
117
|
def process_prepend
|
|
116
118
|
if node.children[2].is_a?(AST::Node) && node.children[2].type == :const
|
|
117
119
|
cp = region.closure
|