solargraph 0.41.0 → 0.42.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eed08215481576c83b9ff5014648c0fa5fe7e458eb59eabcf86632496e4a9f32
4
- data.tar.gz: 8f036f65304ecf1401504a84a49bc34ed6d9be97308be8a1e080be884af100fc
3
+ metadata.gz: d4cd4acd35a7ce8242976cdd2b23d583bbd01392e3d79a16e3b4e181443e4858
4
+ data.tar.gz: 4cb4f369d97681a43d24a8fc3de14a454721c4334426655e23fffafceeb242c7
5
5
  SHA512:
6
- metadata.gz: '024920c42f1f1e37f604b616481bca07b91ec52ed9fd315e6e12a6cc0ab7c0a88ef9ecd827866ab52d66d2f43b679a97c68d3877b88adc5092fc9c2dc2dca5a1'
7
- data.tar.gz: 02c6de30fdac85feedbee8e8b3cbd35ea846a9cbb3a5cb8c476053208a63ae5781a45382fcca43e760875f3f5f911675bdec3f8ba318b122ef6ea0c42f735a29
6
+ metadata.gz: 5918eb0b9797ed3bed1fe97a702761889b4fb5b8280de655820d8a4319eefd651f4958e420b32468aac2ab197f657b8cb1386029654dcbd9b3a30bbdd2ca521b
7
+ data.tar.gz: 649a461beb8b23b69ae40bb347389602e20b42d0a689752d805beb2c08000a863c24d9e45b39d00ee544d37802be0762d9933a6d4e27b2617249d1443ca582f4
data/.travis.yml CHANGED
@@ -8,15 +8,10 @@ rvm:
8
8
  - jruby-head
9
9
  matrix:
10
10
  include:
11
- - rvm: 2.4
12
- os: osx
13
- - rvm: 2.6
14
- os: osx
15
11
  - rvm: 2.7
16
12
  os: osx
17
13
  allow_failures:
18
14
  - rvm: jruby-head
19
- - rvm: 3.0
20
15
  before_install:
21
16
  - gem update --system
22
17
  - gem install bundler
data/CHANGELOG.md CHANGED
@@ -1,3 +1,27 @@
1
+ ## 0.42.2 - June 14, 2021
2
+ - Improve download-core command output
3
+ - Ignore missing requests to client responses
4
+ - Add automatically required gems to YardMap
5
+ - Use closures to identify local variables
6
+
7
+ ## 0.42.1 - June 11, 2021
8
+ - YardMap#change sets new directory (#445)
9
+
10
+ ## 0.42.0 - June 11, 2021
11
+ - Improve YardMap efficiency
12
+ - Bench includes Workspace for cataloging
13
+ - Initialize confirms static features from options (#436)
14
+ - Enable simple repairs without incremental sync (#416)
15
+ - Discard unrecognized client responses
16
+ - Notify on use of closest match for core (#390)
17
+
18
+ ## 0.41.2 - June 9, 2021
19
+ - Rescue InvalidOffset in async diagnosis
20
+ - Remove erroneous escaping from Hover
21
+
22
+ ## 0.41.1 - May 31, 2021
23
+ - ApiMap handles required bundles (#443)
24
+
1
25
  ## 0.41.0 - May 30, 2021
2
26
  - Chain constant at last double colon with more than two nested namespaces
3
27
  - Fill Integer#times return type (#440)
data/lib/.rubocop.yml CHANGED
@@ -1,5 +1,5 @@
1
- Layout/EndOfLine:
2
- Enabled: false
1
+ AllCops:
2
+ NewCops: enable
3
3
  Style/MethodDefParentheses:
4
4
  Enabled: false
5
5
  Layout/EmptyLineAfterGuardClause:
@@ -10,6 +10,8 @@ Lint/RaiseException:
10
10
  Enabled: true
11
11
  Lint/StructNewOverride:
12
12
  Enabled: true
13
+ Metrics/MethodLength:
14
+ Max: 25
13
15
  Style/ExponentialNotation:
14
16
  Enabled: true
15
17
  Style/HashEachMethods:
@@ -18,4 +20,3 @@ Style/HashTransformKeys:
18
20
  Enabled: true
19
21
  Style/HashTransformValues:
20
22
  Enabled: true
21
-
@@ -17,7 +17,6 @@ module Solargraph
17
17
  autoload :BundlerMethods, 'solargraph/api_map/bundler_methods'
18
18
 
19
19
  include SourceToYard
20
- include BundlerMethods
21
20
 
22
21
  # @return [Array<String>]
23
22
  attr_reader :unresolved_requires
@@ -59,30 +58,14 @@ module Solargraph
59
58
  implicit.clear
60
59
  @cache.clear
61
60
  @source_map_hash = bench.source_maps.map { |s| [s.filename, s] }.to_h
62
- pins = []
63
- @required = Set.new
64
- local_path_hash.clear
61
+ pins = bench.source_maps.map(&:pins).flatten
62
+ external_requires = bench.external_requires
65
63
  source_map_hash.each_value do |map|
66
- pins.concat map.pins
67
- @required.merge map.requires.map(&:name)
68
64
  implicit.merge map.environ
69
65
  end
70
- @required.merge implicit.requires
71
- external_requires = []
72
- @required.each do |req|
73
- result = bench.load_paths.find do |path|
74
- full = Pathname.new(path).join("#{req}.rb").to_s
75
- @source_map_hash.key?(full)
76
- end
77
- if result
78
- local_path_hash[req] = Pathname.new(result).join("#{req}.rb").to_s
79
- else
80
- external_requires.push req unless result
81
- end
82
- end
83
- br = @required.include?('bundler/require') ? bench.gemnames.to_h { |gs| [gs.name, gs] } : {}
84
- @required.merge br.keys
85
- yard_map.change(external_requires, br, bench.gemnames)
66
+ external_requires.merge implicit.requires
67
+ external_requires.merge bench.workspace.config.required
68
+ yard_map.change(external_requires, bench.workspace.directory, bench.workspace.source_gems)
86
69
  @store = Store.new(yard_map.pins + implicit.pins + pins)
87
70
  @unresolved_requires = yard_map.unresolved_requires
88
71
  @rebindable_method_names = nil
@@ -105,11 +88,6 @@ module Solargraph
105
88
  @implicit ||= Environ.new
106
89
  end
107
90
 
108
- # @return [Hash{String => String}]
109
- def local_path_hash
110
- @local_paths ||= {}
111
- end
112
-
113
91
  # @param filename [String]
114
92
  # @param position [Position, Array(Integer, Integer)]
115
93
  # @return [Source::Cursor]
@@ -456,27 +434,6 @@ module Solargraph
456
434
  source_map_hash.keys.include?(filename)
457
435
  end
458
436
 
459
- # True if the specified file is included in the workspace.
460
- #
461
- # @param filename [String]
462
- def workspaced? filename
463
- workspace_filenames.include?(filename)
464
- end
465
-
466
- # @param location [Location]
467
- # @return [Location]
468
- def require_reference_at location
469
- map = source_map(location.filename)
470
- pin = map.requires.select { |p| p.location.range.contain?(location.range.start) }.first
471
- return nil if pin.nil?
472
- if local_path_hash.key?(pin.name)
473
- return Location.new(local_path_hash[pin.name], Solargraph::Range.from_to(0, 0, 0, 0))
474
- end
475
- yard_map.require_reference(pin.name)
476
- rescue FileNotFoundError
477
- nil
478
- end
479
-
480
437
  # Check if a class is a superclass of another class.
481
438
  #
482
439
  # @param sup [String] The superclass
@@ -508,11 +465,6 @@ module Solargraph
508
465
 
509
466
  private
510
467
 
511
- # @return [Array<String>]
512
- def workspace_filenames
513
- @workspace_filenames ||= []
514
- end
515
-
516
468
  # A hash of source maps with filename keys.
517
469
  #
518
470
  # @return [Hash{String => SourceMap}]
@@ -9,7 +9,7 @@ module Solargraph
9
9
  # @param directory [String]
10
10
  # @return [Hash]
11
11
  def require_from_bundle directory
12
- @require_from_bundle ||= begin
12
+ begin
13
13
  Solargraph.logger.info "Loading gems for bundler/require"
14
14
  Documentor.specs_from_bundle(directory)
15
15
  rescue BundleNotFoundError => e
@@ -17,11 +17,6 @@ module Solargraph
17
17
  {}
18
18
  end
19
19
  end
20
-
21
- # @return [void]
22
- def reset_require_from_bundle
23
- @require_from_bundle = nil
24
- end
25
20
  end
26
21
  end
27
22
  end
@@ -3,25 +3,25 @@
3
3
  require 'set'
4
4
 
5
5
  module Solargraph
6
- # A container of source maps and gem specs to be cataloged in an ApiMap.
6
+ # A container of source maps and workspace data to be cataloged in an ApiMap.
7
7
  #
8
8
  class Bench
9
9
  # @return [Set<SourceMap>]
10
10
  attr_reader :source_maps
11
11
 
12
- # @return [Set<String>]
13
- attr_reader :load_paths
12
+ # @return [Workspace]
13
+ attr_reader :workspace
14
14
 
15
15
  # @return [Set<String>]
16
- attr_reader :gemnames
16
+ attr_reader :external_requires
17
17
 
18
18
  # @param source_maps [Array<SourceMap>, Set<SourceMap>]
19
- # @param load_paths [Array<String>, Set<String>]
20
- # @param gemnames [Array<String>, Set<String>]
21
- def initialize source_maps: [], load_paths: [], gemnames: []
19
+ # @param workspace [Workspace]
20
+ # @param external_requires [Array<String>, Set<String>]
21
+ def initialize source_maps: [], workspace: Workspace.new, external_requires: []
22
22
  @source_maps = source_maps.to_set
23
- @load_paths = load_paths.to_set
24
- @gemnames = gemnames.to_set
23
+ @workspace = workspace
24
+ @external_requires = external_requires.to_set
25
25
  end
26
26
  end
27
27
  end
@@ -7,7 +7,7 @@ module Solargraph
7
7
  #
8
8
  class TypeCheck < Base
9
9
  def diagnose source, api_map
10
- return [] unless args.include?('always') || api_map.workspaced?(source.filename)
10
+ # return [] unless args.include?('always') || api_map.workspaced?(source.filename)
11
11
  severity = Diagnostics::Severities::ERROR
12
12
  level = (args.reverse.find { |a| ['normal', 'typed', 'strict', 'strong'].include?(a) }) || :normal
13
13
  checker = Solargraph::TypeChecker.new(source.filename, api_map: api_map, level: level.to_sym)
@@ -1,8 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'diff/lcs'
3
4
  require 'observer'
4
- require 'set'
5
5
  require 'securerandom'
6
+ require 'set'
6
7
 
7
8
  module Solargraph
8
9
  module LanguageServer
@@ -30,7 +31,7 @@ module Solargraph
30
31
  @cancel = []
31
32
  @buffer = String.new
32
33
  @stopped = true
33
- @next_request_id = 0
34
+ @next_request_id = 1
34
35
  @dynamic_capabilities = Set.new
35
36
  @registered_capabilities = Set.new
36
37
  end
@@ -107,9 +108,13 @@ module Solargraph
107
108
  end
108
109
  message
109
110
  elsif request['id']
110
- # @todo What if the id is invalid?
111
- requests[request['id']].process(request['result'])
112
- requests.delete request['id']
111
+ if requests[request['id']]
112
+ requests[request['id']].process(request['result'])
113
+ requests.delete request['id']
114
+ else
115
+ logger.warn "Discarding client response to unrecognized message #{request['id']}"
116
+ nil
117
+ end
113
118
  else
114
119
  logger.warn "Invalid message received."
115
120
  logger.debug request
@@ -165,8 +170,6 @@ module Solargraph
165
170
  # @return [void]
166
171
  def open_from_disk uri
167
172
  sources.open_from_disk(uri)
168
- library = library_for(uri)
169
- # library.open_from_disk uri_to_file(uri)
170
173
  diagnoser.schedule uri
171
174
  end
172
175
 
@@ -672,7 +675,8 @@ module Solargraph
672
675
  # @return [Source::Updater]
673
676
  def generate_updater params
674
677
  changes = []
675
- params['contentChanges'].each do |chng|
678
+ params['contentChanges'].each do |recvd|
679
+ chng = check_diff(params['textDocument']['uri'], recvd)
676
680
  changes.push Solargraph::Source::Change.new(
677
681
  (chng['range'].nil? ?
678
682
  nil :
@@ -688,6 +692,36 @@ module Solargraph
688
692
  )
689
693
  end
690
694
 
695
+ # @param uri [String]
696
+ # @param change [Hash]
697
+ # @return [Hash]
698
+ def check_diff uri, change
699
+ return change if change['range']
700
+ source = sources.find(uri)
701
+ return change if source.code.length + 1 != change['text'].length
702
+ diffs = Diff::LCS.diff(source.code, change['text'])
703
+ return change if diffs.length.zero? || diffs.length > 1 || diffs.first.length > 1
704
+ # @type [Diff::LCS::Change]
705
+ diff = diffs.first.first
706
+ return change unless diff.adding? && ['.', ':'].include?(diff.element)
707
+ position = Solargraph::Position.from_offset(source.code, diff.position)
708
+ {
709
+ 'range' => {
710
+ 'start' => {
711
+ 'line' => position.line,
712
+ 'character' => position.character
713
+ },
714
+ 'end' => {
715
+ 'line' => position.line,
716
+ 'character' => position.character
717
+ }
718
+ },
719
+ 'text' => diff.element
720
+ }
721
+ rescue Solargraph::FileNotFoundError
722
+ change
723
+ end
724
+
691
725
  # @return [Hash]
692
726
  def dynamic_capability_options
693
727
  @dynamic_capability_options ||= {
@@ -787,7 +821,7 @@ module Solargraph
787
821
  next unless uuid
788
822
  cur = ((library.source_map_hash.keys.length.to_f / total.to_f) * 100).to_i
789
823
  if cur > pct && cur % mod == 0
790
- pct = cur
824
+ pct = cur
791
825
  send_notification '$/progress', {
792
826
  token: uuid,
793
827
  value: {
@@ -62,7 +62,15 @@ module Solargraph
62
62
  end
63
63
  current = mutex.synchronize { queue.shift }
64
64
  return if queue.include?(current)
65
- host.diagnose current
65
+ begin
66
+ host.diagnose current
67
+ rescue InvalidOffsetError
68
+ # @todo This error can occur when the Source is out of sync with
69
+ # with the ApiMap. It's probably not the best way to handle it,
70
+ # but it's quick and easy.
71
+ Logging.logger.warn "Deferring diagnosis due to invalid offset: #{current}"
72
+ mutex.synchronize { queue.push current }
73
+ end
66
74
  end
67
75
 
68
76
  private
@@ -76,7 +76,7 @@ module Solargraph
76
76
  source = Solargraph::Source.load(UriHelpers.uri_to_file(uri))
77
77
  open_source_hash[uri] = source
78
78
  end
79
-
79
+
80
80
  # Update an existing source.
81
81
  #
82
82
  # @raise [FileNotFoundError] if the URI does not match an open source.
@@ -51,6 +51,7 @@ module Solargraph
51
51
  end
52
52
 
53
53
  def static_completion
54
+ return {} unless host.options['completion']
54
55
  {
55
56
  completionProvider: {
56
57
  resolveProvider: true,
@@ -84,18 +85,21 @@ module Solargraph
84
85
  end
85
86
 
86
87
  def static_hover
88
+ return {} unless host.options['hover']
87
89
  {
88
90
  hoverProvider: true
89
91
  }
90
92
  end
91
93
 
92
94
  def static_document_formatting
95
+ return {} unless host.options['formatting']
93
96
  {
94
97
  documentFormattingProvider: true
95
98
  }
96
99
  end
97
100
 
98
101
  def static_document_symbols
102
+ return {} unless host.options['symbols']
99
103
  {
100
104
  documentSymbolProvider: true
101
105
  }
@@ -108,6 +112,7 @@ module Solargraph
108
112
  end
109
113
 
110
114
  def static_definitions
115
+ return {} unless host.options['definitions']
111
116
  {
112
117
  definitionProvider: true
113
118
  }
@@ -120,12 +125,14 @@ module Solargraph
120
125
  end
121
126
 
122
127
  def static_references
128
+ return {} unless host.options['references']
123
129
  {
124
130
  referencesProvider: true
125
131
  }
126
132
  end
127
133
 
128
134
  def static_folding_range
135
+ return {} unless host.options['folding']
129
136
  {
130
137
  foldingRangeProvider: true
131
138
  }
@@ -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.gsub(':', '\\:') + "`" unless pin.is_a?(Pin::Namespace) || pin.detail.nil?
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")
@@ -42,9 +42,7 @@ module Solargraph
42
42
  # @return [void]
43
43
  def process request
44
44
  message = @host.receive(request)
45
- message.send_response
46
- # tmp = @host.flush
47
- # write tmp unless tmp.empty?
45
+ message && message.send_response
48
46
  end
49
47
 
50
48
  def shutdown
@@ -46,8 +46,10 @@ module Solargraph
46
46
  # @return [void]
47
47
  def attach source
48
48
  mutex.synchronize do
49
- if @current && @current.filename != source.filename && source_map_hash.key?(@current.filename) && !workspace.has_file?(@current.filename)
49
+ if @current && (!source || @current.filename != source.filename) && source_map_hash.key?(@current.filename) && !workspace.has_file?(@current.filename)
50
50
  source_map_hash.delete @current.filename
51
+ source_map_external_require_hash.delete @current.filename
52
+ @external_requires = nil
51
53
  @synchronized = false
52
54
  end
53
55
  @current = source
@@ -252,7 +254,18 @@ module Solargraph
252
254
  end
253
255
 
254
256
  def locate_ref location
255
- api_map.require_reference_at location
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
256
269
  end
257
270
 
258
271
  # Get an array of pins that match a path.
@@ -326,6 +339,7 @@ module Solargraph
326
339
  return [] unless open?(filename)
327
340
  result = []
328
341
  source = read(filename)
342
+ catalog
329
343
  repargs = {}
330
344
  workspace.config.reporters.each do |line|
331
345
  if line == 'all!'
@@ -361,12 +375,10 @@ module Solargraph
361
375
  end
362
376
 
363
377
  def bench
364
- source_maps = @current ? [@current] : []
365
- source_maps.concat source_map_hash.values
366
378
  Bench.new(
367
- source_maps: source_maps,
368
- load_paths: workspace.require_paths,
369
- gemnames: workspace.gemnames
379
+ source_maps: source_map_hash.values,
380
+ workspace: workspace,
381
+ external_requires: external_requires
370
382
  )
371
383
  end
372
384
 
@@ -422,6 +434,8 @@ module Solargraph
422
434
  if src
423
435
  Logging.logger.debug "Mapping #{src.filename}"
424
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]
425
439
  else
426
440
  false
427
441
  end
@@ -431,6 +445,7 @@ module Solargraph
431
445
  def map!
432
446
  workspace.sources.each do |src|
433
447
  source_map_hash[src.filename] = Solargraph::SourceMap.map(src)
448
+ find_external_requires(source_map_hash[src.filename])
434
449
  end
435
450
  self
436
451
  end
@@ -439,8 +454,29 @@ module Solargraph
439
454
  @pins ||= []
440
455
  end
441
456
 
457
+ def external_requires
458
+ @external_requires ||= source_map_external_require_hash.values.flatten.to_set
459
+ end
460
+
442
461
  private
443
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
+
444
480
  # @return [Mutex]
445
481
  def mutex
446
482
  @mutex ||= Mutex.new
@@ -475,6 +511,7 @@ module Solargraph
475
511
  end
476
512
 
477
513
  def maybe_map source
514
+ return unless source
478
515
  if source_map_hash.key?(source.filename)
479
516
  return if source_map_hash[source.filename].code == source.code &&
480
517
  source_map_hash[source.filename].source.synchronized? &&
@@ -483,6 +520,7 @@ module Solargraph
483
520
  new_map = Solargraph::SourceMap.map(source)
484
521
  unless source_map_hash[source.filename].try_merge!(new_map)
485
522
  source_map_hash[source.filename] = new_map
523
+ find_external_requires(source_map_hash[source.filename])
486
524
  @synchronized = false
487
525
  end
488
526
  else
@@ -491,6 +529,7 @@ module Solargraph
491
529
  end
492
530
  else
493
531
  source_map_hash[source.filename] = Solargraph::SourceMap.map(source)
532
+ find_external_requires(source_map_hash[source.filename])
494
533
  @synchronized = false
495
534
  end
496
535
  end
@@ -3,7 +3,8 @@
3
3
  module Solargraph
4
4
  module Pin
5
5
  class LocalVariable < BaseVariable
6
- include Localized
6
+ # @return [Range]
7
+ attr_reader :presence
7
8
 
8
9
  def initialize assignment: nil, presence: nil, **splat
9
10
  super(**splat)
@@ -16,6 +17,51 @@ module Solargraph
16
17
  @presence = pin.presence
17
18
  true
18
19
  end
20
+
21
+ # @param other [Pin::Base] The caller's block
22
+ # @param position [Position, Array(Integer, Integer)] The caller's position
23
+ # @return [Boolean]
24
+ def visible_from?(other, position)
25
+ position = Position.normalize(position)
26
+ other.filename == filename &&
27
+ match_tags(other.full_context.tag, full_context.tag) &&
28
+ (other == closure ||
29
+ (closure.location.range.contain?(other.location.range.start) && closure.location.range.contain?(other.location.range.ending))
30
+ ) &&
31
+ presence.contain?(position)
32
+ end
33
+
34
+ # @param other_loc [Location]
35
+ def visible_at?(other_closure, other_loc)
36
+ return true if location.filename == other_loc.filename &&
37
+ presence.include?(other_loc.range.start) &&
38
+ match_named_closure(other_closure, closure)
39
+ end
40
+
41
+ private
42
+
43
+ # @param tag1 [String]
44
+ # @param tag2 [String]
45
+ # @return [Boolean]
46
+ def match_tags tag1, tag2
47
+ # @todo This is an unfortunate hack made necessary by a discrepancy in
48
+ # how tags indicate the root namespace. The long-term solution is to
49
+ # standardize it, whether it's `Class<>`, an empty string, or
50
+ # something else.
51
+ tag1 == tag2 ||
52
+ (['', 'Class<>'].include?(tag1) && ['', 'Class<>'].include?(tag2))
53
+ end
54
+
55
+ def match_named_closure needle, haystack
56
+ return true if needle == haystack
57
+ cursor = haystack
58
+ until cursor.nil?
59
+ return true if needle.path == cursor.path
60
+ return false if cursor.path && !cursor.path.empty?
61
+ cursor = cursor.closure
62
+ end
63
+ false
64
+ end
19
65
  end
20
66
  end
21
67
  end
@@ -74,10 +74,14 @@ 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
+ if RUBY_VERSION != ver
78
+ puts "Documentation for #{RUBY_VERSION} is not available. Reverting to closest match..."
79
+ end
77
80
  puts "Downloading docs for #{ver}..."
78
81
  Solargraph::YardMap::CoreDocs.download ver
79
82
  # Clear cached documentation if it exists
80
83
  FileUtils.rm_rf Dir.glob(File.join(Solargraph::YardMap::CoreDocs.cache_dir, ver, '*.ser'))
84
+ puts "Download complete."
81
85
  rescue ArgumentError => e
82
86
  STDERR.puts "ERROR: #{e.message}"
83
87
  STDERR.puts "Run `solargraph available-cores` for a list."
@@ -77,6 +77,7 @@ module Solargraph
77
77
  end
78
78
  if match
79
79
  type = extra_return_type(ol, context)
80
+ break if type
80
81
  type = ComplexType.try_parse(*ol.tag(:return).types).self_to(context.to_s).qualify(api_map, context.namespace) if ol.has_tag?(:return) && ol.tag(:return).types && !ol.tag(:return).types.empty? && (type.nil? || type.undefined?)
81
82
  type ||= ComplexType::UNDEFINED
82
83
  end
@@ -170,8 +171,8 @@ module Solargraph
170
171
  # @param context [ComplexType]
171
172
  # @return [ComplexType]
172
173
  def extra_return_type docstring, context
173
- if docstring.has_tag?(:return_single_parameter) && context.subtypes.one?
174
- return context.subtypes.first
174
+ if docstring.has_tag?(:return_single_parameter) #&& context.subtypes.one?
175
+ return context.subtypes.first || ComplexType::UNDEFINED
175
176
  elsif docstring.has_tag?(:return_value_parameter) && context.value_types.one?
176
177
  return context.value_types.first
177
178
  end
@@ -131,7 +131,8 @@ module Solargraph
131
131
  # @return [Array<Pin::LocalVariable>]
132
132
  def locals_at(location)
133
133
  return [] if location.filename != filename
134
- locals.select { |pin| pin.visible_at?(location) }
134
+ closure = locate_named_path_pin(location.range.start.line, location.range.start.character)
135
+ locals.select { |pin| pin.visible_at?(closure, location) }
135
136
  end
136
137
 
137
138
  class << self
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- VERSION = '0.41.0'
4
+ VERSION = '0.42.2'
5
5
  end
@@ -18,6 +18,7 @@ module Solargraph
18
18
 
19
19
  # @return [Array<String>]
20
20
  attr_reader :gemnames
21
+ alias source_gems gemnames
21
22
 
22
23
  # @param directory [String]
23
24
  # @param config [Config, nil]
@@ -20,6 +20,8 @@ module Solargraph
20
20
  autoload :Helpers, 'solargraph/yard_map/helpers'
21
21
  autoload :ToMethod, 'solargraph/yard_map/to_method'
22
22
 
23
+ include ApiMap::BundlerMethods
24
+
23
25
  CoreDocs.require_minimum
24
26
 
25
27
  def stdlib_paths
@@ -37,33 +39,16 @@ module Solargraph
37
39
  end
38
40
  end
39
41
 
40
- # @return [Array<String>]
41
- attr_reader :required
42
-
43
42
  # @return [Boolean]
44
43
  attr_writer :with_dependencies
45
44
 
46
- # A hash of gem names and the version numbers to include in the map.
47
- #
48
- # @return [Hash{String => String}]
49
- attr_reader :gemset
50
-
51
- # @param required [Array<String>]
52
- # @param gemset [Hash{String => String}]
45
+ # @param required [Array<String>, Set<String>]
46
+ # @param directory [String]
47
+ # @param source_gems [Array<String>, Set<String>]
53
48
  # @param with_dependencies [Boolean]
54
- def initialize(required: [], gemset: {}, with_dependencies: true)
55
- # HACK: YardMap needs its own copy of this array
56
- @required = required.clone
57
- # HACK: Hardcoded YAML handling
58
- @required.push 'psych' if @required.include?('yaml')
49
+ def initialize(required: [], directory: '', source_gems: [], with_dependencies: true)
59
50
  @with_dependencies = with_dependencies
60
- @gem_paths = {}
61
- @stdlib_namespaces = []
62
- @gemset = gemset
63
- @source_gems = []
64
- process_requires
65
- yardocs.uniq!
66
- @pin_select_cache = {}
51
+ change required.to_set, directory, source_gems.to_set
67
52
  end
68
53
 
69
54
  # @return [Array<Solargraph::Pin::Base>]
@@ -76,25 +61,24 @@ module Solargraph
76
61
  @with_dependencies
77
62
  end
78
63
 
79
- # @param new_requires [Array<String>]
80
- # @param new_gemset [Hash{String => String}]
64
+ # @param new_requires [Set<String>] Required paths to use for loading gems
65
+ # @param new_directory [String] The workspace directory
66
+ # @param new_source_gems [Set<String>] Gems under local development (i.e., part of the workspace)
81
67
  # @return [Boolean]
82
- def change new_requires, new_gemset, source_gems = []
68
+ def change new_requires, new_directory, new_source_gems
69
+ return false if new_requires == base_required && new_directory == @directory && new_source_gems == @source_gems
70
+ @gem_paths = {}
71
+ base_required.replace new_requires
72
+ required.replace new_requires
83
73
  # HACK: Hardcoded YAML handling
84
- new_requires.push 'psych' if new_requires.include?('yaml')
85
- if new_requires.uniq.sort == required.uniq.sort && new_gemset == gemset && @source_gems.uniq.sort == source_gems.uniq.sort
86
- false
87
- else
88
- required.clear
89
- required.concat new_requires
90
- @gemset = new_gemset
91
- @source_gems = source_gems
92
- process_requires
93
- @rebindable_method_names = nil
94
- @pin_class_hash = nil
95
- @pin_select_cache = {}
96
- true
97
- end
74
+ required.add 'psych' if new_requires.include?('yaml')
75
+ @source_gems = new_source_gems
76
+ @directory = new_directory
77
+ process_requires
78
+ @rebindable_method_names = nil
79
+ @pin_class_hash = nil
80
+ @pin_select_cache = {}
81
+ true
98
82
  end
99
83
 
100
84
  # @return [Set<String>]
@@ -111,6 +95,11 @@ module Solargraph
111
95
  @yardocs ||= []
112
96
  end
113
97
 
98
+ # @return [Set<String>]
99
+ def required
100
+ @required ||= Set.new
101
+ end
102
+
114
103
  # @return [Array<String>]
115
104
  def unresolved_requires
116
105
  @unresolved_requires ||= []
@@ -163,6 +152,14 @@ module Solargraph
163
152
  @stdlib_pins ||= []
164
153
  end
165
154
 
155
+ def base_required
156
+ @base_required ||= Set.new
157
+ end
158
+
159
+ def directory
160
+ @directory ||= ''
161
+ end
162
+
166
163
  private
167
164
 
168
165
  # @return [YardMap::Cache]
@@ -193,6 +190,8 @@ module Solargraph
193
190
 
194
191
  # @return [void]
195
192
  def process_requires
193
+ @gemset = process_gemsets
194
+ required.merge @gemset.keys if required.include?('bundler/require')
196
195
  pins.replace core_pins
197
196
  unresolved_requires.clear
198
197
  stdlib_pins.clear
@@ -252,6 +251,11 @@ module Solargraph
252
251
  pins.concat environ.pins
253
252
  end
254
253
 
254
+ def process_gemsets
255
+ return {} if directory.empty? || !File.file?(File.join(directory, 'Gemfile'))
256
+ require_from_bundle(directory)
257
+ end
258
+
255
259
  # @param spec [Gem::Specification]
256
260
  # @return [void]
257
261
  def add_gem_dependencies spec
@@ -82,6 +82,18 @@ module Solargraph
82
82
  Override.method_return('Class#allocate', 'self'),
83
83
  Override.method_return('Class.allocate', 'Class<Object>'),
84
84
 
85
+ Override.from_comment('Enumerable#detect', %(
86
+ @overload detect(&block)
87
+ @return_single_parameter
88
+ @overload detect()
89
+ @return [Enumerator]
90
+ )),
91
+ Override.from_comment('Enumerable#find', %(
92
+ @overload find(&block)
93
+ @return_single_parameter
94
+ @overload find()
95
+ @return [Enumerator]
96
+ )),
85
97
  Override.method_return('Enumerable#select', 'self'),
86
98
 
87
99
  Override.method_return('File.absolute_path', 'String'),
@@ -111,7 +123,12 @@ module Solargraph
111
123
  @param y [Numeric]
112
124
  @return [Numeric]
113
125
  )),
114
- Override.method_return('Integer#times', 'Enumerator', delete: [:overload]),
126
+ Override.from_comment('Integer#times', %(
127
+ @overload times(&block)
128
+ @return [Integer]
129
+ @overload times()
130
+ @return [Enumerator]
131
+ )),
115
132
 
116
133
  Override.method_return('Kernel#puts', 'nil'),
117
134
 
data/solargraph.gemspec CHANGED
@@ -19,9 +19,10 @@ Gem::Specification.new do |s|
19
19
 
20
20
  s.required_ruby_version = '>= 2.4'
21
21
 
22
- s.add_runtime_dependency 'backport', '~> 1.1'
22
+ s.add_runtime_dependency 'backport', '~> 1.2'
23
23
  s.add_runtime_dependency 'benchmark'
24
24
  s.add_runtime_dependency 'bundler', '>= 1.17.2'
25
+ s.add_runtime_dependency 'diff-lcs', '~> 1.4'
25
26
  s.add_runtime_dependency 'e2mmap'
26
27
  s.add_runtime_dependency 'jaro_winkler', '~> 1.5'
27
28
  s.add_runtime_dependency 'kramdown', '~> 2.3'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solargraph
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.41.0
4
+ version: 0.42.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fred Snyder
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-30 00:00:00.000000000 Z
11
+ date: 2021-06-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: backport
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.1'
19
+ version: '1.2'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.1'
26
+ version: '1.2'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: benchmark
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: 1.17.2
55
+ - !ruby/object:Gem::Dependency
56
+ name: diff-lcs
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.4'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.4'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: e2mmap
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -463,7 +477,6 @@ files:
463
477
  - lib/solargraph/pin/keyword.rb
464
478
  - lib/solargraph/pin/keyword_param.rb
465
479
  - lib/solargraph/pin/local_variable.rb
466
- - lib/solargraph/pin/localized.rb
467
480
  - lib/solargraph/pin/method.rb
468
481
  - lib/solargraph/pin/method_alias.rb
469
482
  - lib/solargraph/pin/namespace.rb
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Solargraph
4
- module Pin
5
- module Localized
6
- # @return [Range]
7
- attr_reader :presence
8
-
9
- # @param other [Pin::Base] The caller's block
10
- # @param position [Position, Array(Integer, Integer)] The caller's position
11
- # @return [Boolean]
12
- def visible_from?(other, position)
13
- position = Position.normalize(position)
14
- other.filename == filename &&
15
- match_tags(other.full_context.tag, full_context.tag) &&
16
- (other == closure ||
17
- (closure.location.range.contain?(other.location.range.start) && closure.location.range.contain?(other.location.range.ending))
18
- ) &&
19
- presence.contain?(position)
20
- end
21
-
22
- # @param other_loc [Location]
23
- def visible_at?(other_loc)
24
- return false if location.filename != other_loc.filename
25
- presence.include?(other_loc.range.start)
26
- end
27
-
28
- private
29
-
30
- # @param tag1 [String]
31
- # @param tag2 [String]
32
- # @return [Boolean]
33
- def match_tags tag1, tag2
34
- # @todo This is an unfortunate hack made necessary by a discrepancy in
35
- # how tags indicate the root namespace. The long-term solution is to
36
- # standardize it, whether it's `Class<>`, an empty string, or
37
- # something else.
38
- tag1 == tag2 ||
39
- (['', 'Class<>'].include?(tag1) && ['', 'Class<>'].include?(tag2))
40
- end
41
- end
42
- end
43
- end