solargraph 0.13.0 → 0.13.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 031c6210f7be6462d12e8ce7dd5c25e30b6c097d
4
- data.tar.gz: 422def5aafa30d23831434dafc5a69eb26d97e55
3
+ metadata.gz: a515f53b1eebf187bf4108abdba6c591f438cdb6
4
+ data.tar.gz: 832c13c89dcdf1bae7cb69aa3433d73346b86bec
5
5
  SHA512:
6
- metadata.gz: e597e62a01501ee615fcac8a194940e9d85ea69346eeb245104340deb9d2db69de1553facb00d9b6e1dc9c9c3b932c8b5f4cda4a00e43bd41be240548633f8f0
7
- data.tar.gz: 6137318fc634b925cc99a86b77b0ca86ae8c7f1ca842f4a864f93b88531151e3a96adc596aeb99fa94cf10c09e690432284090981e3c70b56151e72772d7dd95
6
+ metadata.gz: 74f08e71b1c0834aa6b18f4dcb0e176f91d0a40ac6f8a3fe3c76ad95081096eec4cb2ed904aaa7586433d05f8520214b9d1185755a549a2045870ba661acfd6e
7
+ data.tar.gz: 7ddcdf66369a65533d14e3c13763dad0ecb3f306b4b0ce7ef707802a61ae5b6c6d13a979b48bdd64c76b823064d32d9d1dd57dbd917d60e50dca018db1ab6799
@@ -3,16 +3,23 @@ require 'parser/current'
3
3
  module Solargraph
4
4
  class ApiMap
5
5
  class Source
6
+ # @return [String]
6
7
  attr_reader :code
8
+
9
+ # @return [Parser::AST::Node]
7
10
  attr_reader :node
11
+
12
+ # @return [Array]
8
13
  attr_reader :comments
14
+
15
+ # @return [String]
9
16
  attr_reader :filename
10
17
 
11
18
  include NodeMethods
12
19
 
13
20
  def initialize code, node, comments, filename
14
21
  @code = code
15
- root = AST::Node.new(:begin, [filename])
22
+ root = AST::Node.new(:source, [filename])
16
23
  root = root.append node
17
24
  @node = root
18
25
  @comments = comments
@@ -20,6 +27,8 @@ module Solargraph
20
27
  @filename = filename
21
28
  @namespace_nodes = {}
22
29
  @all_nodes = []
30
+ @node_stack = []
31
+ @node_tree = {}
23
32
  inner_map_node @node
24
33
  end
25
34
 
@@ -55,6 +64,14 @@ module Solargraph
55
64
  @class_variable_pins ||= []
56
65
  end
57
66
 
67
+ def local_variable_pins
68
+ @local_variable_pins ||= []
69
+ end
70
+
71
+ def global_variable_pins
72
+ @global_variable_pins ||= []
73
+ end
74
+
58
75
  def constant_pins
59
76
  @constant_pins ||= []
60
77
  end
@@ -78,6 +95,10 @@ module Solargraph
78
95
  frag.strip.gsub(/,$/, '')
79
96
  end
80
97
 
98
+ def tree_for node
99
+ @node_tree[node] || []
100
+ end
101
+
81
102
  def include? node
82
103
  @all_nodes.include? node
83
104
  end
@@ -115,7 +136,11 @@ module Solargraph
115
136
  source = self
116
137
  if node.kind_of?(AST::Node)
117
138
  @all_nodes.push node
118
- return if node.type == :str or node.type == :dstr
139
+ if node.type == :str or node.type == :dstr
140
+ stack.pop
141
+ return
142
+ end
143
+ @node_stack.unshift node
119
144
  if node.type == :class or node.type == :module
120
145
  visibility = :public
121
146
  if node.children[0].kind_of?(AST::Node) and node.children[0].children[0].kind_of?(AST::Node) and node.children[0].children[0].type == :cbase
@@ -134,6 +159,7 @@ module Solargraph
134
159
  file = source.filename
135
160
  node.children.each do |c|
136
161
  if c.kind_of?(AST::Node)
162
+ @node_tree[c] = @node_stack.clone
137
163
  if c.type == :ivasgn
138
164
  par = find_parent(stack, :class, :module, :def, :defs)
139
165
  local_scope = ( (par.kind_of?(AST::Node) and par.type == :def) ? :instance : :class )
@@ -141,6 +167,7 @@ module Solargraph
141
167
  ora = find_parent(stack, :or_asgn)
142
168
  unless ora.nil?
143
169
  u = c.updated(:ivasgn, c.children + ora.children[1..-1], nil)
170
+ @node_tree[u] = @node_stack.clone
144
171
  instance_variable_pins.push Solargraph::Pin::InstanceVariable.new(self, u, fqn || '', local_scope)
145
172
  end
146
173
  else
@@ -151,11 +178,27 @@ module Solargraph
151
178
  ora = find_parent(stack, :or_asgn)
152
179
  unless ora.nil?
153
180
  u = c.updated(:cvasgn, c.children + ora.children[1..-1], nil)
181
+ @node_tree[u] = @node_stack.clone
154
182
  class_variable_pins.push Solargraph::Pin::ClassVariable.new(self, u, fqn || '')
155
183
  end
156
184
  else
157
185
  class_variable_pins.push Solargraph::Pin::ClassVariable.new(self, c, fqn || '')
158
186
  end
187
+ elsif c.type == :lvasgn
188
+ if c.children[1].nil?
189
+ ora = find_parent(stack, :or_asgn)
190
+ unless ora.nil?
191
+ u = c.updated(:lvasgn, c.children + ora.children[1..-1], nil)
192
+ @node_tree[u] = @node_stack.clone
193
+ @docstring_hash[u.loc] = docstring_for(ora)
194
+ local_variable_pins.push Solargraph::Pin::LocalVariable.new(self, u, fqn || '', @node_stack.clone)
195
+ end
196
+ else
197
+ @node_tree[c] = @node_stack.clone
198
+ local_variable_pins.push Solargraph::Pin::LocalVariable.new(self, c, fqn || '', @node_stack.clone)
199
+ end
200
+ elsif c.type == :gvasgn
201
+ global_variable_pins.push Solargraph::Pin::GlobalVariable.new(self, c, fqn || '')
159
202
  elsif c.type == :sym
160
203
  symbol_pins.push Solargraph::Pin::Symbol.new(self, c, fqn)
161
204
  elsif c.type == :casgn
@@ -165,8 +208,8 @@ module Solargraph
165
208
  if c.kind_of?(AST::Node)
166
209
  if c.type == :def and c.children[0].to_s[0].match(/[a-z]/i)
167
210
  method_pins.push Solargraph::Pin::Method.new(source, c, fqn, scope, visibility)
168
- inner_map_node c, tree, visibility, scope, fqn, stack
169
- next
211
+ #inner_map_node c, tree, visibility, scope, fqn, stack
212
+ #next
170
213
  elsif c.type == :defs
171
214
  method_pins.push Solargraph::Pin::Method.new(source, c, fqn, :class, :public)
172
215
  inner_map_node c, tree, :public, :class, fqn, stack
@@ -181,10 +224,10 @@ module Solargraph
181
224
  elsif c.type == :send and [:attr_reader, :attr_writer, :attr_accessor].include?(c.children[1])
182
225
  c.children[2..-1].each do |a|
183
226
  if c.children[1] == :attr_reader or c.children[1] == :attr_accessor
184
- attribute_pins.push Solargraph::Pin::Attribute.new(self, a, fqn, :reader) #AttrPin.new(c)
227
+ attribute_pins.push Solargraph::Pin::Attribute.new(self, a, fqn, :reader, docstring_for(c)) #AttrPin.new(c)
185
228
  end
186
229
  if c.children[1] == :attr_writer or c.children[1] == :attr_accessor
187
- attribute_pins.push Solargraph::Pin::Attribute.new(self, a, fqn, :writer) #AttrPin.new(c)
230
+ attribute_pins.push Solargraph::Pin::Attribute.new(self, a, fqn, :writer, docstring_for(c)) #AttrPin.new(c)
188
231
  end
189
232
  end
190
233
  elsif c.type == :sclass and c.children[0].type == :self
@@ -198,10 +241,11 @@ module Solargraph
198
241
  required.push c.children[2].children[0].to_s
199
242
  end
200
243
  end
201
- inner_map_node c, tree, visibility, scope, fqn, stack
202
244
  end
245
+ inner_map_node c, tree, visibility, scope, fqn, stack
203
246
  end
204
247
  end
248
+ @node_stack.shift
205
249
  end
206
250
  stack.pop
207
251
  end
@@ -220,11 +264,13 @@ module Solargraph
220
264
  Source.virtual(filename, code)
221
265
  end
222
266
 
267
+ # @return [Solargraph::ApiMap::Source]
223
268
  def virtual filename, code
224
269
  node, comments = Parser::CurrentRuby.parse_with_comments(code)
225
270
  Source.new(code, node, comments, filename)
226
271
  end
227
272
 
273
+ # @return [Solargraph::ApiMap::Source]
228
274
  def fix filename, code, cursor = nil
229
275
  tries = 0
230
276
  code.gsub!(/\r/, '')
@@ -236,10 +282,6 @@ module Solargraph
236
282
  # incomplete trees resulting from short scripts (e.g., a lone variable
237
283
  # assignment).
238
284
  node, comments = Parser::CurrentRuby.parse_with_comments(tmp + "\n_")
239
- #@node = self.api_map.append_node(node, @comments, filename)
240
- #@parsed = tmp
241
- #@code.freeze
242
- #@parsed.freeze
243
285
  Source.new(code, node, comments, filename)
244
286
  rescue Parser::SyntaxError => e
245
287
  if tries < 10
@@ -247,10 +289,10 @@ module Solargraph
247
289
  if tries == 10 and e.message.include?('token $end')
248
290
  tmp += "\nend"
249
291
  else
250
- if !fixed_cursor and !cursor.nil? and e.message.include?('token $end') and cursor >= 2
292
+ if !fixed_cursor and !cursor.nil? and cursor >= 2 and (e.message.include?('token $end') or tmp[cursor - 1] == '$')
251
293
  fixed_cursor = true
252
294
  spot = cursor - 2
253
- if tmp[cursor - 1] == '.'
295
+ if tmp[cursor - 1] == '.' or tmp[cursor - 1] == '$'
254
296
  repl = ';'
255
297
  else
256
298
  repl = '#'
@@ -9,8 +9,6 @@ module Solargraph
9
9
  autoload :Cache, 'solargraph/api_map/cache'
10
10
 
11
11
  @@source_cache = {}
12
- @@yard_map_cache = {}
13
- @@semaphore = Mutex.new
14
12
 
15
13
  KEYWORDS = [
16
14
  '__ENCODING__', '__LINE__', '__FILE__', 'BEGIN', 'END', 'alias', 'and',
@@ -27,10 +25,6 @@ module Solargraph
27
25
  include NodeMethods
28
26
  include YardMethods
29
27
 
30
- # @todo Temp for testing
31
- attr_reader :new_method_pins
32
- attr_reader :new_ivar_pins
33
-
34
28
  # The root directory of the project. The ApiMap will search here for
35
29
  # additional files to parse and analyze.
36
30
  #
@@ -49,30 +43,42 @@ module Solargraph
49
43
  config = ApiMap::Config.new(@workspace)
50
44
  @workspace_files.concat (config.included - config.excluded)
51
45
  @workspace_files.each do |wf|
52
- @@source_cache[wf] ||= Source.load(wf)
46
+ begin
47
+ @@source_cache[wf] ||= Source.load(wf)
48
+ rescue
49
+ STDERR.puts "Failed to load #{wf}"
50
+ end
53
51
  end
54
52
  end
55
53
  @sources = {}
56
54
  @virtual_source = nil
57
55
  @virtual_filename = nil
58
- @stale = false
59
- @new_method_pins = []
60
- @new_ivar_pins = []
56
+ @stale = true
57
+ refresh
61
58
  end
62
59
 
63
60
  # @return [Solargraph::YardMap]
64
61
  def yard_map
65
- @@semaphore.synchronize {
66
- @@yard_map_cache[[required, workspace]] ||= Solargraph::YardMap.new(required: required, workspace: workspace)
67
- }
62
+ refresh
63
+ if @yard_map.nil? || @yard_map.required != required
64
+ @yard_map = Solargraph::YardMap.new(required: required, workspace: workspace)
65
+ end
66
+ @yard_map
68
67
  end
69
68
 
69
+ # @return [Solargraph::ApiMap::Source]
70
70
  def virtualize filename, code, cursor = nil
71
- @stale = true
71
+ unless @virtual_source.nil? or @virtual_filename == filename or @workspace_files.include?(@virtual_filename)
72
+ eliminate @virtual_filename
73
+ end
74
+ refresh
72
75
  @virtual_filename = filename
73
76
  @virtual_source = Source.fix(filename, code, cursor)
77
+ process_virtual
78
+ @virtual_source
74
79
  end
75
80
 
81
+ # @return [Solargraph::ApiMap::Source]
76
82
  def append_source code, filename
77
83
  virtualize filename, code
78
84
  end
@@ -115,18 +121,26 @@ module Solargraph
115
121
  result
116
122
  end
117
123
 
124
+ # @return [Array<Solargraph::Pin::Constant>]
125
+ def get_constant_pins namespace, root
126
+ fqns = find_fully_qualified_namespace(namespace, root)
127
+ @const_pins[fqns] || []
128
+ end
129
+
130
+ # @return [Array<Solargraph::Suggestion>]
118
131
  def get_constants namespace, root
119
132
  result = []
120
133
  fqns = find_fully_qualified_namespace(namespace, root)
121
134
  cp = @const_pins[fqns]
122
135
  unless cp.nil?
123
136
  cp.each do |pin|
124
- result.push Suggestion.pull(pin, resolve_pin_return_type(pin))
137
+ result.push pin_to_suggestion(pin)
125
138
  end
126
139
  end
127
140
  result.concat yard_map.get_constants(namespace, root)
128
141
  end
129
142
 
143
+ # @return [String]
130
144
  def find_fully_qualified_namespace name, root = '', skip = []
131
145
  refresh
132
146
  return nil if skip.include?(root)
@@ -168,40 +182,51 @@ module Solargraph
168
182
  @namespace_map[fqns] || []
169
183
  end
170
184
 
185
+ # @return [Array<Solargraph::Pin::InstanceVariable>]
186
+ def get_instance_variable_pins(namespace, scope = :instance)
187
+ refresh
188
+ (@ivar_pins[namespace] || []).select{ |pin| pin.scope == scope }
189
+ end
190
+
191
+ # @return [Array<Solargraph::Suggestion>]
171
192
  def get_instance_variables(namespace, scope = :instance)
172
193
  refresh
173
194
  result = []
174
195
  ip = @ivar_pins[namespace]
175
196
  unless ip.nil?
176
197
  ip.select{ |pin| pin.scope == scope }.each do |pin|
177
- #result.push pin.suggestion
178
- result.push Suggestion.pull(pin, resolve_pin_return_type(pin))
198
+ result.push pin_to_suggestion(pin)
179
199
  end
180
200
  end
181
201
  result
182
202
  end
183
203
 
204
+ # @return [Array<Solargraph::Pin::ClassVariable>]
205
+ def get_class_variable_pins(namespace)
206
+ refresh
207
+ @cvar_pins[namespace] || []
208
+ end
209
+
210
+ # @return [Array<Solargraph::Suggestion>]
184
211
  def get_class_variables(namespace)
185
212
  refresh
186
213
  result = []
187
214
  ip = @cvar_pins[namespace]
188
215
  unless ip.nil?
189
216
  ip.each do |pin|
190
- result.push Suggestion.pull(pin, resolve_pin_return_type(pin))
217
+ result.push pin_to_suggestion(pin)
191
218
  end
192
219
  end
193
220
  result
194
221
  end
195
222
 
223
+ # @return [Array<Solargraph::Pin::Symbol>]
196
224
  def get_symbols
197
225
  refresh
198
- result = []
199
- @symbol_pins.each do |s|
200
- result.push s
201
- end
202
- result
226
+ @symbol_pins.uniq(&:label)
203
227
  end
204
228
 
229
+ # @return [String]
205
230
  def get_filename_for(node)
206
231
  @sources.each do |filename, source|
207
232
  return source.filename if source.include?(node)
@@ -209,6 +234,7 @@ module Solargraph
209
234
  nil
210
235
  end
211
236
 
237
+ # @return [String]
212
238
  def infer_instance_variable(var, namespace, scope)
213
239
  refresh
214
240
  pins = @ivar_pins[namespace]
@@ -218,6 +244,7 @@ module Solargraph
218
244
  pin.return_type
219
245
  end
220
246
 
247
+ # @return [String]
221
248
  def infer_class_variable(var, namespace)
222
249
  refresh
223
250
  fqns = find_fully_qualified_namespace(namespace)
@@ -228,11 +255,18 @@ module Solargraph
228
255
  pin.return_type
229
256
  end
230
257
 
258
+ # @return [Array<Solargraph::Suggestion>]
231
259
  def get_global_variables
232
- # TODO: Get them
233
- []
260
+ result = []
261
+ @sources.values.each do |s|
262
+ s.global_variable_pins.each do |p|
263
+ result.push pin_to_suggestion(p)
264
+ end
265
+ end
266
+ result
234
267
  end
235
268
 
269
+ # @return [String]
236
270
  def infer_assignment_node_type node, namespace
237
271
  type = cache.get_assignment_node_type(node, namespace)
238
272
  if type.nil?
@@ -261,6 +295,7 @@ module Solargraph
261
295
  type
262
296
  end
263
297
 
298
+ # @return [String]
264
299
  def infer_signature_type signature, namespace, scope: :class
265
300
  if cache.has_signature_type?(signature, namespace, scope)
266
301
  return cache.get_signature_type(signature, namespace, scope)
@@ -322,16 +357,16 @@ module Solargraph
322
357
  refresh
323
358
  namespace = clean_namespace_string(namespace)
324
359
  meths = []
325
- meths += inner_get_methods(namespace, root, []) #unless has_yardoc?
360
+ meths.concat inner_get_methods(namespace, root, []) #unless has_yardoc?
326
361
  yard_meths = yard_map.get_methods(namespace, root, visibility: visibility)
327
362
  if yard_meths.any?
328
363
  meths.concat yard_meths
329
364
  else
330
365
  type = get_namespace_type(namespace, root)
331
366
  if type == :class
332
- meths += yard_map.get_instance_methods('Class')
367
+ meths.concat yard_map.get_instance_methods('Class')
333
368
  elsif type == :module
334
- meths += yard_map.get_methods('Module')
369
+ meths.concat yard_map.get_methods('Module')
335
370
  end
336
371
  end
337
372
  news = meths.select{|s| s.label == 'new'}
@@ -341,7 +376,7 @@ module Solargraph
341
376
  inits = @method_pins[fqns].select{|p| p.name == 'initialize'}
342
377
  meths -= news unless inits.empty?
343
378
  inits.each do |pin|
344
- meths.push Suggestion.new('new', kind: pin.kind, documentation: pin.docstring, detail: pin.namespace, arguments: pin.parameters, path: pin.path)
379
+ meths.push Suggestion.new('new', kind: pin.kind, docstring: pin.docstring, detail: pin.namespace, arguments: pin.parameters, path: pin.path)
345
380
  end
346
381
  end
347
382
  end
@@ -396,6 +431,27 @@ module Solargraph
396
431
  @sources.values
397
432
  end
398
433
 
434
+ def get_path_suggestions path
435
+ result = []
436
+ if path.include?('#')
437
+ # It's an instance method
438
+ parts = path.split('#')
439
+ result = get_instance_methods(parts[0], '', visibility: [:public, :private, :protected]).select{|s| s.label == parts[1]}
440
+ elsif path.include?('.')
441
+ # It's a class method
442
+ parts = path.split('.')
443
+ result = get_instance_methods(parts[0], '', visibility: [:public, :private, :protected]).select{|s| s.label == parts[1]}
444
+ else
445
+ # It's a class or module
446
+ get_namespace_nodes(path).each do |node|
447
+ # TODO This is way underimplemented
448
+ result.push Suggestion.new(path, kind: Suggestion::CLASS)
449
+ end
450
+ result.concat yard_map.objects(path)
451
+ end
452
+ result
453
+ end
454
+
399
455
  private
400
456
 
401
457
  def clear
@@ -409,8 +465,12 @@ module Solargraph
409
465
  def process_maps
410
466
  @sources.clear
411
467
  @workspace_files.each do |f|
412
- @@source_cache[f] ||= Source.load(f)
413
- @sources[f] = @@source_cache[f]
468
+ begin
469
+ @@source_cache[f] ||= Source.load(f)
470
+ @sources[f] = @@source_cache[f]
471
+ rescue
472
+ STDERR.puts "Failed to load #{f}"
473
+ end
414
474
  end
415
475
  cache.clear
416
476
  @ivar_pins = {}
@@ -425,6 +485,7 @@ module Solargraph
425
485
  @namespace_map = {}
426
486
  @namespace_tree = {}
427
487
  @required = []
488
+ @pin_suggestions = {}
428
489
  unless @virtual_source.nil?
429
490
  @sources[@virtual_filename] = @virtual_source
430
491
  end
@@ -442,6 +503,31 @@ module Solargraph
442
503
  @stale = false
443
504
  end
444
505
 
506
+ def process_virtual
507
+ unless @virtual_source.nil?
508
+ cache.clear
509
+ @sources[@virtual_filename] = @virtual_source
510
+ @sources.values.each do |s|
511
+ s.namespace_nodes.each_pair do |k, v|
512
+ @namespace_map[k] ||= []
513
+ @namespace_map[k].concat v
514
+ add_to_namespace_tree k.split('::')
515
+ end
516
+ end
517
+ eliminate @virtual_filename
518
+ map_source @virtual_source
519
+ end
520
+ end
521
+
522
+ def eliminate filename
523
+ [@ivar_pins.values, @cvar_pins.values, @const_pins.values, @method_pins.values, @attr_pins.values].each do |pinsets|
524
+ pinsets.each do |pins|
525
+ pins.delete_if{|pin| pin.filename == filename}
526
+ end
527
+ end
528
+ #@symbol_pins.delete_if{|pin| pin.filename == filename}
529
+ end
530
+
445
531
  # @param [Solargraph::ApiMap::Source]
446
532
  def map_source source
447
533
  source.method_pins.each do |pin|
@@ -484,7 +570,7 @@ module Solargraph
484
570
  @cache ||= Cache.new
485
571
  end
486
572
 
487
- def inner_get_methods(namespace, root = '', skip = [])
573
+ def inner_get_methods(namespace, root = '', skip = [], visibility = [:public])
488
574
  meths = []
489
575
  return meths if skip.include?(namespace)
490
576
  skip.push namespace
@@ -493,7 +579,14 @@ module Solargraph
493
579
  mn = @method_pins[fqns]
494
580
  unless mn.nil?
495
581
  mn.select{ |pin| pin.scope == :class }.each do |pin|
496
- meths.push Suggestion.pull(pin)
582
+ meths.push pin_to_suggestion(pin)
583
+ end
584
+ end
585
+ if visibility.include?(:public) or visibility.include?(:protected)
586
+ sc = @superclasses[fqns]
587
+ unless sc.nil?
588
+ meths.concat inner_get_methods(sc, fqns, skip, visibility - [:private])
589
+ meths.concat yard_map.get_methods(sc, fqns, visibility: visibility - [:private])
497
590
  end
498
591
  end
499
592
  meths.uniq
@@ -507,13 +600,13 @@ module Solargraph
507
600
  an = @attr_pins[fqns]
508
601
  unless an.nil?
509
602
  an.each do |pin|
510
- meths.push Suggestion.pull(pin)
603
+ meths.push pin_to_suggestion(pin)
511
604
  end
512
605
  end
513
606
  mn = @method_pins[fqns]
514
607
  unless mn.nil?
515
608
  mn.select{|pin| visibility.include?(pin.visibility) and pin.scope == :instance }.each do |pin|
516
- meths.push Suggestion.pull(pin)
609
+ meths.push pin_to_suggestion(pin)
517
610
  end
518
611
  end
519
612
  if visibility.include?(:public) or visibility.include?(:protected)
@@ -561,7 +654,7 @@ module Solargraph
561
654
  cp = @const_pins[fqns]
562
655
  unless cp.nil?
563
656
  cp.each do |pin|
564
- result.push Suggestion.pull(pin, resolve_pin_return_type(pin))
657
+ result.push pin_to_suggestion(pin)
565
658
  end
566
659
  end
567
660
  inc = @namespace_includes[fqns]
@@ -582,85 +675,48 @@ module Solargraph
582
675
  #
583
676
  # @return [String] The fully qualified namespace for the signature's type
584
677
  # or nil if a type could not be determined
585
- def inner_infer_signature_type signature, namespace, scope: :instance
586
- orig = namespace
587
- namespace = clean_namespace_string(namespace)
678
+ def inner_infer_signature_type signature, namespace, scope: :instance, top: true
588
679
  return nil if signature.nil?
589
680
  signature.gsub!(/\.$/, '')
590
- if signature.nil? or signature.empty?
681
+ if signature.empty?
591
682
  if scope == :class
592
- return "#{namespace}#class"
683
+ return "Class<#{namespace}>"
593
684
  else
594
685
  return "#{namespace}"
595
686
  end
596
687
  end
597
- if !namespace.nil? and namespace.end_with?('#class')
598
- return inner_infer_signature_type signature, namespace[0..-7], scope: (scope == :class ? :instance : :class)
599
- end
600
688
  parts = signature.split('.')
601
- type = find_fully_qualified_namespace(namespace)
602
- type ||= ''
603
- top = true
604
- while parts.length > 0 and !type.nil?
605
- p = parts.shift
606
- next if p.empty?
607
- next if !type.nil? and !type.empty? and METHODS_RETURNING_SELF.include?(p)
608
- if top and scope == :class
609
- if p == 'self'
610
- top = false
611
- return "Class<#{type}>" if parts.empty?
612
- sub = inner_infer_signature_type(parts.join('.'), type, scope: :class)
613
- return sub unless sub.to_s == ''
614
- next
615
- end
616
- if p == 'new'
617
- scope = :instance
618
- type = namespace
619
- top = false
620
- next
621
- end
622
- first_class = find_fully_qualified_namespace(p, namespace)
623
- sub = nil
624
- sub = inner_infer_signature_type(parts.join('.'), first_class, scope: :class) unless first_class.nil?
625
- return sub unless sub.to_s == ''
689
+ type = namespace || ''
690
+ while (parts.length > 0)
691
+ part = parts.shift
692
+ if top == true and part == 'self'
693
+ top = false
694
+ next
626
695
  end
627
- if top and scope == :instance and p == 'self'
628
- return type if parts.empty?
629
- sub = infer_signature_type(parts.join('.'), type, scope: :instance)
630
- return sub unless sub.to_s == ''
696
+ cls_match = type.match(/^Class<([A-Za-z0-9_:]*?)>$/)
697
+ if cls_match
698
+ type = cls_match[1]
699
+ scope = :class
631
700
  end
632
- if top and scope == :instance and p == '[]' and !orig.nil?
633
- if orig.start_with?('Array<')
634
- match = orig.match(/Array<([a-z0-9:_]*)/i)[1]
635
- type = match
636
- next
637
- end
638
- end
639
- unless p == 'new' and scope != :instance
701
+ if scope == :class and part == 'new'
702
+ scope = :instance
703
+ elsif !METHODS_RETURNING_SELF.include?(part)
704
+ visibility = [:public]
705
+ visibility.concat [:private, :protected] if top
640
706
  if scope == :instance
641
- visibility = [:public]
642
- visibility.push :private, :protected if top
643
- meths = get_instance_methods(type, visibility: visibility)
644
- meths += get_methods('') if top or type.to_s == ''
707
+ meth = get_instance_methods(namespace, visibility: visibility).select{|s| s.label == part}.first
645
708
  else
646
- meths = get_methods(type)
647
- end
648
- meths.delete_if{ |m| m.insert != p }
649
- return nil if meths.empty?
650
- type = nil
651
- match = meths[0].return_type
652
- unless match.nil?
653
- cleaned = clean_namespace_string(match)
654
- if cleaned.end_with?('#class')
655
- return inner_infer_signature_type(parts.join('.'), cleaned.split('#').first, scope: :class)
656
- else
657
- type = find_fully_qualified_namespace(cleaned)
658
- end
709
+ meth = get_methods(namespace, visibility: visibility).select{|s| s.label == part}.first
659
710
  end
711
+ return nil if meth.nil? or meth.return_type.nil?
712
+ type = meth.return_type
713
+ scope = :instance
660
714
  end
661
- scope = :instance
662
715
  top = false
663
716
  end
717
+ if scope == :class
718
+ type = "Class<#{type}>"
719
+ end
664
720
  type
665
721
  end
666
722
 
@@ -685,13 +741,10 @@ module Solargraph
685
741
  result
686
742
  end
687
743
 
688
- def resolve_pin_return_type pin
689
- return pin.return_type unless pin.return_type.nil?
690
- return nil if pin.signature.nil?
691
- # Avoid infinite loops from variable assignments that reference themselves
692
- return nil if pin.name == pin.signature.split('.').first
693
- infer_signature_type(pin.signature, pin.namespace)
744
+ # @param pin [Solargraph::Pin::Base]
745
+ # @return [Solargraph::Suggestion]
746
+ def pin_to_suggestion pin
747
+ @pin_suggestions[pin] ||= Suggestion.pull(pin)
694
748
  end
695
-
696
749
  end
697
750
  end