solargraph 0.13.0 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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