solargraph 0.25.1 → 0.26.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|