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
@@ -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,13 +19,19 @@ module Solargraph
18
19
  @items = types.flat_map(&:items).uniq(&:to_s)
19
20
  end
20
21
 
22
+ # @sg-ignore Fix "Not enough arguments to Module#protected"
23
+ protected def equality_fields
24
+ [self.class, items]
25
+ end
26
+
21
27
  # @param api_map [ApiMap]
22
28
  # @param context [String]
23
29
  # @return [ComplexType]
24
30
  def qualify api_map, context = ''
25
31
  red = reduce_object
26
32
  types = red.items.map do |t|
27
- next t if ['Boolean', 'nil', 'void', 'undefined'].include?(t.name)
33
+ next t if ['nil', 'void', 'undefined'].include?(t.name)
34
+ next t if ['::Boolean'].include?(t.rooted_name)
28
35
  t.qualify api_map, context
29
36
  end
30
37
  ComplexType.new(types).reduce_object
@@ -52,6 +59,16 @@ module Solargraph
52
59
  (@items.length > 1 ? ')' : ''))
53
60
  end
54
61
 
62
+ # @param dst [ComplexType]
63
+ # @return [ComplexType]
64
+ def self_to_type dst
65
+ object_type_dst = dst.reduce_class_type
66
+ transform do |t|
67
+ next t if t.name != 'self'
68
+ object_type_dst
69
+ end
70
+ end
71
+
55
72
  # @yieldparam [UniqueType]
56
73
  # @return [Array]
57
74
  def map &block
@@ -130,14 +147,22 @@ module Solargraph
130
147
  map(&:tag).join(', ')
131
148
  end
132
149
 
150
+ def desc
151
+ rooted_tags
152
+ end
153
+
133
154
  def rooted_tags
134
155
  map(&:rooted_tag).join(', ')
135
156
  end
136
157
 
158
+ # @yieldparam [UniqueType]
137
159
  def all? &block
138
160
  @items.all? &block
139
161
  end
140
162
 
163
+ # @yieldparam [UniqueType]
164
+ # @yieldreturn [Boolean]
165
+ # @return [Boolean]
141
166
  def any? &block
142
167
  @items.compact.any? &block
143
168
  end
@@ -171,16 +196,7 @@ module Solargraph
171
196
  # @return [ComplexType]
172
197
  def resolve_generics definitions, context_type
173
198
  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))
199
+ ComplexType.new(result)
184
200
  end
185
201
 
186
202
  def nullable?
@@ -192,14 +208,43 @@ module Solargraph
192
208
  @items.first.all_params || []
193
209
  end
194
210
 
211
+ # @return [ComplexType]
212
+ def reduce_class_type
213
+ new_items = items.flat_map do |type|
214
+ next type unless ['Module', 'Class'].include?(type.name)
215
+
216
+ type.all_params
217
+ end
218
+ ComplexType.new(new_items)
219
+ end
220
+
221
+ # every type and subtype in this union have been resolved to be
222
+ # fully qualified
223
+ def all_rooted?
224
+ all?(&:all_rooted?)
225
+ end
226
+
227
+ # every top-level type has resolved to be fully qualified; see
228
+ # #all_rooted? to check their subtypes as well
229
+ def rooted?
230
+ all?(&:rooted?)
231
+ end
232
+
195
233
  attr_reader :items
196
234
 
235
+ def rooted?
236
+ @items.all?(&:rooted?)
237
+ end
238
+
197
239
  protected
198
240
 
199
241
  # @return [ComplexType]
200
242
  def reduce_object
201
- return self if name != 'Object' || subtypes.empty?
202
- ComplexType.try_parse(reduce_class(subtypes.join(', ')))
243
+ new_items = items.flat_map do |ut|
244
+ next [ut] if ut.name != 'Object' || ut.subtypes.empty?
245
+ ut.subtypes
246
+ end
247
+ ComplexType.new(new_items)
203
248
  end
204
249
 
205
250
  def bottom?
@@ -219,12 +264,17 @@ module Solargraph
219
264
  # Consumers should not need to use this parameter; it should only be
220
265
  # used internally.
221
266
  #
222
- # @param *strings [Array<String>] The type definitions to parse
267
+ # @param strings [Array<String>] The type definitions to parse
223
268
  # @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>]
269
+ # # @overload parse(*strings, partial: false)
270
+ # # @todo Need ability to use a literal true as a type below
271
+ # # @param partial [Boolean] True if the string is part of a another type
272
+ # # @return [Array<UniqueType>]
273
+ # @sg-ignore
274
+ # @todo To be able to select the right signature above,
275
+ # Chain::Call needs to know the decl type (:arg, :optarg,
276
+ # :kwarg, etc) of the arguments given, instead of just having
277
+ # an array of Chains as the arguments.
228
278
  def parse *strings, partial: false
229
279
  # @type [Hash{Array<String> => ComplexType}]
230
280
  @cache ||= {}
@@ -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,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,48 @@ 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
+ # @param msg [Hash{String => Object}]
98
+ # @todo need boolish type from RBS
99
+ # @return [Object]
100
+ def version_dependent? msg
101
+ msg['textDocument'] && msg['position']
59
102
  end
60
103
  end
61
104
  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
@@ -13,10 +13,10 @@ module Solargraph::LanguageServer::Message::TextDocument
13
13
  def code_location
14
14
  suggestions = host.definitions_at(params['textDocument']['uri'], @line, @column)
15
15
  return nil if suggestions.empty?
16
- suggestions.reject { |pin| pin.location.nil? || pin.location.filename.nil? }.map do |pin|
16
+ suggestions.reject { |pin| pin.best_location.nil? || pin.best_location.filename.nil? }.map do |pin|
17
17
  {
18
- uri: file_to_uri(pin.location.filename),
19
- range: pin.location.range.to_hash
18
+ uri: file_to_uri(pin.best_location.filename),
19
+ range: pin.best_location.range.to_hash
20
20
  }
21
21
  end
22
22
  end
@@ -6,15 +6,15 @@ class Solargraph::LanguageServer::Message::TextDocument::DocumentSymbol < Solarg
6
6
  def process
7
7
  pins = host.document_symbols params['textDocument']['uri']
8
8
  info = pins.map do |pin|
9
- next nil unless pin.location&.filename
9
+ next nil unless pin.best_location&.filename
10
10
 
11
11
  result = {
12
12
  name: pin.name,
13
13
  containerName: pin.namespace,
14
14
  kind: pin.symbol_kind,
15
15
  location: {
16
- uri: file_to_uri(pin.location.filename),
17
- range: pin.location.range.to_hash
16
+ uri: file_to_uri(pin.best_location.filename),
17
+ range: pin.best_location.range.to_hash
18
18
  },
19
19
  deprecated: pin.deprecated?
20
20
  }
@@ -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)
@@ -21,7 +21,7 @@ module Solargraph
21
21
  parts.push pin.documentation unless pin.documentation.nil? || pin.documentation.empty?
22
22
  unless parts.empty?
23
23
  data = parts.join("\n\n")
24
- next if contents.last && contents.last.end_with?(data)
24
+ next if contents.last&.end_with?(data)
25
25
  contents.push data
26
26
  end
27
27
  last_link = this_link unless this_link.nil?
@@ -13,10 +13,10 @@ module Solargraph::LanguageServer::Message::TextDocument
13
13
  def code_location
14
14
  suggestions = host.type_definitions_at(params['textDocument']['uri'], @line, @column)
15
15
  return nil if suggestions.empty?
16
- suggestions.reject { |pin| pin.location.nil? || pin.location.filename.nil? }.map do |pin|
16
+ suggestions.reject { |pin| pin.best_location.nil? || pin.best_location.filename.nil? }.map do |pin|
17
17
  {
18
- uri: file_to_uri(pin.location.filename),
19
- range: pin.location.range.to_hash
18
+ uri: file_to_uri(pin.best_location.filename),
19
+ range: pin.best_location.range.to_hash
20
20
  }
21
21
  end
22
22
  end
@@ -6,14 +6,14 @@ class Solargraph::LanguageServer::Message::Workspace::WorkspaceSymbol < Solargra
6
6
  def process
7
7
  pins = host.query_symbols(params['query'])
8
8
  info = pins.map do |pin|
9
- uri = file_to_uri(pin.location.filename)
9
+ uri = file_to_uri(pin.best_location.filename)
10
10
  {
11
11
  name: pin.path,
12
12
  containerName: pin.namespace,
13
13
  kind: pin.symbol_kind,
14
14
  location: {
15
15
  uri: uri,
16
- range: pin.location.range.to_hash
16
+ range: pin.best_location.range.to_hash
17
17
  },
18
18
  deprecated: pin.deprecated?
19
19
  }
@@ -66,10 +66,10 @@ module Solargraph
66
66
  return unless host.client_supports_progress? && !finished?
67
67
 
68
68
  message = build
69
-
70
- create(host) unless created?
69
+ create(host)
71
70
  host.send_notification '$/progress', message
72
71
  @status = FINISHED if kind == 'end'
72
+ keep_alive host
73
73
  end
74
74
 
75
75
  def created?
@@ -113,6 +113,23 @@ module Solargraph
113
113
  raise "Invalid progress kind #{kind}"
114
114
  end
115
115
  end
116
+
117
+ # @param host [Host]
118
+ def keep_alive host
119
+ mutex.synchronize { @last = Time.now }
120
+ @keep_alive ||= Thread.new do
121
+ until finished?
122
+ sleep 10
123
+ break if finished?
124
+ next if mutex.synchronize { Time.now - @last < 10 }
125
+ send host
126
+ end
127
+ end
128
+ end
129
+
130
+ def mutex
131
+ @mutex ||= Mutex.new
132
+ end
116
133
  end
117
134
  end
118
135
  end