solargraph 0.54.0 → 0.54.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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -0
  3. data/lib/solargraph/api_map/store.rb +8 -4
  4. data/lib/solargraph/api_map.rb +74 -23
  5. data/lib/solargraph/complex_type/type_methods.rb +17 -11
  6. data/lib/solargraph/complex_type/unique_type.rb +72 -9
  7. data/lib/solargraph/complex_type.rb +66 -17
  8. data/lib/solargraph/language_server/host/message_worker.rb +45 -5
  9. data/lib/solargraph/language_server/host.rb +10 -10
  10. data/lib/solargraph/language_server/message/base.rb +18 -11
  11. data/lib/solargraph/language_server/message/text_document/completion.rb +0 -3
  12. data/lib/solargraph/language_server/message/text_document/definition.rb +3 -3
  13. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +3 -3
  14. data/lib/solargraph/language_server/message/text_document/hover.rb +1 -1
  15. data/lib/solargraph/language_server/message/text_document/type_definition.rb +3 -3
  16. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +2 -2
  17. data/lib/solargraph/language_server/progress.rb +19 -2
  18. data/lib/solargraph/library.rb +29 -39
  19. data/lib/solargraph/location.rb +14 -1
  20. data/lib/solargraph/parser/parser_gem/node_chainer.rb +13 -7
  21. data/lib/solargraph/parser/parser_gem/node_methods.rb +1 -1
  22. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +23 -19
  23. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +2 -2
  24. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +8 -2
  25. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +1 -1
  26. data/lib/solargraph/parser.rb +2 -5
  27. data/lib/solargraph/pin/base.rb +16 -3
  28. data/lib/solargraph/pin/base_variable.rb +1 -1
  29. data/lib/solargraph/pin/block.rb +6 -26
  30. data/lib/solargraph/pin/callable.rb +147 -0
  31. data/lib/solargraph/pin/closure.rb +8 -3
  32. data/lib/solargraph/pin/common.rb +2 -6
  33. data/lib/solargraph/pin/conversions.rb +3 -2
  34. data/lib/solargraph/pin/instance_variable.rb +2 -2
  35. data/lib/solargraph/pin/method.rb +51 -31
  36. data/lib/solargraph/pin/namespace.rb +4 -4
  37. data/lib/solargraph/pin/parameter.rb +9 -11
  38. data/lib/solargraph/pin/proxy_type.rb +1 -1
  39. data/lib/solargraph/pin/signature.rb +3 -129
  40. data/lib/solargraph/pin.rb +4 -1
  41. data/lib/solargraph/range.rb +2 -4
  42. data/lib/solargraph/rbs_map/conversions.rb +76 -37
  43. data/lib/solargraph/rbs_map/core_fills.rb +6 -6
  44. data/lib/solargraph/shell.rb +17 -2
  45. data/lib/solargraph/source/chain/array.rb +6 -5
  46. data/lib/solargraph/source/chain/block_symbol.rb +1 -1
  47. data/lib/solargraph/source/chain/block_variable.rb +1 -1
  48. data/lib/solargraph/source/chain/call.rb +81 -51
  49. data/lib/solargraph/source/chain/link.rb +9 -0
  50. data/lib/solargraph/source/chain/or.rb +1 -1
  51. data/lib/solargraph/source/chain.rb +41 -17
  52. data/lib/solargraph/source/cursor.rb +14 -2
  53. data/lib/solargraph/source.rb +102 -85
  54. data/lib/solargraph/source_map/clip.rb +4 -4
  55. data/lib/solargraph/source_map/data.rb +30 -0
  56. data/lib/solargraph/source_map.rb +28 -16
  57. data/lib/solargraph/type_checker/rules.rb +6 -1
  58. data/lib/solargraph/type_checker.rb +7 -7
  59. data/lib/solargraph/version.rb +1 -1
  60. data/lib/solargraph/views/environment.erb +3 -5
  61. data/solargraph.gemspec +4 -4
  62. metadata +20 -24
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 39ad27afe3afb59299f8701f9a7ea3f25a9f95379ef68e53ac0e22eb60f50e21
4
- data.tar.gz: 1f01a9d5cdf4aec5b50c245a25b1f7d83e5d6762e5b8f65bc9ce3555654e21ed
3
+ metadata.gz: 17844968917df55d45751cbe82fa1f59e72f4dbfe7ecce04f73dbccbc1ec2aeb
4
+ data.tar.gz: 1b86c653e3b5c145f1b0aa9c614f136bd6d75c305aeac680f0cfe992ca4ff3dc
5
5
  SHA512:
6
- metadata.gz: 788f17e92a54e782ea1dcf03cb44245df823ba33b4fe866d1afa5d06c7171b40a4e1f93c97c8b26c13b58be7478b9b07cd75d6735da65ecef21ab6a4a61dbb0e
7
- data.tar.gz: 742f43fcdce4a592a80ebe8e3acbd020f39f569cf323621a527e626ae8c6154f7dedb32739b8b62f38e98997ed414ec30471160332ecfb4a2fe5d5052e871333
6
+ metadata.gz: 44d428aa78d2d82bcfb97b650acd72aaf904666702a59e13a87f1a4c4c8bb2fa171afe6f502fb7327a67f12854a3953fccee1fde95f75fc5cf4b4f8742683e8e
7
+ data.tar.gz: ce37d473c9e3b8decd9c97c868fb66ef8441e27aeb10b557abf345bba1ad9c96c8dc5657127ea9680dfa60f59da7663f25ccffed412d5683b272ea3dbf823f76
data/CHANGELOG.md CHANGED
@@ -1,3 +1,30 @@
1
+ ## 0.54.2 - April 28, 2025
2
+ - Resolve generics correctly on mixin inclusion (#898)
3
+ - Pick correct String#split overload (#905)
4
+ - Fix type sent into YARD method (#912)
5
+ - Early CancelRequest handling (#914)
6
+ - Destructure partial yield types (#915)
7
+ - Dependency versions (#916)
8
+
9
+ ## 0.54.1 - April 26, 2025
10
+ - Retire more RubyVM-specific code (#797)
11
+ - Add additional docs for key classes, modules and methods (#802)
12
+ - Populate location information from RBS files (#768)
13
+ - Consolidate parameter handling into Pin::Callable (#844)
14
+ - Adjust local variable presence to start after assignment, not before (#864)
15
+ - Resolve params from ref tags (#872)
16
+ - Reduce use of ComplexType.parse() to preserve rooted? information (#870)
17
+ - Ensure yield return types are qualified (#886)
18
+ - Understand type of 'def foo; @foo ||= bar; end' reader methods (#888)
19
+ - Improvements to #inspect output on pins and chains (#895)
20
+ - Block method resolution improvements (#885)
21
+ - Understand mass assignment into instance variables (#893)
22
+ - Library sync and cache invalidation (#903)
23
+ - Handle super and yield scenarios from blocks (#891)
24
+ - Allow core and stdlib documentation to be uncached (#899)
25
+ - Surface variable names in LSP, e.g., textDocument/hover (#910)
26
+ - Keep idle progress notifications alive (#911)
27
+
1
28
  ## 0.54.0 - April 14, 2025
2
29
  - Add support for simple block argument destructuring (#821)
3
30
  - Benchmark the typecheck command (#852)
@@ -3,6 +3,9 @@
3
3
 
4
4
  module Solargraph
5
5
  class ApiMap
6
+ # Queryable collection of Pins representing a Workspace, gems and the Ruby
7
+ # core.
8
+ #
6
9
  class Store
7
10
  # @return [Array<Solargraph::Pin::Base>]
8
11
  attr_reader :pins
@@ -35,6 +38,7 @@ module Solargraph
35
38
  # @param fqns [String]
36
39
  # @return [String, nil]
37
40
  def get_superclass fqns
41
+ raise "Do not prefix fully qualified namespaces with '::' - #{fqns.inspect}" if fqns.start_with?('::')
38
42
  return superclass_references[fqns].first if superclass_references.key?(fqns)
39
43
  return 'Object' if fqns != 'BasicObject' && namespace_exists?(fqns)
40
44
  return 'Object' if fqns == 'Boolean'
@@ -100,7 +104,7 @@ module Solargraph
100
104
  @namespaces ||= Set.new
101
105
  end
102
106
 
103
- # @return [Array<Solargraph::Pin::Base>]
107
+ # @return [Enumerable<Solargraph::Pin::Base>]
104
108
  def namespace_pins
105
109
  pins_by_class(Solargraph::Pin::Namespace)
106
110
  end
@@ -146,7 +150,7 @@ module Solargraph
146
150
 
147
151
  # @generic T
148
152
  # @param klass [Class<T>]
149
- # @return [Array<T>]
153
+ # @return [Set<T>]
150
154
  def pins_by_class klass
151
155
  # @type [Set<Solargraph::Pin::Base>]
152
156
  s = Set.new
@@ -280,8 +284,8 @@ module Solargraph
280
284
  get_path_pins(pin.path.sub(/#initialize/, '.new')).first
281
285
  end
282
286
  (ovr.tags.map(&:tag_name) + ovr.delete).uniq.each do |tag|
283
- pin.docstring.delete_tags tag.to_sym
284
- new_pin.docstring.delete_tags tag.to_sym if new_pin
287
+ pin.docstring.delete_tags tag
288
+ new_pin.docstring.delete_tags tag if new_pin
285
289
  end
286
290
  ovr.tags.each do |tag|
287
291
  pin.docstring.add_tag(tag)
@@ -5,7 +5,7 @@ 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
@@ -96,6 +96,9 @@ module Solargraph
96
96
  self
97
97
  end
98
98
 
99
+ # @todo need to model type def statement in chains as a symbol so
100
+ # that this overload of 'protected' will typecheck @sg-ignore
101
+ # @sg-ignore
99
102
  protected def equality_fields
100
103
  [self.class, @source_map_hash, implicit, @doc_map, @unresolved_requires, @missing_docs]
101
104
  end
@@ -250,18 +253,20 @@ module Solargraph
250
253
  # @param tag [String, nil] The namespace to
251
254
  # match, complete with generic parameters set to appropriate
252
255
  # values if available
253
- # @param context_tag [String] The context in which the tag was
254
- # referenced; start from here to resolve the name
256
+ # @param context_tag [String] The fully qualified context in which
257
+ # the tag was referenced; start from here to resolve the name.
258
+ # Should not be prefixed with '::'.
255
259
  # @return [String, nil] fully qualified tag
256
260
  def qualify tag, context_tag = ''
257
261
  return tag if ['self', nil].include?(tag)
258
- context_type = ComplexType.try_parse(context_tag)
262
+
263
+ context_type = ComplexType.parse(context_tag).force_rooted
259
264
  return unless context_type
260
265
 
261
266
  type = ComplexType.try_parse(tag)
262
267
  return unless type
263
268
 
264
- fqns = qualify_namespace(type.rooted_namespace, context_type.rooted_namespace)
269
+ fqns = qualify_namespace(type.rooted_namespace, context_type.namespace)
265
270
  return unless fqns
266
271
 
267
272
  fqns + type.substring
@@ -326,6 +331,11 @@ module Solargraph
326
331
  store.pins_by_class(Pin::GlobalVariable)
327
332
  end
328
333
 
334
+ # @return [Enumerable<Solargraph::Pin::Block>]
335
+ def get_block_pins
336
+ store.pins_by_class(Pin::Block)
337
+ end
338
+
329
339
  # Get an array of methods available in a particular context.
330
340
  #
331
341
  # @param rooted_tag [String] The fully qualified namespace to search for methods
@@ -334,6 +344,9 @@ module Solargraph
334
344
  # @param deep [Boolean] True to include superclasses, mixins, etc.
335
345
  # @return [Array<Solargraph::Pin::Method>]
336
346
  def get_methods rooted_tag, scope: :instance, visibility: [:public], deep: true
347
+ rooted_type = ComplexType.try_parse(rooted_tag)
348
+ fqns = rooted_type.namespace
349
+ namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
337
350
  cached = cache.get_methods(rooted_tag, scope, visibility, deep)
338
351
  return cached.clone unless cached.nil?
339
352
  result = []
@@ -373,9 +386,12 @@ module Solargraph
373
386
  result.concat inner_get_methods('Kernel', :instance, [:public], deep, skip) if visibility.include?(:private)
374
387
  result.concat inner_get_methods('Module', scope, visibility, deep, skip) if scope == :module
375
388
  end
376
- resolved = resolve_method_aliases(result, visibility)
377
- cache.set_methods(rooted_tag, scope, visibility, deep, resolved)
378
- resolved
389
+ result = resolve_method_aliases(result, visibility)
390
+ if namespace_pin && rooted_tag != rooted_type.name
391
+ result = result.map { |method_pin| method_pin.resolve_generics(namespace_pin, rooted_type) }
392
+ end
393
+ cache.set_methods(rooted_tag, scope, visibility, deep, result)
394
+ result
379
395
  end
380
396
 
381
397
  # Get an array of method pins for a complex type.
@@ -431,8 +447,13 @@ module Solargraph
431
447
  # @param name [String] Method name to look up
432
448
  # @param scope [Symbol] :instance or :class
433
449
  # @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 }
450
+ def get_method_stack rooted_tag, name, scope: :instance, visibility: [:private, :protected, :public], preserve_generics: false
451
+ rooted_type = ComplexType.parse(rooted_tag)
452
+ fqns = rooted_type.namespace
453
+ namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
454
+ methods = get_methods(rooted_tag, scope: scope, visibility: visibility).select { |p| p.name == name }
455
+ methods = erase_generics(namespace_pin, rooted_type, methods) unless preserve_generics
456
+ methods
436
457
  end
437
458
 
438
459
  # Get an array of all suggestions that match the specified path.
@@ -591,9 +612,10 @@ module Solargraph
591
612
  # @param no_core [Boolean] Skip core classes if true
592
613
  # @return [Array<Pin::Base>]
593
614
  def inner_get_methods rooted_tag, scope, visibility, deep, skip, no_core = false
594
- rooted_type = ComplexType.parse(rooted_tag)
615
+ rooted_type = ComplexType.parse(rooted_tag).force_rooted
595
616
  fqns = rooted_type.namespace
596
617
  fqns_generic_params = rooted_type.all_params
618
+ namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
597
619
  return [] if no_core && fqns =~ /^(Object|BasicObject|Class|Module)$/
598
620
  reqstr = "#{fqns}|#{scope}|#{visibility.sort}|#{deep}"
599
621
  return [] if skip.include?(reqstr)
@@ -608,15 +630,7 @@ module Solargraph
608
630
  # Store#get_methods doesn't know about full tags, just
609
631
  # namespaces; resolving the generics in the method pins is this
610
632
  # 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
633
+ methods = store.get_methods(fqns, scope: scope, visibility: visibility).sort{ |a, b| a.name <=> b.name }
620
634
  result.concat methods
621
635
  if deep
622
636
  if scope == :instance
@@ -629,7 +643,7 @@ module Solargraph
629
643
  # @todo perform the same translation in the other areas
630
644
  # here after adding a spec and handling things correctly
631
645
  # in ApiMap::Store and RbsMap::Conversions
632
- resolved_include_type = ComplexType.parse(rooted_include_tag).resolve_generics(namespace_pin, rooted_type)
646
+ resolved_include_type = ComplexType.parse(rooted_include_tag).force_rooted.resolve_generics(namespace_pin, rooted_type)
633
647
  methods = inner_get_methods(resolved_include_type.tag, scope, visibility, deep, skip, true)
634
648
  result.concat methods
635
649
  end
@@ -690,7 +704,7 @@ module Solargraph
690
704
 
691
705
  # @param namespace [String]
692
706
  # @param context [String]
693
- # @return [String]
707
+ # @return [String, nil]
694
708
  def qualify_lower namespace, context
695
709
  qualify namespace, context.split('::')[0..-2].join('::')
696
710
  end
@@ -802,11 +816,48 @@ module Solargraph
802
816
  name: pin.name,
803
817
  comments: origin.comments,
804
818
  scope: origin.scope,
819
+ # context: pin.context,
805
820
  visibility: origin.visibility,
806
821
  signatures: origin.signatures,
807
- attribute: origin.attribute?
822
+ attribute: origin.attribute?,
823
+ generics: origin.generics,
824
+ return_type: origin.return_type,
808
825
  }
809
826
  Pin::Method.new **args
810
827
  end
828
+
829
+ include Logging
830
+
831
+ private
832
+
833
+ # @param namespace_pin [Pin::Namespace]
834
+ # @param rooted_type [ComplexType]
835
+ # @param pins [Enumerable<Pin::Base>]
836
+ # @return [Array<Pin::Base>]
837
+ def erase_generics(namespace_pin, rooted_type, pins)
838
+ return pins unless should_erase_generics_when_done?(namespace_pin, rooted_type)
839
+
840
+ logger.debug("Erasing generics on namespace_pin=#{namespace_pin} / rooted_type=#{rooted_type}")
841
+ pins.map do |method_pin|
842
+ method_pin.erase_generics(namespace_pin.generics)
843
+ end
844
+ end
845
+
846
+ # @param namespace_pin [Pin::Namespace]
847
+ # @param rooted_type [ComplexType]
848
+ def should_erase_generics_when_done?(namespace_pin, rooted_type)
849
+ has_generics?(namespace_pin) && !can_resolve_generics?(namespace_pin, rooted_type)
850
+ end
851
+
852
+ # @param namespace_pin [Pin::Namespace]
853
+ def has_generics?(namespace_pin)
854
+ namespace_pin && !namespace_pin.generics.empty?
855
+ end
856
+
857
+ # @param namespace_pin [Pin::Namespace]
858
+ # @param rooted_type [ComplexType]
859
+ def can_resolve_generics?(namespace_pin, rooted_type)
860
+ has_generics?(namespace_pin) && !rooted_type.all_params.empty?
861
+ end
811
862
  end
812
863
  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
@@ -26,6 +26,8 @@ module Solargraph
26
26
  if name.start_with?('::')
27
27
  name = name[2..-1]
28
28
  rooted = true
29
+ elsif !can_root_name?(name)
30
+ rooted = true
29
31
  else
30
32
  rooted = false
31
33
  end
@@ -63,7 +65,7 @@ module Solargraph
63
65
  if parameters_type.nil?
64
66
  raise "You must supply parameters_type if you provide parameters" unless key_types.empty? && subtypes.empty?
65
67
  end
66
- raise "Please remove leading :: and set rooted instead - #{name}" if name.start_with?('::')
68
+ raise "Please remove leading :: and set rooted instead - #{name.inspect}" if name.start_with?('::')
67
69
  @name = name
68
70
  @key_types = key_types
69
71
  @subtypes = subtypes
@@ -78,6 +80,24 @@ module Solargraph
78
80
  tag
79
81
  end
80
82
 
83
+ def eql?(other)
84
+ self.class == other.class &&
85
+ @name == other.name &&
86
+ @key_types == other.key_types &&
87
+ @subtypes == other.subtypes &&
88
+ @rooted == other.rooted? &&
89
+ @all_params == other.all_params &&
90
+ @parameters_type == other.parameters_type
91
+ end
92
+
93
+ def ==(other)
94
+ eql?(other)
95
+ end
96
+
97
+ def hash
98
+ [self.class, @name, @key_types, @sub_types, @rooted, @all_params, @parameters_type].hash
99
+ end
100
+
81
101
  # @return [Array<UniqueType>]
82
102
  def items
83
103
  [self]
@@ -108,7 +128,11 @@ module Solargraph
108
128
  # tuples don't have a name; they're just [foo, bar, baz].
109
129
  if substring == '()'
110
130
  # but there are no zero element tuples, so we go with an array
111
- 'Array[]'
131
+ if rooted?
132
+ '::Array[]'
133
+ else
134
+ 'Array[]'
135
+ end
112
136
  else
113
137
  # already generated surrounded by []
114
138
  parameters_as_rbs
@@ -238,6 +262,7 @@ module Solargraph
238
262
  # @return [self]
239
263
  def recreate(new_name: nil, make_rooted: nil, new_key_types: nil, new_subtypes: nil)
240
264
  raise "Please remove leading :: and set rooted instead - #{new_name}" if new_name&.start_with?('::')
265
+
241
266
  new_name ||= name
242
267
  new_key_types ||= @key_types
243
268
  new_subtypes ||= @subtypes
@@ -278,17 +303,26 @@ module Solargraph
278
303
  new_key_types = @key_types.flat_map { |ct| ct.items.map { |ut| ut.transform(&transform_type) } }
279
304
  new_subtypes = @subtypes.flat_map { |ct| ct.items.map { |ut| ut.transform(&transform_type) } }
280
305
  end
281
- new_type = recreate(new_name: new_name || name, new_key_types: new_key_types, new_subtypes: new_subtypes)
306
+ new_type = recreate(new_name: new_name || name, new_key_types: new_key_types, new_subtypes: new_subtypes, make_rooted: @rooted)
282
307
  yield new_type
283
308
  end
284
309
 
285
- # Transform references to the 'self' type to the specified concrete namespace
286
- # @param dst [String]
287
- # @return [UniqueType]
288
- def self_to dst
310
+ # Generate a ComplexType that fully qualifies this type's namespaces.
311
+ #
312
+ # @param api_map [ApiMap] The ApiMap that performs qualification
313
+ # @param context [String] The namespace from which to resolve names
314
+ # @return [self, ComplexType, UniqueType] The generated ComplexType
315
+ def qualify api_map, context = ''
289
316
  transform do |t|
290
- next t if t.name != 'self'
291
- t.recreate(new_name: dst, new_key_types: [], new_subtypes: [])
317
+ next t if t.name == GENERIC_TAG_NAME
318
+ next t if t.duck_type? || t.void? || t.undefined?
319
+ recon = (t.rooted? ? '' : context)
320
+ fqns = api_map.qualify(t.name, recon)
321
+ if fqns.nil?
322
+ next UniqueType::BOOLEAN if t.tag == 'Boolean'
323
+ next UniqueType::UNDEFINED
324
+ end
325
+ t.recreate(new_name: fqns, make_rooted: true)
292
326
  end
293
327
  end
294
328
 
@@ -296,6 +330,35 @@ module Solargraph
296
330
  @name == 'self' || @key_types.any?(&:selfy?) || @subtypes.any?(&:selfy?)
297
331
  end
298
332
 
333
+ # @param dst [ComplexType]
334
+ # @return [self]
335
+ def self_to_type dst
336
+ object_type_dst = dst.reduce_class_type
337
+ transform do |t|
338
+ next t if t.name != 'self'
339
+ object_type_dst
340
+ end
341
+ end
342
+
343
+ def all_rooted?
344
+ return true if name == GENERIC_TAG_NAME
345
+ rooted? && all_params.all?(&:rooted?)
346
+ end
347
+
348
+ def rooted?
349
+ !can_root_name? || @rooted
350
+ end
351
+
352
+ def can_root_name?(name_to_check = name)
353
+ self.class.can_root_name?(name_to_check)
354
+ end
355
+
356
+ # @param name [String]
357
+ def self.can_root_name?(name)
358
+ # name is not lowercase
359
+ !name.empty? && name != name.downcase
360
+ end
361
+
299
362
  UNDEFINED = UniqueType.new('undefined', rooted: false)
300
363
  BOOLEAN = UniqueType.new('Boolean', rooted: true)
301
364
  end
@@ -18,13 +18,27 @@ module Solargraph
18
18
  @items = types.flat_map(&:items).uniq(&:to_s)
19
19
  end
20
20
 
21
+ def eql?(other)
22
+ self.class == other.class &&
23
+ @items == other.items
24
+ end
25
+
26
+ def ==(other)
27
+ self.eql?(other)
28
+ end
29
+
30
+ def hash
31
+ [self.class, @items].hash
32
+ end
33
+
21
34
  # @param api_map [ApiMap]
22
35
  # @param context [String]
23
36
  # @return [ComplexType]
24
37
  def qualify api_map, context = ''
25
38
  red = reduce_object
26
39
  types = red.items.map do |t|
27
- next t if ['Boolean', 'nil', 'void', 'undefined'].include?(t.name)
40
+ next t if ['nil', 'void', 'undefined'].include?(t.name)
41
+ next t if ['::Boolean'].include?(t.rooted_name)
28
42
  t.qualify api_map, context
29
43
  end
30
44
  ComplexType.new(types).reduce_object
@@ -52,6 +66,16 @@ module Solargraph
52
66
  (@items.length > 1 ? ')' : ''))
53
67
  end
54
68
 
69
+ # @param dst [ComplexType]
70
+ # @return [ComplexType]
71
+ def self_to_type dst
72
+ object_type_dst = dst.reduce_class_type
73
+ transform do |t|
74
+ next t if t.name != 'self'
75
+ object_type_dst
76
+ end
77
+ end
78
+
55
79
  # @yieldparam [UniqueType]
56
80
  # @return [Array]
57
81
  def map &block
@@ -171,16 +195,7 @@ module Solargraph
171
195
  # @return [ComplexType]
172
196
  def resolve_generics definitions, context_type
173
197
  result = @items.map { |i| i.resolve_generics(definitions, context_type) }
174
- ComplexType.try_parse(*result.map(&:tag))
175
- end
176
-
177
- # @param dst [String]
178
- # @return [ComplexType]
179
- def self_to dst
180
- return self unless selfy?
181
- red = reduce_class(dst)
182
- result = @items.map { |i| i.self_to red }
183
- ComplexType.try_parse(*result.map(&:tag))
198
+ ComplexType.new(result)
184
199
  end
185
200
 
186
201
  def nullable?
@@ -192,14 +207,43 @@ module Solargraph
192
207
  @items.first.all_params || []
193
208
  end
194
209
 
210
+ # @return [ComplexType]
211
+ def reduce_class_type
212
+ new_items = items.flat_map do |type|
213
+ next type unless ['Module', 'Class'].include?(type.name)
214
+
215
+ type.all_params
216
+ end
217
+ ComplexType.new(new_items)
218
+ end
219
+
220
+ # every type and subtype in this union have been resolved to be
221
+ # fully qualified
222
+ def all_rooted?
223
+ all?(&:all_rooted?)
224
+ end
225
+
226
+ # every top-level type has resolved to be fully qualified; see
227
+ # #all_rooted? to check their subtypes as well
228
+ def rooted?
229
+ all?(&:rooted?)
230
+ end
231
+
195
232
  attr_reader :items
196
233
 
234
+ def rooted?
235
+ @items.all?(&:rooted?)
236
+ end
237
+
197
238
  protected
198
239
 
199
240
  # @return [ComplexType]
200
241
  def reduce_object
201
- return self if name != 'Object' || subtypes.empty?
202
- ComplexType.try_parse(reduce_class(subtypes.join(', ')))
242
+ new_items = items.flat_map do |ut|
243
+ next [ut] if ut.name != 'Object' || ut.subtypes.empty?
244
+ ut.subtypes
245
+ end
246
+ ComplexType.new(new_items)
203
247
  end
204
248
 
205
249
  def bottom?
@@ -221,10 +265,15 @@ module Solargraph
221
265
  #
222
266
  # @param *strings [Array<String>] The type definitions to parse
223
267
  # @return [ComplexType]
224
- # @overload parse(*strings, partial: false)
225
- # @todo Need ability to use a literal true as a type below
226
- # @param partial [Boolean] True if the string is part of a another type
227
- # @return [Array<UniqueType>]
268
+ # # @overload parse(*strings, partial: false)
269
+ # # @todo Need ability to use a literal true as a type below
270
+ # # @param partial [Boolean] True if the string is part of a another type
271
+ # # @return [Array<UniqueType>]
272
+ # @sg-ignore
273
+ # @todo To be able to select the right signature above,
274
+ # Chain::Call needs to know the decl type (:arg, :optarg,
275
+ # :kwarg, etc) of the arguments given, instead of just having
276
+ # an array of Chains as the arguments.
228
277
  def parse *strings, partial: false
229
278
  # @type [Hash{Array<String> => ComplexType}]
230
279
  @cache ||= {}
@@ -3,10 +3,15 @@
3
3
  module Solargraph
4
4
  module LanguageServer
5
5
  class Host
6
- # A serial worker Thread to handle message.
6
+ # A serial worker Thread to handle incoming messages.
7
7
  #
8
- # this make check pending message possible, and maybe cancelled to speedup process
9
8
  class MessageWorker
9
+ UPDATE_METHODS = [
10
+ 'textDocument/didOpen',
11
+ 'textDocument/didChange',
12
+ 'workspace/didChangeWatchedFiles'
13
+ ].freeze
14
+
10
15
  # @param host [Host]
11
16
  def initialize(host)
12
17
  @host = host
@@ -30,7 +35,7 @@ module Solargraph
30
35
  @stopped = true
31
36
  end
32
37
 
33
- # @param message [Hash] The message should be handle. will pass back to Host#receive
38
+ # @param message [Hash] The message to handle. Will be forwarded to Host#receive
34
39
  # @return [void]
35
40
  def queue(message)
36
41
  @mutex.synchronize do
@@ -52,10 +57,45 @@ module Solargraph
52
57
  def tick
53
58
  message = @mutex.synchronize do
54
59
  @resource.wait(@mutex) if messages.empty?
55
- messages.shift
60
+ next_message
56
61
  end
57
62
  handler = @host.receive(message)
58
- handler && handler.send_response
63
+ handler&.send_response
64
+ end
65
+
66
+ private
67
+
68
+ def next_message
69
+ cancel_message || next_priority
70
+ end
71
+
72
+ def cancel_message
73
+ # Handle cancellations first
74
+ idx = messages.find_index { |msg| msg['method'] == '$/cancelRequest' }
75
+ return unless idx
76
+
77
+ msg = messages[idx]
78
+ messages.delete_at idx
79
+ msg
80
+ end
81
+
82
+ def next_priority
83
+ # Prioritize updates and version-dependent messages for performance
84
+ idx = messages.find_index do |msg|
85
+ UPDATE_METHODS.include?(msg['method']) || version_dependent?(msg)
86
+ end
87
+ return messages.shift unless idx
88
+
89
+ msg = messages[idx]
90
+ messages.delete_at idx
91
+ msg
92
+ end
93
+
94
+ # True if the message requires a previous update to have executed in
95
+ # order to work correctly.
96
+ #
97
+ def version_dependent? msg
98
+ msg['textDocument'] && msg['position']
59
99
  end
60
100
  end
61
101
  end