solargraph 0.55.4 → 0.55.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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/plugins.yml +2 -0
  3. data/.github/workflows/typecheck.yml +2 -0
  4. data/.gitignore +2 -0
  5. data/CHANGELOG.md +3 -0
  6. data/README.md +13 -3
  7. data/lib/solargraph/api_map/index.rb +23 -15
  8. data/lib/solargraph/api_map/store.rb +2 -1
  9. data/lib/solargraph/api_map.rb +53 -27
  10. data/lib/solargraph/complex_type/type_methods.rb +5 -1
  11. data/lib/solargraph/complex_type/unique_type.rb +7 -0
  12. data/lib/solargraph/convention/base.rb +3 -3
  13. data/lib/solargraph/convention.rb +3 -3
  14. data/lib/solargraph/doc_map.rb +189 -43
  15. data/lib/solargraph/gem_pins.rb +53 -38
  16. data/lib/solargraph/language_server/host.rb +9 -1
  17. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +1 -0
  18. data/lib/solargraph/language_server/message/extended/document.rb +5 -2
  19. data/lib/solargraph/language_server/message/extended/document_gems.rb +3 -3
  20. data/lib/solargraph/library.rb +7 -4
  21. data/lib/solargraph/location.rb +13 -0
  22. data/lib/solargraph/parser/parser_gem/class_methods.rb +5 -8
  23. data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +2 -2
  24. data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +2 -2
  25. data/lib/solargraph/pin/base.rb +268 -24
  26. data/lib/solargraph/pin/base_variable.rb +9 -8
  27. data/lib/solargraph/pin/callable.rb +69 -0
  28. data/lib/solargraph/pin/closure.rb +12 -0
  29. data/lib/solargraph/pin/local_variable.rb +8 -5
  30. data/lib/solargraph/pin/method.rb +134 -17
  31. data/lib/solargraph/pin/parameter.rb +43 -6
  32. data/lib/solargraph/pin/signature.rb +38 -0
  33. data/lib/solargraph/pin_cache.rb +185 -0
  34. data/lib/solargraph/position.rb +9 -0
  35. data/lib/solargraph/range.rb +9 -0
  36. data/lib/solargraph/rbs_map/conversions.rb +19 -8
  37. data/lib/solargraph/rbs_map/core_map.rb +31 -9
  38. data/lib/solargraph/rbs_map/stdlib_map.rb +15 -5
  39. data/lib/solargraph/rbs_map.rb +74 -17
  40. data/lib/solargraph/shell.rb +11 -15
  41. data/lib/solargraph/source_map.rb +0 -17
  42. data/lib/solargraph/version.rb +1 -1
  43. data/lib/solargraph/views/_method.erb +10 -10
  44. data/lib/solargraph/views/_namespace.erb +3 -3
  45. data/lib/solargraph/views/document.erb +10 -10
  46. data/lib/solargraph/workspace.rb +15 -5
  47. data/lib/solargraph/yardoc.rb +3 -11
  48. data/lib/solargraph.rb +10 -12
  49. data/rbs_collection.yaml +19 -0
  50. data/solargraph.gemspec +1 -0
  51. metadata +19 -7
  52. data/lib/solargraph/cache.rb +0 -77
@@ -25,11 +25,63 @@ module Solargraph
25
25
  closure.namespace
26
26
  end
27
27
 
28
+ def combine_blocks(other)
29
+ if block.nil?
30
+ other.block
31
+ elsif other.block.nil?
32
+ block
33
+ else
34
+ choose_pin_attr(other, :block)
35
+ end
36
+ end
37
+
38
+ # @param other [self]
39
+ # @param attrs [Hash{Symbol => Object}]
40
+ #
41
+ # @return [self]
42
+ def combine_with(other, attrs={})
43
+ new_attrs = {
44
+ block: combine_blocks(other),
45
+ return_type: combine_return_type(other),
46
+ }.merge(attrs)
47
+ new_attrs[:parameters] = choose_parameters(other).clone.freeze unless new_attrs.key?(:parameters)
48
+ super(other, new_attrs)
49
+ end
50
+
28
51
  # @return [::Array<String>]
29
52
  def parameter_names
30
53
  @parameter_names ||= parameters.map(&:name)
31
54
  end
32
55
 
56
+ def generics
57
+ []
58
+ end
59
+
60
+ def choose_parameters(other)
61
+ raise "Trying to combine two pins with different arities - \nself =#{inspect}, \nother=#{other.inspect}, \n\n self.arity=#{self.arity}, \nother.arity=#{other.arity}" if other.arity != arity
62
+ parameters.zip(other.parameters).map do |param, other_param|
63
+ if param.nil? && other_param.block?
64
+ other_param
65
+ elsif other_param.nil? && param.block?
66
+ param
67
+ else
68
+ param.combine_with(other_param)
69
+ end
70
+ end
71
+ end
72
+
73
+ def blockless_parameters
74
+ if parameters.last&.block?
75
+ parameters[0..-2]
76
+ else
77
+ parameters
78
+ end
79
+ end
80
+
81
+ def arity
82
+ [generics, blockless_parameters.map(&:arity_decl), block&.arity]
83
+ end
84
+
33
85
  # @param generics_to_resolve [Enumerable<String>]
34
86
  # @param arg_types [Array<ComplexType>, nil]
35
87
  # @param return_type_context [ComplexType, nil]
@@ -61,6 +113,23 @@ module Solargraph
61
113
  callable
62
114
  end
63
115
 
116
+ def typify api_map
117
+ type = super
118
+ return type if type.defined?
119
+ if method_name.end_with?('?')
120
+ logger.debug { "Callable#typify(self=#{self}) => Boolean (? suffix)" }
121
+ ComplexType::BOOLEAN
122
+ else
123
+ logger.debug { "Callable#typify(self=#{self}) => undefined" }
124
+ ComplexType::UNDEFINED
125
+ end
126
+ end
127
+
128
+ def method_name
129
+ raise "closure was nil in #{self.inspect}" if closure.nil?
130
+ @method_name ||= closure.name
131
+ end
132
+
64
133
  # @param generics_to_resolve [::Array<String>]
65
134
  # @param arg_types [Array<ComplexType>, nil]
66
135
  # @param return_type_context [ComplexType, nil]
@@ -19,6 +19,18 @@ module Solargraph
19
19
  @generic_defaults ||= {}
20
20
  end
21
21
 
22
+ # @param other [self]
23
+ # @param attrs [Hash{Symbol => Object}]
24
+ #
25
+ # @return [self]
26
+ def combine_with(other, attrs={})
27
+ new_attrs = {
28
+ scope: assert_same(other, :scope),
29
+ generics: generics.empty? ? other.generics : generics,
30
+ }.merge(attrs)
31
+ super(other, new_attrs)
32
+ end
33
+
22
34
  def context
23
35
  @context ||= begin
24
36
  result = super
@@ -21,11 +21,14 @@ module Solargraph
21
21
  @presence_certain = presence_certain
22
22
  end
23
23
 
24
- # @param pin [self]
25
- def try_merge! pin
26
- return false unless super
27
- @presence = pin.presence
28
- true
24
+ def combine_with(other, attrs={})
25
+ new_attrs = {
26
+ assignment: assert_same(other, :assignment),
27
+ presence_certain: assert_same(other, :presence_certain?),
28
+ }.merge(attrs)
29
+ new_attrs[:presence] = assert_same(other, :presence) unless attrs.key?(:presence)
30
+
31
+ super(other, new_attrs)
29
32
  end
30
33
 
31
34
  # @param other_closure [Pin::Closure]
@@ -33,6 +33,70 @@ module Solargraph
33
33
  @anon_splat = anon_splat
34
34
  end
35
35
 
36
+ # @return [Array<Pin::Signature>]
37
+ def combine_all_signature_pins(*signature_pins)
38
+ by_arity = {}
39
+ signature_pins.each do |signature_pin|
40
+ by_arity[signature_pin.arity] ||= []
41
+ by_arity[signature_pin.arity] << signature_pin
42
+ end
43
+ by_arity.transform_values! do |same_arity_pins|
44
+ same_arity_pins.reduce(nil) do |memo, signature|
45
+ next signature if memo.nil?
46
+ memo.combine_with(signature)
47
+ end
48
+ end
49
+ by_arity.values.flatten
50
+ end
51
+
52
+ # @param other [Pin::Method]
53
+ # @return [Symbol]
54
+ def combine_visibility(other)
55
+ if dodgy_visibility_source? && !other.dodgy_visibility_source?
56
+ other.visibility
57
+ elsif other.dodgy_visibility_source? && !dodgy_visibility_source?
58
+ visibility
59
+ else
60
+ assert_same(other, :visibility)
61
+ end
62
+ end
63
+
64
+ # @param other [Pin::Method]
65
+ # @return [Array<Pin::Signature>]
66
+ def combine_signatures(other)
67
+ all_undefined = signatures.all? { |sig| sig.return_type.undefined? }
68
+ other_all_undefined = other.signatures.all? { |sig| sig.return_type.undefined? }
69
+ if all_undefined && !other_all_undefined
70
+ other.signatures
71
+ elsif other_all_undefined && !all_undefined
72
+ signatures
73
+ else
74
+ combine_all_signature_pins(*signatures, *other.signatures)
75
+ end
76
+ end
77
+
78
+ def combine_with(other, attrs = {})
79
+ sigs = combine_signatures(other)
80
+ parameters = if sigs.length > 0
81
+ [].freeze
82
+ else
83
+ choose(other, :parameters).clone.freeze
84
+ end
85
+ new_attrs = {
86
+ visibility: combine_visibility(other),
87
+ explicit: explicit? || other.explicit?,
88
+ block: combine_blocks(other),
89
+ node: choose_node(other, :node),
90
+ attribute: prefer_rbs_location(other, :attribute?),
91
+ parameters: parameters,
92
+ signatures: sigs,
93
+ anon_splat: assert_same(other, :anon_splat?),
94
+ return_type: nil # pulled from signatures on first call
95
+ }.merge(attrs)
96
+ super(other, new_attrs)
97
+ end
98
+
99
+ # @param other [Pin::Method]
36
100
  def == other
37
101
  super && other.node == node
38
102
  end
@@ -44,11 +108,24 @@ module Solargraph
44
108
  sig.transform_types(&transform)
45
109
  end
46
110
  m.block = block&.transform_types(&transform)
47
- m.signature_help = nil
48
- m.documentation = nil
111
+ m.reset_generated!
49
112
  m
50
113
  end
51
114
 
115
+ # @return [void]
116
+ def reset_generated!
117
+ super
118
+ unless signatures.empty?
119
+ return_type = nil
120
+ @block = :undefined
121
+ parameters = []
122
+ end
123
+ block&.reset_generated!
124
+ @signatures&.each(&:reset_generated!)
125
+ signature_help = nil
126
+ documentation = nil
127
+ end
128
+
52
129
  def all_rooted?
53
130
  super && parameters.all?(&:all_rooted?) && (!block || block&.all_rooted?) && signatures.all?(&:all_rooted?)
54
131
  end
@@ -57,8 +134,7 @@ module Solargraph
57
134
  # @return [Pin::Method]
58
135
  def with_single_signature(signature)
59
136
  m = proxy signature.return_type
60
- m.signature_help = nil
61
- m.documentation = nil
137
+ m.reset_generated!
62
138
  # @todo populating the single parameters/return_type/block
63
139
  # arguments here seems to be needed for some specs to pass,
64
140
  # even though we have a signature with the same information.
@@ -123,7 +199,7 @@ module Solargraph
123
199
  )
124
200
  end
125
201
  yield_return_type = ComplexType.try_parse(*yieldreturn_tags.flat_map(&:types))
126
- block = Signature.new(generics: generics, parameters: yield_parameters, return_type: yield_return_type, source: source)
202
+ block = Signature.new(generics: generics, parameters: yield_parameters, return_type: yield_return_type, source: source, closure: self)
127
203
  end
128
204
  signature = Signature.new(generics: generics, parameters: parameters, return_type: return_type, block: block, closure: self, source: source)
129
205
  block.closure = signature if block
@@ -142,6 +218,14 @@ module Solargraph
142
218
  end
143
219
  end
144
220
 
221
+ # @param return_type [ComplexType]
222
+ # @return [self]
223
+ def proxy_with_signatures return_type
224
+ out = proxy return_type
225
+ out.signatures = out.signatures.map { |sig| sig.proxy return_type }
226
+ out
227
+ end
228
+
145
229
  # @return [String, nil]
146
230
  def detail
147
231
  # This property is not cached in an instance variable because it can
@@ -193,12 +277,26 @@ module Solargraph
193
277
  @path ||= "#{namespace}#{(scope == :instance ? '#' : '.')}#{name}"
194
278
  end
195
279
 
280
+ # @return [String]
281
+ def method_name
282
+ name
283
+ end
284
+
196
285
  def typify api_map
286
+ logger.debug { "Method#typify(self=#{self}, binder=#{binder}, closure=#{closure}, context=#{context.rooted_tags}, return_type=#{return_type.rooted_tags}) - starting" }
197
287
  decl = super
198
- return decl unless decl.undefined?
288
+ unless decl.undefined?
289
+ logger.debug { "Method#typify(self=#{self}, binder=#{binder}, closure=#{closure}, context=#{context}) => #{decl.rooted_tags.inspect} - decl found" }
290
+ return decl
291
+ end
199
292
  type = see_reference(api_map) || typify_from_super(api_map)
200
- return type.qualify(api_map, namespace) unless type.nil?
201
- name.end_with?('?') ? ComplexType::BOOLEAN : ComplexType::UNDEFINED
293
+ logger.debug { "Method#typify(self=#{self}) - type=#{type&.rooted_tags.inspect}" }
294
+ unless type.nil?
295
+ qualified = type.qualify(api_map, namespace)
296
+ logger.debug { "Method#typify(self=#{self}) => #{qualified.rooted_tags.inspect}" }
297
+ return qualified
298
+ end
299
+ super
202
300
  end
203
301
 
204
302
  # @sg-ignore
@@ -285,14 +383,6 @@ module Solargraph
285
383
  attribute? ? infer_from_iv(api_map) : infer_from_return_nodes(api_map)
286
384
  end
287
385
 
288
- # @param pin [Pin::Method]
289
- def try_merge! pin
290
- return false unless super
291
- @node = pin.node
292
- @resolved_ref_tag = false
293
- true
294
- end
295
-
296
386
  # @return [::Array<Pin::Method>]
297
387
  def overloads
298
388
  # Ignore overload tags with nil parameters. If it's not an array, the
@@ -313,6 +403,7 @@ module Solargraph
313
403
  source: :overloads
314
404
  )
315
405
  end,
406
+ closure: self,
316
407
  return_type: ComplexType.try_parse(*tag.docstring.tags(:return).flat_map(&:types)),
317
408
  source: :overloads,
318
409
  )
@@ -347,6 +438,12 @@ module Solargraph
347
438
  self
348
439
  end
349
440
 
441
+ # @param api_map [ApiMap]
442
+ # @return [Array<Pin::Method>]
443
+ def rest_of_stack api_map
444
+ api_map.get_method_stack(method_namespace, method_name, scope: scope).reject { |pin| pin.path == path }
445
+ end
446
+
350
447
  protected
351
448
 
352
449
  attr_writer :block
@@ -355,6 +452,21 @@ module Solargraph
355
452
 
356
453
  attr_writer :documentation
357
454
 
455
+ def dodgy_visibility_source?
456
+ # as of 2025-03-12, the RBS generator used for
457
+ # e.g. activesupport did not understand 'private' markings
458
+ # inside 'class << self' blocks, but YARD did OK at it
459
+ source == :rbs && scope == :class && type_location&.filename&.include?('generated') && return_type.undefined? ||
460
+ # YARD's RBS generator seems to miss a lot of should-be protected instance methods
461
+ source == :rbs && scope == :instance && namespace.start_with?('YARD::') ||
462
+ # private on attr_readers seems to be broken in Prism's auto-generator script
463
+ source == :rbs && scope == :instance && namespace.start_with?('Prism::') ||
464
+ # The RBS for the RBS gem itself seems to use private as a
465
+ # 'is this a public API' concept, more aggressively than the
466
+ # actual code. Let's respect that and ignore the actual .rb file.
467
+ source == :yardoc && scope == :instance && namespace.start_with?('RBS::')
468
+ end
469
+
358
470
  private
359
471
 
360
472
  # @param name [String]
@@ -415,10 +527,15 @@ module Solargraph
415
527
  resolve_reference match[1], api_map
416
528
  end
417
529
 
530
+ # @return [String]
531
+ def method_namespace
532
+ namespace
533
+ end
534
+
418
535
  # @param api_map [ApiMap]
419
536
  # @return [ComplexType, nil]
420
537
  def typify_from_super api_map
421
- stack = api_map.get_method_stack(namespace, name, scope: scope).reject { |pin| pin.path == path }
538
+ stack = rest_of_stack api_map
422
539
  return nil if stack.empty?
423
540
  stack.each do |pin|
424
541
  return pin.return_type unless pin.return_type.undefined?
@@ -21,6 +21,23 @@ module Solargraph
21
21
  @decl = decl
22
22
  end
23
23
 
24
+ def type_location
25
+ super || closure&.type_location
26
+ end
27
+
28
+ def location
29
+ super || closure&.type_location
30
+ end
31
+
32
+ def combine_with(other, attrs={})
33
+ new_attrs = {
34
+ decl: assert_same(other, :decl),
35
+ presence: choose(other, :presence),
36
+ asgn_code: choose(other, :asgn_code),
37
+ }.merge(attrs)
38
+ super(other, new_attrs)
39
+ end
40
+
24
41
  def keyword?
25
42
  [:kwarg, :kwoptarg].include?(decl)
26
43
  end
@@ -29,6 +46,32 @@ module Solargraph
29
46
  decl == :kwrestarg || (assignment && [:HASH, :hash].include?(assignment.type))
30
47
  end
31
48
 
49
+ def needs_consistent_name?
50
+ keyword?
51
+ end
52
+
53
+ # @return [String]
54
+ def arity_decl
55
+ name = (self.name || '(anon)')
56
+ type = (return_type&.to_rbs || 'untyped')
57
+ case decl
58
+ when :arg
59
+ ""
60
+ when :optarg
61
+ "?"
62
+ when :kwarg
63
+ "#{name}:"
64
+ when :kwoptarg
65
+ "?#{name}:"
66
+ when :restarg
67
+ "*"
68
+ when :kwrestarg
69
+ "**"
70
+ else
71
+ "(unknown decl: #{decl})"
72
+ end
73
+ end
74
+
32
75
  def arg?
33
76
  decl == :arg
34
77
  end
@@ -132,12 +175,6 @@ module Solargraph
132
175
  tag.text
133
176
  end
134
177
 
135
- # @param pin [Pin::Parameter]
136
- def try_merge! pin
137
- return false unless super && closure == pin.closure
138
- true
139
- end
140
-
141
178
  private
142
179
 
143
180
  # @return [YARD::Tags::Tag, nil]
@@ -16,6 +16,44 @@ module Solargraph
16
16
  def identity
17
17
  @identity ||= "signature#{object_id}"
18
18
  end
19
+
20
+ attr_writer :closure
21
+
22
+ def dodgy_return_type_source?
23
+ super || closure&.dodgy_return_type_source?
24
+ end
25
+
26
+ def type_location
27
+ super || closure&.type_location
28
+ end
29
+
30
+ def location
31
+ super || closure&.location
32
+ end
33
+
34
+ def typify api_map
35
+ if return_type.defined?
36
+ qualified = return_type.qualify(api_map, closure.namespace)
37
+ logger.debug { "Signature#typify(self=#{self}) => #{qualified.rooted_tags.inspect}" }
38
+ return qualified
39
+ end
40
+ return ComplexType::UNDEFINED if closure.nil?
41
+ return ComplexType::UNDEFINED unless closure.is_a?(Pin::Method)
42
+ method_stack = closure.rest_of_stack api_map
43
+ logger.debug { "Signature#typify(self=#{self}) - method_stack: #{method_stack}" }
44
+ method_stack.each do |pin|
45
+ sig = pin.signatures.find { |s| s.arity == self.arity }
46
+ next unless sig
47
+ unless sig.return_type.undefined?
48
+ qualified = sig.return_type.qualify(api_map, closure.namespace)
49
+ logger.debug { "Signature#typify(self=#{self}) => #{qualified.rooted_tags.inspect}" }
50
+ return qualified
51
+ end
52
+ end
53
+ out = super
54
+ logger.debug { "Signature#typify(self=#{self}) => #{out}" }
55
+ out
56
+ end
19
57
  end
20
58
  end
21
59
  end
@@ -0,0 +1,185 @@
1
+ require 'fileutils'
2
+ require 'rbs'
3
+
4
+ module Solargraph
5
+ module PinCache
6
+ class << self
7
+ include Logging
8
+
9
+ # The base directory where cached YARD documentation and serialized pins are serialized
10
+ #
11
+ # @return [String]
12
+ def base_dir
13
+ # The directory is not stored in a variable so it can be overridden
14
+ # in specs.
15
+ ENV['SOLARGRAPH_CACHE'] ||
16
+ (ENV['XDG_CACHE_HOME'] ? File.join(ENV['XDG_CACHE_HOME'], 'solargraph') : nil) ||
17
+ File.join(Dir.home, '.cache', 'solargraph')
18
+ end
19
+
20
+ # The working directory for the current Ruby, RBS, and Solargraph versions.
21
+ #
22
+ # @return [String]
23
+ def work_dir
24
+ # The directory is not stored in a variable so it can be overridden
25
+ # in specs.
26
+ File.join(base_dir, "ruby-#{RUBY_VERSION}", "rbs-#{RBS::VERSION}", "solargraph-#{Solargraph::VERSION}")
27
+ end
28
+
29
+ def yardoc_path gemspec
30
+ File.join(base_dir, "yard-#{YARD::VERSION}", "#{gemspec.name}-#{gemspec.version}.yardoc")
31
+ end
32
+
33
+ def stdlib_path
34
+ File.join(work_dir, 'stdlib')
35
+ end
36
+
37
+ def stdlib_require_path require
38
+ File.join(stdlib_path, "#{require}.ser")
39
+ end
40
+
41
+ def deserialize_stdlib_require require
42
+ load(stdlib_require_path(require))
43
+ end
44
+
45
+ def serialize_stdlib_require require, pins
46
+ save(stdlib_require_path(require), pins)
47
+ end
48
+
49
+ def core_path
50
+ File.join(work_dir, 'core.ser')
51
+ end
52
+
53
+ def deserialize_core
54
+ load(core_path)
55
+ end
56
+
57
+ def serialize_core pins
58
+ save(core_path, pins)
59
+ end
60
+
61
+ def yard_gem_path gemspec
62
+ File.join(work_dir, 'yard', "#{gemspec.name}-#{gemspec.version}.ser")
63
+ end
64
+
65
+ def deserialize_yard_gem(gemspec)
66
+ load(yard_gem_path(gemspec))
67
+ end
68
+
69
+ def serialize_yard_gem(gemspec, pins)
70
+ save(yard_gem_path(gemspec), pins)
71
+ end
72
+
73
+ def has_yard?(gemspec)
74
+ exist?(yard_gem_path(gemspec))
75
+ end
76
+
77
+ def rbs_collection_path(gemspec, hash)
78
+ File.join(work_dir, 'rbs', "#{gemspec.name}-#{gemspec.version}-#{hash || 0}.ser")
79
+ end
80
+
81
+ def rbs_collection_path_prefix(gemspec)
82
+ File.join(work_dir, 'rbs', "#{gemspec.name}-#{gemspec.version}-")
83
+ end
84
+
85
+ def deserialize_rbs_collection_gem(gemspec, hash)
86
+ load(rbs_collection_path(gemspec, hash))
87
+ end
88
+
89
+ def serialize_rbs_collection_gem(gemspec, hash, pins)
90
+ save(rbs_collection_path(gemspec, hash), pins)
91
+ end
92
+
93
+ def combined_path(gemspec, hash)
94
+ File.join(work_dir, 'combined', "#{gemspec.name}-#{gemspec.version}-#{hash || 0}.ser")
95
+ end
96
+
97
+ def combined_path_prefix(gemspec)
98
+ File.join(work_dir, 'combined', "#{gemspec.name}-#{gemspec.version}-")
99
+ end
100
+
101
+ def serialize_combined_gem(gemspec, hash, pins)
102
+ save(combined_path(gemspec, hash), pins)
103
+ end
104
+
105
+ def deserialize_combined_gem gemspec, hash
106
+ load(combined_path(gemspec, hash))
107
+ end
108
+
109
+ def has_rbs_collection?(gemspec, hash)
110
+ exist?(rbs_collection_path(gemspec, hash))
111
+ end
112
+
113
+ def uncache_core
114
+ uncache(core_path)
115
+ end
116
+
117
+ def uncache_stdlib
118
+ uncache(stdlib_path)
119
+ end
120
+
121
+ def uncache_gem(gemspec, out: nil)
122
+ uncache(yardoc_path(gemspec), out: out)
123
+ uncache_by_prefix(rbs_collection_path_prefix(gemspec), out: out)
124
+ uncache(yard_gem_path(gemspec), out: out)
125
+ uncache_by_prefix(combined_path_prefix(gemspec), out: out)
126
+ end
127
+
128
+ # @return [void]
129
+ def clear
130
+ FileUtils.rm_rf base_dir, secure: true
131
+ end
132
+
133
+ private
134
+
135
+ # @param file [String]
136
+ # @return [Array<Solargraph::Pin::Base>, nil]
137
+ def load file
138
+ return nil unless File.file?(file)
139
+ Marshal.load(File.read(file, mode: 'rb'))
140
+ rescue StandardError => e
141
+ Solargraph.logger.warn "Failed to load cached file #{file}: [#{e.class}] #{e.message}"
142
+ FileUtils.rm_f file
143
+ nil
144
+ end
145
+
146
+ def exist? *path
147
+ File.file? join(*path)
148
+ end
149
+
150
+ # @param path [Array<String>]
151
+ # @param pins [Array<Pin::Base>]
152
+ # @return [void]
153
+ def save file, pins
154
+ base = File.dirname(file)
155
+ FileUtils.mkdir_p base unless File.directory?(base)
156
+ ser = Marshal.dump(pins)
157
+ File.write file, ser, mode: 'wb'
158
+ logger.debug { "Cache#save: Saved #{pins.length} pins to #{file}" }
159
+ end
160
+
161
+ # @return [void]
162
+ # @param path_segments [Array<String>]
163
+ def uncache *path_segments, out: nil
164
+ path = File.join(*path_segments)
165
+ if File.exist?(path)
166
+ FileUtils.rm_rf path, secure: true
167
+ out.puts "Clearing pin cache in #{path}" unless out.nil?
168
+ end
169
+ end
170
+
171
+ # @return [void]
172
+ # @param path_segments [Array<String>]
173
+ def uncache_by_prefix *path_segments, out: nil
174
+ path = File.join(*path_segments)
175
+ glob = "#{path}*"
176
+ out.puts "Clearing pin cache in #{glob}" unless out.nil?
177
+ Dir.glob(glob).each do |file|
178
+ next unless File.file?(file)
179
+ FileUtils.rm_rf file, secure: true
180
+ out.puts "Clearing pin cache in #{file}" unless out.nil?
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end
@@ -26,6 +26,15 @@ module Solargraph
26
26
  [line, character]
27
27
  end
28
28
 
29
+ def <=>(other)
30
+ return nil unless other.is_a?(Position)
31
+ if line == other.line
32
+ character <=> other.character
33
+ else
34
+ line <=> other.line
35
+ end
36
+ end
37
+
29
38
  # Get a hash of the position. This representation is suitable for use in
30
39
  # the language server protocol.
31
40
  #
@@ -24,6 +24,15 @@ module Solargraph
24
24
  [start, ending]
25
25
  end
26
26
 
27
+ def <=>(other)
28
+ return nil unless other.is_a?(Range)
29
+ if start == other.start
30
+ ending <=> other.ending
31
+ else
32
+ start <=> other.start
33
+ end
34
+ end
35
+
27
36
  # Get a hash of the range. This representation is suitable for use in
28
37
  # the language server protocol.
29
38
  #