solargraph 0.52.0 → 0.53.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.
Files changed (155) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/plugins.yml +40 -0
  3. data/.github/workflows/rspec.yml +1 -3
  4. data/.github/workflows/typecheck.yml +34 -0
  5. data/CHANGELOG.md +30 -0
  6. data/README.md +13 -16
  7. data/SPONSORS.md +1 -7
  8. data/lib/solargraph/api_map/cache.rb +59 -21
  9. data/lib/solargraph/api_map/store.rb +45 -9
  10. data/lib/solargraph/api_map.rb +152 -93
  11. data/lib/solargraph/bench.rb +2 -2
  12. data/lib/solargraph/cache.rb +29 -5
  13. data/lib/solargraph/complex_type/type_methods.rb +53 -8
  14. data/lib/solargraph/complex_type/unique_type.rb +149 -59
  15. data/lib/solargraph/complex_type.rb +62 -9
  16. data/lib/solargraph/convention.rb +0 -1
  17. data/lib/solargraph/converters/dd.rb +5 -0
  18. data/lib/solargraph/converters/dl.rb +3 -0
  19. data/lib/solargraph/converters/dt.rb +3 -0
  20. data/lib/solargraph/diagnostics/rubocop.rb +8 -7
  21. data/lib/solargraph/diagnostics/rubocop_helpers.rb +1 -0
  22. data/lib/solargraph/diagnostics/type_check.rb +1 -0
  23. data/lib/solargraph/diagnostics.rb +2 -2
  24. data/lib/solargraph/doc_map.rb +146 -0
  25. data/lib/solargraph/gem_pins.rb +64 -0
  26. data/lib/solargraph/language_server/host/cataloger.rb +1 -0
  27. data/lib/solargraph/language_server/host/diagnoser.rb +2 -2
  28. data/lib/solargraph/language_server/host/dispatch.rb +10 -4
  29. data/lib/solargraph/language_server/host/message_worker.rb +4 -0
  30. data/lib/solargraph/language_server/host/sources.rb +7 -4
  31. data/lib/solargraph/language_server/host.rb +15 -6
  32. data/lib/solargraph/language_server/message/completion_item/resolve.rb +3 -1
  33. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +13 -1
  34. data/lib/solargraph/language_server/message/initialize.rb +5 -2
  35. data/lib/solargraph/language_server/message/text_document/hover.rb +2 -0
  36. data/lib/solargraph/language_server/message/text_document.rb +0 -1
  37. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +5 -0
  38. data/lib/solargraph/language_server/transport/adapter.rb +16 -1
  39. data/lib/solargraph/language_server/transport/data_reader.rb +2 -0
  40. data/lib/solargraph/library.rb +58 -11
  41. data/lib/solargraph/location.rb +1 -0
  42. data/lib/solargraph/parser/comment_ripper.rb +3 -0
  43. data/lib/solargraph/parser/node_methods.rb +47 -8
  44. data/lib/solargraph/parser/node_processor/base.rb +9 -0
  45. data/lib/solargraph/parser/{legacy → parser_gem}/class_methods.rb +29 -3
  46. data/lib/solargraph/parser/{legacy → parser_gem}/flawed_builder.rb +3 -1
  47. data/lib/solargraph/parser/{legacy → parser_gem}/node_chainer.rb +42 -34
  48. data/lib/solargraph/parser/{legacy → parser_gem}/node_methods.rb +201 -29
  49. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/alias_node.rb +1 -1
  50. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/args_node.rb +4 -1
  51. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/begin_node.rb +1 -1
  52. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/block_node.rb +3 -2
  53. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/casgn_node.rb +2 -2
  54. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/cvasgn_node.rb +1 -1
  55. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/def_node.rb +1 -1
  56. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/defs_node.rb +2 -2
  57. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/gvasgn_node.rb +1 -1
  58. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/ivasgn_node.rb +2 -2
  59. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/lvasgn_node.rb +2 -2
  60. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/namespace_node.rb +2 -2
  61. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/orasgn_node.rb +1 -1
  62. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/resbody_node.rb +3 -3
  63. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/sclass_node.rb +1 -1
  64. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/send_node.rb +2 -2
  65. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/sym_node.rb +1 -1
  66. data/lib/solargraph/parser/parser_gem/node_processors.rb +54 -0
  67. data/lib/solargraph/parser/parser_gem.rb +12 -0
  68. data/lib/solargraph/parser/snippet.rb +2 -0
  69. data/lib/solargraph/parser.rb +8 -11
  70. data/lib/solargraph/pin/base.rb +63 -8
  71. data/lib/solargraph/pin/base_variable.rb +6 -2
  72. data/lib/solargraph/pin/block.rb +11 -6
  73. data/lib/solargraph/pin/closure.rb +17 -2
  74. data/lib/solargraph/pin/common.rb +7 -3
  75. data/lib/solargraph/pin/conversions.rb +33 -3
  76. data/lib/solargraph/pin/documenting.rb +25 -34
  77. data/lib/solargraph/pin/instance_variable.rb +4 -0
  78. data/lib/solargraph/pin/local_variable.rb +13 -1
  79. data/lib/solargraph/pin/method.rb +109 -15
  80. data/lib/solargraph/pin/namespace.rb +16 -10
  81. data/lib/solargraph/pin/parameter.rb +41 -10
  82. data/lib/solargraph/pin/reference/override.rb +2 -2
  83. data/lib/solargraph/pin/reference.rb +8 -0
  84. data/lib/solargraph/pin/search.rb +3 -3
  85. data/lib/solargraph/pin/signature.rb +114 -2
  86. data/lib/solargraph/pin.rb +0 -1
  87. data/lib/solargraph/range.rb +2 -2
  88. data/lib/solargraph/rbs_map/conversions.rb +212 -25
  89. data/lib/solargraph/rbs_map/core_fills.rb +4 -26
  90. data/lib/solargraph/rbs_map/core_map.rb +1 -0
  91. data/lib/solargraph/rbs_map/core_signs.rb +2 -0
  92. data/lib/solargraph/rbs_map/stdlib_map.rb +2 -8
  93. data/lib/solargraph/rbs_map.rb +19 -9
  94. data/lib/solargraph/shell.rb +62 -59
  95. data/lib/solargraph/source/chain/array.rb +4 -1
  96. data/lib/solargraph/source/chain/block_symbol.rb +13 -0
  97. data/lib/solargraph/source/chain/call.rb +95 -26
  98. data/lib/solargraph/source/chain/constant.rb +15 -1
  99. data/lib/solargraph/source/chain/if.rb +23 -0
  100. data/lib/solargraph/source/chain/link.rb +7 -1
  101. data/lib/solargraph/source/chain/or.rb +1 -1
  102. data/lib/solargraph/source/chain/z_super.rb +2 -2
  103. data/lib/solargraph/source/chain.rb +20 -4
  104. data/lib/solargraph/source/change.rb +3 -0
  105. data/lib/solargraph/source/cursor.rb +2 -0
  106. data/lib/solargraph/source/source_chainer.rb +6 -5
  107. data/lib/solargraph/source.rb +15 -16
  108. data/lib/solargraph/source_map/clip.rb +11 -7
  109. data/lib/solargraph/source_map/mapper.rb +10 -0
  110. data/lib/solargraph/source_map.rb +13 -3
  111. data/lib/solargraph/type_checker/checks.rb +10 -2
  112. data/lib/solargraph/type_checker.rb +74 -19
  113. data/lib/solargraph/version.rb +1 -1
  114. data/lib/solargraph/workspace/config.rb +8 -6
  115. data/lib/solargraph/workspace.rb +1 -1
  116. data/lib/solargraph/yard_map/cache.rb +6 -0
  117. data/lib/solargraph/yard_map/helpers.rb +1 -1
  118. data/lib/solargraph/yard_map/mapper/to_method.rb +11 -1
  119. data/lib/solargraph/yard_map/to_method.rb +11 -4
  120. data/lib/solargraph/yard_map.rb +0 -292
  121. data/lib/solargraph/yardoc.rb +52 -0
  122. data/lib/solargraph.rb +4 -1
  123. data/solargraph.gemspec +2 -2
  124. metadata +35 -57
  125. data/lib/solargraph/api_map/bundler_methods.rb +0 -22
  126. data/lib/solargraph/documentor.rb +0 -76
  127. data/lib/solargraph/parser/legacy/node_processors/alias_node.rb +0 -23
  128. data/lib/solargraph/parser/legacy/node_processors/begin_node.rb +0 -15
  129. data/lib/solargraph/parser/legacy/node_processors/sym_node.rb +0 -18
  130. data/lib/solargraph/parser/legacy/node_processors.rb +0 -55
  131. data/lib/solargraph/parser/legacy.rb +0 -12
  132. data/lib/solargraph/parser/rubyvm/class_methods.rb +0 -151
  133. data/lib/solargraph/parser/rubyvm/node_chainer.rb +0 -163
  134. data/lib/solargraph/parser/rubyvm/node_methods.rb +0 -317
  135. data/lib/solargraph/parser/rubyvm/node_processors/args_node.rb +0 -85
  136. data/lib/solargraph/parser/rubyvm/node_processors/block_node.rb +0 -42
  137. data/lib/solargraph/parser/rubyvm/node_processors/casgn_node.rb +0 -33
  138. data/lib/solargraph/parser/rubyvm/node_processors/cvasgn_node.rb +0 -23
  139. data/lib/solargraph/parser/rubyvm/node_processors/def_node.rb +0 -75
  140. data/lib/solargraph/parser/rubyvm/node_processors/defs_node.rb +0 -68
  141. data/lib/solargraph/parser/rubyvm/node_processors/gvasgn_node.rb +0 -23
  142. data/lib/solargraph/parser/rubyvm/node_processors/ivasgn_node.rb +0 -38
  143. data/lib/solargraph/parser/rubyvm/node_processors/kw_arg_node.rb +0 -39
  144. data/lib/solargraph/parser/rubyvm/node_processors/lit_node.rb +0 -20
  145. data/lib/solargraph/parser/rubyvm/node_processors/lvasgn_node.rb +0 -27
  146. data/lib/solargraph/parser/rubyvm/node_processors/namespace_node.rb +0 -39
  147. data/lib/solargraph/parser/rubyvm/node_processors/opt_arg_node.rb +0 -26
  148. data/lib/solargraph/parser/rubyvm/node_processors/orasgn_node.rb +0 -15
  149. data/lib/solargraph/parser/rubyvm/node_processors/resbody_node.rb +0 -51
  150. data/lib/solargraph/parser/rubyvm/node_processors/sclass_node.rb +0 -32
  151. data/lib/solargraph/parser/rubyvm/node_processors/scope_node.rb +0 -15
  152. data/lib/solargraph/parser/rubyvm/node_processors/send_node.rb +0 -279
  153. data/lib/solargraph/parser/rubyvm/node_processors.rb +0 -64
  154. data/lib/solargraph/parser/rubyvm/node_wrapper.rb +0 -47
  155. data/lib/solargraph/parser/rubyvm.rb +0 -40
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rubygems'
4
- require 'set'
5
3
  require 'pathname'
6
4
  require 'yard'
7
5
  require 'solargraph/yard_tags'
@@ -14,9 +12,6 @@ module Solargraph
14
12
  autoload :Cache, 'solargraph/api_map/cache'
15
13
  autoload :SourceToYard, 'solargraph/api_map/source_to_yard'
16
14
  autoload :Store, 'solargraph/api_map/store'
17
- autoload :BundlerMethods, 'solargraph/api_map/bundler_methods'
18
-
19
- include SourceToYard
20
15
 
21
16
  # @return [Array<String>]
22
17
  attr_reader :unresolved_requires
@@ -59,34 +54,33 @@ module Solargraph
59
54
  # Catalog a bench.
60
55
  #
61
56
  # @param bench [Bench]
57
+ # @return [self]
62
58
  def catalog bench
63
59
  implicit.clear
64
60
  @cache.clear
65
61
  @source_map_hash = bench.source_maps.map { |s| [s.filename, s] }.to_h
66
62
  pins = bench.source_maps.map(&:pins).flatten
67
- external_requires = bench.external_requires
68
63
  source_map_hash.each_value do |map|
69
64
  implicit.merge map.environ
70
65
  end
71
- external_requires.merge implicit.requires
72
- external_requires.merge bench.workspace.config.required
73
- @rbs_maps = external_requires.map { |r| load_rbs_map(r) }
74
- unresolved_requires = @rbs_maps.reject(&:resolved?).map(&:library)
75
- yard_map.change(unresolved_requires, bench.workspace.directory, bench.workspace.source_gems)
76
- @store = Store.new(@@core_map.pins + @rbs_maps.flat_map(&:pins) + yard_map.pins + implicit.pins + pins)
77
- @unresolved_requires = yard_map.unresolved_requires
78
- @missing_docs = yard_map.missing_docs
66
+ unresolved_requires = (bench.external_requires + implicit.requires + bench.workspace.config.required).uniq
67
+ @doc_map = DocMap.new(unresolved_requires, []) # @todo Implement gem dependencies
68
+ @store = Store.new(@@core_map.pins + @doc_map.pins + implicit.pins + pins)
69
+ @unresolved_requires = @doc_map.unresolved_requires
70
+ @missing_docs = [] # @todo Implement missing docs
79
71
  @rebindable_method_names = nil
80
72
  store.block_pins.each { |blk| blk.rebind(self) }
81
73
  self
82
74
  end
83
75
 
84
- def core_pins
85
- @@core_map.pins
76
+ # @return [::Array<Gem::Specification>]
77
+ def uncached_gemspecs
78
+ @doc_map&.uncached_gemspecs || []
86
79
  end
87
80
 
88
- def yard_map
89
- @yard_map ||= YardMap.new
81
+ # @return [Array<Pin::Base>]
82
+ def core_pins
83
+ @@core_map.pins
90
84
  end
91
85
 
92
86
  # @param name [String]
@@ -95,6 +89,7 @@ module Solargraph
95
89
  store.named_macros[name]
96
90
  end
97
91
 
92
+ # @return [Set<String>]
98
93
  def required
99
94
  @required ||= Set.new
100
95
  end
@@ -120,7 +115,7 @@ module Solargraph
120
115
  # @return [SourceMap::Clip]
121
116
  def clip_at filename, position
122
117
  position = Position.normalize(position)
123
- SourceMap::Clip.new(self, cursor_at(filename, position))
118
+ clip(cursor_at(filename, position))
124
119
  end
125
120
 
126
121
  # Create an ApiMap with a workspace in the specified directory.
@@ -137,14 +132,31 @@ module Solargraph
137
132
  api_map
138
133
  end
139
134
 
140
- # @return [Enumerable<Solargraph::Pin::Base>]
135
+ # Create an ApiMap with a workspace in the specified directory and cache
136
+ # any missing gems.
137
+ #
138
+ # @param directory [String]
139
+ # @return [ApiMap]
140
+ def self.load_with_cache directory
141
+ api_map = load(directory)
142
+ return api_map if api_map.uncached_gemspecs.empty?
143
+
144
+ api_map.uncached_gemspecs.each do |gemspec|
145
+ Solargraph.logger.info "Caching #{gemspec.name} #{gemspec.version}..."
146
+ pins = GemPins.build(gemspec)
147
+ Solargraph::Cache.save('gems', "#{gemspec.name}-#{gemspec.version}.ser", pins)
148
+ end
149
+ load(directory)
150
+ end
151
+
152
+ # @return [Array<Solargraph::Pin::Base>]
141
153
  def pins
142
154
  store.pins
143
155
  end
144
156
 
157
+ # @return [Set<String>]
145
158
  def rebindable_method_names
146
159
  @rebindable_method_names ||= begin
147
- # result = yard_map.rebindable_method_names
148
160
  result = ['instance_eval', 'instance_exec', 'class_eval', 'class_exec', 'module_eval', 'module_exec', 'define_method'].to_set
149
161
  source_maps.each do |map|
150
162
  result.merge map.rebindable_method_names
@@ -206,22 +218,48 @@ module Solargraph
206
218
  store.fqns_pins(qualify(namespace, context))
207
219
  end
208
220
 
209
- # Get a fully qualified namespace name. This method will start the search
210
- # in the specified context until it finds a match for the name.
221
+ # Determine fully qualified tag for a given tag used inside the
222
+ # definition of another tag ("context"). This method will start
223
+ # the search in the specified context until it finds a match for
224
+ # the tag.
225
+ #
226
+ # Does not recurse into qualifying the type parameters, but
227
+ # returns any which were passed in unchanged.
228
+ #
229
+ # @param tag [String, nil] The namespace to
230
+ # match, complete with generic parameters set to appropriate
231
+ # values if available
232
+ # @param context_tag [String] The context in which the tag was
233
+ # referenced; start from here to resolve the name
234
+ # @return [String, nil] fully qualified tag
235
+ def qualify tag, context_tag = ''
236
+ return tag if ['self', nil].include?(tag)
237
+ context_type = ComplexType.parse(context_tag)
238
+ type = ComplexType.parse(tag)
239
+ fqns = qualify_namespace(type.rooted_namespace, context_type.rooted_namespace)
240
+ return nil if fqns.nil?
241
+ fqns + type.substring
242
+ end
243
+
244
+ # Determine fully qualified namespace for a given namespace used
245
+ # inside the definition of another tag ("context"). This method
246
+ # will start the search in the specified context until it finds a
247
+ # match for the namespace.
211
248
  #
212
- # @param namespace [String, nil] The namespace to match
213
- # @param context [String] The context to search
214
- # @return [String, nil]
215
- def qualify namespace, context = ''
216
- return namespace if ['self', nil].include?(namespace)
217
- cached = cache.get_qualified_namespace(namespace, context)
249
+ # @param namespace [String, nil] The namespace to
250
+ # match
251
+ # @param context_namespace [String] The context namespace in which the
252
+ # tag was referenced; start from here to resolve the name
253
+ # @return [String, nil] fully qualified namespace
254
+ def qualify_namespace(namespace, context_namespace = '')
255
+ cached = cache.get_qualified_namespace(namespace, context_namespace)
218
256
  return cached.clone unless cached.nil?
219
257
  result = if namespace.start_with?('::')
220
258
  inner_qualify(namespace[2..-1], '', Set.new)
221
259
  else
222
- inner_qualify(namespace, context, Set.new)
260
+ inner_qualify(namespace, context_namespace, Set.new)
223
261
  end
224
- cache.set_qualified_namespace(namespace, context, result)
262
+ cache.set_qualified_namespace(namespace, context_namespace, result)
225
263
  result
226
264
  end
227
265
 
@@ -247,7 +285,7 @@ module Solargraph
247
285
  # Get an array of class variable pins for a namespace.
248
286
  #
249
287
  # @param namespace [String] A fully qualified namespace
250
- # @return [Array<Solargraph::Pin::ClassVariable>]
288
+ # @return [Enumerable<Solargraph::Pin::ClassVariable>]
251
289
  def get_class_variable_pins(namespace)
252
290
  prefer_non_nil_variables(store.get_class_variables(namespace))
253
291
  end
@@ -264,32 +302,33 @@ module Solargraph
264
302
 
265
303
  # Get an array of methods available in a particular context.
266
304
  #
267
- # @param fqns [String] The fully qualified namespace to search for methods
305
+ # @param rooted_tag [String] The fully qualified namespace to search for methods
268
306
  # @param scope [Symbol] :class or :instance
269
307
  # @param visibility [Array<Symbol>] :public, :protected, and/or :private
270
308
  # @param deep [Boolean] True to include superclasses, mixins, etc.
271
309
  # @return [Array<Solargraph::Pin::Method>]
272
- def get_methods fqns, scope: :instance, visibility: [:public], deep: true
273
- cached = cache.get_methods(fqns, scope, visibility, deep)
310
+ def get_methods rooted_tag, scope: :instance, visibility: [:public], deep: true
311
+ cached = cache.get_methods(rooted_tag, scope, visibility, deep)
274
312
  return cached.clone unless cached.nil?
275
313
  result = []
276
314
  skip = Set.new
277
- if fqns == ''
315
+ if rooted_tag == ''
278
316
  # @todo Implement domains
279
317
  implicit.domains.each do |domain|
280
318
  type = ComplexType.try_parse(domain)
281
319
  next if type.undefined?
282
320
  result.concat inner_get_methods(type.name, type.scope, visibility, deep, skip)
283
321
  end
284
- result.concat inner_get_methods(fqns, :class, visibility, deep, skip)
285
- result.concat inner_get_methods(fqns, :instance, visibility, deep, skip)
322
+ result.concat inner_get_methods(rooted_tag, :class, visibility, deep, skip)
323
+ result.concat inner_get_methods(rooted_tag, :instance, visibility, deep, skip)
286
324
  result.concat inner_get_methods('Kernel', :instance, visibility, deep, skip)
287
325
  else
288
- result.concat inner_get_methods(fqns, scope, visibility, deep, skip)
326
+ result.concat inner_get_methods(rooted_tag, scope, visibility, deep, skip)
289
327
  result.concat inner_get_methods('Kernel', :instance, [:public], deep, skip) if visibility.include?(:private)
328
+ result.concat inner_get_methods('Module', scope, visibility, deep, skip)
290
329
  end
291
330
  resolved = resolve_method_aliases(result, visibility)
292
- cache.set_methods(fqns, scope, visibility, deep, resolved)
331
+ cache.set_methods(rooted_tag, scope, visibility, deep, resolved)
293
332
  resolved
294
333
  end
295
334
 
@@ -327,27 +366,27 @@ module Solargraph
327
366
  visibility.push :protected
328
367
  visibility.push :private if internal
329
368
  end
330
- result.merge get_methods(type.namespace, scope: type.scope, visibility: visibility)
369
+ result.merge get_methods(type.tag, scope: type.scope, visibility: visibility)
331
370
  end
332
371
  end
333
372
  end
334
373
  result.to_a
335
374
  end
336
375
 
337
- # Get a stack of method pins for a method name in a namespace. The order
338
- # of the pins corresponds to the ancestry chain, with highest precedence
339
- # first.
376
+ # Get a stack of method pins for a method name in a potentially
377
+ # parameterized namespace. The order of the pins corresponds to
378
+ # the ancestry chain, with highest precedence first.
340
379
  #
341
380
  # @example
342
381
  # api_map.get_method_stack('Subclass', 'method_name')
343
382
  # #=> [ <Subclass#method_name pin>, <Superclass#method_name pin> ]
344
383
  #
345
- # @param fqns [String]
346
- # @param name [String]
384
+ # @param rooted_tag [String] Parameterized namespace, fully qualified
385
+ # @param name [String] Method name to look up
347
386
  # @param scope [Symbol] :instance or :class
348
387
  # @return [Array<Solargraph::Pin::Method>]
349
- def get_method_stack fqns, name, scope: :instance
350
- get_methods(fqns, scope: scope, visibility: [:private, :protected, :public]).select { |p| p.name == name }
388
+ def get_method_stack rooted_tag, name, scope: :instance
389
+ get_methods(rooted_tag, scope: scope, visibility: [:private, :protected, :public]).select { |p| p.name == name }
351
390
  end
352
391
 
353
392
  # Get an array of all suggestions that match the specified path.
@@ -355,7 +394,7 @@ module Solargraph
355
394
  # @deprecated Use #get_path_pins instead.
356
395
  #
357
396
  # @param path [String] The path to find
358
- # @return [Array<Solargraph::Pin::Base>]
397
+ # @return [Enumerable<Solargraph::Pin::Base>]
359
398
  def get_path_suggestions path
360
399
  return [] if path.nil?
361
400
  resolve_method_aliases store.get_path_pins(path)
@@ -364,7 +403,7 @@ module Solargraph
364
403
  # Get an array of pins that match the specified path.
365
404
  #
366
405
  # @param path [String]
367
- # @return [Array<Pin::Base>]
406
+ # @return [Enumerable<Pin::Base>]
368
407
  def get_path_pins path
369
408
  get_path_suggestions(path)
370
409
  end
@@ -377,15 +416,9 @@ module Solargraph
377
416
  # @param query [String] The text to match
378
417
  # @return [Array<String>]
379
418
  def search query
380
- rake_yard(store)
381
- found = []
382
- code_object_paths.each do |k|
383
- if (found.empty? || (query.include?('.') || query.include?('#')) || !(k.include?('.') || k.include?('#'))) &&
384
- k.downcase.include?(query.downcase)
385
- found.push k
386
- end
387
- end
388
- found
419
+ pins.map(&:path)
420
+ .compact
421
+ .select { |path| path.downcase.include?(query.downcase) }
389
422
  end
390
423
 
391
424
  # Get YARD documentation for the specified path.
@@ -393,13 +426,13 @@ module Solargraph
393
426
  # @example
394
427
  # api_map.document('String#split')
395
428
  #
429
+ # @todo This method is likely superfluous. Calling get_path_pins directly
430
+ # should be sufficient.
431
+ #
396
432
  # @param path [String] The path to find
397
- # @return [Array<YARD::CodeObjects::Base>]
433
+ # @return [Enumerable<Pin::Base>]
398
434
  def document path
399
- rake_yard(store)
400
- docs = []
401
- docs.push code_object_at(path) unless code_object_at(path).nil?
402
- docs
435
+ get_path_pins(path)
403
436
  end
404
437
 
405
438
  # Get an array of all symbols in the workspace that match the query.
@@ -425,6 +458,10 @@ module Solargraph
425
458
  # @return [SourceMap::Clip]
426
459
  def clip cursor
427
460
  raise FileNotFoundError, "ApiMap did not catalog #{cursor.filename}" unless source_map_hash.key?(cursor.filename)
461
+
462
+ # @todo Clip caches are disabled pending resolution of a stale cache bug
463
+ # cache.get_clip(cursor) ||
464
+ # SourceMap::Clip.new(self, cursor).tap { |clip| cache.set_clip(cursor, clip) }
428
465
  SourceMap::Clip.new(self, cursor)
429
466
  end
430
467
 
@@ -476,13 +513,15 @@ module Solargraph
476
513
  false
477
514
  end
478
515
 
479
- # Check if the host class includes the specified module.
516
+ # Check if the host class includes the specified module, ignoring
517
+ # type parameters used.
518
+ #
519
+ # @param host_ns [String] The class namesapce (no type parameters)
520
+ # @param module_ns [String] The module namespace (no type parameters)
480
521
  #
481
- # @param host [String] The class
482
- # @param mod [String] The module
483
522
  # @return [Boolean]
484
- def type_include?(host, mod)
485
- store.get_includes(host).include?(mod)
523
+ def type_include?(host_ns, module_ns)
524
+ store.get_includes(host_ns).map { |inc_tag| ComplexType.parse(inc_tag).name }.include?(module_ns)
486
525
  end
487
526
 
488
527
  private
@@ -492,14 +531,6 @@ module Solargraph
492
531
  # @return [Hash{String => SourceMap}]
493
532
  attr_reader :source_map_hash
494
533
 
495
- # @param library [String]
496
- # @return [RbsMap]
497
- def load_rbs_map library
498
- # map = RbsMap.load(library)
499
- # return map if map.resolved?
500
- RbsMap::StdlibMap.load(library)
501
- end
502
-
503
534
  # @return [ApiMap::Store]
504
535
  def store
505
536
  @store ||= Store.new
@@ -508,15 +539,19 @@ module Solargraph
508
539
  # @return [Solargraph::ApiMap::Cache]
509
540
  attr_reader :cache
510
541
 
511
- # @param fqns [String] A fully qualified namespace
542
+ # @param rooted_tag [String] A fully qualified namespace, with
543
+ # generic parameter values if applicable
512
544
  # @param scope [Symbol] :class or :instance
513
545
  # @param visibility [Array<Symbol>] :public, :protected, and/or :private
514
546
  # @param deep [Boolean]
515
547
  # @param skip [Set<String>]
516
548
  # @param no_core [Boolean] Skip core classes if true
517
549
  # @return [Array<Pin::Base>]
518
- def inner_get_methods fqns, scope, visibility, deep, skip, no_core = false
519
- return [] if no_core && fqns =~ /^(Object|BasicObject|Class|Module|Kernel)$/
550
+ def inner_get_methods rooted_tag, scope, visibility, deep, skip, no_core = false
551
+ rooted_type = ComplexType.parse(rooted_tag)
552
+ fqns = rooted_type.namespace
553
+ fqns_generic_params = rooted_type.all_params
554
+ return [] if no_core && fqns =~ /^(Object|BasicObject|Class|Module)$/
520
555
  reqstr = "#{fqns}|#{scope}|#{visibility.sort}|#{deep}"
521
556
  return [] if skip.include?(reqstr)
522
557
  skip.add reqstr
@@ -527,12 +562,33 @@ module Solargraph
527
562
  result.concat inner_get_methods(fqim, scope, visibility, deep, skip, true) unless fqim.nil?
528
563
  end
529
564
  end
530
- result.concat store.get_methods(fqns, scope: scope, visibility: visibility).sort{ |a, b| a.name <=> b.name }
565
+ # Store#get_methods doesn't know about full tags, just
566
+ # namespaces; resolving the generics in the method pins is this
567
+ # class' responsibility
568
+ raw_methods = store.get_methods(fqns, scope: scope, visibility: visibility).sort{ |a, b| a.name <=> b.name }
569
+ namespace_pin = store.get_path_pins(fqns).select{|p| p.is_a?(Pin::Namespace)}.first
570
+ methods = if rooted_tag != fqns
571
+ methods = raw_methods.map do |method_pin|
572
+ method_pin.resolve_generics(namespace_pin, rooted_type)
573
+ end
574
+ else
575
+ raw_methods
576
+ end
577
+ result.concat methods
531
578
  if deep
532
579
  if scope == :instance
533
- store.get_includes(fqns).reverse.each do |im|
534
- fqim = qualify(im, fqns)
535
- result.concat inner_get_methods(fqim, scope, visibility, deep, skip, true) unless fqim.nil?
580
+ store.get_includes(fqns).reverse.each do |include_tag|
581
+ rooted_include_tag = qualify(include_tag, rooted_tag)
582
+ # Ensure the types returned by the included methods are
583
+ # relative to the generics passed to the include. e.g.,
584
+ # Foo<String> might include Enumerable<String>
585
+ #
586
+ # @todo perform the same translation in the other areas
587
+ # here after adding a spec and handling things correctly
588
+ # in ApiMap::Store and RbsMap::Conversions
589
+ resolved_include_type = ComplexType.parse(rooted_include_tag).resolve_generics(namespace_pin, rooted_type)
590
+ methods = inner_get_methods(resolved_include_type.tag, scope, visibility, deep, skip, true)
591
+ result.concat methods
536
592
  end
537
593
  fqsc = qualify_superclass(fqns)
538
594
  unless fqsc.nil?
@@ -596,6 +652,8 @@ module Solargraph
596
652
  qualify namespace, context.split('::')[0..-2].join('::')
597
653
  end
598
654
 
655
+ # @param fqsub [String]
656
+ # @return [String, nil]
599
657
  def qualify_superclass fqsub
600
658
  sup = store.get_superclass(fqsub)
601
659
  return nil if sup.nil?
@@ -605,11 +663,12 @@ module Solargraph
605
663
  qualify(sup, parts.join('::'))
606
664
  end
607
665
 
608
- # @param name [String]
609
- # @param root [String]
610
- # @param skip [Set<String>]
611
- # @return [String, nil]
666
+ # @param name [String] Namespace to fully qualify
667
+ # @param root [String] The context to search
668
+ # @param skip [Set<String>] Contexts already searched
669
+ # @return [String, nil] Fully qualified ("rooted") namespace
612
670
  def inner_qualify name, root, skip
671
+ return name if name == ComplexType::GENERIC_TAG_NAME
613
672
  return nil if name.nil?
614
673
  return nil if skip.include?(root)
615
674
  skip.add root
@@ -659,8 +718,8 @@ module Solargraph
659
718
 
660
719
  # Sort an array of pins to put nil or undefined variables last.
661
720
  #
662
- # @param pins [Array<Solargraph::Pin::Base>]
663
- # @return [Array<Solargraph::Pin::Base>]
721
+ # @param pins [Enumerable<Solargraph::Pin::Base>]
722
+ # @return [Enumerable<Solargraph::Pin::Base>]
664
723
  def prefer_non_nil_variables pins
665
724
  result = []
666
725
  nil_pins = []
@@ -674,8 +733,8 @@ module Solargraph
674
733
  result + nil_pins
675
734
  end
676
735
 
677
- # @param pins [Array<Pin::Base>]
678
- # @param visibility [Array<Symbol>]
736
+ # @param pins [Enumerable<Pin::Base>]
737
+ # @param visibility [Enumerable<Symbol>]
679
738
  # @return [Array<Pin::Base>]
680
739
  def resolve_method_aliases pins, visibility = [:public, :private, :protected]
681
740
  result = []
@@ -692,7 +751,7 @@ module Solargraph
692
751
  def resolve_method_alias pin
693
752
  return pin if !pin.is_a?(Pin::MethodAlias) || @method_alias_stack.include?(pin.path)
694
753
  @method_alias_stack.push pin.path
695
- origin = get_method_stack(pin.full_context.namespace, pin.original, scope: pin.scope).first
754
+ origin = get_method_stack(pin.full_context.tag, pin.original, scope: pin.scope).first
696
755
  @method_alias_stack.pop
697
756
  return pin if origin.nil?
698
757
  args = {
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'set'
4
3
 
5
4
  module Solargraph
6
5
  # A container of source maps and workspace data to be cataloged in an ApiMap.
@@ -21,7 +20,8 @@ module Solargraph
21
20
  def initialize source_maps: [], workspace: Workspace.new, external_requires: []
22
21
  @source_maps = source_maps.to_set
23
22
  @workspace = workspace
24
- @external_requires = external_requires.to_set
23
+ @external_requires = external_requires.reject { |path| workspace.would_require?(path) }
24
+ .to_set
25
25
  end
26
26
  end
27
27
  end
@@ -1,4 +1,5 @@
1
1
  require 'fileutils'
2
+ require 'rbs'
2
3
 
3
4
  module Solargraph
4
5
  module Cache
@@ -14,7 +15,7 @@ module Solargraph
14
15
  File.join(Dir.home, '.cache', 'solargraph')
15
16
  end
16
17
 
17
- # The working directory for the current Ruby and Solargraph versions.
18
+ # The working directory for the current Ruby, RBS, and Solargraph versions.
18
19
  #
19
20
  # @return [String]
20
21
  def work_dir
@@ -23,9 +24,21 @@ module Solargraph
23
24
  File.join(base_dir, "ruby-#{RUBY_VERSION}", "rbs-#{RBS::VERSION}", "solargraph-#{Solargraph::VERSION}")
24
25
  end
25
26
 
27
+ # Append the given path to the current cache directory (`work_dir`).
28
+ #
29
+ # @example
30
+ # Cache.join('date-3.4.1.ser')
31
+ #
32
+ # @param path [Array<String>]
33
+ # @return [String]
34
+ def join *path
35
+ File.join(work_dir, *path)
36
+ end
37
+
38
+ # @param path [Array<String>]
26
39
  # @return [Array<Solargraph::Pin::Base>, nil]
27
40
  def load *path
28
- file = File.join(work_dir, *path)
41
+ file = join(*path)
29
42
  return nil unless File.file?(file)
30
43
  Marshal.load(File.read(file, mode: 'rb'))
31
44
  rescue StandardError => e
@@ -34,17 +47,28 @@ module Solargraph
34
47
  nil
35
48
  end
36
49
 
37
- # @return [Boolean]
50
+ def exist? *path
51
+ File.file? join(*path)
52
+ end
53
+
54
+ # @param path [Array<String>]
55
+ # @param pins [Array<Pin::Base>]
56
+ # @return [void]
38
57
  def save *path, pins
39
- return false if pins.empty?
40
58
  file = File.join(work_dir, *path)
41
59
  base = File.dirname(file)
42
60
  FileUtils.mkdir_p base unless File.directory?(base)
43
61
  ser = Marshal.dump(pins)
44
62
  File.write file, ser, mode: 'wb'
45
- true
46
63
  end
47
64
 
65
+ # @return [void]
66
+ # @param path [Array<String>]
67
+ def uncache *path
68
+ FileUtils.rm_rf File.join(work_dir, *path), secure: true
69
+ end
70
+
71
+ # @return [void]
48
72
  def clear
49
73
  FileUtils.rm_rf base_dir, secure: true
50
74
  end
@@ -2,9 +2,23 @@
2
2
 
3
3
  module Solargraph
4
4
  class ComplexType
5
- # Methods for accessing type data.
5
+ # Methods for accessing type data available from
6
+ # both ComplexType and UniqueType.
6
7
  #
8
+ # @abstract This mixin relies on these -
9
+ # instance variables:
10
+ # @name: String
11
+ # @subtypes: Array<ComplexType>
12
+ # @rooted: boolish
13
+ # methods:
14
+ # transform()
7
15
  module TypeMethods
16
+ # @!method transform(new_name = nil, &transform_type)
17
+ # @param new_name [String, nil]
18
+ # @yieldparam t [UniqueType]
19
+ # @yieldreturn [UniqueType]
20
+ # @return [UniqueType, nil]
21
+
8
22
  # @return [String]
9
23
  attr_reader :name
10
24
 
@@ -24,8 +38,7 @@ module Solargraph
24
38
 
25
39
  # @return [Boolean]
26
40
  def nil_type?
27
- @nil_type = (name.casecmp('nil') == 0) if @nil_type.nil?
28
- @nil_type
41
+ @nil_type ||= (name.casecmp('nil') == 0)
29
42
  end
30
43
 
31
44
  # @return [Boolean]
@@ -45,6 +58,22 @@ module Solargraph
45
58
  name == 'undefined'
46
59
  end
47
60
 
61
+ # @param generics_to_erase [Enumerable<String>]
62
+ # @return [self]
63
+ def erase_generics(generics_to_erase)
64
+ transform do |type|
65
+ if type.name == ComplexType::GENERIC_TAG_NAME
66
+ if type.all_params.length == 1 && generics_to_erase.include?(type.all_params.first.to_s)
67
+ ComplexType::UNDEFINED
68
+ else
69
+ type
70
+ end
71
+ else
72
+ type
73
+ end
74
+ end
75
+ end
76
+
48
77
  # @return [Boolean]
49
78
  def list_parameters?
50
79
  substring.start_with?('<')
@@ -80,12 +109,25 @@ module Solargraph
80
109
  end.call
81
110
  end
82
111
 
83
- # @return [Symbol] :class or :instance
112
+ # @return [String]
113
+ def rooted_namespace
114
+ return namespace unless rooted?
115
+ "::#{namespace}"
116
+ end
117
+
118
+ # @return [String]
119
+ def rooted_name
120
+ return name unless rooted?
121
+ "::#{name}"
122
+ end
123
+
124
+ # @return [::Symbol] :class or :instance
84
125
  def scope
85
126
  @scope ||= :instance if duck_type? || nil_type?
86
127
  @scope ||= (name == 'Class' || name == 'Module') && !subtypes.empty? ? :class : :instance
87
128
  end
88
129
 
130
+ # @param other [Object]
89
131
  def == other
90
132
  return false unless self.class == other.class
91
133
  tag == other.tag
@@ -99,7 +141,7 @@ module Solargraph
99
141
  #
100
142
  # @param api_map [ApiMap] The ApiMap that performs qualification
101
143
  # @param context [String] The namespace from which to resolve names
102
- # @return [ComplexType] The generated ComplexType
144
+ # @return [self, ComplexType, UniqueType] The generated ComplexType
103
145
  def qualify api_map, context = ''
104
146
  return self if name == GENERIC_TAG_NAME
105
147
  return ComplexType.new([self]) if duck_type? || void? || undefined?
@@ -110,13 +152,16 @@ module Solargraph
110
152
  return UniqueType::UNDEFINED
111
153
  end
112
154
  fqns = "::#{fqns}" # Ensure the resulting complex type is rooted
113
- ltypes = key_types.map { |t| t.qualify api_map, context }.uniq
114
- rtypes = value_types.map { |t| t.qualify api_map, context }.uniq
155
+ all_ltypes = key_types.map { |t| t.qualify api_map, context }.uniq
156
+ all_rtypes = value_types.map { |t| t.qualify api_map, context }
115
157
  if list_parameters?
158
+ rtypes = all_rtypes.uniq
116
159
  Solargraph::ComplexType.parse("#{fqns}<#{rtypes.map(&:tag).join(', ')}>")
117
160
  elsif fixed_parameters?
118
- Solargraph::ComplexType.parse("#{fqns}(#{rtypes.map(&:tag).join(', ')})")
161
+ Solargraph::ComplexType.parse("#{fqns}(#{all_rtypes.map(&:tag).join(', ')})")
119
162
  elsif hash_parameters?
163
+ ltypes = all_ltypes.uniq
164
+ rtypes = all_rtypes.uniq
120
165
  Solargraph::ComplexType.parse("#{fqns}{#{ltypes.map(&:tag).join(', ')} => #{rtypes.map(&:tag).join(', ')}}")
121
166
  else
122
167
  Solargraph::ComplexType.parse(fqns)