solargraph 0.54.0 → 0.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +58 -0
- data/lib/solargraph/api_map/cache.rb +10 -1
- data/lib/solargraph/api_map/index.rb +167 -0
- data/lib/solargraph/api_map/store.rb +75 -122
- data/lib/solargraph/api_map.rb +109 -41
- data/lib/solargraph/bench.rb +17 -1
- data/lib/solargraph/complex_type/type_methods.rb +17 -11
- data/lib/solargraph/complex_type/unique_type.rb +181 -17
- data/lib/solargraph/complex_type.rb +103 -24
- data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +51 -0
- data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +100 -0
- data/lib/solargraph/convention/struct_definition.rb +101 -0
- data/lib/solargraph/convention.rb +2 -0
- data/lib/solargraph/doc_map.rb +43 -18
- data/lib/solargraph/equality.rb +33 -0
- data/lib/solargraph/language_server/host/message_worker.rb +51 -5
- data/lib/solargraph/language_server/host.rb +13 -11
- data/lib/solargraph/language_server/message/base.rb +19 -12
- data/lib/solargraph/language_server/message/initialize.rb +3 -1
- data/lib/solargraph/language_server/message/text_document/completion.rb +0 -3
- data/lib/solargraph/language_server/message/text_document/definition.rb +3 -3
- data/lib/solargraph/language_server/message/text_document/document_symbol.rb +3 -3
- data/lib/solargraph/language_server/message/text_document/formatting.rb +4 -0
- data/lib/solargraph/language_server/message/text_document/hover.rb +1 -1
- data/lib/solargraph/language_server/message/text_document/type_definition.rb +3 -3
- data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +2 -2
- data/lib/solargraph/language_server/progress.rb +19 -2
- data/lib/solargraph/library.rb +31 -41
- data/lib/solargraph/location.rb +29 -1
- data/lib/solargraph/parser/comment_ripper.rb +11 -6
- data/lib/solargraph/parser/flow_sensitive_typing.rb +226 -0
- data/lib/solargraph/parser/node_methods.rb +15 -1
- data/lib/solargraph/parser/parser_gem/class_methods.rb +11 -6
- data/lib/solargraph/parser/parser_gem/node_chainer.rb +10 -10
- data/lib/solargraph/parser/parser_gem/node_methods.rb +6 -4
- data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +21 -0
- data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +23 -19
- data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +21 -1
- data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +21 -0
- data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +8 -2
- data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +26 -5
- data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +41 -0
- data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +1 -1
- data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +28 -0
- data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +28 -0
- data/lib/solargraph/parser/parser_gem/node_processors.rb +10 -0
- data/lib/solargraph/parser.rb +3 -5
- data/lib/solargraph/pin/base.rb +47 -17
- data/lib/solargraph/pin/base_variable.rb +11 -4
- data/lib/solargraph/pin/block.rb +8 -26
- data/lib/solargraph/pin/breakable.rb +9 -0
- data/lib/solargraph/pin/callable.rb +147 -0
- data/lib/solargraph/pin/closure.rb +8 -3
- data/lib/solargraph/pin/common.rb +2 -6
- data/lib/solargraph/pin/conversions.rb +3 -2
- data/lib/solargraph/pin/instance_variable.rb +2 -2
- data/lib/solargraph/pin/local_variable.rb +7 -1
- data/lib/solargraph/pin/method.rb +76 -48
- data/lib/solargraph/pin/namespace.rb +14 -11
- data/lib/solargraph/pin/parameter.rb +24 -17
- data/lib/solargraph/pin/proxy_type.rb +13 -7
- data/lib/solargraph/pin/signature.rb +3 -129
- data/lib/solargraph/pin/until.rb +18 -0
- data/lib/solargraph/pin/while.rb +18 -0
- data/lib/solargraph/pin.rb +7 -1
- data/lib/solargraph/position.rb +7 -0
- data/lib/solargraph/range.rb +9 -4
- data/lib/solargraph/rbs_map/conversions.rb +84 -45
- data/lib/solargraph/rbs_map/core_fills.rb +16 -9
- data/lib/solargraph/rbs_map.rb +1 -0
- data/lib/solargraph/shell.rb +19 -2
- data/lib/solargraph/source/chain/array.rb +10 -8
- 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 +125 -61
- data/lib/solargraph/source/chain/constant.rb +1 -1
- data/lib/solargraph/source/chain/hash.rb +8 -2
- data/lib/solargraph/source/chain/if.rb +5 -0
- data/lib/solargraph/source/chain/link.rb +28 -5
- data/lib/solargraph/source/chain/literal.rb +27 -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 +131 -63
- data/lib/solargraph/source/cursor.rb +3 -2
- data/lib/solargraph/source/source_chainer.rb +2 -2
- data/lib/solargraph/source.rb +104 -86
- data/lib/solargraph/source_map/clip.rb +8 -6
- data/lib/solargraph/source_map/data.rb +30 -0
- data/lib/solargraph/source_map.rb +28 -16
- data/lib/solargraph/type_checker/checks.rb +4 -0
- data/lib/solargraph/type_checker/rules.rb +6 -1
- data/lib/solargraph/type_checker.rb +48 -21
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/views/environment.erb +3 -5
- data/lib/solargraph/workspace/config.rb +7 -3
- data/lib/solargraph/workspace.rb +1 -1
- data/lib/solargraph/yard_map/mapper/to_constant.rb +1 -0
- data/lib/solargraph/yard_map/mapper/to_method.rb +42 -15
- data/lib/solargraph/yard_map/mapper/to_namespace.rb +1 -0
- data/lib/solargraph/yard_map/mapper.rb +1 -0
- data/lib/solargraph/yardoc.rb +1 -1
- data/lib/solargraph.rb +1 -0
- data/solargraph.gemspec +5 -5
- metadata +41 -25
|
@@ -5,10 +5,17 @@ require 'solargraph/source/chain/link'
|
|
|
5
5
|
|
|
6
6
|
module Solargraph
|
|
7
7
|
class Source
|
|
8
|
-
#
|
|
9
|
-
#
|
|
8
|
+
#
|
|
9
|
+
# Represents an expression as a single call chain at the parse
|
|
10
|
+
# tree level, made up of constants, variables, and method calls,
|
|
11
|
+
# each represented as a Link object.
|
|
12
|
+
#
|
|
13
|
+
# Computes Pins and/or ComplexTypes representing the interpreted
|
|
14
|
+
# expression.
|
|
10
15
|
#
|
|
11
16
|
class Chain
|
|
17
|
+
include Equality
|
|
18
|
+
|
|
12
19
|
autoload :Link, 'solargraph/source/chain/link'
|
|
13
20
|
autoload :Call, 'solargraph/source/chain/call'
|
|
14
21
|
autoload :QCall, 'solargraph/source/chain/q_call'
|
|
@@ -32,7 +39,7 @@ module Solargraph
|
|
|
32
39
|
@@inference_invalidation_key = nil
|
|
33
40
|
@@inference_cache = {}
|
|
34
41
|
|
|
35
|
-
UNDEFINED_CALL = Chain::Call.new('<undefined>')
|
|
42
|
+
UNDEFINED_CALL = Chain::Call.new('<undefined>', nil)
|
|
36
43
|
UNDEFINED_CONSTANT = Chain::Constant.new('<undefined>')
|
|
37
44
|
|
|
38
45
|
# @return [::Array<Source::Chain::Link>]
|
|
@@ -40,6 +47,11 @@ module Solargraph
|
|
|
40
47
|
|
|
41
48
|
attr_reader :node
|
|
42
49
|
|
|
50
|
+
# @sg-ignore Fix "Not enough arguments to Module#protected"
|
|
51
|
+
protected def equality_fields
|
|
52
|
+
[links, node]
|
|
53
|
+
end
|
|
54
|
+
|
|
43
55
|
# @param node [Parser::AST::Node, nil]
|
|
44
56
|
# @param links [::Array<Chain::Link>]
|
|
45
57
|
# @param splat [Boolean]
|
|
@@ -61,54 +73,95 @@ module Solargraph
|
|
|
61
73
|
@base ||= Chain.new(links[0..-2])
|
|
62
74
|
end
|
|
63
75
|
|
|
64
|
-
#
|
|
65
|
-
#
|
|
66
|
-
# @param
|
|
76
|
+
# Determine potential Pins returned by this chain of words
|
|
77
|
+
#
|
|
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.
|
|
67
90
|
#
|
|
68
|
-
#
|
|
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'.
|
|
97
|
+
#
|
|
98
|
+
# @param locals [::Array<Pin::LocalVariable>] Any local
|
|
99
|
+
# variables / method parameters etc visible by the statement
|
|
100
|
+
#
|
|
101
|
+
# @return [::Array<Pin::Base>] Pins representing possible return
|
|
102
|
+
# types of this method.
|
|
69
103
|
def define api_map, name_pin, locals
|
|
70
104
|
return [] if undefined?
|
|
105
|
+
|
|
106
|
+
# working_pin is the surrounding closure pin for the link
|
|
107
|
+
# being processed, whose #binder method will provide the LHS /
|
|
108
|
+
# 'self type' of the next link (same as the #return_type method
|
|
109
|
+
# --the type of the result so far).
|
|
110
|
+
#
|
|
111
|
+
# @todo ProxyType uses 'type' for the binder, but '
|
|
71
112
|
working_pin = name_pin
|
|
72
113
|
links[0..-2].each do |link|
|
|
73
114
|
pins = link.resolve(api_map, working_pin, locals)
|
|
74
|
-
type =
|
|
75
|
-
|
|
76
|
-
|
|
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)
|
|
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}" }
|
|
77
127
|
end
|
|
78
|
-
links.last.last_context =
|
|
128
|
+
links.last.last_context = working_pin
|
|
79
129
|
links.last.resolve(api_map, working_pin, locals)
|
|
80
130
|
end
|
|
81
131
|
|
|
82
132
|
# @param api_map [ApiMap]
|
|
83
133
|
# @param name_pin [Pin::Base]
|
|
84
|
-
# @param locals [::
|
|
134
|
+
# @param locals [::Array<Pin::LocalVariable>]
|
|
85
135
|
# @return [ComplexType]
|
|
86
136
|
# @sg-ignore
|
|
87
137
|
def infer api_map, name_pin, locals
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
@@inference_cache = {}
|
|
138
|
+
cache_key = [node, node&.location, links, name_pin&.return_type, locals]
|
|
139
|
+
if @@inference_invalidation_key == api_map.hash
|
|
140
|
+
cached = @@inference_cache[cache_key]
|
|
141
|
+
return cached if cached
|
|
142
|
+
else
|
|
94
143
|
@@inference_invalidation_key = api_map.hash
|
|
144
|
+
@@inference_cache = {}
|
|
95
145
|
end
|
|
96
|
-
|
|
97
|
-
|
|
146
|
+
out = infer_uncached(api_map, name_pin, locals).downcast_to_literal_if_possible
|
|
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}" }
|
|
148
|
+
@@inference_cache[cache_key] = out
|
|
98
149
|
end
|
|
99
150
|
|
|
100
151
|
# @param api_map [ApiMap]
|
|
101
152
|
# @param name_pin [Pin::Base]
|
|
102
|
-
# @param locals [::
|
|
153
|
+
# @param locals [::Array<Pin::LocalVariable>]
|
|
103
154
|
# @return [ComplexType]
|
|
104
155
|
def infer_uncached api_map, name_pin, locals
|
|
105
|
-
from_here = base.infer(api_map, name_pin, locals) unless links.length == 1
|
|
106
|
-
if from_here
|
|
107
|
-
name_pin = name_pin.proxy(from_here)
|
|
108
|
-
end
|
|
109
156
|
pins = define(api_map, name_pin, locals)
|
|
110
|
-
|
|
111
|
-
|
|
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
|
|
112
165
|
end
|
|
113
166
|
|
|
114
167
|
# @return [Boolean]
|
|
@@ -137,6 +190,18 @@ module Solargraph
|
|
|
137
190
|
links.any?(&:nullable?)
|
|
138
191
|
end
|
|
139
192
|
|
|
193
|
+
include Logging
|
|
194
|
+
|
|
195
|
+
def desc
|
|
196
|
+
links.map(&:desc).to_s
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def to_s
|
|
200
|
+
desc
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
include Logging
|
|
204
|
+
|
|
140
205
|
private
|
|
141
206
|
|
|
142
207
|
# @param pins [::Array<Pin::Base>]
|
|
@@ -144,16 +209,18 @@ module Solargraph
|
|
|
144
209
|
# @param api_map [ApiMap]
|
|
145
210
|
# @param locals [::Enumerable<Pin::LocalVariable>]
|
|
146
211
|
# @return [ComplexType]
|
|
147
|
-
def
|
|
148
|
-
|
|
212
|
+
def infer_from_definitions pins, context, api_map, locals
|
|
213
|
+
# @type [::Array<ComplexType>]
|
|
214
|
+
types = []
|
|
215
|
+
unresolved_pins = []
|
|
149
216
|
# @todo this param tag shouldn't be needed to probe the type
|
|
150
217
|
# @todo ...but given it is needed, typecheck should complain that it is needed
|
|
151
218
|
# @param pin [Pin::Base]
|
|
152
219
|
pins.each do |pin|
|
|
153
220
|
# Avoid infinite recursion
|
|
154
|
-
next if @@inference_stack.include?(pin
|
|
221
|
+
next if @@inference_stack.include?(pin)
|
|
155
222
|
|
|
156
|
-
@@inference_stack.push pin
|
|
223
|
+
@@inference_stack.push pin
|
|
157
224
|
type = pin.typify(api_map)
|
|
158
225
|
@@inference_stack.pop
|
|
159
226
|
if type.defined?
|
|
@@ -161,45 +228,46 @@ module Solargraph
|
|
|
161
228
|
# @todo even at strong, no typechecking complaint
|
|
162
229
|
# happens when a [Pin::Base,nil] is passed into a method
|
|
163
230
|
# that accepts only [Pin::Namespace] as an argument
|
|
164
|
-
type = type.resolve_generics(pin.closure, context.
|
|
165
|
-
end
|
|
166
|
-
if type.defined?
|
|
167
|
-
possibles.push type
|
|
168
|
-
break if pin.is_a?(Pin::Method)
|
|
231
|
+
type = type.resolve_generics(pin.closure, context.binder)
|
|
169
232
|
end
|
|
233
|
+
types << type
|
|
234
|
+
else
|
|
235
|
+
unresolved_pins << pin
|
|
170
236
|
end
|
|
171
237
|
end
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
@@inference_depth += 1
|
|
177
|
-
# @param pin [Pin::Base]
|
|
178
|
-
pins.each do |pin|
|
|
179
|
-
# Avoid infinite recursion
|
|
180
|
-
next if @@inference_stack.include?(pin.identity)
|
|
181
|
-
|
|
182
|
-
@@inference_stack.push pin.identity
|
|
183
|
-
type = pin.probe(api_map)
|
|
184
|
-
@@inference_stack.pop
|
|
185
|
-
if type.defined?
|
|
186
|
-
possibles.push type
|
|
187
|
-
break if pin.is_a?(Pin::Method)
|
|
188
|
-
end
|
|
189
|
-
end
|
|
190
|
-
@@inference_depth -= 1
|
|
238
|
+
|
|
239
|
+
# Limit method inference recursion
|
|
240
|
+
if @@inference_depth >= 10 && pins.first.is_a?(Pin::Method)
|
|
241
|
+
return ComplexType::UNDEFINED
|
|
191
242
|
end
|
|
192
|
-
return ComplexType::UNDEFINED if possibles.empty?
|
|
193
243
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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
|
|
199
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
|
|
200
268
|
return type if context.nil? || context.return_type.undefined?
|
|
201
269
|
|
|
202
|
-
type.
|
|
270
|
+
type.self_to_type(context.return_type)
|
|
203
271
|
end
|
|
204
272
|
|
|
205
273
|
# @param type [ComplexType]
|
|
@@ -207,7 +275,7 @@ module Solargraph
|
|
|
207
275
|
def maybe_nil type
|
|
208
276
|
return type if type.undefined? || type.void? || type.nullable?
|
|
209
277
|
return type unless nullable?
|
|
210
|
-
ComplexType.
|
|
278
|
+
ComplexType.new(type.items + [ComplexType::NIL])
|
|
211
279
|
end
|
|
212
280
|
end
|
|
213
281
|
end
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module Solargraph
|
|
4
4
|
class Source
|
|
5
|
-
# Information about a
|
|
6
|
-
# there.
|
|
5
|
+
# Information about a single Position in a Source, including the
|
|
6
|
+
# word located there.
|
|
7
7
|
#
|
|
8
8
|
class Cursor
|
|
9
9
|
# @return [Position]
|
|
@@ -35,6 +35,7 @@ module Solargraph
|
|
|
35
35
|
# The part of the word before the current position. Given the text
|
|
36
36
|
# `foo.bar`, the start_of_word at position(0, 6) is `ba`.
|
|
37
37
|
#
|
|
38
|
+
# @sg-ignore Improve resolution of String#match below
|
|
38
39
|
# @return [String]
|
|
39
40
|
def start_of_word
|
|
40
41
|
@start_of_word ||= begin
|
|
@@ -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?('..')
|
data/lib/solargraph/source.rb
CHANGED
|
@@ -19,13 +19,22 @@ module Solargraph
|
|
|
19
19
|
attr_reader :filename
|
|
20
20
|
|
|
21
21
|
# @return [String]
|
|
22
|
-
|
|
22
|
+
def code
|
|
23
|
+
finalize
|
|
24
|
+
@code
|
|
25
|
+
end
|
|
23
26
|
|
|
24
|
-
# @return [Parser::AST::Node]
|
|
25
|
-
|
|
27
|
+
# @return [Parser::AST::Node, nil]
|
|
28
|
+
def node
|
|
29
|
+
finalize
|
|
30
|
+
@node
|
|
31
|
+
end
|
|
26
32
|
|
|
27
33
|
# @return [Hash{Integer => Array<String>}]
|
|
28
|
-
|
|
34
|
+
def comments
|
|
35
|
+
finalize
|
|
36
|
+
@comments
|
|
37
|
+
end
|
|
29
38
|
|
|
30
39
|
# @todo Deprecate?
|
|
31
40
|
# @return [Integer]
|
|
@@ -39,17 +48,6 @@ module Solargraph
|
|
|
39
48
|
@repaired = code
|
|
40
49
|
@filename = filename
|
|
41
50
|
@version = version
|
|
42
|
-
@domains = []
|
|
43
|
-
begin
|
|
44
|
-
@node, @comments = Solargraph::Parser.parse_with_comments(@code, filename)
|
|
45
|
-
@parsed = true
|
|
46
|
-
rescue Parser::SyntaxError, EncodingError => e
|
|
47
|
-
@node = nil
|
|
48
|
-
@comments = {}
|
|
49
|
-
@parsed = false
|
|
50
|
-
ensure
|
|
51
|
-
@code.freeze
|
|
52
|
-
end
|
|
53
51
|
end
|
|
54
52
|
|
|
55
53
|
# @param range [Solargraph::Range]
|
|
@@ -64,9 +62,9 @@ module Solargraph
|
|
|
64
62
|
# @param c2 [Integer]
|
|
65
63
|
# @return [String]
|
|
66
64
|
def from_to l1, c1, l2, c2
|
|
67
|
-
b = Solargraph::Position.line_char_to_offset(
|
|
68
|
-
e = Solargraph::Position.line_char_to_offset(
|
|
69
|
-
|
|
65
|
+
b = Solargraph::Position.line_char_to_offset(code, l1, c1)
|
|
66
|
+
e = Solargraph::Position.line_char_to_offset(code, l2, c2)
|
|
67
|
+
code[b..e-1]
|
|
70
68
|
end
|
|
71
69
|
|
|
72
70
|
# Get the nearest node that contains the specified index.
|
|
@@ -85,10 +83,9 @@ module Solargraph
|
|
|
85
83
|
# @param column [Integer]
|
|
86
84
|
# @return [Array<AST::Node>]
|
|
87
85
|
def tree_at(line, column)
|
|
88
|
-
# offset = Position.line_char_to_offset(@code, line, column)
|
|
89
86
|
position = Position.new(line, column)
|
|
90
87
|
stack = []
|
|
91
|
-
inner_tree_at
|
|
88
|
+
inner_tree_at node, position, stack
|
|
92
89
|
stack
|
|
93
90
|
end
|
|
94
91
|
|
|
@@ -104,68 +101,53 @@ module Solargraph
|
|
|
104
101
|
@version = updater.version
|
|
105
102
|
return self
|
|
106
103
|
end
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
104
|
+
Source.new(@code, filename, updater.version).tap do |src|
|
|
105
|
+
src.repaired = @repaired
|
|
106
|
+
src.error_ranges.concat error_ranges
|
|
107
|
+
src.changes.concat(changes + updater.changes)
|
|
111
108
|
end
|
|
112
|
-
incr_code = updater.repair(@repaired)
|
|
113
|
-
synced = Source.new(incr_code, filename)
|
|
114
|
-
synced.error_ranges.concat (error_ranges + updater.changes.map(&:range))
|
|
115
|
-
synced.code = real_code
|
|
116
|
-
synced.version = updater.version
|
|
117
|
-
synced
|
|
118
109
|
end
|
|
119
110
|
|
|
120
111
|
# @param position [Position, Array(Integer, Integer)]
|
|
121
112
|
# @return [Source::Cursor]
|
|
122
113
|
def cursor_at position
|
|
114
|
+
finalize
|
|
123
115
|
Cursor.new(self, position)
|
|
124
116
|
end
|
|
125
117
|
|
|
126
118
|
# @return [Boolean]
|
|
127
119
|
def parsed?
|
|
120
|
+
finalize
|
|
128
121
|
@parsed
|
|
129
122
|
end
|
|
130
123
|
|
|
131
124
|
def repaired?
|
|
132
|
-
|
|
125
|
+
code != @repaired
|
|
133
126
|
end
|
|
134
127
|
|
|
135
128
|
# @param position [Position]
|
|
136
129
|
# @return [Boolean]
|
|
137
130
|
def string_at? position
|
|
138
|
-
if
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
return true if [:STR, :str].include?(node.type) && range.include?(position) && range.start != position
|
|
155
|
-
if node.type == :dstr
|
|
156
|
-
inner = node_at(position.line, position.column)
|
|
157
|
-
next if inner.nil?
|
|
158
|
-
inner_range = Range.from_node(inner)
|
|
159
|
-
next unless range.include?(inner_range.ending)
|
|
160
|
-
return true if inner.type == :str
|
|
161
|
-
inner_code = at(Solargraph::Range.new(inner_range.start, position))
|
|
162
|
-
return true if (inner.type == :dstr && inner_range.ending.character <= position.character) && !inner_code.end_with?('}') ||
|
|
163
|
-
(inner.type != :dstr && inner_range.ending.line == position.line && position.character <= inner_range.ending.character && inner_code.end_with?('}'))
|
|
164
|
-
end
|
|
165
|
-
break if range.ending.line > position.line
|
|
131
|
+
return false if Position.to_offset(code, position) >= code.length
|
|
132
|
+
string_nodes.each do |node|
|
|
133
|
+
range = Range.from_node(node)
|
|
134
|
+
next if range.ending.line < position.line
|
|
135
|
+
break if range.ending.line > position.line
|
|
136
|
+
return true if node.type == :str && range.include?(position) && range.start != position
|
|
137
|
+
return true if [:STR, :str].include?(node.type) && range.include?(position) && range.start != position
|
|
138
|
+
if node.type == :dstr
|
|
139
|
+
inner = node_at(position.line, position.column)
|
|
140
|
+
next if inner.nil?
|
|
141
|
+
inner_range = Range.from_node(inner)
|
|
142
|
+
next unless range.include?(inner_range.ending)
|
|
143
|
+
return true if inner.type == :str
|
|
144
|
+
inner_code = at(Solargraph::Range.new(inner_range.start, position))
|
|
145
|
+
return true if (inner.type == :dstr && inner_range.ending.character <= position.character) && !inner_code.end_with?('}') ||
|
|
146
|
+
(inner.type != :dstr && inner_range.ending.line == position.line && position.character <= inner_range.ending.character && inner_code.end_with?('}'))
|
|
166
147
|
end
|
|
167
|
-
|
|
148
|
+
break if range.ending.line > position.line
|
|
168
149
|
end
|
|
150
|
+
false
|
|
169
151
|
end
|
|
170
152
|
|
|
171
153
|
# @return [::Array<Range>]
|
|
@@ -199,8 +181,8 @@ module Solargraph
|
|
|
199
181
|
# @return [String]
|
|
200
182
|
def code_for(node)
|
|
201
183
|
rng = Range.from_node(node)
|
|
202
|
-
b = Position.line_char_to_offset(
|
|
203
|
-
e = Position.line_char_to_offset(
|
|
184
|
+
b = Position.line_char_to_offset(code, rng.start.line, rng.start.column)
|
|
185
|
+
e = Position.line_char_to_offset(code, rng.ending.line, rng.ending.column)
|
|
204
186
|
frag = code[b..e-1].to_s
|
|
205
187
|
frag.strip.gsub(/,$/, '')
|
|
206
188
|
end
|
|
@@ -225,15 +207,9 @@ module Solargraph
|
|
|
225
207
|
Location.new(filename, range)
|
|
226
208
|
end
|
|
227
209
|
|
|
228
|
-
FOLDING_NODE_TYPES =
|
|
229
|
-
%i[
|
|
230
|
-
CLASS SCLASS MODULE DEFN DEFS IF WHILE UNLESS ITER STR HASH ARRAY LIST
|
|
231
|
-
].freeze
|
|
232
|
-
else
|
|
233
|
-
%i[
|
|
210
|
+
FOLDING_NODE_TYPES = %i[
|
|
234
211
|
class sclass module def defs if str dstr array while unless kwbegin hash block
|
|
235
212
|
].freeze
|
|
236
|
-
end
|
|
237
213
|
|
|
238
214
|
# Get an array of ranges that can be folded, e.g., the range of a class
|
|
239
215
|
# definition or an if condition.
|
|
@@ -251,8 +227,7 @@ module Solargraph
|
|
|
251
227
|
end
|
|
252
228
|
|
|
253
229
|
def synchronized?
|
|
254
|
-
|
|
255
|
-
@synchronized
|
|
230
|
+
true
|
|
256
231
|
end
|
|
257
232
|
|
|
258
233
|
# Get a hash of comments grouped by the line numbers of the associated code.
|
|
@@ -262,8 +237,9 @@ module Solargraph
|
|
|
262
237
|
@associated_comments ||= begin
|
|
263
238
|
result = {}
|
|
264
239
|
buffer = String.new('')
|
|
240
|
+
# @type [Integer, nil]
|
|
265
241
|
last = nil
|
|
266
|
-
|
|
242
|
+
comments.each_pair do |num, snip|
|
|
267
243
|
if !last || num == last + 1
|
|
268
244
|
buffer.concat "#{snip.text}\n"
|
|
269
245
|
else
|
|
@@ -295,12 +271,9 @@ module Solargraph
|
|
|
295
271
|
def inner_folding_ranges top, result = [], parent = nil
|
|
296
272
|
return unless Parser.is_ast_node?(top)
|
|
297
273
|
if FOLDING_NODE_TYPES.include?(top.type)
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
range
|
|
301
|
-
if result.empty? || range.start.line > result.last.start.line
|
|
302
|
-
result.push range unless range.ending.line - range.start.line < 2
|
|
303
|
-
end
|
|
274
|
+
range = Range.from_node(top)
|
|
275
|
+
if result.empty? || range.start.line > result.last.start.line
|
|
276
|
+
result.push range unless range.ending.line - range.start.line < 2
|
|
304
277
|
end
|
|
305
278
|
end
|
|
306
279
|
top.children.each do |child|
|
|
@@ -334,19 +307,19 @@ module Solargraph
|
|
|
334
307
|
|
|
335
308
|
# A hash of line numbers and their associated comments.
|
|
336
309
|
#
|
|
337
|
-
# @return [Hash{Integer => Array<String
|
|
310
|
+
# @return [Hash{Integer => Array<String>, nil}]
|
|
338
311
|
def stringified_comments
|
|
339
312
|
@stringified_comments ||= {}
|
|
340
313
|
end
|
|
341
314
|
|
|
342
315
|
# @return [Array<Parser::AST::Node>]
|
|
343
316
|
def string_nodes
|
|
344
|
-
@string_nodes ||= string_nodes_in(
|
|
317
|
+
@string_nodes ||= string_nodes_in(node)
|
|
345
318
|
end
|
|
346
319
|
|
|
347
320
|
# @return [Array<Range>]
|
|
348
321
|
def comment_ranges
|
|
349
|
-
@comment_ranges ||=
|
|
322
|
+
@comment_ranges ||= comments.values.map(&:range)
|
|
350
323
|
end
|
|
351
324
|
|
|
352
325
|
# Get an array of foldable comment block ranges. Blocks are excluded if
|
|
@@ -402,16 +375,59 @@ module Solargraph
|
|
|
402
375
|
|
|
403
376
|
protected
|
|
404
377
|
|
|
378
|
+
# @return [Array<Change>]
|
|
379
|
+
def changes
|
|
380
|
+
@changes ||= []
|
|
381
|
+
end
|
|
382
|
+
|
|
405
383
|
# @return [String]
|
|
406
384
|
attr_writer :filename
|
|
407
385
|
|
|
408
386
|
# @return [Integer]
|
|
409
387
|
attr_writer :version
|
|
410
388
|
|
|
389
|
+
def finalize
|
|
390
|
+
return if @finalized && changes.empty?
|
|
391
|
+
|
|
392
|
+
changes.each do |change|
|
|
393
|
+
@code = change.write(@code)
|
|
394
|
+
end
|
|
395
|
+
@finalized = true
|
|
396
|
+
begin
|
|
397
|
+
@node, @comments = Solargraph::Parser.parse_with_comments(@code, filename)
|
|
398
|
+
@parsed = true
|
|
399
|
+
@repaired = @code
|
|
400
|
+
rescue Parser::SyntaxError, EncodingError => e
|
|
401
|
+
@node = nil
|
|
402
|
+
@comments = {}
|
|
403
|
+
@parsed = false
|
|
404
|
+
ensure
|
|
405
|
+
@code.freeze
|
|
406
|
+
end
|
|
407
|
+
if !@parsed && !changes.empty?
|
|
408
|
+
changes.each do |change|
|
|
409
|
+
@repaired = change.repair(@repaired)
|
|
410
|
+
end
|
|
411
|
+
error_ranges.concat(changes.map(&:range))
|
|
412
|
+
begin
|
|
413
|
+
@node, @comments = Solargraph::Parser.parse_with_comments(@repaired, filename)
|
|
414
|
+
@parsed = true
|
|
415
|
+
rescue Parser::SyntaxError, EncodingError => e
|
|
416
|
+
@node = nil
|
|
417
|
+
@comments = {}
|
|
418
|
+
@parsed = false
|
|
419
|
+
end
|
|
420
|
+
elsif @parsed
|
|
421
|
+
error_ranges.clear
|
|
422
|
+
end
|
|
423
|
+
changes.clear
|
|
424
|
+
end
|
|
425
|
+
|
|
411
426
|
# @param val [String]
|
|
412
427
|
# @return [String]
|
|
413
428
|
def code=(val)
|
|
414
|
-
@code_lines= nil
|
|
429
|
+
@code_lines = nil
|
|
430
|
+
@finalized = false
|
|
415
431
|
@code = val
|
|
416
432
|
end
|
|
417
433
|
|
|
@@ -422,7 +438,12 @@ module Solargraph
|
|
|
422
438
|
attr_writer :error_ranges
|
|
423
439
|
|
|
424
440
|
# @return [String]
|
|
425
|
-
|
|
441
|
+
attr_writer :repaired
|
|
442
|
+
|
|
443
|
+
def repaired
|
|
444
|
+
finalize
|
|
445
|
+
@repaired
|
|
446
|
+
end
|
|
426
447
|
|
|
427
448
|
# @return [Boolean]
|
|
428
449
|
attr_writer :parsed
|
|
@@ -433,9 +454,6 @@ module Solargraph
|
|
|
433
454
|
# @return [Boolean]
|
|
434
455
|
attr_writer :synchronized
|
|
435
456
|
|
|
436
|
-
# @return [Source::Updater]
|
|
437
|
-
attr_accessor :last_updater
|
|
438
|
-
|
|
439
457
|
private
|
|
440
458
|
|
|
441
459
|
# @return [Array<String>]
|