solargraph 0.39.13 → 0.40.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -7
  3. data/CHANGELOG.md +984 -0
  4. data/Rakefile +12 -1
  5. data/SPONSORS.md +1 -0
  6. data/lib/solargraph.rb +2 -4
  7. data/lib/solargraph/api_map.rb +75 -74
  8. data/lib/solargraph/api_map/cache.rb +2 -2
  9. data/lib/solargraph/api_map/store.rb +4 -8
  10. data/lib/solargraph/{bundle.rb → bench.rb} +6 -2
  11. data/lib/solargraph/compat.rb +14 -0
  12. data/lib/solargraph/complex_type.rb +2 -2
  13. data/lib/solargraph/convention.rb +14 -5
  14. data/lib/solargraph/convention/base.rb +16 -8
  15. data/lib/solargraph/convention/gemfile.rb +2 -5
  16. data/lib/solargraph/convention/gemspec.rb +3 -6
  17. data/lib/solargraph/convention/rspec.rb +3 -6
  18. data/lib/solargraph/documentor.rb +2 -0
  19. data/lib/solargraph/environ.rb +11 -6
  20. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +6 -1
  21. data/lib/solargraph/language_server/message/text_document/definition.rb +1 -1
  22. data/lib/solargraph/library.rb +7 -7
  23. data/lib/solargraph/parser/legacy/node_chainer.rb +7 -7
  24. data/lib/solargraph/parser/legacy/node_processors/ivasgn_node.rb +1 -1
  25. data/lib/solargraph/parser/legacy/node_processors/send_node.rb +36 -23
  26. data/lib/solargraph/parser/node_processor/base.rb +3 -0
  27. data/lib/solargraph/parser/rubyvm/node_chainer.rb +9 -9
  28. data/lib/solargraph/parser/rubyvm/node_methods.rb +1 -1
  29. data/lib/solargraph/parser/rubyvm/node_processors.rb +1 -0
  30. data/lib/solargraph/parser/rubyvm/node_processors/args_node.rb +35 -11
  31. data/lib/solargraph/parser/rubyvm/node_processors/ivasgn_node.rb +1 -1
  32. data/lib/solargraph/parser/rubyvm/node_processors/send_node.rb +40 -29
  33. data/lib/solargraph/pin.rb +0 -3
  34. data/lib/solargraph/pin/common.rb +1 -1
  35. data/lib/solargraph/pin/conversions.rb +3 -4
  36. data/lib/solargraph/pin/documenting.rb +3 -9
  37. data/lib/solargraph/pin/method.rb +141 -7
  38. data/lib/solargraph/pin/method_alias.rb +1 -1
  39. data/lib/solargraph/position.rb +2 -14
  40. data/lib/solargraph/shell.rb +1 -0
  41. data/lib/solargraph/source.rb +10 -6
  42. data/lib/solargraph/source/chain.rb +18 -5
  43. data/lib/solargraph/source_map.rb +4 -1
  44. data/lib/solargraph/source_map/clip.rb +3 -2
  45. data/lib/solargraph/source_map/mapper.rb +10 -6
  46. data/lib/solargraph/type_checker.rb +35 -39
  47. data/lib/solargraph/type_checker/param_def.rb +1 -1
  48. data/lib/solargraph/version.rb +1 -1
  49. data/lib/solargraph/yard_map.rb +40 -47
  50. data/lib/solargraph/yard_map/core_fills.rb +185 -0
  51. data/lib/solargraph/yard_map/helpers.rb +16 -0
  52. data/lib/solargraph/yard_map/mapper.rb +14 -8
  53. data/lib/solargraph/{pin/yard_pin/constant.rb → yard_map/mapper/to_constant.rb} +6 -6
  54. data/lib/solargraph/yard_map/mapper/to_method.rb +78 -0
  55. data/lib/solargraph/{pin/yard_pin/namespace.rb → yard_map/mapper/to_namespace.rb} +6 -6
  56. data/lib/solargraph/yard_map/rdoc_to_yard.rb +1 -1
  57. data/lib/solargraph/yard_map/stdlib_fills.rb +43 -0
  58. data/lib/solargraph/yard_map/to_method.rb +79 -0
  59. data/solargraph.gemspec +4 -4
  60. metadata +20 -34
  61. data/lib/solargraph/core_fills.rb +0 -159
  62. data/lib/solargraph/pin/attribute.rb +0 -49
  63. data/lib/solargraph/pin/base_method.rb +0 -141
  64. data/lib/solargraph/pin/yard_pin.rb +0 -12
  65. data/lib/solargraph/pin/yard_pin/method.rb +0 -80
  66. data/lib/solargraph/pin/yard_pin/yard_mixin.rb +0 -20
  67. data/lib/solargraph/stdlib_fills.rb +0 -40
  68. data/travis-bundler.rb +0 -11
@@ -6,7 +6,7 @@ module Solargraph
6
6
  # examples that defer mapping are aliases for superclass methods or
7
7
  # methods from included modules.
8
8
  #
9
- class MethodAlias < BaseMethod
9
+ class MethodAlias < Method
10
10
  # @return [::Symbol]
11
11
  attr_reader :scope
12
12
 
@@ -40,20 +40,8 @@ module Solargraph
40
40
  # @param position [Position]
41
41
  # @return [Integer]
42
42
  def self.to_offset text, position
43
- result = 0
44
- feed = 0
45
- line = position.line
46
- column = position.character
47
- text.lines.each do |l|
48
- line_length = l.length
49
- if feed == line
50
- result += column
51
- break
52
- end
53
- result += line_length
54
- feed += 1
55
- end
56
- result
43
+ return 0 if text.empty?
44
+ text.lines[0...position.line].sum(&:length) + position.character
57
45
  end
58
46
 
59
47
  # Get a numeric offset for the specified text and a position identified
@@ -153,6 +153,7 @@ module Solargraph
153
153
  probcount += problems.length
154
154
  end
155
155
  puts "#{probcount} problem#{probcount != 1 ? 's' : ''} found#{files.length != 1 ? " in #{filecount} of #{files.length} files" : ''}."
156
+ exit 1 if probcount > 0
156
157
  end
157
158
 
158
159
  desc 'scan', 'Test the workspace for problems'
@@ -284,7 +284,7 @@ module Solargraph
284
284
 
285
285
  FOLDING_NODE_TYPES = if Parser.rubyvm?
286
286
  %i[
287
- CLASS SCLASS MODULE DEFN DEFS IF WHILE UNLESS ITER STR
287
+ CLASS SCLASS MODULE DEFN DEFS IF WHILE UNLESS ITER STR HASH ARRAY LIST
288
288
  ].freeze
289
289
  else
290
290
  %i[
@@ -345,18 +345,22 @@ module Solargraph
345
345
 
346
346
  # @param top [Parser::AST::Node]
347
347
  # @param result [Array<Range>]
348
+ # @param parent [Symbol]
348
349
  # @return [void]
349
- def inner_folding_ranges top, result = []
350
+ def inner_folding_ranges top, result = [], parent = nil
350
351
  # return unless top.is_a?(::Parser::AST::Node)
351
352
  return unless Parser.is_ast_node?(top)
352
353
  if FOLDING_NODE_TYPES.include?(top.type)
353
- range = Range.from_node(top)
354
- if result.empty? || range.start.line > result.last.start.line
355
- result.push range unless range.ending.line - range.start.line < 2
354
+ # @todo Smelly exception for hash's first-level array in RubyVM
355
+ unless [:ARRAY, :LIST].include?(top.type) && parent == :HASH
356
+ range = Range.from_node(top)
357
+ if result.empty? || range.start.line > result.last.start.line
358
+ result.push range unless range.ending.line - range.start.line < 2
359
+ end
356
360
  end
357
361
  end
358
362
  top.children.each do |child|
359
- inner_folding_ranges(child, result)
363
+ inner_folding_ranges(child, result, top.type)
360
364
  end
361
365
  end
362
366
 
@@ -110,18 +110,21 @@ module Solargraph
110
110
  # @param api_map [ApiMap]
111
111
  # @return [ComplexType]
112
112
  def infer_first_defined pins, context, api_map
113
- type = ComplexType::UNDEFINED
113
+ possibles = []
114
114
  pins.each do |pin|
115
115
  # Avoid infinite recursion
116
116
  next if @@inference_stack.include?(pin.identity)
117
117
  @@inference_stack.push pin.identity
118
118
  type = pin.typify(api_map)
119
119
  @@inference_stack.pop
120
- break if type.defined?
120
+ if type.defined?
121
+ possibles.push type
122
+ break if pin.is_a?(Pin::Method)
123
+ end
121
124
  end
122
- if type.undefined?
125
+ if possibles.empty?
123
126
  # Limit method inference recursion
124
- return type if @@inference_depth >= 10 && pins.first.is_a?(Pin::BaseMethod)
127
+ return ComplexType::UNDEFINED if @@inference_depth >= 10 && pins.first.is_a?(Pin::Method)
125
128
  @@inference_depth += 1
126
129
  pins.each do |pin|
127
130
  # Avoid infinite recursion
@@ -129,10 +132,20 @@ module Solargraph
129
132
  @@inference_stack.push pin.identity
130
133
  type = pin.probe(api_map)
131
134
  @@inference_stack.pop
132
- break if type.defined?
135
+ if type.defined?
136
+ possibles.push type
137
+ break if pin.is_a?(Pin::Method)
138
+ end
133
139
  end
134
140
  @@inference_depth -= 1
135
141
  end
142
+ return ComplexType::UNDEFINED if possibles.empty?
143
+ type = if possibles.length > 1
144
+ sorted = possibles.map { |t| t.rooted? ? "::#{t}" : t.to_s }.sort { |a, _| a == 'nil' ? 1 : 0 }
145
+ ComplexType.parse(*sorted)
146
+ else
147
+ possibles.first
148
+ end
136
149
  return type if context.nil? || context.return_type.undefined?
137
150
  type.self_to(context.return_type.namespace)
138
151
  end
@@ -30,7 +30,7 @@ module Solargraph
30
30
  @source = source.dup
31
31
  @pins = pins
32
32
  @locals = locals
33
- environ.merge Convention.for(source)
33
+ environ.merge Convention.for_local(self) unless filename.nil?
34
34
  @pin_class_hash = pins.to_set.classify(&:class).transform_values(&:to_a)
35
35
  @pin_select_cache = {}
36
36
  end
@@ -168,6 +168,9 @@ module Solargraph
168
168
  position = Position.new(line, character)
169
169
  found = nil
170
170
  pins.each do |pin|
171
+ # @todo Attribute pins should not be treated like closures, but
172
+ # there's probably a better way to handle it
173
+ next if pin.is_a?(Pin::Method) && pin.attribute?
171
174
  found = pin if (klasses.empty? || klasses.any? { |kls| pin.is_a?(kls) } ) && pin.location.range.contain?(position)
172
175
  break if pin.location.range.start.line > line
173
176
  end
@@ -149,7 +149,7 @@ module Solargraph
149
149
  frag_start = cursor.start_of_word.to_s.downcase
150
150
  filtered = result.uniq(&:name).select { |s|
151
151
  s.name.downcase.start_with?(frag_start) &&
152
- (!s.is_a?(Pin::BaseMethod) || s.name.match(/^[a-z0-9_]+(\!|\?|=)?$/i))
152
+ (!s.is_a?(Pin::Method) || s.name.match(/^[a-z0-9_]+(\!|\?|=)?$/i))
153
153
  }
154
154
  Completion.new(filtered, cursor.range)
155
155
  end
@@ -212,7 +212,8 @@ module Solargraph
212
212
  result.concat api_map.get_constants(context_pin.context.namespace, *gates)
213
213
  result.concat api_map.get_methods(block.binder.namespace, scope: block.binder.scope, visibility: [:public, :private, :protected])
214
214
  result.concat api_map.get_methods('Kernel')
215
- result.concat ApiMap.keywords
215
+ # result.concat ApiMap.keywords
216
+ result.concat api_map.keyword_pins
216
217
  result.concat yielded_self_pins
217
218
  end
218
219
  end
@@ -126,27 +126,31 @@ module Solargraph
126
126
  namespace = closure_at(source_position)
127
127
  t = (directive.tag.types.nil? || directive.tag.types.empty?) ? nil : directive.tag.types.flatten.join('')
128
128
  if t.nil? || t.include?('r')
129
- pins.push Solargraph::Pin::Attribute.new(
129
+ pins.push Solargraph::Pin::Method.new(
130
130
  location: location,
131
131
  closure: namespace,
132
132
  name: directive.tag.name,
133
133
  comments: docstring.all.to_s,
134
- access: :reader,
135
134
  scope: namespace.is_a?(Pin::Singleton) ? :class : :instance,
136
135
  visibility: :public,
137
- explicit: false
136
+ explicit: false,
137
+ attribute: true
138
138
  )
139
139
  end
140
140
  if t.nil? || t.include?('w')
141
- pins.push Solargraph::Pin::Attribute.new(
141
+ pins.push Solargraph::Pin::Method.new(
142
142
  location: location,
143
143
  closure: namespace,
144
144
  name: "#{directive.tag.name}=",
145
145
  comments: docstring.all.to_s,
146
- access: :writer,
147
146
  scope: namespace.is_a?(Pin::Singleton) ? :class : :instance,
148
- visibility: :public
147
+ visibility: :public,
148
+ attribute: true
149
149
  )
150
+ pins.last.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last)
151
+ if pins.last.return_type.defined?
152
+ pins.last.docstring.add_tag YARD::Tags::Tag.new(:param, '', pins.last.return_type.to_s.split(', '), 'value')
153
+ end
150
154
  end
151
155
  when 'parse'
152
156
  begin
@@ -73,15 +73,15 @@ module Solargraph
73
73
  # @return [Array<Problem>]
74
74
  def method_tag_problems
75
75
  result = []
76
- # @param pin [Pin::BaseMethod]
77
- source_map.pins_by_class(Pin::BaseMethod).each do |pin|
76
+ # @param pin [Pin::Method]
77
+ source_map.pins_by_class(Pin::Method).each do |pin|
78
78
  result.concat method_return_type_problems_for(pin)
79
79
  result.concat method_param_type_problems_for(pin)
80
80
  end
81
81
  result
82
82
  end
83
83
 
84
- # @param pin [Pin::BaseMethod]
84
+ # @param pin [Pin::Method]
85
85
  # @return [Array<Problem>]
86
86
  def method_return_type_problems_for pin
87
87
  result = []
@@ -115,7 +115,7 @@ module Solargraph
115
115
  pin.location && source_map.source.comment_at?(pin.location.range.ending)
116
116
  end
117
117
 
118
- # @param pin [Pin::BaseMethod]
118
+ # @param pin [Pin::Method]
119
119
  # @return [Array<Problem>]
120
120
  def method_param_type_problems_for pin
121
121
  stack = api_map.get_method_stack(pin.namespace, pin.name, scope: pin.scope)
@@ -147,14 +147,12 @@ module Solargraph
147
147
  result = []
148
148
  all_variables.each do |pin|
149
149
  if pin.return_type.defined?
150
- # @todo Somwhere in here we still need to determine if the variable is defined by an external call
151
150
  declared = pin.typify(api_map)
152
151
  if declared.defined?
153
152
  if rules.validate_tags?
154
153
  inferred = pin.probe(api_map)
155
154
  if inferred.undefined?
156
155
  next if rules.ignore_all_undefined?
157
- # next unless internal?(pin) # @todo This might be redundant for variables
158
156
  if declared_externally?(pin)
159
157
  ignored_pins.push pin
160
158
  else
@@ -172,7 +170,6 @@ module Solargraph
172
170
  result.push Problem.new(pin.location, "Unresolved type #{pin.return_type} for variable #{pin.name}", pin: pin)
173
171
  end
174
172
  else
175
- # @todo Check if the variable is defined by an external call
176
173
  inferred = pin.probe(api_map)
177
174
  if inferred.undefined? && declared_externally?(pin)
178
175
  ignored_pins.push pin
@@ -227,7 +224,7 @@ module Solargraph
227
224
  base = base.base
228
225
  end
229
226
  closest = found.typify(api_map) if found
230
- if !found || closest.defined? || internal?(found)
227
+ if !found || (closest.defined? && internal_or_core?(found))
231
228
  unless ignored_pins.include?(found)
232
229
  result.push Problem.new(location, "Unresolved call to #{missing.links.last.word}")
233
230
  @marked_ranges.push rng
@@ -244,8 +241,8 @@ module Solargraph
244
241
  base = chain
245
242
  until base.links.length == 1 && base.undefined?
246
243
  pins = base.define(api_map, block_pin, locals)
247
- if pins.first.is_a?(Pin::BaseMethod)
248
- # @type [Pin::BaseMethod]
244
+ if pins.first.is_a?(Pin::Method)
245
+ # @type [Pin::Method]
249
246
  pin = pins.first
250
247
  ap = if base.links.last.is_a?(Solargraph::Source::Chain::ZSuper)
251
248
  arity_problems_for(pin, fake_args_for(block_pin), location)
@@ -313,7 +310,6 @@ module Solargraph
313
310
  end
314
311
  else
315
312
  if par.decl == :kwarg
316
- # @todo Problem: missing required keyword argument
317
313
  result.push Problem.new(location, "Call to #{pin.path} is missing keyword argument #{par.name}")
318
314
  end
319
315
  end
@@ -360,9 +356,14 @@ module Solargraph
360
356
 
361
357
  # @param pin [Pin::Base]
362
358
  def internal? pin
359
+ return false if pin.nil?
363
360
  pin.location && api_map.bundled?(pin.location.filename)
364
361
  end
365
362
 
363
+ def internal_or_core? pin
364
+ internal?(pin) || api_map.yard_map.core_pins.include?(pin) || api_map.yard_map.stdlib_pins.include?(pin)
365
+ end
366
+
366
367
  # @param pin [Pin::Base]
367
368
  def external? pin
368
369
  !internal? pin
@@ -395,7 +396,7 @@ module Solargraph
395
396
  true
396
397
  end
397
398
 
398
- # @param pin [Pin::BaseMethod]
399
+ # @param pin [Pin::Method]
399
400
  def arity_problems_for(pin, arguments, location)
400
401
  return [] unless pin.explicit?
401
402
  return [] if pin.parameters.empty? && arguments.empty?
@@ -410,29 +411,29 @@ module Solargraph
410
411
  if unchecked.empty? && pin.parameters.any? { |param| param.decl == :kwarg }
411
412
  return [Problem.new(location, "Missing keyword arguments to #{pin.path}")]
412
413
  end
414
+ settled_kwargs = 0
413
415
  unless unchecked.empty?
414
416
  kwargs = convert_hash(unchecked.last.node)
415
- # unless kwargs.empty?
416
- if pin.parameters.any? { |param| [:kwarg, :kwoptarg].include?(param.decl) || param.kwrestarg? }
417
- if kwargs.empty?
418
- add_params += 1
419
- else
420
- unchecked.pop
421
- pin.parameters.each do |param|
422
- next unless param.keyword?
423
- if kwargs.key?(param.name.to_sym)
424
- kwargs.delete param.name.to_sym
425
- elsif param.decl == :kwarg
426
- return [Problem.new(location, "Missing keyword argument #{param.name} to #{pin.path}")]
427
- end
428
- end
429
- kwargs.clear if pin.parameters.any?(&:kwrestarg?)
430
- unless kwargs.empty?
431
- return [Problem.new(location, "Unrecognized keyword argument #{kwargs.keys.first} to #{pin.path}")]
417
+ if pin.parameters.any? { |param| [:kwarg, :kwoptarg].include?(param.decl) || param.kwrestarg? }
418
+ if kwargs.empty?
419
+ add_params += 1
420
+ else
421
+ unchecked.pop
422
+ pin.parameters.each do |param|
423
+ next unless param.keyword?
424
+ if kwargs.key?(param.name.to_sym)
425
+ kwargs.delete param.name.to_sym
426
+ settled_kwargs += 1
427
+ elsif param.decl == :kwarg
428
+ return [Problem.new(location, "Missing keyword argument #{param.name} to #{pin.path}")]
432
429
  end
433
430
  end
431
+ kwargs.clear if pin.parameters.any?(&:kwrestarg?)
432
+ unless kwargs.empty?
433
+ return [Problem.new(location, "Unrecognized keyword argument #{kwargs.keys.first} to #{pin.path}")]
434
+ end
434
435
  end
435
- # end
436
+ end
436
437
  end
437
438
  req = required_param_count(pin)
438
439
  if req + add_params < unchecked.length
@@ -443,23 +444,18 @@ module Solargraph
443
444
  return []
444
445
  end
445
446
  return [Problem.new(location, "Too many arguments to #{pin.path}")]
446
- elsif unchecked.length < req && (arguments.empty? || !arguments.last.splat?)
447
+ elsif unchecked.length < req - settled_kwargs && (arguments.empty? || !arguments.last.splat?)
447
448
  return [Problem.new(location, "Not enough arguments to #{pin.path}")]
448
449
  end
449
450
  []
450
451
  end
451
452
 
452
- # @param pin [Pin::BaseMethod]
453
+ # @param pin [Pin::Method]
453
454
  def required_param_count(pin)
454
- count = 0
455
- pin.parameters.each do |param|
456
- break unless param.decl == :arg
457
- count += 1
458
- end
459
- count
455
+ pin.parameters.sum { |param| %i[arg kwarg].include?(param.decl) ? 1 : 0 }
460
456
  end
461
457
 
462
- # @param pin [Pin::BaseMethod]
458
+ # @param pin [Pin::Method]
463
459
  def optional_param_count(pin)
464
460
  count = 0
465
461
  pin.parameters.each do |param|
@@ -20,7 +20,7 @@ module Solargraph
20
20
  class << self
21
21
  # Get an array of ParamDefs from a method pin.
22
22
  #
23
- # @param pin [Solargraph::Pin::BaseMethod]
23
+ # @param pin [Solargraph::Pin::Method]
24
24
  # @return [Array<ParamDef>]
25
25
  def from pin
26
26
  result = []
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- VERSION = '0.39.13'
4
+ VERSION = '0.40.0'
5
5
  end
@@ -10,11 +10,15 @@ module Solargraph
10
10
  # stdlib, and gems.
11
11
  #
12
12
  class YardMap
13
- autoload :Cache, 'solargraph/yard_map/cache'
14
- autoload :CoreDocs, 'solargraph/yard_map/core_docs'
15
- autoload :CoreGen, 'solargraph/yard_map/core_gen'
16
- autoload :Mapper, 'solargraph/yard_map/mapper'
17
- autoload :RdocToYard, 'solargraph/yard_map/rdoc_to_yard'
13
+ autoload :Cache, 'solargraph/yard_map/cache'
14
+ autoload :CoreDocs, 'solargraph/yard_map/core_docs'
15
+ autoload :CoreGen, 'solargraph/yard_map/core_gen'
16
+ autoload :Mapper, 'solargraph/yard_map/mapper'
17
+ autoload :RdocToYard, 'solargraph/yard_map/rdoc_to_yard'
18
+ autoload :CoreFills, 'solargraph/yard_map/core_fills'
19
+ autoload :StdlibFills, 'solargraph/yard_map/stdlib_fills'
20
+ autoload :Helpers, 'solargraph/yard_map/helpers'
21
+ autoload :ToMethod, 'solargraph/yard_map/to_method'
18
22
 
19
23
  CoreDocs.require_minimum
20
24
 
@@ -50,6 +54,8 @@ module Solargraph
50
54
  def initialize(required: [], gemset: {}, with_dependencies: true)
51
55
  # HACK: YardMap needs its own copy of this array
52
56
  @required = required.clone
57
+ # HACK: Hardcoded YAML handling
58
+ @required.push 'psych' if @required.include?('yaml')
53
59
  @with_dependencies = with_dependencies
54
60
  @gem_paths = {}
55
61
  @stdlib_namespaces = []
@@ -74,6 +80,8 @@ module Solargraph
74
80
  # @param new_gemset [Hash{String => String}]
75
81
  # @return [Boolean]
76
82
  def change new_requires, new_gemset, source_gems = []
83
+ # HACK: Hardcoded YAML handling
84
+ new_requires.push 'psych' if new_requires.include?('yaml')
77
85
  if new_requires.uniq.sort == required.uniq.sort && new_gemset == gemset && @source_gems.uniq.sort == source_gems.uniq.sort
78
86
  false
79
87
  else
@@ -131,7 +139,7 @@ module Solargraph
131
139
  # @param path [String]
132
140
  # @return [Pin::Base]
133
141
  def path_pin path
134
- pins.select{ |p| p.path == path }.first
142
+ pins.select { |p| p.path == path }.first
135
143
  end
136
144
 
137
145
  # Get the location of a file referenced by a require path.
@@ -151,6 +159,10 @@ module Solargraph
151
159
  nil
152
160
  end
153
161
 
162
+ def stdlib_pins
163
+ @stdlib_pins ||= []
164
+ end
165
+
154
166
  private
155
167
 
156
168
  # @return [YardMap::Cache]
@@ -158,10 +170,12 @@ module Solargraph
158
170
  @cache ||= YardMap::Cache.new
159
171
  end
160
172
 
173
+ # @return [Hash]
161
174
  def pin_class_hash
162
175
  @pin_class_hash ||= pins.to_set.classify(&:class).transform_values(&:to_a)
163
176
  end
164
177
 
178
+ # @return [Array<Pin::Base>]
165
179
  def pins_by_class klass
166
180
  @pin_select_cache[klass] ||= pin_class_hash.select { |key, _| key <= klass }.values.flatten
167
181
  end
@@ -179,12 +193,13 @@ module Solargraph
179
193
 
180
194
  # @return [void]
181
195
  def process_requires
182
- pins.clear
196
+ pins.replace core_pins
183
197
  unresolved_requires.clear
184
- # stdnames = {}
198
+ stdlib_pins.clear
199
+ environ = Convention.for_global(self)
185
200
  done = []
186
201
  from_std = []
187
- required.each do |r|
202
+ (required + environ.requires).each do |r|
188
203
  next if r.nil? || r.empty? || done.include?(r)
189
204
  done.push r
190
205
  cached = cache.get_path_pins(r)
@@ -209,7 +224,6 @@ module Solargraph
209
224
  yardocs.unshift yd
210
225
  result.concat process_yardoc yd, spec
211
226
  result.concat add_gem_dependencies(spec) if with_dependencies?
212
- stdlib_fill r, result
213
227
  end
214
228
  rescue Gem::LoadError => e
215
229
  base = r.split('/').first
@@ -219,7 +233,7 @@ module Solargraph
219
233
  if stdtmp.empty?
220
234
  unresolved_requires.push r
221
235
  else
222
- stdlib_fill base, stdtmp
236
+ stdlib_pins.concat stdtmp
223
237
  result.concat stdtmp
224
238
  end
225
239
  end
@@ -229,7 +243,13 @@ module Solargraph
229
243
  pins.concat result
230
244
  end
231
245
  end
232
- pins.concat core_pins
246
+ if required.include?('yaml') && required.include?('psych')
247
+ # HACK: Hardcoded YAML handling
248
+ # @todo Why can't this be handled with an override or a virtual pin?
249
+ pin = path_pin('YAML')
250
+ pin.instance_variable_set(:@return_type, ComplexType.parse('Module<Psych>')) unless pin.nil?
251
+ end
252
+ pins.concat environ.pins
233
253
  end
234
254
 
235
255
  # @param spec [Gem::Specification]
@@ -327,22 +347,6 @@ module Solargraph
327
347
  spec
328
348
  end
329
349
 
330
- # @param path [String]
331
- # @param pins [Array<Pin::Base>]
332
- # @return [void]
333
- def stdlib_fill path, pins
334
- StdlibFills.get(path).each do |ovr|
335
- pin = pins.select { |p| p.path == ovr.name }.first
336
- next if pin.nil?
337
- (ovr.tags.map(&:tag_name) + ovr.delete).uniq.each do |tag|
338
- pin.docstring.delete_tags tag.to_sym
339
- end
340
- ovr.tags.each do |tag|
341
- pin.docstring.add_tag(tag)
342
- end
343
- end
344
- end
345
-
346
350
  def load_core_pins
347
351
  yd = CoreDocs.yardoc_file
348
352
  ser = File.join(File.dirname(yd), 'core.ser')
@@ -360,23 +364,7 @@ module Solargraph
360
364
  else
361
365
  read_core_and_save_cache(yd, ser)
362
366
  end
363
- # HACK: Add Errno exception classes
364
- errno = result.select{ |pin| pin.path == 'Errno' }.first
365
- Errno.constants.each do |const|
366
- result.push Solargraph::Pin::Namespace.new(type: :class, name: const.to_s, closure: errno)
367
- result.push Solargraph::Pin::Reference::Superclass.new(closure: result.last, name: 'SystemCallError')
368
- end
369
- CoreFills::OVERRIDES.each do |ovr|
370
- pin = result.select { |p| p.path == ovr.name }.first
371
- next if pin.nil?
372
- (ovr.tags.map(&:tag_name) + ovr.delete).uniq.each do |tag|
373
- pin.docstring.delete_tags tag.to_sym
374
- end
375
- ovr.tags.each do |tag|
376
- pin.docstring.add_tag(tag)
377
- end
378
- end
379
- result
367
+ ApiMap::Store.new(result + CoreFills::ALL).pins.reject { |pin| pin.is_a?(Pin::Reference::Override) }
380
368
  end
381
369
 
382
370
  def read_core_and_save_cache yd, ser
@@ -384,7 +372,7 @@ module Solargraph
384
372
  load_yardoc yd
385
373
  result.concat Mapper.new(YARD::Registry.all).map
386
374
  # HACK: Assume core methods with a single `args` parameter accept restarg
387
- result.select { |pin| pin.is_a?(Solargraph::Pin::BaseMethod )}.each do |pin|
375
+ result.select { |pin| pin.is_a?(Solargraph::Pin::Method )}.each do |pin|
388
376
  if pin.parameters.length == 1 && pin.parameters.first.name == 'args' && pin.parameters.first.decl == :arg
389
377
  # @todo Smelly instance variable access
390
378
  pin.parameters.first.instance_variable_set(:@decl, :restarg)
@@ -403,7 +391,7 @@ module Solargraph
403
391
 
404
392
  def load_stdlib_pins base
405
393
  ser = File.join(File.dirname(CoreDocs.yardoc_stdlib_file), "#{base}.ser")
406
- if File.file?(ser)
394
+ result = if File.file?(ser)
407
395
  Solargraph.logger.info "Loading #{base} stdlib from cache"
408
396
  file = File.open(ser, 'rb')
409
397
  dump = file.read
@@ -418,6 +406,11 @@ module Solargraph
418
406
  else
419
407
  read_stdlib_and_save_cache(base, ser)
420
408
  end
409
+ fills = StdlibFills.get(base)
410
+ unless fills.empty?
411
+ result = ApiMap::Store.new(result + fills).pins.reject { |pin| pin.is_a?(Pin::Reference::Override) }
412
+ end
413
+ result
421
414
  end
422
415
 
423
416
  def read_stdlib_and_save_cache base, ser