solargraph 0.40.3 → 0.42.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +0 -5
- data/CHANGELOG.md +34 -0
- data/README.md +15 -0
- data/SPONSORS.md +1 -0
- data/lib/.rubocop.yml +4 -3
- data/lib/solargraph.rb +8 -7
- data/lib/solargraph/api_map.rb +40 -111
- data/lib/solargraph/api_map/store.rb +5 -0
- data/lib/solargraph/bench.rb +13 -16
- data/lib/solargraph/compat.rb +15 -1
- data/lib/solargraph/diagnostics/rubocop.rb +10 -2
- data/lib/solargraph/diagnostics/rubocop_helpers.rb +18 -0
- data/lib/solargraph/diagnostics/type_check.rb +1 -1
- data/lib/solargraph/language_server/host.rb +108 -7
- data/lib/solargraph/language_server/host/diagnoser.rb +9 -1
- data/lib/solargraph/language_server/host/sources.rb +1 -1
- data/lib/solargraph/language_server/message/completion_item/resolve.rb +1 -0
- data/lib/solargraph/language_server/message/extended/environment.rb +3 -3
- data/lib/solargraph/language_server/message/initialize.rb +37 -35
- data/lib/solargraph/language_server/message/text_document/formatting.rb +28 -7
- data/lib/solargraph/language_server/message/text_document/hover.rb +1 -1
- data/lib/solargraph/library.rb +132 -22
- data/lib/solargraph/parser/rubyvm/node_chainer.rb +0 -1
- data/lib/solargraph/parser/rubyvm/node_processors/args_node.rb +11 -12
- data/lib/solargraph/parser/rubyvm/node_processors/opt_arg_node.rb +1 -6
- data/lib/solargraph/shell.rb +5 -1
- data/lib/solargraph/source.rb +1 -1
- data/lib/solargraph/source/chain/head.rb +0 -16
- data/lib/solargraph/source/source_chainer.rb +1 -0
- data/lib/solargraph/source_map/mapper.rb +0 -5
- data/lib/solargraph/type_checker.rb +2 -2
- data/lib/solargraph/type_checker/checks.rb +4 -4
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/workspace.rb +1 -0
- data/lib/solargraph/workspace/config.rb +4 -3
- data/lib/solargraph/yard_map.rb +41 -39
- data/lib/solargraph/yard_map/core_fills.rb +1 -0
- data/solargraph.gemspec +1 -0
- metadata +16 -2
@@ -17,7 +17,7 @@ module Solargraph
|
|
17
17
|
if !this_link.nil? && this_link != last_link
|
18
18
|
parts.push this_link
|
19
19
|
end
|
20
|
-
parts.push pin.detail
|
20
|
+
parts.push "`#{pin.detail}`" unless pin.is_a?(Pin::Namespace) || pin.detail.nil?
|
21
21
|
parts.push pin.documentation unless pin.documentation.nil? || pin.documentation.empty?
|
22
22
|
unless parts.empty?
|
23
23
|
data = parts.join("\n\n")
|
data/lib/solargraph/library.rb
CHANGED
@@ -20,9 +20,7 @@ module Solargraph
|
|
20
20
|
def initialize workspace = Solargraph::Workspace.new, name = nil
|
21
21
|
@workspace = workspace
|
22
22
|
@name = name
|
23
|
-
|
24
|
-
@synchronized = true
|
25
|
-
@catalog_mutex = Mutex.new
|
23
|
+
@synchronized = false
|
26
24
|
end
|
27
25
|
|
28
26
|
def inspect
|
@@ -48,9 +46,15 @@ module Solargraph
|
|
48
46
|
# @return [void]
|
49
47
|
def attach source
|
50
48
|
mutex.synchronize do
|
51
|
-
@
|
49
|
+
if @current && @current.filename != source.filename && source_map_hash.key?(@current.filename) && !workspace.has_file?(@current.filename)
|
50
|
+
source_map_hash.delete @current.filename
|
51
|
+
source_map_external_require_hash.delete @current.filename
|
52
|
+
@external_requires = nil
|
53
|
+
@synchronized = false
|
54
|
+
end
|
52
55
|
@current = source
|
53
|
-
|
56
|
+
maybe_map @current
|
57
|
+
api_map.catalog bench unless synchronized?
|
54
58
|
end
|
55
59
|
end
|
56
60
|
|
@@ -110,9 +114,9 @@ module Solargraph
|
|
110
114
|
mutex.synchronize do
|
111
115
|
next if File.directory?(filename) || !File.exist?(filename)
|
112
116
|
next unless contain?(filename) || open?(filename) || workspace.would_merge?(filename)
|
113
|
-
@synchronized = false
|
114
117
|
source = Solargraph::Source.load_string(File.read(filename), filename)
|
115
118
|
workspace.merge(source)
|
119
|
+
maybe_map source
|
116
120
|
result = true
|
117
121
|
end
|
118
122
|
result
|
@@ -158,6 +162,8 @@ module Solargraph
|
|
158
162
|
position = Position.new(line, column)
|
159
163
|
cursor = Source::Cursor.new(read(filename), position)
|
160
164
|
api_map.clip(cursor).complete
|
165
|
+
rescue FileNotFoundError => e
|
166
|
+
handle_file_not_found filename, e
|
161
167
|
end
|
162
168
|
|
163
169
|
# Get definition suggestions for the expression at the specified file and
|
@@ -186,6 +192,8 @@ module Solargraph
|
|
186
192
|
else
|
187
193
|
api_map.clip(cursor).define.map { |pin| pin.realize(api_map) }
|
188
194
|
end
|
195
|
+
rescue FileNotFoundError => e
|
196
|
+
handle_file_not_found(filename, e)
|
189
197
|
end
|
190
198
|
|
191
199
|
# Get signature suggestions for the method at the specified file and
|
@@ -246,7 +254,18 @@ module Solargraph
|
|
246
254
|
end
|
247
255
|
|
248
256
|
def locate_ref location
|
249
|
-
|
257
|
+
map = source_map_hash[location.filename]
|
258
|
+
return if map.nil?
|
259
|
+
pin = map.requires.select { |p| p.location.range.contain?(location.range.start) }.first
|
260
|
+
return nil if pin.nil?
|
261
|
+
workspace.require_paths.each do |path|
|
262
|
+
full = Pathname.new(path).join("#{pin.name}.rb").to_s
|
263
|
+
next unless source_map_hash.key?(full)
|
264
|
+
return Location.new(full, Solargraph::Range.from_to(0, 0, 0, 0))
|
265
|
+
end
|
266
|
+
api_map.yard_map.require_reference(pin.name)
|
267
|
+
rescue FileNotFoundError
|
268
|
+
nil
|
250
269
|
end
|
251
270
|
|
252
271
|
# Get an array of pins that match a path.
|
@@ -260,14 +279,12 @@ module Solargraph
|
|
260
279
|
# @param query [String]
|
261
280
|
# @return [Array<YARD::CodeObjects::Base>]
|
262
281
|
def document query
|
263
|
-
catalog
|
264
282
|
api_map.document query
|
265
283
|
end
|
266
284
|
|
267
285
|
# @param query [String]
|
268
286
|
# @return [Array<String>]
|
269
287
|
def search query
|
270
|
-
catalog
|
271
288
|
api_map.search query
|
272
289
|
end
|
273
290
|
|
@@ -276,7 +293,6 @@ module Solargraph
|
|
276
293
|
# @param query [String]
|
277
294
|
# @return [Array<Pin::Base>]
|
278
295
|
def query_symbols query
|
279
|
-
catalog
|
280
296
|
api_map.query_symbols query
|
281
297
|
end
|
282
298
|
|
@@ -295,10 +311,13 @@ module Solargraph
|
|
295
311
|
# @param path [String]
|
296
312
|
# @return [Array<Solargraph::Pin::Base>]
|
297
313
|
def path_pins path
|
298
|
-
catalog
|
299
314
|
api_map.get_path_suggestions(path)
|
300
315
|
end
|
301
316
|
|
317
|
+
def source_maps
|
318
|
+
source_map_hash.values
|
319
|
+
end
|
320
|
+
|
302
321
|
# Get the current text of a file in the library.
|
303
322
|
#
|
304
323
|
# @param filename [String]
|
@@ -318,9 +337,9 @@ module Solargraph
|
|
318
337
|
# be an option to do so.
|
319
338
|
#
|
320
339
|
return [] unless open?(filename)
|
321
|
-
catalog
|
322
340
|
result = []
|
323
341
|
source = read(filename)
|
342
|
+
catalog
|
324
343
|
repargs = {}
|
325
344
|
workspace.config.reporters.each do |line|
|
326
345
|
if line == 'all!'
|
@@ -346,7 +365,7 @@ module Solargraph
|
|
346
365
|
#
|
347
366
|
# @return [void]
|
348
367
|
def catalog
|
349
|
-
|
368
|
+
mutex.synchronize do
|
350
369
|
break if synchronized?
|
351
370
|
logger.info "Cataloging #{workspace.directory.empty? ? 'generic workspace' : workspace.directory}"
|
352
371
|
api_map.catalog bench
|
@@ -355,6 +374,14 @@ module Solargraph
|
|
355
374
|
end
|
356
375
|
end
|
357
376
|
|
377
|
+
def bench
|
378
|
+
Bench.new(
|
379
|
+
source_maps: source_map_hash.values,
|
380
|
+
workspace: workspace,
|
381
|
+
external_requires: external_requires
|
382
|
+
)
|
383
|
+
end
|
384
|
+
|
358
385
|
# Get an array of foldable ranges for the specified file.
|
359
386
|
#
|
360
387
|
# @deprecated The library should not need to handle folding ranges. The
|
@@ -381,16 +408,75 @@ module Solargraph
|
|
381
408
|
# @param source [Source]
|
382
409
|
# @return [Boolean] True if the source was merged into the workspace.
|
383
410
|
def merge source
|
411
|
+
Logging.logger.debug "Merging source: #{source.filename}"
|
384
412
|
result = false
|
385
413
|
mutex.synchronize do
|
386
414
|
result = workspace.merge(source)
|
387
|
-
|
415
|
+
maybe_map source
|
388
416
|
end
|
417
|
+
# catalog
|
389
418
|
result
|
390
419
|
end
|
391
420
|
|
421
|
+
def source_map_hash
|
422
|
+
@source_map_hash ||= {}
|
423
|
+
end
|
424
|
+
|
425
|
+
def mapped?
|
426
|
+
(workspace.filenames - source_map_hash.keys).empty?
|
427
|
+
end
|
428
|
+
|
429
|
+
def next_map
|
430
|
+
return false if mapped?
|
431
|
+
mutex.synchronize do
|
432
|
+
@synchronized = false
|
433
|
+
src = workspace.sources.find { |s| !source_map_hash.key?(s.filename) }
|
434
|
+
if src
|
435
|
+
Logging.logger.debug "Mapping #{src.filename}"
|
436
|
+
source_map_hash[src.filename] = Solargraph::SourceMap.map(src)
|
437
|
+
find_external_requires(source_map_hash[src.filename])
|
438
|
+
source_map_hash[src.filename]
|
439
|
+
else
|
440
|
+
false
|
441
|
+
end
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
def map!
|
446
|
+
workspace.sources.each do |src|
|
447
|
+
source_map_hash[src.filename] = Solargraph::SourceMap.map(src)
|
448
|
+
find_external_requires(source_map_hash[src.filename])
|
449
|
+
end
|
450
|
+
self
|
451
|
+
end
|
452
|
+
|
453
|
+
def pins
|
454
|
+
@pins ||= []
|
455
|
+
end
|
456
|
+
|
457
|
+
def external_requires
|
458
|
+
@external_requires ||= source_map_external_require_hash.values.flatten.to_set
|
459
|
+
end
|
460
|
+
|
392
461
|
private
|
393
462
|
|
463
|
+
def source_map_external_require_hash
|
464
|
+
@source_map_external_require_hash ||= {}
|
465
|
+
end
|
466
|
+
|
467
|
+
# @param source_map [SourceMap]
|
468
|
+
def find_external_requires source_map
|
469
|
+
new_set = source_map.requires.map(&:name).to_set
|
470
|
+
# return if new_set == source_map_external_require_hash[source_map.filename]
|
471
|
+
source_map_external_require_hash[source_map.filename] = new_set.reject do |path|
|
472
|
+
workspace.require_paths.any? do |base|
|
473
|
+
full = Pathname.new(base).join("#{path}.rb").to_s
|
474
|
+
workspace.filenames.include?(full)
|
475
|
+
end
|
476
|
+
end
|
477
|
+
@external_requires = nil
|
478
|
+
end
|
479
|
+
|
394
480
|
# @return [Mutex]
|
395
481
|
def mutex
|
396
482
|
@mutex ||= Mutex.new
|
@@ -401,14 +487,6 @@ module Solargraph
|
|
401
487
|
@api_map ||= Solargraph::ApiMap.new
|
402
488
|
end
|
403
489
|
|
404
|
-
# @return [Bench]
|
405
|
-
def bench
|
406
|
-
Bench.new(
|
407
|
-
workspace: workspace,
|
408
|
-
opened: @current ? [@current] : []
|
409
|
-
)
|
410
|
-
end
|
411
|
-
|
412
490
|
# Get the source for an open file or create a new source if the file
|
413
491
|
# exists on disk. Sources created from disk are not added to the open
|
414
492
|
# workspace files, i.e., the version on disk remains the authoritative
|
@@ -422,5 +500,37 @@ module Solargraph
|
|
422
500
|
raise FileNotFoundError, "File not found: #{filename}" unless workspace.has_file?(filename)
|
423
501
|
workspace.source(filename)
|
424
502
|
end
|
503
|
+
|
504
|
+
def handle_file_not_found filename, error
|
505
|
+
if workspace.source(filename)
|
506
|
+
Solargraph.logger.debug "#{filename} is not cataloged in the ApiMap"
|
507
|
+
nil
|
508
|
+
else
|
509
|
+
raise error
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
def maybe_map source
|
514
|
+
if source_map_hash.key?(source.filename)
|
515
|
+
return if source_map_hash[source.filename].code == source.code &&
|
516
|
+
source_map_hash[source.filename].source.synchronized? &&
|
517
|
+
source.synchronized?
|
518
|
+
if source.synchronized?
|
519
|
+
new_map = Solargraph::SourceMap.map(source)
|
520
|
+
unless source_map_hash[source.filename].try_merge!(new_map)
|
521
|
+
source_map_hash[source.filename] = new_map
|
522
|
+
find_external_requires(source_map_hash[source.filename])
|
523
|
+
@synchronized = false
|
524
|
+
end
|
525
|
+
else
|
526
|
+
# @todo Smelly instance variable access
|
527
|
+
source_map_hash[source.filename].instance_variable_set(:@source, source)
|
528
|
+
end
|
529
|
+
else
|
530
|
+
source_map_hash[source.filename] = Solargraph::SourceMap.map(source)
|
531
|
+
find_external_requires(source_map_hash[source.filename])
|
532
|
+
@synchronized = false
|
533
|
+
end
|
534
|
+
end
|
425
535
|
end
|
426
536
|
end
|
@@ -109,7 +109,6 @@ module Solargraph
|
|
109
109
|
end
|
110
110
|
|
111
111
|
def node_to_argchains node
|
112
|
-
# @todo Process array, splat, argscat
|
113
112
|
return [] unless Parser.is_ast_node?(node)
|
114
113
|
if [:ZARRAY, :ARRAY, :LIST].include?(node.type)
|
115
114
|
node.children[0..-2].map { |c| NodeChainer.chain(c) }
|
@@ -32,7 +32,6 @@ module Solargraph
|
|
32
32
|
region.closure.parameters.push locals.last
|
33
33
|
end
|
34
34
|
end
|
35
|
-
# @todo Optional args, keyword args, etc.
|
36
35
|
if node.children[6]
|
37
36
|
locals.push Solargraph::Pin::Parameter.new(
|
38
37
|
location: region.closure.location,
|
@@ -55,19 +54,19 @@ module Solargraph
|
|
55
54
|
)
|
56
55
|
region.closure.parameters.push locals.last
|
57
56
|
end
|
58
|
-
if node.children.last
|
59
|
-
locals.push Solargraph::Pin::Parameter.new(
|
60
|
-
location: region.closure.location,
|
61
|
-
closure: region.closure,
|
62
|
-
comments: comments_for(node),
|
63
|
-
name: node.children.last.to_s,
|
64
|
-
presence: region.closure.location.range,
|
65
|
-
decl: :blockarg
|
66
|
-
)
|
67
|
-
region.closure.parameters.push locals.last
|
68
|
-
end
|
69
57
|
end
|
70
58
|
process_children
|
59
|
+
if node.children.last
|
60
|
+
locals.push Solargraph::Pin::Parameter.new(
|
61
|
+
location: region.closure.location,
|
62
|
+
closure: region.closure,
|
63
|
+
comments: comments_for(node),
|
64
|
+
name: node.children.last.to_s,
|
65
|
+
presence: region.closure.location.range,
|
66
|
+
decl: :blockarg
|
67
|
+
)
|
68
|
+
region.closure.parameters.push locals.last
|
69
|
+
end
|
71
70
|
end
|
72
71
|
|
73
72
|
private
|
@@ -16,12 +16,7 @@ module Solargraph
|
|
16
16
|
presence: region.closure.location.range,
|
17
17
|
decl: :optarg
|
18
18
|
)
|
19
|
-
|
20
|
-
if idx
|
21
|
-
region.closure.parameters.insert idx, locals.last
|
22
|
-
else
|
23
|
-
region.closure.parameters.push locals.last
|
24
|
-
end
|
19
|
+
region.closure.parameters.push locals.last
|
25
20
|
node.children[1] && NodeProcessor.process(node.children[1], region, pins, locals)
|
26
21
|
end
|
27
22
|
end
|
data/lib/solargraph/shell.rb
CHANGED
@@ -74,7 +74,11 @@ module Solargraph
|
|
74
74
|
desc 'download-core [VERSION]', 'Download core documentation'
|
75
75
|
def download_core version = nil
|
76
76
|
ver = version || Solargraph::YardMap::CoreDocs.best_download
|
77
|
-
|
77
|
+
if RUBY_VERSION != ver
|
78
|
+
puts "Documentation for #{RUBY_VERSION} is not available. Reverting to closest match..."
|
79
|
+
else
|
80
|
+
puts "Downloading docs for #{ver}..."
|
81
|
+
end
|
78
82
|
Solargraph::YardMap::CoreDocs.download ver
|
79
83
|
# Clear cached documentation if it exists
|
80
84
|
FileUtils.rm_rf Dir.glob(File.join(Solargraph::YardMap::CoreDocs.cache_dir, ver, '*.ser'))
|
data/lib/solargraph/source.rb
CHANGED
@@ -13,22 +13,6 @@ module Solargraph
|
|
13
13
|
# return super_pins(api_map, name_pin) if word == 'super'
|
14
14
|
[]
|
15
15
|
end
|
16
|
-
|
17
|
-
# @todo This is temporary. Chain heads need to handle arguments to
|
18
|
-
# `super`.
|
19
|
-
# def arguments
|
20
|
-
# []
|
21
|
-
# end
|
22
|
-
|
23
|
-
private
|
24
|
-
|
25
|
-
# # @param api_map [ApiMap]
|
26
|
-
# # @param name_pin [Pin::Base]
|
27
|
-
# # @return [Array<Pin::Base>]
|
28
|
-
# def super_pins api_map, name_pin
|
29
|
-
# pins = api_map.get_method_stack(name_pin.namespace, name_pin.name, scope: name_pin.scope)
|
30
|
-
# pins.reject{|p| p.path == name_pin.path}
|
31
|
-
# end
|
32
16
|
end
|
33
17
|
end
|
34
18
|
end
|
@@ -34,6 +34,7 @@ module Solargraph
|
|
34
34
|
# Special handling for files that end with an integer and a period
|
35
35
|
return Chain.new([Chain::Literal.new('Integer'), Chain::UNDEFINED_CALL]) if phrase =~ /^[0-9]+\.$/
|
36
36
|
return Chain.new([Chain::Literal.new('Symbol')]) if phrase.start_with?(':') && !phrase.start_with?('::')
|
37
|
+
return SourceChainer.chain(source, Position.new(position.line, position.character + 1)) if end_of_phrase.strip == '::' && source.code[Position.to_offset(source.code, position)].to_s.match?(/[a-z]/i)
|
37
38
|
begin
|
38
39
|
return Chain.new([]) if phrase.end_with?('..')
|
39
40
|
node = nil
|
@@ -71,11 +71,6 @@ module Solargraph
|
|
71
71
|
pos = Solargraph::Position.new(comment_position.line + line_num - 1, comment_position.column)
|
72
72
|
process_directive(source_position, pos, d)
|
73
73
|
last_line = line_num + 1
|
74
|
-
# @todo The below call assumes the topmost comment line. The above
|
75
|
-
# process occasionally emits incorrect comment positions due to
|
76
|
-
# blank lines in comment blocks, but at least it processes all the
|
77
|
-
# directives.
|
78
|
-
# process_directive(source_position, comment_position, d)
|
79
74
|
end
|
80
75
|
end
|
81
76
|
|
@@ -95,7 +95,7 @@ module Solargraph
|
|
95
95
|
result.push Problem.new(pin.location, "Untyped method #{pin.path} could not be inferred")
|
96
96
|
end
|
97
97
|
elsif rules.validate_tags?
|
98
|
-
unless pin.node.nil? || declared.void? ||
|
98
|
+
unless pin.node.nil? || declared.void? || virtual_pin?(pin) || abstract?(pin)
|
99
99
|
inferred = pin.probe(api_map).self_to(pin.full_context.namespace)
|
100
100
|
if inferred.undefined?
|
101
101
|
unless rules.ignore_all_undefined? || external?(pin)
|
@@ -111,7 +111,7 @@ module Solargraph
|
|
111
111
|
result
|
112
112
|
end
|
113
113
|
|
114
|
-
def
|
114
|
+
def virtual_pin? pin
|
115
115
|
pin.location && source_map.source.comment_at?(pin.location.range.ending)
|
116
116
|
end
|
117
117
|
|
@@ -75,7 +75,7 @@ module Solargraph
|
|
75
75
|
true
|
76
76
|
end
|
77
77
|
|
78
|
-
# @param type [ComplexType]
|
78
|
+
# @param type [ComplexType::UniqueType]
|
79
79
|
# @return [String]
|
80
80
|
def fuzz type
|
81
81
|
if type.parameters?
|
@@ -86,13 +86,13 @@ module Solargraph
|
|
86
86
|
end
|
87
87
|
|
88
88
|
# @param api_map [ApiMap]
|
89
|
-
# @param cls1 [ComplexType]
|
90
|
-
# @param cls2 [ComplexType]
|
89
|
+
# @param cls1 [ComplexType::UniqueType]
|
90
|
+
# @param cls2 [ComplexType::UniqueType]
|
91
91
|
# @return [Boolean]
|
92
92
|
def either_way?(api_map, cls1, cls2)
|
93
93
|
f1 = fuzz(cls1)
|
94
94
|
f2 = fuzz(cls2)
|
95
|
-
api_map.super_and_sub?(f1, f2) || api_map.super_and_sub?(f2, f1)
|
95
|
+
api_map.type_include?(f1, f2) || api_map.super_and_sub?(f1, f2) || api_map.super_and_sub?(f2, f1)
|
96
96
|
end
|
97
97
|
end
|
98
98
|
end
|