solargraph 0.54.4 → 0.56.2
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 +2 -0
- data/.github/workflows/typecheck.yml +3 -1
- data/.gitignore +2 -0
- data/CHANGELOG.md +62 -0
- data/README.md +13 -3
- data/lib/solargraph/api_map/index.rb +24 -16
- data/lib/solargraph/api_map/store.rb +48 -23
- data/lib/solargraph/api_map.rb +175 -77
- data/lib/solargraph/bench.rb +17 -1
- data/lib/solargraph/complex_type/type_methods.rb +6 -1
- data/lib/solargraph/complex_type/unique_type.rb +98 -9
- data/lib/solargraph/complex_type.rb +35 -6
- data/lib/solargraph/convention/base.rb +3 -3
- data/lib/solargraph/convention/data_definition/data_assignment_node.rb +60 -0
- data/lib/solargraph/convention/data_definition/data_definition_node.rb +89 -0
- data/lib/solargraph/convention/data_definition.rb +104 -0
- data/lib/solargraph/convention/gemspec.rb +2 -1
- data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +60 -0
- data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +100 -0
- data/lib/solargraph/convention/struct_definition.rb +141 -0
- data/lib/solargraph/convention.rb +5 -3
- data/lib/solargraph/doc_map.rb +277 -57
- data/lib/solargraph/gem_pins.rb +53 -37
- data/lib/solargraph/language_server/host/message_worker.rb +10 -7
- data/lib/solargraph/language_server/host.rb +12 -2
- data/lib/solargraph/language_server/message/extended/check_gem_version.rb +2 -0
- data/lib/solargraph/language_server/message/extended/document.rb +5 -2
- data/lib/solargraph/language_server/message/extended/document_gems.rb +3 -3
- data/lib/solargraph/library.rb +45 -17
- data/lib/solargraph/location.rb +21 -0
- data/lib/solargraph/logging.rb +1 -0
- data/lib/solargraph/parser/comment_ripper.rb +12 -6
- data/lib/solargraph/parser/flow_sensitive_typing.rb +227 -0
- data/lib/solargraph/parser/node_methods.rb +14 -0
- data/lib/solargraph/parser/node_processor/base.rb +9 -4
- data/lib/solargraph/parser/node_processor.rb +21 -8
- data/lib/solargraph/parser/parser_gem/class_methods.rb +16 -14
- data/lib/solargraph/parser/parser_gem/node_chainer.rb +10 -10
- data/lib/solargraph/parser/parser_gem/node_methods.rb +4 -2
- data/lib/solargraph/parser/parser_gem/node_processors/alias_node.rb +2 -1
- data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +21 -0
- data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +4 -2
- data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +4 -2
- data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +2 -1
- data/lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb +2 -1
- data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +6 -3
- data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +2 -1
- data/lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb +2 -1
- data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +21 -0
- data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +4 -2
- data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +2 -1
- data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +4 -1
- data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +8 -7
- data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +42 -0
- data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +1 -0
- data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +3 -1
- data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +4 -3
- data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +28 -16
- data/lib/solargraph/parser/parser_gem/node_processors/sym_node.rb +3 -1
- data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -0
- data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +29 -0
- data/lib/solargraph/parser/parser_gem/node_processors.rb +14 -0
- data/lib/solargraph/parser/region.rb +1 -1
- data/lib/solargraph/parser.rb +1 -0
- data/lib/solargraph/pin/base.rb +316 -28
- data/lib/solargraph/pin/base_variable.rb +16 -9
- data/lib/solargraph/pin/block.rb +2 -0
- data/lib/solargraph/pin/breakable.rb +9 -0
- data/lib/solargraph/pin/callable.rb +74 -3
- data/lib/solargraph/pin/closure.rb +18 -1
- data/lib/solargraph/pin/common.rb +5 -0
- data/lib/solargraph/pin/delegated_method.rb +20 -1
- data/lib/solargraph/pin/documenting.rb +16 -0
- data/lib/solargraph/pin/keyword.rb +7 -2
- data/lib/solargraph/pin/local_variable.rb +15 -6
- data/lib/solargraph/pin/method.rb +169 -43
- data/lib/solargraph/pin/namespace.rb +17 -9
- data/lib/solargraph/pin/parameter.rb +60 -11
- data/lib/solargraph/pin/proxy_type.rb +12 -6
- data/lib/solargraph/pin/reference/override.rb +10 -6
- data/lib/solargraph/pin/reference/require.rb +2 -2
- data/lib/solargraph/pin/signature.rb +42 -0
- data/lib/solargraph/pin/singleton.rb +1 -1
- data/lib/solargraph/pin/symbol.rb +3 -2
- data/lib/solargraph/pin/until.rb +18 -0
- data/lib/solargraph/pin/while.rb +18 -0
- data/lib/solargraph/pin.rb +4 -1
- data/lib/solargraph/pin_cache.rb +185 -0
- data/lib/solargraph/position.rb +9 -0
- data/lib/solargraph/range.rb +9 -0
- data/lib/solargraph/rbs_map/conversions.rb +221 -67
- data/lib/solargraph/rbs_map/core_fills.rb +32 -16
- data/lib/solargraph/rbs_map/core_map.rb +34 -11
- data/lib/solargraph/rbs_map/stdlib_map.rb +15 -5
- data/lib/solargraph/rbs_map.rb +74 -17
- data/lib/solargraph/shell.rb +17 -18
- data/lib/solargraph/source/chain/array.rb +11 -7
- data/lib/solargraph/source/chain/block_symbol.rb +1 -1
- data/lib/solargraph/source/chain/block_variable.rb +1 -1
- data/lib/solargraph/source/chain/call.rb +53 -23
- data/lib/solargraph/source/chain/constant.rb +1 -1
- data/lib/solargraph/source/chain/hash.rb +4 -3
- data/lib/solargraph/source/chain/head.rb +1 -1
- data/lib/solargraph/source/chain/if.rb +1 -1
- data/lib/solargraph/source/chain/link.rb +2 -0
- data/lib/solargraph/source/chain/literal.rb +22 -2
- data/lib/solargraph/source/chain/or.rb +1 -1
- data/lib/solargraph/source/chain/z_super.rb +1 -1
- data/lib/solargraph/source/chain.rb +78 -48
- data/lib/solargraph/source/source_chainer.rb +2 -2
- data/lib/solargraph/source_map/clip.rb +3 -1
- data/lib/solargraph/source_map/mapper.rb +9 -5
- data/lib/solargraph/source_map.rb +0 -17
- data/lib/solargraph/type_checker/checks.rb +4 -0
- data/lib/solargraph/type_checker.rb +35 -8
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/views/_method.erb +10 -10
- data/lib/solargraph/views/_namespace.erb +3 -3
- data/lib/solargraph/views/document.erb +10 -10
- data/lib/solargraph/workspace/config.rb +1 -1
- data/lib/solargraph/workspace.rb +23 -5
- data/lib/solargraph/yard_map/helpers.rb +29 -1
- data/lib/solargraph/yard_map/mapper/to_constant.rb +7 -5
- data/lib/solargraph/yard_map/mapper/to_method.rb +53 -18
- data/lib/solargraph/yard_map/mapper/to_namespace.rb +9 -7
- data/lib/solargraph/yard_map/mapper.rb +4 -3
- data/lib/solargraph/yard_map/to_method.rb +4 -2
- data/lib/solargraph/yardoc.rb +7 -8
- data/lib/solargraph.rb +32 -1
- data/rbs/fills/tuple.rbs +150 -0
- data/rbs_collection.yaml +19 -0
- data/solargraph.gemspec +2 -1
- metadata +37 -9
- data/lib/solargraph/cache.rb +0 -77
@@ -15,7 +15,7 @@ module Solargraph
|
|
15
15
|
# @param with_block [Boolean] True if the chain is inside a block
|
16
16
|
# @param head [Boolean] True if the call is the start of its chain
|
17
17
|
def initialize word, with_block = false
|
18
|
-
super(word, [], with_block)
|
18
|
+
super(word, nil, [], with_block)
|
19
19
|
end
|
20
20
|
|
21
21
|
# @param api_map [ApiMap]
|
@@ -39,7 +39,7 @@ module Solargraph
|
|
39
39
|
@@inference_invalidation_key = nil
|
40
40
|
@@inference_cache = {}
|
41
41
|
|
42
|
-
UNDEFINED_CALL = Chain::Call.new('<undefined>')
|
42
|
+
UNDEFINED_CALL = Chain::Call.new('<undefined>', nil)
|
43
43
|
UNDEFINED_CONSTANT = Chain::Constant.new('<undefined>')
|
44
44
|
|
45
45
|
# @return [::Array<Source::Chain::Link>]
|
@@ -75,16 +75,27 @@ module Solargraph
|
|
75
75
|
|
76
76
|
# Determine potential Pins returned by this chain of words
|
77
77
|
#
|
78
|
-
# @param api_map [ApiMap]
|
79
|
-
#
|
80
|
-
#
|
81
|
-
#
|
78
|
+
# @param api_map [ApiMap] @param name_pin [Pin::Base] A pin
|
79
|
+
# representing the place in which expression is evaluated (e.g.,
|
80
|
+
# a Method pin, or a Module or Class pin if not run within a
|
81
|
+
# method - both in terms of the closure around the chain, as well
|
82
|
+
# as the self type used for any method calls in head position.
|
83
|
+
#
|
84
|
+
# Requirements for name_pin:
|
85
|
+
#
|
86
|
+
# * name_pin.context: This should be a type representing the
|
87
|
+
# namespace where we can look up non-local variables and
|
88
|
+
# method names. If it is a Class<X>, we will look up
|
89
|
+
# :class scoped methods/variables.
|
82
90
|
#
|
83
|
-
#
|
84
|
-
#
|
85
|
-
#
|
91
|
+
# * name_pin.binder: Used for method call lookups only
|
92
|
+
# (Chain::Call links). For method calls as the first
|
93
|
+
# element in the chain, 'name_pin.binder' should be the
|
94
|
+
# same as name_pin.context above. For method calls later
|
95
|
+
# in the chain (e.g., 'b' in a.b.c), it should represent
|
96
|
+
# 'a'.
|
86
97
|
#
|
87
|
-
# @param locals [::
|
98
|
+
# @param locals [::Array<Pin::LocalVariable>] Any local
|
88
99
|
# variables / method parameters etc visible by the statement
|
89
100
|
#
|
90
101
|
# @return [::Array<Pin::Base>] Pins representing possible return
|
@@ -101,16 +112,25 @@ module Solargraph
|
|
101
112
|
working_pin = name_pin
|
102
113
|
links[0..-2].each do |link|
|
103
114
|
pins = link.resolve(api_map, working_pin, locals)
|
104
|
-
type =
|
105
|
-
|
106
|
-
|
115
|
+
type = infer_from_definitions(pins, working_pin, api_map, locals)
|
116
|
+
if type.undefined?
|
117
|
+
logger.debug { "Chain#define(links=#{links.map(&:desc)}, name_pin=#{name_pin.inspect}, locals=#{locals}) => [] - undefined type from #{link.desc}" }
|
118
|
+
return []
|
119
|
+
end
|
120
|
+
# We continue to use the context from the head pin, in case
|
121
|
+
# we need it to, for instance, provide context for a block
|
122
|
+
# evaluation. However, we use the last link's return type
|
123
|
+
# for the binder, as this is chaining off of it, and the
|
124
|
+
# binder is now the lhs of the rhs we are evaluating.
|
125
|
+
working_pin = Pin::ProxyType.anonymous(name_pin.context, binder: type, closure: name_pin, source: :chain)
|
126
|
+
logger.debug { "Chain#define(links=#{links.map(&:desc)}, name_pin=#{name_pin.inspect}, locals=#{locals}) - after processing #{link.desc}, new working_pin=#{working_pin} with binder #{working_pin.binder}" }
|
107
127
|
end
|
108
128
|
links.last.last_context = working_pin
|
109
129
|
links.last.resolve(api_map, working_pin, locals)
|
110
130
|
end
|
111
131
|
|
112
132
|
# @param api_map [ApiMap]
|
113
|
-
# @param name_pin [Pin::Base]
|
133
|
+
# @param name_pin [Pin::Base] The pin for the closure in which this code runs
|
114
134
|
# @param locals [::Array<Pin::LocalVariable>]
|
115
135
|
# @return [ComplexType]
|
116
136
|
# @sg-ignore
|
@@ -123,7 +143,7 @@ module Solargraph
|
|
123
143
|
@@inference_invalidation_key = api_map.hash
|
124
144
|
@@inference_cache = {}
|
125
145
|
end
|
126
|
-
out = infer_uncached
|
146
|
+
out = infer_uncached(api_map, name_pin, locals).downcast_to_literal_if_possible
|
127
147
|
logger.debug { "Chain#infer() - caching result - cache_key_hash=#{cache_key.hash}, links.map(&:hash)=#{links.map(&:hash)}, links=#{links}, cache_key.map(&:hash) = #{cache_key.map(&:hash)}, cache_key=#{cache_key}" }
|
128
148
|
@@inference_cache[cache_key] = out
|
129
149
|
end
|
@@ -134,8 +154,14 @@ module Solargraph
|
|
134
154
|
# @return [ComplexType]
|
135
155
|
def infer_uncached api_map, name_pin, locals
|
136
156
|
pins = define(api_map, name_pin, locals)
|
137
|
-
|
138
|
-
|
157
|
+
if pins.empty?
|
158
|
+
logger.debug { "Chain#infer_uncached(links=#{links.map(&:desc)}, locals=#{locals.map(&:desc)}) => undefined - no pins" }
|
159
|
+
return ComplexType::UNDEFINED
|
160
|
+
end
|
161
|
+
type = infer_from_definitions(pins, links.last.last_context, api_map, locals)
|
162
|
+
out = maybe_nil(type)
|
163
|
+
logger.debug { "Chain#infer_uncached(links=#{self.links.map(&:desc)}, locals=#{locals.map(&:desc)}, name_pin=#{name_pin}, name_pin.closure=#{name_pin.closure.inspect}, name_pin.binder=#{name_pin.binder}) => #{out.rooted_tags.inspect}" }
|
164
|
+
out
|
139
165
|
end
|
140
166
|
|
141
167
|
# @return [Boolean]
|
@@ -174,6 +200,8 @@ module Solargraph
|
|
174
200
|
desc
|
175
201
|
end
|
176
202
|
|
203
|
+
include Logging
|
204
|
+
|
177
205
|
private
|
178
206
|
|
179
207
|
# @param pins [::Array<Pin::Base>]
|
@@ -181,8 +209,10 @@ module Solargraph
|
|
181
209
|
# @param api_map [ApiMap]
|
182
210
|
# @param locals [::Enumerable<Pin::LocalVariable>]
|
183
211
|
# @return [ComplexType]
|
184
|
-
def
|
185
|
-
|
212
|
+
def infer_from_definitions pins, context, api_map, locals
|
213
|
+
# @type [::Array<ComplexType>]
|
214
|
+
types = []
|
215
|
+
unresolved_pins = []
|
186
216
|
# @todo this param tag shouldn't be needed to probe the type
|
187
217
|
# @todo ...but given it is needed, typecheck should complain that it is needed
|
188
218
|
# @param pin [Pin::Base]
|
@@ -200,41 +230,41 @@ module Solargraph
|
|
200
230
|
# that accepts only [Pin::Namespace] as an argument
|
201
231
|
type = type.resolve_generics(pin.closure, context.binder)
|
202
232
|
end
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
end
|
233
|
+
types << type
|
234
|
+
else
|
235
|
+
unresolved_pins << pin
|
207
236
|
end
|
208
237
|
end
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
@@inference_depth += 1
|
214
|
-
# @param pin [Pin::Base]
|
215
|
-
pins.each do |pin|
|
216
|
-
# Avoid infinite recursion
|
217
|
-
next if @@inference_stack.include?(pin)
|
218
|
-
|
219
|
-
@@inference_stack.push pin
|
220
|
-
type = pin.probe(api_map)
|
221
|
-
@@inference_stack.pop
|
222
|
-
if type.defined?
|
223
|
-
possibles.push type
|
224
|
-
break if pin.is_a?(Pin::Method)
|
225
|
-
end
|
226
|
-
end
|
227
|
-
@@inference_depth -= 1
|
238
|
+
|
239
|
+
# Limit method inference recursion
|
240
|
+
if @@inference_depth >= 10 && pins.first.is_a?(Pin::Method)
|
241
|
+
return ComplexType::UNDEFINED
|
228
242
|
end
|
229
|
-
return ComplexType::UNDEFINED if possibles.empty?
|
230
243
|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
244
|
+
@@inference_depth += 1
|
245
|
+
# @param pin [Pin::Base]
|
246
|
+
unresolved_pins.each do |pin|
|
247
|
+
# Avoid infinite recursion
|
248
|
+
if @@inference_stack.include?(pin.identity)
|
249
|
+
next
|
250
|
+
end
|
251
|
+
|
252
|
+
@@inference_stack.push(pin.identity)
|
253
|
+
type = pin.probe(api_map)
|
254
|
+
@@inference_stack.pop
|
255
|
+
types.push type if type
|
237
256
|
end
|
257
|
+
@@inference_depth -= 1
|
258
|
+
|
259
|
+
type = if types.empty?
|
260
|
+
ComplexType::UNDEFINED
|
261
|
+
elsif types.length > 1
|
262
|
+
# Move nil to the end by convention
|
263
|
+
sorted = types.flat_map(&:items).sort { |a, _| a.tag == 'nil' ? 1 : 0 }
|
264
|
+
ComplexType.new(sorted.uniq)
|
265
|
+
else
|
266
|
+
ComplexType.new(types)
|
267
|
+
end
|
238
268
|
return type if context.nil? || context.return_type.undefined?
|
239
269
|
|
240
270
|
type.self_to_type(context.return_type)
|
@@ -32,8 +32,8 @@ module Solargraph
|
|
32
32
|
# @return [Source::Chain]
|
33
33
|
def chain
|
34
34
|
# Special handling for files that end with an integer and a period
|
35
|
-
return Chain.new([Chain::Literal.new('Integer'), Chain::UNDEFINED_CALL]) if phrase =~ /^[0-9]+\.$/
|
36
|
-
return Chain.new([Chain::Literal.new('Symbol')]) if phrase.start_with?(':') && !phrase.start_with?('::')
|
35
|
+
return Chain.new([Chain::Literal.new('Integer', Integer(phrase[0..-2])), Chain::UNDEFINED_CALL]) if phrase =~ /^[0-9]+\.$/
|
36
|
+
return Chain.new([Chain::Literal.new('Symbol', phrase[1..].to_sym)]) if phrase.start_with?(':') && !phrase.start_with?('::')
|
37
37
|
return SourceChainer.chain(source, Position.new(position.line, position.character + 1)) if end_of_phrase.strip == '::' && source.code[Position.to_offset(source.code, position)].to_s.match?(/[a-z]/i)
|
38
38
|
begin
|
39
39
|
return Chain.new([]) if phrase.end_with?('..')
|
@@ -11,7 +11,9 @@ module Solargraph
|
|
11
11
|
def initialize api_map, cursor
|
12
12
|
@api_map = api_map
|
13
13
|
@cursor = cursor
|
14
|
-
|
14
|
+
block_pin = block
|
15
|
+
block_pin.rebind(api_map) if block_pin.is_a?(Pin::Block) && !Solargraph::Range.from_node(block_pin.receiver).contain?(cursor.range.start)
|
16
|
+
@in_block = nil
|
15
17
|
end
|
16
18
|
|
17
19
|
# @return [Array<Pin::Base>] Relevant pins for infering the type of the Cursor's position
|
@@ -41,7 +41,7 @@ module Solargraph
|
|
41
41
|
s = Position.new(0, 0)
|
42
42
|
e = Position.from_offset(code, code.length)
|
43
43
|
location = Location.new(filename, Range.new(s, e))
|
44
|
-
[[Pin::Namespace.new(location: location, name: '')], []]
|
44
|
+
[[Pin::Namespace.new(location: location, name: '', source: :source_map)], []]
|
45
45
|
end
|
46
46
|
|
47
47
|
class << self
|
@@ -55,6 +55,7 @@ module Solargraph
|
|
55
55
|
|
56
56
|
# @return [Array<Solargraph::Pin::Base>]
|
57
57
|
def pins
|
58
|
+
# @type [Array<Solargraph::Pin::Base>]
|
58
59
|
@pins ||= []
|
59
60
|
end
|
60
61
|
|
@@ -140,7 +141,8 @@ module Solargraph
|
|
140
141
|
scope: namespace.is_a?(Pin::Singleton) ? :class : :instance,
|
141
142
|
visibility: :public,
|
142
143
|
explicit: false,
|
143
|
-
attribute: true
|
144
|
+
attribute: true,
|
145
|
+
source: :source_map
|
144
146
|
)
|
145
147
|
end
|
146
148
|
if t.nil? || t.include?('w')
|
@@ -151,10 +153,11 @@ module Solargraph
|
|
151
153
|
comments: docstring.all.to_s,
|
152
154
|
scope: namespace.is_a?(Pin::Singleton) ? :class : :instance,
|
153
155
|
visibility: :public,
|
154
|
-
attribute: true
|
156
|
+
attribute: true,
|
157
|
+
source: :source_map
|
155
158
|
)
|
156
159
|
pins.push method_pin
|
157
|
-
method_pin.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last)
|
160
|
+
method_pin.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last, source: :source_map)
|
158
161
|
if pins.last.return_type.defined?
|
159
162
|
pins.last.docstring.add_tag YARD::Tags::Tag.new(:param, '', pins.last.return_type.to_s.split(', '), 'value')
|
160
163
|
end
|
@@ -205,7 +208,8 @@ module Solargraph
|
|
205
208
|
namespace = closure_at(source_position) || Pin::ROOT_PIN
|
206
209
|
namespace.domains.concat directive.tag.types unless directive.tag.types.nil?
|
207
210
|
when 'override'
|
208
|
-
pins.push Pin::Reference::Override.new(location, directive.tag.name, docstring.tags
|
211
|
+
pins.push Pin::Reference::Override.new(location, directive.tag.name, docstring.tags,
|
212
|
+
source: :source_map)
|
209
213
|
when 'macro'
|
210
214
|
# @todo Handle macros
|
211
215
|
end
|
@@ -118,23 +118,6 @@ module Solargraph
|
|
118
118
|
_locate_pin line, character, Pin::Namespace, Pin::Method, Pin::Block
|
119
119
|
end
|
120
120
|
|
121
|
-
# @todo Candidate for deprecation
|
122
|
-
#
|
123
|
-
# @param other_map [SourceMap]
|
124
|
-
# @return [Boolean]
|
125
|
-
def try_merge! other_map
|
126
|
-
return false if pins.length != other_map.pins.length || locals.length != other_map.locals.length || requires.map(&:name).uniq.sort != other_map.requires.map(&:name).uniq.sort
|
127
|
-
|
128
|
-
pins.each_index do |i|
|
129
|
-
return false unless pins[i].try_merge!(other_map.pins[i])
|
130
|
-
end
|
131
|
-
locals.each_index do |i|
|
132
|
-
return false unless locals[i].try_merge!(other_map.locals[i])
|
133
|
-
end
|
134
|
-
@source = other_map.source
|
135
|
-
true
|
136
|
-
end
|
137
|
-
|
138
121
|
# @param name [String]
|
139
122
|
# @return [Array<Location>]
|
140
123
|
def references name
|
@@ -50,6 +50,8 @@ module Solargraph
|
|
50
50
|
# @param inferred [ComplexType]
|
51
51
|
# @return [Boolean]
|
52
52
|
def any_types_match? api_map, expected, inferred
|
53
|
+
expected = expected.downcast_to_literal_if_possible
|
54
|
+
inferred = inferred.downcast_to_literal_if_possible
|
53
55
|
return duck_types_match?(api_map, expected, inferred) if expected.duck_type?
|
54
56
|
# walk through the union expected type and see if any members
|
55
57
|
# of the union match the inferred type
|
@@ -71,6 +73,8 @@ module Solargraph
|
|
71
73
|
# @param expected [ComplexType]
|
72
74
|
# @return [Boolean]
|
73
75
|
def all_types_match? api_map, inferred, expected
|
76
|
+
expected = expected.downcast_to_literal_if_possible
|
77
|
+
inferred = inferred.downcast_to_literal_if_possible
|
74
78
|
return duck_types_match?(api_map, expected, inferred) if expected.duck_type?
|
75
79
|
inferred.each do |inf|
|
76
80
|
next if inf.duck_type?
|
@@ -470,12 +470,22 @@ module Solargraph
|
|
470
470
|
# @param pin [Pin::Method]
|
471
471
|
# @return [Hash{String => Hash{Symbol => String, ComplexType}}]
|
472
472
|
def param_hash(pin)
|
473
|
-
tags = pin.docstring.tags(:param)
|
474
|
-
return {} if tags.empty?
|
475
473
|
# @type [Hash{String => Hash{Symbol => String, ComplexType}}]
|
476
474
|
result = {}
|
475
|
+
pin.parameters.each do |param|
|
476
|
+
type = param.typify(api_map)
|
477
|
+
next if type.nil? || type.undefined?
|
478
|
+
result[param.name.to_s] = {
|
479
|
+
tagged: type.tags,
|
480
|
+
qualified: type
|
481
|
+
}
|
482
|
+
end
|
483
|
+
# see if we have additional tags to pay attention to from YARD -
|
484
|
+
# e.g., kwargs in a **restkwargs splat
|
485
|
+
tags = pin.docstring.tags(:param)
|
477
486
|
tags.each do |tag|
|
478
|
-
next if
|
487
|
+
next if result.key? tag.name.to_s
|
488
|
+
next if tag.types.nil?
|
479
489
|
result[tag.name.to_s] = {
|
480
490
|
tagged: tag.types.join(', '),
|
481
491
|
qualified: Solargraph::ComplexType.try_parse(*tag.types).qualify(api_map, pin.full_context.namespace)
|
@@ -487,11 +497,27 @@ module Solargraph
|
|
487
497
|
# @param pins [Array<Pin::Method>]
|
488
498
|
# @return [Hash{String => Hash{Symbol => String, ComplexType}}]
|
489
499
|
def first_param_hash(pins)
|
490
|
-
pins.
|
491
|
-
|
492
|
-
|
500
|
+
return {} if pins.empty?
|
501
|
+
first_pin_type = pins.first.typify(api_map)
|
502
|
+
first_pin = pins.first.proxy first_pin_type
|
503
|
+
param_names = first_pin.parameter_names
|
504
|
+
results = param_hash(first_pin)
|
505
|
+
pins[1..].each do |pin|
|
506
|
+
# @todo this assignment from parametric use of Hash should not lose its generic
|
507
|
+
# @type [Hash{String => Hash{Symbol => BasicObject}}]
|
508
|
+
|
509
|
+
# documentation of types in superclasses should fail back to
|
510
|
+
# subclasses if the subclass hasn't documented something
|
511
|
+
superclass_results = param_hash(pin)
|
512
|
+
superclass_results.each do |param_name, details|
|
513
|
+
next unless param_names.include?(param_name)
|
514
|
+
|
515
|
+
results[param_name] ||= {}
|
516
|
+
results[param_name][:tagged] ||= details[:tagged]
|
517
|
+
results[param_name][:qualified] ||= details[:qualified]
|
518
|
+
end
|
493
519
|
end
|
494
|
-
|
520
|
+
results
|
495
521
|
end
|
496
522
|
|
497
523
|
# @param pin [Pin::Base]
|
@@ -584,7 +610,8 @@ module Solargraph
|
|
584
610
|
kwargs.delete param.name.to_sym
|
585
611
|
settled_kwargs += 1
|
586
612
|
elsif param.decl == :kwarg
|
587
|
-
|
613
|
+
last_arg_last_link = arguments.last.links.last
|
614
|
+
return [] if last_arg_last_link.is_a?(Solargraph::Source::Chain::Hash) && last_arg_last_link.splatted?
|
588
615
|
return [Problem.new(location, "Missing keyword argument #{param.name} to #{pin.path}")]
|
589
616
|
end
|
590
617
|
end
|
data/lib/solargraph/version.rb
CHANGED
@@ -2,33 +2,33 @@
|
|
2
2
|
Namespace:
|
3
3
|
</h2>
|
4
4
|
<p>
|
5
|
-
<a href="solargraph:/document?query=<%= CGI.escape
|
5
|
+
<a href="solargraph:/document?query=<%= CGI.escape pin.namespace.path %>"><%= pin.namespace %></a>
|
6
6
|
</p>
|
7
7
|
<h2>
|
8
8
|
Overview:
|
9
9
|
</h2>
|
10
|
-
<%= htmlify
|
10
|
+
<%= htmlify pin.docstring %>
|
11
11
|
<p class="document-section">
|
12
|
-
<big><strong>Visibility:</strong></big> <%=
|
12
|
+
<big><strong>Visibility:</strong></big> <%= pin.visibility %>
|
13
13
|
</p>
|
14
|
-
<% unless
|
14
|
+
<% unless pin.docstring.tags(:param).empty? %>
|
15
15
|
<h2>
|
16
16
|
Parameters:
|
17
17
|
</h2>
|
18
18
|
<ul>
|
19
|
-
<%
|
19
|
+
<% pin.docstring.tags(:param).each do |tag| %>
|
20
20
|
<li>
|
21
21
|
<%= erb :_name_type_tag, layout: false, locals: {tag: tag} %>
|
22
22
|
</li>
|
23
23
|
<% end %>
|
24
24
|
</ul>
|
25
25
|
<% end %>
|
26
|
-
<% unless
|
26
|
+
<% unless pin.docstring.tags(:raise).empty? %>
|
27
27
|
<h2>
|
28
28
|
Raises:
|
29
29
|
</h2>
|
30
30
|
<ul>
|
31
|
-
<%
|
31
|
+
<% pin.docstring.tags(:raise).each do |tag| %>
|
32
32
|
<li>
|
33
33
|
<%= erb :_name_type_tag, layout: false, locals: {tag: tag} %>
|
34
34
|
</li>
|
@@ -38,20 +38,20 @@
|
|
38
38
|
<h2>
|
39
39
|
Returns:
|
40
40
|
</h2>
|
41
|
-
<% if
|
41
|
+
<% if pin.docstring.tag(:return).nil? %>
|
42
42
|
<p>
|
43
43
|
Undefined/unknown
|
44
44
|
</p>
|
45
45
|
<% else %>
|
46
46
|
<ul>
|
47
|
-
<%
|
47
|
+
<% pin.tags(:return).each do |tag| %>
|
48
48
|
<li>
|
49
49
|
<%= erb :_name_type_tag, layout: false, locals: {tag: tag} %>
|
50
50
|
</li>
|
51
51
|
<% end %>
|
52
52
|
</ul>
|
53
53
|
<% end %>
|
54
|
-
<% examples =
|
54
|
+
<% examples = pin.docstring.tags(:example) %>
|
55
55
|
<% unless examples.nil? %>
|
56
56
|
<% examples.each do |example| %>
|
57
57
|
<h2>
|
@@ -1,12 +1,12 @@
|
|
1
1
|
<h2>
|
2
2
|
Overview:
|
3
3
|
</h2>
|
4
|
-
<%= htmlify
|
4
|
+
<%= htmlify pin.docstring %>
|
5
5
|
<h2>
|
6
6
|
Class Methods
|
7
7
|
</h2>
|
8
8
|
<ul class="doc-list">
|
9
|
-
<%
|
9
|
+
<% api_map.get_methods(pin.path, scope: :class, deep: false).sort{|a, b| a.name <=> b.name}.each do |meth| %>
|
10
10
|
<li>
|
11
11
|
<a href="solargraph:/document?query=<%= CGI.escape(meth.path) %>"><%= meth.name %></a>
|
12
12
|
</li>
|
@@ -16,7 +16,7 @@
|
|
16
16
|
Instance Methods
|
17
17
|
</h2>
|
18
18
|
<ul class="doc-list">
|
19
|
-
<%
|
19
|
+
<% api_map.get_methods(pin.path, scope: :instance, deep: false).sort{|a, b| a.name <=> b.name}.each do |meth| %>
|
20
20
|
<li>
|
21
21
|
<a href="solargraph:/document?query=<%= CGI.escape(meth.path) %>"><%= meth.name %></a>
|
22
22
|
</li>
|
@@ -1,23 +1,23 @@
|
|
1
|
-
<%
|
1
|
+
<% pins.each do |pin| %>
|
2
2
|
<h1>
|
3
|
-
<%=
|
4
|
-
<% if
|
5
|
-
<small>(<%=
|
3
|
+
<%= pin.name %>
|
4
|
+
<% if pin.is_a?(Solargraph::Pin::Method) && !pin.parameters.empty? %>
|
5
|
+
<small>(<%= pin.parameters.map {|p| "#{p[0]}#{p[1] and p[0].end_with?(':') ? ' ' : (p[1] ? ' = ' : '')}#{p[1]}"}.join(', ') %>)</small>
|
6
6
|
<% end %>
|
7
7
|
</h1>
|
8
|
-
<% unless
|
8
|
+
<% unless pins.map(&:location).compact.empty? %>
|
9
9
|
<h2>
|
10
10
|
Defined in:
|
11
11
|
</h2>
|
12
12
|
<ul>
|
13
|
-
<%
|
13
|
+
<% pins.map(&:location).compact.map(&:filename).each do |f| %>
|
14
14
|
<li><%= f %></li>
|
15
15
|
<% end %>
|
16
16
|
</ul>
|
17
17
|
<% end %>
|
18
|
-
<% if
|
19
|
-
<%= erb :_namespace, layout: false, locals: {
|
20
|
-
<% elsif
|
21
|
-
<%= erb :_method, layout: false, locals: {
|
18
|
+
<% if pin.is_a?(Solargraph::Pin::Namespace) %>
|
19
|
+
<%= erb :_namespace, layout: false, locals: {api_map: api_map, pin: pin} %>
|
20
|
+
<% elsif pin.is_a?(Solargraph::Pin::Method) %>
|
21
|
+
<%= erb :_method, layout: false, locals: {api_map: api_map, pin: pin} %>
|
22
22
|
<% end %>
|
23
23
|
<% end %>
|
@@ -151,7 +151,7 @@ module Solargraph
|
|
151
151
|
# @return [Hash{String => Array, Hash, Integer}]
|
152
152
|
def default_config
|
153
153
|
{
|
154
|
-
'include' => ['**/*.rb'],
|
154
|
+
'include' => ['Rakefile', 'Gemfile', '*.gemspec', '**/*.rb'],
|
155
155
|
'exclude' => ['spec/**/*', 'test/**/*', 'vendor/**/*', '.bundle/**/*'],
|
156
156
|
'require' => [],
|
157
157
|
'domains' => [],
|
data/lib/solargraph/workspace.rb
CHANGED
@@ -106,7 +106,7 @@ module Solargraph
|
|
106
106
|
def would_require? path
|
107
107
|
require_paths.each do |rp|
|
108
108
|
full = File.join rp, path
|
109
|
-
return true if File.
|
109
|
+
return true if File.file?(full) || File.file?(full << ".rb")
|
110
110
|
end
|
111
111
|
false
|
112
112
|
end
|
@@ -133,6 +133,15 @@ module Solargraph
|
|
133
133
|
@gem_rbs_collection ||= read_rbs_collection_path
|
134
134
|
end
|
135
135
|
|
136
|
+
def rbs_collection_config_path
|
137
|
+
@rbs_collection_config_path ||= begin
|
138
|
+
unless directory.empty? || directory == '*'
|
139
|
+
yaml_file = File.join(directory, 'rbs_collection.yaml')
|
140
|
+
yaml_file if File.file?(yaml_file)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
136
145
|
# Synchronize the workspace from the provided updater.
|
137
146
|
#
|
138
147
|
# @param updater [Source::Updater]
|
@@ -146,6 +155,14 @@ module Solargraph
|
|
146
155
|
server['commandPath'] || 'solargraph'
|
147
156
|
end
|
148
157
|
|
158
|
+
# True if the workspace has a root Gemfile.
|
159
|
+
#
|
160
|
+
# @todo Handle projects with custom Bundler/Gemfile setups (see DocMap#gemspecs_required_from_bundler)
|
161
|
+
#
|
162
|
+
def gemfile?
|
163
|
+
directory && File.file?(File.join(directory, 'Gemfile'))
|
164
|
+
end
|
165
|
+
|
149
166
|
private
|
150
167
|
|
151
168
|
# The language server configuration (or an empty hash if the workspace was
|
@@ -214,7 +231,7 @@ module Solargraph
|
|
214
231
|
def configured_require_paths
|
215
232
|
return ['lib'] if directory.empty?
|
216
233
|
return [File.join(directory, 'lib')] if config.require_paths.empty?
|
217
|
-
config.require_paths.map{|p| File.join(directory, p)}
|
234
|
+
config.require_paths.map { |p| File.join(directory, p) }
|
218
235
|
end
|
219
236
|
|
220
237
|
# @return [void]
|
@@ -230,10 +247,11 @@ module Solargraph
|
|
230
247
|
|
231
248
|
# @return [String, nil]
|
232
249
|
def read_rbs_collection_path
|
233
|
-
|
234
|
-
return unless File.file?(yaml_file)
|
250
|
+
return unless rbs_collection_config_path
|
235
251
|
|
236
|
-
YAML.load_file(
|
252
|
+
path = YAML.load_file(rbs_collection_config_path)&.fetch('path')
|
253
|
+
# make fully qualified
|
254
|
+
File.expand_path(path, directory)
|
237
255
|
end
|
238
256
|
end
|
239
257
|
end
|
@@ -7,10 +7,38 @@ module Solargraph
|
|
7
7
|
# @param spec [Gem::Specification, nil]
|
8
8
|
# @return [Solargraph::Location, nil]
|
9
9
|
def object_location code_object, spec
|
10
|
-
|
10
|
+
if spec.nil? || code_object.nil? || code_object.file.nil? || code_object.line.nil?
|
11
|
+
if code_object.namespace.is_a?(YARD::CodeObjects::NamespaceObject)
|
12
|
+
# If the code object is a namespace, use the namespace's location
|
13
|
+
return object_location(code_object.namespace, spec)
|
14
|
+
end
|
15
|
+
return Solargraph::Location.new(__FILE__, Solargraph::Range.from_to(__LINE__ - 1, 0, __LINE__ - 1, 0))
|
16
|
+
end
|
11
17
|
file = File.join(spec.full_gem_path, code_object.file)
|
12
18
|
Solargraph::Location.new(file, Solargraph::Range.from_to(code_object.line - 1, 0, code_object.line - 1, 0))
|
13
19
|
end
|
20
|
+
|
21
|
+
# @param code_object [YARD::CodeObjects::Base]
|
22
|
+
# @param spec [Gem::Specification, nil]
|
23
|
+
# @return [Solargraph::Pin::Namespace]
|
24
|
+
def create_closure_namespace_for(code_object, spec)
|
25
|
+
code_object_for_location = code_object
|
26
|
+
# code_object.namespace is sometimes a YARD proxy object pointing to a method path ("Object#new")
|
27
|
+
code_object_for_location = code_object.namespace if code_object.namespace.is_a?(YARD::CodeObjects::NamespaceObject)
|
28
|
+
namespace_location = object_location(code_object_for_location, spec)
|
29
|
+
ns_name = code_object.namespace.to_s
|
30
|
+
if ns_name.empty?
|
31
|
+
Solargraph::Pin::ROOT_PIN
|
32
|
+
else
|
33
|
+
Solargraph::Pin::Namespace.new(
|
34
|
+
name: ns_name,
|
35
|
+
closure: Pin::ROOT_PIN,
|
36
|
+
gates: [code_object.namespace.to_s],
|
37
|
+
source: :yardoc,
|
38
|
+
location: namespace_location
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
14
42
|
end
|
15
43
|
end
|
16
44
|
end
|
@@ -7,17 +7,19 @@ module Solargraph
|
|
7
7
|
extend YardMap::Helpers
|
8
8
|
|
9
9
|
# @param code_object [YARD::CodeObjects::Base]
|
10
|
+
# @param closure [Pin::Closure, nil]
|
11
|
+
# @param spec [Gem::Specification, nil]
|
12
|
+
# @return [Pin::Constant]
|
10
13
|
def self.make code_object, closure = nil, spec = nil
|
11
|
-
closure ||=
|
12
|
-
|
13
|
-
gates: [code_object.namespace.to_s]
|
14
|
-
)
|
14
|
+
closure ||= create_closure_namespace_for(code_object, spec)
|
15
|
+
|
15
16
|
Pin::Constant.new(
|
16
17
|
location: object_location(code_object, spec),
|
17
18
|
closure: closure,
|
18
19
|
name: code_object.name.to_s,
|
19
20
|
comments: code_object.docstring ? code_object.docstring.all.to_s : '',
|
20
|
-
visibility: code_object.visibility
|
21
|
+
visibility: code_object.visibility,
|
22
|
+
source: :yardoc
|
21
23
|
)
|
22
24
|
end
|
23
25
|
end
|