solargraph 0.4.2 → 0.5.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: 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