solargraph 0.53.3 → 0.54.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +29 -0
- data/lib/solargraph/api_map/cache.rb +2 -12
- data/lib/solargraph/api_map/store.rb +11 -6
- data/lib/solargraph/api_map.rb +80 -26
- data/lib/solargraph/complex_type/type_methods.rb +62 -30
- data/lib/solargraph/complex_type/unique_type.rb +117 -66
- data/lib/solargraph/complex_type.rb +41 -25
- data/lib/solargraph/doc_map.rb +19 -3
- data/lib/solargraph/gem_pins.rb +9 -1
- data/lib/solargraph/language_server/host/dispatch.rb +8 -1
- data/lib/solargraph/language_server/host/sources.rb +1 -61
- data/lib/solargraph/language_server/host.rb +39 -68
- data/lib/solargraph/language_server/message/base.rb +1 -1
- data/lib/solargraph/language_server/message/initialize.rb +14 -0
- data/lib/solargraph/language_server/message/text_document/formatting.rb +1 -0
- data/lib/solargraph/language_server/progress.rb +118 -0
- data/lib/solargraph/language_server.rb +1 -0
- data/lib/solargraph/library.rb +136 -96
- data/lib/solargraph/parser/node_processor/base.rb +3 -2
- data/lib/solargraph/parser/node_processor.rb +1 -0
- data/lib/solargraph/parser/parser_gem/class_methods.rb +3 -7
- data/lib/solargraph/parser/parser_gem/node_methods.rb +0 -4
- data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +47 -0
- data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +5 -3
- data/lib/solargraph/parser/parser_gem/node_processors.rb +2 -0
- data/lib/solargraph/pin/base_variable.rb +34 -5
- data/lib/solargraph/pin/block.rb +62 -7
- data/lib/solargraph/pin/delegated_method.rb +5 -1
- data/lib/solargraph/pin/documenting.rb +2 -0
- data/lib/solargraph/pin/method.rb +4 -2
- data/lib/solargraph/pin/parameter.rb +5 -28
- data/lib/solargraph/rbs_map/conversions.rb +12 -6
- data/lib/solargraph/rbs_map/core_fills.rb +4 -1
- data/lib/solargraph/rbs_map.rb +11 -3
- data/lib/solargraph/shell.rb +18 -13
- data/lib/solargraph/source/chain.rb +20 -0
- data/lib/solargraph/source/updater.rb +1 -0
- data/lib/solargraph/source.rb +0 -44
- data/lib/solargraph/source_map/clip.rb +1 -0
- data/lib/solargraph/source_map/mapper.rb +3 -2
- data/lib/solargraph/source_map.rb +10 -0
- data/lib/solargraph/type_checker.rb +44 -18
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/workspace/config.rb +2 -1
- data/lib/solargraph/workspace.rb +13 -0
- data/lib/solargraph/yard_map/mapper/to_method.rb +5 -2
- metadata +4 -3
- data/lib/solargraph/language_server/host/cataloger.rb +0 -57
@@ -9,10 +9,14 @@ module Solargraph
|
|
9
9
|
# @return [Parser::AST::Node, nil]
|
10
10
|
attr_reader :assignment
|
11
11
|
|
12
|
+
attr_accessor :mass_assignment
|
13
|
+
|
12
14
|
# @param assignment [Parser::AST::Node, nil]
|
13
15
|
def initialize assignment: nil, **splat
|
14
16
|
super(**splat)
|
15
17
|
@assignment = assignment
|
18
|
+
# @type [nil, ::Array(Parser::AST::Node, Integer)]
|
19
|
+
@mass_assignment = nil
|
16
20
|
end
|
17
21
|
|
18
22
|
def completion_item_kind
|
@@ -36,10 +40,12 @@ module Solargraph
|
|
36
40
|
true
|
37
41
|
end
|
38
42
|
|
39
|
-
|
40
|
-
|
43
|
+
# @param parent_node [Parser::AST::Node]
|
44
|
+
# @param api_map [ApiMap]
|
45
|
+
# @return [::Array<ComplexType>]
|
46
|
+
def return_types_from_node(parent_node, api_map)
|
41
47
|
types = []
|
42
|
-
value_position_nodes_only(
|
48
|
+
value_position_nodes_only(parent_node).each do |node|
|
43
49
|
# Nil nodes may not have a location
|
44
50
|
if node.nil? || node.type == :NIL || node.type == :nil
|
45
51
|
types.push ComplexType::NIL
|
@@ -55,8 +61,31 @@ module Solargraph
|
|
55
61
|
types.push result unless result.undefined?
|
56
62
|
end
|
57
63
|
end
|
58
|
-
|
59
|
-
|
64
|
+
types
|
65
|
+
end
|
66
|
+
|
67
|
+
# @param api_map [ApiMap]
|
68
|
+
# @return [ComplexType]
|
69
|
+
def probe api_map
|
70
|
+
unless @assignment.nil?
|
71
|
+
types = return_types_from_node(@assignment, api_map)
|
72
|
+
return ComplexType.new(types.uniq) unless types.empty?
|
73
|
+
end
|
74
|
+
|
75
|
+
unless @mass_assignment.nil?
|
76
|
+
mass_node, index = @mass_assignment
|
77
|
+
types = return_types_from_node(mass_node, api_map)
|
78
|
+
types.map! do |type|
|
79
|
+
if type.tuple?
|
80
|
+
type.all_params[index]
|
81
|
+
elsif ['::Array', '::Set', '::Enumerable'].include?(type.rooted_name)
|
82
|
+
type.all_params.first
|
83
|
+
end
|
84
|
+
end.compact!
|
85
|
+
return ComplexType.new(types.uniq) unless types.empty?
|
86
|
+
end
|
87
|
+
|
88
|
+
ComplexType::UNDEFINED
|
60
89
|
end
|
61
90
|
|
62
91
|
def == other
|
data/lib/solargraph/pin/block.rb
CHANGED
@@ -18,17 +18,18 @@ module Solargraph
|
|
18
18
|
@receiver = receiver
|
19
19
|
@context = context
|
20
20
|
@parameters = args
|
21
|
+
@return_type = ComplexType.parse('::Proc')
|
21
22
|
@node = node
|
22
23
|
end
|
23
24
|
|
24
25
|
# @param api_map [ApiMap]
|
25
26
|
# @return [void]
|
26
27
|
def rebind api_map
|
27
|
-
@
|
28
|
+
@rebind ||= maybe_rebind(api_map)
|
28
29
|
end
|
29
30
|
|
30
31
|
def binder
|
31
|
-
@
|
32
|
+
@rebind&.defined? ? @rebind : closure.binder
|
32
33
|
end
|
33
34
|
|
34
35
|
# @return [::Array<Parameter>]
|
@@ -41,20 +42,74 @@ module Solargraph
|
|
41
42
|
@parameter_names ||= parameters.map(&:name)
|
42
43
|
end
|
43
44
|
|
45
|
+
# @param yield_types [::Array<ComplexType>]
|
46
|
+
# @param parameters [::Array<Parameter>]
|
47
|
+
#
|
48
|
+
# @return [::Array<ComplexType>]
|
49
|
+
def destructure_yield_types(yield_types, parameters)
|
50
|
+
return yield_types if yield_types.length == parameters.length
|
51
|
+
|
52
|
+
# yielding a tuple into a block will destructure the tuple
|
53
|
+
if yield_types.length == 1
|
54
|
+
yield_type = yield_types.first
|
55
|
+
return yield_type.all_params if yield_type.tuple? && yield_type.all_params.length == parameters.length
|
56
|
+
end
|
57
|
+
parameters.map { ComplexType::UNDEFINED }
|
58
|
+
end
|
59
|
+
|
60
|
+
# @todo the next step with parameters, arguments, destructuring,
|
61
|
+
# kwargs, etc logic is probably either creating a Parameters
|
62
|
+
# or Callable pin that encapsulates and shares the logic
|
63
|
+
# between methods, blocks and signatures. It could live in
|
64
|
+
# Signature if Method didn't also own potentially different
|
65
|
+
# set of parameters, generics and return types.
|
66
|
+
|
67
|
+
# @param api_map [ApiMap]
|
68
|
+
# @return [::Array<ComplexType>]
|
69
|
+
def typify_parameters(api_map)
|
70
|
+
chain = Parser.chain(receiver, filename, node)
|
71
|
+
clip = api_map.clip_at(location.filename, location.range.start)
|
72
|
+
locals = clip.locals - [self]
|
73
|
+
meths = chain.define(api_map, closure, locals)
|
74
|
+
# @todo Convert logic to use signatures
|
75
|
+
meths.each do |meth|
|
76
|
+
next if meth.block.nil?
|
77
|
+
|
78
|
+
yield_types = meth.block.parameters.map(&:return_type)
|
79
|
+
# 'arguments' is what the method says it will yield to the
|
80
|
+
# block; 'parameters' is what the block accepts
|
81
|
+
argument_types = destructure_yield_types(yield_types, parameters)
|
82
|
+
param_types = argument_types.each_with_index.map do |arg_type, idx|
|
83
|
+
param = parameters[idx]
|
84
|
+
param_type = chain.base.infer(api_map, param, locals)
|
85
|
+
unless arg_type.nil?
|
86
|
+
if arg_type.generic? && param_type.defined?
|
87
|
+
namespace_pin = api_map.get_namespace_pins(meth.namespace, closure.namespace).first
|
88
|
+
arg_type.resolve_generics(namespace_pin, param_type)
|
89
|
+
else
|
90
|
+
arg_type.self_to(chain.base.infer(api_map, self, locals).namespace).qualify(api_map, meth.context.namespace)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
return param_types if param_types.all?(&:defined?)
|
95
|
+
end
|
96
|
+
parameters.map { ComplexType::UNDEFINED }
|
97
|
+
end
|
98
|
+
|
44
99
|
private
|
45
100
|
|
46
101
|
# @param api_map [ApiMap]
|
47
|
-
# @return [ComplexType
|
48
|
-
def
|
49
|
-
return
|
102
|
+
# @return [ComplexType]
|
103
|
+
def maybe_rebind api_map
|
104
|
+
return ComplexType::UNDEFINED unless receiver
|
50
105
|
|
51
106
|
chain = Parser.chain(receiver, location.filename)
|
52
107
|
locals = api_map.source_map(location.filename).locals_at(location)
|
53
108
|
receiver_pin = chain.define(api_map, self, locals).first
|
54
|
-
return
|
109
|
+
return ComplexType::UNDEFINED unless receiver_pin
|
55
110
|
|
56
111
|
types = receiver_pin.docstring.tag(:yieldreceiver)&.types
|
57
|
-
return
|
112
|
+
return ComplexType::UNDEFINED unless types&.any?
|
58
113
|
|
59
114
|
target = chain.base.infer(api_map, receiver_pin, locals)
|
60
115
|
target = full_context unless target.defined?
|
@@ -11,8 +11,9 @@ module Solargraph
|
|
11
11
|
# given closure/scope, and the delegated method will then be resolved
|
12
12
|
# to a method pin on that type.
|
13
13
|
#
|
14
|
-
# @param
|
14
|
+
# @param method [Method, nil] an already resolved method pin.
|
15
15
|
# @param receiver [Source::Chain, nil] the source code used to resolve the receiver for this delegated method.
|
16
|
+
# @param name [String]
|
16
17
|
# @param receiver_method_name [String] the method name that will be called on the receiver (defaults to :name).
|
17
18
|
def initialize(method: nil, receiver: nil, name: method&.name, receiver_method_name: name, **splat)
|
18
19
|
raise ArgumentError, 'either :method or :receiver is required' if (method && receiver) || (!method && !receiver)
|
@@ -37,6 +38,7 @@ module Solargraph
|
|
37
38
|
end
|
38
39
|
end
|
39
40
|
|
41
|
+
# @param api_map [ApiMap]
|
40
42
|
def resolvable?(api_map)
|
41
43
|
resolve_method(api_map)
|
42
44
|
!!@resolved_method
|
@@ -79,6 +81,7 @@ module Solargraph
|
|
79
81
|
# helper to print a source chain as code, probably not 100% correct.
|
80
82
|
#
|
81
83
|
# @param chain [Source::Chain]
|
84
|
+
# @return [String]
|
82
85
|
def print_chain(chain)
|
83
86
|
out = +''
|
84
87
|
chain.links.each_with_index do |link, index|
|
@@ -91,6 +94,7 @@ module Solargraph
|
|
91
94
|
end
|
92
95
|
out << link.word
|
93
96
|
end
|
97
|
+
out
|
94
98
|
end
|
95
99
|
end
|
96
100
|
end
|
@@ -135,7 +135,7 @@ module Solargraph
|
|
135
135
|
result = []
|
136
136
|
result.push generate_signature(parameters, top_type) if top_type.defined?
|
137
137
|
result.concat(overloads.map { |meth| generate_signature(meth.parameters, meth.return_type) }) unless overloads.empty?
|
138
|
-
result.push generate_signature(parameters,
|
138
|
+
result.push generate_signature(parameters, @return_type || ComplexType::UNDEFINED) if result.empty?
|
139
139
|
result
|
140
140
|
end
|
141
141
|
end
|
@@ -288,7 +288,9 @@ module Solargraph
|
|
288
288
|
|
289
289
|
# @return [::Array<Pin::Method>]
|
290
290
|
def overloads
|
291
|
-
|
291
|
+
# Ignore overload tags with nil parameters. If it's not an array, the
|
292
|
+
# tag's source is likely malformed.
|
293
|
+
@overloads ||= docstring.tags(:overload).select(&:parameters).map do |tag|
|
292
294
|
Pin::Signature.new(
|
293
295
|
generics,
|
294
296
|
tag.parameters.map do |src|
|
@@ -76,6 +76,7 @@ module Solargraph
|
|
76
76
|
end
|
77
77
|
end
|
78
78
|
|
79
|
+
# @return [ComplexType]
|
79
80
|
def return_type
|
80
81
|
if @return_type.nil?
|
81
82
|
@return_type = ComplexType::UNDEFINED
|
@@ -99,7 +100,9 @@ module Solargraph
|
|
99
100
|
#
|
100
101
|
# @return [Integer]
|
101
102
|
def index
|
102
|
-
|
103
|
+
# @type [Method, Block]
|
104
|
+
method_pin = closure
|
105
|
+
method_pin.parameter_names.index(name)
|
103
106
|
end
|
104
107
|
|
105
108
|
# @param api_map [ApiMap]
|
@@ -140,33 +143,7 @@ module Solargraph
|
|
140
143
|
# @return [ComplexType]
|
141
144
|
def typify_block_param api_map
|
142
145
|
if closure.is_a?(Pin::Block) && closure.receiver
|
143
|
-
|
144
|
-
clip = api_map.clip_at(location.filename, location.range.start)
|
145
|
-
locals = clip.locals - [self]
|
146
|
-
meths = chain.define(api_map, closure, locals)
|
147
|
-
receiver_type = chain.base.infer(api_map, closure, locals)
|
148
|
-
meths.each do |meth|
|
149
|
-
block_signature = meth.block
|
150
|
-
if block_signature
|
151
|
-
yield_type = block_signature.parameters[index]&.return_type
|
152
|
-
else
|
153
|
-
# @todo move the yieldparam tag parsing logic into the
|
154
|
-
# creation of Method pins so we don't need this else
|
155
|
-
# statement
|
156
|
-
yps = meth.docstring.tags(:yieldparam)
|
157
|
-
unless yps[index].nil? or yps[index].types.nil? or yps[index].types.empty?
|
158
|
-
yield_type = ComplexType.try_parse(yps[index].types.first)
|
159
|
-
end
|
160
|
-
end
|
161
|
-
unless yield_type.nil?
|
162
|
-
if yield_type.generic? && receiver_type.defined?
|
163
|
-
namespace_pin = api_map.get_namespace_pins(meth.namespace, closure.namespace).first
|
164
|
-
return yield_type.resolve_generics(namespace_pin, receiver_type)
|
165
|
-
else
|
166
|
-
return yield_type.self_to(chain.base.infer(api_map, closure, locals).namespace).qualify(api_map, meth.context.namespace)
|
167
|
-
end
|
168
|
-
end
|
169
|
-
end
|
146
|
+
return closure.typify_parameters(api_map)[index]
|
170
147
|
end
|
171
148
|
ComplexType::UNDEFINED
|
172
149
|
end
|
@@ -38,6 +38,8 @@ module Solargraph
|
|
38
38
|
environment = RBS::Environment.from_loader(loader).resolve_type_names
|
39
39
|
cursor = pins.length
|
40
40
|
environment.declarations.each { |decl| convert_decl_to_pin(decl, Solargraph::Pin::ROOT_PIN) }
|
41
|
+
added_pins = pins[cursor..-1]
|
42
|
+
added_pins.each { |pin| pin.source = :rbs }
|
41
43
|
end
|
42
44
|
|
43
45
|
# @param decl [RBS::AST::Declarations::Base]
|
@@ -514,13 +516,17 @@ module Solargraph
|
|
514
516
|
|
515
517
|
# @param type_name [RBS::TypeName]
|
516
518
|
# @param type_args [Enumerable<RBS::Types::Bases::Base>]
|
517
|
-
# @return [ComplexType]
|
519
|
+
# @return [ComplexType::UniqueType]
|
518
520
|
def generic_type(type_name, type_args)
|
519
521
|
base = RBS_TO_YARD_TYPE[type_name.relative!.to_s] || type_name.relative!.to_s
|
520
|
-
params = type_args.map { |a| other_type_to_tag(a) }.reject { |t| t == 'undefined' }
|
521
|
-
|
522
|
-
|
523
|
-
|
522
|
+
params = type_args.map { |a| other_type_to_tag(a) }.reject { |t| t == 'undefined' }.map do |t|
|
523
|
+
ComplexType.try_parse(t)
|
524
|
+
end
|
525
|
+
if base == 'Hash' && params.length == 2
|
526
|
+
ComplexType::UniqueType.new(base, [params.first], [params.last], rooted: true, parameters_type: :hash)
|
527
|
+
else
|
528
|
+
ComplexType::UniqueType.new(base, [], params, rooted: true, parameters_type: :list)
|
529
|
+
end
|
524
530
|
end
|
525
531
|
|
526
532
|
# @param type_name [RBS::TypeName]
|
@@ -584,7 +590,7 @@ module Solargraph
|
|
584
590
|
end
|
585
591
|
|
586
592
|
# @param decl [RBS::AST::Declarations::Class, RBS::AST::Declarations::Module]
|
587
|
-
# @param
|
593
|
+
# @param namespace [Pin::Namespace]
|
588
594
|
# @return [void]
|
589
595
|
def add_mixins decl, namespace
|
590
596
|
decl.each_mixin do |mixin|
|
@@ -29,7 +29,10 @@ module Solargraph
|
|
29
29
|
Override.from_comment('Module#class_eval', '@yieldreceiver [Class<self>]'),
|
30
30
|
Override.from_comment('Module#class_exec', '@yieldreceiver [Class<self>]'),
|
31
31
|
Override.from_comment('Module#module_eval', '@yieldreceiver [Module<self>]'),
|
32
|
-
Override.from_comment('Module#module_exec', '@yieldreceiver [Module<self>]')
|
32
|
+
Override.from_comment('Module#module_exec', '@yieldreceiver [Module<self>]'),
|
33
|
+
# RBS does not define Class with a generic, so all calls to
|
34
|
+
# generic() return an 'untyped'. We can do better:
|
35
|
+
Override.method_return('Class#allocate', 'self')
|
33
36
|
]
|
34
37
|
|
35
38
|
# HACK: Add Errno exception classes
|
data/lib/solargraph/rbs_map.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'pathname'
|
3
4
|
require 'rbs'
|
4
5
|
|
5
6
|
module Solargraph
|
@@ -17,10 +18,13 @@ module Solargraph
|
|
17
18
|
attr_reader :library
|
18
19
|
|
19
20
|
# @param library [String]
|
20
|
-
|
21
|
+
# @param version [String, nil]
|
22
|
+
# @param directories [Array<Pathname, String>]
|
23
|
+
def initialize library, version = nil, directories: []
|
21
24
|
@library = library
|
22
25
|
@version = version
|
23
26
|
@collection = nil
|
27
|
+
@directories = directories
|
24
28
|
loader = RBS::EnvironmentLoader.new(core_root: nil, repository: repository)
|
25
29
|
add_library loader, library, version
|
26
30
|
return unless resolved?
|
@@ -47,7 +51,11 @@ module Solargraph
|
|
47
51
|
end
|
48
52
|
|
49
53
|
def repository
|
50
|
-
@repository ||= RBS::Repository.new(no_stdlib: false)
|
54
|
+
@repository ||= RBS::Repository.new(no_stdlib: false).tap do |repo|
|
55
|
+
# @todo Temporarily ignoring external directories due to issues with
|
56
|
+
# incomplete/broken gem_rbs_collection installations
|
57
|
+
# @directories.each { |dir| repo.add(Pathname.new(dir)) }
|
58
|
+
end
|
51
59
|
end
|
52
60
|
|
53
61
|
# @param library [String]
|
@@ -71,7 +79,7 @@ module Solargraph
|
|
71
79
|
Solargraph.logger.info "#{short_name} successfully loaded library #{library}"
|
72
80
|
true
|
73
81
|
else
|
74
|
-
Solargraph.logger.
|
82
|
+
Solargraph.logger.info "#{short_name} failed to load library #{library}"
|
75
83
|
false
|
76
84
|
end
|
77
85
|
end
|
data/lib/solargraph/shell.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'benchmark'
|
3
4
|
require 'thor'
|
4
5
|
require 'yard'
|
5
6
|
|
@@ -149,23 +150,28 @@ module Solargraph
|
|
149
150
|
# @return [void]
|
150
151
|
def typecheck *files
|
151
152
|
directory = File.realpath(options[:directory])
|
152
|
-
api_map = Solargraph::ApiMap.load_with_cache(directory)
|
153
|
+
api_map = Solargraph::ApiMap.load_with_cache(directory, $stdout)
|
154
|
+
probcount = 0
|
153
155
|
if files.empty?
|
154
156
|
files = api_map.source_maps.map(&:filename)
|
155
157
|
else
|
156
158
|
files.map! { |file| File.realpath(file) }
|
157
159
|
end
|
158
|
-
probcount = 0
|
159
160
|
filecount = 0
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
161
|
+
|
162
|
+
time = Benchmark.measure {
|
163
|
+
files.each do |file|
|
164
|
+
checker = TypeChecker.new(file, api_map: api_map, level: options[:level].to_sym)
|
165
|
+
problems = checker.problems
|
166
|
+
next if problems.empty?
|
167
|
+
problems.sort! { |a, b| a.location.range.start.line <=> b.location.range.start.line }
|
168
|
+
puts problems.map { |prob| "#{prob.location.filename}:#{prob.location.range.start.line + 1} - #{prob.message}" }.join("\n")
|
169
|
+
filecount += 1
|
170
|
+
probcount += problems.length
|
171
|
+
end
|
172
|
+
# "
|
173
|
+
}
|
174
|
+
puts "Typecheck finished in #{time.real} seconds."
|
169
175
|
puts "#{probcount} problem#{probcount != 1 ? 's' : ''} found#{files.length != 1 ? " in #{filecount} of #{files.length} files" : ''}."
|
170
176
|
# "
|
171
177
|
exit 1 if probcount > 0
|
@@ -182,11 +188,10 @@ module Solargraph
|
|
182
188
|
option :verbose, type: :boolean, aliases: :v, desc: 'Verbose output', default: false
|
183
189
|
# @return [void]
|
184
190
|
def scan
|
185
|
-
require 'benchmark'
|
186
191
|
directory = File.realpath(options[:directory])
|
187
192
|
api_map = nil
|
188
193
|
time = Benchmark.measure {
|
189
|
-
api_map = Solargraph::ApiMap.load_with_cache(directory)
|
194
|
+
api_map = Solargraph::ApiMap.load_with_cache(directory, $stdout)
|
190
195
|
api_map.pins.each do |pin|
|
191
196
|
begin
|
192
197
|
puts pin_description(pin) if options[:verbose]
|
@@ -29,6 +29,8 @@ module Solargraph
|
|
29
29
|
|
30
30
|
@@inference_stack = []
|
31
31
|
@@inference_depth = 0
|
32
|
+
@@inference_invalidation_key = nil
|
33
|
+
@@inference_cache = {}
|
32
34
|
|
33
35
|
UNDEFINED_CALL = Chain::Call.new('<undefined>')
|
34
36
|
UNDEFINED_CONSTANT = Chain::Constant.new('<undefined>')
|
@@ -81,7 +83,25 @@ module Solargraph
|
|
81
83
|
# @param name_pin [Pin::Base]
|
82
84
|
# @param locals [::Enumerable<Pin::LocalVariable>]
|
83
85
|
# @return [ComplexType]
|
86
|
+
# @sg-ignore
|
84
87
|
def infer api_map, name_pin, locals
|
88
|
+
out = nil
|
89
|
+
cached = @@inference_cache[[node, node.location, links.map(&:word), name_pin&.return_type, locals]] unless node.nil?
|
90
|
+
return cached if cached && @@inference_invalidation_key == api_map.hash
|
91
|
+
out = infer_uncached api_map, name_pin, locals
|
92
|
+
if @@inference_invalidation_key != api_map.hash
|
93
|
+
@@inference_cache = {}
|
94
|
+
@@inference_invalidation_key = api_map.hash
|
95
|
+
end
|
96
|
+
@@inference_cache[[node, node.location, links.map(&:word), name_pin&.return_type, locals]] = out unless node.nil?
|
97
|
+
out
|
98
|
+
end
|
99
|
+
|
100
|
+
# @param api_map [ApiMap]
|
101
|
+
# @param name_pin [Pin::Base]
|
102
|
+
# @param locals [::Enumerable<Pin::LocalVariable>]
|
103
|
+
# @return [ComplexType]
|
104
|
+
def infer_uncached api_map, name_pin, locals
|
85
105
|
from_here = base.infer(api_map, name_pin, locals) unless links.length == 1
|
86
106
|
if from_here
|
87
107
|
name_pin = name_pin.proxy(from_here)
|
data/lib/solargraph/source.rb
CHANGED
@@ -92,50 +92,6 @@ module Solargraph
|
|
92
92
|
stack
|
93
93
|
end
|
94
94
|
|
95
|
-
# Start synchronizing the source. This method updates the code without
|
96
|
-
# parsing a new AST. The resulting Source object will be marked not
|
97
|
-
# synchronized (#synchronized? == false).
|
98
|
-
#
|
99
|
-
# @param updater [Source::Updater]
|
100
|
-
# @return [Source]
|
101
|
-
def start_synchronize updater
|
102
|
-
raise 'Invalid synchronization' unless updater.filename == filename
|
103
|
-
real_code = updater.write(@code)
|
104
|
-
src = Source.allocate
|
105
|
-
src.filename = filename
|
106
|
-
src.code = real_code
|
107
|
-
src.version = updater.version
|
108
|
-
src.parsed = parsed?
|
109
|
-
src.repaired = updater.repair(@repaired)
|
110
|
-
src.synchronized = false
|
111
|
-
src.node = @node
|
112
|
-
src.comments = @comments
|
113
|
-
src.error_ranges = error_ranges
|
114
|
-
src.last_updater = updater
|
115
|
-
return src.finish_synchronize unless real_code.lines.length == @code.lines.length
|
116
|
-
src
|
117
|
-
end
|
118
|
-
|
119
|
-
# Finish synchronizing a source that was updated via #start_synchronize.
|
120
|
-
# This method returns self if the source is already synchronized. Otherwise
|
121
|
-
# it parses the AST and returns a new synchronized Source.
|
122
|
-
#
|
123
|
-
# @return [Source]
|
124
|
-
def finish_synchronize
|
125
|
-
return self if synchronized?
|
126
|
-
synced = Source.new(@code, filename)
|
127
|
-
if synced.parsed?
|
128
|
-
synced.version = version
|
129
|
-
return synced
|
130
|
-
end
|
131
|
-
synced = Source.new(@repaired, filename)
|
132
|
-
synced.error_ranges.concat (error_ranges + last_updater.changes.map(&:range))
|
133
|
-
synced.code = @code
|
134
|
-
synced.synchronized = true
|
135
|
-
synced.version = version
|
136
|
-
synced
|
137
|
-
end
|
138
|
-
|
139
95
|
# Synchronize the Source with an update. This method applies changes to the
|
140
96
|
# code, parses the new code's AST, and returns the resulting Source object.
|
141
97
|
#
|
@@ -144,7 +144,7 @@ module Solargraph
|
|
144
144
|
)
|
145
145
|
end
|
146
146
|
if t.nil? || t.include?('w')
|
147
|
-
|
147
|
+
method_pin = Solargraph::Pin::Method.new(
|
148
148
|
location: location,
|
149
149
|
closure: namespace,
|
150
150
|
name: "#{directive.tag.name}=",
|
@@ -153,7 +153,8 @@ module Solargraph
|
|
153
153
|
visibility: :public,
|
154
154
|
attribute: true
|
155
155
|
)
|
156
|
-
pins.
|
156
|
+
pins.push method_pin
|
157
|
+
method_pin.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last)
|
157
158
|
if pins.last.return_type.defined?
|
158
159
|
pins.last.docstring.add_tag YARD::Tags::Tag.new(:param, '', pins.last.return_type.to_s.split(', '), 'value')
|
159
160
|
end
|
@@ -40,6 +40,16 @@ module Solargraph
|
|
40
40
|
@pin_select_cache[klass] ||= @pin_class_hash.select { |key, _| key <= klass }.values.flatten
|
41
41
|
end
|
42
42
|
|
43
|
+
# A hash representing the state of the source map's API.
|
44
|
+
#
|
45
|
+
# ApiMap#catalog uses this value to determine whether it needs to clear its
|
46
|
+
# cache.
|
47
|
+
#
|
48
|
+
# @return [Integer]
|
49
|
+
def api_hash
|
50
|
+
@api_hash ||= (pins_by_class(Pin::Constant) + pins_by_class(Pin::Namespace).select { |pin| pin.namespace.to_s > '' } + pins_by_class(Pin::Reference) + pins_by_class(Pin::Method).map(&:node)).hash
|
51
|
+
end
|
52
|
+
|
43
53
|
# @return [String]
|
44
54
|
def filename
|
45
55
|
source.filename
|