solargraph 0.25.1 → 0.26.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.rb +18 -16
- data/lib/solargraph/api_map.rb +100 -161
- data/lib/solargraph/api_map/source_to_yard.rb +9 -9
- data/lib/solargraph/api_map/store.rb +50 -13
- data/lib/solargraph/basic_type.rb +33 -0
- data/lib/solargraph/basic_type_methods.rb +111 -0
- data/lib/solargraph/complex_type.rb +51 -89
- data/lib/solargraph/core_fills.rb +12 -8
- data/lib/solargraph/diagnostics/type_not_defined.rb +2 -2
- data/lib/solargraph/language_server.rb +3 -0
- data/lib/solargraph/language_server/completion_item_kinds.rb +2 -0
- data/lib/solargraph/language_server/error_codes.rb +2 -0
- data/lib/solargraph/language_server/host.rb +53 -6
- data/lib/solargraph/language_server/message.rb +13 -0
- data/lib/solargraph/language_server/message/text_document/definition.rb +4 -6
- data/lib/solargraph/language_server/message/text_document/document_symbol.rb +2 -1
- data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +2 -1
- data/lib/solargraph/language_server/message_types.rb +2 -0
- data/lib/solargraph/language_server/request.rb +4 -0
- data/lib/solargraph/language_server/symbol_kinds.rb +28 -26
- data/lib/solargraph/language_server/transport.rb +3 -0
- data/lib/solargraph/language_server/uri_helpers.rb +2 -0
- data/lib/solargraph/library.rb +12 -7
- data/lib/solargraph/pin.rb +1 -1
- data/lib/solargraph/pin/attribute.rb +5 -5
- data/lib/solargraph/pin/base.rb +51 -16
- data/lib/solargraph/pin/base_variable.rb +25 -7
- data/lib/solargraph/pin/block.rb +18 -1
- data/lib/solargraph/pin/block_parameter.rb +42 -5
- data/lib/solargraph/pin/conversions.rb +4 -2
- data/lib/solargraph/pin/method.rb +6 -6
- data/lib/solargraph/pin/method_parameter.rb +6 -6
- data/lib/solargraph/pin/namespace.rb +7 -2
- data/lib/solargraph/pin/proxy_type.rb +39 -0
- data/lib/solargraph/pin/symbol.rb +20 -12
- data/lib/solargraph/pin/yard_pin/method.rb +2 -2
- data/lib/solargraph/source.rb +89 -38
- data/lib/solargraph/source/call_chainer.rb +273 -0
- data/lib/solargraph/source/chain.rb +104 -0
- data/lib/solargraph/source/chain/call.rb +72 -0
- data/lib/solargraph/source/chain/class_variable.rb +11 -0
- data/lib/solargraph/source/chain/constant.rb +17 -0
- data/lib/solargraph/source/chain/definition.rb +16 -0
- data/lib/solargraph/source/chain/global_variable.rb +11 -0
- data/lib/solargraph/source/chain/head.rb +20 -0
- data/lib/solargraph/source/chain/instance_variable.rb +11 -0
- data/lib/solargraph/source/chain/link.rb +33 -0
- data/lib/solargraph/source/chain/literal.rb +21 -0
- data/lib/solargraph/source/chain/variable.rb +11 -0
- data/lib/solargraph/source/change.rb +3 -1
- data/lib/solargraph/{api_map → source}/completion.rb +3 -1
- data/lib/solargraph/source/encoding_fixes.rb +21 -0
- data/lib/solargraph/source/fragment.rb +139 -284
- data/lib/solargraph/source/mapper.rb +27 -16
- data/lib/solargraph/source/node_chainer.rb +94 -0
- data/lib/solargraph/source/node_methods.rb +2 -2
- data/lib/solargraph/source/position.rb +4 -0
- data/lib/solargraph/source/range.rb +10 -2
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/yard_map.rb +13 -2
- metadata +20 -6
- data/lib/solargraph/api_map/probe.rb +0 -251
- data/lib/solargraph/api_map/type_methods.rb +0 -40
- data/lib/solargraph/pin/proxy_method.rb +0 -30
@@ -51,8 +51,13 @@ module Solargraph
|
|
51
51
|
@path ||= (namespace.empty? ? '' : "#{namespace}::") + name
|
52
52
|
end
|
53
53
|
|
54
|
-
def
|
55
|
-
@
|
54
|
+
def return_complex_type
|
55
|
+
@return_complex_type ||= ComplexType.parse( (type == :class ? 'Class' : 'Module') + "<#{path}>" )
|
56
|
+
end
|
57
|
+
|
58
|
+
def infer api_map
|
59
|
+
# Assuming that namespace pins are always fully qualified
|
60
|
+
return_complex_type
|
56
61
|
end
|
57
62
|
end
|
58
63
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Solargraph
|
2
|
+
module Pin
|
3
|
+
class ProxyType < Base
|
4
|
+
# @param location [Solargraph::Source::Location]
|
5
|
+
# @param namespace [String]
|
6
|
+
# @param name [String]
|
7
|
+
# @param return_type [ComplexType]
|
8
|
+
def initialize location, namespace, name, return_type
|
9
|
+
super(location, namespace, name, '')
|
10
|
+
@return_complex_type = return_type
|
11
|
+
end
|
12
|
+
|
13
|
+
def scope
|
14
|
+
return_complex_type.scope
|
15
|
+
end
|
16
|
+
|
17
|
+
def path
|
18
|
+
@path ||= begin
|
19
|
+
result = namespace.to_s
|
20
|
+
result += '::' unless result.empty? or name.to_s.empty?
|
21
|
+
result += name.to_s
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def named_context
|
26
|
+
path
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param return_type [ComplexType]
|
30
|
+
# @return [ProxyType]
|
31
|
+
def self.anonymous return_type
|
32
|
+
parts = return_type.namespace.split('::')
|
33
|
+
namespace = parts[0..-2].join('::').to_s
|
34
|
+
name = parts.last.to_s
|
35
|
+
ProxyType.new(nil, namespace, name, return_type)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -1,19 +1,15 @@
|
|
1
1
|
module Solargraph
|
2
2
|
module Pin
|
3
|
-
class Symbol
|
4
|
-
|
5
|
-
|
6
|
-
attr_reader :location
|
7
|
-
|
8
|
-
attr_reader :name
|
9
|
-
|
3
|
+
class Symbol < Base
|
4
|
+
# @param location [Solargraph::Source::Location]
|
5
|
+
# @param name [String]
|
10
6
|
def initialize location, name
|
11
7
|
@name = name
|
12
8
|
@location = location
|
13
9
|
end
|
14
10
|
|
15
|
-
def
|
16
|
-
|
11
|
+
def namespace
|
12
|
+
''
|
17
13
|
end
|
18
14
|
|
19
15
|
def kind
|
@@ -21,7 +17,7 @@ module Solargraph
|
|
21
17
|
end
|
22
18
|
|
23
19
|
def path
|
24
|
-
|
20
|
+
''
|
25
21
|
end
|
26
22
|
|
27
23
|
def identifier
|
@@ -32,8 +28,20 @@ module Solargraph
|
|
32
28
|
Solargraph::LanguageServer::CompletionItemKinds::KEYWORD
|
33
29
|
end
|
34
30
|
|
35
|
-
def
|
36
|
-
'
|
31
|
+
def comments
|
32
|
+
''
|
33
|
+
end
|
34
|
+
|
35
|
+
def return_complex_type
|
36
|
+
@return_complex_type ||= Solargraph::ComplexType::SYMBOL
|
37
|
+
end
|
38
|
+
|
39
|
+
def directives
|
40
|
+
[]
|
41
|
+
end
|
42
|
+
|
43
|
+
def deprecated?
|
44
|
+
false
|
37
45
|
end
|
38
46
|
end
|
39
47
|
end
|
@@ -9,8 +9,8 @@ module Solargraph
|
|
9
9
|
super(location, code_object.namespace.to_s, code_object.name.to_s, comments, code_object.scope, code_object.visibility, get_parameters(code_object))
|
10
10
|
end
|
11
11
|
|
12
|
-
def
|
13
|
-
@
|
12
|
+
def return_complex_type
|
13
|
+
@return_complex_type ||= Solargraph::ComplexType.parse(Solargraph::CoreFills::CUSTOM_RETURN_TYPES[path]) if Solargraph::CoreFills::CUSTOM_RETURN_TYPES.has_key?(path)
|
14
14
|
super
|
15
15
|
end
|
16
16
|
|
data/lib/solargraph/source.rb
CHANGED
@@ -13,6 +13,13 @@ module Solargraph
|
|
13
13
|
autoload :Change, 'solargraph/source/change'
|
14
14
|
autoload :Mapper, 'solargraph/source/mapper'
|
15
15
|
autoload :NodeMethods, 'solargraph/source/node_methods'
|
16
|
+
autoload :Chain, 'solargraph/source/chain'
|
17
|
+
autoload :EncodingFixes, 'solargraph/source/encoding_fixes'
|
18
|
+
autoload :CallChainer, 'solargraph/source/call_chainer'
|
19
|
+
autoload :NodeChainer, 'solargraph/source/node_chainer'
|
20
|
+
autoload :Completion, 'solargraph/source/completion'
|
21
|
+
|
22
|
+
include EncodingFixes
|
16
23
|
|
17
24
|
# @return [String]
|
18
25
|
attr_reader :code
|
@@ -60,21 +67,32 @@ module Solargraph
|
|
60
67
|
# @param filename [String]
|
61
68
|
def initialize code, filename = nil
|
62
69
|
begin
|
63
|
-
@code = code
|
64
|
-
@fixed = code
|
70
|
+
@code = normalize(code)
|
71
|
+
@fixed = @code
|
65
72
|
@filename = filename
|
66
73
|
@version = 0
|
67
74
|
@domains = []
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
hard_fix_node
|
72
|
-
end
|
75
|
+
parse
|
76
|
+
rescue Parser::SyntaxError, EncodingError => e
|
77
|
+
hard_fix_node
|
73
78
|
rescue Exception => e
|
74
|
-
|
79
|
+
STDERR.puts e.message
|
80
|
+
STDERR.puts e.backtrace
|
81
|
+
raise "Error parsing #{filename || '(source)'}: [#{e.class}] #{e.message}"
|
75
82
|
end
|
76
83
|
end
|
77
84
|
|
85
|
+
# @param range [Solargraph::Source::Range]
|
86
|
+
def at range
|
87
|
+
from_to range.start.line, range.start.character, range.ending.line, range.ending.character
|
88
|
+
end
|
89
|
+
|
90
|
+
def from_to l1, c1, l2, c2
|
91
|
+
b = Solargraph::Source::Position.line_char_to_offset(@code, l1, c1)
|
92
|
+
e = Solargraph::Source::Position.line_char_to_offset(@code, l2, c2)
|
93
|
+
@code[b..e-1]
|
94
|
+
end
|
95
|
+
|
78
96
|
def macro path
|
79
97
|
@path_macros[path]
|
80
98
|
end
|
@@ -122,11 +140,6 @@ module Solargraph
|
|
122
140
|
pins.select{|pin| pin.kind == Pin::CLASS_VARIABLE}
|
123
141
|
end
|
124
142
|
|
125
|
-
# @return [Array<Solargraph::Pin::Base>]
|
126
|
-
def locals
|
127
|
-
@locals
|
128
|
-
end
|
129
|
-
|
130
143
|
# @return [Array<Solargraph::Pin::GlobalVariable>]
|
131
144
|
def global_variable_pins
|
132
145
|
pins.select{|pin| pin.kind == Pin::GLOBAL_VARIABLE}
|
@@ -168,6 +181,11 @@ module Solargraph
|
|
168
181
|
_locate_pin line, character, Pin::NAMESPACE, Pin::METHOD
|
169
182
|
end
|
170
183
|
|
184
|
+
# Locate the namespace pin at the specified line and character.
|
185
|
+
#
|
186
|
+
# @param line [line]
|
187
|
+
# @param character [character]
|
188
|
+
# @return [Pin::Namespace]
|
171
189
|
def locate_namespace_pin line, character
|
172
190
|
_locate_pin line, character, Pin::NAMESPACE
|
173
191
|
end
|
@@ -191,21 +209,43 @@ module Solargraph
|
|
191
209
|
# @param column [Integer]
|
192
210
|
# @return [Boolean]
|
193
211
|
def string_at?(line, column)
|
194
|
-
node = node_at(line, column)
|
195
|
-
# @todo raise InvalidOffset or InvalidRange or something?
|
196
|
-
return false if node.nil?
|
197
|
-
node.type == :str or node.type == :dstr
|
212
|
+
# node = node_at(line, column)
|
213
|
+
# # @todo raise InvalidOffset or InvalidRange or something?
|
214
|
+
# return false if node.nil?
|
215
|
+
# node.type == :str or node.type == :dstr
|
216
|
+
pos = Source::Position.new(line, column)
|
217
|
+
@strings.each do |str|
|
218
|
+
return true if str.contain?(pos)
|
219
|
+
break if str.start.line > pos.line
|
220
|
+
end
|
221
|
+
false
|
222
|
+
end
|
223
|
+
|
224
|
+
# True if the specified location is inside a comment.
|
225
|
+
#
|
226
|
+
# @param line [Integer]
|
227
|
+
# @param column [Integer]
|
228
|
+
# @return [Boolean]
|
229
|
+
def comment_at?(line, column)
|
230
|
+
pos = Source::Position.new(line, column)
|
231
|
+
@comment_ranges.each do |cmnt|
|
232
|
+
return true if cmnt.include?(pos)
|
233
|
+
break if cmnt.start.line > pos.line
|
234
|
+
end
|
235
|
+
false
|
198
236
|
end
|
199
237
|
|
200
238
|
# Get an array of nodes containing the specified index, starting with the
|
201
239
|
# nearest node and ending with the root.
|
202
240
|
#
|
203
|
-
# @param
|
241
|
+
# @param line [Integer]
|
242
|
+
# @param column [Integer]
|
204
243
|
# @return [Array<AST::Node>]
|
205
244
|
def tree_at(line, column)
|
206
|
-
offset = Position.line_char_to_offset(@code, line, column)
|
245
|
+
# offset = Position.line_char_to_offset(@code, line, column)
|
246
|
+
position = Position.new(line, column)
|
207
247
|
stack = []
|
208
|
-
inner_tree_at @node,
|
248
|
+
inner_tree_at @node, position, stack
|
209
249
|
stack
|
210
250
|
end
|
211
251
|
|
@@ -219,8 +259,7 @@ module Solargraph
|
|
219
259
|
@code = updater.write(original_code)
|
220
260
|
@fixed = updater.write(original_code, true)
|
221
261
|
@version = updater.version
|
222
|
-
return if @code == original_code
|
223
|
-
return unless reparse
|
262
|
+
return if @code == original_code or !reparse
|
224
263
|
begin
|
225
264
|
parse
|
226
265
|
@fixed = @code
|
@@ -252,8 +291,8 @@ module Solargraph
|
|
252
291
|
# @param location [Solargraph::Source::Location]
|
253
292
|
# @return [Solargraph::Pin::Base]
|
254
293
|
def locate_pin location
|
255
|
-
return nil unless location.start_with?("#{filename}:")
|
256
|
-
|
294
|
+
# return nil unless location.start_with?("#{filename}:")
|
295
|
+
pins.select{|pin| pin.location == location}
|
257
296
|
end
|
258
297
|
|
259
298
|
# @param line [Integer] A zero-based line number
|
@@ -284,7 +323,7 @@ module Solargraph
|
|
284
323
|
def inner_node_references name, top
|
285
324
|
result = []
|
286
325
|
if top.kind_of?(AST::Node)
|
287
|
-
if
|
326
|
+
if top.children.any?{|c| c.to_s == name}
|
288
327
|
result.push top
|
289
328
|
end
|
290
329
|
top.children.each { |c| result.concat inner_node_references(name, c) }
|
@@ -292,15 +331,15 @@ module Solargraph
|
|
292
331
|
result
|
293
332
|
end
|
294
333
|
|
295
|
-
def inner_tree_at node,
|
334
|
+
def inner_tree_at node, position, stack
|
296
335
|
return if node.nil?
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
336
|
+
here = Range.from_to(node.loc.expression.line, node.loc.expression.column, node.loc.expression.last_line, node.loc.expression.last_column)
|
337
|
+
if here.contain?(position)
|
338
|
+
stack.unshift node
|
339
|
+
node.children.each do |c|
|
340
|
+
next unless c.is_a?(AST::Node)
|
341
|
+
next if c.loc.expression.nil?
|
342
|
+
inner_tree_at(c, position, stack)
|
304
343
|
end
|
305
344
|
end
|
306
345
|
end
|
@@ -327,8 +366,8 @@ module Solargraph
|
|
327
366
|
parser = Parser::CurrentRuby.new(FlawedBuilder.new)
|
328
367
|
parser.diagnostics.all_errors_are_fatal = true
|
329
368
|
parser.diagnostics.ignore_warnings = true
|
330
|
-
buffer = Parser::Source::Buffer.new(filename,
|
331
|
-
buffer.source = code.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '
|
369
|
+
buffer = Parser::Source::Buffer.new(filename, 0)
|
370
|
+
buffer.source = code.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '_')
|
332
371
|
parser.parse_with_comments(buffer)
|
333
372
|
end
|
334
373
|
|
@@ -337,7 +376,9 @@ module Solargraph
|
|
337
376
|
synchronize_mapped *new_map_data
|
338
377
|
end
|
339
378
|
|
340
|
-
def synchronize_mapped new_pins, new_locals, new_requires, new_symbols #, new_path_macros, new_domains
|
379
|
+
def synchronize_mapped new_pins, new_locals, new_requires, new_symbols, new_strings, new_comment_ranges #, new_path_macros, new_domains
|
380
|
+
@strings = new_strings
|
381
|
+
@comment_ranges = new_comment_ranges
|
341
382
|
return if @requires == new_requires and @symbols == new_symbols and try_merge(new_pins, new_locals)
|
342
383
|
@pins = new_pins
|
343
384
|
@locals = new_locals
|
@@ -387,7 +428,6 @@ module Solargraph
|
|
387
428
|
elsif d.tag.tag_name == 'method'
|
388
429
|
gen_src = Source.new("def #{d.tag.name};end", filename)
|
389
430
|
gen_pin = gen_src.pins.last # Method is last pin after root namespace
|
390
|
-
# next if ns.nil? or ns.empty? # @todo Add methods to global namespace?
|
391
431
|
@pins.push Solargraph::Pin::Method.new(pin.location, pin.path, gen_pin.name, docstring.all, :instance, :public, [])
|
392
432
|
elsif d.tag.tag_name == 'macro'
|
393
433
|
@path_macros[pin.path] = d
|
@@ -404,7 +444,9 @@ module Solargraph
|
|
404
444
|
# @param filename [String]
|
405
445
|
# @return [Solargraph::Source]
|
406
446
|
def load filename
|
407
|
-
|
447
|
+
file = File.open(filename, 'rb')
|
448
|
+
code = file.read
|
449
|
+
file.close
|
408
450
|
Source.load_string(code, filename)
|
409
451
|
end
|
410
452
|
|
@@ -414,6 +456,15 @@ module Solargraph
|
|
414
456
|
def load_string code, filename = nil
|
415
457
|
Source.new code, filename
|
416
458
|
end
|
459
|
+
|
460
|
+
def parse_node code, filename
|
461
|
+
parser = Parser::CurrentRuby.new(FlawedBuilder.new)
|
462
|
+
parser.diagnostics.all_errors_are_fatal = true
|
463
|
+
parser.diagnostics.ignore_warnings = true
|
464
|
+
buffer = Parser::Source::Buffer.new(nil, 0)
|
465
|
+
buffer.source = code.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '_')
|
466
|
+
parser.parse(buffer)
|
467
|
+
end
|
417
468
|
end
|
418
469
|
end
|
419
470
|
end
|
@@ -0,0 +1,273 @@
|
|
1
|
+
module Solargraph
|
2
|
+
class Source
|
3
|
+
# Information about a location in a source, including the location's word
|
4
|
+
# and signature, literal values at the base of signatures, and whether the
|
5
|
+
# location is inside a string or comment. ApiMaps use Fragments to provide
|
6
|
+
# results for completion and definition queries.
|
7
|
+
#
|
8
|
+
class CallChainer
|
9
|
+
include NodeMethods
|
10
|
+
|
11
|
+
private_class_method :new
|
12
|
+
|
13
|
+
class << self
|
14
|
+
# @param source [Source]
|
15
|
+
# @param line [Integer]
|
16
|
+
# @param column [Integer]
|
17
|
+
# @return [Source::Chain]
|
18
|
+
def chain source, line, column
|
19
|
+
new(source, line, column).chain
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param source [Solargraph::Source]
|
24
|
+
# @param line [Integer]
|
25
|
+
# @param column [Integer]
|
26
|
+
def initialize source, line, column
|
27
|
+
@source = source
|
28
|
+
# @source.code = source.code
|
29
|
+
@line = line
|
30
|
+
@column = column
|
31
|
+
@calculated_literal = false
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [Source::Chain]
|
35
|
+
def chain
|
36
|
+
links = []
|
37
|
+
# @todo Smelly colon handling
|
38
|
+
if @source.code[0..offset-1].end_with?(':') and !@source.code[0..offset-1].end_with?('::')
|
39
|
+
links.push Chain::Link.new
|
40
|
+
links.push Chain::Link.new
|
41
|
+
elsif @source.string_at?(line, column)
|
42
|
+
links.push Chain::Literal.new('String')
|
43
|
+
else
|
44
|
+
links.push Chain::Literal.new(base_literal) if base_literal?
|
45
|
+
sig = whole_signature
|
46
|
+
unless sig.empty?
|
47
|
+
sig = sig[1..-1] if sig.start_with?('.')
|
48
|
+
head = true
|
49
|
+
sig.split('.', -1).each do |word|
|
50
|
+
if word.include?('::')
|
51
|
+
# @todo Smelly way of handling constants
|
52
|
+
parts = (word.start_with?('::') ? word[2..-1] : word).split('::', -1)
|
53
|
+
last = parts.pop
|
54
|
+
links.push Chain::Constant.new(parts.join('::')) unless parts.empty?
|
55
|
+
links.push (last.nil? ? Chain::UNDEFINED_CONSTANT : Chain::Constant.new(last))
|
56
|
+
else
|
57
|
+
links.push word_to_link(word, head)
|
58
|
+
end
|
59
|
+
head = false
|
60
|
+
end
|
61
|
+
end
|
62
|
+
# Literal string hack
|
63
|
+
links.push Chain::UNDEFINED_CALL if base_literal? and @source.code[offset - 1] == '.' and links.length == 1
|
64
|
+
end
|
65
|
+
@chain ||= Chain.new(source.filename, links)
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
# The zero-based line number of the fragment's location.
|
71
|
+
#
|
72
|
+
# @return [Integer]
|
73
|
+
attr_reader :line
|
74
|
+
|
75
|
+
# The zero-based column number of the fragment's location.
|
76
|
+
#
|
77
|
+
# @return [Integer]
|
78
|
+
attr_reader :column
|
79
|
+
|
80
|
+
# @return [Solargraph::Source]
|
81
|
+
attr_reader :source
|
82
|
+
|
83
|
+
def word_to_link word, head
|
84
|
+
if word.start_with?('@@')
|
85
|
+
Chain::ClassVariable.new(word)
|
86
|
+
elsif word.start_with?('@')
|
87
|
+
Chain::InstanceVariable.new(word)
|
88
|
+
elsif word.start_with?('$')
|
89
|
+
Chain::GlobalVariable.new(word)
|
90
|
+
elsif word.end_with?(':')
|
91
|
+
Chain::Link.new
|
92
|
+
elsif word.empty?
|
93
|
+
Chain::UNDEFINED_CALL
|
94
|
+
elsif head and !@source.code[signature_data[0]..-1].match(/^[\s]*?#{word}[\s]*?\(/)
|
95
|
+
# The head needs to allow for ambiguous references to constants and
|
96
|
+
# methods. For example, `String` might be either. If the word is not
|
97
|
+
# followed by an open parenthesis, use Chain::Head for ambiguous
|
98
|
+
# results.
|
99
|
+
Chain::Head.new(word)
|
100
|
+
else
|
101
|
+
Chain::Call.new(word)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# An alias for #column.
|
106
|
+
#
|
107
|
+
# @return [Integer]
|
108
|
+
def character
|
109
|
+
@column
|
110
|
+
end
|
111
|
+
|
112
|
+
# @return [Source::Position]
|
113
|
+
def position
|
114
|
+
@position ||= Position.new(line, column)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Get the signature up to the current offset. Given the text `foo.bar`,
|
118
|
+
# the signature at offset 5 is `foo.b`.
|
119
|
+
#
|
120
|
+
# @return [String]
|
121
|
+
def signature
|
122
|
+
@signature ||= signature_data[1].to_s
|
123
|
+
end
|
124
|
+
|
125
|
+
# Get the remainder of the word after the current offset. Given the text
|
126
|
+
# `foobar` with an offset of 3, the remainder is `bar`.
|
127
|
+
#
|
128
|
+
# @return [String]
|
129
|
+
def remainder
|
130
|
+
@remainder ||= remainder_at(offset)
|
131
|
+
end
|
132
|
+
|
133
|
+
# Get the whole signature at the current offset, including the final
|
134
|
+
# word and its remainder.
|
135
|
+
#
|
136
|
+
# @return [String]
|
137
|
+
def whole_signature
|
138
|
+
@whole_signature ||= signature + remainder
|
139
|
+
end
|
140
|
+
|
141
|
+
# True if the current offset is inside a string.
|
142
|
+
#
|
143
|
+
# @return [Boolean]
|
144
|
+
def string?
|
145
|
+
# @string ||= (node.type == :str or node.type == :dstr)
|
146
|
+
@string ||= @source.string_at?(line, character)
|
147
|
+
end
|
148
|
+
|
149
|
+
# True if the fragment is a signature that stems from a literal value.
|
150
|
+
#
|
151
|
+
# @return [Boolean]
|
152
|
+
def base_literal?
|
153
|
+
!base_literal.nil?
|
154
|
+
end
|
155
|
+
|
156
|
+
# The type of literal value at the root of the signature (or nil).
|
157
|
+
#
|
158
|
+
# @return [String]
|
159
|
+
def base_literal
|
160
|
+
if @base_literal.nil? and !@calculated_literal
|
161
|
+
@calculated_literal = true
|
162
|
+
if signature.start_with?('.')
|
163
|
+
pn = @source.node_at(line, column - 2)
|
164
|
+
@base_literal = infer_literal_node_type(pn) unless pn.nil?
|
165
|
+
end
|
166
|
+
end
|
167
|
+
@base_literal
|
168
|
+
end
|
169
|
+
|
170
|
+
# @return [Integer]
|
171
|
+
def offset
|
172
|
+
@offset ||= get_offset(line, column)
|
173
|
+
end
|
174
|
+
|
175
|
+
def get_offset line, column
|
176
|
+
Position.line_char_to_offset(@source.code, line, column)
|
177
|
+
end
|
178
|
+
|
179
|
+
def signature_data
|
180
|
+
@signature_data ||= get_signature_data_at(offset)
|
181
|
+
end
|
182
|
+
|
183
|
+
def get_signature_data_at index
|
184
|
+
brackets = 0
|
185
|
+
squares = 0
|
186
|
+
parens = 0
|
187
|
+
signature = ''
|
188
|
+
index -=1
|
189
|
+
in_whitespace = false
|
190
|
+
while index >= 0
|
191
|
+
pos = Position.from_offset(@source.code, index)
|
192
|
+
break if index > 0 and @source.comment_at?(pos.line, pos.character)
|
193
|
+
unless !in_whitespace and string?
|
194
|
+
break if brackets > 0 or parens > 0 or squares > 0
|
195
|
+
char = @source.code[index, 1]
|
196
|
+
break if char.nil? # @todo Is this the right way to handle this?
|
197
|
+
if brackets.zero? and parens.zero? and squares.zero? and [' ', "\r", "\n", "\t"].include?(char)
|
198
|
+
in_whitespace = true
|
199
|
+
else
|
200
|
+
if brackets.zero? and parens.zero? and squares.zero? and in_whitespace
|
201
|
+
unless char == '.' or @source.code[index+1..-1].strip.start_with?('.')
|
202
|
+
old = @source.code[index+1..-1]
|
203
|
+
nxt = @source.code[index+1..-1].lstrip
|
204
|
+
index += (@source.code[index+1..-1].length - @source.code[index+1..-1].lstrip.length)
|
205
|
+
break
|
206
|
+
end
|
207
|
+
end
|
208
|
+
if char == ')'
|
209
|
+
parens -=1
|
210
|
+
elsif char == ']'
|
211
|
+
squares -=1
|
212
|
+
elsif char == '}'
|
213
|
+
brackets -= 1
|
214
|
+
elsif char == '('
|
215
|
+
parens += 1
|
216
|
+
elsif char == '{'
|
217
|
+
brackets += 1
|
218
|
+
elsif char == '['
|
219
|
+
squares += 1
|
220
|
+
signature = ".[]#{signature}" if parens.zero? and brackets.zero? and squares.zero? and @source.code[index-2] != '%'
|
221
|
+
end
|
222
|
+
if brackets.zero? and parens.zero? and squares.zero?
|
223
|
+
break if ['"', "'", ',', ';', '%'].include?(char)
|
224
|
+
signature = char + signature if char.match(/[a-z0-9:\._@\$\?\!]/i) and @source.code[index - 1] != '%'
|
225
|
+
break if char == '$'
|
226
|
+
if char == '@'
|
227
|
+
signature = "@#{signature}" if @source.code[index-1, 1] == '@'
|
228
|
+
break
|
229
|
+
end
|
230
|
+
end
|
231
|
+
in_whitespace = false
|
232
|
+
end
|
233
|
+
end
|
234
|
+
index -= 1
|
235
|
+
end
|
236
|
+
# @todo Smelly exceptional case for integer literals
|
237
|
+
match = signature.match(/^[0-9]+/)
|
238
|
+
if match
|
239
|
+
index += match[0].length
|
240
|
+
signature = signature[match[0].length..-1].to_s
|
241
|
+
@base_literal = 'Integer'
|
242
|
+
# @todo Smelly exceptional case for array literals
|
243
|
+
elsif signature.start_with?('.[]')
|
244
|
+
index += 2
|
245
|
+
signature = signature[3..-1].to_s
|
246
|
+
@base_literal = 'Array'
|
247
|
+
elsif signature.start_with?('.')
|
248
|
+
pos = Position.from_offset(source.code, index)
|
249
|
+
node = source.node_at(pos.line, pos.character)
|
250
|
+
lit = infer_literal_node_type(node)
|
251
|
+
unless lit.nil?
|
252
|
+
signature = signature[1..-1].to_s
|
253
|
+
index += 1
|
254
|
+
@base_literal = lit
|
255
|
+
end
|
256
|
+
end
|
257
|
+
[index + 1, signature]
|
258
|
+
end
|
259
|
+
|
260
|
+
# @return [String]
|
261
|
+
def remainder_at index
|
262
|
+
cursor = index
|
263
|
+
while cursor < @source.code.length
|
264
|
+
char = @source.code[cursor, 1]
|
265
|
+
break if char.nil? or char == ''
|
266
|
+
break unless char.match(/[a-z0-9_\?\!]/i)
|
267
|
+
cursor += 1
|
268
|
+
end
|
269
|
+
@source.code[index..cursor-1].to_s
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|