solargraph 0.13.0 → 0.13.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,7 +5,7 @@ module Solargraph
5
5
 
6
6
  # The root node of the parsed code.
7
7
  #
8
- # @return [AST::Node]
8
+ # @return [Parser::AST::Node]
9
9
  attr_reader :node
10
10
 
11
11
  # The source code being analyzed.
@@ -39,13 +39,11 @@ module Solargraph
39
39
  self.api_map.refresh
40
40
  end
41
41
 
42
- # Get the ApiMap that was generated for this CodeMap.
42
+ # Get the associated ApiMap.
43
43
  #
44
44
  # @return [Solargraph::ApiMap]
45
45
  def api_map
46
46
  @api_map ||= ApiMap.new(workspace)
47
- #@api_map.refresh
48
- #@api_map
49
47
  end
50
48
 
51
49
  # Get the offset of the specified line and column.
@@ -67,7 +65,7 @@ module Solargraph
67
65
  offset += l.length
68
66
  }
69
67
  end
70
- offset + col
68
+ offset + col
71
69
  end
72
70
 
73
71
  # Get an array of nodes containing the specified index, starting with the
@@ -156,25 +154,6 @@ module Solargraph
156
154
  end
157
155
  end
158
156
 
159
- # Select the phrase that directly precedes the specified index.
160
- # A phrase can consist of most types of characters other than whitespace,
161
- # semi-colons, equal signs, parentheses, or brackets.
162
- #
163
- # @param index [Integer]
164
- # @return [String]
165
- def phrase_at index
166
- word = ''
167
- cursor = index - 1
168
- while cursor > -1
169
- char = @code[cursor, 1]
170
- break if char.nil? or char == ''
171
- break unless char.match(/[\s;=\(\)\[\]\{\}]/).nil?
172
- word = char + word
173
- cursor -= 1
174
- end
175
- word
176
- end
177
-
178
157
  # Select the word that directly precedes the specified index.
179
158
  # A word can only consist of letters, numbers, and underscores.
180
159
  #
@@ -186,6 +165,7 @@ module Solargraph
186
165
  while cursor > -1
187
166
  char = @code[cursor, 1]
188
167
  break if char.nil? or char == ''
168
+ word = char + word if char == '$'
189
169
  break unless char.match(/[a-z0-9_]/i)
190
170
  word = char + word
191
171
  cursor -= 1
@@ -193,6 +173,7 @@ module Solargraph
193
173
  word
194
174
  end
195
175
 
176
+ # @return [Array<Solargraph::Suggestion>]
196
177
  def get_class_variables_at(index)
197
178
  ns = namespace_at(index) || ''
198
179
  api_map.get_class_variables(ns)
@@ -209,73 +190,59 @@ module Solargraph
209
190
  # Get suggestions for code completion at the specified location in the
210
191
  # source.
211
192
  #
212
- # @return [Array<Suggestions>] The completion suggestions
213
- def suggest_at index, filtered: false, with_snippets: false
193
+ # @return [Array<Solargraph::Suggestion>] The completion suggestions
194
+ def suggest_at index, filtered: true, with_snippets: false
214
195
  return [] if string_at?(index) or string_at?(index - 1) or comment_at?(index)
215
- result = []
216
196
  signature = get_signature_at(index)
217
- if signature.start_with?(':')
218
- result.concat api_map.get_symbols
219
- else
220
- if index == 0 or @code[index - 1].match(/[\.\s]/)
221
- type = infer_signature_at(index)
222
- else
223
- if signature.include?('.')
224
- last_period = @code[0..index].rindex('.')
225
- if last_period.nil?
226
- type = infer_signature_at(index)
227
- else
228
- type = infer_signature_at(last_period)
229
- end
230
- else
231
- if signature.start_with?('@@')
232
- return get_class_variables_at(index)
233
- elsif signature.start_with?('@')
234
- return get_instance_variables_at(index)
235
- elsif signature.start_with?('$')
236
- return api_map.get_global_variables
237
- else
238
- type = infer_signature_at(index)
239
- end
240
- end
197
+ unless signature.include?('.')
198
+ if signature.start_with?(':')
199
+ return api_map.get_symbols
200
+ elsif signature.start_with?('@@')
201
+ return get_class_variables_at(index)
202
+ elsif signature.start_with?('@')
203
+ return get_instance_variables_at(index)
204
+ elsif signature.start_with?('$')
205
+ return api_map.get_global_variables
241
206
  end
242
- if type.nil?
243
- unless signature.include?('.')
244
- phrase = phrase_at(index)
245
- signature = get_signature_at(index)
246
- namespace = namespace_at(index)
247
- if phrase.include?('::')
248
- parts = phrase.split('::', -1)
249
- ns = parts[0..-2].join('::')
250
- if parts.last.include?('.')
251
- ns = parts[0..-2].join('::') + '::' + parts.last[0..parts.last.index('.')-1]
252
- result = api_map.get_methods(ns)
253
- else
254
- result = api_map.namespaces_in(ns, namespace)
207
+ end
208
+ result = []
209
+ type = nil
210
+ if signature.include?('.')
211
+ type = infer_signature_at(index)
212
+ if type.nil? and signature.include?('.')
213
+ last_period = @code[0..index].rindex('.')
214
+ type = infer_signature_at(last_period)
215
+ end
216
+ end
217
+ if type.nil?
218
+ unless signature.include?('.')
219
+ namespace = namespace_at(index)
220
+ if signature.include?('::')
221
+ parts = signature.split('::', -1)
222
+ ns = parts[0..-2].join('::')
223
+ result = api_map.namespaces_in(ns, namespace)
224
+ else
225
+ type = infer_literal_node_type(node_at(index - 2))
226
+ if type.nil?
227
+ current_namespace = namespace_at(index)
228
+ parts = current_namespace.to_s.split('::')
229
+ result += get_snippets_at(index) if with_snippets
230
+ result += get_local_variables_and_methods_at(index)
231
+ result += ApiMap.get_keywords
232
+ while parts.length > 0
233
+ ns = parts.join('::')
234
+ result += api_map.namespaces_in(ns, namespace)
235
+ parts.pop
255
236
  end
237
+ result += api_map.namespaces_in('')
238
+ result += api_map.get_instance_methods('Kernel')
256
239
  else
257
- type = infer_literal_node_type(node_at(index - 2))
258
- if type.nil?
259
- current_namespace = namespace_at(index)
260
- parts = current_namespace.to_s.split('::')
261
- result += get_snippets_at(index) if with_snippets
262
- result += get_local_variables_and_methods_at(index)
263
- result += ApiMap.get_keywords
264
- while parts.length > 0
265
- ns = parts.join('::')
266
- result += api_map.namespaces_in(ns, namespace)
267
- parts.pop
268
- end
269
- result += api_map.namespaces_in('')
270
- result += api_map.get_instance_methods('Kernel')
271
- else
272
- result.concat api_map.get_instance_methods(type)
273
- end
240
+ result.concat api_map.get_instance_methods(type)
274
241
  end
275
242
  end
276
- else
277
- result.concat api_map.get_instance_methods(type)
278
243
  end
244
+ else
245
+ result.concat api_map.get_instance_methods(type)
279
246
  end
280
247
  result = reduce_starting_with(result, word_at(index)) if filtered
281
248
  result.uniq{|s| s.path}.sort{|a,b| a.label <=> b.label}
@@ -285,7 +252,8 @@ module Solargraph
285
252
  sig = signature_index_before(index)
286
253
  return [] if sig.nil?
287
254
  word = word_at(sig)
288
- suggest_at(sig).reject{|s| s.label != word}
255
+ sugg = suggest_at(sig - word.length)
256
+ sugg.select{|s| s.label == word}
289
257
  end
290
258
 
291
259
  def resolve_object_at index
@@ -323,7 +291,7 @@ module Solargraph
323
291
  if path.start_with?('Class<')
324
292
  path.gsub!(/^Class<([a-z0-9_:]*)>#([a-z0-9_]*)$/i, '\\1.\\2')
325
293
  end
326
- api_map.yard_map.objects(path, ns_here)
294
+ api_map.get_path_suggestions(path)
327
295
  end
328
296
 
329
297
  # Infer the type of the signature located at the specified index.
@@ -360,6 +328,7 @@ module Solargraph
360
328
  node = parent_node_from(index, :class, :module, :def, :defs) || @node
361
329
  result = infer_signature_from_node signature, node
362
330
  if result.nil? or result.empty?
331
+ # The rest of this routine is dedicated to method and block parameters
363
332
  arg = nil
364
333
  if node.type == :def or node.type == :defs or node.type == :block
365
334
  # Check for method arguments
@@ -370,7 +339,7 @@ module Solargraph
370
339
  if parts[1].nil?
371
340
  result = arg.return_type
372
341
  else
373
- result = api_map.infer_signature_type(parts[1], parts[0], scope: :instance)
342
+ result = api_map.infer_signature_type(parts[1], arg.return_type, scope: :instance)
374
343
  end
375
344
  end
376
345
  end
@@ -445,20 +414,12 @@ module Solargraph
445
414
  return nil if start.nil?
446
415
  remainder = parts[1..-1]
447
416
  if start.start_with?('@@')
448
- #type = api_map.infer_class_variable(start, ns_here)
449
- #return nil if type.nil?
450
- #return type if remainder.empty?
451
- #return api_map.infer_signature_type(remainder.join('.'), type, scope: :instance)
452
- cv = api_map.get_class_variables(ns_here).select{|s| s.label == start}.first
453
- return cv.return_type unless cv.nil?
417
+ cv = api_map.get_class_variable_pins(ns_here).select{|s| s.name == start}.first
418
+ return (cv.return_type || api_map.infer_assignment_node_type(cv.node, cv.namespace)) unless cv.nil?
454
419
  elsif start.start_with?('@')
455
420
  scope = (node.type == :def ? :instance : :class)
456
- #type = api_map.infer_instance_variable(start, ns_here, scope)
457
- #return nil if type.nil?
458
- #return type if remainder.empty?
459
- #return api_map.infer_signature_type(remainder.join('.'), type, scope: :instance)
460
- iv = api_map.get_instance_variables(ns_here, scope).select{|s| s.label == start}.first
461
- return iv.return_type unless iv.nil?
421
+ iv = api_map.get_instance_variable_pins(ns_here, scope).select{|s| s.name == start}.first
422
+ return (iv.return_type || api_map.infer_assignment_node_type(iv.node, iv.namespace)) unless iv.nil?
462
423
  end
463
424
  var = find_local_variable_node(start, node)
464
425
  if var.nil?
@@ -512,81 +473,11 @@ module Solargraph
512
473
  # @param index [Integer]
513
474
  # @return [String]
514
475
  def get_signature_at index
515
- brackets = 0
516
- squares = 0
517
- parens = 0
518
- signature = ''
519
- index -=1
520
- while index >= 0
521
- unless string_at?(index)
522
- break if brackets > 0 or parens > 0 or squares > 0
523
- char = @code[index, 1]
524
- if char == ')'
525
- parens -=1
526
- elsif char == ']'
527
- squares -=1
528
- elsif char == '}'
529
- brackets -= 1
530
- elsif char == '('
531
- parens += 1
532
- elsif char == '{'
533
- brackets += 1
534
- elsif char == '['
535
- squares += 1
536
- signature = ".[]#{signature}" if squares == 0 and @code[index-2] != '%'
537
- end
538
- if brackets == 0 and parens == 0 and squares == 0
539
- break if ['"', "'", ',', ' ', "\t", "\n", ';', '%'].include?(char)
540
- signature = char + signature if char.match(/[a-z0-9:\._@]/i) and @code[index - 1] != '%'
541
- if char == '@'
542
- signature = "@#{signature}" if @code[index-1, 1] == '@'
543
- break
544
- end
545
- end
546
- end
547
- index -= 1
548
- end
549
- signature = signature[1..-1] if signature.start_with?('.')
550
- #signature = signature[2..-1] if signature.start_with?('[]')
551
- signature
476
+ get_signature_data_at(index)[1]
552
477
  end
553
478
 
554
479
  def get_signature_index_at index
555
- brackets = 0
556
- squares = 0
557
- parens = 0
558
- signature = ''
559
- index -=1
560
- while index >= 0
561
- break if brackets > 0 or parens > 0 or squares > 0
562
- char = @code[index, 1]
563
- if char == ')'
564
- parens -=1
565
- elsif char == ']'
566
- squares -=1
567
- elsif char == '}'
568
- brackets -= 1
569
- elsif char == '('
570
- parens += 1
571
- elsif char == '{'
572
- brackets += 1
573
- elsif char == '['
574
- squares += 1
575
- signature = ".[]#{signature}" if squares == 0
576
- end
577
- if brackets == 0 and parens == 0 and squares == 0
578
- break if ['"', "'", ',', ' ', "\t", "\n", ';'].include?(char)
579
- signature = char + signature if char.match(/[a-z0-9:\._@]/i)
580
- if char == '@'
581
- signature = "@#{signature}" if @code[index-1, 1] == '@'
582
- break
583
- end
584
- end
585
- index -= 1
586
- end
587
- signature = signature[1..-1] if signature.start_with?('.')
588
- signature = signature[2..-1] if signature.start_with?('[]')
589
- index + 1
480
+ get_signature_data_at(index)[0]
590
481
  end
591
482
 
592
483
  def get_snippets_at(index)
@@ -616,7 +507,8 @@ module Solargraph
616
507
  def get_local_variables_and_methods_at(index)
617
508
  result = []
618
509
  local = parent_node_from(index, :class, :module, :def, :defs) || @node
619
- result += get_local_variables_from(local)
510
+ #result += get_local_variables_from(local)
511
+ result += get_local_variables_from(node_at(index))
620
512
  scope = namespace_at(index) || @node
621
513
  if local.type == :def
622
514
  result += api_map.get_instance_methods(scope, visibility: [:public, :private, :protected])
@@ -627,12 +519,53 @@ module Solargraph
627
519
  result += get_method_arguments_from local
628
520
  end
629
521
  result += get_yieldparams_at(index)
630
- result += api_map.get_methods('Kernel')
522
+ # @todo This might not be necessary.
523
+ #result += api_map.get_methods('Kernel')
631
524
  result
632
525
  end
633
526
 
634
527
  private
635
528
 
529
+ def get_signature_data_at index
530
+ brackets = 0
531
+ squares = 0
532
+ parens = 0
533
+ signature = ''
534
+ index -=1
535
+ while index >= 0
536
+ unless string_at?(index)
537
+ break if brackets > 0 or parens > 0 or squares > 0
538
+ char = @code[index, 1]
539
+ if char == ')'
540
+ parens -=1
541
+ elsif char == ']'
542
+ squares -=1
543
+ elsif char == '}'
544
+ brackets -= 1
545
+ elsif char == '('
546
+ parens += 1
547
+ elsif char == '{'
548
+ brackets += 1
549
+ elsif char == '['
550
+ squares += 1
551
+ signature = ".[]#{signature}" if squares == 0 and @code[index-2] != '%'
552
+ end
553
+ if brackets == 0 and parens == 0 and squares == 0
554
+ break if ['"', "'", ',', ' ', "\t", "\n", ';', '%'].include?(char)
555
+ signature = char + signature if char.match(/[a-z0-9:\._@\$]/i) and @code[index - 1] != '%'
556
+ break if char == '$'
557
+ if char == '@'
558
+ signature = "@#{signature}" if @code[index-1, 1] == '@'
559
+ break
560
+ end
561
+ end
562
+ end
563
+ index -= 1
564
+ end
565
+ signature = signature[1..-1] if signature.start_with?('.')
566
+ [index + 1, signature]
567
+ end
568
+
636
569
  # Get a node's arguments as an array of suggestions. The node's type must
637
570
  # be a method (:def or :defs).
638
571
  #
@@ -682,8 +615,8 @@ module Solargraph
682
615
  meths += api_map.get_methods('')
683
616
  meth = meths.keep_if{ |s| s.to_s == block_node.children[0].children[1].to_s }.first
684
617
  yps = []
685
- unless meth.nil? or meth.documentation.nil?
686
- yps = meth.documentation.tags(:yieldparam) || []
618
+ unless meth.nil? or meth.docstring.nil?
619
+ yps = meth.docstring.tags(:yieldparam) || []
687
620
  end
688
621
  i = 0
689
622
  block_node.children[1].children.each do |a|
@@ -703,24 +636,22 @@ module Solargraph
703
636
  }
704
637
  end
705
638
 
639
+ # Find all the local variables in the node's scope.
640
+ #
641
+ # @return [Array<Solargraph::Suggestion>]
706
642
  def get_local_variables_from(node)
707
643
  node ||= @node
644
+ namespace = namespace_from(node)
708
645
  arr = []
709
- node.children.each { |c|
710
- if c.kind_of?(AST::Node)
711
- if c.type == :lvasgn
712
- type = api_map.infer_assignment_node_type(c, namespace_from(c))
713
- arr.push Suggestion.new(c.children[0], kind: Suggestion::VARIABLE, documentation: api_map.get_comment_for(c), return_type: type)
714
- else
715
- arr += get_local_variables_from(c) unless [:class, :module, :def, :defs].include?(c.type)
716
- end
717
- end
718
- }
646
+ @source.local_variable_pins.select{|p| p.visible_from?(node) }.each do |pin|
647
+ #arr.push Suggestion.new(pin.name, kind: Suggestion::VARIABLE, return_type: api_map.infer_assignment_node_type(pin.node, namespace))
648
+ arr.push Suggestion.new(pin.name, kind: Suggestion::VARIABLE)
649
+ end
719
650
  arr
720
651
  end
721
652
 
722
653
  def inner_node_at(index, node, arr)
723
- node.children.each { |c|
654
+ node.children.each do |c|
724
655
  if c.kind_of?(AST::Node)
725
656
  unless c.loc.expression.nil?
726
657
  if index >= c.loc.expression.begin_pos
@@ -735,7 +666,7 @@ module Solargraph
735
666
  end
736
667
  inner_node_at(index, c, arr)
737
668
  end
738
- }
669
+ end
739
670
  end
740
671
 
741
672
  def find_local_variable_node name, scope
@@ -762,11 +693,9 @@ module Solargraph
762
693
  while parts.length > 0
763
694
  result = api_map.find_fully_qualified_namespace("#{conc}::#{parts[0]}", namespace)
764
695
  if result.nil? or result.empty?
765
- #sugg = api_map.namespaces_in(conc, namespace).select{ |s| s.label == parts[0] }.first
766
- bla = api_map.get_constants(conc, namespace)
767
- sugg = api_map.get_constants(conc, namespace).select{|s| s.label == parts[0]}.first
768
- return nil if sugg.nil?
769
- result = sugg.return_type
696
+ pin = api_map.get_constant_pins(conc, namespace).select{|s| s.name == parts[0]}.first
697
+ return nil if pin.nil?
698
+ result = pin.return_type || api_map.infer_assignment_node_type(pin.node, namespace)
770
699
  break if result.nil?
771
700
  is_constant = true
772
701
  conc = result
@@ -1,9 +1,11 @@
1
1
  module Solargraph
2
2
  module NodeMethods
3
+ # @return [String]
3
4
  def unpack_name(node)
4
5
  pack_name(node).join("::")
5
6
  end
6
-
7
+
8
+ # @return [Array<String>]
7
9
  def pack_name(node)
8
10
  parts = []
9
11
  if node.kind_of?(AST::Node)
@@ -22,6 +24,7 @@ module Solargraph
22
24
  parts
23
25
  end
24
26
 
27
+ # @return [String]
25
28
  def const_from node
26
29
  if node.kind_of?(AST::Node) and node.type == :const
27
30
  result = ''
@@ -39,6 +42,7 @@ module Solargraph
39
42
  end
40
43
  end
41
44
 
45
+ # @return [String]
42
46
  def infer_literal_node_type node
43
47
  return nil unless node.kind_of?(AST::Node)
44
48
  if node.type == :str or node.type == :dstr
@@ -3,14 +3,27 @@ module Solargraph
3
3
  class Attribute < Base
4
4
  attr_reader :access
5
5
 
6
- def initialize source, node, namespace, access
6
+ def initialize source, node, namespace, access, docstring
7
7
  super(source, node, namespace)
8
8
  @access = access
9
+ @docstring = docstring
9
10
  end
10
11
 
11
12
  def name
12
13
  @name ||= "#{node.children[0]}#{access == :writer ? '=' : ''}"
13
14
  end
15
+
16
+ def path
17
+ @path ||= namespace + '#' + name
18
+ end
19
+
20
+ def return_type
21
+ if @return_type.nil? and !docstring.nil?
22
+ tag = docstring.tag(:return)
23
+ @return_type = tag.types[0] unless tag.nil?
24
+ end
25
+ @return_type
26
+ end
14
27
  end
15
28
  end
16
29
  end
@@ -1,8 +1,13 @@
1
1
  module Solargraph
2
2
  module Pin
3
3
  class Base
4
+ # @return [Solargraph::ApiMap::Source]
4
5
  attr_reader :source
6
+
7
+ # @return [Parser::AST::Node]
5
8
  attr_reader :node
9
+
10
+ # @return [String]
6
11
  attr_reader :namespace
7
12
 
8
13
  def initialize source, node, namespace
@@ -11,37 +16,50 @@ module Solargraph
11
16
  @namespace = namespace
12
17
  end
13
18
 
19
+ # @return [YARD::Docstring]
14
20
  def docstring
15
21
  @docstring ||= source.docstring_for(node)
16
22
  end
17
23
 
24
+ # @return [String]
18
25
  def name
19
26
  nil
20
27
  end
21
28
 
29
+ # @return [String]
22
30
  def path
23
31
  nil
24
32
  end
25
33
 
34
+ # @return [String]
26
35
  def kind
27
36
  nil
28
37
  end
29
38
 
39
+ # @return [String]
30
40
  def return_type
31
41
  nil
32
42
  end
33
43
 
44
+ # @return [String]
34
45
  def signature
35
46
  nil
36
47
  end
37
48
 
49
+ # @return [String]
38
50
  def value
39
51
  nil
40
52
  end
41
53
 
54
+ # @return [Array<String>]
42
55
  def parameters
43
56
  []
44
57
  end
58
+
59
+ # @return [String]
60
+ def filename
61
+ source.filename
62
+ end
45
63
  end
46
64
  end
47
65
  end
@@ -0,0 +1,6 @@
1
+ module Solargraph
2
+ module Pin
3
+ class GlobalVariable < BaseVariable
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,25 @@
1
+ module Solargraph
2
+ module Pin
3
+ class LocalVariable < BaseVariable
4
+ def initialize source, node, namespace, ancestors
5
+ super(source, node, namespace)
6
+ @tree = []
7
+ ancestors.each do |parent|
8
+ if [:block, :def, :defs, :class, :module, :source].include? parent.type
9
+ @tree.push parent
10
+ break unless parent.type == :block
11
+ end
12
+ end
13
+ end
14
+
15
+ def visible_from? node
16
+ parents = [node] + (source.tree_for(node) || [])
17
+ parents.each do |p|
18
+ return true if @tree[0] == p
19
+ return false if [:def, :defs, :class, :module].include?(p.type)
20
+ end
21
+ false
22
+ end
23
+ end
24
+ end
25
+ end
@@ -50,6 +50,8 @@ module Solargraph
50
50
  list.children.each { |c|
51
51
  if c.type == :arg
52
52
  args.push c.children[0].to_s
53
+ elsif c.type == :restarg
54
+ args.push "*#{c.children[0]}"
53
55
  elsif c.type == :optarg
54
56
  args.push "#{c.children[0]} = #{source.code_for(c.children[1])}"
55
57
  elsif c.type == :kwarg
@@ -6,6 +6,8 @@ module Solargraph
6
6
  autoload :BaseVariable, 'solargraph/pin/base_variable'
7
7
  autoload :InstanceVariable, 'solargraph/pin/instance_variable'
8
8
  autoload :ClassVariable, 'solargraph/pin/class_variable'
9
+ autoload :LocalVariable, 'solargraph/pin/local_variable'
10
+ autoload :GlobalVariable, 'solargraph/pin/global_variable'
9
11
  autoload :Constant, 'solargraph/pin/constant'
10
12
  autoload :Symbol, 'solargraph/pin/symbol'
11
13
  end