solargraph 0.12.2 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1ff8b6c3f8f03cacb73e9a6b398d3f984dc2e240
4
- data.tar.gz: 58449ce1fd531e8426bcbad2733487a66a23ff3a
3
+ metadata.gz: 031c6210f7be6462d12e8ce7dd5c25e30b6c097d
4
+ data.tar.gz: 422def5aafa30d23831434dafc5a69eb26d97e55
5
5
  SHA512:
6
- metadata.gz: dc8c5611342e2be6c3fbcd354663e611523b2aaf756f738a08f20bcd1a4ca8a9e6a278a64cb31541d09e8243be8170e2c7588de827ae6d36cf95253594eed3cc
7
- data.tar.gz: a1d6666d9098e565b2d663a5d1ece4a617fb3ea1d0a97ca4703f4d114656afb831552b60345b9970abb6081028bc623ad65e117dfb98f20029f120e2a665f437
6
+ metadata.gz: e597e62a01501ee615fcac8a194940e9d85ea69346eeb245104340deb9d2db69de1553facb00d9b6e1dc9c9c3b932c8b5f4cda4a00e43bd41be240548633f8f0
7
+ data.tar.gz: 6137318fc634b925cc99a86b77b0ca86ae8c7f1ca842f4a864f93b88531151e3a96adc596aeb99fa94cf10c09e690432284090981e3c70b56151e72772d7dd95
@@ -14,6 +14,7 @@ module Solargraph
14
14
  autoload :Server, 'solargraph/server'
15
15
  autoload :YardMap, 'solargraph/yard_map'
16
16
  autoload :YardMethods, 'solargraph/yard_methods'
17
+ autoload :Pin, 'solargraph/pin'
17
18
 
18
19
  YARDOC_PATH = File.join(File.realpath(File.dirname(__FILE__)), '..', 'yardoc')
19
20
  YARD_EXTENSION_FILE = File.join(File.realpath(File.dirname(__FILE__)), 'yard-solargraph.rb')
@@ -4,13 +4,11 @@ require 'thread'
4
4
 
5
5
  module Solargraph
6
6
  class ApiMap
7
- autoload :Config, 'solargraph/api_map/config'
8
- autoload :Cache, 'solargraph/api_map/cache'
9
- autoload :MethodPin, 'solargraph/api_map/method_pin'
10
- autoload :AttrPin, 'solargraph/api_map/attr_pin'
11
- autoload :IvarPin, 'solargraph/api_map/ivar_pin'
12
- autoload :CvarPin, 'solargraph/api_map/cvar_pin'
7
+ autoload :Config, 'solargraph/api_map/config'
8
+ autoload :Source, 'solargraph/api_map/source'
9
+ autoload :Cache, 'solargraph/api_map/cache'
13
10
 
11
+ @@source_cache = {}
14
12
  @@yard_map_cache = {}
15
13
  @@semaphore = Mutex.new
16
14
 
@@ -22,17 +20,6 @@ module Solargraph
22
20
  'then', 'true', 'undef', 'unless', 'until', 'when', 'while', 'yield'
23
21
  ].freeze
24
22
 
25
- MAPPABLE_NODES = [
26
- :array, :hash, :str, :dstr, :int, :float, :sym, :block, :class, :sclass,
27
- :module, :def, :defs, :ivasgn, :gvasgn, :lvasgn, :cvasgn, :casgn,
28
- :or_asgn, :const, :lvar, :args, :kwargs
29
- ].freeze
30
-
31
- MAPPABLE_METHODS = [
32
- :include, :extend, :require, :autoload, :attr_reader, :attr_writer,
33
- :attr_accessor, :private, :public, :protected
34
- ].freeze
35
-
36
23
  METHODS_RETURNING_SELF = [
37
24
  'clone', 'dup', 'freeze', 'taint', 'untaint'
38
25
  ].freeze
@@ -40,6 +27,10 @@ module Solargraph
40
27
  include NodeMethods
41
28
  include YardMethods
42
29
 
30
+ # @todo Temp for testing
31
+ attr_reader :new_method_pins
32
+ attr_reader :new_ivar_pins
33
+
43
34
  # The root directory of the project. The ApiMap will search here for
44
35
  # additional files to parse and analyze.
45
36
  #
@@ -53,61 +44,37 @@ module Solargraph
53
44
  def initialize workspace = nil
54
45
  @workspace = workspace.gsub(/\\/, '/') unless workspace.nil?
55
46
  clear
47
+ @workspace_files = []
56
48
  unless @workspace.nil?
57
49
  config = ApiMap::Config.new(@workspace)
58
- config.included.each { |f|
59
- unless config.excluded.include?(f)
60
- append_file f
61
- end
62
- }
50
+ @workspace_files.concat (config.included - config.excluded)
51
+ @workspace_files.each do |wf|
52
+ @@source_cache[wf] ||= Source.load(wf)
53
+ end
63
54
  end
55
+ @sources = {}
56
+ @virtual_source = nil
57
+ @virtual_filename = nil
58
+ @stale = false
59
+ @new_method_pins = []
60
+ @new_ivar_pins = []
64
61
  end
65
62
 
66
63
  # @return [Solargraph::YardMap]
67
64
  def yard_map
68
65
  @@semaphore.synchronize {
69
- @yard_map ||= @@yard_map_cache[[required, workspace]] || Solargraph::YardMap.new(required: required, workspace: workspace)
70
- @@yard_map_cache[[required, workspace]] ||= @yard_map
66
+ @@yard_map_cache[[required, workspace]] ||= Solargraph::YardMap.new(required: required, workspace: workspace)
71
67
  }
72
68
  end
73
69
 
74
- # Add a file to the map.
75
- #
76
- # @param filename [String]
77
- # @return [AST::Node]
78
- def append_file filename
79
- append_source File.read(filename), filename
80
- end
81
-
82
- # Add a string of source code to the map.
83
- #
84
- # @param text [String]
85
- # @param filename [String]
86
- # @return [AST::Node]
87
- def append_source text, filename = nil
88
- @file_source[filename] = text
89
- begin
90
- node, comments = Parser::CurrentRuby.parse_with_comments(text)
91
- append_node(node, comments, filename)
92
- rescue Parser::SyntaxError => e
93
- STDERR.puts "Error parsing '#{filename}': #{e.message}"
94
- nil
95
- end
70
+ def virtualize filename, code, cursor = nil
71
+ @stale = true
72
+ @virtual_filename = filename
73
+ @virtual_source = Source.fix(filename, code, cursor)
96
74
  end
97
75
 
98
- # Add an AST node to the map.
99
- #
100
- # @return [AST::Node]
101
- def append_node node, comments, filename = nil
102
- @stale = true
103
- @file_comments[filename] = associate_comments(node, comments)
104
- mapified = reduce(node, @file_comments[filename])
105
- root = AST::Node.new(:begin, [filename])
106
- root = root.append mapified
107
- @file_nodes[filename] = root
108
- @required.uniq!
109
- #process_maps
110
- root
76
+ def append_source code, filename
77
+ virtualize filename, code
111
78
  end
112
79
 
113
80
  def refresh force = false
@@ -120,13 +87,13 @@ module Solargraph
120
87
  # @return [YARD::Docstring]
121
88
  def get_comment_for node
122
89
  filename = get_filename_for(node)
123
- return nil if @file_comments[filename].nil?
124
- @file_comments[filename][node.loc]
90
+ return nil if @sources[filename].nil?
91
+ @sources[filename].docstring_for(node)
125
92
  end
126
93
 
127
94
  # @return [Array<Solargraph::Suggestion>]
128
95
  def self.get_keywords
129
- @keyword_suggestions ||= (KEYWORDS + MAPPABLE_METHODS).map{ |s|
96
+ @keyword_suggestions ||= KEYWORDS.map{ |s|
130
97
  Suggestion.new(s.to_s, kind: Suggestion::KEYWORD, detail: 'Keyword')
131
98
  }.freeze
132
99
  end
@@ -145,16 +112,21 @@ module Solargraph
145
112
  result = []
146
113
  result += inner_namespaces_in(name, root, [])
147
114
  result += yard_map.get_constants name, root
148
- fqns = find_fully_qualified_namespace(name, root)
149
- unless fqns.nil?
150
- nodes = get_namespace_nodes(fqns)
151
- get_include_strings_from(*nodes).each { |i|
152
- result += yard_map.get_constants(i, root)
153
- }
154
- end
155
115
  result
156
116
  end
157
117
 
118
+ def get_constants namespace, root
119
+ result = []
120
+ fqns = find_fully_qualified_namespace(namespace, root)
121
+ cp = @const_pins[fqns]
122
+ unless cp.nil?
123
+ cp.each do |pin|
124
+ result.push Suggestion.pull(pin, resolve_pin_return_type(pin))
125
+ end
126
+ end
127
+ result.concat yard_map.get_constants(namespace, root)
128
+ end
129
+
158
130
  def find_fully_qualified_namespace name, root = '', skip = []
159
131
  refresh
160
132
  return nil if skip.include?(root)
@@ -168,7 +140,7 @@ module Solargraph
168
140
  else
169
141
  if (root == '')
170
142
  return name unless @namespace_map[name].nil?
171
- get_include_strings_from(*@file_nodes.values).each { |i|
143
+ get_include_strings_from(*file_nodes).each { |i|
172
144
  reroot = "#{root == '' ? '' : root + '::'}#{i}"
173
145
  recname = find_fully_qualified_namespace name.to_s, reroot, skip
174
146
  return recname unless recname.nil?
@@ -181,7 +153,7 @@ module Solargraph
181
153
  roots.pop
182
154
  end
183
155
  return name unless @namespace_map[name].nil?
184
- get_include_strings_from(*@file_nodes.values).each { |i|
156
+ get_include_strings_from(*file_nodes).each { |i|
185
157
  recname = find_fully_qualified_namespace name, i, skip
186
158
  return recname unless recname.nil?
187
159
  }
@@ -191,7 +163,7 @@ module Solargraph
191
163
  end
192
164
 
193
165
  def get_namespace_nodes(fqns)
194
- return @file_nodes.values if fqns == '' or fqns.nil?
166
+ return file_nodes if fqns == '' or fqns.nil?
195
167
  refresh
196
168
  @namespace_map[fqns] || []
197
169
  end
@@ -202,7 +174,8 @@ module Solargraph
202
174
  ip = @ivar_pins[namespace]
203
175
  unless ip.nil?
204
176
  ip.select{ |pin| pin.scope == scope }.each do |pin|
205
- result.push pin.suggestion
177
+ #result.push pin.suggestion
178
+ result.push Suggestion.pull(pin, resolve_pin_return_type(pin))
206
179
  end
207
180
  end
208
181
  result
@@ -214,114 +187,45 @@ module Solargraph
214
187
  ip = @cvar_pins[namespace]
215
188
  unless ip.nil?
216
189
  ip.each do |pin|
217
- result.push pin.suggestion
190
+ result.push Suggestion.pull(pin, resolve_pin_return_type(pin))
218
191
  end
219
192
  end
220
193
  result
221
194
  end
222
195
 
223
- def find_parent(node, *types)
224
- parents = @parent_stack[node]
225
- parents.each { |p|
226
- return p if types.include?(p.type)
227
- }
228
- nil
229
- end
230
-
231
- def get_root_for(node)
232
- s = @parent_stack[node]
233
- return nil if s.nil?
234
- return node if s.empty?
235
- s.last
196
+ def get_symbols
197
+ refresh
198
+ result = []
199
+ @symbol_pins.each do |s|
200
+ result.push s
201
+ end
202
+ result
236
203
  end
237
204
 
238
205
  def get_filename_for(node)
239
- root = get_root_for(node)
240
- root.nil? ? nil : root.children[0]
241
- end
242
-
243
- def yardoc_has_file?(file)
244
- return false if workspace.nil?
245
- if @yardoc_files.nil?
246
- @yardoc_files = []
247
- yard_options[:include].each { |glob|
248
- Dir[File.join workspace, glob].each { |f|
249
- @yardoc_files.push File.absolute_path(f)
250
- }
251
- }
206
+ @sources.each do |filename, source|
207
+ return source.filename if source.include?(node)
252
208
  end
253
- @yardoc_files.include?(file)
209
+ nil
254
210
  end
255
211
 
256
212
  def infer_instance_variable(var, namespace, scope)
257
- result = nil
258
- vn = nil
259
- fqns = find_fully_qualified_namespace(namespace)
260
- unless fqns.nil?
261
- get_namespace_nodes(fqns).each { |node|
262
- vn = find_instance_variable_assignment(var, node, scope)
263
- break unless vn.nil?
264
- }
265
- end
266
- result = infer_assignment_node_type(vn, namespace) unless vn.nil?
267
- result
213
+ refresh
214
+ pins = @ivar_pins[namespace]
215
+ return nil if pins.nil?
216
+ pin = pins.select{|p| p.name == var and p.scope == scope}.first
217
+ return nil if pin.nil?
218
+ pin.return_type
268
219
  end
269
220
 
270
221
  def infer_class_variable(var, namespace)
271
- result = nil
272
- vn = nil
222
+ refresh
273
223
  fqns = find_fully_qualified_namespace(namespace)
274
- unless fqns.nil?
275
- get_namespace_nodes(fqns).each { |node|
276
- vn = find_class_variable_assignment(var, node)
277
- break unless vn.nil?
278
- }
279
- end
280
- unless vn.nil?
281
- cmnt = get_comment_for(vn)
282
- unless cmnt.nil?
283
- tag = cmnt.tag(:type)
284
- result = tag.types[0] unless tag.nil? or tag.types.empty?
285
- end
286
- result = infer_literal_node_type(vn.children[1]) if result.nil?
287
- if result.nil?
288
- signature = resolve_node_signature(vn.children[1])
289
- result = infer_signature_type(signature, namespace)
290
- end
291
- end
292
- result
293
- end
294
-
295
- def find_instance_variable_assignment(var, node, scope)
296
- node.children.each { |c|
297
- if c.kind_of?(AST::Node)
298
- is_inst = !find_parent(c, :def).nil?
299
- if c.type == :ivasgn and ( (scope == :instance and is_inst) or (scope != :instance and !is_inst) )
300
- if c.children[0].to_s == var
301
- return c
302
- end
303
- else
304
- inner = find_instance_variable_assignment(var, c, scope)
305
- return inner unless inner.nil?
306
- end
307
- end
308
- }
309
- nil
310
- end
311
-
312
- def find_class_variable_assignment(var, node)
313
- node.children.each { |c|
314
- next unless c.kind_of?(AST::Node)
315
- if c.type == :cvasgn
316
- if c.children[0].to_s == var
317
- return c
318
- end
319
- else
320
- inner = find_class_variable_assignment(var, c)
321
- return inner unless inner.nil?
322
- end
323
- }
324
- nil
224
+ pins = @cvar_pins[fqns]
225
+ return nil if pins.nil?
226
+ pin = pins.select{|p| p.name == var}.first
227
+ return nil if pin.nil?
228
+ pin.return_type
325
229
  end
326
230
 
327
231
  def get_global_variables
@@ -358,8 +262,9 @@ module Solargraph
358
262
  end
359
263
 
360
264
  def infer_signature_type signature, namespace, scope: :class
361
- cached = cache.get_signature_type(signature, namespace, scope)
362
- return cached unless cached.nil?
265
+ if cache.has_signature_type?(signature, namespace, scope)
266
+ return cache.get_signature_type(signature, namespace, scope)
267
+ end
363
268
  return nil if signature.nil? or signature.empty?
364
269
  result = nil
365
270
  if namespace.end_with?('#class')
@@ -428,8 +333,19 @@ module Solargraph
428
333
  elsif type == :module
429
334
  meths += yard_map.get_methods('Module')
430
335
  end
431
- meths
432
336
  end
337
+ news = meths.select{|s| s.label == 'new'}
338
+ unless news.empty?
339
+ fqns = find_fully_qualified_namespace(namespace, root)
340
+ if @method_pins[fqns]
341
+ inits = @method_pins[fqns].select{|p| p.name == 'initialize'}
342
+ meths -= news unless inits.empty?
343
+ 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)
345
+ end
346
+ end
347
+ end
348
+ meths
433
349
  end
434
350
 
435
351
  # Get an array of instance methods that are available in the specified
@@ -459,27 +375,6 @@ module Solargraph
459
375
  meths
460
376
  end
461
377
 
462
- def get_superclass(namespace, root = '')
463
- fqns = find_fully_qualified_namespace(namespace, root)
464
- nodes = get_namespace_nodes(fqns)
465
- nodes.each { |n|
466
- if n.kind_of?(AST::Node)
467
- if n.type == :class and !n.children[1].nil?
468
- return unpack_name(n.children[1])
469
- end
470
- end
471
- }
472
- return nil
473
- end
474
-
475
- def self.current
476
- if @current.nil?
477
- @current = ApiMap.new
478
- @current.merge(Parser::CurrentRuby.parse(File.read("#{Solargraph::STUB_PATH}/ruby/2.3.0/core.rb")))
479
- end
480
- @current
481
- end
482
-
483
378
  def get_include_strings_from *nodes
484
379
  arr = []
485
380
  nodes.each { |node|
@@ -492,42 +387,19 @@ module Solargraph
492
387
  arr
493
388
  end
494
389
 
495
- def code_for node
496
- src = @file_source[get_filename_for(node)]
497
- return nil if src.nil?
498
- b = node.location.expression.begin.begin_pos
499
- e = node.location.expression.end.end_pos
500
- src[b..e].strip.gsub(/,$/, '')
501
- end
390
+ def update filename
391
+ @@source_cache[filename] ||= Source.load(filename)
392
+ cache.clear
393
+ end
502
394
 
503
- # Update the YARD documentation for the current workspace.
504
- #
505
- def update_yardoc
506
- if workspace.nil?
507
- STDERR.puts "No workspace specified for yardoc update."
508
- else
509
- Dir.chdir(workspace) do
510
- STDERR.puts "Updating the yardoc for #{workspace}..."
511
- cmd = "yardoc -e #{Solargraph::YARD_EXTENSION_FILE}"
512
- STDERR.puts "Update yardoc with #{cmd}"
513
- STDERR.puts `#{cmd}`
514
- unless $?.success?
515
- STDERR.puts "There was an error processing the workspace yardoc."
516
- end
517
- end
518
- @@semaphore.synchronize {
519
- @@yard_map_cache.clear
520
- }
521
- end
395
+ def sources
396
+ @sources.values
522
397
  end
523
398
 
524
399
  private
525
400
 
526
401
  def clear
527
402
  @stale = false
528
- @file_source = {}
529
- @file_nodes = {}
530
- @file_comments = {}
531
403
  @parent_stack = {}
532
404
  @namespace_map = {}
533
405
  @namespace_tree = {}
@@ -535,54 +407,83 @@ module Solargraph
535
407
  end
536
408
 
537
409
  def process_maps
410
+ @sources.clear
411
+ @workspace_files.each do |f|
412
+ @@source_cache[f] ||= Source.load(f)
413
+ @sources[f] = @@source_cache[f]
414
+ end
538
415
  cache.clear
539
416
  @ivar_pins = {}
540
417
  @cvar_pins = {}
418
+ @const_pins = {}
541
419
  @method_pins = {}
542
- @attr_nodes = {}
420
+ @symbol_pins = []
421
+ @attr_pins = {}
543
422
  @namespace_includes = {}
544
423
  @superclasses = {}
545
424
  @parent_stack = {}
546
425
  @namespace_map = {}
547
426
  @namespace_tree = {}
548
- @file_nodes.values.each { |f|
549
- map_parents f
550
- map_namespaces f
427
+ @required = []
428
+ unless @virtual_source.nil?
429
+ @sources[@virtual_filename] = @virtual_source
430
+ end
431
+ @sources.values.each do |s|
432
+ s.namespace_nodes.each_pair do |k, v|
433
+ @namespace_map[k] ||= []
434
+ @namespace_map[k].concat v
435
+ add_to_namespace_tree k.split('::')
436
+ end
437
+ end
438
+ @sources.values.each { |s|
439
+ map_source s
551
440
  }
441
+ @required.uniq!
552
442
  @stale = false
553
443
  end
554
444
 
445
+ # @param [Solargraph::ApiMap::Source]
446
+ def map_source source
447
+ source.method_pins.each do |pin|
448
+ @method_pins[pin.namespace] ||= []
449
+ @method_pins[pin.namespace].push pin
450
+ end
451
+ source.attribute_pins.each do |pin|
452
+ @attr_pins[pin.namespace] ||= []
453
+ @attr_pins[pin.namespace].push pin
454
+ end
455
+ source.instance_variable_pins.each do |pin|
456
+ @ivar_pins[pin.namespace] ||= []
457
+ @ivar_pins[pin.namespace].push pin
458
+ end
459
+ source.class_variable_pins.each do |pin|
460
+ @cvar_pins[pin.namespace] ||= []
461
+ @cvar_pins[pin.namespace].push pin
462
+ end
463
+ source.constant_pins.each do |pin|
464
+ @const_pins[pin.namespace] ||= []
465
+ @const_pins[pin.namespace].push pin
466
+ end
467
+ source.symbol_pins.each do |pin|
468
+ @symbol_pins.push Suggestion.new(pin.name, kind: Suggestion::CONSTANT, return_type: 'Symbol')
469
+ end
470
+ source.namespace_includes.each_pair do |ns, i|
471
+ @namespace_includes[ns] ||= []
472
+ @namespace_includes[ns].concat(i).uniq!
473
+ end
474
+ source.superclasses.each_pair do |cls, sup|
475
+ @superclasses[cls] = sup
476
+ end
477
+ source.required.each do |r|
478
+ required.push r
479
+ end
480
+ end
481
+
555
482
  # @return [Solargraph::ApiMap::Cache]
556
483
  def cache
557
484
  @cache ||= Cache.new
558
485
  end
559
486
 
560
- def associate_comments node, comments
561
- comment_hash = Parser::Source::Comment.associate_locations(node, comments)
562
- yard_hash = {}
563
- comment_hash.each_pair { |k, v|
564
- ctxt = ''
565
- num = nil
566
- started = false
567
- v.each { |l|
568
- # Trim the comment and minimum leading whitespace
569
- p = l.text.gsub(/^#/, '')
570
- if num.nil? and !p.strip.empty?
571
- num = p.index(/[^ ]/)
572
- started = true
573
- elsif started and !p.strip.empty?
574
- cur = p.index(/[^ ]/)
575
- num = cur if cur < num
576
- end
577
- if started
578
- ctxt += "#{p[num..-1]}\n"
579
- end
580
- }
581
- yard_hash[k] = YARD::Docstring.parser.parse(ctxt).to_docstring
582
- }
583
- yard_hash
584
- end
585
-
586
487
  def inner_get_methods(namespace, root = '', skip = [])
587
488
  meths = []
588
489
  return meths if skip.include?(namespace)
@@ -592,7 +493,7 @@ module Solargraph
592
493
  mn = @method_pins[fqns]
593
494
  unless mn.nil?
594
495
  mn.select{ |pin| pin.scope == :class }.each do |pin|
595
- meths.push pin.suggestion(self)
496
+ meths.push Suggestion.pull(pin)
596
497
  end
597
498
  end
598
499
  meths.uniq
@@ -603,21 +504,24 @@ module Solargraph
603
504
  meths = []
604
505
  return meths if skip.include?(fqns)
605
506
  skip.push fqns
606
- an = @attr_nodes[fqns]
507
+ an = @attr_pins[fqns]
607
508
  unless an.nil?
608
509
  an.each do |pin|
609
- meths.concat pin.suggestions
510
+ meths.push Suggestion.pull(pin)
610
511
  end
611
512
  end
612
513
  mn = @method_pins[fqns]
613
514
  unless mn.nil?
614
515
  mn.select{|pin| visibility.include?(pin.visibility) and pin.scope == :instance }.each do |pin|
615
- meths.push pin.suggestion(self)
516
+ meths.push Suggestion.pull(pin)
616
517
  end
617
518
  end
618
519
  if visibility.include?(:public) or visibility.include?(:protected)
619
520
  sc = @superclasses[fqns]
620
- meths.concat inner_get_instance_methods(sc, fqns, skip, visibility - [:private]) unless sc.nil?
521
+ unless sc.nil?
522
+ meths.concat inner_get_instance_methods(sc, fqns, skip, visibility - [:private])
523
+ meths.concat yard_map.get_instance_methods(sc, fqns, visibility: visibility - [:private])
524
+ end
621
525
  end
622
526
  im = @namespace_includes[fqns]
623
527
  unless im.nil?
@@ -634,7 +538,6 @@ module Solargraph
634
538
  unless fqns.nil? or skip.include?(fqns)
635
539
  skip.push fqns
636
540
  nodes = get_namespace_nodes(fqns)
637
- nodes.delete_if { |n| yardoc_has_file?(get_filename_for(n))}
638
541
  unless nodes.empty?
639
542
  cursor = @namespace_tree
640
543
  parts = fqns.split('::')
@@ -655,29 +558,18 @@ module Solargraph
655
558
  end
656
559
  result.push Suggestion.new(k, kind: kind, detail: detail)
657
560
  }
658
- nodes = get_namespace_nodes(fqns)
659
- nodes.each do |n|
660
- result.concat get_constant_nodes(n, fqns)
661
- get_include_strings_from(n).each { |i|
662
- result += inner_namespaces_in(i, fqns, skip)
663
- }
561
+ cp = @const_pins[fqns]
562
+ unless cp.nil?
563
+ cp.each do |pin|
564
+ result.push Suggestion.pull(pin, resolve_pin_return_type(pin))
565
+ end
566
+ end
567
+ inc = @namespace_includes[fqns]
568
+ unless inc.nil?
569
+ inc.each do |i|
570
+ result.concat inner_namespaces_in(i, fqns, skip)
571
+ end
664
572
  end
665
- end
666
- end
667
- end
668
- result
669
- end
670
-
671
- def get_constant_nodes(node, fqns)
672
- result = []
673
- node.children.each do |n|
674
- if n.kind_of?(AST::Node)
675
- if n.type == :casgn
676
- cmnt = get_comment_for(n)
677
- type = infer_assignment_node_type(n, fqns)
678
- result.push Suggestion.new(n.children[1].to_s, kind: Suggestion::CONSTANT, documentation: cmnt, return_type: type)
679
- else
680
- result.concat get_constant_nodes(n, fqns) unless n.type == :class or n.type == :module
681
573
  end
682
574
  end
683
575
  end
@@ -772,98 +664,6 @@ module Solargraph
772
664
  type
773
665
  end
774
666
 
775
- def mappable?(node)
776
- if node.kind_of?(AST::Node) and MAPPABLE_NODES.include?(node.type)
777
- true
778
- elsif node.kind_of?(AST::Node) and node.type == :send and node.children[0] == nil and MAPPABLE_METHODS.include?(node.children[1])
779
- true
780
- else
781
- false
782
- end
783
- end
784
-
785
- def reduce node, comment_hash
786
- return node unless node.kind_of?(AST::Node)
787
- mappable = get_mappable_nodes(node.children, comment_hash)
788
- result = node.updated nil, mappable
789
- result
790
- end
791
-
792
- def get_mappable_nodes arr, comment_hash
793
- result = []
794
- arr.each { |n|
795
- if mappable?(n)
796
- min = minify(n, comment_hash)
797
- result.push min
798
- else
799
- next unless n.kind_of?(AST::Node)
800
- result += get_mappable_nodes(n.children, comment_hash)
801
- end
802
- }
803
- result
804
- end
805
-
806
- def minify node, comment_hash
807
- return node if node.type == :args
808
- type = node.type
809
- children = []
810
- if node.type == :class or node.type == :block or node.type == :sclass
811
- children += node.children[0, 2]
812
- children += get_mappable_nodes(node.children[2..-1], comment_hash)
813
- elsif node.type == :const or node.type == :args or node.type == :kwargs
814
- children += node.children
815
- elsif node.type == :def
816
- children += node.children[0, 2]
817
- children += get_mappable_nodes(node.children[2..-1], comment_hash)
818
- elsif node.type == :defs
819
- children += node.children[0, 3]
820
- children += get_mappable_nodes(node.children[3..-1], comment_hash)
821
- elsif node.type == :module
822
- children += node.children[0, 1]
823
- children += get_mappable_nodes(node.children[1..-1], comment_hash)
824
- elsif node.type == :ivasgn or node.type == :gvasgn or node.type == :lvasgn or node.type == :cvasgn or node.type == :casgn
825
- children += node.children
826
- elsif node.type == :send and node.children[1] == :include
827
- children += node.children[0,3]
828
- elsif node.type == :send and node.children[1] == :require
829
- if node.children[2].children[0].kind_of?(String)
830
- path = node.children[2].children[0].to_s
831
- @required.push(path) unless local_path?(path)
832
- end
833
- children += node.children[0, 3]
834
- elsif node.type == :send and node.children[1] == :autoload
835
- @required.push(node.children[3].children[0]) if node.children[3].children[0].kind_of?(String)
836
- type = :require
837
- children += node.children[1, 3]
838
- elsif node.type == :send or node.type == :lvar
839
- children += node.children
840
- elsif node.type == :or_asgn
841
- # TODO: The api_map should ignore local variables.
842
- type = node.children[0].type
843
- children.push node.children[0].children[0], node.children[1]
844
- elsif [:array, :hash, :str, :dstr, :int, :float, :sym].include?(node.type)
845
- # @todo Do we really care about the details?
846
- end
847
- result = node.updated(type, children)
848
- result
849
- end
850
-
851
- def local_path? path
852
- return false if workspace.nil?
853
- return true if File.exist?(File.join workspace, 'lib', path)
854
- return true if File.exist?(File.join workspace, 'lib', "#{path}.rb")
855
- false
856
- end
857
-
858
- def map_parents node, tree = []
859
- if node.kind_of?(AST::Node)
860
- @parent_stack[node] = tree
861
- node.children.each { |c|
862
- map_parents c, [node] + tree
863
- }
864
- end
865
- end
866
-
867
667
  def add_to_namespace_tree tree
868
668
  cursor = @namespace_tree
869
669
  tree.each { |t|
@@ -872,69 +672,8 @@ module Solargraph
872
672
  }
873
673
  end
874
674
 
875
- def map_namespaces node, tree = [], visibility = :public, scope = :instance, fqn = nil
876
- if node.kind_of?(AST::Node)
877
- return if node.type == :str or node.type == :dstr
878
- if node.type == :class or node.type == :module
879
- visibility = :public
880
- 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
881
- tree = pack_name(node.children[0])
882
- else
883
- tree = tree + pack_name(node.children[0])
884
- end
885
- add_to_namespace_tree tree
886
- fqn = tree.join('::')
887
- @namespace_map[fqn] ||= []
888
- @namespace_map[fqn].push node
889
- if node.type == :class and !node.children[1].nil?
890
- sc = unpack_name(node.children[1])
891
- @superclasses[fqn] = sc
892
- end
893
- end
894
- file = get_filename_for(node)
895
- in_yardoc = yardoc_has_file?(file)
896
- node.children.each do |c|
897
- if c.kind_of?(AST::Node)
898
- if c.type == :ivasgn
899
- @ivar_pins[fqn || ''] ||= []
900
- par = find_parent(c, :class, :module, :def, :defs)
901
- local_scope = ( (par.kind_of?(AST::Node) and par.type == :def) ? :instance : :class )
902
- @ivar_pins[fqn || ''].push IvarPin.new(self, c, fqn || '', local_scope, get_comment_for(c))
903
- elsif c.type == :cvasgn
904
- @cvar_pins[fqn] ||= []
905
- @cvar_pins[fqn].push CvarPin.new(self, c, fqn, get_comment_for(c))
906
- else
907
- unless fqn.nil? or in_yardoc
908
- if c.kind_of?(AST::Node)
909
- if c.type == :def and c.children[0].to_s[0].match(/[a-z]/i)
910
- @method_pins[fqn] ||= []
911
- @method_pins[fqn].push MethodPin.new(c, fqn, scope, visibility, get_comment_for(c))
912
- map_namespaces c, tree, visibility, scope, fqn
913
- next
914
- elsif c.type == :defs
915
- @method_pins[fqn] ||= []
916
- @method_pins[fqn].push MethodPin.new(c, fqn, :class, :public, get_comment_for(c))
917
- map_namespaces c, tree, :public, :class, fqn
918
- next
919
- elsif c.type == :send and [:public, :protected, :private].include?(c.children[1])
920
- visibility = c.children[1]
921
- elsif c.type == :send and c.children[1] == :include and node.type == :class
922
- @namespace_includes[fqn] ||= []
923
- @namespace_includes[fqn].push unpack_name(c.children[2])
924
- elsif c.type == :send and [:attr_reader, :attr_writer, :attr_accessor].include?(c.children[1])
925
- @attr_nodes[fqn] ||= []
926
- @attr_nodes[fqn].push AttrPin.new(c)
927
- elsif c.type == :sclass and c.children[0].type == :self
928
- map_namespaces c, tree, :public, :class, fqn
929
- next
930
- end
931
- end
932
- end
933
- map_namespaces c, tree, visibility, scope, fqn
934
- end
935
- end
936
- end
937
- end
675
+ def file_nodes
676
+ @sources.values.map(&:node)
938
677
  end
939
678
 
940
679
  def clean_namespace_string namespace
@@ -945,5 +684,14 @@ module Solargraph
945
684
  end
946
685
  result
947
686
  end
687
+
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)
694
+ end
695
+
948
696
  end
949
697
  end