solargraph 0.60.3 → 0.61.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.
- checksums.yaml +4 -4
- data/.github/workflows/plugins.yml +1 -5
- data/.github/workflows/rspec.yml +3 -1
- data/CHANGELOG.md +26 -0
- data/Gemfile +0 -3
- data/lib/solargraph/api_map/index.rb +15 -2
- data/lib/solargraph/api_map/store.rb +13 -0
- data/lib/solargraph/api_map.rb +60 -81
- data/lib/solargraph/cache_dir.rb +58 -0
- data/lib/solargraph/collection/base.rb +50 -0
- data/lib/solargraph/collection/core.rb +17 -0
- data/lib/solargraph/collection/gem.rb +57 -0
- data/lib/solargraph/collection/rbs.rb +49 -0
- data/lib/solargraph/collection/stdlib.rb +25 -0
- data/lib/solargraph/collection.rb +31 -0
- data/lib/solargraph/complex_type.rb +18 -5
- data/lib/solargraph/convention.rb +5 -3
- data/lib/solargraph/diagnostics/require_not_found.rb +0 -4
- data/lib/solargraph/external.rb +159 -0
- data/lib/solargraph/language_server/message/text_document/definition.rb +4 -1
- data/lib/solargraph/language_server/message/text_document/hover.rb +1 -2
- data/lib/solargraph/library.rb +11 -24
- data/lib/solargraph/metagem.rb +80 -0
- data/lib/solargraph/parser/parser_gem/node_chainer.rb +22 -3
- data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +14 -5
- data/lib/solargraph/pin/closure.rb +17 -0
- data/lib/solargraph/pin/factory_parameter.rb +84 -0
- data/lib/solargraph/pin/method.rb +7 -4
- data/lib/solargraph/pin.rb +1 -0
- data/lib/solargraph/rbs_map/base.rb +31 -0
- data/lib/solargraph/rbs_map/conversions.rb +3 -1
- data/lib/solargraph/rbs_map/{core_map.rb → core.rb} +18 -39
- data/lib/solargraph/rbs_map/core_fills.rb +1 -1
- data/lib/solargraph/rbs_map/gem.rb +13 -0
- data/lib/solargraph/rbs_map/helpers.rb +60 -0
- data/lib/solargraph/rbs_map/path.rb +19 -0
- data/lib/solargraph/rbs_map/stdlib.rb +24 -0
- data/lib/solargraph/rbs_map.rb +7 -198
- data/lib/solargraph/repo.rb +177 -0
- data/lib/solargraph/shell.rb +49 -99
- data/lib/solargraph/source/chain/call.rb +190 -67
- data/lib/solargraph/source/chain/literal.rb +17 -17
- data/lib/solargraph/source/chain/parameter.rb +79 -0
- data/lib/solargraph/source/chain.rb +44 -5
- data/lib/solargraph/source/source_chainer.rb +40 -44
- data/lib/solargraph/source_map/clip.rb +5 -3
- data/lib/solargraph/source_map/mapper.rb +6 -2
- data/lib/solargraph/source_map.rb +10 -5
- data/lib/solargraph/type_checker/rules.rb +2 -1
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/workspace.rb +0 -1
- data/lib/solargraph/yard_map/cache.rb +1 -1
- data/lib/solargraph/yard_map/directives/attribute_directive.rb +1 -1
- data/lib/solargraph/yard_map/directives/domain_directive.rb +1 -1
- data/lib/solargraph/yard_map/directives/method_directive.rb +1 -1
- data/lib/solargraph/yard_map/directives/override_directive.rb +1 -1
- data/lib/solargraph/yard_map/directives/parse_directive.rb +1 -1
- data/lib/solargraph/yard_map/directives/scope_directive.rb +178 -0
- data/lib/solargraph/yard_map/directives/visibility_directive.rb +1 -1
- data/lib/solargraph/yard_map/directives.rb +5 -2
- data/lib/solargraph/yard_map/helpers.rb +1 -1
- data/lib/solargraph/yard_map/macro.rb +34 -5
- data/lib/solargraph/yard_map/mapper/to_constant.rb +1 -1
- data/lib/solargraph/yard_map/mapper/to_method.rb +1 -1
- data/lib/solargraph/yard_map/mapper/to_namespace.rb +1 -1
- data/lib/solargraph/yard_map/mapper.rb +37 -4
- data/lib/solargraph/yard_map.rb +1 -1
- data/lib/solargraph/yardoc.rb +43 -60
- data/lib/solargraph.rb +6 -3
- data/solargraph.gemspec +1 -1
- metadata +22 -9
- data/lib/solargraph/doc_map.rb +0 -439
- data/lib/solargraph/gem_pins.rb +0 -87
- data/lib/solargraph/pin_cache.rb +0 -248
- data/lib/solargraph/rbs_map/stdlib_map.rb +0 -71
- data/lib/solargraph/workspace/gemspecs.rb +0 -373
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
require 'rbs'
|
|
4
4
|
|
|
5
5
|
module Solargraph
|
|
6
|
-
|
|
6
|
+
module RbsMap
|
|
7
7
|
# Functions for converting RBS declarations to Solargraph pins
|
|
8
8
|
#
|
|
9
9
|
class Conversions
|
|
@@ -49,6 +49,8 @@ module Solargraph
|
|
|
49
49
|
return
|
|
50
50
|
end
|
|
51
51
|
environment.declarations.each { |decl| convert_decl_to_pin(decl, Solargraph::Pin::ROOT_PIN) }
|
|
52
|
+
rescue RBS::DuplicatedDeclarationError => e
|
|
53
|
+
Solargraph.logger.warn "RBS conversion failed due to duplicate declarations: [#{e.class}] #{e.message}"
|
|
52
54
|
end
|
|
53
55
|
|
|
54
56
|
# @param decl [RBS::AST::Declarations::Base]
|
|
@@ -1,39 +1,32 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Solargraph
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class CoreMap
|
|
8
|
-
include Logging
|
|
4
|
+
module RbsMap
|
|
5
|
+
class Core < Base
|
|
6
|
+
FILLS_DIRECTORY = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', 'rbs', 'fills'))
|
|
9
7
|
|
|
10
|
-
def
|
|
11
|
-
|
|
8
|
+
def pins
|
|
9
|
+
@pins ||= generate_pins
|
|
12
10
|
end
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
# @return [RBS::EnvironmentLoader]
|
|
13
|
+
def loader
|
|
14
|
+
@loader ||= RBS::EnvironmentLoader.new(repository: repository)
|
|
15
|
+
end
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def pins out: $stderr
|
|
21
|
-
return @pins if @pins
|
|
22
|
-
@pins = cache_core(out: out)
|
|
17
|
+
def repository
|
|
18
|
+
@repository ||= RBS::Repository.new(no_stdlib: false)
|
|
23
19
|
end
|
|
24
20
|
|
|
25
|
-
|
|
21
|
+
private
|
|
22
|
+
|
|
26
23
|
# @return [Array<Pin::Base>]
|
|
27
|
-
def
|
|
28
|
-
new_pins =
|
|
29
|
-
cache = PinCache.deserialize_core
|
|
30
|
-
return cache if cache
|
|
31
|
-
new_pins.concat conversions.pins
|
|
24
|
+
def generate_pins
|
|
25
|
+
new_pins = RbsMap::Conversions.new(loader: loader).pins
|
|
32
26
|
|
|
33
27
|
# Avoid RBS::DuplicatedDeclarationError by loading in a different EnvironmentLoader
|
|
34
28
|
fill_loader = RBS::EnvironmentLoader.new(core_root: nil, repository: RBS::Repository.new(no_stdlib: false))
|
|
35
29
|
fill_loader.add(path: Pathname(FILLS_DIRECTORY))
|
|
36
|
-
out&.puts 'Caching RBS pins for Ruby core'
|
|
37
30
|
fill_conversions = Conversions.new(loader: fill_loader)
|
|
38
31
|
new_pins.concat fill_conversions.pins
|
|
39
32
|
|
|
@@ -42,23 +35,9 @@ module Solargraph
|
|
|
42
35
|
|
|
43
36
|
# process overrides, then remove any which couldn't be resolved
|
|
44
37
|
processed = ApiMap::Store.new(new_pins).pins.reject { |p| p.is_a?(Solargraph::Pin::Reference::Override) }
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
new_pins
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
private
|
|
53
|
-
|
|
54
|
-
# @return [RBS::EnvironmentLoader]
|
|
55
|
-
def loader
|
|
56
|
-
@loader ||= RBS::EnvironmentLoader.new(repository: RBS::Repository.new(no_stdlib: false))
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
# @return [Conversions]
|
|
60
|
-
def conversions
|
|
61
|
-
@conversions ||= Conversions.new(loader: loader)
|
|
38
|
+
# serial = Marshal.dump(processed)
|
|
39
|
+
# File.write cache_file, serial, mode: 'wb'
|
|
40
|
+
processed
|
|
62
41
|
end
|
|
63
42
|
end
|
|
64
43
|
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solargraph
|
|
4
|
+
module RbsMap
|
|
5
|
+
module Helpers
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
# @param orig_pins [Array<Pin::Base>]
|
|
9
|
+
# @param rbs_pins [Array<Pin::Base>]
|
|
10
|
+
# @return [Array<Pin::Base>]
|
|
11
|
+
def combine orig_pins, rbs_pins
|
|
12
|
+
in_orig = Set.new
|
|
13
|
+
# @todo There's gotta be a better way!
|
|
14
|
+
rbs_api_map = Solargraph::ApiMap.new(pins: rbs_pins)
|
|
15
|
+
combined = orig_pins.map do |orig_pin|
|
|
16
|
+
in_orig.add orig_pin.path
|
|
17
|
+
rbs_pin = rbs_api_map.get_path_pins(orig_pin.path).filter { |pin| pin.is_a? Pin::Method }.first
|
|
18
|
+
next orig_pin unless rbs_pin && orig_pin.instance_of?(Pin::Method)
|
|
19
|
+
|
|
20
|
+
unless rbs_pin
|
|
21
|
+
# @sg-ignore https://github.com/castwide/solargraph/pull/1114
|
|
22
|
+
Solargraph.logger.debug { "GemPins.combine: No rbs pin for #{orig_pin.path} - using YARD's '#{orig_pin.inspect} (return_type=#{orig_pin.return_type}; signatures=#{orig_pin.signatures})" }
|
|
23
|
+
next orig_pin
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
out = combine_method_pins(rbs_pin, orig_pin)
|
|
27
|
+
Solargraph.logger.debug { "GemPins.combine: Combining yard.path=#{orig_pin.path} - rbs=#{rbs_pin.inspect} with yard=#{orig_pin.inspect} into #{out}" }
|
|
28
|
+
out
|
|
29
|
+
end
|
|
30
|
+
in_rbs_only = rbs_pins.select do |pin|
|
|
31
|
+
pin.path.nil? || !in_orig.include?(pin.path)
|
|
32
|
+
end
|
|
33
|
+
out = combined + in_rbs_only
|
|
34
|
+
Solargraph.logger.debug { "GemPins#combine: Returning #{out.length} combined pins" }
|
|
35
|
+
out
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @param pins [Array<Pin::Method>]
|
|
39
|
+
# @return [Pin::Method, nil]
|
|
40
|
+
def combine_method_pins(*pins)
|
|
41
|
+
# @type [Pin::Method, nil]
|
|
42
|
+
combined_pin = nil
|
|
43
|
+
# @param memo [Pin::Method, nil]
|
|
44
|
+
# @param pin [Pin::Method]
|
|
45
|
+
out = pins.reduce(combined_pin) do |memo, pin|
|
|
46
|
+
next pin if memo.nil?
|
|
47
|
+
if memo == pin && memo.source != :combined
|
|
48
|
+
# @todo we should track down situations where we are handled
|
|
49
|
+
# the same pin from the same source here and eliminate them -
|
|
50
|
+
# this is an efficiency workaround for now
|
|
51
|
+
next memo
|
|
52
|
+
end
|
|
53
|
+
memo.combine_with(pin)
|
|
54
|
+
end
|
|
55
|
+
Solargraph.logger.debug { "GemPins.combine_method_pins(pins.length=#{pins.length}, pins=#{pins}) => #{out.inspect}" }
|
|
56
|
+
out
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solargraph
|
|
4
|
+
module RbsMap
|
|
5
|
+
class Path < Base
|
|
6
|
+
# @param path [String]
|
|
7
|
+
# @return [Array<Pin::Base>]
|
|
8
|
+
def self.pins path
|
|
9
|
+
new(path).pins
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# @param path [String]
|
|
13
|
+
def initialize path
|
|
14
|
+
super()
|
|
15
|
+
loader.add path: Pathname.new(path)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rbs'
|
|
4
|
+
|
|
5
|
+
module Solargraph
|
|
6
|
+
module RbsMap
|
|
7
|
+
# Ruby stdlib pins
|
|
8
|
+
#
|
|
9
|
+
class Stdlib < Base
|
|
10
|
+
def initialize library
|
|
11
|
+
super()
|
|
12
|
+
loader.add(library: library.gsub('/', '-')) if self.class.has?(library)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def repository
|
|
16
|
+
@repository ||= RBS::Repository.new(no_stdlib: false)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.has? library
|
|
20
|
+
!!RBS::Collection::Sources::Stdlib.instance.has?(library.gsub('/', '-'), nil)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
data/lib/solargraph/rbs_map.rb
CHANGED
|
@@ -1,207 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'digest'
|
|
4
|
-
require 'pathname'
|
|
5
3
|
require 'rbs'
|
|
6
4
|
|
|
7
5
|
module Solargraph
|
|
8
|
-
|
|
6
|
+
module RbsMap
|
|
7
|
+
autoload :Base, 'solargraph/rbs_map/base'
|
|
9
8
|
autoload :Conversions, 'solargraph/rbs_map/conversions'
|
|
10
|
-
autoload :
|
|
9
|
+
autoload :Core, 'solargraph/rbs_map/core'
|
|
11
10
|
autoload :CoreFills, 'solargraph/rbs_map/core_fills'
|
|
12
|
-
autoload :
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
# @type [Hash{String => RbsMap}]
|
|
17
|
-
@@rbs_maps_hash = {}
|
|
18
|
-
|
|
19
|
-
attr_reader :library, :rbs_collection_paths, :rbs_collection_config_path
|
|
20
|
-
|
|
21
|
-
# @param library [String]
|
|
22
|
-
# @param version [String, nil]
|
|
23
|
-
# @param rbs_collection_config_path [String, Pathname, nil]
|
|
24
|
-
# @param rbs_collection_paths [Array<Pathname, String>]
|
|
25
|
-
# @param out [StringIO, IO, nil] where to log messages
|
|
26
|
-
def initialize library, version = nil, rbs_collection_config_path: nil, rbs_collection_paths: [], out: $stderr
|
|
27
|
-
if rbs_collection_config_path.nil? && !rbs_collection_paths.empty?
|
|
28
|
-
raise 'Please provide rbs_collection_config_path if you provide rbs_collection_paths'
|
|
29
|
-
end
|
|
30
|
-
@library = library
|
|
31
|
-
@version = version
|
|
32
|
-
@rbs_collection_config_path = rbs_collection_config_path
|
|
33
|
-
@rbs_collection_paths = rbs_collection_paths
|
|
34
|
-
add_library loader, library, version
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
CACHE_KEY_GEM_EXPORT = 'gem-export'
|
|
38
|
-
CACHE_KEY_UNRESOLVED = 'unresolved'
|
|
39
|
-
CACHE_KEY_STDLIB = 'stdlib'
|
|
40
|
-
CACHE_KEY_LOCAL = 'local'
|
|
41
|
-
|
|
42
|
-
# @param cache_key [String, nil]
|
|
43
|
-
# @return [String, nil] a description of the source of the RBS info
|
|
44
|
-
def self.rbs_source_desc cache_key
|
|
45
|
-
case cache_key
|
|
46
|
-
when CACHE_KEY_GEM_EXPORT
|
|
47
|
-
'RBS gem export'
|
|
48
|
-
when CACHE_KEY_UNRESOLVED
|
|
49
|
-
nil
|
|
50
|
-
when CACHE_KEY_STDLIB
|
|
51
|
-
'RBS standard library'
|
|
52
|
-
when CACHE_KEY_LOCAL
|
|
53
|
-
'local RBS shims'
|
|
54
|
-
else
|
|
55
|
-
'RBS collection'
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
# @return [RBS::EnvironmentLoader]
|
|
60
|
-
def loader
|
|
61
|
-
@loader ||= RBS::EnvironmentLoader.new(core_root: nil, repository: repository)
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
# @return [String] representing the version of the RBS info fetched
|
|
65
|
-
# for the given library. Must change when the RBS info is
|
|
66
|
-
# updated upstream for the same library and version. May change
|
|
67
|
-
# if the config for where information comes form changes.
|
|
68
|
-
def cache_key
|
|
69
|
-
return CACHE_KEY_UNRESOLVED unless resolved?
|
|
70
|
-
|
|
71
|
-
@cache_key ||= begin
|
|
72
|
-
# @type [String, nil]
|
|
73
|
-
data = nil
|
|
74
|
-
# @type gem_config [nil, Hash{String => Hash{String => String}}]
|
|
75
|
-
gem_config = nil
|
|
76
|
-
if rbs_collection_config_path
|
|
77
|
-
# @sg-ignore rbs_collection_config_path is not nil here
|
|
78
|
-
lockfile_path = RBS::Collection::Config.to_lockfile_path(Pathname.new(rbs_collection_config_path))
|
|
79
|
-
if lockfile_path.exist?
|
|
80
|
-
collection_config = RBS::Collection::Config.from_path lockfile_path
|
|
81
|
-
gem_config = collection_config.gem(library)
|
|
82
|
-
data = gem_config&.to_s
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
if gem_config.nil?
|
|
86
|
-
CACHE_KEY_STDLIB
|
|
87
|
-
else
|
|
88
|
-
# @type [String]
|
|
89
|
-
source = gem_config.dig('source', 'type')
|
|
90
|
-
case source
|
|
91
|
-
when 'rubygems'
|
|
92
|
-
CACHE_KEY_GEM_EXPORT
|
|
93
|
-
when 'local'
|
|
94
|
-
CACHE_KEY_LOCAL
|
|
95
|
-
when 'stdlib'
|
|
96
|
-
CACHE_KEY_STDLIB
|
|
97
|
-
else
|
|
98
|
-
Digest::SHA1.hexdigest(data || '')
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
# @param gemspec [Gem::Specification, Bundler::LazySpecification]
|
|
105
|
-
# @param rbs_collection_path [String, Pathname, nil]
|
|
106
|
-
# @param rbs_collection_config_path [String, Pathname, nil]
|
|
107
|
-
# @return [RbsMap]
|
|
108
|
-
def self.from_gemspec gemspec, rbs_collection_path, rbs_collection_config_path
|
|
109
|
-
# prefers stdlib RBS if available
|
|
110
|
-
rbs_map = RbsMap::StdlibMap.new(gemspec.name)
|
|
111
|
-
return rbs_map if rbs_map.resolved?
|
|
112
|
-
|
|
113
|
-
rbs_map = RbsMap.new(gemspec.name, gemspec.version,
|
|
114
|
-
rbs_collection_paths: [rbs_collection_path].compact,
|
|
115
|
-
rbs_collection_config_path: rbs_collection_config_path)
|
|
116
|
-
return rbs_map if rbs_map.resolved?
|
|
117
|
-
|
|
118
|
-
# try any version of the gem in the collection
|
|
119
|
-
RbsMap.new(gemspec.name, nil,
|
|
120
|
-
rbs_collection_paths: [rbs_collection_path].compact,
|
|
121
|
-
rbs_collection_config_path: rbs_collection_config_path)
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
# @param out [IO, nil] where to log messages
|
|
125
|
-
# @return [Array<Pin::Base>]
|
|
126
|
-
def pins out: $stderr
|
|
127
|
-
@pins ||= if resolved?
|
|
128
|
-
conversions.pins
|
|
129
|
-
else
|
|
130
|
-
[]
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
# @generic T
|
|
135
|
-
# @param path [String]
|
|
136
|
-
# @param klass [Class<generic<T>>]
|
|
137
|
-
#
|
|
138
|
-
# @sg-ignore Need to be able to resolve generics based on a
|
|
139
|
-
# Class<generic<T>> param
|
|
140
|
-
# @return [generic<T>, nil]
|
|
141
|
-
def path_pin path, klass = Pin::Base
|
|
142
|
-
pin = pins.find { |p| p.path == path }
|
|
143
|
-
pin if pin.is_a?(klass)
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
# @param path [String]
|
|
147
|
-
# @return [Array<Pin::Base>]
|
|
148
|
-
def path_pins path
|
|
149
|
-
pins.select { |p| p.path == path }
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
def resolved?
|
|
153
|
-
@resolved
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
# @return [RBS::Repository]
|
|
157
|
-
def repository
|
|
158
|
-
@repository ||= RBS::Repository.new(no_stdlib: false).tap do |repo|
|
|
159
|
-
@rbs_collection_paths.each do |dir|
|
|
160
|
-
dir_path = Pathname.new(dir)
|
|
161
|
-
repo.add(dir_path) if dir_path.exist? && dir_path.directory?
|
|
162
|
-
end
|
|
163
|
-
end
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
# @param library [String]
|
|
167
|
-
# @return [RbsMap]
|
|
168
|
-
def self.load library
|
|
169
|
-
@@rbs_maps_hash[library] ||= RbsMap.new(library)
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
private
|
|
173
|
-
|
|
174
|
-
# @return [Conversions]
|
|
175
|
-
def conversions
|
|
176
|
-
@conversions ||= Conversions.new(loader: loader)
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
def resolve_dependencies?
|
|
180
|
-
# we need to resolve dependencies via gemfile.lock manually for
|
|
181
|
-
# YARD regardless, so use same mechanism here so we don't
|
|
182
|
-
# duplicate work generating pins from dependencies
|
|
183
|
-
false
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
# @param loader [RBS::EnvironmentLoader]
|
|
187
|
-
# @param library [String]
|
|
188
|
-
# @param version [String, nil] the version of the library to load, or nil for any
|
|
189
|
-
# @param out [StringIO, IO, nil] where to log messages
|
|
190
|
-
# @return [Boolean] true if adding the library succeeded
|
|
191
|
-
def add_library loader, library, version, out: $stderr
|
|
192
|
-
@resolved = if loader.has_library?(library: library, version: version)
|
|
193
|
-
loader.add library: library, version: version, resolve_dependencies: resolve_dependencies?
|
|
194
|
-
logger.debug { "#{short_name} successfully loaded library #{library}:#{version}" }
|
|
195
|
-
true
|
|
196
|
-
else
|
|
197
|
-
logger.info { "#{short_name} did not find data for library #{library}:#{version}" }
|
|
198
|
-
false
|
|
199
|
-
end
|
|
200
|
-
end
|
|
201
|
-
|
|
202
|
-
# @return [String]
|
|
203
|
-
def short_name
|
|
204
|
-
self.class.name.split('::').last
|
|
205
|
-
end
|
|
11
|
+
autoload :Gem, 'solargraph/rbs_map/gem'
|
|
12
|
+
autoload :Helpers, 'solargraph/rbs_map/helpers'
|
|
13
|
+
autoload :Path, 'solargraph/rbs_map/path'
|
|
14
|
+
autoload :Stdlib, 'solargraph/rbs_map/stdlib'
|
|
206
15
|
end
|
|
207
16
|
end
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'open3'
|
|
4
|
+
require 'json'
|
|
5
|
+
|
|
6
|
+
module Solargraph
|
|
7
|
+
# A repository for the gems available in the specified directory. If the
|
|
8
|
+
# directory has a Gemfile, Repo will use its gem definitions. Otherwise it
|
|
9
|
+
# uses the system gems.
|
|
10
|
+
#
|
|
11
|
+
class Repo
|
|
12
|
+
# @return [String, nil]
|
|
13
|
+
attr_reader :directory
|
|
14
|
+
|
|
15
|
+
# @param directory [String, nil]
|
|
16
|
+
def initialize directory
|
|
17
|
+
@directory = File.expand_path(directory) if directory
|
|
18
|
+
build_from_directory
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Find a metagem by path from the directory's bundle definition or the
|
|
22
|
+
# system gems.
|
|
23
|
+
#
|
|
24
|
+
# @param path [String]
|
|
25
|
+
# @return [Metagem, nil]
|
|
26
|
+
def find_by_path path
|
|
27
|
+
return system_find_by_path(path) unless bundled?
|
|
28
|
+
|
|
29
|
+
bundled_metagem_path_map[path]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Find a metagem by name from the directory's bundle definition or the
|
|
33
|
+
# system gems.
|
|
34
|
+
#
|
|
35
|
+
# @param name [String]
|
|
36
|
+
# @return [Metagem, nil]
|
|
37
|
+
def find_by_name name
|
|
38
|
+
return system_find_by_name(name) unless bundled?
|
|
39
|
+
|
|
40
|
+
bundled_metagem_name_map[name]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Get an array of metagems by its bundle group name. Returns an empty array
|
|
44
|
+
# if the repo isn't bundled or the group doesn't exist.
|
|
45
|
+
#
|
|
46
|
+
# @param group [Symbol]
|
|
47
|
+
# @return [Array<Metagem>]
|
|
48
|
+
def find_by_group group
|
|
49
|
+
return system_find_by_group(group) unless bundled?
|
|
50
|
+
|
|
51
|
+
bundled_group_map[group] || []
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# True if the directory has a bundle definition.
|
|
55
|
+
#
|
|
56
|
+
# @note If Solargraph itself is running in this directory's bundled
|
|
57
|
+
# environment, `bundled?` is false.
|
|
58
|
+
#
|
|
59
|
+
def bundled?
|
|
60
|
+
!!@metagems
|
|
61
|
+
end
|
|
62
|
+
alias bundle? bundled?
|
|
63
|
+
|
|
64
|
+
# Metagems from the directory's bundle definition, or an empty array if a
|
|
65
|
+
# bundle definition does not exist.
|
|
66
|
+
#
|
|
67
|
+
# @return [Array<Solargraph::Metagem>]
|
|
68
|
+
def bundled
|
|
69
|
+
@metagems || []
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
# @return [String, nil]
|
|
75
|
+
def gemfile
|
|
76
|
+
@gemfile ||= File.expand_path('Gemfile', directory) if directory
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# @return [String, nil]
|
|
80
|
+
def lockfile
|
|
81
|
+
@lockfile ||= File.expand_path('Gemfile.lock', directory) if directory
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def bundled_directory?
|
|
85
|
+
directory && File.file?(gemfile) && File.file?(lockfile)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Load metagems from the directory's bundle definition if available.
|
|
89
|
+
#
|
|
90
|
+
# This method performs gem collection in a separate process to suppress
|
|
91
|
+
# output from the `Bundler.definition.specs` call.
|
|
92
|
+
#
|
|
93
|
+
# @return [Array<Metagem>, nil]
|
|
94
|
+
def build_from_directory
|
|
95
|
+
return unless bundled_directory? && ENV['BUNDLE_GEMFILE'] != gemfile
|
|
96
|
+
|
|
97
|
+
Solargraph.with_clean_env do
|
|
98
|
+
cmd = ['ruby', '-e', bundle_script]
|
|
99
|
+
o, e, s = Open3.capture3(*cmd, chdir: directory)
|
|
100
|
+
if s.success?
|
|
101
|
+
json = o && !o.empty? ? JSON.parse(o.strip.split("\n").last, symbolize_names: true) : []
|
|
102
|
+
@metagems = json[:metagems].map { |data| Metagem.new(**data) }
|
|
103
|
+
@bundled_group_map = json[:groups].transform_values { |names| names.map { |name| bundled_metagem_name_map[name] } }
|
|
104
|
+
else
|
|
105
|
+
Solargraph.logger.warn "Failed to load gems from bundle at #{directory}: #{e}"
|
|
106
|
+
nil
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def bundle_script
|
|
112
|
+
"
|
|
113
|
+
require 'bundler/setup'
|
|
114
|
+
require 'json'
|
|
115
|
+
|
|
116
|
+
puts Bundler.definition.specs.length
|
|
117
|
+
metagems = Bundler.definition.specs.map do |spec|
|
|
118
|
+
{
|
|
119
|
+
name: spec.name,
|
|
120
|
+
full_path: spec.full_gem_path,
|
|
121
|
+
spec_file: spec.spec_file,
|
|
122
|
+
source: spec.source.to_s,
|
|
123
|
+
version: spec.version,
|
|
124
|
+
require_paths: spec.require_paths,
|
|
125
|
+
dependencies: spec.dependencies.map(&:name)
|
|
126
|
+
}
|
|
127
|
+
end
|
|
128
|
+
groups = Bundler.definition.groups.to_h do |group|
|
|
129
|
+
[group, Bundler.definition.specs_for([group]).map(&:name)]
|
|
130
|
+
end
|
|
131
|
+
puts({ groups: groups, metagems: metagems }.to_json)
|
|
132
|
+
"
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# @param path [String]
|
|
136
|
+
# @return [Metagem, nil]
|
|
137
|
+
def system_find_by_path path
|
|
138
|
+
gem = Gem::Specification.find_by_path(path)
|
|
139
|
+
gem && Metagem.from_specification(gem)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# @param name [String]
|
|
143
|
+
# @return [Metagem, nil]
|
|
144
|
+
def system_find_by_name name
|
|
145
|
+
gem = Gem::Specification.find_by_name(name)
|
|
146
|
+
gem && Metagem.from_specification(gem)
|
|
147
|
+
rescue Gem::MissingSpecError => _e
|
|
148
|
+
nil
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# @param group [Symbol]
|
|
152
|
+
# @return [Array<Metagem>]
|
|
153
|
+
def system_find_by_group group
|
|
154
|
+
return [] unless ENV['BUNDLE_GEMFILE']
|
|
155
|
+
|
|
156
|
+
Bundler.definition
|
|
157
|
+
.specs_for([group])
|
|
158
|
+
.map { |spec| Metagem.from_specification(spec) }
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def bundled_metagem_name_map
|
|
162
|
+
@bundled_metagem_name_map ||= bundled.to_set
|
|
163
|
+
.classify(&:name)
|
|
164
|
+
.transform_values(&:first)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def bundled_metagem_path_map
|
|
168
|
+
@bundled_metagem_path_map ||= Hash.new do |hash, path|
|
|
169
|
+
hash[path] = bundled.find { |mg| mg.require?(path) }
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def bundled_group_map
|
|
174
|
+
@bundled_group_map ||= {}
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|