solargraph 0.54.1 → 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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -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 +68 -121
  6. data/lib/solargraph/api_map.rb +79 -28
  7. data/lib/solargraph/bench.rb +17 -1
  8. data/lib/solargraph/complex_type/type_methods.rb +7 -0
  9. data/lib/solargraph/complex_type/unique_type.rb +21 -1
  10. data/lib/solargraph/complex_type.rb +13 -12
  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 +23 -6
  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/formatting.rb +4 -0
  20. data/lib/solargraph/library.rb +7 -8
  21. data/lib/solargraph/location.rb +7 -0
  22. data/lib/solargraph/parser/node_methods.rb +1 -1
  23. data/lib/solargraph/parser/node_processor.rb +1 -0
  24. data/lib/solargraph/parser/parser_gem/class_methods.rb +2 -6
  25. data/lib/solargraph/parser/parser_gem/node_chainer.rb +7 -13
  26. data/lib/solargraph/parser/parser_gem/node_methods.rb +2 -2
  27. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +2 -2
  28. data/lib/solargraph/pin/base.rb +28 -18
  29. data/lib/solargraph/pin/base_variable.rb +3 -2
  30. data/lib/solargraph/pin/block.rb +1 -3
  31. data/lib/solargraph/pin/callable.rb +3 -3
  32. data/lib/solargraph/pin/method.rb +6 -0
  33. data/lib/solargraph/pin/namespace.rb +1 -1
  34. data/lib/solargraph/pin/parameter.rb +2 -1
  35. data/lib/solargraph/position.rb +7 -0
  36. data/lib/solargraph/range.rb +7 -0
  37. data/lib/solargraph/rbs_map/conversions.rb +8 -2
  38. data/lib/solargraph/rbs_map.rb +1 -0
  39. data/lib/solargraph/shell.rb +2 -0
  40. data/lib/solargraph/source/chain/array.rb +1 -1
  41. data/lib/solargraph/source/chain/call.rb +15 -8
  42. data/lib/solargraph/source/chain/hash.rb +5 -0
  43. data/lib/solargraph/source/chain/if.rb +5 -0
  44. data/lib/solargraph/source/chain/link.rb +17 -5
  45. data/lib/solargraph/source/chain/literal.rb +5 -0
  46. data/lib/solargraph/source/chain.rb +33 -19
  47. data/lib/solargraph/source/cursor.rb +1 -11
  48. data/lib/solargraph/source.rb +2 -1
  49. data/lib/solargraph/source_map/clip.rb +1 -1
  50. data/lib/solargraph/type_checker.rb +8 -8
  51. data/lib/solargraph/version.rb +1 -1
  52. data/lib/solargraph/workspace/config.rb +7 -3
  53. data/lib/solargraph/workspace.rb +1 -1
  54. data/lib/solargraph/yard_map/mapper/to_constant.rb +1 -0
  55. data/lib/solargraph/yard_map/mapper/to_namespace.rb +1 -0
  56. data/lib/solargraph/yard_map/mapper.rb +1 -0
  57. data/lib/solargraph/yardoc.rb +1 -1
  58. data/lib/solargraph.rb +1 -0
  59. data/solargraph.gemspec +5 -5
  60. metadata +27 -25
@@ -12,6 +12,7 @@ module Solargraph
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.).
@@ -341,8 +350,12 @@ module Solargraph
341
350
  # @param deep [Boolean] True to include superclasses, mixins, etc.
342
351
  # @return [Array<Solargraph::Pin::Method>]
343
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
344
356
  cached = cache.get_methods(rooted_tag, scope, visibility, deep)
345
357
  return cached.clone unless cached.nil?
358
+ # @type [Array<Solargraph::Pin::Method>]
346
359
  result = []
347
360
  skip = Set.new
348
361
  if rooted_tag == ''
@@ -380,9 +393,12 @@ module Solargraph
380
393
  result.concat inner_get_methods('Kernel', :instance, [:public], deep, skip) if visibility.include?(:private)
381
394
  result.concat inner_get_methods('Module', scope, visibility, deep, skip) if scope == :module
382
395
  end
383
- resolved = resolve_method_aliases(result, visibility)
384
- cache.set_methods(rooted_tag, scope, visibility, deep, resolved)
385
- 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
386
402
  end
387
403
 
388
404
  # Get an array of method pins for a complex type.
@@ -438,8 +454,13 @@ module Solargraph
438
454
  # @param name [String] Method name to look up
439
455
  # @param scope [Symbol] :instance or :class
440
456
  # @return [Array<Solargraph::Pin::Method>]
441
- def get_method_stack rooted_tag, name, scope: :instance
442
- 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
443
464
  end
444
465
 
445
466
  # Get an array of all suggestions that match the specified path.
@@ -601,6 +622,7 @@ module Solargraph
601
622
  rooted_type = ComplexType.parse(rooted_tag).force_rooted
602
623
  fqns = rooted_type.namespace
603
624
  fqns_generic_params = rooted_type.all_params
625
+ namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
604
626
  return [] if no_core && fqns =~ /^(Object|BasicObject|Class|Module)$/
605
627
  reqstr = "#{fqns}|#{scope}|#{visibility.sort}|#{deep}"
606
628
  return [] if skip.include?(reqstr)
@@ -615,15 +637,7 @@ module Solargraph
615
637
  # Store#get_methods doesn't know about full tags, just
616
638
  # namespaces; resolving the generics in the method pins is this
617
639
  # class' responsibility
618
- raw_methods = store.get_methods(fqns, scope: scope, visibility: visibility).sort{ |a, b| a.name <=> b.name }
619
- namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
620
- methods = if namespace_pin && rooted_tag != fqns
621
- methods = raw_methods.map do |method_pin|
622
- method_pin.resolve_generics(namespace_pin, rooted_type)
623
- end
624
- else
625
- raw_methods
626
- end
640
+ methods = store.get_methods(fqns, scope: scope, visibility: visibility).sort{ |a, b| a.name <=> b.name }
627
641
  result.concat methods
628
642
  if deep
629
643
  if scope == :instance
@@ -768,8 +782,8 @@ module Solargraph
768
782
 
769
783
  # Sort an array of pins to put nil or undefined variables last.
770
784
  #
771
- # @param pins [Enumerable<Solargraph::Pin::Base>]
772
- # @return [Enumerable<Solargraph::Pin::Base>]
785
+ # @param pins [Enumerable<Pin::BaseVariable>]
786
+ # @return [Enumerable<Pin::BaseVariable>]
773
787
  def prefer_non_nil_variables pins
774
788
  result = []
775
789
  nil_pins = []
@@ -809,11 +823,48 @@ module Solargraph
809
823
  name: pin.name,
810
824
  comments: origin.comments,
811
825
  scope: origin.scope,
826
+ # context: pin.context,
812
827
  visibility: origin.visibility,
813
828
  signatures: origin.signatures,
814
- attribute: origin.attribute?
829
+ attribute: origin.attribute?,
830
+ generics: origin.generics,
831
+ return_type: origin.return_type,
815
832
  }
816
833
  Pin::Method.new **args
817
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
818
869
  end
819
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
@@ -129,6 +129,13 @@ module Solargraph
129
129
  end.call
130
130
  end
131
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
+
132
139
  # @return [String]
133
140
  def rooted_namespace
134
141
  return namespace unless rooted? && can_root_name?(namespace)
@@ -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
@@ -112,6 +118,10 @@ module Solargraph
112
118
  end
113
119
  end
114
120
 
121
+ def desc
122
+ rooted_tags
123
+ end
124
+
115
125
  # @return [String]
116
126
  def to_rbs
117
127
  if duck_type?
@@ -235,7 +245,17 @@ module Solargraph
235
245
  if t.name == GENERIC_TAG_NAME
236
246
  idx = definitions.generics.index(t.subtypes.first&.name)
237
247
  next t if idx.nil?
238
- 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
239
259
  else
240
260
  t
241
261
  end
@@ -7,6 +7,7 @@ module Solargraph
7
7
  GENERIC_TAG_NAME = 'generic'.freeze
8
8
  # @!parse
9
9
  # include TypeMethods
10
+ include Equality
10
11
 
11
12
  autoload :TypeMethods, 'solargraph/complex_type/type_methods'
12
13
  autoload :UniqueType, 'solargraph/complex_type/unique_type'
@@ -18,17 +19,9 @@ module Solargraph
18
19
  @items = types.flat_map(&:items).uniq(&:to_s)
19
20
  end
20
21
 
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
22
+ # @sg-ignore Fix "Not enough arguments to Module#protected"
23
+ protected def equality_fields
24
+ [self.class, items]
32
25
  end
33
26
 
34
27
  # @param api_map [ApiMap]
@@ -154,14 +147,22 @@ module Solargraph
154
147
  map(&:tag).join(', ')
155
148
  end
156
149
 
150
+ def desc
151
+ rooted_tags
152
+ end
153
+
157
154
  def rooted_tags
158
155
  map(&:rooted_tag).join(', ')
159
156
  end
160
157
 
158
+ # @yieldparam [UniqueType]
161
159
  def all? &block
162
160
  @items.all? &block
163
161
  end
164
162
 
163
+ # @yieldparam [UniqueType]
164
+ # @yieldreturn [Boolean]
165
+ # @return [Boolean]
165
166
  def any? &block
166
167
  @items.compact.any? &block
167
168
  end
@@ -263,7 +264,7 @@ module Solargraph
263
264
  # Consumers should not need to use this parameter; it should only be
264
265
  # used internally.
265
266
  #
266
- # @param *strings [Array<String>] The type definitions to parse
267
+ # @param strings [Array<String>] The type definitions to parse
267
268
  # @return [ComplexType]
268
269
  # # @overload parse(*strings, partial: false)
269
270
  # # @todo Need ability to use a literal true as a type below
@@ -11,6 +11,7 @@ module Solargraph
11
11
  autoload :Gemspec, 'solargraph/convention/gemspec'
12
12
  autoload :Rakefile, 'solargraph/convention/rakefile'
13
13
 
14
+ # @type [Set<Convention::Base>]
14
15
  @@conventions = Set.new
15
16
 
16
17
  # @param convention [Class<Convention::Base>]
@@ -112,6 +112,7 @@ module Solargraph
112
112
  true
113
113
  end
114
114
 
115
+ # @param gemspec [Gem::Specification]
115
116
  def update_from_collection gemspec, gempins
116
117
  return gempins unless @rbs_path && File.directory?(@rbs_path)
117
118
  return gempins if RbsMap.new(gemspec.name, gemspec.version).resolved?
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ # @abstract This mixin relies on these -
5
+ # methods:
6
+ # equality_fields()
7
+ module Equality
8
+ # @!method equality_fields
9
+ # @return [Array]
10
+
11
+ # @param other [Object]
12
+ # @return [Boolean]
13
+ def eql?(other)
14
+ self.class.eql?(other.class) &&
15
+ equality_fields.eql?(other.equality_fields)
16
+ end
17
+
18
+ # @param other [Object]
19
+ # @return [Boolean]
20
+ def ==(other)
21
+ self.eql?(other)
22
+ end
23
+
24
+ def hash
25
+ equality_fields.hash
26
+ end
27
+
28
+ def freeze
29
+ equality_fields.each(&:freeze)
30
+ super
31
+ end
32
+ end
33
+ end
@@ -3,12 +3,14 @@
3
3
  module Solargraph
4
4
  module LanguageServer
5
5
  class Host
6
- # A serial worker Thread to handle message.
7
- #
8
- # this make check pending message possible, and maybe cancelled to speedup process
6
+ # A serial worker Thread to handle incoming messages.
9
7
  #
10
8
  class MessageWorker
11
- UPDATE_METHODS = ['textDocument/didOpen', 'textDocument/didChange', 'workspace/didChangeWatchedFiles'].freeze
9
+ UPDATE_METHODS = [
10
+ 'textDocument/didOpen',
11
+ 'textDocument/didChange',
12
+ 'workspace/didChangeWatchedFiles'
13
+ ].freeze
12
14
 
13
15
  # @param host [Host]
14
16
  def initialize(host)
@@ -64,12 +66,24 @@ module Solargraph
64
66
  private
65
67
 
66
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
67
83
  # Prioritize updates and version-dependent messages for performance
68
84
  idx = messages.find_index do |msg|
69
85
  UPDATE_METHODS.include?(msg['method']) || version_dependent?(msg)
70
86
  end
71
- # @todo We might want to clear duplicate instances of this message
72
- # that occur before the next update
73
87
  return messages.shift unless idx
74
88
 
75
89
  msg = messages[idx]
@@ -80,6 +94,9 @@ module Solargraph
80
94
  # True if the message requires a previous update to have executed in
81
95
  # order to work correctly.
82
96
  #
97
+ # @param msg [Hash{String => Object}]
98
+ # @todo need boolish type from RBS
99
+ # @return [Object]
83
100
  def version_dependent? msg
84
101
  msg['textDocument'] && msg['position']
85
102
  end
@@ -24,10 +24,8 @@ module Solargraph
24
24
  attr_writer :client_capabilities
25
25
 
26
26
  def initialize
27
- @cancel_semaphore = Mutex.new
28
27
  @buffer_semaphore = Mutex.new
29
28
  @request_mutex = Mutex.new
30
- @cancel = []
31
29
  @buffer = String.new
32
30
  @stopped = true
33
31
  @next_request_id = 1
@@ -65,7 +63,7 @@ module Solargraph
65
63
  # @param id [Integer]
66
64
  # @return [void]
67
65
  def cancel id
68
- @cancel_semaphore.synchronize { @cancel.push id }
66
+ cancelled.push id
69
67
  end
70
68
 
71
69
  # True if the host received a request to cancel the method with the
@@ -74,9 +72,7 @@ module Solargraph
74
72
  # @param id [Integer]
75
73
  # @return [Boolean]
76
74
  def cancel? id
77
- result = false
78
- @cancel_semaphore.synchronize { result = @cancel.include? id }
79
- result
75
+ cancelled.include? id
80
76
  end
81
77
 
82
78
  # Delete the specified ID from the list of cancelled IDs if it exists.
@@ -84,7 +80,7 @@ module Solargraph
84
80
  # @param id [Integer]
85
81
  # @return [void]
86
82
  def clear id
87
- @cancel_semaphore.synchronize { @cancel.delete id }
83
+ cancelled.delete id
88
84
  end
89
85
 
90
86
  # Called by adapter, to handle the request
@@ -101,11 +97,11 @@ module Solargraph
101
97
  # @return [Solargraph::LanguageServer::Message::Base, nil] The message handler.
102
98
  def receive request
103
99
  if request['method']
104
- logger.info "Server received #{request['method']}"
100
+ logger.info "Host received ##{request['id']} #{request['method']}"
105
101
  logger.debug request
106
102
  message = Message.select(request['method']).new(self, request)
107
103
  begin
108
- message.process
104
+ message.process unless cancel?(request['id'])
109
105
  rescue StandardError => e
110
106
  logger.warn "Error processing request: [#{e.class}] #{e.message}"
111
107
  logger.warn e.backtrace.join("\n")
@@ -381,7 +377,6 @@ module Solargraph
381
377
  envelope = "Content-Length: #{json.bytesize}\r\n\r\n#{json}"
382
378
  queue envelope
383
379
  @next_request_id += 1
384
- logger.info "Server sent #{method}"
385
380
  logger.debug params
386
381
  end
387
382
  end
@@ -476,6 +471,7 @@ module Solargraph
476
471
  def locate_pins params
477
472
  return [] unless params['data'] && params['data']['uri']
478
473
  library = library_for(params['data']['uri'])
474
+ # @type [Array<Pin::Base>]
479
475
  result = []
480
476
  if params['data']['location']
481
477
  location = Location.new(
@@ -575,7 +571,7 @@ module Solargraph
575
571
  # @param column [Integer]
576
572
  # @param strip [Boolean] Strip special characters from variable names
577
573
  # @param only [Boolean] If true, search current file only
578
- # @return [Array<Solargraph::Range>]
574
+ # @return [Array<Solargraph::Location>]
579
575
  def references_from uri, line, column, strip: true, only: false
580
576
  library = library_for(uri)
581
577
  library.references_from(uri_to_file(uri), line, column, strip: strip, only: only)
@@ -693,6 +689,11 @@ module Solargraph
693
689
 
694
690
  private
695
691
 
692
+ # @return [Array<Integer>]
693
+ def cancelled
694
+ @cancelled ||= []
695
+ end
696
+
696
697
  # @return [MessageWorker]
697
698
  def message_worker
698
699
  @message_worker ||= MessageWorker.new(self)
@@ -16,7 +16,7 @@ module Solargraph
16
16
  # @return [String]
17
17
  attr_reader :method
18
18
 
19
- # @return [Hash{String => Array, Hash, String, Integer}]
19
+ # @return [Hash{String => Array<undefined>, Hash{String => undefined}, String, Integer}]
20
20
  attr_reader :params
21
21
 
22
22
  # @return [Hash, Array, nil]
@@ -61,18 +61,11 @@ module Solargraph
61
61
  # @return [void]
62
62
  def send_response
63
63
  return if id.nil?
64
- if host.cancel?(id)
65
- # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#cancelRequest
66
- # cancel should send response RequestCancelled
67
- Solargraph::Logging.logger.info "Cancelled response to #{method}"
68
- set_result nil
69
- set_error ErrorCodes::REQUEST_CANCELLED, "cancelled by client"
70
- else
71
- Solargraph::Logging.logger.info "Sending response to #{method}"
72
- end
64
+
65
+ accept_or_cancel
73
66
  response = {
74
- jsonrpc: "2.0",
75
- id: id,
67
+ jsonrpc: '2.0',
68
+ id: id
76
69
  }
77
70
  response[:result] = result unless result.nil?
78
71
  response[:error] = error unless error.nil?
@@ -83,6 +76,20 @@ module Solargraph
83
76
  host.queue envelope
84
77
  host.clear id
85
78
  end
79
+
80
+ private
81
+
82
+ def accept_or_cancel
83
+ if host.cancel?(id)
84
+ # https://microsoft.github.io/language-server-protocol/specifications/specification-current/#cancelRequest
85
+ # cancel should send response RequestCancelled
86
+ Solargraph::Logging.logger.info "Cancelled response to ##{id} #{method}"
87
+ set_result nil
88
+ set_error ErrorCodes::REQUEST_CANCELLED, 'Cancelled by client'
89
+ else
90
+ Solargraph::Logging.logger.info "Sending response to ##{id} #{method}"
91
+ end
92
+ end
86
93
  end
87
94
  end
88
95
  end
@@ -174,7 +174,9 @@ module Solargraph
174
174
 
175
175
  # @param section [String]
176
176
  # @param capability [String]
177
- # @return [Boolean]
177
+ # @todo Need support for RBS' boolish "type", which doesn't
178
+ # enforce strict true/false-ness
179
+ # @sg-ignore
178
180
  def dynamic_registration_for? section, capability
179
181
  result = (params['capabilities'] &&
180
182
  params['capabilities'][section] &&
@@ -12,9 +12,6 @@ module Solargraph
12
12
  col = params['position']['character']
13
13
  begin
14
14
  completion = host.completions_at(params['textDocument']['uri'], line, col)
15
- if host.cancel?(id)
16
- return set_result(empty_result) if host.cancel?(id)
17
- end
18
15
  items = []
19
16
  last_context = nil
20
17
  idx = -1
@@ -33,6 +33,7 @@ module Solargraph
33
33
 
34
34
  private
35
35
 
36
+ # @param corrections [String]
36
37
  def log_corrections(corrections)
37
38
  corrections = corrections&.strip
38
39
  return if corrections&.empty?
@@ -51,6 +52,7 @@ module Solargraph
51
52
  conf['rubocop'] || {}
52
53
  end
53
54
 
55
+ # @param config [Hash{String => String}]
54
56
  def cli_args file_uri, config
55
57
  file = UriHelpers.uri_to_file(file_uri)
56
58
  args = [
@@ -68,6 +70,7 @@ module Solargraph
68
70
  args + [file]
69
71
  end
70
72
 
73
+ # @param config [Hash{String => String}]
71
74
  def formatter_class(config)
72
75
  if self.class.const_defined?('BlankRubocopFormatter')
73
76
  # @sg-ignore
@@ -79,6 +82,7 @@ module Solargraph
79
82
  end
80
83
  end
81
84
 
85
+ # @param value [Array, String]
82
86
  def cop_list(value)
83
87
  value = value.join(',') if value.respond_to?(:join)
84
88
  return nil if value == '' || !value.is_a?(String)