solargraph 0.58.1 → 0.58.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/.gitignore +1 -0
- data/CHANGELOG.md +7 -1
- data/lib/solargraph/api_map/cache.rb +110 -110
- data/lib/solargraph/api_map/constants.rb +279 -279
- data/lib/solargraph/api_map/index.rb +193 -193
- data/lib/solargraph/api_map/source_to_yard.rb +97 -97
- data/lib/solargraph/api_map/store.rb +384 -384
- data/lib/solargraph/api_map.rb +945 -945
- data/lib/solargraph/complex_type/type_methods.rb +228 -228
- data/lib/solargraph/complex_type/unique_type.rb +482 -482
- data/lib/solargraph/complex_type.rb +444 -444
- data/lib/solargraph/convention/data_definition/data_definition_node.rb +91 -91
- data/lib/solargraph/convention/data_definition.rb +105 -105
- data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +61 -61
- data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +102 -102
- data/lib/solargraph/convention/struct_definition.rb +164 -164
- data/lib/solargraph/diagnostics/require_not_found.rb +53 -53
- data/lib/solargraph/diagnostics/rubocop.rb +118 -118
- data/lib/solargraph/diagnostics/rubocop_helpers.rb +68 -68
- data/lib/solargraph/diagnostics/type_check.rb +55 -55
- data/lib/solargraph/doc_map.rb +439 -439
- data/lib/solargraph/equality.rb +34 -34
- data/lib/solargraph/gem_pins.rb +98 -98
- data/lib/solargraph/language_server/host/diagnoser.rb +89 -89
- data/lib/solargraph/language_server/host/dispatch.rb +130 -130
- data/lib/solargraph/language_server/host/message_worker.rb +112 -112
- data/lib/solargraph/language_server/host/sources.rb +99 -99
- data/lib/solargraph/language_server/host.rb +878 -878
- data/lib/solargraph/language_server/message/extended/check_gem_version.rb +114 -114
- data/lib/solargraph/language_server/message/extended/document.rb +23 -23
- data/lib/solargraph/language_server/message/text_document/completion.rb +56 -56
- data/lib/solargraph/language_server/message/text_document/definition.rb +40 -40
- data/lib/solargraph/language_server/message/text_document/document_symbol.rb +26 -26
- data/lib/solargraph/language_server/message/text_document/formatting.rb +148 -148
- data/lib/solargraph/language_server/message/text_document/hover.rb +58 -58
- data/lib/solargraph/language_server/message/text_document/signature_help.rb +24 -24
- data/lib/solargraph/language_server/message/text_document/type_definition.rb +25 -25
- data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +23 -23
- data/lib/solargraph/library.rb +683 -683
- data/lib/solargraph/location.rb +82 -82
- data/lib/solargraph/logging.rb +37 -37
- data/lib/solargraph/parser/comment_ripper.rb +69 -69
- data/lib/solargraph/parser/flow_sensitive_typing.rb +255 -255
- data/lib/solargraph/parser/node_processor/base.rb +92 -92
- data/lib/solargraph/parser/node_processor.rb +62 -62
- data/lib/solargraph/parser/parser_gem/class_methods.rb +149 -149
- data/lib/solargraph/parser/parser_gem/node_chainer.rb +166 -166
- data/lib/solargraph/parser/parser_gem/node_methods.rb +486 -486
- data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +22 -22
- data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +59 -59
- data/lib/solargraph/parser/parser_gem/node_processors/begin_node.rb +15 -15
- data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +46 -46
- data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +53 -53
- data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +23 -23
- data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +40 -40
- data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +29 -29
- data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +59 -59
- data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +98 -98
- data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +17 -17
- data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +38 -38
- data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +52 -52
- data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +291 -291
- data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +29 -29
- data/lib/solargraph/parser/parser_gem/node_processors.rb +70 -70
- data/lib/solargraph/parser/region.rb +69 -69
- data/lib/solargraph/parser/snippet.rb +17 -17
- data/lib/solargraph/pin/base.rb +729 -729
- data/lib/solargraph/pin/base_variable.rb +126 -126
- data/lib/solargraph/pin/block.rb +104 -104
- data/lib/solargraph/pin/breakable.rb +9 -9
- data/lib/solargraph/pin/callable.rb +231 -231
- data/lib/solargraph/pin/closure.rb +72 -72
- data/lib/solargraph/pin/common.rb +79 -79
- data/lib/solargraph/pin/conversions.rb +123 -123
- data/lib/solargraph/pin/delegated_method.rb +120 -120
- data/lib/solargraph/pin/documenting.rb +114 -114
- data/lib/solargraph/pin/instance_variable.rb +34 -34
- data/lib/solargraph/pin/keyword.rb +20 -20
- data/lib/solargraph/pin/local_variable.rb +75 -75
- data/lib/solargraph/pin/method.rb +672 -672
- data/lib/solargraph/pin/method_alias.rb +34 -34
- data/lib/solargraph/pin/namespace.rb +115 -115
- data/lib/solargraph/pin/parameter.rb +275 -275
- data/lib/solargraph/pin/proxy_type.rb +39 -39
- data/lib/solargraph/pin/reference/override.rb +47 -47
- data/lib/solargraph/pin/reference/superclass.rb +15 -15
- data/lib/solargraph/pin/reference.rb +39 -39
- data/lib/solargraph/pin/search.rb +61 -61
- data/lib/solargraph/pin/signature.rb +61 -61
- data/lib/solargraph/pin/symbol.rb +53 -53
- data/lib/solargraph/pin/until.rb +18 -18
- data/lib/solargraph/pin/while.rb +18 -18
- data/lib/solargraph/pin.rb +44 -44
- data/lib/solargraph/pin_cache.rb +245 -245
- data/lib/solargraph/position.rb +132 -119
- data/lib/solargraph/range.rb +112 -112
- data/lib/solargraph/rbs_map/conversions.rb +823 -823
- data/lib/solargraph/rbs_map/core_map.rb +58 -58
- data/lib/solargraph/rbs_map/stdlib_map.rb +43 -43
- data/lib/solargraph/rbs_map.rb +163 -163
- data/lib/solargraph/shell.rb +352 -352
- data/lib/solargraph/source/chain/call.rb +337 -337
- data/lib/solargraph/source/chain/constant.rb +26 -26
- data/lib/solargraph/source/chain/hash.rb +34 -34
- data/lib/solargraph/source/chain/if.rb +28 -28
- data/lib/solargraph/source/chain/instance_variable.rb +13 -13
- data/lib/solargraph/source/chain/literal.rb +48 -48
- data/lib/solargraph/source/chain/or.rb +23 -23
- data/lib/solargraph/source/chain.rb +291 -291
- data/lib/solargraph/source/change.rb +82 -82
- data/lib/solargraph/source/cursor.rb +166 -166
- data/lib/solargraph/source/source_chainer.rb +194 -194
- data/lib/solargraph/source/updater.rb +55 -55
- data/lib/solargraph/source.rb +498 -498
- data/lib/solargraph/source_map/clip.rb +226 -226
- data/lib/solargraph/source_map/data.rb +34 -34
- data/lib/solargraph/source_map/mapper.rb +259 -259
- data/lib/solargraph/source_map.rb +212 -212
- data/lib/solargraph/type_checker/checks.rb +124 -124
- data/lib/solargraph/type_checker/param_def.rb +37 -37
- data/lib/solargraph/type_checker/problem.rb +32 -32
- data/lib/solargraph/type_checker/rules.rb +84 -84
- data/lib/solargraph/type_checker.rb +814 -814
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/workspace/config.rb +255 -255
- data/lib/solargraph/workspace/require_paths.rb +97 -97
- data/lib/solargraph/workspace.rb +220 -220
- data/lib/solargraph/yard_map/helpers.rb +44 -44
- data/lib/solargraph/yard_map/mapper/to_method.rb +130 -130
- data/lib/solargraph/yard_map/mapper/to_namespace.rb +31 -31
- data/lib/solargraph/yard_map/mapper.rb +79 -79
- data/lib/solargraph/yard_map/to_method.rb +89 -89
- data/lib/solargraph/yardoc.rb +87 -87
- data/lib/solargraph.rb +105 -105
- data/rbs_collection.yaml +1 -1
- metadata +12 -12
- /data/{sig → rbs}/shims/ast/0/node.rbs +0 -0
- /data/{sig → rbs}/shims/ast/2.4/.rbs_meta.yaml +0 -0
- /data/{sig → rbs}/shims/ast/2.4/ast.rbs +0 -0
- /data/{sig → rbs}/shims/parser/3.2.0.1/builders/default.rbs +0 -0
- /data/{sig → rbs}/shims/parser/3.2.0.1/manifest.yaml +0 -0
- /data/{sig → rbs}/shims/parser/3.2.0.1/parser.rbs +0 -0
- /data/{sig → rbs}/shims/parser/3.2.0.1/polyfill.rbs +0 -0
- /data/{sig → rbs}/shims/thor/1.2.0.1/.rbs_meta.yaml +0 -0
- /data/{sig → rbs}/shims/thor/1.2.0.1/manifest.yaml +0 -0
- /data/{sig → rbs}/shims/thor/1.2.0.1/thor.rbs +0 -0
|
@@ -1,279 +1,279 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Solargraph
|
|
4
|
-
class ApiMap
|
|
5
|
-
# Methods for handling constants.
|
|
6
|
-
#
|
|
7
|
-
class Constants
|
|
8
|
-
# @param store [Store]
|
|
9
|
-
def initialize store
|
|
10
|
-
@store = store
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
# Resolve a name to a fully qualified namespace or constant.
|
|
14
|
-
#
|
|
15
|
-
# `Constants#resolve` finds fully qualified (absolute)
|
|
16
|
-
# namespaces based on relative names and the open gates
|
|
17
|
-
# (namespaces) provided. Names must be runtime-visible (erased)
|
|
18
|
-
# non-literal types, non-duck, non-signature types - e.g.,
|
|
19
|
-
# TrueClass, NilClass, Integer and Hash instead of true, nil,
|
|
20
|
-
# 96, or Hash{String => Symbol}
|
|
21
|
-
#
|
|
22
|
-
# Note: You may want to be using #qualify. Notably, #resolve:
|
|
23
|
-
# - does not handle anything with type parameters
|
|
24
|
-
# - will not gracefully handle nil, self and Boolean
|
|
25
|
-
# - will return a constant name instead of following its assignment
|
|
26
|
-
#
|
|
27
|
-
# @param name [String] Namespace which may relative and not be rooted.
|
|
28
|
-
# @param gates [Array<Array<String>, String>] Namespaces to search while resolving the name
|
|
29
|
-
#
|
|
30
|
-
# @return [String, nil] fully qualified namespace (i.e., is
|
|
31
|
-
# absolute, but will not start with ::)
|
|
32
|
-
def resolve(name, *gates)
|
|
33
|
-
return store.get_path_pins(name[2..]).first&.path if name.start_with?('::')
|
|
34
|
-
|
|
35
|
-
flat = gates.flatten
|
|
36
|
-
flat.push '' if flat.empty?
|
|
37
|
-
if cached_resolve.include? [name, flat]
|
|
38
|
-
cached_result = cached_resolve[[name, flat]]
|
|
39
|
-
# don't recurse
|
|
40
|
-
return nil if cached_result == :in_process
|
|
41
|
-
return cached_result
|
|
42
|
-
end
|
|
43
|
-
resolve_and_cache(name, flat)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
# Get a fully qualified namespace from a reference pin.
|
|
47
|
-
#
|
|
48
|
-
# @param pin [Pin::Reference]
|
|
49
|
-
# @return [String, nil]
|
|
50
|
-
def dereference pin
|
|
51
|
-
qualify_type(pin.type, *pin.reference_gates)&.tag
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# Collect a list of all constants defined in the specified gates.
|
|
55
|
-
#
|
|
56
|
-
# @param gates [Array<Array<String>, String>]
|
|
57
|
-
# @return [Array<Solargraph::Pin::Namespace, Solargraph::Pin::Constant>]
|
|
58
|
-
def collect(*gates)
|
|
59
|
-
flat = gates.flatten
|
|
60
|
-
cached_collect[flat] || collect_and_cache(flat)
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
# Determine a fully qualified namespace for a given tag
|
|
64
|
-
# referenced from the specified open gates. This method will
|
|
65
|
-
# search in each gate until it finds a match for the name.
|
|
66
|
-
#
|
|
67
|
-
# @param tag [String, nil] The type to match
|
|
68
|
-
# @param gates [Array<String>]
|
|
69
|
-
# @return [String, nil] fully qualified tag
|
|
70
|
-
def qualify tag, *gates
|
|
71
|
-
type = ComplexType.try_parse(tag)
|
|
72
|
-
qualify_type(type, *gates)&.tag
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
# @param type [ComplexType, nil] The type to match
|
|
76
|
-
# @param gates [Array<String>]
|
|
77
|
-
#
|
|
78
|
-
# @return [ComplexType, nil] A new rooted ComplexType
|
|
79
|
-
def qualify_type type, *gates
|
|
80
|
-
return nil if type.nil?
|
|
81
|
-
return type if type.selfy? || type.literal? || type.tag == 'nil' || type.interface? ||
|
|
82
|
-
type.tag == 'Boolean'
|
|
83
|
-
|
|
84
|
-
gates.push '' unless gates.include?('')
|
|
85
|
-
fqns = resolve(type.rooted_namespace, *gates)
|
|
86
|
-
return unless fqns
|
|
87
|
-
pin = store.get_path_pins(fqns).first
|
|
88
|
-
if pin.is_a?(Pin::Constant)
|
|
89
|
-
const = Solargraph::Parser::NodeMethods.unpack_name(pin.assignment)
|
|
90
|
-
return unless const
|
|
91
|
-
fqns = resolve(const, *pin.gates)
|
|
92
|
-
end
|
|
93
|
-
type.recreate(new_name: fqns, make_rooted: true)
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
# @return [void]
|
|
97
|
-
def clear
|
|
98
|
-
[cached_collect, cached_resolve].each(&:clear)
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
private
|
|
102
|
-
|
|
103
|
-
# @return [Store]
|
|
104
|
-
attr_reader :store
|
|
105
|
-
|
|
106
|
-
# @param name [String]
|
|
107
|
-
# @param gates [Array<String>]
|
|
108
|
-
# @return [String, nil]
|
|
109
|
-
def resolve_and_cache name, gates
|
|
110
|
-
cached_resolve[[name, gates]] = :in_process
|
|
111
|
-
cached_resolve[[name, gates]] = resolve_uncached(name, gates)
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
# @param name [String]
|
|
115
|
-
# @param gates [Array<String>]
|
|
116
|
-
# @return [String, nil]
|
|
117
|
-
def resolve_uncached name, gates
|
|
118
|
-
resolved = nil
|
|
119
|
-
base = gates
|
|
120
|
-
parts = name.split('::')
|
|
121
|
-
first = nil
|
|
122
|
-
parts.each.with_index do |nam, idx|
|
|
123
|
-
resolved, remainder = complex_resolve(nam, base, idx != parts.length - 1)
|
|
124
|
-
first ||= remainder
|
|
125
|
-
if resolved
|
|
126
|
-
base = [resolved]
|
|
127
|
-
else
|
|
128
|
-
return resolve(name, first) unless first.empty?
|
|
129
|
-
end
|
|
130
|
-
end
|
|
131
|
-
resolved
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
# @todo I'm not sure of a better way to express the return value in YARD.
|
|
135
|
-
# It's a tuple where the first element is a nullable string. Something
|
|
136
|
-
# like `Array(String|nil, Array<String>)` would be more accurate.
|
|
137
|
-
#
|
|
138
|
-
# @param name [String]
|
|
139
|
-
# @param gates [Array<String>]
|
|
140
|
-
# @param internal [Boolean] True if the name is not the last in the namespace
|
|
141
|
-
# @return [Array(Object, Array<String>)]
|
|
142
|
-
def complex_resolve name, gates, internal
|
|
143
|
-
resolved = nil
|
|
144
|
-
gates.each.with_index do |gate, idx|
|
|
145
|
-
resolved = simple_resolve(name, gate, internal)
|
|
146
|
-
return [resolved, gates[(idx + 1)..]] if resolved
|
|
147
|
-
store.get_ancestor_references(gate).each do |ref|
|
|
148
|
-
return ref.name.sub(/^::/, '') if ref.name.end_with?("::#{name}") && ref.name.start_with?('::')
|
|
149
|
-
|
|
150
|
-
mixin = resolve(ref.name, ref.reference_gates)
|
|
151
|
-
next unless mixin
|
|
152
|
-
|
|
153
|
-
resolved = simple_resolve(name, mixin, internal)
|
|
154
|
-
return [resolved, gates[(idx + 1)..]] if resolved
|
|
155
|
-
end
|
|
156
|
-
end
|
|
157
|
-
[nil, []]
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
# @param name [String]
|
|
161
|
-
# @param gate [String]
|
|
162
|
-
# @param internal [Boolean] True if the name is not the last in the namespace
|
|
163
|
-
# @return [String, nil]
|
|
164
|
-
def simple_resolve name, gate, internal
|
|
165
|
-
here = "#{gate}::#{name}".sub(/^::/, '').sub(/::$/, '')
|
|
166
|
-
pin = store.get_path_pins(here).first
|
|
167
|
-
if pin.is_a?(Pin::Constant) && internal
|
|
168
|
-
const = Solargraph::Parser::NodeMethods.unpack_name(pin.assignment)
|
|
169
|
-
return unless const
|
|
170
|
-
resolve(const, pin.gates)
|
|
171
|
-
else
|
|
172
|
-
pin&.path
|
|
173
|
-
end
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
# @param gates [Array<String>]
|
|
177
|
-
# @return [Array<Solargraph::Pin::Namespace, Solargraph::Pin::Constant>]
|
|
178
|
-
def collect_and_cache gates
|
|
179
|
-
skip = Set.new
|
|
180
|
-
cached_collect[gates] = gates.flat_map do |gate|
|
|
181
|
-
inner_get_constants(gate, %i[public private], skip)
|
|
182
|
-
end
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
# @return [Hash{Array(String, Array<String>) => String, :in_process, nil}]
|
|
186
|
-
def cached_resolve
|
|
187
|
-
@cached_resolve ||= {}
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
# @return [Hash{Array<String> => Array<Solargraph::Pin::Namespace, Solargraph::Pin::Constant>}]
|
|
191
|
-
def cached_collect
|
|
192
|
-
@cached_collect ||= {}
|
|
193
|
-
end
|
|
194
|
-
|
|
195
|
-
# Determine fully qualified namespace for a given namespace used
|
|
196
|
-
# inside the definition of another tag ("context"). This method
|
|
197
|
-
# will start the search in the specified context until it finds a
|
|
198
|
-
# match for the namespace.
|
|
199
|
-
#
|
|
200
|
-
# @param namespace [String, nil] The namespace to
|
|
201
|
-
# match
|
|
202
|
-
# @param context_namespace [String] The context namespace in which the
|
|
203
|
-
# tag was referenced; start from here to resolve the name
|
|
204
|
-
# @return [String, nil] fully qualified namespace
|
|
205
|
-
def qualify_namespace namespace, context_namespace = ''
|
|
206
|
-
if namespace.start_with?('::')
|
|
207
|
-
inner_qualify(namespace[2..], '', Set.new)
|
|
208
|
-
else
|
|
209
|
-
inner_qualify(namespace, context_namespace, Set.new)
|
|
210
|
-
end
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
# @param name [String] Namespace to fully qualify
|
|
214
|
-
# @param root [String] The context to search
|
|
215
|
-
# @param skip [Set<String>] Contexts already searched
|
|
216
|
-
# @return [String, nil] Fully qualified ("rooted") namespace
|
|
217
|
-
def inner_qualify name, root, skip
|
|
218
|
-
return name if name == ComplexType::GENERIC_TAG_NAME
|
|
219
|
-
return nil if name.nil?
|
|
220
|
-
return nil if skip.include?(root)
|
|
221
|
-
skip.add root
|
|
222
|
-
possibles = []
|
|
223
|
-
if name == ''
|
|
224
|
-
return '' if root == ''
|
|
225
|
-
|
|
226
|
-
inner_qualify(root, '', skip)
|
|
227
|
-
else
|
|
228
|
-
return name if root == '' && store.namespace_exists?(name)
|
|
229
|
-
roots = root.to_s.split('::')
|
|
230
|
-
while roots.length.positive?
|
|
231
|
-
fqns = "#{roots.join('::')}::#{name}"
|
|
232
|
-
return fqns if store.namespace_exists?(fqns)
|
|
233
|
-
incs = store.get_includes(roots.join('::'))
|
|
234
|
-
incs.each do |inc|
|
|
235
|
-
foundinc = inner_qualify(name, inc.type.to_s, skip)
|
|
236
|
-
possibles.push foundinc unless foundinc.nil?
|
|
237
|
-
end
|
|
238
|
-
roots.pop
|
|
239
|
-
end
|
|
240
|
-
if possibles.empty?
|
|
241
|
-
incs = store.get_includes('')
|
|
242
|
-
incs.each do |inc|
|
|
243
|
-
foundinc = inner_qualify(name, inc.type.to_s, skip)
|
|
244
|
-
possibles.push foundinc unless foundinc.nil?
|
|
245
|
-
end
|
|
246
|
-
end
|
|
247
|
-
return name if store.namespace_exists?(name)
|
|
248
|
-
possibles.last
|
|
249
|
-
end
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
# @param fqns [String]
|
|
253
|
-
# @param visibility [Array<Symbol>]
|
|
254
|
-
# @param skip [Set<String>]
|
|
255
|
-
# @return [Array<Solargraph::Pin::Namespace, Solargraph::Pin::Constant>]
|
|
256
|
-
def inner_get_constants fqns, visibility, skip
|
|
257
|
-
return [] if fqns.nil? || skip.include?(fqns)
|
|
258
|
-
skip.add fqns
|
|
259
|
-
result = []
|
|
260
|
-
|
|
261
|
-
store.get_prepends(fqns).each do |pre|
|
|
262
|
-
pre_fqns = resolve(pre.name, pre.closure.gates - skip.to_a)
|
|
263
|
-
result.concat inner_get_constants(pre_fqns, [:public], skip)
|
|
264
|
-
end
|
|
265
|
-
result.concat(store.get_constants(fqns, visibility).sort { |a, b| a.name <=> b.name })
|
|
266
|
-
store.get_includes(fqns).each do |pin|
|
|
267
|
-
inc_fqns = resolve(pin.name, pin.closure.gates - skip.to_a)
|
|
268
|
-
result.concat inner_get_constants(inc_fqns, [:public], skip)
|
|
269
|
-
end
|
|
270
|
-
sc_ref = store.get_superclass(fqns)
|
|
271
|
-
if sc_ref
|
|
272
|
-
fqsc = dereference(sc_ref)
|
|
273
|
-
result.concat inner_get_constants(fqsc, [:public], skip) unless %w[Object BasicObject].include?(fqsc)
|
|
274
|
-
end
|
|
275
|
-
result
|
|
276
|
-
end
|
|
277
|
-
end
|
|
278
|
-
end
|
|
279
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solargraph
|
|
4
|
+
class ApiMap
|
|
5
|
+
# Methods for handling constants.
|
|
6
|
+
#
|
|
7
|
+
class Constants
|
|
8
|
+
# @param store [Store]
|
|
9
|
+
def initialize store
|
|
10
|
+
@store = store
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Resolve a name to a fully qualified namespace or constant.
|
|
14
|
+
#
|
|
15
|
+
# `Constants#resolve` finds fully qualified (absolute)
|
|
16
|
+
# namespaces based on relative names and the open gates
|
|
17
|
+
# (namespaces) provided. Names must be runtime-visible (erased)
|
|
18
|
+
# non-literal types, non-duck, non-signature types - e.g.,
|
|
19
|
+
# TrueClass, NilClass, Integer and Hash instead of true, nil,
|
|
20
|
+
# 96, or Hash{String => Symbol}
|
|
21
|
+
#
|
|
22
|
+
# Note: You may want to be using #qualify. Notably, #resolve:
|
|
23
|
+
# - does not handle anything with type parameters
|
|
24
|
+
# - will not gracefully handle nil, self and Boolean
|
|
25
|
+
# - will return a constant name instead of following its assignment
|
|
26
|
+
#
|
|
27
|
+
# @param name [String] Namespace which may relative and not be rooted.
|
|
28
|
+
# @param gates [Array<Array<String>, String>] Namespaces to search while resolving the name
|
|
29
|
+
#
|
|
30
|
+
# @return [String, nil] fully qualified namespace (i.e., is
|
|
31
|
+
# absolute, but will not start with ::)
|
|
32
|
+
def resolve(name, *gates)
|
|
33
|
+
return store.get_path_pins(name[2..]).first&.path if name.start_with?('::')
|
|
34
|
+
|
|
35
|
+
flat = gates.flatten
|
|
36
|
+
flat.push '' if flat.empty?
|
|
37
|
+
if cached_resolve.include? [name, flat]
|
|
38
|
+
cached_result = cached_resolve[[name, flat]]
|
|
39
|
+
# don't recurse
|
|
40
|
+
return nil if cached_result == :in_process
|
|
41
|
+
return cached_result
|
|
42
|
+
end
|
|
43
|
+
resolve_and_cache(name, flat)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Get a fully qualified namespace from a reference pin.
|
|
47
|
+
#
|
|
48
|
+
# @param pin [Pin::Reference]
|
|
49
|
+
# @return [String, nil]
|
|
50
|
+
def dereference pin
|
|
51
|
+
qualify_type(pin.type, *pin.reference_gates)&.tag
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Collect a list of all constants defined in the specified gates.
|
|
55
|
+
#
|
|
56
|
+
# @param gates [Array<Array<String>, String>]
|
|
57
|
+
# @return [Array<Solargraph::Pin::Namespace, Solargraph::Pin::Constant>]
|
|
58
|
+
def collect(*gates)
|
|
59
|
+
flat = gates.flatten
|
|
60
|
+
cached_collect[flat] || collect_and_cache(flat)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Determine a fully qualified namespace for a given tag
|
|
64
|
+
# referenced from the specified open gates. This method will
|
|
65
|
+
# search in each gate until it finds a match for the name.
|
|
66
|
+
#
|
|
67
|
+
# @param tag [String, nil] The type to match
|
|
68
|
+
# @param gates [Array<String>]
|
|
69
|
+
# @return [String, nil] fully qualified tag
|
|
70
|
+
def qualify tag, *gates
|
|
71
|
+
type = ComplexType.try_parse(tag)
|
|
72
|
+
qualify_type(type, *gates)&.tag
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# @param type [ComplexType, nil] The type to match
|
|
76
|
+
# @param gates [Array<String>]
|
|
77
|
+
#
|
|
78
|
+
# @return [ComplexType, nil] A new rooted ComplexType
|
|
79
|
+
def qualify_type type, *gates
|
|
80
|
+
return nil if type.nil?
|
|
81
|
+
return type if type.selfy? || type.literal? || type.tag == 'nil' || type.interface? ||
|
|
82
|
+
type.tag == 'Boolean'
|
|
83
|
+
|
|
84
|
+
gates.push '' unless gates.include?('')
|
|
85
|
+
fqns = resolve(type.rooted_namespace, *gates)
|
|
86
|
+
return unless fqns
|
|
87
|
+
pin = store.get_path_pins(fqns).first
|
|
88
|
+
if pin.is_a?(Pin::Constant)
|
|
89
|
+
const = Solargraph::Parser::NodeMethods.unpack_name(pin.assignment)
|
|
90
|
+
return unless const
|
|
91
|
+
fqns = resolve(const, *pin.gates)
|
|
92
|
+
end
|
|
93
|
+
type.recreate(new_name: fqns, make_rooted: true)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# @return [void]
|
|
97
|
+
def clear
|
|
98
|
+
[cached_collect, cached_resolve].each(&:clear)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
private
|
|
102
|
+
|
|
103
|
+
# @return [Store]
|
|
104
|
+
attr_reader :store
|
|
105
|
+
|
|
106
|
+
# @param name [String]
|
|
107
|
+
# @param gates [Array<String>]
|
|
108
|
+
# @return [String, nil]
|
|
109
|
+
def resolve_and_cache name, gates
|
|
110
|
+
cached_resolve[[name, gates]] = :in_process
|
|
111
|
+
cached_resolve[[name, gates]] = resolve_uncached(name, gates)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# @param name [String]
|
|
115
|
+
# @param gates [Array<String>]
|
|
116
|
+
# @return [String, nil]
|
|
117
|
+
def resolve_uncached name, gates
|
|
118
|
+
resolved = nil
|
|
119
|
+
base = gates
|
|
120
|
+
parts = name.split('::')
|
|
121
|
+
first = nil
|
|
122
|
+
parts.each.with_index do |nam, idx|
|
|
123
|
+
resolved, remainder = complex_resolve(nam, base, idx != parts.length - 1)
|
|
124
|
+
first ||= remainder
|
|
125
|
+
if resolved
|
|
126
|
+
base = [resolved]
|
|
127
|
+
else
|
|
128
|
+
return resolve(name, first) unless first.empty?
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
resolved
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# @todo I'm not sure of a better way to express the return value in YARD.
|
|
135
|
+
# It's a tuple where the first element is a nullable string. Something
|
|
136
|
+
# like `Array(String|nil, Array<String>)` would be more accurate.
|
|
137
|
+
#
|
|
138
|
+
# @param name [String]
|
|
139
|
+
# @param gates [Array<String>]
|
|
140
|
+
# @param internal [Boolean] True if the name is not the last in the namespace
|
|
141
|
+
# @return [Array(Object, Array<String>)]
|
|
142
|
+
def complex_resolve name, gates, internal
|
|
143
|
+
resolved = nil
|
|
144
|
+
gates.each.with_index do |gate, idx|
|
|
145
|
+
resolved = simple_resolve(name, gate, internal)
|
|
146
|
+
return [resolved, gates[(idx + 1)..]] if resolved
|
|
147
|
+
store.get_ancestor_references(gate).each do |ref|
|
|
148
|
+
return ref.name.sub(/^::/, '') if ref.name.end_with?("::#{name}") && ref.name.start_with?('::')
|
|
149
|
+
|
|
150
|
+
mixin = resolve(ref.name, ref.reference_gates)
|
|
151
|
+
next unless mixin
|
|
152
|
+
|
|
153
|
+
resolved = simple_resolve(name, mixin, internal)
|
|
154
|
+
return [resolved, gates[(idx + 1)..]] if resolved
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
[nil, []]
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# @param name [String]
|
|
161
|
+
# @param gate [String]
|
|
162
|
+
# @param internal [Boolean] True if the name is not the last in the namespace
|
|
163
|
+
# @return [String, nil]
|
|
164
|
+
def simple_resolve name, gate, internal
|
|
165
|
+
here = "#{gate}::#{name}".sub(/^::/, '').sub(/::$/, '')
|
|
166
|
+
pin = store.get_path_pins(here).first
|
|
167
|
+
if pin.is_a?(Pin::Constant) && internal
|
|
168
|
+
const = Solargraph::Parser::NodeMethods.unpack_name(pin.assignment)
|
|
169
|
+
return unless const
|
|
170
|
+
resolve(const, pin.gates)
|
|
171
|
+
else
|
|
172
|
+
pin&.path
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# @param gates [Array<String>]
|
|
177
|
+
# @return [Array<Solargraph::Pin::Namespace, Solargraph::Pin::Constant>]
|
|
178
|
+
def collect_and_cache gates
|
|
179
|
+
skip = Set.new
|
|
180
|
+
cached_collect[gates] = gates.flat_map do |gate|
|
|
181
|
+
inner_get_constants(gate, %i[public private], skip)
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# @return [Hash{Array(String, Array<String>) => String, :in_process, nil}]
|
|
186
|
+
def cached_resolve
|
|
187
|
+
@cached_resolve ||= {}
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# @return [Hash{Array<String> => Array<Solargraph::Pin::Namespace, Solargraph::Pin::Constant>}]
|
|
191
|
+
def cached_collect
|
|
192
|
+
@cached_collect ||= {}
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Determine fully qualified namespace for a given namespace used
|
|
196
|
+
# inside the definition of another tag ("context"). This method
|
|
197
|
+
# will start the search in the specified context until it finds a
|
|
198
|
+
# match for the namespace.
|
|
199
|
+
#
|
|
200
|
+
# @param namespace [String, nil] The namespace to
|
|
201
|
+
# match
|
|
202
|
+
# @param context_namespace [String] The context namespace in which the
|
|
203
|
+
# tag was referenced; start from here to resolve the name
|
|
204
|
+
# @return [String, nil] fully qualified namespace
|
|
205
|
+
def qualify_namespace namespace, context_namespace = ''
|
|
206
|
+
if namespace.start_with?('::')
|
|
207
|
+
inner_qualify(namespace[2..], '', Set.new)
|
|
208
|
+
else
|
|
209
|
+
inner_qualify(namespace, context_namespace, Set.new)
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# @param name [String] Namespace to fully qualify
|
|
214
|
+
# @param root [String] The context to search
|
|
215
|
+
# @param skip [Set<String>] Contexts already searched
|
|
216
|
+
# @return [String, nil] Fully qualified ("rooted") namespace
|
|
217
|
+
def inner_qualify name, root, skip
|
|
218
|
+
return name if name == ComplexType::GENERIC_TAG_NAME
|
|
219
|
+
return nil if name.nil?
|
|
220
|
+
return nil if skip.include?(root)
|
|
221
|
+
skip.add root
|
|
222
|
+
possibles = []
|
|
223
|
+
if name == ''
|
|
224
|
+
return '' if root == ''
|
|
225
|
+
|
|
226
|
+
inner_qualify(root, '', skip)
|
|
227
|
+
else
|
|
228
|
+
return name if root == '' && store.namespace_exists?(name)
|
|
229
|
+
roots = root.to_s.split('::')
|
|
230
|
+
while roots.length.positive?
|
|
231
|
+
fqns = "#{roots.join('::')}::#{name}"
|
|
232
|
+
return fqns if store.namespace_exists?(fqns)
|
|
233
|
+
incs = store.get_includes(roots.join('::'))
|
|
234
|
+
incs.each do |inc|
|
|
235
|
+
foundinc = inner_qualify(name, inc.type.to_s, skip)
|
|
236
|
+
possibles.push foundinc unless foundinc.nil?
|
|
237
|
+
end
|
|
238
|
+
roots.pop
|
|
239
|
+
end
|
|
240
|
+
if possibles.empty?
|
|
241
|
+
incs = store.get_includes('')
|
|
242
|
+
incs.each do |inc|
|
|
243
|
+
foundinc = inner_qualify(name, inc.type.to_s, skip)
|
|
244
|
+
possibles.push foundinc unless foundinc.nil?
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
return name if store.namespace_exists?(name)
|
|
248
|
+
possibles.last
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# @param fqns [String]
|
|
253
|
+
# @param visibility [Array<Symbol>]
|
|
254
|
+
# @param skip [Set<String>]
|
|
255
|
+
# @return [Array<Solargraph::Pin::Namespace, Solargraph::Pin::Constant>]
|
|
256
|
+
def inner_get_constants fqns, visibility, skip
|
|
257
|
+
return [] if fqns.nil? || skip.include?(fqns)
|
|
258
|
+
skip.add fqns
|
|
259
|
+
result = []
|
|
260
|
+
|
|
261
|
+
store.get_prepends(fqns).each do |pre|
|
|
262
|
+
pre_fqns = resolve(pre.name, pre.closure.gates - skip.to_a)
|
|
263
|
+
result.concat inner_get_constants(pre_fqns, [:public], skip)
|
|
264
|
+
end
|
|
265
|
+
result.concat(store.get_constants(fqns, visibility).sort { |a, b| a.name <=> b.name })
|
|
266
|
+
store.get_includes(fqns).each do |pin|
|
|
267
|
+
inc_fqns = resolve(pin.name, pin.closure.gates - skip.to_a)
|
|
268
|
+
result.concat inner_get_constants(inc_fqns, [:public], skip)
|
|
269
|
+
end
|
|
270
|
+
sc_ref = store.get_superclass(fqns)
|
|
271
|
+
if sc_ref
|
|
272
|
+
fqsc = dereference(sc_ref)
|
|
273
|
+
result.concat inner_get_constants(fqsc, [:public], skip) unless %w[Object BasicObject].include?(fqsc)
|
|
274
|
+
end
|
|
275
|
+
result
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
end
|