solargraph 0.53.0 → 0.54.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +45 -0
  3. data/lib/solargraph/api_map/cache.rb +2 -12
  4. data/lib/solargraph/api_map/source_to_yard.rb +17 -10
  5. data/lib/solargraph/api_map/store.rb +11 -6
  6. data/lib/solargraph/api_map.rb +80 -38
  7. data/lib/solargraph/bench.rb +1 -0
  8. data/lib/solargraph/complex_type/type_methods.rb +62 -30
  9. data/lib/solargraph/complex_type/unique_type.rb +149 -75
  10. data/lib/solargraph/complex_type.rb +41 -25
  11. data/lib/solargraph/doc_map.rb +65 -24
  12. data/lib/solargraph/gem_pins.rb +10 -2
  13. data/lib/solargraph/language_server/host/dispatch.rb +13 -2
  14. data/lib/solargraph/language_server/host/sources.rb +1 -61
  15. data/lib/solargraph/language_server/host.rb +41 -69
  16. data/lib/solargraph/language_server/message/base.rb +1 -1
  17. data/lib/solargraph/language_server/message/initialize.rb +14 -0
  18. data/lib/solargraph/language_server/message/text_document/formatting.rb +1 -0
  19. data/lib/solargraph/language_server/progress.rb +118 -0
  20. data/lib/solargraph/language_server.rb +1 -0
  21. data/lib/solargraph/library.rb +149 -97
  22. data/lib/solargraph/page.rb +6 -0
  23. data/lib/solargraph/parser/node_processor/base.rb +3 -2
  24. data/lib/solargraph/parser/node_processor.rb +1 -0
  25. data/lib/solargraph/parser/parser_gem/class_methods.rb +3 -7
  26. data/lib/solargraph/parser/parser_gem/node_methods.rb +0 -4
  27. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +6 -19
  28. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +47 -0
  29. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +5 -3
  30. data/lib/solargraph/parser/parser_gem/node_processors.rb +2 -0
  31. data/lib/solargraph/pin/base_variable.rb +35 -6
  32. data/lib/solargraph/pin/block.rb +70 -32
  33. data/lib/solargraph/pin/delegated_method.rb +5 -1
  34. data/lib/solargraph/pin/documenting.rb +2 -0
  35. data/lib/solargraph/pin/method.rb +4 -2
  36. data/lib/solargraph/pin/parameter.rb +5 -28
  37. data/lib/solargraph/rbs_map/conversions.rb +16 -47
  38. data/lib/solargraph/rbs_map/core_fills.rb +12 -6
  39. data/lib/solargraph/rbs_map/core_map.rb +2 -13
  40. data/lib/solargraph/rbs_map.rb +17 -7
  41. data/lib/solargraph/shell.rb +18 -13
  42. data/lib/solargraph/source/chain.rb +20 -0
  43. data/lib/solargraph/source/updater.rb +1 -0
  44. data/lib/solargraph/source.rb +0 -44
  45. data/lib/solargraph/source_map/clip.rb +3 -2
  46. data/lib/solargraph/source_map/mapper.rb +3 -2
  47. data/lib/solargraph/source_map.rb +8 -6
  48. data/lib/solargraph/type_checker.rb +58 -13
  49. data/lib/solargraph/version.rb +1 -1
  50. data/lib/solargraph/workspace/config.rb +2 -1
  51. data/lib/solargraph/workspace.rb +27 -1
  52. data/lib/solargraph/yard_map/mapper/to_method.rb +5 -2
  53. metadata +4 -4
  54. data/lib/solargraph/language_server/host/cataloger.rb +0 -57
  55. data/lib/solargraph/rbs_map/core_signs.rb +0 -35
@@ -42,6 +42,7 @@ module Solargraph
42
42
  @output
43
43
  end
44
44
 
45
+ # @param text [String]
45
46
  # @return [String]
46
47
  def repair text
47
48
  changes.each do |ch|
@@ -92,50 +92,6 @@ module Solargraph
92
92
  stack
93
93
  end
94
94
 
95
- # Start synchronizing the source. This method updates the code without
96
- # parsing a new AST. The resulting Source object will be marked not
97
- # synchronized (#synchronized? == false).
98
- #
99
- # @param updater [Source::Updater]
100
- # @return [Source]
101
- def start_synchronize updater
102
- raise 'Invalid synchronization' unless updater.filename == filename
103
- real_code = updater.write(@code)
104
- src = Source.allocate
105
- src.filename = filename
106
- src.code = real_code
107
- src.version = updater.version
108
- src.parsed = parsed?
109
- src.repaired = updater.repair(@repaired)
110
- src.synchronized = false
111
- src.node = @node
112
- src.comments = @comments
113
- src.error_ranges = error_ranges
114
- src.last_updater = updater
115
- return src.finish_synchronize unless real_code.lines.length == @code.lines.length
116
- src
117
- end
118
-
119
- # Finish synchronizing a source that was updated via #start_synchronize.
120
- # This method returns self if the source is already synchronized. Otherwise
121
- # it parses the AST and returns a new synchronized Source.
122
- #
123
- # @return [Source]
124
- def finish_synchronize
125
- return self if synchronized?
126
- synced = Source.new(@code, filename)
127
- if synced.parsed?
128
- synced.version = version
129
- return synced
130
- end
131
- synced = Source.new(@repaired, filename)
132
- synced.error_ranges.concat (error_ranges + last_updater.changes.map(&:range))
133
- synced.code = @code
134
- synced.synchronized = true
135
- synced.version = version
136
- synced
137
- end
138
-
139
95
  # Synchronize the Source with an update. This method applies changes to the
140
96
  # code, parses the new code's AST, and returns the resulting Source object.
141
97
  #
@@ -11,6 +11,7 @@ module Solargraph
11
11
  def initialize api_map, cursor
12
12
  @api_map = api_map
13
13
  @cursor = cursor
14
+ block.rebind(api_map) if block.is_a?(Pin::Block)
14
15
  end
15
16
 
16
17
  # @return [Array<Pin::Base>] Relevant pins for infering the type of the Cursor's position
@@ -50,9 +51,9 @@ module Solargraph
50
51
  def infer
51
52
  result = cursor.chain.infer(api_map, block, locals)
52
53
  if result.tag == 'Class'
53
- # HACK: Exception to return Object from Class#new
54
+ # HACK: Exception to return BasicObject from Class#new
54
55
  dfn = cursor.chain.define(api_map, block, locals).first
55
- return ComplexType.try_parse('Object') if dfn && dfn.path == 'Class#new'
56
+ return ComplexType.try_parse('BasicObject') if dfn && dfn.path == 'Class#new'
56
57
  end
57
58
  return result unless result.tag == 'self'
58
59
  ComplexType.try_parse(cursor.chain.base.infer(api_map, block, locals).tag)
@@ -144,7 +144,7 @@ module Solargraph
144
144
  )
145
145
  end
146
146
  if t.nil? || t.include?('w')
147
- pins.push Solargraph::Pin::Method.new(
147
+ method_pin = Solargraph::Pin::Method.new(
148
148
  location: location,
149
149
  closure: namespace,
150
150
  name: "#{directive.tag.name}=",
@@ -153,7 +153,8 @@ module Solargraph
153
153
  visibility: :public,
154
154
  attribute: true
155
155
  )
156
- pins.last.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last)
156
+ pins.push method_pin
157
+ method_pin.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last)
157
158
  if pins.last.return_type.defined?
158
159
  pins.last.docstring.add_tag YARD::Tags::Tag.new(:param, '', pins.last.return_type.to_s.split(', '), 'value')
159
160
  end
@@ -40,12 +40,14 @@ module Solargraph
40
40
  @pin_select_cache[klass] ||= @pin_class_hash.select { |key, _| key <= klass }.values.flatten
41
41
  end
42
42
 
43
- # @return [Set<String>]
44
- def rebindable_method_names
45
- @rebindable_method_names ||= pins_by_class(Pin::Method)
46
- .select { |pin| pin.comments && pin.comments.include?('@yieldreceiver') }
47
- .map(&:name)
48
- .to_set
43
+ # A hash representing the state of the source map's API.
44
+ #
45
+ # ApiMap#catalog uses this value to determine whether it needs to clear its
46
+ # cache.
47
+ #
48
+ # @return [Integer]
49
+ def api_hash
50
+ @api_hash ||= (pins_by_class(Pin::Constant) + pins_by_class(Pin::Namespace).select { |pin| pin.namespace.to_s > '' } + pins_by_class(Pin::Reference) + pins_by_class(Pin::Method).map(&:node)).hash
49
51
  end
50
52
 
51
53
  # @return [String]
@@ -268,6 +268,7 @@ module Solargraph
268
268
  base = base.base
269
269
  end
270
270
  closest = found.typify(api_map) if found
271
+ # @todo remove the internal_or_core? check at a higher-than-strict level
271
272
  if !found || found.is_a?(Pin::BaseVariable) || (closest.defined? && internal_or_core?(found))
272
273
  unless closest.generic? || ignored_pins.include?(found)
273
274
  result.push Problem.new(location, "Unresolved call to #{missing.links.last.word}")
@@ -290,17 +291,31 @@ module Solargraph
290
291
  result = []
291
292
  base = chain
292
293
  until base.links.length == 1 && base.undefined?
294
+ last_base_link = base.links.last
295
+ break unless last_base_link.is_a?(Solargraph::Source::Chain::Call)
296
+
297
+ arguments = last_base_link.arguments
298
+
293
299
  pins = base.define(api_map, block_pin, locals)
294
300
 
295
- if pins.first.is_a?(Pin::DelegatedMethod) && !pins.first.resolvable?(api_map)
301
+ first_pin = pins.first
302
+ if first_pin.is_a?(Pin::DelegatedMethod) && !first_pin.resolvable?(api_map)
296
303
  # Do nothing, as we can't find the actual method implementation
297
- elsif pins.first.is_a?(Pin::Method)
304
+ elsif first_pin.is_a?(Pin::Method)
298
305
  # @type [Pin::Method]
299
- pin = pins.first
306
+ pin = first_pin
300
307
  ap = if base.links.last.is_a?(Solargraph::Source::Chain::ZSuper)
301
308
  arity_problems_for(pin, fake_args_for(block_pin), location)
309
+ elsif pin.path == 'Class#new'
310
+ fqns = if base.links.one?
311
+ block_pin.namespace
312
+ else
313
+ base.base.infer(api_map, block_pin, locals).namespace
314
+ end
315
+ init = api_map.get_method_stack(fqns, 'initialize').first
316
+ init ? arity_problems_for(init, arguments, location) : []
302
317
  else
303
- arity_problems_for(pin, base.links.last.arguments, location)
318
+ arity_problems_for(pin, arguments, location)
304
319
  end
305
320
  unless ap.empty?
306
321
  result.concat ap
@@ -313,18 +328,26 @@ module Solargraph
313
328
  all_errors = []
314
329
  pin.signatures.sort { |sig| sig.parameters.length }.each do |sig|
315
330
  errors = []
316
- # @todo these should be able to be probed
317
- # @param par [Parameter]
318
- # @param idx [Integer]
319
331
  sig.parameters.each_with_index do |par, idx|
320
- argchain = base.links.last.arguments[idx]
332
+ # @todo add logic mapping up restarg parameters with
333
+ # arguments (including restarg arguments). Use tuples
334
+ # when possible, and when not, ensure provably
335
+ # incorrect situations are detected.
336
+ break if par.decl == :restarg # bail out pending better arg processing
337
+ argchain = arguments[idx]
321
338
  if argchain.nil?
322
339
  if par.decl == :arg
323
- errors.push Problem.new(location, "Not enough arguments to #{pin.path}")
324
- next
340
+ final_arg = arguments.last
341
+ if final_arg && final_arg.node.type == :splat
342
+ argchain = final_arg
343
+ next # don't try to apply the type of the splat - unlikely to be specific enough
344
+ else
345
+ errors.push Problem.new(location, "Not enough arguments to #{pin.path}")
346
+ next
347
+ end
325
348
  else
326
- last = base.links.last.arguments.last
327
- argchain = last if last && [:kwsplat, :hash].include?(last.node.type)
349
+ final_arg = arguments.last
350
+ argchain = final_arg if final_arg && [:kwsplat, :hash].include?(final_arg.node.type)
328
351
  end
329
352
  end
330
353
  if argchain
@@ -332,7 +355,29 @@ module Solargraph
332
355
  errors.concat kwarg_problems_for sig, argchain, api_map, block_pin, locals, location, pin, params, idx
333
356
  next
334
357
  else
358
+ if argchain.node.type == :splat && argchain == arguments.last
359
+ final_arg = argchain
360
+ end
361
+ if (final_arg && final_arg.node.type == :splat)
362
+ # The final argument given has been seen and was a
363
+ # splat, which doesn't give us useful types or
364
+ # arities against positional parameters, so let's
365
+ # continue on in case there are any required
366
+ # kwargs we should warn about
367
+ next
368
+ end
369
+
370
+ if argchain.node.type == :splat && par != sig.parameters.last
371
+ # we have been given a splat and there are more
372
+ # arguments to come.
373
+
374
+ # @todo Improve this so that we can skip past the
375
+ # rest of the positional parameters here but still
376
+ # process the kwargs
377
+ break
378
+ end
335
379
  ptype = params.key?(par.name) ? params[par.name][:qualified] : ComplexType::UNDEFINED
380
+ ptype = ptype.self_to(par.context.namespace)
336
381
  if ptype.nil?
337
382
  # @todo Some level (strong, I guess) should require the param here
338
383
  else
@@ -442,7 +487,7 @@ module Solargraph
442
487
  def first_param_hash(pins)
443
488
  pins.each do |pin|
444
489
  # @todo this assignment from parametric use of Hash should not lose its generic
445
- # @type [Hash{String => Hash{Symbol => BasicObject}]
490
+ # @type [Hash{String => Hash{Symbol => BasicObject}}]
446
491
  result = param_hash(pin)
447
492
  return result unless result.empty?
448
493
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- VERSION = '0.53.0'
4
+ VERSION = '0.54.0'
5
5
  end
@@ -14,7 +14,8 @@ module Solargraph
14
14
  # @return [String]
15
15
  attr_reader :directory
16
16
 
17
- # @return [Hash{String => BasicObject}]
17
+ # @todo To make this strongly typed we'll need a record syntax
18
+ # @return [Hash{String => undefined}]
18
19
  attr_reader :raw_data
19
20
 
20
21
  # @param directory [String]
@@ -25,9 +25,11 @@ module Solargraph
25
25
 
26
26
  # @param directory [String]
27
27
  # @param config [Config, nil]
28
- def initialize directory = '', config = nil
28
+ # @param server [Hash]
29
+ def initialize directory = '', config = nil, server = {}
29
30
  @directory = directory
30
31
  @config = config
32
+ @server = server
31
33
  load_sources
32
34
  @gemnames = []
33
35
  @require_paths = generate_require_paths
@@ -126,6 +128,11 @@ module Solargraph
126
128
  end
127
129
  end
128
130
 
131
+ # @return [String, nil]
132
+ def rbs_collection_path
133
+ @gem_rbs_collection ||= read_rbs_collection_path
134
+ end
135
+
129
136
  # Synchronize the workspace from the provided updater.
130
137
  #
131
138
  # @param updater [Source::Updater]
@@ -134,8 +141,19 @@ module Solargraph
134
141
  source_hash[updater.filename] = source_hash[updater.filename].synchronize(updater)
135
142
  end
136
143
 
144
+ # @return [String]
145
+ def command_path
146
+ server['commandPath'] || 'solargraph'
147
+ end
148
+
137
149
  private
138
150
 
151
+ # The language server configuration (or an empty hash if the workspace was
152
+ # not initialized from a server).
153
+ #
154
+ # @return [Hash]
155
+ attr_reader :server
156
+
139
157
  # @return [Hash{String => Solargraph::Source}]
140
158
  def source_hash
141
159
  @source_hash ||= {}
@@ -209,5 +227,13 @@ module Solargraph
209
227
  end
210
228
  end
211
229
  end
230
+
231
+ # @return [String, nil]
232
+ def read_rbs_collection_path
233
+ yaml_file = File.join(directory, 'rbs_collection.yaml')
234
+ return unless File.file?(yaml_file)
235
+
236
+ YAML.load_file(yaml_file)&.fetch('path')
237
+ end
212
238
  end
213
239
  end
@@ -19,17 +19,20 @@ module Solargraph
19
19
  gates: [code_object.namespace.to_s]
20
20
  )
21
21
  location = object_location(code_object, spec)
22
+ name ||= code_object.name.to_s
23
+ return_type = ComplexType::SELF if name == 'new'
22
24
  comments = code_object.docstring ? code_object.docstring.all.to_s : ''
23
25
  pin = Pin::Method.new(
24
26
  location: location,
25
27
  closure: closure,
26
- name: name || code_object.name.to_s,
28
+ name: name,
27
29
  comments: comments,
28
30
  scope: scope || code_object.scope,
29
31
  visibility: visibility || code_object.visibility,
30
32
  # @todo Might need to convert overloads to signatures
31
33
  parameters: [],
32
- explicit: code_object.is_explicit?
34
+ explicit: code_object.is_explicit?,
35
+ return_type: return_type
33
36
  )
34
37
  pin.parameters.concat get_parameters(code_object, location, comments, pin)
35
38
  pin
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solargraph
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.53.0
4
+ version: 0.54.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fred Snyder
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-03-25 00:00:00.000000000 Z
11
+ date: 2025-04-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: backport
@@ -428,7 +428,6 @@ files:
428
428
  - lib/solargraph/language_server/completion_item_kinds.rb
429
429
  - lib/solargraph/language_server/error_codes.rb
430
430
  - lib/solargraph/language_server/host.rb
431
- - lib/solargraph/language_server/host/cataloger.rb
432
431
  - lib/solargraph/language_server/host/diagnoser.rb
433
432
  - lib/solargraph/language_server/host/dispatch.rb
434
433
  - lib/solargraph/language_server/host/message_worker.rb
@@ -479,6 +478,7 @@ files:
479
478
  - lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb
480
479
  - lib/solargraph/language_server/message/workspace/workspace_symbol.rb
481
480
  - lib/solargraph/language_server/message_types.rb
481
+ - lib/solargraph/language_server/progress.rb
482
482
  - lib/solargraph/language_server/request.rb
483
483
  - lib/solargraph/language_server/symbol_kinds.rb
484
484
  - lib/solargraph/language_server/transport.rb
@@ -511,6 +511,7 @@ files:
511
511
  - lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb
512
512
  - lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb
513
513
  - lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb
514
+ - lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb
514
515
  - lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb
515
516
  - lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb
516
517
  - lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb
@@ -558,7 +559,6 @@ files:
558
559
  - lib/solargraph/rbs_map/conversions.rb
559
560
  - lib/solargraph/rbs_map/core_fills.rb
560
561
  - lib/solargraph/rbs_map/core_map.rb
561
- - lib/solargraph/rbs_map/core_signs.rb
562
562
  - lib/solargraph/rbs_map/stdlib_map.rb
563
563
  - lib/solargraph/server_methods.rb
564
564
  - lib/solargraph/shell.rb
@@ -1,57 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Solargraph
4
- module LanguageServer
5
- class Host
6
- # An asynchronous library cataloging handler.
7
- #
8
- class Cataloger
9
- # @param host [Host]
10
- def initialize host
11
- @host = host
12
- @stopped = true
13
- end
14
-
15
- # Stop the catalog thread.
16
- #
17
- # @return [void]
18
- def stop
19
- @stopped = true
20
- end
21
-
22
- # True if the cataloger is stopped.
23
- #
24
- # @return [Boolean]
25
- def stopped?
26
- @stopped
27
- end
28
-
29
- # Start the catalog thread.
30
- #
31
- # @return [void]
32
- def start
33
- return unless stopped?
34
- @stopped = false
35
- Thread.new do
36
- until stopped?
37
- tick
38
- sleep 0.1
39
- end
40
- end
41
- end
42
-
43
- # Perform cataloging.
44
- #
45
- # @return [void]
46
- def tick
47
- host.catalog
48
- end
49
-
50
- private
51
-
52
- # @return [Host]
53
- attr_reader :host
54
- end
55
- end
56
- end
57
- end
@@ -1,35 +0,0 @@
1
- module Solargraph
2
- class RbsMap
3
- module CoreSigns
4
- Override = Pin::Reference::Override
5
-
6
- class Stub
7
- attr_reader :parameters
8
-
9
- attr_reader :return_type
10
-
11
- # @param parameters [Array<Hash>]
12
- # @param return_type [String]
13
- def initialize parameters, return_type
14
- @parameters = parameters
15
- @return_type = return_type
16
- end
17
- end
18
-
19
- SIGNATURE_MAP = {
20
- 'Object#class' => [
21
- Stub.new(
22
- [],
23
- 'Class<self>'
24
- )
25
- ]
26
- }
27
-
28
- # @param path [String]
29
- # @return [Array<Stub>]
30
- def self.sign path
31
- SIGNATURE_MAP[path]
32
- end
33
- end
34
- end
35
- end