solargraph 0.60.4 → 0.62.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/linting.yml +24 -2
- data/.github/workflows/plugins.yml +19 -1
- data/.github/workflows/rspec.yml +17 -2
- data/.github/workflows/typecheck.yml +6 -0
- data/CHANGELOG.md +54 -0
- data/Gemfile +0 -3
- data/lib/solargraph/api_map/index.rb +28 -4
- data/lib/solargraph/api_map/store.rb +19 -0
- data/lib/solargraph/api_map.rb +91 -80
- 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 +59 -0
- data/lib/solargraph/collection/stdlib.rb +25 -0
- data/lib/solargraph/collection.rb +30 -0
- data/lib/solargraph/complex_type/unique_type.rb +13 -0
- data/lib/solargraph/complex_type.rb +23 -3
- data/lib/solargraph/convention.rb +5 -7
- data/lib/solargraph/diagnostics/require_not_found.rb +0 -4
- data/lib/solargraph/diagnostics/type_check.rb +0 -1
- data/lib/solargraph/external.rb +187 -0
- data/lib/solargraph/language_server/host/diagnoser.rb +1 -1
- data/lib/solargraph/language_server/host/dispatch.rb +4 -2
- data/lib/solargraph/language_server/host.rb +10 -2
- data/lib/solargraph/language_server/message/completion_item/resolve.rb +10 -3
- data/lib/solargraph/language_server/message/text_document/definition.rb +5 -2
- data/lib/solargraph/language_server/message/text_document/hover.rb +1 -2
- data/lib/solargraph/library.rb +26 -53
- data/lib/solargraph/metagem.rb +80 -0
- data/lib/solargraph/parser/node_processor/base.rb +6 -0
- data/lib/solargraph/parser/parser_gem/node_chainer.rb +22 -3
- data/lib/solargraph/parser/parser_gem/node_methods.rb +29 -6
- data/lib/solargraph/parser/parser_gem/node_processors/class_definition/class_assignment_node.rb +98 -0
- data/lib/solargraph/parser/parser_gem/node_processors/class_definition.rb +54 -0
- data/lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb +1 -0
- data/lib/solargraph/parser/parser_gem/node_processors/data_definition/data_assignment_node.rb +65 -0
- data/lib/solargraph/parser/parser_gem/node_processors/data_definition/data_definition_node.rb +97 -0
- data/lib/solargraph/parser/parser_gem/node_processors/data_definition.rb +110 -0
- data/lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb +1 -0
- data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +2 -0
- data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +1 -0
- data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +185 -0
- data/lib/solargraph/parser/parser_gem/node_processors/struct_definition/struct_assignment_node.rb +66 -0
- data/lib/solargraph/parser/parser_gem/node_processors/struct_definition/struct_definition_node.rb +108 -0
- data/lib/solargraph/parser/parser_gem/node_processors/struct_definition.rb +170 -0
- data/lib/solargraph/parser/parser_gem/node_processors.rb +33 -29
- data/lib/solargraph/pin/base.rb +25 -1
- data/lib/solargraph/pin/base_variable.rb +31 -1
- data/lib/solargraph/pin/callable.rb +11 -2
- data/lib/solargraph/pin/closure.rb +17 -0
- data/lib/solargraph/pin/delegated_method.rb +5 -0
- data/lib/solargraph/pin/duck_method.rb +36 -16
- data/lib/solargraph/pin/factory_parameter.rb +84 -0
- data/lib/solargraph/pin/instance_variable.rb +45 -0
- data/lib/solargraph/pin/method.rb +10 -0
- data/lib/solargraph/pin/method_alias.rb +4 -0
- data/lib/solargraph/pin/parameter.rb +1 -1
- data/lib/solargraph/pin/reference/included_mixin.rb +22 -0
- data/lib/solargraph/pin/reference.rb +1 -0
- data/lib/solargraph/pin.rb +1 -0
- data/lib/solargraph/plugin/active_support.rb +100 -0
- data/lib/solargraph/plugin/addon.rb +16 -0
- data/lib/solargraph/plugin/base.rb +25 -0
- data/lib/solargraph/plugin/panel.rb +37 -0
- data/lib/solargraph/plugin.rb +26 -0
- data/lib/solargraph/rbs_collection.rb +119 -0
- data/lib/solargraph/rbs_map/base.rb +31 -0
- data/lib/solargraph/rbs_map/conversions.rb +109 -48
- 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 +16 -0
- data/lib/solargraph/rbs_map/helpers.rb +61 -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/rbs_translator.rb +105 -70
- data/lib/solargraph/repo.rb +177 -0
- data/lib/solargraph/shell.rb +49 -99
- data/lib/solargraph/source/chain/call.rb +58 -14
- data/lib/solargraph/source/chain/literal.rb +17 -17
- data/lib/solargraph/source/chain/parameter.rb +79 -0
- data/lib/solargraph/source/chain.rb +8 -0
- data/lib/solargraph/source/node_map.rb +27 -0
- data/lib/solargraph/source/source_chainer.rb +40 -44
- data/lib/solargraph/source.rb +17 -0
- data/lib/solargraph/source_map/call.rb +18 -0
- data/lib/solargraph/source_map/clip.rb +5 -3
- data/lib/solargraph/source_map/mapper.rb +6 -2
- data/lib/solargraph/source_map.rb +28 -9
- data/lib/solargraph/type_checker/rules.rb +3 -2
- data/lib/solargraph/type_checker.rb +4 -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 +85 -6
- 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 +2 -2
- data/lib/solargraph/yard_map.rb +1 -1
- data/lib/solargraph/yardoc.rb +43 -60
- data/lib/solargraph.rb +8 -3
- data/rbs/fills/boolean/0/boolean.rbs +12 -0
- data/solargraph.gemspec +2 -2
- metadata +41 -18
- data/lib/solargraph/convention/active_support_concern.rb +0 -111
- data/lib/solargraph/convention/data_definition/data_assignment_node.rb +0 -61
- data/lib/solargraph/convention/data_definition/data_definition_node.rb +0 -93
- data/lib/solargraph/convention/data_definition.rb +0 -108
- data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +0 -62
- data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +0 -103
- data/lib/solargraph/convention/struct_definition.rb +0 -168
- 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
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require 'rbs'
|
|
5
|
+
|
|
6
|
+
module Solargraph
|
|
7
|
+
module CacheDir
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
# The base directory for cached YARD documentation and serialized pins.
|
|
11
|
+
#
|
|
12
|
+
# @return [String]
|
|
13
|
+
def base_dir
|
|
14
|
+
ENV['SOLARGRAPH_CACHE'] ||
|
|
15
|
+
(ENV['XDG_CACHE_HOME'] ? File.join(ENV['XDG_CACHE_HOME'], 'solargraph') : nil) ||
|
|
16
|
+
File.join(Dir.home, '.cache', 'solargraph')
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# The working directory for the current Ruby, RBS, and Solargraph versions.
|
|
20
|
+
#
|
|
21
|
+
# @return [String]
|
|
22
|
+
def work_dir
|
|
23
|
+
File.join(base_dir, "ruby-#{RUBY_VERSION}", "rbs-#{RBS::VERSION}", "solargraph-#{Solargraph::VERSION}")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# The current gem directory.
|
|
27
|
+
#
|
|
28
|
+
# @return [String]
|
|
29
|
+
def gem_dir
|
|
30
|
+
File.join(work_dir, 'gems')
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# The current stdlib directory.
|
|
34
|
+
#
|
|
35
|
+
# @return [String]
|
|
36
|
+
def stdlib_dir
|
|
37
|
+
File.join(work_dir, 'stdlib')
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# The current RBS collection directory.
|
|
41
|
+
#
|
|
42
|
+
# @return [String]
|
|
43
|
+
def rbs_dir
|
|
44
|
+
File.join(work_dir, 'rbs')
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# The directory for the current YARD version.
|
|
48
|
+
#
|
|
49
|
+
# @return [String]
|
|
50
|
+
def yard_dir
|
|
51
|
+
File.join(base_dir, "yard-#{YARD::VERSION}", "yard-activesupport-concern-#{YARD::ActiveSupport::Concern::VERSION}")
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def clear
|
|
55
|
+
FileUtils.rm_rf base_dir
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
|
|
5
|
+
module Solargraph
|
|
6
|
+
module Collection
|
|
7
|
+
# The base class for cacheable pin collections. Subclasses need to
|
|
8
|
+
# implement #pins and #cache_file.
|
|
9
|
+
#
|
|
10
|
+
# @!method pins
|
|
11
|
+
# The array of pins for this collection.
|
|
12
|
+
# @abstract
|
|
13
|
+
# @return [Array<Pin::Base>]
|
|
14
|
+
# @!method cache_file
|
|
15
|
+
# The file that will store serialized pins.
|
|
16
|
+
# @abstract
|
|
17
|
+
# @return [String]
|
|
18
|
+
class Base
|
|
19
|
+
# @return [Array<Pin::Base>]
|
|
20
|
+
def load
|
|
21
|
+
Collection.cached(cache_file) { file_cache_pins || generate_pin_caches }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @return [Array<Pin::Base>]
|
|
25
|
+
def self.load(...)
|
|
26
|
+
new(...).load
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.uncache(...)
|
|
30
|
+
cache_file = new(...).cache_file
|
|
31
|
+
FileUtils.rm_rf cache_file
|
|
32
|
+
Collection.mem_cache.delete cache_file
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def file_cache_pins
|
|
38
|
+
Marshal.load(File.read(cache_file, mode: 'rb')) if File.exist?(cache_file) # rubocop:disable Security/MarshalLoad
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def generate_pin_caches
|
|
42
|
+
Collection.mem_cache[cache_file] = pins
|
|
43
|
+
serial = Marshal.dump(pins)
|
|
44
|
+
FileUtils.mkdir_p File.dirname(cache_file)
|
|
45
|
+
File.write cache_file, serial, mode: 'wb'
|
|
46
|
+
pins
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solargraph
|
|
4
|
+
module Collection
|
|
5
|
+
# Cacheable core pins.
|
|
6
|
+
#
|
|
7
|
+
class Core < Base
|
|
8
|
+
def cache_file
|
|
9
|
+
File.join CacheDir.work_dir, 'core.ser'
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def pins
|
|
13
|
+
@pins ||= RbsMap::Core.pins
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solargraph
|
|
4
|
+
module Collection
|
|
5
|
+
# Cacheable gem pins.
|
|
6
|
+
#
|
|
7
|
+
class Gem < Base
|
|
8
|
+
include Logging
|
|
9
|
+
|
|
10
|
+
attr_reader :metagem
|
|
11
|
+
|
|
12
|
+
@@path_source_cache = {}
|
|
13
|
+
|
|
14
|
+
# @param metagem [Metagem]
|
|
15
|
+
def initialize metagem
|
|
16
|
+
super()
|
|
17
|
+
@metagem = metagem
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def cache_file
|
|
21
|
+
File.join CacheDir.gem_dir, "#{metagem.cache_name}.ser"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def load
|
|
25
|
+
return super if metagem.cacheable?
|
|
26
|
+
@@path_source_cache[metagem.full_path] ||= pins
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def pins
|
|
30
|
+
@pins ||= without_yard
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.cached? metagem
|
|
34
|
+
metagem.cacheable? && File.exist?(new(metagem).cache_file)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def with_yard
|
|
40
|
+
code_objects = Yardoc.load!(metagem)
|
|
41
|
+
yard_pins = YardMap::Mapper.new(code_objects, metagem).map
|
|
42
|
+
rbs_pins = RbsMap::Gem.pins(metagem)
|
|
43
|
+
RbsMap::Helpers.combine(yard_pins, rbs_pins)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def without_yard
|
|
47
|
+
files = metagem.require_paths.flat_map { |path| Dir.glob(File.join(metagem.full_path, path, '**', '*.rb')) }
|
|
48
|
+
source_maps = files.map { |file| Solargraph::SourceMap.load(file) }
|
|
49
|
+
|
|
50
|
+
# Generating an ApiMap is necessary for processing macros
|
|
51
|
+
bench = Bench.new(source_maps: source_maps)
|
|
52
|
+
source_pins = ApiMap.new.catalog(bench).pins.select { |pin| files.include?(pin.filename) }
|
|
53
|
+
|
|
54
|
+
rbs_pins = RbsMap::Gem.pins(metagem)
|
|
55
|
+
RbsMap::Helpers.combine(source_pins, rbs_pins)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solargraph
|
|
4
|
+
module Collection
|
|
5
|
+
# Cacheable stdlib pins.
|
|
6
|
+
#
|
|
7
|
+
class Stdlib < Base
|
|
8
|
+
attr_reader :library
|
|
9
|
+
|
|
10
|
+
# @param library [String]
|
|
11
|
+
def initialize library
|
|
12
|
+
super()
|
|
13
|
+
@library = library
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def pins
|
|
17
|
+
@pins ||= RbsMap::Stdlib.pins(library)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def cache_file
|
|
21
|
+
File.join CacheDir.stdlib_dir, "#{library}.ser"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solargraph
|
|
4
|
+
module Collection
|
|
5
|
+
autoload :Base, 'solargraph/collection/base'
|
|
6
|
+
autoload :Core, 'solargraph/collection/core'
|
|
7
|
+
autoload :Gem, 'solargraph/collection/gem'
|
|
8
|
+
autoload :Stdlib, 'solargraph/collection/stdlib'
|
|
9
|
+
|
|
10
|
+
# A hash for storing pin collections in memory.
|
|
11
|
+
#
|
|
12
|
+
# @return [Hash{String => Array<Pin::Base>}]
|
|
13
|
+
def self.mem_cache
|
|
14
|
+
@mem_cache ||= {}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# The pins cached under a key, storing the block result on a miss.
|
|
18
|
+
#
|
|
19
|
+
# @param key [String]
|
|
20
|
+
# @return [Array<Pin::Base>]
|
|
21
|
+
def self.cached key
|
|
22
|
+
mem_cache.fetch(key) { mem_cache[key] = yield }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @return [void]
|
|
26
|
+
def self.clear_mem_cache
|
|
27
|
+
mem_cache.clear
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -11,6 +11,13 @@ module Solargraph
|
|
|
11
11
|
|
|
12
12
|
attr_reader :all_params, :subtypes, :key_types
|
|
13
13
|
|
|
14
|
+
# @type [Hash{String => String}]
|
|
15
|
+
ANONYMOUS_NAME_BY_STARTING_TAG = {
|
|
16
|
+
'{' => 'Hash',
|
|
17
|
+
'(' => 'Array',
|
|
18
|
+
'<' => 'Array'
|
|
19
|
+
}.freeze
|
|
20
|
+
|
|
14
21
|
# Create a UniqueType with the specified name and an optional substring.
|
|
15
22
|
# The substring is the parameter section of a parametrized type, e.g.,
|
|
16
23
|
# for the type `Array<String>`, the name is `Array` and the substring is
|
|
@@ -22,6 +29,9 @@ module Solargraph
|
|
|
22
29
|
# @return [UniqueType]
|
|
23
30
|
def self.parse name, substring = '', make_rooted: nil
|
|
24
31
|
raise ComplexTypeError, "Illegal prefix: #{name}" if name.start_with?(':::')
|
|
32
|
+
# Anonymous shorthand (`<A>`, `(A)`, `{A=>B}`) defaults the
|
|
33
|
+
# omitted type name to Array or Hash, before the rooted check below.
|
|
34
|
+
name = ANONYMOUS_NAME_BY_STARTING_TAG.fetch(substring.chars.fetch(0)) if name.empty? && !substring.empty?
|
|
25
35
|
if name.start_with?('::')
|
|
26
36
|
name = name[2..]
|
|
27
37
|
rooted = true
|
|
@@ -443,6 +453,9 @@ module Solargraph
|
|
|
443
453
|
# @param context_type [ComplexType] The receiver type
|
|
444
454
|
# @return [UniqueType, ComplexType]
|
|
445
455
|
def resolve_generics definitions, context_type
|
|
456
|
+
# Method-scoped generics bind per call site via
|
|
457
|
+
# resolve_generics_from_context, not against a receiver's type params.
|
|
458
|
+
return self if definitions.is_a?(Pin::Callable)
|
|
446
459
|
return self if definitions.nil? || definitions.generics.empty?
|
|
447
460
|
|
|
448
461
|
transform(name) do |t|
|
|
@@ -50,9 +50,10 @@ module Solargraph
|
|
|
50
50
|
def resolve_generics_from_context generics_to_resolve, context_type, resolved_generic_values: {}
|
|
51
51
|
return self unless generic?
|
|
52
52
|
|
|
53
|
-
ComplexType.new(@items.map do |
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
ComplexType.new(@items.map do |unique_type|
|
|
54
|
+
unique_type.resolve_generics_from_context(generics_to_resolve,
|
|
55
|
+
destructure_context_type(unique_type, context_type),
|
|
56
|
+
resolved_generic_values: resolved_generic_values)
|
|
56
57
|
end)
|
|
57
58
|
end
|
|
58
59
|
|
|
@@ -561,6 +562,25 @@ module Solargraph
|
|
|
561
562
|
|
|
562
563
|
private
|
|
563
564
|
|
|
565
|
+
# Narrows what a generic member binds against: context members this type
|
|
566
|
+
# already matches concretely are dropped. Nothing left over means no value
|
|
567
|
+
# can reach the generic, which is bot.
|
|
568
|
+
#
|
|
569
|
+
# @param unique_type [UniqueType] A member of @items
|
|
570
|
+
# @param context_type [ComplexType, UniqueType, nil]
|
|
571
|
+
# @return [ComplexType, UniqueType, nil]
|
|
572
|
+
def destructure_context_type unique_type, context_type
|
|
573
|
+
return context_type unless unique_type.generic? && context_type.is_a?(ComplexType) && @items.length > 1
|
|
574
|
+
|
|
575
|
+
concrete_items = @items.reject(&:generic?)
|
|
576
|
+
return context_type if concrete_items.empty?
|
|
577
|
+
|
|
578
|
+
remaining_items = context_type.items.reject { |ct| concrete_items.any? { |ci| ci.name == ct.name } }
|
|
579
|
+
return BOT if remaining_items.empty?
|
|
580
|
+
|
|
581
|
+
ComplexType.new(remaining_items)
|
|
582
|
+
end
|
|
583
|
+
|
|
564
584
|
# @todo This is a quick and dirty hack that forces `self` keywords
|
|
565
585
|
# to reference an instance of their class and never the class itself.
|
|
566
586
|
# This behavior may change depending on which result is expected
|
|
@@ -9,9 +9,6 @@ module Solargraph
|
|
|
9
9
|
autoload :Gemfile, 'solargraph/convention/gemfile'
|
|
10
10
|
autoload :Gemspec, 'solargraph/convention/gemspec'
|
|
11
11
|
autoload :Rakefile, 'solargraph/convention/rakefile'
|
|
12
|
-
autoload :StructDefinition, 'solargraph/convention/struct_definition'
|
|
13
|
-
autoload :DataDefinition, 'solargraph/convention/data_definition'
|
|
14
|
-
autoload :ActiveSupportConcern, 'solargraph/convention/active_support_concern'
|
|
15
12
|
|
|
16
13
|
# @type [Set<Convention::Base>]
|
|
17
14
|
@@conventions = Set.new
|
|
@@ -38,12 +35,14 @@ module Solargraph
|
|
|
38
35
|
result
|
|
39
36
|
end
|
|
40
37
|
|
|
41
|
-
# @
|
|
38
|
+
# @todo Determine what argument needs to be passed here
|
|
39
|
+
#
|
|
40
|
+
# @param object [Object]
|
|
42
41
|
# @return [Environ]
|
|
43
|
-
def self.for_global
|
|
42
|
+
def self.for_global object
|
|
44
43
|
result = Environ.new
|
|
45
44
|
@@conventions.each do |conv|
|
|
46
|
-
result.merge conv.global(
|
|
45
|
+
result.merge conv.global(object)
|
|
47
46
|
end
|
|
48
47
|
result
|
|
49
48
|
end
|
|
@@ -73,6 +72,5 @@ module Solargraph
|
|
|
73
72
|
register Gemfile
|
|
74
73
|
register Gemspec
|
|
75
74
|
register Rakefile
|
|
76
|
-
register ActiveSupportConcern
|
|
77
75
|
end
|
|
78
76
|
end
|
|
@@ -13,10 +13,6 @@ module Solargraph
|
|
|
13
13
|
# @sg-ignore Need to add nil check here
|
|
14
14
|
map = api_map.source_map(source.filename)
|
|
15
15
|
map.requires.each { |ref| refs[ref.name] = ref }
|
|
16
|
-
api_map.missing_docs.each do |r|
|
|
17
|
-
next unless refs.key?(r)
|
|
18
|
-
result.push docs_error(r, refs[r].location)
|
|
19
|
-
end
|
|
20
16
|
api_map.unresolved_requires.each do |r|
|
|
21
17
|
next unless refs.key?(r)
|
|
22
18
|
result.push require_error(r, refs[r].location)
|
|
@@ -11,7 +11,6 @@ module Solargraph
|
|
|
11
11
|
# return [] unless args.include?('always') || api_map.workspaced?(source.filename)
|
|
12
12
|
severity = Diagnostics::Severities::ERROR
|
|
13
13
|
level = args.reverse.find { |a| %w[normal typed strict strong].include?(a) } || :normal
|
|
14
|
-
# @sg-ignore sensitive typing needs to handle || on nil types
|
|
15
14
|
checker = Solargraph::TypeChecker.new(source.filename, api_map: api_map, level: level.to_sym)
|
|
16
15
|
checker.problems
|
|
17
16
|
.sort { |a, b| a.location.range.start.line <=> b.location.range.start.line }
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'set'
|
|
4
|
+
|
|
5
|
+
module Solargraph
|
|
6
|
+
# @todo This class might need a way to track changes to the repo, e.g.,
|
|
7
|
+
# bundle or dependency updates
|
|
8
|
+
#
|
|
9
|
+
class External
|
|
10
|
+
# @return [String]
|
|
11
|
+
attr_reader :directory
|
|
12
|
+
|
|
13
|
+
# @return [Array<String>]
|
|
14
|
+
attr_reader :requires
|
|
15
|
+
|
|
16
|
+
attr_reader :extra_gem_names
|
|
17
|
+
|
|
18
|
+
# @param directory [String]
|
|
19
|
+
# @param requires [Array<String>]
|
|
20
|
+
# @param extra_gem_names [Array<String>]
|
|
21
|
+
def initialize directory, requires, extra_gem_names = []
|
|
22
|
+
@repo = Repo.new(directory)
|
|
23
|
+
@directory = directory
|
|
24
|
+
@requires = requires
|
|
25
|
+
@extra_gem_names = extra_gem_names
|
|
26
|
+
update!
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def configured_gem_names
|
|
30
|
+
loaded_gems.map(&:name) + unloaded_gems.map(&:name) + extra_gem_names
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def unresolved_requires
|
|
34
|
+
@unresolved_requires ||= []
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def unresolved_dependencies
|
|
38
|
+
@unresolved_dependencies ||= []
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def loaded_gems
|
|
42
|
+
@loaded_gems ||= Set.new
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def unloaded_gems
|
|
46
|
+
@unloaded_gems ||= Set.new
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def unfound_gem_names
|
|
50
|
+
@unfound_gem_names ||= []
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def loaded_stdlibs
|
|
54
|
+
@loaded_stdlibs ||= Set.new
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def pins
|
|
58
|
+
@pins ||= []
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# @return [Integer] incremented whenever the external pin set is rebuilt
|
|
62
|
+
def generation
|
|
63
|
+
@generation ||= 0
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# @param new_requires [Array<String>]
|
|
67
|
+
# @return [Boolean]
|
|
68
|
+
def update new_requires, new_gem_names = []
|
|
69
|
+
return false if requires == new_requires && new_gem_names == extra_gem_names && !cache_changed?
|
|
70
|
+
|
|
71
|
+
requires.replace new_requires
|
|
72
|
+
extra_gem_names.replace new_gem_names
|
|
73
|
+
update!
|
|
74
|
+
true
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# @return [Array<String>]
|
|
78
|
+
def rbs_collection_paths
|
|
79
|
+
@rbs_collection_paths ||= read_rbs_collection_paths
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# @return [String, nil]
|
|
83
|
+
def rbs_collection_config_path
|
|
84
|
+
# @todo Get rid of the '*' case
|
|
85
|
+
@rbs_collection_config_path ||= unless directory.nil? || directory.empty? || directory == '*'
|
|
86
|
+
yaml_file = File.join(directory, 'rbs_collection.yaml')
|
|
87
|
+
yaml_file if File.file?(yaml_file)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
|
|
93
|
+
def update!
|
|
94
|
+
@generation = generation + 1
|
|
95
|
+
clear_all
|
|
96
|
+
load_requires
|
|
97
|
+
load_gem_names
|
|
98
|
+
# load_rbs_collection
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def cache_changed?
|
|
102
|
+
unloaded_gems.any? { |gem| Collection::Gem.cached?(gem) }
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def load_requires
|
|
106
|
+
bundler_require = false
|
|
107
|
+
|
|
108
|
+
requires.uniq.each do |path|
|
|
109
|
+
if path == 'bundler/require'
|
|
110
|
+
bundler_require = true
|
|
111
|
+
end
|
|
112
|
+
if RbsMap::Stdlib.has?(path)
|
|
113
|
+
pins.concat Collection::Stdlib.load(path) if loaded_stdlibs.add?(path)
|
|
114
|
+
else
|
|
115
|
+
metagem = @repo.find_by_path(path)
|
|
116
|
+
next unresolved_requires.push(path) unless metagem
|
|
117
|
+
process_gem metagem
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
return unless bundler_require
|
|
122
|
+
|
|
123
|
+
@repo.find_by_group(:default).each { |metagem| process_gem metagem }
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def load_gem_names
|
|
127
|
+
extra_gem_names.uniq.each do |gem|
|
|
128
|
+
metagem = @repo.find_by_name(gem)
|
|
129
|
+
next unfound_gem_names.push(gem) unless metagem
|
|
130
|
+
process_gem metagem
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def rbs_collection
|
|
135
|
+
@rbs_collection ||= RbsCollection.new(rbs_collection_lockfile)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def rbs_collection_lockfile
|
|
139
|
+
return if directory.nil? || directory.empty? || directory == '*'
|
|
140
|
+
lockfile = File.join(directory, 'rbs_collection.lock.yaml')
|
|
141
|
+
lockfile if File.file?(lockfile)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def clear_all
|
|
145
|
+
pins.clear
|
|
146
|
+
unresolved_requires.clear
|
|
147
|
+
unresolved_dependencies.clear
|
|
148
|
+
loaded_gems.clear
|
|
149
|
+
unloaded_gems.clear
|
|
150
|
+
loaded_stdlibs.clear
|
|
151
|
+
unfound_gem_names.clear
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def process_gem metagem
|
|
155
|
+
return if loaded_gems.include?(metagem) || unloaded_gems.include?(metagem)
|
|
156
|
+
|
|
157
|
+
if metagem.cacheable? && !Collection::Gem.cached?(metagem)
|
|
158
|
+
unloaded_gems.add metagem
|
|
159
|
+
else
|
|
160
|
+
loaded_gems.add metagem
|
|
161
|
+
base_pins = Collection::Gem.load(metagem)
|
|
162
|
+
rbsc_pins = rbs_collection.load(metagem)
|
|
163
|
+
if rbsc_pins.empty?
|
|
164
|
+
pins.concat base_pins
|
|
165
|
+
else
|
|
166
|
+
pins.concat RbsMap::Helpers.combine(base_pins, rbsc_pins)
|
|
167
|
+
end
|
|
168
|
+
# Gems might not declare stdlib dependencies as gem dependencies
|
|
169
|
+
# (e.g., `activesupport` requires `date`)
|
|
170
|
+
base_pins.select { |pin| pin.is_a?(Pin::Reference::Require) && RbsMap::Stdlib.has?(pin.name) }
|
|
171
|
+
.each do |pin|
|
|
172
|
+
pins.concat Collection::Stdlib.load(pin.name) if loaded_stdlibs.add?(pin.name)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
load_dependencies metagem
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def load_dependencies parent
|
|
179
|
+
parent.dependencies.each do |name|
|
|
180
|
+
next if loaded_gems.map(&:name).include?(name) || unloaded_gems.map(&:name).include?(name)
|
|
181
|
+
metagem = @repo.find_by_name(name)
|
|
182
|
+
next unresolved_dependencies.push(name) unless metagem
|
|
183
|
+
process_gem metagem
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
@@ -118,11 +118,13 @@ module Solargraph
|
|
|
118
118
|
.tap { |lib| lib.add_observer self }
|
|
119
119
|
end
|
|
120
120
|
|
|
121
|
-
#
|
|
121
|
+
# The Library observer callback that #generic_library registers.
|
|
122
122
|
#
|
|
123
|
+
# @abstract
|
|
124
|
+
# @param progress [Solargraph::LanguageServer::Progress, nil]
|
|
123
125
|
# @return [void]
|
|
124
126
|
def update progress
|
|
125
|
-
|
|
127
|
+
raise NotImplementedError, 'Host::Dispatch requires an update method'
|
|
126
128
|
end
|
|
127
129
|
end
|
|
128
130
|
end
|
|
@@ -495,7 +495,7 @@ module Solargraph
|
|
|
495
495
|
)
|
|
496
496
|
result.concat(library.locate_pins(location).select { |pin| pin.name == params['label'] })
|
|
497
497
|
end
|
|
498
|
-
if params['data']['path']
|
|
498
|
+
if params['data']['path'] && !params['data']['path'].empty?
|
|
499
499
|
result.concat library.path_pins(params['data']['path'])
|
|
500
500
|
# @todo This exception is necessary because `Library#path_pins` does
|
|
501
501
|
# not perform a namespace method query, so the implicit `.new` pin
|
|
@@ -706,6 +706,15 @@ module Solargraph
|
|
|
706
706
|
client_capabilities['window'] && client_capabilities['window']['workDoneProgress']
|
|
707
707
|
end
|
|
708
708
|
|
|
709
|
+
# The Library observer callback that sends a library's progress to
|
|
710
|
+
# the client.
|
|
711
|
+
#
|
|
712
|
+
# @param progress [Solargraph::LanguageServer::Progress, nil]
|
|
713
|
+
# @return [void]
|
|
714
|
+
def update progress
|
|
715
|
+
progress&.send(self)
|
|
716
|
+
end
|
|
717
|
+
|
|
709
718
|
private
|
|
710
719
|
|
|
711
720
|
# @return [Array<Integer>]
|
|
@@ -736,7 +745,6 @@ module Solargraph
|
|
|
736
745
|
# @return [String]
|
|
737
746
|
def normalize_separators path
|
|
738
747
|
return path if File::ALT_SEPARATOR.nil?
|
|
739
|
-
# @sg-ignore flow sensitive typing needs to handle constants
|
|
740
748
|
path.gsub(File::ALT_SEPARATOR, File::SEPARATOR)
|
|
741
749
|
end
|
|
742
750
|
|
|
@@ -7,6 +7,12 @@ module Solargraph
|
|
|
7
7
|
# completionItem/resolve message handler
|
|
8
8
|
#
|
|
9
9
|
class Resolve < Base
|
|
10
|
+
# Properties that must not be changed during completionItem/resolve
|
|
11
|
+
# according to the LSP specification.
|
|
12
|
+
#
|
|
13
|
+
# @see https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_completion
|
|
14
|
+
IMMUTABLE_FIELDS = %i[label kind sortText filterText insertText textEdit data].freeze
|
|
15
|
+
|
|
10
16
|
def process
|
|
11
17
|
pins = host.locate_pins(params)
|
|
12
18
|
set_result merge(pins)
|
|
@@ -17,14 +23,15 @@ module Solargraph
|
|
|
17
23
|
# @param pins [Array<Pin::Base>]
|
|
18
24
|
# @return [Hash]
|
|
19
25
|
def merge pins
|
|
20
|
-
|
|
26
|
+
original = params.transform_keys(&:to_sym)
|
|
27
|
+
return original if pins.empty?
|
|
21
28
|
docs = pins
|
|
22
29
|
.reject { |pin| pin.documentation.empty? && pin.return_type.undefined? }
|
|
23
|
-
result =
|
|
24
|
-
.transform_keys(&:to_sym)
|
|
30
|
+
result = original
|
|
25
31
|
.merge(pins.first.resolve_completion_item)
|
|
26
32
|
.merge(documentation: markup_content(join_docs(docs)))
|
|
27
33
|
result[:detail] = pins.first.detail
|
|
34
|
+
IMMUTABLE_FIELDS.each { |field| result[field] = original[field] }
|
|
28
35
|
result
|
|
29
36
|
end
|
|
30
37
|
|
|
@@ -15,7 +15,7 @@ module Solargraph
|
|
|
15
15
|
|
|
16
16
|
# @return [Array<Hash>, nil]
|
|
17
17
|
def code_location
|
|
18
|
-
suggestions = host.definitions_at(params['textDocument']['uri'], @line, @column)
|
|
18
|
+
suggestions = host.definitions_at(params['textDocument']['uri'], @line, @column) || []
|
|
19
19
|
# @sg-ignore Need to add nil check here
|
|
20
20
|
return nil if suggestions.empty?
|
|
21
21
|
# @sg-ignore Need to add nil check here
|
|
@@ -29,7 +29,10 @@ module Solargraph
|
|
|
29
29
|
|
|
30
30
|
# @return [Array<Hash>, nil]
|
|
31
31
|
def require_location
|
|
32
|
-
# @todo Terrible hack
|
|
32
|
+
# @todo Terrible hack - move this logic to [Solargraph::Source::Chain::Parameter]
|
|
33
|
+
# @example
|
|
34
|
+
# require 'click_me'
|
|
35
|
+
# ^^^^^^^^^^
|
|
33
36
|
lib = host.library_for(params['textDocument']['uri'])
|
|
34
37
|
rloc = Solargraph::Location.new(uri_to_file(params['textDocument']['uri']),
|
|
35
38
|
Solargraph::Range.from_to(@line, @column, @line, @column))
|
|
@@ -11,8 +11,7 @@ module Solargraph
|
|
|
11
11
|
contents = []
|
|
12
12
|
suggestions = host.definitions_at(params['textDocument']['uri'], line, col)
|
|
13
13
|
last_link = nil
|
|
14
|
-
|
|
15
|
-
suggestions.each do |pin|
|
|
14
|
+
suggestions&.each do |pin|
|
|
16
15
|
parts = []
|
|
17
16
|
this_link = host.options['enablePages'] ? pin.link_documentation : pin.text_documentation
|
|
18
17
|
parts.push this_link if !this_link.nil? && this_link != last_link
|