solargraph 0.18.3 → 0.19.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/lib/solargraph/api_map/probe.rb +222 -0
- data/lib/solargraph/api_map/source_to_yard.rb +3 -3
- data/lib/solargraph/api_map/store.rb +135 -0
- data/lib/solargraph/api_map.rb +169 -609
- data/lib/solargraph/diagnostics/rubocop.rb +4 -4
- data/lib/solargraph/language_server/host.rb +53 -19
- data/lib/solargraph/language_server/message/extended/document.rb +1 -1
- data/lib/solargraph/language_server/message/extended/search.rb +1 -1
- data/lib/solargraph/language_server/message/method_not_found.rb +1 -1
- data/lib/solargraph/language_server/message/text_document/definition.rb +2 -15
- data/lib/solargraph/language_server/message/text_document/document_symbol.rb +2 -15
- data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +3 -15
- data/lib/solargraph/language_server/message_types.rb +10 -0
- data/lib/solargraph/language_server.rb +1 -0
- data/lib/solargraph/library.rb +8 -0
- data/lib/solargraph/node_methods.rb +6 -1
- data/lib/solargraph/page.rb +2 -1
- data/lib/solargraph/pin/attribute.rb +8 -12
- data/lib/solargraph/pin/base.rb +20 -95
- data/lib/solargraph/pin/base_variable.rb +15 -74
- data/lib/solargraph/pin/block.rb +21 -0
- data/lib/solargraph/pin/block_parameter.rb +30 -44
- data/lib/solargraph/pin/class_variable.rb +3 -0
- data/lib/solargraph/pin/constant.rb +4 -8
- data/lib/solargraph/pin/conversions.rb +4 -3
- data/lib/solargraph/pin/documenting.rb +27 -0
- data/lib/solargraph/pin/global_variable.rb +3 -0
- data/lib/solargraph/pin/instance_variable.rb +5 -4
- data/lib/solargraph/pin/local_variable.rb +8 -15
- data/lib/solargraph/pin/localized.rb +12 -0
- data/lib/solargraph/pin/method.rb +6 -67
- data/lib/solargraph/pin/method_parameter.rb +24 -11
- data/lib/solargraph/pin/namespace.rb +26 -35
- data/lib/solargraph/pin/reference.rb +15 -8
- data/lib/solargraph/pin/symbol.rb +34 -3
- data/lib/solargraph/pin/yard_object.rb +11 -4
- data/lib/solargraph/pin.rb +16 -2
- data/lib/solargraph/server.rb +2 -2
- data/lib/solargraph/source/change.rb +10 -13
- data/lib/solargraph/source/fragment.rb +42 -94
- data/lib/solargraph/source/location.rb +13 -0
- data/lib/solargraph/source/mapper.rb +426 -0
- data/lib/solargraph/source/position.rb +1 -0
- data/lib/solargraph/source/range.rb +11 -3
- data/lib/solargraph/source.rb +93 -284
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/views/_method.erb +59 -60
- data/lib/solargraph/views/_name_type_tag.erb +10 -0
- data/lib/solargraph/views/_namespace.erb +26 -26
- data/lib/solargraph/views/document.erb +23 -16
- data/lib/solargraph/views/layout.erb +38 -10
- data/lib/solargraph/views/search.erb +12 -11
- data/lib/solargraph/workspace/config.rb +27 -6
- data/lib/solargraph/workspace.rb +10 -2
- data/lib/solargraph.rb +10 -2
- data/lib/yard-solargraph.rb +3 -0
- metadata +25 -20
- data/lib/solargraph/pin/directed/attribute.rb +0 -20
- data/lib/solargraph/pin/directed/method.rb +0 -22
- data/lib/solargraph/pin/directed.rb +0 -9
- data/lib/solargraph/pin/parameter.rb +0 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7584a4d20d5562279e2e86887fe56a448776ba91
|
4
|
+
data.tar.gz: bd353cd7bf5140f80f0a53155bfc03cb0dc1d73b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eb78c3e91842800f4646fc5c9f96371914226c17a6b3350b1bf4f5ec2e4e9b00801fa39034b52c859ee63613abef3d453aa87d0a18235e6bcb1093d62573de1a
|
7
|
+
data.tar.gz: 4cef990fe1e4935f478cac2a4b24067ca668f2c83de44ffb9d09e5b3faf46179ef4c2ec9fccd88681f07a4921fb58b6d2dba1ce06fb500e04ac9ab77522e71ee
|
@@ -0,0 +1,222 @@
|
|
1
|
+
module Solargraph
|
2
|
+
class ApiMap
|
3
|
+
class Probe
|
4
|
+
class VirtualPin
|
5
|
+
attr_reader :return_type
|
6
|
+
def initialize return_type
|
7
|
+
@return_type = return_type
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# @return [Solargraph::ApiMap]
|
12
|
+
attr_reader :api_map
|
13
|
+
|
14
|
+
def initialize api_map
|
15
|
+
@api_map = api_map
|
16
|
+
end
|
17
|
+
|
18
|
+
# Get all matching pins for the signature.
|
19
|
+
#
|
20
|
+
# @return [Array<Pin::Base>]
|
21
|
+
def infer_signature_pins signature, context_pin, locals
|
22
|
+
return [] if signature.nil? or signature.empty?
|
23
|
+
base, rest = signature.split('.', 2)
|
24
|
+
return infer_word_pins(base, context_pin, locals) if rest.nil?
|
25
|
+
pins = infer_word_pins(base, context_pin, locals).map do |pin|
|
26
|
+
next pin unless pin.return_type.nil?
|
27
|
+
type = resolve_pin_type(pin)
|
28
|
+
VirtualPin.new(type)
|
29
|
+
end
|
30
|
+
return [] if pins.empty?
|
31
|
+
rest = rest.split('.')
|
32
|
+
last = rest.pop
|
33
|
+
rest.each do |meth|
|
34
|
+
found = nil
|
35
|
+
pins.each do |pin|
|
36
|
+
found = infer_method_name_pins(meth, pin)
|
37
|
+
next if found.empty?
|
38
|
+
pins = found
|
39
|
+
break
|
40
|
+
end
|
41
|
+
return [] if found.nil?
|
42
|
+
end
|
43
|
+
pins.each do |pin|
|
44
|
+
found = infer_method_name_pins(last, pin)
|
45
|
+
return found unless found.empty?
|
46
|
+
end
|
47
|
+
[]
|
48
|
+
end
|
49
|
+
|
50
|
+
# Get the return type for the signature.
|
51
|
+
#
|
52
|
+
# @return [String]
|
53
|
+
def infer_signature_type signature, context_pin, locals
|
54
|
+
pins = infer_signature_pins(signature, context_pin, locals)
|
55
|
+
pins.each do |pin|
|
56
|
+
type = resolve_pin_type(pin)
|
57
|
+
return qualify(type, pin.named_context) unless type.nil?
|
58
|
+
end
|
59
|
+
nil
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# Word search is ALWAYS internal
|
65
|
+
def infer_word_pins word, context_pin, locals
|
66
|
+
return [] if word.empty?
|
67
|
+
lvars = locals.select{|pin| pin.name == word}
|
68
|
+
return lvars unless lvars.empty?
|
69
|
+
namespace, scope = extract_namespace_and_scope_from_pin(context_pin)
|
70
|
+
return api_map.pins.select{|pin| word_matches_context?(word, namespace, scope, pin)} if variable_name?(word)
|
71
|
+
result = []
|
72
|
+
result.concat api_map.get_path_suggestions(word)
|
73
|
+
result.concat api_map.get_methods(namespace, scope: scope, visibility: [:public, :private, :protected]).select{|pin| pin.name == word} unless word.include?('::')
|
74
|
+
result.concat api_map.get_constants('', namespace).select{|pin| pin.name == word}
|
75
|
+
result
|
76
|
+
end
|
77
|
+
|
78
|
+
# Method name search is external by default
|
79
|
+
def infer_method_name_pins method_name, context_pin, internal = false
|
80
|
+
namespace, scope = extract_namespace_and_scope(context_pin.return_type)
|
81
|
+
visibility = [:public]
|
82
|
+
visibility.push :protected, :private if internal
|
83
|
+
result = api_map.get_methods(namespace, scope: scope, visibility: visibility).select{|pin| pin.name == method_name}
|
84
|
+
# @todo This needs more rules. Probably need to update YardObject for it.
|
85
|
+
return result if result.empty?
|
86
|
+
return result unless method_name == 'new' and result.first.path == 'Class#new'
|
87
|
+
result.unshift virtual_new_pin(result.first, context_pin)
|
88
|
+
result
|
89
|
+
end
|
90
|
+
|
91
|
+
# Word and context matching rules for ApiMap source pins.
|
92
|
+
#
|
93
|
+
# @return [Boolean]
|
94
|
+
def word_matches_context? word, namespace, scope, pin
|
95
|
+
return false unless word == pin.name
|
96
|
+
return true if pin.kind == Pin::NAMESPACE and pin.path == namespace and scope == :class
|
97
|
+
return true if pin.kind == Pin::METHOD and pin.namespace == namespace and pin.scope == scope
|
98
|
+
# @todo Handle instance variables, class variables, etc. in various ways
|
99
|
+
pin.namespace == namespace and pin.scope == scope
|
100
|
+
end
|
101
|
+
|
102
|
+
# Fully qualify the namespace in a type.
|
103
|
+
#
|
104
|
+
# @return [String]
|
105
|
+
def qualify type, context
|
106
|
+
rns, rsc = extract_namespace_and_scope(type)
|
107
|
+
res = api_map.qualify(rns, context)
|
108
|
+
return res if rsc == :instance
|
109
|
+
type.sub(/<#{rns}>/, "<#{res}>")
|
110
|
+
end
|
111
|
+
|
112
|
+
# Extract a namespace from a type.
|
113
|
+
#
|
114
|
+
# @example
|
115
|
+
# extract_namespace('String') => 'String'
|
116
|
+
# extract_namespace('Class<String>') => 'String'
|
117
|
+
#
|
118
|
+
# @return [String]
|
119
|
+
def extract_namespace type
|
120
|
+
extract_namespace_and_scope(type)[0]
|
121
|
+
end
|
122
|
+
|
123
|
+
# Extract a namespace and a scope (:instance or :class) from a type.
|
124
|
+
#
|
125
|
+
# @example
|
126
|
+
# extract_namespace('String') #=> ['String', :instance]
|
127
|
+
# extract_namespace('Class<String>') #=> ['String', :class]
|
128
|
+
# extract_namespace('Module<Enumerable') #=> ['Enumberable', :class]
|
129
|
+
#
|
130
|
+
# @return [Array] The namespace (String) and scope (Symbol).
|
131
|
+
def extract_namespace_and_scope type
|
132
|
+
scope = :instance
|
133
|
+
result = type.to_s.gsub(/<.*$/, '')
|
134
|
+
if (result == 'Class' or result == 'Module') and type.include?('<')
|
135
|
+
result = type.match(/<([a-z0-9:_]*)/i)[1]
|
136
|
+
scope = :class
|
137
|
+
end
|
138
|
+
[result, scope]
|
139
|
+
end
|
140
|
+
|
141
|
+
# Extract a namespace and a scope from a pin. For now, the pin must
|
142
|
+
# be either a method or a namespace. It probably makes sense to support
|
143
|
+
# blocks at some point.
|
144
|
+
#
|
145
|
+
# @return [Array] The namespace (String) and scope (Symbol).
|
146
|
+
def extract_namespace_and_scope_from_pin pin
|
147
|
+
return [pin.namespace, pin.scope] if pin.kind == Pin::METHOD
|
148
|
+
return [pin.namespace, :class] if pin.kind == Pin::NAMESPACE
|
149
|
+
# @todo Is :class appropriate for blocks?
|
150
|
+
return [pin.namespace, :class] if pin.kind == Pin::BLOCK
|
151
|
+
raise "Unable to extract namespace and scope from #{pin.path}"
|
152
|
+
end
|
153
|
+
|
154
|
+
# Determine whether or not the word represents a variable. This method
|
155
|
+
# is used to keep the probe from performing unnecessary constant and
|
156
|
+
# method searches.
|
157
|
+
#
|
158
|
+
# @return [Boolean]
|
159
|
+
def variable_name? word
|
160
|
+
word.start_with?('@') or word.start_with?('$')
|
161
|
+
end
|
162
|
+
|
163
|
+
# Create a `new` pin to facilitate type inference. This is necessary for
|
164
|
+
# classes from YARD and classes in the namespace that do not have an
|
165
|
+
# `initialize` method.
|
166
|
+
#
|
167
|
+
# @return [Pin::Method]
|
168
|
+
def virtual_new_pin new_pin, context_pin
|
169
|
+
pin = Pin::Method.new(new_pin.location, new_pin.namespace, new_pin.name, new_pin.docstring, new_pin.scope, new_pin.visibility, new_pin.parameters)
|
170
|
+
# @todo Smelly instance variable access.
|
171
|
+
pin.instance_variable_set(:@return_type, context_pin.path)
|
172
|
+
pin
|
173
|
+
end
|
174
|
+
|
175
|
+
def resolve_pin_type pin
|
176
|
+
pin.return_type
|
177
|
+
return pin.return_type unless pin.return_type.nil?
|
178
|
+
return resolve_block_parameter(pin) if pin.kind == Pin::BLOCK_PARAMETER
|
179
|
+
return resolve_variable(pin) if pin.variable?
|
180
|
+
nil
|
181
|
+
end
|
182
|
+
|
183
|
+
def resolve_block_parameter pin
|
184
|
+
return pin.return_type unless pin.return_type.nil?
|
185
|
+
signature = pin.block.receiver
|
186
|
+
# @todo Not sure if assuming the first pin is good here
|
187
|
+
meth = @api_map.probe.infer_signature_pins(signature, pin.block, []).first
|
188
|
+
return nil if meth.nil?
|
189
|
+
if (Solargraph::CoreFills::METHODS_WITH_YIELDPARAM_SUBTYPES.include?(meth.path))
|
190
|
+
base = signature.split('.')[0..-2].join('.')
|
191
|
+
return nil if base.nil? or base.empty?
|
192
|
+
# @todo Not sure if assuming the first pin is good here
|
193
|
+
bmeth = @api_map.probe.infer_signature_pins(base, pin.block, []).first
|
194
|
+
return nil if bmeth.nil?
|
195
|
+
subtypes = get_subtypes(bmeth.return_type)
|
196
|
+
return subtypes[0]
|
197
|
+
else
|
198
|
+
unless meth.docstring.nil?
|
199
|
+
yps = meth.docstring.tags(:yieldparam)
|
200
|
+
unless yps[pin.index].nil? or yps[pin.index].types.nil? or yps[pin.index].types.empty?
|
201
|
+
return yps[pin.index].types[0]
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
nil
|
206
|
+
end
|
207
|
+
|
208
|
+
def resolve_variable(pin)
|
209
|
+
return nil if pin.nil_assignment?
|
210
|
+
# @todo Do we need the locals here?
|
211
|
+
return infer_signature_type(pin.signature, pin.context, [])
|
212
|
+
end
|
213
|
+
|
214
|
+
def get_subtypes type
|
215
|
+
return nil if type.nil?
|
216
|
+
match = type.match(/<([a-z0-9_:, ]*)>/i)
|
217
|
+
return [] if match.nil?
|
218
|
+
match[1].split(',').map(&:strip)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
@@ -25,7 +25,7 @@ module Solargraph
|
|
25
25
|
code_object_map[pin.path] ||= YARD::CodeObjects::ModuleObject.new(root_code_object, pin.path)
|
26
26
|
end
|
27
27
|
code_object_map[pin.path].docstring = pin.docstring unless pin.docstring.nil?
|
28
|
-
code_object_map[pin.path].files.push pin.
|
28
|
+
code_object_map[pin.path].files.push pin.location.filename
|
29
29
|
end
|
30
30
|
s.namespace_pins.each do |pin|
|
31
31
|
pin.include_references.each do |ref|
|
@@ -35,13 +35,13 @@ module Solargraph
|
|
35
35
|
s.attribute_pins.each do |pin|
|
36
36
|
code_object_map[pin.path] ||= YARD::CodeObjects::MethodObject.new(code_object_at(pin.namespace), pin.name, :instance)
|
37
37
|
code_object_map[pin.path].docstring = pin.docstring unless pin.docstring.nil?
|
38
|
-
code_object_map[pin.path].files.push pin.
|
38
|
+
code_object_map[pin.path].files.push pin.location.filename
|
39
39
|
#code_object_map[pin.path].parameters = []
|
40
40
|
end
|
41
41
|
s.method_pins.each do |pin|
|
42
42
|
code_object_map[pin.path] ||= YARD::CodeObjects::MethodObject.new(code_object_at(pin.namespace), pin.name, pin.scope)
|
43
43
|
code_object_map[pin.path].docstring = pin.docstring unless pin.docstring.nil?
|
44
|
-
code_object_map[pin.path].files.push pin.
|
44
|
+
code_object_map[pin.path].files.push pin.location.filename
|
45
45
|
code_object_map[pin.path].parameters = pin.parameters.map do |p|
|
46
46
|
n = p.match(/^[a-z0-9_]*:?/i)[0]
|
47
47
|
v = nil
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Solargraph
|
4
|
+
class ApiMap
|
5
|
+
class Store
|
6
|
+
# @param sources [Solargraph::Source]
|
7
|
+
def initialize sources
|
8
|
+
update *sources
|
9
|
+
index
|
10
|
+
end
|
11
|
+
|
12
|
+
def pins
|
13
|
+
@pins ||= []
|
14
|
+
end
|
15
|
+
|
16
|
+
def remove *sources
|
17
|
+
sources.each do |source|
|
18
|
+
pins.delete_if { |pin| pin.filename == source.filename }
|
19
|
+
symbols.delete_if { |pin| pin.filename == source.filename }
|
20
|
+
end
|
21
|
+
index
|
22
|
+
end
|
23
|
+
|
24
|
+
def update *sources
|
25
|
+
sources.each do |source|
|
26
|
+
pins.delete_if { |pin| pin.filename == source.filename }
|
27
|
+
symbols.delete_if { |pin| pin.filename == source.filename }
|
28
|
+
pins.concat source.pins
|
29
|
+
symbols.concat source.symbols
|
30
|
+
end
|
31
|
+
index
|
32
|
+
end
|
33
|
+
|
34
|
+
def get_constants fqns, visibility = [:public]
|
35
|
+
namespace_pins(fqns).select { |pin|
|
36
|
+
!pin.name.empty? and (pin.kind == Pin::NAMESPACE or pin.kind == Pin::CONSTANT) and visibility.include?(pin.visibility)
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_methods fqns, scope: :instance, visibility: [:public]
|
41
|
+
namespace_pins(fqns).select{ |pin|
|
42
|
+
pin.kind == Pin::METHOD and (pin.scope == scope or fqns == '') and visibility.include?(pin.visibility)
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
def get_attrs fqns
|
47
|
+
namespace_pins(fqns).select{ |pin| pin.kind == Pin::ATTRIBUTE }
|
48
|
+
end
|
49
|
+
|
50
|
+
def get_superclass fqns
|
51
|
+
fqns_pins(fqns).each do |pin|
|
52
|
+
return pin.superclass_reference.name unless pin.superclass_reference.nil?
|
53
|
+
end
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
|
57
|
+
def get_includes fqns
|
58
|
+
result = []
|
59
|
+
fqns_pins(fqns).each do |pin|
|
60
|
+
result.concat pin.include_references.map(&:name)
|
61
|
+
end
|
62
|
+
result
|
63
|
+
end
|
64
|
+
|
65
|
+
def get_extends fqns
|
66
|
+
result = []
|
67
|
+
fqns_pins(fqns).each do |pin|
|
68
|
+
result.concat pin.extend_references.map(&:name)
|
69
|
+
end
|
70
|
+
result
|
71
|
+
end
|
72
|
+
|
73
|
+
def get_path_pins path
|
74
|
+
base = path.sub(/(#|\.|::)[a-z0-9_]*(\?|\!)?$/i, '')
|
75
|
+
base = '' if base == path
|
76
|
+
namespace_pins(base).select{ |pin| pin.path == path }
|
77
|
+
end
|
78
|
+
|
79
|
+
def get_instance_variables(fqns, scope = :instance)
|
80
|
+
namespace_pins(fqns).select{|pin| pin.kind == Pin::INSTANCE_VARIABLE and pin.scope == scope}
|
81
|
+
end
|
82
|
+
|
83
|
+
def get_symbols
|
84
|
+
symbols.uniq(&:name)
|
85
|
+
end
|
86
|
+
|
87
|
+
def namespace_exists?(fqns)
|
88
|
+
fqns_pins(fqns).any?
|
89
|
+
end
|
90
|
+
|
91
|
+
def namespaces
|
92
|
+
@namespaces ||= Set.new
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def fqns_pins fqns
|
98
|
+
# @todo We probably want to ignore '' namespace here
|
99
|
+
return [] if fqns.nil? #or fqns.empty?
|
100
|
+
if fqns.include?('::')
|
101
|
+
parts = fqns.split('::')
|
102
|
+
name = parts.pop
|
103
|
+
base = parts.join('::')
|
104
|
+
else
|
105
|
+
base = ''
|
106
|
+
name = fqns
|
107
|
+
end
|
108
|
+
namespace_pins(base).select{|pin| pin.name == name and pin.kind == Pin::NAMESPACE}
|
109
|
+
end
|
110
|
+
|
111
|
+
def symbols
|
112
|
+
@symbols ||= []
|
113
|
+
end
|
114
|
+
|
115
|
+
def namespace_pins name
|
116
|
+
namespace_map[name] || []
|
117
|
+
end
|
118
|
+
|
119
|
+
def namespace_map
|
120
|
+
@namespace_map ||= {}
|
121
|
+
end
|
122
|
+
|
123
|
+
def index
|
124
|
+
namespace_map.clear
|
125
|
+
namespaces.clear
|
126
|
+
namespace_map[''] = []
|
127
|
+
pins.each do |pin|
|
128
|
+
namespace_map[pin.namespace] ||= []
|
129
|
+
namespace_map[pin.namespace].push pin
|
130
|
+
namespaces.add pin.path if pin.kind == Pin::NAMESPACE and !pin.path.empty?
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|