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
data/lib/solargraph/shell.rb
CHANGED
|
@@ -98,60 +98,37 @@ module Solargraph
|
|
|
98
98
|
# @return [void]
|
|
99
99
|
def clear
|
|
100
100
|
puts 'Deleting all cached documentation (gems, core and stdlib)'
|
|
101
|
-
Solargraph::
|
|
101
|
+
Solargraph::CacheDir.clear
|
|
102
102
|
end
|
|
103
103
|
map 'clear-cache' => :clear
|
|
104
104
|
map 'clear-cores' => :clear
|
|
105
105
|
|
|
106
|
-
desc 'cache', 'Cache a gem', hide: true
|
|
107
|
-
option :rebuild, type: :boolean, desc: 'Rebuild existing documentation', default: false
|
|
108
|
-
# @return [void]
|
|
109
|
-
# @param gem [String]
|
|
110
|
-
# @param version [String, nil]
|
|
111
|
-
def cache gem, version = nil
|
|
112
|
-
gemspec = Gem::Specification.find_by_name(gem, version)
|
|
113
|
-
|
|
114
|
-
if options[:rebuild] || !PinCache.has_yard?(gemspec)
|
|
115
|
-
pins = GemPins.build_yard_pins(['yard-activesupport-concern'], gemspec)
|
|
116
|
-
PinCache.serialize_yard_gem(gemspec, pins)
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
workspace = Solargraph::Workspace.new(Dir.pwd) if File.exist?('rbs_collection.yaml')
|
|
120
|
-
rbs_map = RbsMap.from_gemspec(gemspec, workspace&.rbs_collection_path, workspace&.rbs_collection_config_path)
|
|
121
|
-
if options[:rebuild] || !PinCache.has_rbs_collection?(gemspec, rbs_map.cache_key)
|
|
122
|
-
PinCache.serialize_rbs_collection_gem(gemspec, rbs_map.cache_key, rbs_map.pins)
|
|
123
|
-
end
|
|
124
|
-
rescue Gem::MissingSpecError
|
|
125
|
-
warn "Gem '#{gem}' not found"
|
|
126
|
-
end
|
|
127
|
-
|
|
128
106
|
desc 'uncache GEM [...GEM]', 'Delete specific cached gem documentation'
|
|
129
107
|
long_desc %(
|
|
130
108
|
Specify one or more gem names to clear. 'core' or 'stdlib' may
|
|
131
109
|
also be specified to clear cached system documentation.
|
|
132
110
|
Documentation will be regenerated as needed.
|
|
133
111
|
)
|
|
112
|
+
option :directory, type: :string, desc: 'Workspace directory', default: Dir.pwd
|
|
134
113
|
# @param gems [Array<String>]
|
|
135
114
|
# @return [void]
|
|
136
115
|
def uncache *gems
|
|
137
116
|
raise ArgumentError, 'No gems specified.' if gems.empty?
|
|
117
|
+
repo = Solargraph::Repo.new(options[:directory])
|
|
118
|
+
|
|
138
119
|
gems.each do |gem|
|
|
139
120
|
if gem == 'core'
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
next
|
|
121
|
+
Solargraph::Collection::Core.uncache
|
|
122
|
+
elsif gem == 'stdlib'
|
|
123
|
+
FileUtils.rm_rf CacheDir.stdlib_dir
|
|
124
|
+
else
|
|
125
|
+
metagem = repo.find_by_name(gem)
|
|
126
|
+
Solargraph::Collection::Gem.uncache(metagem) if metagem
|
|
147
127
|
end
|
|
148
|
-
|
|
149
|
-
spec = Gem::Specification.find_by_name(gem)
|
|
150
|
-
PinCache.uncache_gem(spec, out: $stdout)
|
|
151
128
|
end
|
|
152
129
|
end
|
|
153
130
|
|
|
154
|
-
desc '
|
|
131
|
+
desc 'cache GEM_NAME [...GEM_NAME] [core]', 'Cache documentation for
|
|
155
132
|
installed libraries'
|
|
156
133
|
long_desc %( This command will cache the
|
|
157
134
|
generated type documentation for the specified libraries. While
|
|
@@ -172,56 +149,51 @@ module Solargraph
|
|
|
172
149
|
If the library is already cached, it will be rebuilt if the
|
|
173
150
|
--rebuild option is set.
|
|
174
151
|
|
|
175
|
-
Cached documentation is stored in #{
|
|
152
|
+
Cached documentation is stored in #{CacheDir.base_dir}, which
|
|
176
153
|
can be stored between CI runs.
|
|
177
154
|
)
|
|
155
|
+
option :directory, type: :string, desc: 'Workspace directory', default: Dir.pwd
|
|
178
156
|
option :rebuild, type: :boolean, desc: 'Rebuild existing documentation', default: false
|
|
179
157
|
# @param names [Array<String>]
|
|
180
158
|
# @return [void]
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
warn "Gem '#{name}' not found"
|
|
216
|
-
rescue Gem::Requirement::BadRequirementError => e
|
|
217
|
-
warn "Gem '#{name}' failed while loading"
|
|
218
|
-
warn e.message
|
|
219
|
-
# @sg-ignore Need to add nil check here
|
|
220
|
-
warn e.backtrace.join("\n")
|
|
221
|
-
end
|
|
222
|
-
warn "Documentation cached for #{names.count} gems."
|
|
159
|
+
# @param [Array<Object>] gem_names
|
|
160
|
+
def cache *gem_names
|
|
161
|
+
repo = Solargraph::Repo.new(options[:directory])
|
|
162
|
+
metagems = if gem_names.empty?
|
|
163
|
+
if repo.bundled?
|
|
164
|
+
repo.bundled.select(&:cacheable?)
|
|
165
|
+
else
|
|
166
|
+
Gem::Specification.all_names
|
|
167
|
+
.map { |name| Gem::Specification.find_by_full_name(name) }
|
|
168
|
+
.map { |gemspec| Metagem.from_specification(gemspec) }
|
|
169
|
+
end
|
|
170
|
+
else
|
|
171
|
+
gem_names.each_with_object([]) do |name, result|
|
|
172
|
+
if name == 'core'
|
|
173
|
+
Collection::Core.uncache if options[:rebuild]
|
|
174
|
+
puts 'Caching core'
|
|
175
|
+
Collection::Core.load
|
|
176
|
+
else
|
|
177
|
+
# @todo Quick and dirty hack for solargraph-rspec require bug
|
|
178
|
+
# (see https://github.com/lekemula/solargraph-rspec/pull/38)
|
|
179
|
+
# TL;DR: `repo.find_by_path` should not be necessary
|
|
180
|
+
found = repo.find_by_name(name) || repo.find_by_path(name)
|
|
181
|
+
if found
|
|
182
|
+
result.push found
|
|
183
|
+
else
|
|
184
|
+
warn "Gem #{name} not found"
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
metagems.each do |metagem|
|
|
190
|
+
Collection::Gem.uncache(metagem) if options[:rebuild]
|
|
191
|
+
puts "Caching #{metagem.name} #{metagem.version} (#{metagem.cache_name})"
|
|
192
|
+
Collection::Gem.load(metagem)
|
|
223
193
|
end
|
|
194
|
+
puts "Documentation cached for #{metagems.count} gems."
|
|
224
195
|
end
|
|
196
|
+
map 'gems' => :cache
|
|
225
197
|
|
|
226
198
|
desc 'reporters', 'Get a list of diagnostics reporters'
|
|
227
199
|
# @return [void]
|
|
@@ -596,27 +568,5 @@ module Solargraph
|
|
|
596
568
|
puts pin.inspect
|
|
597
569
|
end
|
|
598
570
|
end
|
|
599
|
-
|
|
600
|
-
# @param gemspec [Gem::Specification, nil]
|
|
601
|
-
# @param rebuild [Boolean]
|
|
602
|
-
# @return [void]
|
|
603
|
-
def do_cache gemspec, rebuild: false
|
|
604
|
-
if gemspec.nil?
|
|
605
|
-
warn "Gem '#{gemspec&.name}' not found"
|
|
606
|
-
else
|
|
607
|
-
if rebuild || !PinCache.has_yard?(gemspec)
|
|
608
|
-
pins = GemPins.build_yard_pins(['yard-activesupport-concern'], gemspec)
|
|
609
|
-
PinCache.serialize_yard_gem(gemspec, pins)
|
|
610
|
-
end
|
|
611
|
-
|
|
612
|
-
workspace = Solargraph::Workspace.new(Dir.pwd)
|
|
613
|
-
rbs_map = RbsMap.from_gemspec(gemspec, workspace.rbs_collection_path, workspace.rbs_collection_config_path)
|
|
614
|
-
if rebuild || !PinCache.has_rbs_collection?(gemspec, rbs_map.cache_key)
|
|
615
|
-
# cache pins even if result is zero, so we don't retry building pins
|
|
616
|
-
pins = rbs_map.pins || []
|
|
617
|
-
PinCache.serialize_rbs_collection_gem(gemspec, rbs_map.cache_key, pins)
|
|
618
|
-
end
|
|
619
|
-
end
|
|
620
|
-
end
|
|
621
571
|
end
|
|
622
572
|
end
|
|
@@ -55,18 +55,39 @@ module Solargraph
|
|
|
55
55
|
# chain.rb#maybe_nil will add the nil type later, we just
|
|
56
56
|
# need to worry about the not-nil case
|
|
57
57
|
|
|
58
|
-
# @sg-ignore Need to handle duck-typed method calls on union types
|
|
59
58
|
binder = binder.without_nil if nullable?
|
|
60
|
-
#
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
# Resolve one unique type at a time, so a `self` return type narrows to the one that supplied the pin.
|
|
60
|
+
# @type [::Array<Pin::Base>]
|
|
61
|
+
resolved = []
|
|
62
|
+
unresolved_type = false
|
|
63
|
+
binder.each_unique_type do |context|
|
|
64
|
+
if context.duck_type? && context.name[1..] == word
|
|
65
|
+
# explicit: false skips arity checking; the duck type
|
|
66
|
+
# only tells us the method exists, not its signature
|
|
67
|
+
pin = Pin::DuckMethod.new(name: word, source: :chain, explicit: false)
|
|
68
|
+
else
|
|
69
|
+
ns_tag = context.namespace == '' ? '' : context.namespace_type.tag
|
|
70
|
+
stack = api_map.get_method_stack(ns_tag, word, scope: context.scope)
|
|
71
|
+
pin = stack.first
|
|
72
|
+
end
|
|
73
|
+
if pin.nil?
|
|
74
|
+
unresolved_type = true
|
|
75
|
+
next
|
|
76
|
+
end
|
|
77
|
+
# Bind name_pin to this type alone, so #inferred_pins (incl. Class#new) cannot see the others.
|
|
78
|
+
type_name_pin = Pin::ProxyType.anonymous(name_pin.context,
|
|
79
|
+
closure: name_pin.closure,
|
|
80
|
+
gates: name_pin.gates,
|
|
81
|
+
binder: context,
|
|
82
|
+
source: :chain)
|
|
83
|
+
resolved.concat inferred_pins([pin], api_map, type_name_pin, locals)
|
|
65
84
|
end
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
85
|
+
return [] if unresolved_type && !api_map.loose_unions
|
|
86
|
+
return [] if resolved.empty?
|
|
87
|
+
# Accumulating every type's result treats the binder as a union, which is the only
|
|
88
|
+
# multi-type binder ComplexType builds; dedup on rooted_tags rather than #tag, which
|
|
89
|
+
# reports only the first member and so collapses two distinct unions.
|
|
90
|
+
resolved.uniq { |pin| [pin.path, pin.return_type.rooted_tags] }
|
|
70
91
|
end
|
|
71
92
|
|
|
72
93
|
private
|
|
@@ -153,16 +174,18 @@ module Solargraph
|
|
|
153
174
|
# qualify(), however, happens in the namespace where
|
|
154
175
|
# the docs were written - from the method pin.
|
|
155
176
|
# @todo Need to add nil check here
|
|
156
|
-
if new_return_type.defined?
|
|
157
|
-
|
|
158
|
-
|
|
177
|
+
type = if new_return_type.defined?
|
|
178
|
+
with_params(new_return_type.self_to_type(self_type), self_type).qualify(api_map, *pin.gates)
|
|
179
|
+
else
|
|
180
|
+
inferr_from_factory_parameters(api_map, pin)
|
|
181
|
+
end
|
|
159
182
|
type ||= ComplexType::UNDEFINED
|
|
160
183
|
[type, new_signature_pin]
|
|
161
184
|
end
|
|
162
185
|
|
|
163
186
|
# @param pins [::Enumerable<Pin::Base>]
|
|
164
187
|
# @param api_map [ApiMap]
|
|
165
|
-
# @param name_pin [Pin::Base]
|
|
188
|
+
# @param name_pin [Pin::Base] its #binder resolves `self` in the declarations of `pins`
|
|
166
189
|
# @param locals [::Array<Solargraph::Pin::LocalVariable, Solargraph::Pin::Parameter>]
|
|
167
190
|
# @return [::Array<Pin::Base>]
|
|
168
191
|
def inferred_pins pins, api_map, name_pin, locals
|
|
@@ -219,6 +242,27 @@ module Solargraph
|
|
|
219
242
|
end
|
|
220
243
|
end
|
|
221
244
|
|
|
245
|
+
# @param api_map [ApiMap]
|
|
246
|
+
# @param method_pin [Pin::Method]
|
|
247
|
+
# @return [ComplexType, nil]
|
|
248
|
+
def inferr_from_factory_parameters api_map, method_pin
|
|
249
|
+
factory_parameter = api_map.factory_parameters_for_method(method_pin).find do |factory_param|
|
|
250
|
+
method_pin.parameters.each_with_index.find do |param, index|
|
|
251
|
+
current_argument = arguments[index]
|
|
252
|
+
next unless current_argument&.literal?
|
|
253
|
+
# @type [Solargraph::Source::Chain::Literal]
|
|
254
|
+
last_link = current_argument.links.last
|
|
255
|
+
argument_value = last_link.value
|
|
256
|
+
|
|
257
|
+
param.name == factory_param.param_name && argument_value == factory_param.value
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
return nil if factory_parameter.nil?
|
|
262
|
+
|
|
263
|
+
factory_parameter.return_type.qualify(api_map, method_pin.namespace)
|
|
264
|
+
end
|
|
265
|
+
|
|
222
266
|
# @param pin [Pin::Base]
|
|
223
267
|
# @param api_map [ApiMap]
|
|
224
268
|
# @param context [ComplexType, ComplexType::UniqueType]
|
|
@@ -6,29 +6,29 @@ module Solargraph
|
|
|
6
6
|
class Source
|
|
7
7
|
class Chain
|
|
8
8
|
class Literal < Link
|
|
9
|
-
attr_reader :word
|
|
9
|
+
attr_reader :word
|
|
10
|
+
|
|
11
|
+
# @return [::String, ::Symbol]
|
|
12
|
+
attr_reader :value
|
|
13
|
+
|
|
14
|
+
# @return [Parser::AST::Node]
|
|
15
|
+
attr_reader :node
|
|
10
16
|
|
|
11
17
|
# @param type [String]
|
|
12
18
|
# @param node [Parser::AST::Node, Object]
|
|
13
19
|
def initialize type, node
|
|
14
20
|
super("<#{type}>")
|
|
21
|
+
@node = node
|
|
15
22
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
# @value = false
|
|
26
|
-
# # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
|
|
27
|
-
# elsif %i[int sym].include?(node.type)
|
|
28
|
-
# # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
|
|
29
|
-
# @value = node.children.first
|
|
30
|
-
# end
|
|
31
|
-
# end
|
|
23
|
+
if node.is_a?(::Parser::AST::Node)
|
|
24
|
+
if node.type == :true
|
|
25
|
+
@value = true
|
|
26
|
+
elsif node.type == :false
|
|
27
|
+
@value = false
|
|
28
|
+
elsif %i[int sym].include?(node.type)
|
|
29
|
+
@value = node.children.first
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
32
|
@type = type
|
|
33
33
|
# @literal_type = ComplexType.try_parse(@value.inspect)
|
|
34
34
|
@literal_type = ComplexType::UNDEFINED
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solargraph
|
|
4
|
+
class Source
|
|
5
|
+
class Chain
|
|
6
|
+
# A method parameter represented as a link in a method call chain.
|
|
7
|
+
#
|
|
8
|
+
# @example
|
|
9
|
+
# some_method('literal_value')
|
|
10
|
+
# ^^^^^^^^^^^^^
|
|
11
|
+
class Parameter < Link
|
|
12
|
+
# @param literal [Chain::Literal] - literal argument
|
|
13
|
+
# @param method_call_chain [Chain] method call that contains the argument
|
|
14
|
+
def initialize literal, method_call_chain # rubocop:disable Lint/MissingSuper
|
|
15
|
+
@literal = literal
|
|
16
|
+
@method_call_chain = method_call_chain
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def word
|
|
20
|
+
@word ||= "#{method_name}(.., #{@literal.word}, ..)"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @param api_map [ApiMap]
|
|
24
|
+
# @param name_pin [Pin::Base]
|
|
25
|
+
# @param locals [::Array<Pin::Parameter, Pin::LocalVariable>]
|
|
26
|
+
# @return [::Array<Pin::Base>]
|
|
27
|
+
def resolve api_map, name_pin, locals
|
|
28
|
+
# @type [Pin::Method]
|
|
29
|
+
method_pin = method_call_chain.define(api_map, name_pin, locals)&.first
|
|
30
|
+
return [] unless method_pin
|
|
31
|
+
|
|
32
|
+
# Use indexed lookup for better performance
|
|
33
|
+
api_map.factory_parameters_for_method(method_pin).select do |fp|
|
|
34
|
+
param_index = method_pin.parameters.find_index { |param| param.name == fp.param_name }
|
|
35
|
+
next if param_index.nil?
|
|
36
|
+
next false unless fp.value == literal_value
|
|
37
|
+
|
|
38
|
+
# a splat takes every argument from its position on
|
|
39
|
+
method_pin.parameters[param_index].restarg? ? current_index >= param_index : current_index == param_index
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @return [Boolean] true if this is a parameter of Kernel#require
|
|
44
|
+
def require_parameter?
|
|
45
|
+
method_call_chain.links.last.word == 'require'
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
# @return [Chain::Literal]
|
|
51
|
+
attr_reader :literal
|
|
52
|
+
# @ return [Chain]
|
|
53
|
+
attr_reader :method_call_chain
|
|
54
|
+
|
|
55
|
+
# @return [String] The name of the method that this parameter belongs to
|
|
56
|
+
def method_name
|
|
57
|
+
@method_name ||= method_call.word
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# @return [Chain::Call]
|
|
61
|
+
def method_call
|
|
62
|
+
@method_call_chain.links.last
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# The index of the literal in the method call chain.
|
|
66
|
+
# @return [Integer]
|
|
67
|
+
def current_index
|
|
68
|
+
@current_index ||= method_call_chain.node.children[2..].index(literal.node)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# @return [::String, ::Symbol] The literal value of the parameter
|
|
72
|
+
# @sg-ignore false "return type could not be inferred"
|
|
73
|
+
def literal_value
|
|
74
|
+
literal.node.children.first
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -34,6 +34,7 @@ module Solargraph
|
|
|
34
34
|
autoload :ZSuper, 'solargraph/source/chain/z_super'
|
|
35
35
|
autoload :Hash, 'solargraph/source/chain/hash'
|
|
36
36
|
autoload :Array, 'solargraph/source/chain/array'
|
|
37
|
+
autoload :Parameter, 'solargraph/source/chain/parameter'
|
|
37
38
|
|
|
38
39
|
@@inference_stack = []
|
|
39
40
|
@@inference_depth = 0
|
|
@@ -182,6 +183,13 @@ module Solargraph
|
|
|
182
183
|
links.last.is_a?(Chain::Literal)
|
|
183
184
|
end
|
|
184
185
|
|
|
186
|
+
# @return [Boolean]
|
|
187
|
+
# @sg-ignore false "return type could not be inferred"
|
|
188
|
+
def require_parameter?
|
|
189
|
+
# @sg-ignore false "Unresolved call to require_parameter? on Solargraph::Source::Chain::Link"
|
|
190
|
+
links.last.is_a?(Chain::Parameter) && links.last.require_parameter?
|
|
191
|
+
end
|
|
192
|
+
|
|
185
193
|
def undefined?
|
|
186
194
|
links.any?(&:undefined?)
|
|
187
195
|
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solargraph
|
|
4
|
+
class Source
|
|
5
|
+
class NodeMap
|
|
6
|
+
# @param node [Parser::AST::Node]
|
|
7
|
+
def initialize node
|
|
8
|
+
@node = node
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def find_by_type type
|
|
12
|
+
node_map[type] || []
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def node_map
|
|
18
|
+
@node_map ||= flatten(@node).to_set.classify(&:type)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def flatten from
|
|
22
|
+
return [] unless from.is_a?(::Parser::AST::Node)
|
|
23
|
+
[from].concat(from.children.flat_map { |child| flatten child })
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -31,55 +31,18 @@ module Solargraph
|
|
|
31
31
|
|
|
32
32
|
# @return [Source::Chain]
|
|
33
33
|
def chain
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
# return Chain.new([Chain::Literal.new('Integer', Integer(phrase[0..-2])),
|
|
37
|
-
# Chain::UNDEFINED_CALL])
|
|
38
|
-
# end
|
|
39
|
-
if phrase.start_with?(':') && !phrase.start_with?('::')
|
|
40
|
-
return Chain.new([Chain::Literal.new('Symbol',
|
|
41
|
-
# @sg-ignore Need to add nil check here
|
|
42
|
-
phrase[1..].to_sym)])
|
|
43
|
-
end
|
|
44
|
-
if end_of_phrase.strip == '::' && source.code[Position.to_offset(
|
|
45
|
-
source.code, position
|
|
46
|
-
)].to_s.match?(/[a-z]/i)
|
|
47
|
-
return SourceChainer.chain(source,
|
|
48
|
-
Position.new(position.line,
|
|
49
|
-
position.character + 1))
|
|
50
|
-
end
|
|
34
|
+
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)
|
|
35
|
+
|
|
51
36
|
begin
|
|
52
|
-
|
|
53
|
-
# @type [::Parser::AST::Node, nil]
|
|
54
|
-
node = nil
|
|
55
|
-
# @type [::Parser::AST::Node, nil]
|
|
56
|
-
parent = nil
|
|
57
|
-
if !source.repaired? && source.parsed? && source.synchronized?
|
|
58
|
-
tree = source.tree_at(position.line, position.column)
|
|
59
|
-
node, parent = tree[0..2]
|
|
60
|
-
elsif source.parsed? && source.repaired? && end_of_phrase == '.'
|
|
61
|
-
node, parent = source.tree_at(fixed_position.line, fixed_position.column)[0..2]
|
|
62
|
-
# provide filename and line so that we can look up local variables there later
|
|
63
|
-
node = Parser.parse(fixed_phrase, source.filename, fixed_position.line) if node.nil?
|
|
64
|
-
elsif source.repaired?
|
|
65
|
-
node = Parser.parse(fixed_phrase, source.filename, fixed_position.line)
|
|
66
|
-
else
|
|
67
|
-
unless source.error_ranges.any? do |r|
|
|
68
|
-
r.nil? || r.include?(fixed_position)
|
|
69
|
-
end
|
|
70
|
-
node, parent = source.tree_at(fixed_position.line,
|
|
71
|
-
fixed_position.column)[0..2]
|
|
72
|
-
end
|
|
73
|
-
# Exception for positions that chain literal nodes in unsynchronized sources
|
|
74
|
-
node = nil unless source.synchronized? || !Parser.infer_literal_node_type(node).nil?
|
|
75
|
-
node = Parser.parse(fixed_phrase, source.filename, fixed_position.line) if node.nil?
|
|
76
|
-
end
|
|
37
|
+
node, parent, tree = node_from_position
|
|
77
38
|
rescue Parser::SyntaxError
|
|
78
39
|
return Chain.new([Chain::UNDEFINED_CALL])
|
|
79
40
|
end
|
|
41
|
+
|
|
80
42
|
return Chain.new([Chain::UNDEFINED_CALL]) if node.nil? || (node.type == :sym && !phrase.start_with?(':'))
|
|
81
|
-
|
|
82
|
-
chain = Parser.chain(node, source.filename, parent)
|
|
43
|
+
|
|
44
|
+
chain = Parser.chain(node, source.filename, parent, tree)
|
|
45
|
+
|
|
83
46
|
if source.repaired? || !source.parsed? || !source.synchronized?
|
|
84
47
|
if end_of_phrase.strip == '.'
|
|
85
48
|
chain.links.push Chain::UNDEFINED_CALL
|
|
@@ -94,6 +57,39 @@ module Solargraph
|
|
|
94
57
|
|
|
95
58
|
private
|
|
96
59
|
|
|
60
|
+
# @return [Array(Parser::AST::Node, Parser::AST::Node, Array<Parser::AST::Node>)]
|
|
61
|
+
# parent node, child node, and the tree at the position
|
|
62
|
+
def node_from_position
|
|
63
|
+
node = nil
|
|
64
|
+
parent = nil
|
|
65
|
+
tree = nil
|
|
66
|
+
|
|
67
|
+
if source.parsed? && !source.repaired? && source.synchronized?
|
|
68
|
+
tree = source.tree_at(position.line, position.column)
|
|
69
|
+
node, parent = tree[0..2]
|
|
70
|
+
elsif source.parsed? && source.repaired? && end_of_phrase == '.'
|
|
71
|
+
tree = source.tree_at(fixed_position.line, fixed_position.column)
|
|
72
|
+
node, parent = tree[0..2]
|
|
73
|
+
# provide filename and line so that we can look up local variables there later
|
|
74
|
+
node = Parser.parse(fixed_phrase, source.filename, fixed_position.line) if node.nil?
|
|
75
|
+
tree = source.tree_at(fixed_position.line, fixed_position.column) if node
|
|
76
|
+
elsif source.repaired?
|
|
77
|
+
node = Parser.parse(fixed_phrase, source.filename, fixed_position.line)
|
|
78
|
+
tree = source.tree_at(fixed_position.line, fixed_position.column) if node
|
|
79
|
+
else
|
|
80
|
+
unless source.error_ranges.any? { |r| r.nil? || r.include?(fixed_position) }
|
|
81
|
+
tree = source.tree_at(fixed_position.line, fixed_position.column)
|
|
82
|
+
node, parent = tree[0..2]
|
|
83
|
+
end
|
|
84
|
+
# Exception for positions that chain literal nodes in unsynchronized sources
|
|
85
|
+
node = nil unless source.synchronized? || !Parser.infer_literal_node_type(node).nil?
|
|
86
|
+
node = Parser.parse(fixed_phrase, source.filename, fixed_position.line) if node.nil?
|
|
87
|
+
tree = source.tree_at(fixed_position.line, fixed_position.column) if node
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
[node, parent, tree]
|
|
91
|
+
end
|
|
92
|
+
|
|
97
93
|
# @return [Position]
|
|
98
94
|
attr_reader :position
|
|
99
95
|
|
data/lib/solargraph/source.rb
CHANGED
|
@@ -12,6 +12,7 @@ module Solargraph
|
|
|
12
12
|
autoload :Cursor, 'solargraph/source/cursor'
|
|
13
13
|
autoload :Chain, 'solargraph/source/chain'
|
|
14
14
|
autoload :SourceChainer, 'solargraph/source/source_chainer'
|
|
15
|
+
autoload :NodeMap, 'solargraph/source/node_map'
|
|
15
16
|
|
|
16
17
|
include EncodingFixes
|
|
17
18
|
|
|
@@ -213,6 +214,21 @@ module Solargraph
|
|
|
213
214
|
end
|
|
214
215
|
end
|
|
215
216
|
|
|
217
|
+
# The RBS type in an rbs-inline type assertion, i.e., a `#: TYPE`
|
|
218
|
+
# comment that trails the node on its last line.
|
|
219
|
+
#
|
|
220
|
+
# @example
|
|
221
|
+
# x = [] #: Array[String]
|
|
222
|
+
#
|
|
223
|
+
# @param node [AST::Node]
|
|
224
|
+
# @return [String, nil]
|
|
225
|
+
def type_assertion_for node
|
|
226
|
+
rng = Range.from_node(node)
|
|
227
|
+
return nil if rng.nil?
|
|
228
|
+
rest = code_lines[rng.ending.line]&.[](rng.ending.column..)
|
|
229
|
+
rest&.[](/\A\s*#:(.*)$/, 1)&.strip
|
|
230
|
+
end
|
|
231
|
+
|
|
216
232
|
# A location representing the file in its entirety.
|
|
217
233
|
#
|
|
218
234
|
# @return [Location]
|
|
@@ -457,6 +473,7 @@ module Solargraph
|
|
|
457
473
|
# @return [String]
|
|
458
474
|
def code= val
|
|
459
475
|
@code_lines = nil
|
|
476
|
+
@node_map = nil
|
|
460
477
|
@finalized = false
|
|
461
478
|
@code = val
|
|
462
479
|
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solargraph
|
|
4
|
+
class SourceMap
|
|
5
|
+
class Call
|
|
6
|
+
attr_reader :source_map, :chain, :closure
|
|
7
|
+
|
|
8
|
+
# @param source_map [SourceMap]
|
|
9
|
+
# @param chain [Source::Chain]
|
|
10
|
+
# @param closure [Pin::Closure]
|
|
11
|
+
def initialize source_map, chain, closure
|
|
12
|
+
@source_map = source_map
|
|
13
|
+
@chain = chain
|
|
14
|
+
@closure = closure
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -13,14 +13,14 @@ module Solargraph
|
|
|
13
13
|
@cursor = cursor
|
|
14
14
|
closure_pin = closure
|
|
15
15
|
# @sg-ignore Need to add nil check here
|
|
16
|
-
if closure_pin.is_a?(Pin::Block) && !Solargraph::Range.from_node(closure_pin.receiver)
|
|
16
|
+
if closure_pin.is_a?(Pin::Block) && !Solargraph::Range.from_node(closure_pin.receiver)&.contain?(cursor.range.start)
|
|
17
17
|
closure_pin.rebind(api_map)
|
|
18
18
|
end
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
# @return [Array<Pin::Base>] Relevant pins for infering the type of the Cursor's position
|
|
22
22
|
def define
|
|
23
|
-
return [] if cursor.comment? || cursor.chain.literal?
|
|
23
|
+
return [] if cursor.comment? || cursor.chain.literal? || cursor.chain.require_parameter?
|
|
24
24
|
result = cursor.chain.define(api_map, closure, locals)
|
|
25
25
|
result.concat file_global_methods
|
|
26
26
|
if result.empty?
|
|
@@ -40,7 +40,8 @@ module Solargraph
|
|
|
40
40
|
# @return [Completion]
|
|
41
41
|
def complete
|
|
42
42
|
return package_completions([]) if !source_map.source.parsed? || cursor.string?
|
|
43
|
-
|
|
43
|
+
# TODO: Improve magic word comparsion == '<::Symbol>', too fragile
|
|
44
|
+
if cursor.chain.literal? && cursor.chain.links.last.word == '<::Symbol>'
|
|
44
45
|
return package_completions(api_map.get_symbols)
|
|
45
46
|
end
|
|
46
47
|
return Completion.new([], cursor.range) if cursor.chain.literal?
|
|
@@ -211,6 +212,7 @@ module Solargraph
|
|
|
211
212
|
result.concat api_map.get_constants('', cursor.start_of_constant? ? '' : context_pin.full_context.namespace, *gates) # .select { |pin| pin.name.start_with?(full) }
|
|
212
213
|
end
|
|
213
214
|
else
|
|
215
|
+
type = type.qualify(api_map, *gates)
|
|
214
216
|
result.concat api_map.get_constants(type.namespace,
|
|
215
217
|
cursor.start_of_constant? ? '' : context_pin.full_context.namespace, *gates)
|
|
216
218
|
end
|