solargraph 0.55.4 → 0.56.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 (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 +17 -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 +200 -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 +6 -3
  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 +16 -18
  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 +6 -9
  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
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'pathname'
4
+ require 'benchmark'
5
+
3
6
  module Solargraph
4
7
  # A collection of pins generated from required gems.
5
8
  #
@@ -8,6 +11,7 @@ module Solargraph
8
11
 
9
12
  # @return [Array<String>]
10
13
  attr_reader :requires
14
+ alias required requires
11
15
 
12
16
  # @return [Array<Gem::Specification>]
13
17
  attr_reader :preferences
@@ -16,11 +20,27 @@ module Solargraph
16
20
  attr_reader :pins
17
21
 
18
22
  # @return [Array<Gem::Specification>]
19
- attr_reader :uncached_gemspecs
23
+ def uncached_gemspecs
24
+ (uncached_yard_gemspecs + uncached_rbs_collection_gemspecs).sort.
25
+ uniq { |gemspec| "#{gemspec.name}:#{gemspec.version}" }
26
+ end
27
+
28
+ # @return [Array<Gem::Specification>]
29
+ attr_reader :uncached_yard_gemspecs
30
+
31
+ # @return [Array<Gem::Specification>]
32
+ attr_reader :uncached_rbs_collection_gemspecs
33
+
34
+ attr_reader :rbs_collection_path
35
+
36
+ attr_reader :rbs_collection_config_path
20
37
 
21
38
  # @return [Workspace, nil]
22
39
  attr_reader :workspace
23
40
 
41
+ # @return [Environ]
42
+ attr_reader :environ
43
+
24
44
  # @param requires [Array<String>]
25
45
  # @param preferences [Array<Gem::Specification>]
26
46
  # @param workspace [Workspace, nil]
@@ -28,7 +48,58 @@ module Solargraph
28
48
  @requires = requires.compact
29
49
  @preferences = preferences.compact
30
50
  @workspace = workspace
31
- generate
51
+ @rbs_collection_path = workspace&.rbs_collection_path
52
+ @rbs_collection_config_path = workspace&.rbs_collection_config_path
53
+ @environ = Convention.for_global(self)
54
+ load_serialized_gem_pins
55
+ pins.concat @environ.pins
56
+ end
57
+
58
+ def cache_all!(out)
59
+ # if we log at debug level:
60
+ if logger.info?
61
+ gem_desc = uncached_gemspecs.map { |gemspec| "#{gemspec.name}:#{gemspec.version}" }.join(', ')
62
+ logger.info "Caching pins for gems: #{gem_desc}" unless uncached_gemspecs.empty?
63
+ end
64
+ logger.debug { "Caching for YARD: #{uncached_yard_gemspecs.map(&:name)}" }
65
+ logger.debug { "Caching for RBS collection: #{uncached_rbs_collection_gemspecs.map(&:name)}" }
66
+ load_serialized_gem_pins
67
+ uncached_gemspecs.each do |gemspec|
68
+ cache(gemspec, out: out)
69
+ end
70
+ load_serialized_gem_pins
71
+ @uncached_rbs_collection_gemspecs = []
72
+ @uncached_yard_gemspecs = []
73
+ end
74
+
75
+ def cache_yard_pins(gemspec, out)
76
+ pins = GemPins.build_yard_pins(gemspec)
77
+ PinCache.serialize_yard_gem(gemspec, pins)
78
+ logger.info { "Cached #{pins.length} YARD pins for gem #{gemspec.name}:#{gemspec.version}" } unless pins.empty?
79
+ end
80
+
81
+ def cache_rbs_collection_pins(gemspec, out)
82
+ rbs_map = RbsMap.from_gemspec(gemspec, rbs_collection_path, rbs_collection_config_path)
83
+ pins = rbs_map.pins
84
+ rbs_version_cache_key = rbs_map.cache_key
85
+ # cache pins even if result is zero, so we don't retry building pins
86
+ pins ||= []
87
+ PinCache.serialize_rbs_collection_gem(gemspec, rbs_version_cache_key, pins)
88
+ logger.info { "Cached #{pins.length} RBS collection pins for gem #{gemspec.name} #{gemspec.version} with cache_key #{rbs_version_cache_key.inspect}" unless pins.empty? }
89
+ end
90
+
91
+ # @param gemspec [Gem::Specification]
92
+ def cache(gemspec, rebuild: false, out: nil)
93
+ build_yard = uncached_yard_gemspecs.include?(gemspec) || rebuild
94
+ build_rbs_collection = uncached_rbs_collection_gemspecs.include?(gemspec) || rebuild
95
+ if build_yard || build_rbs_collection
96
+ type = []
97
+ type << 'YARD' if build_yard
98
+ type << 'RBS collection' if build_rbs_collection
99
+ out.puts("Caching #{type.join(' and ')} pins for gem #{gemspec.name}:#{gemspec.version}") if out
100
+ end
101
+ cache_yard_pins(gemspec, out) if build_yard
102
+ cache_rbs_collection_pins(gemspec, out) if build_rbs_collection
32
103
  end
33
104
 
34
105
  # @return [Array<Gem::Specification>]
@@ -41,9 +112,28 @@ module Solargraph
41
112
  @unresolved_requires ||= required_gems_map.select { |_, gemspecs| gemspecs.nil? }.keys
42
113
  end
43
114
 
44
- # @return [Hash{Gem::Specification => Array[Pin::Base]}]
45
- def self.gems_in_memory
46
- @gems_in_memory ||= {}
115
+ def self.all_yard_gems_in_memory
116
+ @yard_gems_in_memory ||= {}
117
+ end
118
+
119
+ def self.all_rbs_collection_gems_in_memory
120
+ @rbs_collection_gems_in_memory ||= {}
121
+ end
122
+
123
+ def yard_pins_in_memory
124
+ self.class.all_yard_gems_in_memory
125
+ end
126
+
127
+ def rbs_collection_pins_in_memory
128
+ self.class.all_rbs_collection_gems_in_memory[rbs_collection_path] ||= {}
129
+ end
130
+
131
+ def self.all_combined_pins_in_memory
132
+ @combined_pins_in_memory ||= {}
133
+ end
134
+
135
+ def combined_pins_in_memory
136
+ self.class.all_combined_pins_in_memory
47
137
  end
48
138
 
49
139
  # @return [Set<Gem::Specification>]
@@ -54,20 +144,29 @@ module Solargraph
54
144
  private
55
145
 
56
146
  # @return [void]
57
- def generate
147
+ def load_serialized_gem_pins
58
148
  @pins = []
59
- @uncached_gemspecs = []
60
- required_gems_map.each do |path, gemspecs|
61
- if gemspecs.nil?
62
- try_stdlib_map path
63
- else
64
- gemspecs.each do |gemspec|
65
- try_cache gemspec
66
- end
149
+ @uncached_yard_gemspecs = []
150
+ @uncached_rbs_collection_gemspecs = []
151
+ with_gemspecs, without_gemspecs = required_gems_map.partition { |_, v| v }
152
+ paths = Hash[without_gemspecs].keys
153
+ gemspecs = Hash[with_gemspecs].values.flatten.compact + dependencies.to_a
154
+
155
+ paths.each do |path|
156
+ rbs_pins = deserialize_stdlib_rbs_map path
157
+ end
158
+
159
+ logger.debug { "DocMap#load_serialized_gem_pins: Combining pins..." }
160
+ time = Benchmark.measure do
161
+ gemspecs.each do |gemspec|
162
+ pins = deserialize_combined_pin_cache gemspec
163
+ @pins.concat pins if pins
67
164
  end
68
165
  end
69
- dependencies.each { |dep| try_cache dep }
70
- @uncached_gemspecs.uniq!
166
+ logger.info { "DocMap#load_serialized_gem_pins: Loaded and processed serialized pins together in #{time.real} seconds" }
167
+ @uncached_yard_gemspecs.uniq!
168
+ @uncached_rbs_collection_gemspecs.uniq!
169
+ nil
71
170
  end
72
171
 
73
172
  # @return [Hash{String => Array<Gem::Specification>}]
@@ -80,32 +179,97 @@ module Solargraph
80
179
  @preference_map ||= preferences.to_h { |gemspec| [gemspec.name, gemspec] }
81
180
  end
82
181
 
182
+ # @param gemspec [Gem::Specification]
183
+ # @return [Array<Pin::Base>]
184
+ def deserialize_yard_pin_cache gemspec
185
+ if yard_pins_in_memory.key?([gemspec.name, gemspec.version])
186
+ return yard_pins_in_memory[[gemspec.name, gemspec.version]]
187
+ end
188
+
189
+ cached = PinCache.deserialize_yard_gem(gemspec)
190
+ if cached
191
+ logger.info { "Loaded #{cached.length} cached YARD pins from #{gemspec.name}:#{gemspec.version}" }
192
+ yard_pins_in_memory[[gemspec.name, gemspec.version]] = cached
193
+ cached
194
+ else
195
+ logger.debug "No YARD pin cache for #{gemspec.name}:#{gemspec.version}"
196
+ @uncached_yard_gemspecs.push gemspec
197
+ nil
198
+ end
199
+ end
200
+
83
201
  # @param gemspec [Gem::Specification]
84
202
  # @return [void]
85
- def try_cache gemspec
86
- return if try_gem_in_memory(gemspec)
87
- cache_file = File.join('gems', "#{gemspec.name}-#{gemspec.version}.ser")
88
- if Cache.exist?(cache_file)
89
- cached = Cache.load(cache_file)
90
- gempins = update_from_collection(gemspec, cached)
91
- self.class.gems_in_memory[gemspec] = gempins
92
- @pins.concat gempins
203
+ def deserialize_combined_pin_cache(gemspec)
204
+ unless combined_pins_in_memory[[gemspec.name, gemspec.version]].nil?
205
+ return combined_pins_in_memory[[gemspec.name, gemspec.version]]
206
+ end
207
+
208
+ rbs_map = RbsMap.from_gemspec(gemspec, rbs_collection_path, rbs_collection_config_path)
209
+ rbs_version_cache_key = rbs_map.cache_key
210
+
211
+ cached = PinCache.deserialize_combined_gem(gemspec, rbs_version_cache_key)
212
+ if cached
213
+ logger.info { "Loaded #{cached.length} cached YARD pins from #{gemspec.name}:#{gemspec.version}" }
214
+ combined_pins_in_memory[[gemspec.name, gemspec.version]] = cached
215
+ return combined_pins_in_memory[[gemspec.name, gemspec.version]]
216
+ end
217
+
218
+ rbs_collection_pins = deserialize_rbs_collection_cache gemspec, rbs_version_cache_key
219
+
220
+ yard_pins = deserialize_yard_pin_cache gemspec
221
+
222
+ if !rbs_collection_pins.nil? && !yard_pins.nil?
223
+ logger.debug { "Combining pins for #{gemspec.name}:#{gemspec.version}" }
224
+ combined_pins = GemPins.combine(yard_pins, rbs_collection_pins)
225
+ PinCache.serialize_combined_gem(gemspec, rbs_version_cache_key, combined_pins)
226
+ combined_pins_in_memory[[gemspec.name, gemspec.version]] = combined_pins
227
+ logger.info { "Generated #{combined_pins_in_memory[[gemspec.name, gemspec.version]].length} combined pins for #{gemspec.name} #{gemspec.version}" }
228
+ return combined_pins
229
+ end
230
+
231
+ if !yard_pins.nil?
232
+ logger.debug { "Using only YARD pins for #{gemspec.name}:#{gemspec.version}" }
233
+ combined_pins_in_memory[[gemspec.name, gemspec.version]] = yard_pins
234
+ return combined_pins_in_memory[[gemspec.name, gemspec.version]]
235
+ elsif !rbs_collection_pins.nil?
236
+ logger.debug { "Using only RBS collection pins for #{gemspec.name}:#{gemspec.version}" }
237
+ combined_pins_in_memory[[gemspec.name, gemspec.version]] = rbs_collection_pins
238
+ return combined_pins_in_memory[[gemspec.name, gemspec.version]]
93
239
  else
94
- Solargraph.logger.debug "No pin cache for #{gemspec.name} #{gemspec.version}"
95
- @uncached_gemspecs.push gemspec
240
+ logger.debug { "Pins not yet cached for #{gemspec.name}:#{gemspec.version}" }
241
+ return nil
96
242
  end
97
243
  end
98
244
 
99
245
  # @param path [String] require path that might be in the RBS stdlib collection
100
246
  # @return [void]
101
- def try_stdlib_map path
247
+ def deserialize_stdlib_rbs_map path
102
248
  map = RbsMap::StdlibMap.load(path)
103
249
  if map.resolved?
104
- Solargraph.logger.debug "Loading stdlib pins for #{path}"
250
+ logger.debug { "Loading stdlib pins for #{path}" }
105
251
  @pins.concat map.pins
252
+ logger.debug { "Loaded #{map.pins.length} stdlib pins for #{path}" }
253
+ map.pins
106
254
  else
107
255
  # @todo Temporarily ignoring unresolved `require 'set'`
108
- Solargraph.logger.debug "Require path #{path} could not be resolved" unless path == 'set'
256
+ logger.debug { "Require path #{path} could not be resolved in RBS" } unless path == 'set'
257
+ nil
258
+ end
259
+ end
260
+
261
+ # @return [Array<Pin::Base>, nil]
262
+ def deserialize_rbs_collection_cache gemspec, rbs_version_cache_key
263
+ return if rbs_collection_pins_in_memory.key?([gemspec, rbs_version_cache_key])
264
+ cached = PinCache.deserialize_rbs_collection_gem(gemspec, rbs_version_cache_key)
265
+ if cached
266
+ logger.info { "Loaded #{cached.length} pins from RBS collection cache for #{gemspec.name}:#{gemspec.version}" } unless cached.empty?
267
+ rbs_collection_pins_in_memory[[gemspec, rbs_version_cache_key]] = cached
268
+ cached
269
+ else
270
+ logger.debug "No RBS collection pin cache for #{gemspec.name} #{gemspec.version}"
271
+ @uncached_rbs_collection_gemspecs.push gemspec
272
+ nil
109
273
  end
110
274
  end
111
275
 
@@ -119,18 +283,6 @@ module Solargraph
119
283
  true
120
284
  end
121
285
 
122
- # @param gemspec [Gem::Specification]
123
- def update_from_collection gemspec, gempins
124
- return gempins unless workspace&.rbs_collection_path && File.directory?(workspace&.rbs_collection_path)
125
- return gempins if RbsMap.new(gemspec.name, gemspec.version).resolved?
126
-
127
- rbs_map = RbsMap.new(gemspec.name, gemspec.version, directories: [workspace&.rbs_collection_path])
128
- return gempins unless rbs_map.resolved?
129
-
130
- Solargraph.logger.info "Updating #{gemspec.name} #{gemspec.version} from collection"
131
- GemPins.combine(gempins, rbs_map)
132
- end
133
-
134
286
  # @param path [String]
135
287
  # @return [::Array<Gem::Specification>, nil]
136
288
  def resolve_path_to_gemspecs path
@@ -149,7 +301,7 @@ module Solargraph
149
301
  file = "lib/#{path}.rb"
150
302
  gemspec = potential_gemspec if potential_gemspec.files.any? { |gemspec_file| file == gemspec_file }
151
303
  rescue Gem::MissingSpecError
152
- Solargraph.logger.debug "Require path #{path} could not be resolved to a gem via find_by_path or guess of #{gem_name_guess}"
304
+ logger.debug { "Require path #{path} could not be resolved to a gem via find_by_path or guess of #{gem_name_guess}" }
153
305
  []
154
306
  end
155
307
  end
@@ -187,7 +339,7 @@ module Solargraph
187
339
  dep ||= Gem::Specification.find_by_name(spec.name, spec.requirement)
188
340
  deps.merge fetch_dependencies(dep) if deps.add?(dep)
189
341
  rescue Gem::MissingSpecError
190
- Solargraph.logger.warn "Gem dependency #{spec.name} #{spec.requirement} for #{gemspec.name} not found."
342
+ Solargraph.logger.warn "Gem dependency #{spec.name} #{spec.requirement} for #{gemspec.name} not found in RubyGems."
191
343
  end.to_a
192
344
  end
193
345
 
@@ -197,6 +349,11 @@ module Solargraph
197
349
  gemspec.dependencies - gemspec.development_dependencies
198
350
  end
199
351
 
352
+
353
+ def inspect
354
+ self.class.inspect
355
+ end
356
+
200
357
  def gemspecs_required_from_bundler
201
358
  if workspace&.directory && Bundler.definition&.lockfile&.to_s&.start_with?(workspace.directory)
202
359
  # Find only the gems bundler is now using
@@ -7,60 +7,75 @@ module Solargraph
7
7
  # documentation.
8
8
  #
9
9
  module GemPins
10
- # Build an array of pins from a gem specification. The process starts with
11
- # YARD, enhances the resulting pins with RBS definitions, and appends RBS
12
- # pins that don't exist in the YARD mapping.
13
- #
10
+ class << self
11
+ include Logging
12
+ end
13
+
14
14
  # @param gemspec [Gem::Specification]
15
15
  # @return [Array<Pin::Base>]
16
- def self.build(gemspec)
17
- yard_pins = build_yard_pins(gemspec)
18
- rbs_map = RbsMap.from_gemspec(gemspec)
19
- combine yard_pins, rbs_map
16
+ def self.build_yard_pins(gemspec)
17
+ Yardoc.cache(gemspec) unless Yardoc.cached?(gemspec)
18
+ yardoc = Yardoc.load!(gemspec)
19
+ YardMap::Mapper.new(yardoc, gemspec).map
20
+ end
21
+
22
+ # @param pins [Array<Pin::Base>]
23
+ def self.combine_method_pins_by_path(pins)
24
+ # bad_pins = pins.select { |pin| pin.is_a?(Pin::Method) && pin.path == 'StringIO.open' && pin.source == :rbs }; raise "wtf: #{bad_pins}" if bad_pins.length > 1
25
+ method_pins, alias_pins = pins.partition { |pin| pin.class == Pin::Method }
26
+ by_path = method_pins.group_by(&:path)
27
+ by_path.transform_values! do |pins|
28
+ GemPins.combine_method_pins(*pins)
29
+ end
30
+ by_path.values + alias_pins
31
+ end
32
+
33
+ def self.combine_method_pins(*pins)
34
+ out = pins.reduce(nil) do |memo, pin|
35
+ next pin if memo.nil?
36
+ if memo == pin && memo.source != :combined
37
+ # @todo we should track down situations where we are handled
38
+ # the same pin from the same source here and eliminate them -
39
+ # this is an efficiency workaround for now
40
+ next memo
41
+ end
42
+ memo.combine_with(pin)
43
+ end
44
+ logger.debug { "GemPins.combine_method_pins(pins.length=#{pins.length}, pins=#{pins}) => #{out.inspect}" }
45
+ out
20
46
  end
21
47
 
22
48
  # @param yard_pins [Array<Pin::Base>]
23
49
  # @param rbs_map [RbsMap]
24
50
  # @return [Array<Pin::Base>]
25
- def self.combine(yard_pins, rbs_map)
51
+ def self.combine(yard_pins, rbs_pins)
26
52
  in_yard = Set.new
27
- combined = yard_pins.map do |yard|
28
- in_yard.add yard.path
29
- next yard unless yard.is_a?(Pin::Method)
53
+ rbs_api_map = Solargraph::ApiMap.new(pins: rbs_pins)
54
+ combined = yard_pins.map do |yard_pin|
55
+ in_yard.add yard_pin.path
56
+ rbs_pin = rbs_api_map.get_path_pins(yard_pin.path).filter { |pin| pin.is_a? Pin::Method }.first
57
+ next yard_pin unless rbs_pin && yard_pin.class == Pin::Method
30
58
 
31
- rbs = rbs_map.path_pin(yard.path, Pin::Method)
32
- next yard unless rbs
59
+ unless rbs_pin
60
+ logger.debug { "GemPins.combine: No rbs pin for #{yard_pin.path} - using YARD's '#{yard_pin.inspect} (return_type=#{yard_pin.return_type}; signatures=#{yard_pin.signatures})" }
61
+ next yard_pin
62
+ end
33
63
 
34
- # @sg-ignore
35
- yard.class.new(
36
- location: yard.location,
37
- closure: yard.closure,
38
- name: yard.name,
39
- comments: yard.comments,
40
- scope: yard.scope,
41
- parameters: rbs.parameters,
42
- generics: rbs.generics,
43
- node: yard.node,
44
- signatures: yard.signatures,
45
- return_type: best_return_type(rbs.return_type, yard.return_type),
46
- source: :gem_pins
47
- )
64
+ out = combine_method_pins(rbs_pin, yard_pin)
65
+ logger.debug { "GemPins.combine: Combining yard.path=#{yard_pin.path} - rbs=#{rbs_pin.inspect} with yard=#{yard_pin.inspect} into #{out}" }
66
+ out
48
67
  end
49
- in_rbs = rbs_map.pins.reject { |pin| in_yard.include?(pin.path) }
50
- combined + in_rbs
68
+ in_rbs_only = rbs_pins.select do |pin|
69
+ pin.path.nil? || !in_yard.include?(pin.path)
70
+ end
71
+ out = combined + in_rbs_only
72
+ logger.debug { "GemPins#combine: Returning #{out.length} combined pins" }
73
+ out
51
74
  end
52
75
 
53
76
  class << self
54
77
  private
55
78
 
56
- # @param gemspec [Gem::Specification]
57
- # @return [Array<Pin::Base>]
58
- def build_yard_pins(gemspec)
59
- Yardoc.cache(gemspec) unless Yardoc.cached?(gemspec)
60
- yardoc = Yardoc.load!(gemspec)
61
- YardMap::Mapper.new(yardoc, gemspec).map
62
- end
63
-
64
79
  # Select the first defined type.
65
80
  #
66
81
  # @param choices [Array<ComplexType>]
@@ -299,6 +299,10 @@ module Solargraph
299
299
  end
300
300
  end
301
301
 
302
+ def command_path
303
+ options['commandPath'] || 'solargraph'
304
+ end
305
+
302
306
  # Prepare multiple folders.
303
307
  #
304
308
  # @param array [Array<Hash{String => String}>]
@@ -598,7 +602,11 @@ module Solargraph
598
602
  # @return [Array]
599
603
  def document query
600
604
  result = []
601
- libraries.each { |lib| result.concat lib.document(query) }
605
+ if libraries.empty?
606
+ result.concat generic_library.document(query)
607
+ else
608
+ libraries.each { |lib| result.concat lib.document(query) }
609
+ end
602
610
  result
603
611
  end
604
612
 
@@ -83,6 +83,7 @@ module Solargraph
83
83
  @fetched = true
84
84
  begin
85
85
  @available ||= begin
86
+ # @sg-ignore
86
87
  # @type [Gem::Dependency, nil]
87
88
  tuple = CheckGemVersion.fetcher.search_for_dependency(Gem::Dependency.new('solargraph')).flatten.first
88
89
  if tuple.nil?
@@ -6,12 +6,15 @@ module Solargraph
6
6
  module Extended
7
7
  class Document < Base
8
8
  def process
9
- objects = host.document(params['query'])
9
+ api_map, pins = host.document(params['query'])
10
10
  page = Solargraph::Page.new(host.options['viewsPath'])
11
- content = page.render('document', layout: true, locals: {objects: objects})
11
+ content = page.render('document', layout: true, locals: { api_map: api_map, pins: pins })
12
12
  set_result(
13
13
  content: content
14
14
  )
15
+ rescue StandardError => e
16
+ Solargraph.logger.warn "Error processing document: [#{e.class}] #{e.message}"
17
+ Solargraph.logger.debug e.backtrace.join("\n")
15
18
  end
16
19
  end
17
20
  end
@@ -11,9 +11,9 @@ module Solargraph
11
11
  #
12
12
  class DocumentGems < Base
13
13
  def process
14
- cmd = "yard gems"
15
- cmd += " --rebuild" if params['rebuild']
16
- o, s = Open3.capture2(cmd)
14
+ cmd = [host.command_path, 'gems']
15
+ cmd.push '--rebuild' if params['rebuild']
16
+ o, s = Open3.capture2(*cmd)
17
17
  if s != 0
18
18
  host.show_message "An error occurred while building gem documentation.", LanguageServer::MessageTypes::ERROR
19
19
  set_result({
@@ -327,9 +327,10 @@ module Solargraph
327
327
 
328
328
  # @param query [String]
329
329
  # @return [Enumerable<YARD::CodeObjects::Base>]
330
+ # @return [Array(ApiMap, Enumerable<Pin::Base>)]
330
331
  def document query
331
332
  sync_catalog
332
- mutex.synchronize { api_map.document query }
333
+ mutex.synchronize { [api_map, api_map.get_path_pins(query)] }
333
334
  end
334
335
 
335
336
  # @param query [String]
@@ -587,7 +588,8 @@ module Solargraph
587
588
  # @return [void]
588
589
  def cache_next_gemspec
589
590
  return if @cache_progress
590
- spec = api_map.uncached_gemspecs.find { |spec| !cache_errors.include?(spec) }
591
+ spec = (api_map.uncached_yard_gemspecs + api_map.uncached_rbs_collection_gemspecs).
592
+ find { |spec| !cache_errors.include?(spec) }
591
593
  return end_cache_progress unless spec
592
594
 
593
595
  pending = api_map.uncached_gemspecs.length - cache_errors.length - 1
@@ -654,7 +656,8 @@ module Solargraph
654
656
  api_map.catalog bench
655
657
  source_map_hash.values.each { |map| find_external_requires(map) }
656
658
  logger.info "Catalog complete (#{api_map.source_maps.length} files, #{api_map.pins.length} pins)"
657
- logger.info "#{api_map.uncached_gemspecs.length} uncached gemspecs"
659
+ logger.info "#{api_map.uncached_yard_gemspecs.length} uncached YARD gemspecs"
660
+ logger.info "#{api_map.uncached_rbs_collection_gemspecs.length} uncached RBS collection gemspecs"
658
661
  cache_next_gemspec
659
662
  @sync_count = 0
660
663
  end
@@ -25,6 +25,19 @@ module Solargraph
25
25
  [filename, range]
26
26
  end
27
27
 
28
+ def <=>(other)
29
+ return nil unless other.is_a?(Location)
30
+ if filename == other.filename
31
+ range <=> other.range
32
+ else
33
+ filename <=> other.filename
34
+ end
35
+ end
36
+
37
+ def rbs?
38
+ filename.end_with?('.rbs')
39
+ end
40
+
28
41
  # @param location [self]
29
42
  def contain? location
30
43
  range.contain?(location.range.start) && range.contain?(location.range.ending) && filename == location.filename
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'parser/current'
4
- require 'parser/source/buffer'
3
+ require 'prism'
5
4
 
6
5
  # Awaiting ability to use a version containing https://github.com/whitequark/parser/pull/1076
7
6
  #
@@ -39,12 +38,10 @@ module Solargraph
39
38
 
40
39
  # @return [::Parser::Base]
41
40
  def parser
42
- # @todo Consider setting an instance variable. We might not need to
43
- # recreate the parser every time we use it.
44
- parser = ::Parser::CurrentRuby.new(FlawedBuilder.new)
45
- parser.diagnostics.all_errors_are_fatal = true
46
- parser.diagnostics.ignore_warnings = true
47
- parser
41
+ @parser ||= Prism::Translation::Parser.new(FlawedBuilder.new).tap do |parser|
42
+ parser.diagnostics.all_errors_are_fatal = true
43
+ parser.diagnostics.ignore_warnings = true
44
+ end
48
45
  end
49
46
 
50
47
  # @param source [Source]
@@ -30,8 +30,8 @@ module Solargraph
30
30
  process_children
31
31
  end
32
32
 
33
- # TODO: Move this out of [CasgnNode] once [Solargraph::Parser::NodeProcessor] supports
34
- # multiple processors.
33
+ # @todo Move this out of [CasgnNode] once [Solargraph::Parser::NodeProcessor] supports
34
+ # multiple processors.
35
35
  def process_struct_assignment
36
36
  processor_klass = Convention::StructDefinition::NodeProcessors::StructNode
37
37
  processor = processor_klass.new(node, region, pins, locals)
@@ -45,8 +45,8 @@ module Solargraph
45
45
  process_children region.update(closure: nspin, visibility: :public)
46
46
  end
47
47
 
48
- # TODO: Move this out of [NamespaceNode] once [Solargraph::Parser::NodeProcessor] supports
49
- # multiple processors.
48
+ # @todo Move this out of [NamespaceNode] once [Solargraph::Parser::NodeProcessor] supports
49
+ # multiple processors.
50
50
  def process_struct_definition
51
51
  processor_klass = Convention::StructDefinition::NodeProcessors::StructNode
52
52
  processor = processor_klass.new(node, region, pins, locals)