solargraph 0.54.0 → 0.54.5

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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +45 -0
  3. data/lib/solargraph/api_map/cache.rb +10 -1
  4. data/lib/solargraph/api_map/index.rb +167 -0
  5. data/lib/solargraph/api_map/store.rb +72 -121
  6. data/lib/solargraph/api_map.rb +94 -36
  7. data/lib/solargraph/bench.rb +17 -1
  8. data/lib/solargraph/complex_type/type_methods.rb +17 -11
  9. data/lib/solargraph/complex_type/unique_type.rb +93 -10
  10. data/lib/solargraph/complex_type.rb +68 -18
  11. data/lib/solargraph/convention.rb +1 -0
  12. data/lib/solargraph/doc_map.rb +1 -0
  13. data/lib/solargraph/equality.rb +33 -0
  14. data/lib/solargraph/language_server/host/message_worker.rb +48 -5
  15. data/lib/solargraph/language_server/host.rb +12 -11
  16. data/lib/solargraph/language_server/message/base.rb +19 -12
  17. data/lib/solargraph/language_server/message/initialize.rb +3 -1
  18. data/lib/solargraph/language_server/message/text_document/completion.rb +0 -3
  19. data/lib/solargraph/language_server/message/text_document/definition.rb +3 -3
  20. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +3 -3
  21. data/lib/solargraph/language_server/message/text_document/formatting.rb +4 -0
  22. data/lib/solargraph/language_server/message/text_document/hover.rb +1 -1
  23. data/lib/solargraph/language_server/message/text_document/type_definition.rb +3 -3
  24. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +2 -2
  25. data/lib/solargraph/language_server/progress.rb +19 -2
  26. data/lib/solargraph/library.rb +31 -41
  27. data/lib/solargraph/location.rb +21 -1
  28. data/lib/solargraph/parser/node_methods.rb +1 -1
  29. data/lib/solargraph/parser/node_processor.rb +1 -0
  30. data/lib/solargraph/parser/parser_gem/class_methods.rb +2 -6
  31. data/lib/solargraph/parser/parser_gem/node_methods.rb +3 -3
  32. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +23 -19
  33. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +8 -2
  34. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +1 -1
  35. data/lib/solargraph/parser.rb +2 -5
  36. data/lib/solargraph/pin/base.rb +41 -17
  37. data/lib/solargraph/pin/base_variable.rb +4 -3
  38. data/lib/solargraph/pin/block.rb +6 -26
  39. data/lib/solargraph/pin/callable.rb +147 -0
  40. data/lib/solargraph/pin/closure.rb +8 -3
  41. data/lib/solargraph/pin/common.rb +2 -6
  42. data/lib/solargraph/pin/conversions.rb +3 -2
  43. data/lib/solargraph/pin/instance_variable.rb +2 -2
  44. data/lib/solargraph/pin/method.rb +57 -31
  45. data/lib/solargraph/pin/namespace.rb +5 -5
  46. data/lib/solargraph/pin/parameter.rb +11 -12
  47. data/lib/solargraph/pin/proxy_type.rb +1 -1
  48. data/lib/solargraph/pin/signature.rb +3 -129
  49. data/lib/solargraph/pin.rb +4 -1
  50. data/lib/solargraph/position.rb +7 -0
  51. data/lib/solargraph/range.rb +9 -4
  52. data/lib/solargraph/rbs_map/conversions.rb +77 -38
  53. data/lib/solargraph/rbs_map/core_fills.rb +6 -6
  54. data/lib/solargraph/rbs_map.rb +1 -0
  55. data/lib/solargraph/shell.rb +19 -2
  56. data/lib/solargraph/source/chain/array.rb +7 -6
  57. data/lib/solargraph/source/chain/block_symbol.rb +1 -1
  58. data/lib/solargraph/source/chain/block_variable.rb +1 -1
  59. data/lib/solargraph/source/chain/call.rb +90 -55
  60. data/lib/solargraph/source/chain/hash.rb +5 -0
  61. data/lib/solargraph/source/chain/if.rb +5 -0
  62. data/lib/solargraph/source/chain/link.rb +26 -5
  63. data/lib/solargraph/source/chain/literal.rb +5 -0
  64. data/lib/solargraph/source/chain/or.rb +1 -1
  65. data/lib/solargraph/source/chain.rb +68 -30
  66. data/lib/solargraph/source/cursor.rb +3 -2
  67. data/lib/solargraph/source.rb +104 -86
  68. data/lib/solargraph/source_map/clip.rb +5 -5
  69. data/lib/solargraph/source_map/data.rb +30 -0
  70. data/lib/solargraph/source_map.rb +28 -16
  71. data/lib/solargraph/type_checker/rules.rb +6 -1
  72. data/lib/solargraph/type_checker.rb +15 -15
  73. data/lib/solargraph/version.rb +1 -1
  74. data/lib/solargraph/views/environment.erb +3 -5
  75. data/lib/solargraph/workspace/config.rb +7 -3
  76. data/lib/solargraph/workspace.rb +1 -1
  77. data/lib/solargraph/yard_map/mapper/to_constant.rb +1 -0
  78. data/lib/solargraph/yard_map/mapper/to_namespace.rb +1 -0
  79. data/lib/solargraph/yard_map/mapper.rb +1 -0
  80. data/lib/solargraph/yardoc.rb +1 -1
  81. data/lib/solargraph.rb +1 -0
  82. data/solargraph.gemspec +5 -5
  83. metadata +29 -25
@@ -5,13 +5,14 @@ require 'yard'
5
5
  require 'solargraph/yard_tags'
6
6
 
7
7
  module Solargraph
8
- # An aggregate provider for information about workspaces, sources, gems, and
8
+ # An aggregate provider for information about Workspaces, Sources, gems, and
9
9
  # the Ruby core.
10
10
  #
11
11
  class ApiMap
12
12
  autoload :Cache, 'solargraph/api_map/cache'
13
13
  autoload :SourceToYard, 'solargraph/api_map/source_to_yard'
14
14
  autoload :Store, 'solargraph/api_map/store'
15
+ autoload :Index, 'solargraph/api_map/index'
15
16
 
16
17
  # @return [Array<String>]
17
18
  attr_reader :unresolved_requires
@@ -48,6 +49,15 @@ module Solargraph
48
49
  equality_fields.hash
49
50
  end
50
51
 
52
+ def to_s
53
+ self.class.to_s
54
+ end
55
+
56
+ # avoid enormous dump
57
+ def inspect
58
+ to_s
59
+ end
60
+
51
61
  # @param pins [Array<Pin::Base>]
52
62
  # @return [self]
53
63
  def index pins
@@ -56,7 +66,7 @@ module Solargraph
56
66
  @source_map_hash = {}
57
67
  implicit.clear
58
68
  cache.clear
59
- @store = Store.new(@@core_map.pins + pins)
69
+ store.update @@core_map.pins, pins
60
70
  self
61
71
  end
62
72
 
@@ -75,10 +85,9 @@ module Solargraph
75
85
  # @param bench [Bench]
76
86
  # @return [self]
77
87
  def catalog bench
78
- old_api_hash = @source_map_hash&.values&.map(&:api_hash)
79
- need_to_uncache = (old_api_hash != bench.source_maps.map(&:api_hash))
80
- @source_map_hash = bench.source_maps.map { |s| [s.filename, s] }.to_h
81
- pins = bench.source_maps.flat_map(&:pins).flatten
88
+ @source_map_hash = bench.source_map_hash
89
+ iced_pins = bench.icebox.flat_map(&:pins)
90
+ live_pins = bench.live_map&.pins || []
82
91
  implicit.clear
83
92
  source_map_hash.each_value do |map|
84
93
  implicit.merge map.environ
@@ -87,17 +96,17 @@ module Solargraph
87
96
  if @unresolved_requires != unresolved_requires || @doc_map&.uncached_gemspecs&.any?
88
97
  @doc_map = DocMap.new(unresolved_requires, [], bench.workspace.rbs_collection_path) # @todo Implement gem preferences
89
98
  @unresolved_requires = unresolved_requires
90
- need_to_uncache = true
91
99
  end
92
- @store = Store.new(@@core_map.pins + @doc_map.pins + implicit.pins + pins)
93
- @cache.clear if need_to_uncache
94
-
100
+ @cache.clear if store.update(@@core_map.pins, @doc_map.pins, implicit.pins, iced_pins, live_pins)
95
101
  @missing_docs = [] # @todo Implement missing docs
96
102
  self
97
103
  end
98
104
 
105
+ # @todo need to model type def statement in chains as a symbol so
106
+ # that this overload of 'protected' will typecheck @sg-ignore
107
+ # @sg-ignore
99
108
  protected def equality_fields
100
- [self.class, @source_map_hash, implicit, @doc_map, @unresolved_requires, @missing_docs]
109
+ [self.class, @source_map_hash, implicit, @doc_map, @unresolved_requires]
101
110
  end
102
111
 
103
112
  # @return [::Array<Gem::Specification>]
@@ -183,7 +192,7 @@ module Solargraph
183
192
 
184
193
  # @return [Array<Solargraph::Pin::Base>]
185
194
  def pins
186
- store.pins
195
+ store.pins.clone.freeze
187
196
  end
188
197
 
189
198
  # An array of pins based on Ruby keywords (`if`, `end`, etc.).
@@ -250,18 +259,20 @@ module Solargraph
250
259
  # @param tag [String, nil] The namespace to
251
260
  # match, complete with generic parameters set to appropriate
252
261
  # values if available
253
- # @param context_tag [String] The context in which the tag was
254
- # referenced; start from here to resolve the name
262
+ # @param context_tag [String] The fully qualified context in which
263
+ # the tag was referenced; start from here to resolve the name.
264
+ # Should not be prefixed with '::'.
255
265
  # @return [String, nil] fully qualified tag
256
266
  def qualify tag, context_tag = ''
257
267
  return tag if ['self', nil].include?(tag)
258
- context_type = ComplexType.try_parse(context_tag)
268
+
269
+ context_type = ComplexType.parse(context_tag).force_rooted
259
270
  return unless context_type
260
271
 
261
272
  type = ComplexType.try_parse(tag)
262
273
  return unless type
263
274
 
264
- fqns = qualify_namespace(type.rooted_namespace, context_type.rooted_namespace)
275
+ fqns = qualify_namespace(type.rooted_namespace, context_type.namespace)
265
276
  return unless fqns
266
277
 
267
278
  fqns + type.substring
@@ -326,6 +337,11 @@ module Solargraph
326
337
  store.pins_by_class(Pin::GlobalVariable)
327
338
  end
328
339
 
340
+ # @return [Enumerable<Solargraph::Pin::Block>]
341
+ def get_block_pins
342
+ store.pins_by_class(Pin::Block)
343
+ end
344
+
329
345
  # Get an array of methods available in a particular context.
330
346
  #
331
347
  # @param rooted_tag [String] The fully qualified namespace to search for methods
@@ -334,8 +350,12 @@ module Solargraph
334
350
  # @param deep [Boolean] True to include superclasses, mixins, etc.
335
351
  # @return [Array<Solargraph::Pin::Method>]
336
352
  def get_methods rooted_tag, scope: :instance, visibility: [:public], deep: true
353
+ rooted_type = ComplexType.try_parse(rooted_tag)
354
+ fqns = rooted_type.namespace
355
+ namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
337
356
  cached = cache.get_methods(rooted_tag, scope, visibility, deep)
338
357
  return cached.clone unless cached.nil?
358
+ # @type [Array<Solargraph::Pin::Method>]
339
359
  result = []
340
360
  skip = Set.new
341
361
  if rooted_tag == ''
@@ -373,9 +393,12 @@ module Solargraph
373
393
  result.concat inner_get_methods('Kernel', :instance, [:public], deep, skip) if visibility.include?(:private)
374
394
  result.concat inner_get_methods('Module', scope, visibility, deep, skip) if scope == :module
375
395
  end
376
- resolved = resolve_method_aliases(result, visibility)
377
- cache.set_methods(rooted_tag, scope, visibility, deep, resolved)
378
- resolved
396
+ result = resolve_method_aliases(result, visibility)
397
+ if namespace_pin && rooted_tag != rooted_type.name
398
+ result = result.map { |method_pin| method_pin.resolve_generics(namespace_pin, rooted_type) }
399
+ end
400
+ cache.set_methods(rooted_tag, scope, visibility, deep, result)
401
+ result
379
402
  end
380
403
 
381
404
  # Get an array of method pins for a complex type.
@@ -431,8 +454,13 @@ module Solargraph
431
454
  # @param name [String] Method name to look up
432
455
  # @param scope [Symbol] :instance or :class
433
456
  # @return [Array<Solargraph::Pin::Method>]
434
- def get_method_stack rooted_tag, name, scope: :instance
435
- get_methods(rooted_tag, scope: scope, visibility: [:private, :protected, :public]).select { |p| p.name == name }
457
+ def get_method_stack rooted_tag, name, scope: :instance, visibility: [:private, :protected, :public], preserve_generics: false
458
+ rooted_type = ComplexType.parse(rooted_tag)
459
+ fqns = rooted_type.namespace
460
+ namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
461
+ methods = get_methods(rooted_tag, scope: scope, visibility: visibility).select { |p| p.name == name }
462
+ methods = erase_generics(namespace_pin, rooted_type, methods) unless preserve_generics
463
+ methods
436
464
  end
437
465
 
438
466
  # Get an array of all suggestions that match the specified path.
@@ -591,9 +619,10 @@ module Solargraph
591
619
  # @param no_core [Boolean] Skip core classes if true
592
620
  # @return [Array<Pin::Base>]
593
621
  def inner_get_methods rooted_tag, scope, visibility, deep, skip, no_core = false
594
- rooted_type = ComplexType.parse(rooted_tag)
622
+ rooted_type = ComplexType.parse(rooted_tag).force_rooted
595
623
  fqns = rooted_type.namespace
596
624
  fqns_generic_params = rooted_type.all_params
625
+ namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
597
626
  return [] if no_core && fqns =~ /^(Object|BasicObject|Class|Module)$/
598
627
  reqstr = "#{fqns}|#{scope}|#{visibility.sort}|#{deep}"
599
628
  return [] if skip.include?(reqstr)
@@ -608,15 +637,7 @@ module Solargraph
608
637
  # Store#get_methods doesn't know about full tags, just
609
638
  # namespaces; resolving the generics in the method pins is this
610
639
  # class' responsibility
611
- raw_methods = store.get_methods(fqns, scope: scope, visibility: visibility).sort{ |a, b| a.name <=> b.name }
612
- namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
613
- methods = if namespace_pin && rooted_tag != fqns
614
- methods = raw_methods.map do |method_pin|
615
- method_pin.resolve_generics(namespace_pin, rooted_type)
616
- end
617
- else
618
- raw_methods
619
- end
640
+ methods = store.get_methods(fqns, scope: scope, visibility: visibility).sort{ |a, b| a.name <=> b.name }
620
641
  result.concat methods
621
642
  if deep
622
643
  if scope == :instance
@@ -629,7 +650,7 @@ module Solargraph
629
650
  # @todo perform the same translation in the other areas
630
651
  # here after adding a spec and handling things correctly
631
652
  # in ApiMap::Store and RbsMap::Conversions
632
- resolved_include_type = ComplexType.parse(rooted_include_tag).resolve_generics(namespace_pin, rooted_type)
653
+ resolved_include_type = ComplexType.parse(rooted_include_tag).force_rooted.resolve_generics(namespace_pin, rooted_type)
633
654
  methods = inner_get_methods(resolved_include_type.tag, scope, visibility, deep, skip, true)
634
655
  result.concat methods
635
656
  end
@@ -690,7 +711,7 @@ module Solargraph
690
711
 
691
712
  # @param namespace [String]
692
713
  # @param context [String]
693
- # @return [String]
714
+ # @return [String, nil]
694
715
  def qualify_lower namespace, context
695
716
  qualify namespace, context.split('::')[0..-2].join('::')
696
717
  end
@@ -761,8 +782,8 @@ module Solargraph
761
782
 
762
783
  # Sort an array of pins to put nil or undefined variables last.
763
784
  #
764
- # @param pins [Enumerable<Solargraph::Pin::Base>]
765
- # @return [Enumerable<Solargraph::Pin::Base>]
785
+ # @param pins [Enumerable<Pin::BaseVariable>]
786
+ # @return [Enumerable<Pin::BaseVariable>]
766
787
  def prefer_non_nil_variables pins
767
788
  result = []
768
789
  nil_pins = []
@@ -802,11 +823,48 @@ module Solargraph
802
823
  name: pin.name,
803
824
  comments: origin.comments,
804
825
  scope: origin.scope,
826
+ # context: pin.context,
805
827
  visibility: origin.visibility,
806
828
  signatures: origin.signatures,
807
- attribute: origin.attribute?
829
+ attribute: origin.attribute?,
830
+ generics: origin.generics,
831
+ return_type: origin.return_type,
808
832
  }
809
833
  Pin::Method.new **args
810
834
  end
835
+
836
+ include Logging
837
+
838
+ private
839
+
840
+ # @param namespace_pin [Pin::Namespace]
841
+ # @param rooted_type [ComplexType]
842
+ # @param pins [Enumerable<Pin::Base>]
843
+ # @return [Array<Pin::Base>]
844
+ def erase_generics(namespace_pin, rooted_type, pins)
845
+ return pins unless should_erase_generics_when_done?(namespace_pin, rooted_type)
846
+
847
+ logger.debug("Erasing generics on namespace_pin=#{namespace_pin} / rooted_type=#{rooted_type}")
848
+ pins.map do |method_pin|
849
+ method_pin.erase_generics(namespace_pin.generics)
850
+ end
851
+ end
852
+
853
+ # @param namespace_pin [Pin::Namespace]
854
+ # @param rooted_type [ComplexType]
855
+ def should_erase_generics_when_done?(namespace_pin, rooted_type)
856
+ has_generics?(namespace_pin) && !can_resolve_generics?(namespace_pin, rooted_type)
857
+ end
858
+
859
+ # @param namespace_pin [Pin::Namespace]
860
+ def has_generics?(namespace_pin)
861
+ namespace_pin && !namespace_pin.generics.empty?
862
+ end
863
+
864
+ # @param namespace_pin [Pin::Namespace]
865
+ # @param rooted_type [ComplexType]
866
+ def can_resolve_generics?(namespace_pin, rooted_type)
867
+ has_generics?(namespace_pin) && !rooted_type.all_params.empty?
868
+ end
811
869
  end
812
870
  end
@@ -11,18 +11,34 @@ module Solargraph
11
11
  # @return [Workspace]
12
12
  attr_reader :workspace
13
13
 
14
+ # @return [SourceMap]
15
+ attr_reader :live_map
16
+
14
17
  # @return [Set<String>]
15
18
  attr_reader :external_requires
16
19
 
17
20
  # @param source_maps [Array<SourceMap>, Set<SourceMap>]
18
21
  # @param workspace [Workspace]
22
+ # @param live_map [SourceMap, nil]
19
23
  # @param external_requires [Array<String>, Set<String>]
20
- def initialize source_maps: [], workspace: Workspace.new, external_requires: []
24
+ def initialize source_maps: [], workspace: Workspace.new, live_map: nil, external_requires: []
21
25
  @source_maps = source_maps.to_set
22
26
  @workspace = workspace
27
+ @live_map = live_map
23
28
  @external_requires = external_requires.reject { |path| workspace.would_require?(path) }
24
29
  .compact
25
30
  .to_set
26
31
  end
32
+
33
+ # @return [Hash{String => SourceMap}]
34
+ def source_map_hash
35
+ # @todo Work around #to_h bug in current Ruby head (3.5) with #map#to_h
36
+ @source_map_hash ||= source_maps.map { |s| [s.filename, s] }
37
+ .to_h
38
+ end
39
+
40
+ def icebox
41
+ @icebox ||= (source_maps - [live_map])
42
+ end
27
43
  end
28
44
  end
@@ -12,12 +12,20 @@ module Solargraph
12
12
  # @rooted: boolish
13
13
  # methods:
14
14
  # transform()
15
+ # all_params()
16
+ # rooted?()
17
+ # can_root_name?()
15
18
  module TypeMethods
16
19
  # @!method transform(new_name = nil, &transform_type)
17
20
  # @param new_name [String, nil]
18
21
  # @yieldparam t [UniqueType]
19
22
  # @yieldreturn [UniqueType]
20
23
  # @return [UniqueType, nil]
24
+ # @!method all_params
25
+ # @return [Array<ComplexType>]
26
+ # @!method rooted?
27
+ # @!method can_root_name?(name_to_check = nil)
28
+ # @param name_to_check [String, nil]
21
29
 
22
30
  # @return [String]
23
31
  attr_reader :name
@@ -45,11 +53,6 @@ module Solargraph
45
53
  @nil_type ||= (name.casecmp('nil') == 0)
46
54
  end
47
55
 
48
- # @return [Boolean]
49
- def parameters?
50
- !substring.empty?
51
- end
52
-
53
56
  def tuple?
54
57
  @tuple_type ||= (name == 'Tuple') || (name == 'Array' && subtypes.length >= 1 && fixed_parameters?)
55
58
  end
@@ -126,15 +129,22 @@ module Solargraph
126
129
  end.call
127
130
  end
128
131
 
132
+ def namespace_type
133
+ return ComplexType.parse('::Object') if duck_type?
134
+ return ComplexType.parse('::NilClass') if nil_type?
135
+ return subtypes.first if (name == 'Class' || name == 'Module') && !subtypes.empty?
136
+ self
137
+ end
138
+
129
139
  # @return [String]
130
140
  def rooted_namespace
131
- return namespace unless rooted?
141
+ return namespace unless rooted? && can_root_name?(namespace)
132
142
  "::#{namespace}"
133
143
  end
134
144
 
135
145
  # @return [String]
136
146
  def rooted_name
137
- return name unless rooted?
147
+ return name unless @rooted && can_root_name?
138
148
  "::#{name}"
139
149
  end
140
150
 
@@ -177,10 +187,6 @@ module Solargraph
177
187
  tag == other.tag
178
188
  end
179
189
 
180
- def rooted?
181
- @rooted
182
- end
183
-
184
190
  # Generate a ComplexType that fully qualifies this type's namespaces.
185
191
  #
186
192
  # @param api_map [ApiMap] The ApiMap that performs qualification
@@ -7,9 +7,15 @@ module Solargraph
7
7
  #
8
8
  class UniqueType
9
9
  include TypeMethods
10
+ include Equality
10
11
 
11
12
  attr_reader :all_params, :subtypes, :key_types
12
13
 
14
+ # @sg-ignore Fix "Not enough arguments to Module#protected"
15
+ protected def equality_fields
16
+ [@name, @all_params, @subtypes, @key_types]
17
+ end
18
+
13
19
  # Create a UniqueType with the specified name and an optional substring.
14
20
  # The substring is the parameter section of a parametrized type, e.g.,
15
21
  # for the type `Array<String>`, the name is `Array` and the substring is
@@ -26,6 +32,8 @@ module Solargraph
26
32
  if name.start_with?('::')
27
33
  name = name[2..-1]
28
34
  rooted = true
35
+ elsif !can_root_name?(name)
36
+ rooted = true
29
37
  else
30
38
  rooted = false
31
39
  end
@@ -63,7 +71,7 @@ module Solargraph
63
71
  if parameters_type.nil?
64
72
  raise "You must supply parameters_type if you provide parameters" unless key_types.empty? && subtypes.empty?
65
73
  end
66
- raise "Please remove leading :: and set rooted instead - #{name}" if name.start_with?('::')
74
+ raise "Please remove leading :: and set rooted instead - #{name.inspect}" if name.start_with?('::')
67
75
  @name = name
68
76
  @key_types = key_types
69
77
  @subtypes = subtypes
@@ -78,6 +86,24 @@ module Solargraph
78
86
  tag
79
87
  end
80
88
 
89
+ def eql?(other)
90
+ self.class == other.class &&
91
+ @name == other.name &&
92
+ @key_types == other.key_types &&
93
+ @subtypes == other.subtypes &&
94
+ @rooted == other.rooted? &&
95
+ @all_params == other.all_params &&
96
+ @parameters_type == other.parameters_type
97
+ end
98
+
99
+ def ==(other)
100
+ eql?(other)
101
+ end
102
+
103
+ def hash
104
+ [self.class, @name, @key_types, @sub_types, @rooted, @all_params, @parameters_type].hash
105
+ end
106
+
81
107
  # @return [Array<UniqueType>]
82
108
  def items
83
109
  [self]
@@ -92,6 +118,10 @@ module Solargraph
92
118
  end
93
119
  end
94
120
 
121
+ def desc
122
+ rooted_tags
123
+ end
124
+
95
125
  # @return [String]
96
126
  def to_rbs
97
127
  if duck_type?
@@ -108,7 +138,11 @@ module Solargraph
108
138
  # tuples don't have a name; they're just [foo, bar, baz].
109
139
  if substring == '()'
110
140
  # but there are no zero element tuples, so we go with an array
111
- 'Array[]'
141
+ if rooted?
142
+ '::Array[]'
143
+ else
144
+ 'Array[]'
145
+ end
112
146
  else
113
147
  # already generated surrounded by []
114
148
  parameters_as_rbs
@@ -211,7 +245,17 @@ module Solargraph
211
245
  if t.name == GENERIC_TAG_NAME
212
246
  idx = definitions.generics.index(t.subtypes.first&.name)
213
247
  next t if idx.nil?
214
- context_type.all_params[idx] || ComplexType::UNDEFINED
248
+ if context_type.parameters_type == :hash
249
+ if idx == 0
250
+ next ComplexType.new(context_type.key_types)
251
+ elsif idx == 1
252
+ next ComplexType.new(context_type.subtypes)
253
+ else
254
+ next ComplexType::UNDEFINED
255
+ end
256
+ else
257
+ context_type.all_params[idx] || ComplexType::UNDEFINED
258
+ end
215
259
  else
216
260
  t
217
261
  end
@@ -238,6 +282,7 @@ module Solargraph
238
282
  # @return [self]
239
283
  def recreate(new_name: nil, make_rooted: nil, new_key_types: nil, new_subtypes: nil)
240
284
  raise "Please remove leading :: and set rooted instead - #{new_name}" if new_name&.start_with?('::')
285
+
241
286
  new_name ||= name
242
287
  new_key_types ||= @key_types
243
288
  new_subtypes ||= @subtypes
@@ -278,17 +323,26 @@ module Solargraph
278
323
  new_key_types = @key_types.flat_map { |ct| ct.items.map { |ut| ut.transform(&transform_type) } }
279
324
  new_subtypes = @subtypes.flat_map { |ct| ct.items.map { |ut| ut.transform(&transform_type) } }
280
325
  end
281
- new_type = recreate(new_name: new_name || name, new_key_types: new_key_types, new_subtypes: new_subtypes)
326
+ new_type = recreate(new_name: new_name || name, new_key_types: new_key_types, new_subtypes: new_subtypes, make_rooted: @rooted)
282
327
  yield new_type
283
328
  end
284
329
 
285
- # Transform references to the 'self' type to the specified concrete namespace
286
- # @param dst [String]
287
- # @return [UniqueType]
288
- def self_to dst
330
+ # Generate a ComplexType that fully qualifies this type's namespaces.
331
+ #
332
+ # @param api_map [ApiMap] The ApiMap that performs qualification
333
+ # @param context [String] The namespace from which to resolve names
334
+ # @return [self, ComplexType, UniqueType] The generated ComplexType
335
+ def qualify api_map, context = ''
289
336
  transform do |t|
290
- next t if t.name != 'self'
291
- t.recreate(new_name: dst, new_key_types: [], new_subtypes: [])
337
+ next t if t.name == GENERIC_TAG_NAME
338
+ next t if t.duck_type? || t.void? || t.undefined?
339
+ recon = (t.rooted? ? '' : context)
340
+ fqns = api_map.qualify(t.name, recon)
341
+ if fqns.nil?
342
+ next UniqueType::BOOLEAN if t.tag == 'Boolean'
343
+ next UniqueType::UNDEFINED
344
+ end
345
+ t.recreate(new_name: fqns, make_rooted: true)
292
346
  end
293
347
  end
294
348
 
@@ -296,6 +350,35 @@ module Solargraph
296
350
  @name == 'self' || @key_types.any?(&:selfy?) || @subtypes.any?(&:selfy?)
297
351
  end
298
352
 
353
+ # @param dst [ComplexType]
354
+ # @return [self]
355
+ def self_to_type dst
356
+ object_type_dst = dst.reduce_class_type
357
+ transform do |t|
358
+ next t if t.name != 'self'
359
+ object_type_dst
360
+ end
361
+ end
362
+
363
+ def all_rooted?
364
+ return true if name == GENERIC_TAG_NAME
365
+ rooted? && all_params.all?(&:rooted?)
366
+ end
367
+
368
+ def rooted?
369
+ !can_root_name? || @rooted
370
+ end
371
+
372
+ def can_root_name?(name_to_check = name)
373
+ self.class.can_root_name?(name_to_check)
374
+ end
375
+
376
+ # @param name [String]
377
+ def self.can_root_name?(name)
378
+ # name is not lowercase
379
+ !name.empty? && name != name.downcase
380
+ end
381
+
299
382
  UNDEFINED = UniqueType.new('undefined', rooted: false)
300
383
  BOOLEAN = UniqueType.new('Boolean', rooted: true)
301
384
  end