solargraph 0.4.2 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ef9da7f38a0665ebb95d8e779b511d54da955619
4
- data.tar.gz: 4715dd2076a1497cf00cebb8456510a33f5ca43e
3
+ metadata.gz: 7d08698f9e91d4e08d2826494bb69eeb81fd8675
4
+ data.tar.gz: 4fb6952fdc4c54106ffdefa2d596ad764cc6d0c9
5
5
  SHA512:
6
- metadata.gz: 7999e6032efaf061fbf36a7e9ae2b6f1b81955324c68c465bd8b17039a740cdb2015fbb71207df9ebce2235d60847a07dfca118296dcb6b7b4a05b9abe9049d2
7
- data.tar.gz: 9a8632ccc9e26c082df9162f945257414562558fb65629b33b07e218399c76e7c93d43a9b31ef38465ac331f58acc1b172948245fdae10e076ebe40443300bb7
6
+ metadata.gz: 336d028676662a1315d4c9686c8c09a052618cf2ac0156a9fcf89e376ffcbec53b32c819ac5c9c77291c20eb88c63ed0fa3f1e19e185e5a1924e34e68651d045
7
+ data.tar.gz: 73d41b0eee445ad355d675ab4cff779330053130dd6fe1e85f5da21a1cd4a8373d707e60401675c8855002809bad34b1bfb8b04ff134717296120cfe9a166934
data/lib/solargraph.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'solargraph/version'
2
2
  require 'rubygems/package'
3
+ require 'yard'
3
4
 
4
5
  module Solargraph
5
6
  autoload :Analyzer, 'solargraph/analyzer'
@@ -38,3 +39,6 @@ unless File.exist?(version_dir)
38
39
  tar_extract.close
39
40
  #FileUtils.rm File.join(cache_dir, '2.0.0.tar.gz')
40
41
  end
42
+
43
+ # Define a @type tag to be used for documenting variables
44
+ YARD::Tags::Library.define_tag("Type", :type, :with_types_and_name)
@@ -12,20 +12,25 @@ module Solargraph
12
12
  'nil', 'not', 'or', 'redo', 'rescue', 'retry', 'return', 'self', 'super',
13
13
  'then', 'true', 'undef', 'unless', 'until', 'when', 'while', 'yield'
14
14
  ]
15
-
15
+
16
16
  MAPPABLE_METHODS = [
17
- :include, :require, :autoload, :attr_reader, :attr_writer, :attr_accessor, :private, :public, :protected
17
+ :include, :extend, :require, :autoload, :attr_reader, :attr_writer, :attr_accessor, :private, :public, :protected,
18
+ :solargraph_include_public_methods
18
19
  ]
19
20
 
20
21
  include NodeMethods
21
-
22
+
22
23
  attr_reader :workspace
23
24
  attr_reader :required
24
-
25
+
25
26
  def initialize workspace = nil
26
27
  @workspace = workspace
27
28
  clear
28
29
  unless @workspace.nil?
30
+ extra = File.join(workspace, '.solargraph')
31
+ if File.exist?(extra)
32
+ append_file(extra)
33
+ end
29
34
  files = []
30
35
  opts = options
31
36
  (opts[:include] - opts[:exclude]).each { |glob|
@@ -51,18 +56,29 @@ module Solargraph
51
56
 
52
57
  def options
53
58
  o = {
54
- include: ['app/**/*.rb', 'lib/**/*.rb'],
59
+ include: [],
55
60
  exclude: []
56
61
  }
57
- yaml = File.join(workspace, '.solargraph.yml')
58
- if workspace && File.exist?(yaml)
59
- l = YAML.load_file(yaml)
60
- o[:include].concat l['include'] unless l['include'].nil?
61
- o[:exclude].concat l[:exclude] unless l['exclude'].nil?
62
+ unless workspace.nil?
63
+ yardopts_file = File.join(workspace, '.yardopts')
64
+ if File.exist?(yardopts_file)
65
+ yardopts = File.read(yardopts_file)
66
+ yardopts.lines.each { |line|
67
+ arg = line.strip
68
+ if !arg.start_with?('-')
69
+ o[:include].push arg
70
+ end
71
+ }
72
+ end
62
73
  end
74
+ o[:include].concat ['app/**/*.rb', 'lib/**/*.rb'] if o[:include].empty?
63
75
  o
64
76
  end
65
77
 
78
+ def yard_map
79
+ @yard_map ||= YardMap.new(required: required, workspace: workspace)
80
+ end
81
+
66
82
  def append_file filename
67
83
  append_source File.read(filename), filename
68
84
  end
@@ -80,6 +96,7 @@ module Solargraph
80
96
  @file_nodes[filename] = root
81
97
  @required.uniq!
82
98
  process_maps
99
+ root
83
100
  end
84
101
 
85
102
  def associate_comments node, comments
@@ -88,7 +105,7 @@ module Solargraph
88
105
  comment_hash.each_pair { |k, v|
89
106
  ctxt = ''
90
107
  v.each { |l|
91
- ctxt += l.text.gsub(/^#/, '') + "\n"
108
+ ctxt += l.text.gsub(/^# /, '') + "\n"
92
109
  }
93
110
  parser = YARD::DocstringParser.new
94
111
  yard_hash[k] = parser.parse(ctxt).to_docstring
@@ -133,13 +150,12 @@ module Solargraph
133
150
  def namespaces_in name, root = ''
134
151
  result = []
135
152
  result += inner_namespaces_in(name, root, [])
136
- yard = YardMap.new(required: @required, workspace: @workspace)
137
- result += yard.get_constants name, root
153
+ result += yard_map.get_constants name, root
138
154
  fqns = find_fully_qualified_namespace(name, root)
139
155
  unless fqns.nil?
140
156
  nodes = get_namespace_nodes(fqns)
141
157
  get_include_strings_from(*nodes).each { |i|
142
- result += yard.get_constants(i, root)
158
+ result += yard_map.get_constants(i, root)
143
159
  }
144
160
  end
145
161
  result
@@ -198,7 +214,7 @@ module Solargraph
198
214
  return name unless @namespace_map[name].nil?
199
215
  get_include_strings_from(*@file_nodes.values).each { |i|
200
216
  reroot = "#{root == '' ? '' : root + '::'}#{i}"
201
- recname = find_fully_qualified_namespace name, reroot, skip
217
+ recname = find_fully_qualified_namespace name.to_s, reroot, skip
202
218
  return recname unless recname.nil?
203
219
  }
204
220
  else
@@ -215,11 +231,11 @@ module Solargraph
215
231
  }
216
232
  end
217
233
  end
218
- nil
234
+ yard_map.find_fully_qualified_namespace(name, root)
219
235
  end
220
236
 
221
237
  def get_namespace_nodes(fqns)
222
- return @file_nodes.values if fqns == ''
238
+ return @file_nodes.values if fqns == '' or fqns.nil?
223
239
  @namespace_map[fqns] || []
224
240
  end
225
241
 
@@ -259,7 +275,7 @@ module Solargraph
259
275
  if c.kind_of?(AST::Node)
260
276
  is_inst = !find_parent(c, :def).nil?
261
277
  if c.type == :ivasgn and c.children[0] and ( (scope == :instance and is_inst) or (scope != :instance and !is_inst) )
262
- arr.push Suggestion.new(c.children[0], kind: Suggestion::VARIABLE)
278
+ arr.push Suggestion.new(c.children[0], kind: Suggestion::VARIABLE, documentation: get_comment_for(c))
263
279
  end
264
280
  arr += inner_get_instance_variables(c, scope) unless [:class, :module].include?(c.type)
265
281
  end
@@ -269,14 +285,28 @@ module Solargraph
269
285
  end
270
286
 
271
287
  def infer_instance_variable(var, namespace, scope = :instance)
288
+ result = nil
272
289
  vn = nil
273
- if namespace_exists?(namespace)
274
- get_namespace_nodes(namespace).each { |node|
290
+ fqns = find_fully_qualified_namespace(namespace)
291
+ unless fqns.nil?
292
+ get_namespace_nodes(fqns).each { |node|
275
293
  vn = find_instance_variable_assignment(var, node, scope)
276
294
  break unless vn.nil?
277
295
  }
278
296
  end
279
- infer(vn.children[1]) unless vn.nil?
297
+ unless vn.nil?
298
+ cmnt = get_comment_for(vn)
299
+ unless cmnt.nil?
300
+ tag = cmnt.tag(:type)
301
+ result = tag.types[0] unless tag.nil? or tag.types.empty?
302
+ end
303
+ result = infer(vn.children[1]) if result.nil?
304
+ if result.nil?
305
+ signature = resolve_node_signature(vn.children[1])
306
+ result = infer_signature_type(signature, namespace || '', scope: scope)
307
+ end
308
+ end
309
+ result
280
310
  end
281
311
 
282
312
  def find_instance_variable_assignment(var, node, scope)
@@ -295,12 +325,56 @@ module Solargraph
295
325
  }
296
326
  nil
297
327
  end
298
-
328
+
299
329
  def get_global_variables
300
330
  # TODO: Get them
301
331
  []
302
332
  end
303
-
333
+
334
+ # Get a fully qualified namespace for the given signature.
335
+ # The signature should be in the form of a method chain, e.g.,
336
+ # method1.method2
337
+ #
338
+ # @return [String] The fully qualified namespace for the signature's type
339
+ # or nil if a type could not be determined
340
+ def infer_signature_type signature, namespace, scope: :instance
341
+ parts = signature.split('.')
342
+ type = find_fully_qualified_namespace(namespace)
343
+ type ||= ''
344
+ top = true
345
+ while parts.length > 0 and !type.nil?
346
+ p = parts.shift
347
+ unless p == 'new' and scope != :instance
348
+ if scope == :instance
349
+ meths = get_instance_methods(type)
350
+ meths += get_methods('') if top
351
+ else
352
+ meths = get_methods(type)
353
+ #meths += get_methods('') if top
354
+ end
355
+ meths.delete_if{ |m| m.insert != p }
356
+ return nil if meths.empty?
357
+ type = nil
358
+ unless meths[0].documentation.nil?
359
+ match = meths[0].documentation.all.match(/@return \[([a-z0-9:_]*)/i)
360
+ type = find_fully_qualified_namespace(match[1]) unless match.nil?
361
+ end
362
+ end
363
+ scope = :instance
364
+ top = false
365
+ end
366
+ type
367
+ end
368
+
369
+ def get_method_return_value namespace, root, method, scope = :instance
370
+ meths = get_methods(namespace, root).delete_if{ |m| m.insert != method }
371
+ meths.each { |m|
372
+ r = get_return_tag(m)
373
+ return r unless r.nil?
374
+ }
375
+ nil
376
+ end
377
+
304
378
  def get_namespace_type namespace, root = ''
305
379
  type = nil
306
380
  fqns = find_fully_qualified_namespace(namespace, root)
@@ -314,16 +388,15 @@ module Solargraph
314
388
  def get_methods(namespace, root = '', visibility: [:public])
315
389
  meths = []
316
390
  meths += inner_get_methods(namespace, root, []) #unless has_yardoc?
317
- yard = YardMap.new(required: @required, workspace: @workspace)
318
- yard_meths = yard.get_methods(namespace, root, visibility: visibility)
391
+ yard_meths = yard_map.get_methods(namespace, root, visibility: visibility)
319
392
  if yard_meths.any?
320
393
  meths.concat yard_meths
321
394
  else
322
395
  type = get_namespace_type(namespace, root)
323
396
  if type == :class
324
- meths += yard.get_instance_methods('Class')
397
+ meths += yard_map.get_instance_methods('Class')
325
398
  elsif type == :module
326
- meths += yard.get_methods('Module')
399
+ meths += yard_map.get_methods('Module')
327
400
  end
328
401
  meths
329
402
  end
@@ -350,23 +423,23 @@ module Solargraph
350
423
  def get_instance_methods(namespace, root = '', visibility: [:public])
351
424
  meths = []
352
425
  meths += inner_get_instance_methods(namespace, root, []) #unless has_yardoc?
353
- yard = YardMap.new(required: @required, workspace: @workspace)
354
- yard_meths = yard.get_instance_methods(namespace, root, visibility: visibility)
426
+ fqns = find_fully_qualified_namespace(namespace, root)
427
+ yard_meths = yard_map.get_instance_methods(fqns, '', visibility: visibility)
355
428
  if yard_meths.any?
356
429
  meths.concat yard_meths
357
430
  else
358
431
  type = get_namespace_type(namespace, root)
359
432
  if type == :class
360
- meths += yard.get_instance_methods('Object')
433
+ meths += yard_map.get_instance_methods('Object')
361
434
  elsif type == :module
362
- meths += yard.get_instance_methods('Module')
435
+ meths += yard_map.get_instance_methods('Module')
363
436
  end
364
437
  # TODO: Look out for repeats. Consider not doing this at all.
365
- sc = get_superclass(namespace, root)
366
- until sc.nil?
367
- meths += yard.get_instance_methods(sc, root)
368
- sc = get_superclass(sc)
369
- end
438
+ #sc = get_superclass(namespace, root)
439
+ #until sc.nil?
440
+ # meths += yard.get_instance_methods(sc, root)
441
+ # sc = get_superclass(sc)
442
+ #end
370
443
  end
371
444
  meths
372
445
  end
@@ -420,26 +493,39 @@ module Solargraph
420
493
  s = unpack_name(n.children[1])
421
494
  meths += inner_get_methods(s, root, skip)
422
495
  end
423
- n.children.each { |c|
424
- if c.kind_of?(AST::Node) and c.type == :defs
425
- docstring = get_comment_for(c)
426
- label = "#{c.children[1]}"
427
- args = get_method_args(c)
428
- label += " #{args.join(', ')}" unless args.empty?
429
- meths.push Suggestion.new(label, insert: c.children[1].to_s, kind: Suggestion::METHOD, detail: 'Method', documentation: docstring) if c.children[1].to_s[0].match(/[a-z_]/i) and c.children[1] != :def
430
- elsif c.kind_of?(AST::Node) and c.type == :send and c.children[1] == :include
431
- # TODO: This might not be right. Should we be getting singleton methods
432
- # from an include, or only from an extend?
433
- i = unpack_name(c.children[2])
434
- meths += inner_get_methods(i, root, skip) unless i == 'Kernel'
435
- end
436
- }
496
+ meths += inner_get_methods_from_node(n, root, skip)
437
497
  end
438
498
  #end
439
499
  }
440
500
  meths.uniq
441
501
  end
442
502
 
503
+ def inner_get_methods_from_node node, root, skip
504
+ meths = []
505
+ node.children.each { |c|
506
+ if c.kind_of?(AST::Node)
507
+ if c.type == :defs
508
+ docstring = get_comment_for(c)
509
+ label = "#{c.children[1]}"
510
+ args = get_method_args(c)
511
+ label += " #{args.join(', ')}" unless args.empty?
512
+ meths.push Suggestion.new(label, insert: c.children[1].to_s, kind: Suggestion::METHOD, detail: 'Method', documentation: docstring) if c.children[1].to_s[0].match(/[a-z_]/i) and c.children[1] != :def
513
+ elsif c.type == :send and c.children[1] == :include
514
+ # TODO: This might not be right. Should we be getting singleton methods
515
+ # from an include, or only from an extend?
516
+ i = unpack_name(c.children[2])
517
+ meths += inner_get_methods(i, root, skip) unless i == 'Kernel'
518
+ elsif c.type == :send and c.children[1] == :solargraph_include_public_methods
519
+ i = unpack_name(c.children[2])
520
+ meths += get_instance_methods(i, root, visibility: [:public])
521
+ else
522
+ meths += inner_get_methods_from_node(c, root, skip)
523
+ end
524
+ end
525
+ }
526
+ meths
527
+ end
528
+
443
529
  def inner_get_instance_methods(namespace, root, skip)
444
530
  fqns = find_fully_qualified_namespace(namespace, root)
445
531
  meths = []
@@ -481,19 +567,20 @@ module Solargraph
481
567
  }
482
568
  end
483
569
  end
484
- get_include_strings_from(n).each { |i|
485
- meths += inner_get_instance_methods(i, fqns, skip)
486
- }
487
570
  }
488
571
  end
489
572
  #end
573
+ # This is necessary to get included modules from workspace definitions
574
+ get_include_strings_from(n).each { |i|
575
+ meths += inner_get_instance_methods(i, fqns, skip)
576
+ }
490
577
  }
491
578
  meths.uniq
492
579
  end
493
580
 
494
581
  def mappable?(node)
495
582
  # TODO Add node.type :casgn (constant assignment)
496
- if node.kind_of?(AST::Node) and (node.type == :class or node.type == :module or node.type == :def or node.type == :defs or node.type == :ivasgn or node.type == :gvasgn or node.type == :or_asgn)
583
+ if node.kind_of?(AST::Node) and (node.type == :class or node.type == :module or node.type == :def or node.type == :defs or node.type == :ivasgn or node.type == :gvasgn or node.type == :lvasgn or node.type == :or_asgn)
497
584
  true
498
585
  elsif node.kind_of?(AST::Node) and node.type == :send and node.children[0] == nil and MAPPABLE_METHODS.include?(node.children[1])
499
586
  true
@@ -539,7 +626,7 @@ module Solargraph
539
626
  elsif node.type == :module
540
627
  children += node.children[0, 1]
541
628
  children += get_mappable_nodes(node.children[1..-1], comment_hash)
542
- elsif node.type == :ivasgn or node.type == :gvasgn
629
+ elsif node.type == :ivasgn or node.type == :gvasgn or node.type == :lvasgn
543
630
  children += node.children
544
631
  elsif node.type == :send and node.children[1] == :include
545
632
  children += node.children[0,3]
@@ -4,9 +4,9 @@ module Solargraph
4
4
  class CodeMap
5
5
  attr_accessor :node
6
6
  attr_accessor :api_map
7
-
7
+
8
8
  include NodeMethods
9
-
9
+
10
10
  def initialize code: '', filename: nil, workspace: nil, api_map: nil
11
11
  unless workspace.nil?
12
12
  workspace = workspace.gsub(File::ALT_SEPARATOR, File::SEPARATOR) unless File::ALT_SEPARATOR.nil?
@@ -17,11 +17,7 @@ module Solargraph
17
17
  end
18
18
  @api_map = api_map
19
19
  if @api_map.nil?
20
- if workspace.nil?
21
- @api_map = ApiMap.new(workspace)
22
- else
23
- @api_map = ApiMap.new(workspace)
24
- end
20
+ @api_map = ApiMap.new(workspace)
25
21
  end
26
22
 
27
23
  @code = code.gsub(/\r/, '')
@@ -29,8 +25,8 @@ module Solargraph
29
25
  # Hide incomplete code to avoid syntax errors
30
26
  tmp = "#{@code}\nX".gsub(/[\.@]([\s])/, '#\1').gsub(/([\A\s]?)def([\s]*?[\n\Z])/, '\1#ef\2')
31
27
  begin
32
- @node, comments = Parser::CurrentRuby.parse_with_comments(tmp)
33
- @api_map.append_node(@node, comments, filename)
28
+ node, comments = Parser::CurrentRuby.parse_with_comments(tmp)
29
+ @node = @api_map.append_node(node, comments, filename)
34
30
  rescue Parser::SyntaxError => e
35
31
  if tries < 10
36
32
  tries += 1
@@ -154,14 +150,19 @@ module Solargraph
154
150
  return [] if string_at?(index)
155
151
  result = []
156
152
  phrase = phrase_at(index)
157
- if phrase.start_with?('@')
158
- if phrase.include?('.')
153
+ signature = get_signature_at(index)
154
+ if signature.start_with?('@')
155
+ parts = signature.split('.')
156
+ var = parts.shift
157
+ if parts.length > 0 or signature.end_with?('.')
159
158
  result = []
160
- # TODO: Temporarily assuming one period
161
- var = phrase[0..phrase.index('.')-1]
162
159
  ns = namespace_at(index)
163
- obj = @api_map.infer_instance_variable(var, ns)
164
- result = @api_map.get_instance_methods(obj) unless obj.nil?
160
+ scope = :class
161
+ node = parent_node_from(index, :def, :defs, :class, :module)
162
+ scope = :instance if !node.nil? and node.type == :def
163
+ obj = @api_map.infer_instance_variable(var, ns, scope)
164
+ type = @api_map.infer_signature_type(parts.join('.'), obj, scope: :instance)
165
+ result = @api_map.get_instance_methods(type) unless type.nil?
165
166
  else
166
167
  result = get_instance_variables_at(index)
167
168
  end
@@ -179,7 +180,7 @@ module Solargraph
179
180
  else
180
181
  result = @api_map.namespaces_in(ns)
181
182
  end
182
- elsif phrase.include?('.')
183
+ elsif signature.include?('.')
183
184
  result = resolve_signature_at @code[0, index].rindex('.')
184
185
  else
185
186
  current_namespace = namespace_at(index)
@@ -198,74 +199,70 @@ module Solargraph
198
199
  result = reduce_starting_with(result, word_at(index)) if filtered
199
200
  result
200
201
  end
201
-
202
+
203
+ # Find the signature at the specified index and get suggestions based
204
+ # on its inferred type.
205
+ #
206
+ # @return [Array<Solargraph::Suggestion>]
202
207
  def resolve_signature_at index
208
+ result = []
203
209
  signature = get_signature_at(index)
204
210
  ns_here = namespace_at(index)
205
- parts = signature.split('.')
206
- first = parts.shift
207
211
  scope = parent_node_from(index, :class, :module, :def, :defs) || @node
208
- var = find_local_variable_node(first, scope)
212
+ parts = signature.split('.')
213
+ var = find_local_variable_node(parts[0], scope)
209
214
  if var.nil?
210
- # It's not a locally assigned variable.
211
- if ['STDERR','STDOUT','STDIN'].include?(first)
212
- obj = 'IO'
213
- if parts.length == 0
214
- return @api_map.get_instance_methods('IO')
215
- end
215
+ # It's not a local variable
216
+ fqns = @api_map.find_fully_qualified_namespace(signature, ns_here)
217
+ if fqns.nil?
218
+ # It's a method call
219
+ type = @api_map.infer_signature_type(signature, ns_here, scope: :class)
220
+ result.concat @api_map.get_instance_methods(type) unless type.nil?
216
221
  else
217
- if parts.length == 0
218
- # HACK: Assume that it's a constant (class or module) if it starts with an uppercase letter
219
- if first[0] == first[0].upcase
220
- return @api_map.get_methods(first, ns_here)
221
- else
222
- if scope.type == :def
223
- meths = @api_map.get_instance_methods(ns_here).delete_if{|m| m.insert != first}
224
- return [] if meths.empty?
225
- return [] if meths[0].documentation.nil?
226
- match = meths[0].documentation.all.match(/@return \[([a-z0-9:_]*)/i)
227
- return [] if match[1].nil?
228
- return @api_map.get_instance_methods(match[1], ns_here)
229
- else
230
- meths = @api_map.get_methods(ns_here).delete_if{|m| m.insert != first}
231
- return [] if meths.empty?
232
- return [] if meths[0].documentation.nil?
233
- match = meths[0].documentation.all.match(/@return \[([a-z0-9:_]*)/i)
234
- return [] if match[1].nil?
235
- return @api_map.get_instance_methods(match[1], ns_here)
236
- end
237
- end
238
- end
239
- meth = parts.shift
240
- if meth == 'new'
241
- obj = first
222
+ result.concat @api_map.get_methods(fqns)
223
+ end
224
+ else
225
+ # It's a local variable. Get the type from the node
226
+ type = get_type_comment(var)
227
+ type = infer(var.children[1]) if type.nil?
228
+ if type.nil?
229
+ vsig = resolve_node_signature(var.children[1])
230
+ vparts = vsig.split('.')
231
+ fqns = @api_map.find_fully_qualified_namespace(vparts[0], ns_here)
232
+ if fqns.nil?
233
+ vtype = @api_map.infer_signature_type(vsig, ns_here, scope: :instance)
242
234
  else
243
- obj = get_method_return_value first, ns_here, meth, :class
235
+ vtype = @api_map.infer_signature_type(vparts[1..-1].join('.'), fqns, scope: :class)
244
236
  end
237
+ fqns = @api_map.find_fully_qualified_namespace(vtype, ns_here)
238
+ signature = parts[1..-1].join('.')
239
+ type = @api_map.infer_signature_type(signature, fqns, scope: :instance)
245
240
  end
246
- while parts.length > 0
247
- obj = get_instance_method_return_value obj, ns_here, parts.shift
248
- end
249
- return @api_map.get_instance_methods(obj) unless obj.nil?
250
- else
251
- obj = infer(var.children[1])
252
- while parts.length > 0
253
- meth = parts.shift
254
- obj = get_instance_method_return_value obj, ns_here, meth
241
+ unless type.nil?
242
+ lparts = signature.split('.')
243
+ if lparts.length > 1
244
+ lsig = lparts[1..-1].join('.')
245
+ ltype = @api_map.infer_signature_type(lsig, type, scope: :instance)
246
+ result.concat @api_map.get_instance_methods(ltype) unless ltype.nil?
247
+ else
248
+ result.concat @api_map.get_instance_methods(type)
249
+ end
255
250
  end
256
- return @api_map.get_instance_methods(obj) unless obj.nil?
257
251
  end
252
+ result
258
253
  end
259
254
 
260
- def get_method_return_value namespace, root, method, scope = :instance
261
- meths = @api_map.get_methods(namespace, root).delete_if{ |m| m.insert != method }
262
- meths.each { |m|
263
- r = get_return_tag(m)
264
- return r unless r.nil?
265
- }
266
- nil
255
+ def get_type_comment node
256
+ obj = nil
257
+ cmnt = @api_map.get_comment_for(node)
258
+ unless cmnt.nil?
259
+ tag = cmnt.tag(:type)
260
+ obj = tag.types[0] unless tag.nil? or tag.types.empty?
261
+ end
262
+ obj
267
263
  end
268
264
 
265
+ # @todo Candidate for deprecation
269
266
  def get_instance_method_return_value namespace, root, method
270
267
  meths = @api_map.get_instance_methods(namespace, root).delete_if{ |m| m.insert != method }
271
268
  meths.each { |m|
@@ -284,13 +281,6 @@ module Solargraph
284
281
  end
285
282
 
286
283
  def get_signature_at index
287
- #node = node_at(index - 1)
288
- #return '' unless node.type == :send
289
- #parts = []
290
- #build_signature(node, parts)
291
- #return parts.join('.')
292
-
293
- # TODO: Alternate rough version
294
284
  brackets = 0
295
285
  squares = 0
296
286
  parens = 0
@@ -314,7 +304,8 @@ module Solargraph
314
304
  end
315
305
  if brackets == 0 and parens == 0 and squares == 0
316
306
  break if ['"', "'", ',', ' ', "\t", "\n"].include?(char)
317
- signature = char + signature if char.match(/[a-z0-9:\._]/i)
307
+ signature = char + signature if char.match(/[a-z0-9:\._@]/i)
308
+ break if char == '@'
318
309
  end
319
310
  index -= 1
320
311
  end
@@ -364,7 +355,7 @@ module Solargraph
364
355
  result += @api_map.get_methods('Kernel')
365
356
  result
366
357
  end
367
-
358
+
368
359
  private
369
360
 
370
361
  def reduce_starting_with(suggestions, word)
@@ -379,7 +370,7 @@ module Solargraph
379
370
  node.children.each { |c|
380
371
  if c.kind_of?(AST::Node)
381
372
  if c.type == :lvasgn
382
- arr.push Suggestion.new(c.children[0], kind: Suggestion::VARIABLE)
373
+ arr.push Suggestion.new(c.children[0], kind: Suggestion::VARIABLE, documentation: @api_map.get_comment_for(c))
383
374
  else
384
375
  arr += get_local_variables_from(c) unless [:class, :module, :def, :defs].include?(c.type)
385
376
  end
@@ -387,7 +378,7 @@ module Solargraph
387
378
  }
388
379
  arr
389
380
  end
390
-
381
+
391
382
  def inner_node_at(index, node, arr)
392
383
  node.children.each { |c|
393
384
  if c.kind_of?(AST::Node)
@@ -406,7 +397,7 @@ module Solargraph
406
397
  end
407
398
  }
408
399
  end
409
-
400
+
410
401
  def find_local_variable_node name, scope
411
402
  scope.children.each { |c|
412
403
  if c.kind_of?(AST::Node)
@@ -422,6 +413,6 @@ module Solargraph
422
413
  }
423
414
  nil
424
415
  end
425
-
416
+
426
417
  end
427
418
  end
@@ -33,18 +33,34 @@ module Solargraph
33
33
  return 'Integer'
34
34
  elsif node.type == :float
35
35
  return 'Float'
36
- elsif node.type == :send
37
- if node.children[0].nil?
38
- # TODO Another local variable or method or something? sheesh
39
- else
40
- ns = unpack_name(node.children[0])
41
- if node.children[1] == :new
42
- return ns
36
+ end
37
+ nil
38
+ end
39
+
40
+ # Get a call signature from a node.
41
+ # The result should be a string in the form of a method path, e.g.,
42
+ # String.new or variable.method.
43
+ #
44
+ # @return [String]
45
+ def resolve_node_signature node
46
+ stack_node_signature(node).join('.')
47
+ end
48
+
49
+ def stack_node_signature node
50
+ parts = []
51
+ if node.kind_of?(AST::Node)
52
+ if node.type == :send
53
+ unless node.children[0].nil?
54
+ parts = [unpack_name(node.children[0])] + parts
43
55
  end
56
+ parts += stack_node_signature(node.children[1])
57
+ else
58
+ parts = [unpack_name(node)] + stack_node_signature(node.children[1])
44
59
  end
45
- elsif node.type == :cbase or node.type == :const
46
- unpack_name node
60
+ else
61
+ parts.push node.to_s
47
62
  end
63
+ parts
48
64
  end
49
65
  end
50
66
  end
@@ -1,3 +1,3 @@
1
1
  module Solargraph
2
- VERSION = '0.4.2'
2
+ VERSION = '0.5.0'
3
3
  end
@@ -126,11 +126,11 @@ module Solargraph
126
126
  yard = YARD::Registry.load! y
127
127
  unless yard.nil?
128
128
  ns = nil
129
- if scope == ''
130
- ns = yard.at(namespace)
131
- else
129
+ #if scope == ''
130
+ # ns = yard.at(namespace)
131
+ #else
132
132
  ns = find_first_resolved_namespace(yard, namespace, scope)
133
- end
133
+ #end
134
134
  unless ns.nil? or !ns.kind_of?(YARD::CodeObjects::NamespaceObject)
135
135
  ns.meths(scope: :class, visibility: visibility).each { |m|
136
136
  # HACK: Fix return tags in core documentation
@@ -156,11 +156,11 @@ module Solargraph
156
156
  yard = YARD::Registry.load! y
157
157
  unless yard.nil?
158
158
  ns = nil
159
- if scope == ''
160
- ns = yard.at(namespace)
161
- else
159
+ #if scope == ''
160
+ # ns = yard.at(namespace)
161
+ #else
162
162
  ns = find_first_resolved_namespace(yard, namespace, scope)
163
- end
163
+ #end
164
164
  unless ns.nil?
165
165
  ns.meths(scope: :instance, visibility: visibility).each { |m|
166
166
  # HACK: Fix return tags in core documentation
@@ -186,6 +186,11 @@ module Solargraph
186
186
  Gem::Specification.map{ |s| s.name }.uniq
187
187
  end
188
188
 
189
+ def find_fully_qualified_namespace namespace, scope
190
+ obj = resolve(namespace, scope)
191
+ return obj.path unless obj.nil?
192
+ end
193
+
189
194
  private
190
195
 
191
196
  def get_method_args meth
@@ -215,7 +220,8 @@ module Solargraph
215
220
  def find_first_resolved_namespace yard, namespace, scope
216
221
  parts = scope.split('::')
217
222
  while parts.length > 0
218
- ns = yard.resolve(P(scope), namespace)
223
+ puts "Looking for #{namespace} in #{parts.join('::')}"
224
+ ns = yard.resolve(P(parts.join('::')), namespace, true)
219
225
  return ns unless ns.nil?
220
226
  parts.pop
221
227
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solargraph
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fred Snyder
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-04-08 00:00:00.000000000 Z
11
+ date: 2017-04-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser