solargraph 0.40.3 → 0.42.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/.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
|