solargraph 0.8.0 → 0.8.6
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 +4 -4
- data/lib/solargraph/api_map.rb +50 -22
- data/lib/solargraph/code_map.rb +217 -203
- data/lib/solargraph/node_methods.rb +23 -21
- data/lib/solargraph/server.rb +3 -2
- data/lib/solargraph/suggestion.rb +23 -8
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/yard_map.rb +9 -3
- metadata +2 -4
- data/lib/solargraph/live_parser.rb +0 -265
- data/lib/solargraph/views/foo.erb +0 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 02c54193138513541a17e784338430260259b223
|
|
4
|
+
data.tar.gz: f464a3a964e88b6f82d250e065d92e0062c3a135
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b548477ca4dc01db1785654d68d779a1ae3e29fb29c7702efc006ce2abfd8101df8735395328bb11db9a19abfc568d93896fadc392c8eb9af4126450fb7df162
|
|
7
|
+
data.tar.gz: dd27df9547f7dddfff89b98af23cf61d9dfaeb5937f790885c30607b7ccc7f3d5151132fb1affa8183b4f766d257731129b504a1dbcc43c1791deec07f719a7c
|
data/lib/solargraph/api_map.rb
CHANGED
|
@@ -10,7 +10,7 @@ module Solargraph
|
|
|
10
10
|
'begin', 'break', 'case', 'class', 'def', 'defined?', 'do', 'else',
|
|
11
11
|
'elsif', 'end', 'ensure', 'false', 'for', 'if', 'in', 'module', 'next',
|
|
12
12
|
'nil', 'not', 'or', 'redo', 'rescue', 'retry', 'return', 'self', 'super',
|
|
13
|
-
'then', 'true', 'undef', 'unless', 'until', 'when', 'while', 'yield'
|
|
13
|
+
'then', 'true', 'undef', 'unless', 'until', 'when', 'while', 'yield'
|
|
14
14
|
]
|
|
15
15
|
|
|
16
16
|
MAPPABLE_METHODS = [
|
|
@@ -39,7 +39,7 @@ module Solargraph
|
|
|
39
39
|
files -= Dir[File.join @workspace, glob]
|
|
40
40
|
}
|
|
41
41
|
files.uniq.each { |f|
|
|
42
|
-
append_file f
|
|
42
|
+
append_file f if File.file?(f)
|
|
43
43
|
}
|
|
44
44
|
end
|
|
45
45
|
end
|
|
@@ -97,6 +97,7 @@ module Solargraph
|
|
|
97
97
|
@file_comments[filename][node.loc]
|
|
98
98
|
end
|
|
99
99
|
|
|
100
|
+
|
|
100
101
|
def self.get_keywords
|
|
101
102
|
result = []
|
|
102
103
|
keywords = KEYWORDS + ['attr_reader', 'attr_writer', 'attr_accessor', 'private', 'public', 'protected']
|
|
@@ -337,6 +338,7 @@ module Solargraph
|
|
|
337
338
|
while parts.length > 0 and !type.nil?
|
|
338
339
|
p = parts.shift
|
|
339
340
|
if top and scope == :class
|
|
341
|
+
next if p == 'new'
|
|
340
342
|
first_class = find_fully_qualified_namespace(p, namespace)
|
|
341
343
|
sub = nil
|
|
342
344
|
sub = infer_signature_type(parts.join('.'), first_class, scope: :class) unless first_class.nil?
|
|
@@ -371,6 +373,10 @@ module Solargraph
|
|
|
371
373
|
type
|
|
372
374
|
end
|
|
373
375
|
|
|
376
|
+
# Get an array of singleton methods that are available in the specified
|
|
377
|
+
# namespace.
|
|
378
|
+
#
|
|
379
|
+
# @return [Array<Solargraph::Suggestion>]
|
|
374
380
|
def get_methods(namespace, root = '', visibility: [:public])
|
|
375
381
|
meths = []
|
|
376
382
|
meths += inner_get_methods(namespace, root, []) #unless has_yardoc?
|
|
@@ -399,15 +405,24 @@ module Solargraph
|
|
|
399
405
|
}
|
|
400
406
|
return args if list.nil?
|
|
401
407
|
list.children.each { |c|
|
|
408
|
+
STDERR.puts "Args child is #{c.type}"
|
|
402
409
|
if c.type == :arg
|
|
403
410
|
args.push c.children[0]
|
|
404
|
-
elsif c.type == :
|
|
411
|
+
elsif c.type == :optarg
|
|
412
|
+
args.push "#{c.children[0]} = _"
|
|
413
|
+
elsif c.type == :kwarg
|
|
405
414
|
args.push "#{c.children[0]}:"
|
|
415
|
+
elsif c.type == :kwoptarg
|
|
416
|
+
args.push "#{c.children[0]}: _"
|
|
406
417
|
end
|
|
407
418
|
}
|
|
408
419
|
args
|
|
409
420
|
end
|
|
410
421
|
|
|
422
|
+
# Get an array of instance methods that are available in the specified
|
|
423
|
+
# namespace.
|
|
424
|
+
#
|
|
425
|
+
# @return [Array<Solargraph::Suggestion>]
|
|
411
426
|
def get_instance_methods(namespace, root = '', visibility: [:public])
|
|
412
427
|
meths = []
|
|
413
428
|
meths += inner_get_instance_methods(namespace, root, []) #unless has_yardoc?
|
|
@@ -459,22 +474,23 @@ module Solargraph
|
|
|
459
474
|
arr
|
|
460
475
|
end
|
|
461
476
|
|
|
477
|
+
# Update the YARD documentation for the current workspace.
|
|
478
|
+
#
|
|
462
479
|
def update_yardoc
|
|
463
480
|
if workspace.nil?
|
|
464
481
|
STDERR.puts "No workspace specified for yardoc update."
|
|
465
482
|
else
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
STDERR.puts "Updating the yardoc..."
|
|
469
|
-
globs = yard_options[:include] - yard_options[:exclude]
|
|
483
|
+
Thread.new {
|
|
484
|
+
Dir.chdir(workspace) do
|
|
485
|
+
STDERR.puts "Updating the yardoc for #{workspace}..."
|
|
470
486
|
cmd = "yardoc -e #{Solargraph::YARD_EXTENSION_FILE}"
|
|
471
487
|
STDERR.puts "Update yardoc with #{cmd}"
|
|
472
488
|
STDERR.puts `#{cmd}`
|
|
473
489
|
unless $?.success?
|
|
474
490
|
STDERR.puts "There was an error processing the workspace yardoc."
|
|
475
491
|
end
|
|
476
|
-
|
|
477
|
-
|
|
492
|
+
end
|
|
493
|
+
}
|
|
478
494
|
end
|
|
479
495
|
end
|
|
480
496
|
|
|
@@ -509,14 +525,16 @@ module Solargraph
|
|
|
509
525
|
docstring = get_comment_for(c)
|
|
510
526
|
label = "#{c.children[1]}"
|
|
511
527
|
args = get_method_args(c)
|
|
512
|
-
|
|
528
|
+
if (c.children[1].to_s[0].match(/[a-z_]/i) and c.children[1] != :def)
|
|
529
|
+
meths.push Suggestion.new(label, insert: c.children[1].to_s.gsub(/=/, ' = '), kind: Suggestion::METHOD, detail: 'Method', documentation: docstring, arguments: args)
|
|
530
|
+
end
|
|
513
531
|
elsif c.type == :send and c.children[1] == :include
|
|
514
532
|
# TODO: This might not be right. Should we be getting singleton methods
|
|
515
533
|
# from an include, or only from an extend?
|
|
516
534
|
i = unpack_name(c.children[2])
|
|
517
|
-
meths
|
|
535
|
+
meths.concat inner_get_methods(i, root, skip) unless i == 'Kernel'
|
|
518
536
|
else
|
|
519
|
-
meths
|
|
537
|
+
meths.concat inner_get_methods_from_node(c, root, skip)
|
|
520
538
|
end
|
|
521
539
|
end
|
|
522
540
|
}
|
|
@@ -553,16 +571,16 @@ module Solargraph
|
|
|
553
571
|
meths.push Suggestion.new(label, insert: c.children[0].to_s.gsub(/=/, ' = '), kind: Suggestion::METHOD, documentation: cmnt, detail: fqns, arguments: args) if c.children[0].to_s[0].match(/[a-z]/i)
|
|
554
572
|
elsif c.kind_of?(AST::Node) and c.type == :send and c.children[1] == :attr_reader
|
|
555
573
|
c.children[2..-1].each { |x|
|
|
556
|
-
meths.push Suggestion.new(x.children[0], kind: Suggestion::
|
|
574
|
+
meths.push Suggestion.new(x.children[0], kind: Suggestion::FIELD) if x.type == :sym
|
|
557
575
|
}
|
|
558
576
|
elsif c.kind_of?(AST::Node) and c.type == :send and c.children[1] == :attr_writer
|
|
559
577
|
c.children[2..-1].each { |x|
|
|
560
|
-
meths.push Suggestion.new("#{x.children[0]}=", kind: Suggestion::
|
|
578
|
+
meths.push Suggestion.new("#{x.children[0]}=", kind: Suggestion::FIELD) if x.type == :sym
|
|
561
579
|
}
|
|
562
580
|
elsif c.kind_of?(AST::Node) and c.type == :send and c.children[1] == :attr_accessor
|
|
563
581
|
c.children[2..-1].each { |x|
|
|
564
|
-
meths.push Suggestion.new(x.children[0], kind: Suggestion::
|
|
565
|
-
meths.push Suggestion.new("#{x.children[0]}=", kind: Suggestion::
|
|
582
|
+
meths.push Suggestion.new(x.children[0], kind: Suggestion::FIELD) if x.type == :sym
|
|
583
|
+
meths.push Suggestion.new("#{x.children[0]}=", kind: Suggestion::FIELD) if x.type == :sym
|
|
566
584
|
}
|
|
567
585
|
end
|
|
568
586
|
end
|
|
@@ -580,7 +598,7 @@ module Solargraph
|
|
|
580
598
|
def mappable?(node)
|
|
581
599
|
return true if node.kind_of?(AST::Node) and [:array, :hash, :str, :int, :float].include?(node.type)
|
|
582
600
|
# TODO Add node.type :casgn (constant assignment)
|
|
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 or node.type == :const or node.type == :lvar)
|
|
601
|
+
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 or node.type == :const or node.type == :lvar or node.type == :args or node.type == :kwargs)
|
|
584
602
|
true
|
|
585
603
|
elsif node.kind_of?(AST::Node) and node.type == :send and node.children[0] == nil and MAPPABLE_METHODS.include?(node.children[1])
|
|
586
604
|
true
|
|
@@ -588,7 +606,7 @@ module Solargraph
|
|
|
588
606
|
false
|
|
589
607
|
end
|
|
590
608
|
end
|
|
591
|
-
|
|
609
|
+
|
|
592
610
|
def reduce node, comment_hash
|
|
593
611
|
return node unless node.kind_of?(AST::Node)
|
|
594
612
|
mappable = get_mappable_nodes(node.children, comment_hash)
|
|
@@ -618,7 +636,7 @@ module Solargraph
|
|
|
618
636
|
children += node.children[0, 2]
|
|
619
637
|
children += get_mappable_nodes(node.children[2..-1], comment_hash)
|
|
620
638
|
#children += get_mappable_nodes(node.children, comment_hash)
|
|
621
|
-
elsif node.type == :const
|
|
639
|
+
elsif node.type == :const or node.type == :args or node.type == :kwargs
|
|
622
640
|
children += node.children
|
|
623
641
|
elsif node.type == :def
|
|
624
642
|
children += node.children[0, 2]
|
|
@@ -634,7 +652,10 @@ module Solargraph
|
|
|
634
652
|
elsif node.type == :send and node.children[1] == :include
|
|
635
653
|
children += node.children[0,3]
|
|
636
654
|
elsif node.type == :send and node.children[1] == :require
|
|
637
|
-
|
|
655
|
+
if node.children[2].children[0].kind_of?(String)
|
|
656
|
+
path = node.children[2].children[0].to_s
|
|
657
|
+
@required.push(path) unless local_path?(path)
|
|
658
|
+
end
|
|
638
659
|
children += node.children[0, 3]
|
|
639
660
|
elsif node.type == :send and node.children[1] == :autoload
|
|
640
661
|
@required.push(node.children[3].children[0]) if node.children[3].children[0].kind_of?(String)
|
|
@@ -653,6 +674,13 @@ module Solargraph
|
|
|
653
674
|
result
|
|
654
675
|
end
|
|
655
676
|
|
|
677
|
+
def local_path? path
|
|
678
|
+
return false if workspace.nil?
|
|
679
|
+
return true if File.exist?(File.join workspace, 'lib', path)
|
|
680
|
+
return true if File.exist?(File.join workspace, 'lib', "#{path}.rb")
|
|
681
|
+
false
|
|
682
|
+
end
|
|
683
|
+
|
|
656
684
|
def map_parents node, tree = []
|
|
657
685
|
if node.kind_of?(AST::Node)
|
|
658
686
|
@parent_stack[node] = tree
|
|
@@ -669,7 +697,7 @@ module Solargraph
|
|
|
669
697
|
cursor = cursor[t.to_s]
|
|
670
698
|
}
|
|
671
699
|
end
|
|
672
|
-
|
|
700
|
+
|
|
673
701
|
def map_namespaces node, tree = []
|
|
674
702
|
if node.kind_of?(AST::Node)
|
|
675
703
|
if node.type == :class or node.type == :module
|
|
@@ -687,6 +715,6 @@ module Solargraph
|
|
|
687
715
|
map_namespaces c, tree
|
|
688
716
|
}
|
|
689
717
|
end
|
|
690
|
-
end
|
|
718
|
+
end
|
|
691
719
|
end
|
|
692
720
|
end
|
data/lib/solargraph/code_map.rb
CHANGED
|
@@ -3,81 +3,73 @@ require 'parser/current'
|
|
|
3
3
|
module Solargraph
|
|
4
4
|
class CodeMap
|
|
5
5
|
attr_accessor :node
|
|
6
|
-
attr_accessor :api_map
|
|
7
6
|
attr_reader :code
|
|
8
7
|
attr_reader :parsed
|
|
8
|
+
attr_reader :filename
|
|
9
|
+
attr_reader :workspace
|
|
9
10
|
|
|
10
11
|
include NodeMethods
|
|
11
12
|
|
|
12
13
|
def initialize code: '', filename: nil, workspace: nil, api_map: nil
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if workspace.nil? and !filename.nil?
|
|
17
|
-
filename = filename.gsub(File::ALT_SEPARATOR, File::SEPARATOR) unless File::ALT_SEPARATOR.nil?
|
|
18
|
-
workspace = CodeMap.find_workspace(filename)
|
|
19
|
-
end
|
|
14
|
+
@workspace = workspace
|
|
15
|
+
# HACK: Adjust incoming filename's path separator for yardoc file comparisons
|
|
16
|
+
filename = filename.gsub(File::ALT_SEPARATOR, File::SEPARATOR) unless filename.nil? or File::ALT_SEPARATOR.nil?
|
|
20
17
|
@filename = filename
|
|
21
18
|
@api_map = api_map
|
|
22
|
-
if @api_map.nil?
|
|
23
|
-
@api_map = ApiMap.new(workspace)
|
|
24
|
-
end
|
|
25
19
|
@code = code.gsub(/\r/, '')
|
|
26
20
|
tries = 0
|
|
27
21
|
tmp = @code
|
|
28
22
|
begin
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
# HACK: The current file is parsed with a trailing underscore to fix
|
|
24
|
+
# incomplete trees resulting from short scripts (e.g., a lone variable
|
|
25
|
+
# assignment).
|
|
26
|
+
node, comments = Parser::CurrentRuby.parse_with_comments(tmp + "\n_")
|
|
27
|
+
@node = self.api_map.append_node(node, comments, filename)
|
|
31
28
|
@parsed = tmp
|
|
32
29
|
@code.freeze
|
|
33
30
|
@parsed.freeze
|
|
34
31
|
rescue Parser::SyntaxError => e
|
|
35
32
|
if tries < 10
|
|
36
|
-
STDERR.puts "Error parsing #{filename}: #{e.message}"
|
|
37
|
-
STDERR.puts "Retrying..."
|
|
38
33
|
tries += 1
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if tmp[spot] == '@' or tmp[spot] == ':'
|
|
42
|
-
# Stub unfinished instance variables and symbols
|
|
43
|
-
spot -= 1
|
|
44
|
-
elsif tmp[spot - 1] == '.'
|
|
45
|
-
# Stub unfinished method calls
|
|
46
|
-
repl = '#' if spot == tmp.length or tmp[spot] == '\n'
|
|
47
|
-
spot -= 2
|
|
34
|
+
if tries == 10 and e.message.include?('token $end')
|
|
35
|
+
tmp += "\nend"
|
|
48
36
|
else
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
37
|
+
spot = e.diagnostic.location.begin_pos
|
|
38
|
+
repl = '_'
|
|
39
|
+
if tmp[spot] == '@' or tmp[spot] == ':'
|
|
40
|
+
# Stub unfinished instance variables and symbols
|
|
41
|
+
spot -= 1
|
|
42
|
+
elsif tmp[spot - 1] == '.'
|
|
43
|
+
# Stub unfinished method calls
|
|
44
|
+
repl = '#' if spot == tmp.length or tmp[spot] == '\n'
|
|
45
|
+
spot -= 2
|
|
46
|
+
else
|
|
47
|
+
# Stub the whole line
|
|
48
|
+
spot = beginning_of_line_from(tmp, spot)
|
|
49
|
+
repl = '#'
|
|
50
|
+
if tmp[spot+1..-1].rstrip == 'end'
|
|
51
|
+
repl= 'end;end'
|
|
52
|
+
end
|
|
54
53
|
end
|
|
54
|
+
tmp = tmp[0..spot] + repl + tmp[spot+repl.length+1..-1].to_s
|
|
55
55
|
end
|
|
56
|
-
tmp = tmp[0..spot] + repl + tmp[spot+repl.length+1..-1].to_s
|
|
57
56
|
retry
|
|
58
57
|
end
|
|
59
58
|
raise e
|
|
60
59
|
end
|
|
61
60
|
end
|
|
62
61
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
lastname = nil
|
|
67
|
-
result = nil
|
|
68
|
-
until dirname == lastname
|
|
69
|
-
if File.file?("#{dirname}/Gemfile")
|
|
70
|
-
result = dirname
|
|
71
|
-
break
|
|
72
|
-
end
|
|
73
|
-
lastname = dirname
|
|
74
|
-
dirname = File.dirname(dirname)
|
|
75
|
-
end
|
|
76
|
-
result ||= File.dirname(filename)
|
|
77
|
-
result.gsub!(File::ALT_SEPARATOR, File::SEPARATOR) unless File::ALT_SEPARATOR.nil?
|
|
78
|
-
result
|
|
62
|
+
# @return [Solargraph::ApiMap]
|
|
63
|
+
def api_map
|
|
64
|
+
@api_map ||= ApiMap.new(workspace)
|
|
79
65
|
end
|
|
80
66
|
|
|
67
|
+
# Get the offset of the specified line and column.
|
|
68
|
+
# The offset (also called the "index") is typically used to identify the
|
|
69
|
+
# cursor's location in the code when generating suggestions.
|
|
70
|
+
# The line and column numbers should start at zero.
|
|
71
|
+
#
|
|
72
|
+
# @return [Integer]
|
|
81
73
|
def get_offset line, col
|
|
82
74
|
offset = 0
|
|
83
75
|
if line > 0
|
|
@@ -88,10 +80,6 @@ module Solargraph
|
|
|
88
80
|
offset + col
|
|
89
81
|
end
|
|
90
82
|
|
|
91
|
-
def merge node
|
|
92
|
-
api_map.merge node
|
|
93
|
-
end
|
|
94
|
-
|
|
95
83
|
def tree_at(index)
|
|
96
84
|
arr = []
|
|
97
85
|
arr.push @node
|
|
@@ -103,6 +91,9 @@ module Solargraph
|
|
|
103
91
|
tree_at(index).first
|
|
104
92
|
end
|
|
105
93
|
|
|
94
|
+
# Determine if the specified index is inside a string.
|
|
95
|
+
#
|
|
96
|
+
# @return [Boolean]
|
|
106
97
|
def string_at?(index)
|
|
107
98
|
n = node_at(index)
|
|
108
99
|
n.kind_of?(AST::Node) and n.type == :str
|
|
@@ -118,6 +109,11 @@ module Solargraph
|
|
|
118
109
|
@node
|
|
119
110
|
end
|
|
120
111
|
|
|
112
|
+
# Get the namespace at the specified location. For example, given the code
|
|
113
|
+
# `class Foo; def bar; end; end`, index 14 (the center) is in the
|
|
114
|
+
# "Foo" namespace.
|
|
115
|
+
#
|
|
116
|
+
# @return [String]
|
|
121
117
|
def namespace_at(index)
|
|
122
118
|
tree = tree_at(index)
|
|
123
119
|
return nil if tree.length == 0
|
|
@@ -132,6 +128,19 @@ module Solargraph
|
|
|
132
128
|
parts.join("::")
|
|
133
129
|
end
|
|
134
130
|
|
|
131
|
+
# Get the namespace for the specified node. For example, given the code
|
|
132
|
+
# `class Foo; def bar; end; end`, the node for `def bar` is in the "Foo"
|
|
133
|
+
# namespace.
|
|
134
|
+
#
|
|
135
|
+
# @return [String]
|
|
136
|
+
def namespace_from(node)
|
|
137
|
+
if node.respond_to?(:loc)
|
|
138
|
+
namespace_at(node.loc.expression.begin_pos)
|
|
139
|
+
else
|
|
140
|
+
''
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
135
144
|
def phrase_at index
|
|
136
145
|
word = ''
|
|
137
146
|
cursor = index - 1
|
|
@@ -161,50 +170,48 @@ module Solargraph
|
|
|
161
170
|
def get_instance_variables_at(index)
|
|
162
171
|
node = parent_node_from(index, :def, :defs, :class, :module)
|
|
163
172
|
ns = namespace_at(index) || ''
|
|
164
|
-
|
|
173
|
+
api_map.get_instance_variables(ns, (node.type == :def ? :instance : :class))
|
|
165
174
|
end
|
|
166
175
|
|
|
176
|
+
# Get suggestions for code completion at the specified location in the
|
|
177
|
+
# source.
|
|
178
|
+
#
|
|
179
|
+
# @return [Array<Suggestions>] The completion suggestions
|
|
167
180
|
def suggest_at index, filtered: false, with_snippets: false
|
|
168
|
-
|
|
169
|
-
return [] if string_at?(index)
|
|
181
|
+
return [] if string_at?(index) or string_at?(index - 1)
|
|
170
182
|
result = []
|
|
171
183
|
phrase = phrase_at(index)
|
|
172
184
|
signature = get_signature_at(index)
|
|
173
|
-
|
|
185
|
+
namespace = namespace_at(index)
|
|
186
|
+
if signature.include?('.')
|
|
187
|
+
# Check for literals first
|
|
174
188
|
type = infer(node_at(index - 2))
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
scope = :instance if !node.nil? and node.type == :def
|
|
186
|
-
obj = @api_map.infer_instance_variable(var, ns, scope)
|
|
187
|
-
type = @api_map.infer_signature_type(parts.join('.'), obj, scope: :instance)
|
|
188
|
-
result = @api_map.get_instance_methods(type) unless type.nil?
|
|
189
|
+
if type.nil?
|
|
190
|
+
nearest = @code[0, index].rindex('.')
|
|
191
|
+
revised = signature[0..nearest-index-1]
|
|
192
|
+
type = infer_signature_at(nearest) unless revised.empty?
|
|
193
|
+
if !type.nil?
|
|
194
|
+
result.concat api_map.get_instance_methods(type) unless type.nil?
|
|
195
|
+
elsif !revised.include?('.')
|
|
196
|
+
fqns = api_map.find_fully_qualified_namespace(revised, namespace)
|
|
197
|
+
result.concat api_map.get_methods(fqns) unless fqns.nil?
|
|
198
|
+
end
|
|
189
199
|
else
|
|
190
|
-
result
|
|
200
|
+
result.concat api_map.get_instance_methods(type)
|
|
191
201
|
end
|
|
202
|
+
elsif signature.start_with?('@')
|
|
203
|
+
result.concat get_instance_variables_at(index)
|
|
192
204
|
elsif phrase.start_with?('$')
|
|
193
|
-
result
|
|
194
|
-
elsif phrase.start_with?(':') and !phrase.start_with?('::')
|
|
195
|
-
# TODO: It's a symbol. Nothing to do for now.
|
|
196
|
-
return []
|
|
205
|
+
result.concat api_map.get_global_variables
|
|
197
206
|
elsif phrase.include?('::')
|
|
198
207
|
parts = phrase.split('::', -1)
|
|
199
208
|
ns = parts[0..-2].join('::')
|
|
200
209
|
if parts.last.include?('.')
|
|
201
210
|
ns = parts[0..-2].join('::') + '::' + parts.last[0..parts.last.index('.')-1]
|
|
202
|
-
result =
|
|
211
|
+
result = api_map.get_methods(ns)
|
|
203
212
|
else
|
|
204
|
-
result =
|
|
213
|
+
result = api_map.namespaces_in(ns, namespace)
|
|
205
214
|
end
|
|
206
|
-
elsif signature.include?('.')
|
|
207
|
-
result = resolve_signature_at @code[0, index].rindex('.')
|
|
208
215
|
else
|
|
209
216
|
current_namespace = namespace_at(index)
|
|
210
217
|
parts = current_namespace.to_s.split('::')
|
|
@@ -213,20 +220,20 @@ module Solargraph
|
|
|
213
220
|
result += ApiMap.get_keywords
|
|
214
221
|
while parts.length > 0
|
|
215
222
|
ns = parts.join('::')
|
|
216
|
-
result +=
|
|
223
|
+
result += api_map.namespaces_in(ns, namespace)
|
|
217
224
|
parts.pop
|
|
218
225
|
end
|
|
219
|
-
result +=
|
|
220
|
-
result +=
|
|
221
|
-
unless @filename.nil? or @api_map.yardoc_has_file?(@filename)
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
end
|
|
226
|
+
result += api_map.namespaces_in('')
|
|
227
|
+
result += api_map.get_instance_methods('Kernel')
|
|
228
|
+
#unless @filename.nil? or @api_map.yardoc_has_file?(@filename)
|
|
229
|
+
# m = @code.match(/# +@bind \[([a-z0-9_:]*)/i)
|
|
230
|
+
# unless m.nil?
|
|
231
|
+
# @api_map.get_instance_methods(m[1])
|
|
232
|
+
# end
|
|
233
|
+
#end
|
|
227
234
|
end
|
|
228
235
|
result = reduce_starting_with(result, word_at(index)) if filtered
|
|
229
|
-
result.uniq{|s| s.path}
|
|
236
|
+
result.uniq{|s| s.path}.sort{|a,b| a.label <=> b.label}
|
|
230
237
|
end
|
|
231
238
|
|
|
232
239
|
def signatures_at index
|
|
@@ -237,6 +244,7 @@ module Solargraph
|
|
|
237
244
|
end
|
|
238
245
|
|
|
239
246
|
def resolve_object_at index
|
|
247
|
+
return [] if string_at?(index)
|
|
240
248
|
signature = get_signature_at(index)
|
|
241
249
|
cursor = index
|
|
242
250
|
while @code[cursor] =~ /[a-z0-9_\?]/i
|
|
@@ -245,149 +253,123 @@ module Solargraph
|
|
|
245
253
|
break if cursor >= @code.length
|
|
246
254
|
end
|
|
247
255
|
return [] if signature.to_s == ''
|
|
256
|
+
path = nil
|
|
248
257
|
ns_here = namespace_at(index)
|
|
249
|
-
|
|
258
|
+
node = parent_node_from(index, :class, :module, :def, :defs) || @node
|
|
250
259
|
parts = signature.split('.')
|
|
251
260
|
if parts.length > 1
|
|
252
261
|
beginner = parts[0..-2].join('.')
|
|
262
|
+
type = infer_signature_from_node(beginner, node)
|
|
253
263
|
ender = parts.last
|
|
264
|
+
path = "#{type}##{ender}"
|
|
254
265
|
else
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
if var.nil?
|
|
260
|
-
# It's not a local variable
|
|
261
|
-
fqns = @api_map.find_fully_qualified_namespace(beginner, ns_here)
|
|
262
|
-
if fqns.nil?
|
|
263
|
-
# It's a method call
|
|
264
|
-
if ender.nil?
|
|
265
|
-
return @api_map.yard_map.objects(beginner, ns_here)
|
|
266
|
-
else
|
|
267
|
-
sig_scope = (scope.type == :def ? :instance : :class)
|
|
268
|
-
type = @api_map.infer_signature_type(beginner, ns_here, scope: sig_scope)
|
|
269
|
-
return [] if type.nil?
|
|
270
|
-
path = type
|
|
271
|
-
path += "##{ender}" unless ender.nil?
|
|
272
|
-
return @api_map.yard_map.objects(path, ns_here)
|
|
273
|
-
end
|
|
266
|
+
if local_variable_in_node?(signature, node)
|
|
267
|
+
path = infer_signature_from_node(signature, node)
|
|
268
|
+
elsif signature.start_with?('@')
|
|
269
|
+
path = api_map.infer_instance_variable(signature, ns_here, (node.type == :def ? :instance : :class))
|
|
274
270
|
else
|
|
275
|
-
path =
|
|
276
|
-
path += "##{ender}" unless ender.nil?
|
|
277
|
-
return @api_map.yard_map.objects(path, ns_here)
|
|
271
|
+
path = signature
|
|
278
272
|
end
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
type = get_type_comment(var)
|
|
282
|
-
type = infer(var.children[1]) if type.nil?
|
|
283
|
-
if type.nil?
|
|
284
|
-
vsig = resolve_node_signature(var.children[1])
|
|
285
|
-
vparts = vsig.split('.')
|
|
286
|
-
fqns = @api_map.find_fully_qualified_namespace(vparts[0], ns_here)
|
|
287
|
-
if fqns.nil?
|
|
288
|
-
vtype = @api_map.infer_signature_type(vsig, ns_here, scope: :instance)
|
|
289
|
-
else
|
|
290
|
-
vtype = @api_map.infer_signature_type(vparts[1..-1].join('.'), fqns, scope: :class)
|
|
291
|
-
end
|
|
292
|
-
return [] if vtype.nil?
|
|
293
|
-
fqns = @api_map.find_fully_qualified_namespace(vtype, ns_here)
|
|
294
|
-
if ender.nil?
|
|
295
|
-
signature = parts[1..-1].join('.')
|
|
296
|
-
else
|
|
297
|
-
signature = parts[1..-2].join('.')
|
|
298
|
-
end
|
|
299
|
-
type = @api_map.infer_signature_type(signature, fqns, scope: :instance)
|
|
300
|
-
end
|
|
301
|
-
unless type.nil?
|
|
302
|
-
path = type
|
|
303
|
-
path += "##{ender}" unless ender.nil?
|
|
304
|
-
return @api_map.yard_map.objects(path, ns_here)
|
|
273
|
+
if path.nil?
|
|
274
|
+
path = api_map.find_fully_qualified_namespace(signature, ns_here)
|
|
305
275
|
end
|
|
306
276
|
end
|
|
307
|
-
return []
|
|
277
|
+
return [] if path.nil?
|
|
278
|
+
return api_map.yard_map.objects(path, ns_here)
|
|
308
279
|
end
|
|
309
280
|
|
|
310
|
-
|
|
311
|
-
# on its inferred type.
|
|
312
|
-
#
|
|
313
|
-
# @return [Array<Solargraph::Suggestion>]
|
|
314
|
-
def resolve_signature_at index
|
|
315
|
-
result = []
|
|
281
|
+
def infer_signature_at index
|
|
316
282
|
signature = get_signature_at(index)
|
|
317
|
-
|
|
318
|
-
|
|
283
|
+
node = parent_node_from(index, :class, :module, :def, :defs) || @node
|
|
284
|
+
infer_signature_from_node signature, node
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def local_variable_in_node?(name, node)
|
|
288
|
+
return true unless find_local_variable_node(name, node).nil?
|
|
289
|
+
if node.type == :def or node.type == :defs
|
|
290
|
+
args = get_method_arguments_from(node).keep_if{|a| a.label == name}
|
|
291
|
+
return true unless args.empty?
|
|
292
|
+
end
|
|
293
|
+
false
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def infer_signature_from_node signature, node
|
|
297
|
+
inferred = nil
|
|
319
298
|
parts = signature.split('.')
|
|
320
|
-
|
|
299
|
+
ns_here = namespace_from(node)
|
|
300
|
+
start = parts[0]
|
|
301
|
+
return nil if start.nil?
|
|
302
|
+
remainder = parts[1..-1]
|
|
303
|
+
scope = :instance
|
|
304
|
+
var = find_local_variable_node(start, node)
|
|
321
305
|
if var.nil?
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
# It's a method call
|
|
326
|
-
sig_scope = (scope.type == :def ? :instance : :class)
|
|
327
|
-
type = @api_map.infer_signature_type(signature, ns_here, scope: sig_scope)
|
|
328
|
-
result.concat @api_map.get_instance_methods(type) unless type.nil?
|
|
306
|
+
if start.start_with?('@')
|
|
307
|
+
scope2 = (node.type == :def ? :instance : :class)
|
|
308
|
+
type = api_map.infer_instance_variable(start, ns_here, scope2)
|
|
329
309
|
else
|
|
330
|
-
if
|
|
331
|
-
|
|
310
|
+
if node.type == :def or node.type == :defs
|
|
311
|
+
args = get_method_arguments_from(node).keep_if{|a| a.label == start}
|
|
312
|
+
if args.empty?
|
|
313
|
+
scope = :class
|
|
314
|
+
type = api_map.find_fully_qualified_namespace(start, ns_here)
|
|
315
|
+
if type.nil?
|
|
316
|
+
# It's a method call
|
|
317
|
+
sig_scope = (node.type == :def ? :instance : :class)
|
|
318
|
+
type = api_map.infer_signature_type(start, ns_here, scope: sig_scope)
|
|
319
|
+
else
|
|
320
|
+
return nil if remainder.empty?
|
|
321
|
+
end
|
|
322
|
+
else
|
|
323
|
+
cmnt = api_map.get_comment_for(node)
|
|
324
|
+
unless cmnt.nil?
|
|
325
|
+
params = cmnt.tags(:param)
|
|
326
|
+
unless params.nil?
|
|
327
|
+
params.each do |p|
|
|
328
|
+
if p.name == args[0].label
|
|
329
|
+
type = p.types[0]
|
|
330
|
+
break
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
end
|
|
335
|
+
end
|
|
332
336
|
else
|
|
333
|
-
|
|
337
|
+
scope = :class
|
|
338
|
+
type = api_map.find_fully_qualified_namespace(start, ns_here)
|
|
339
|
+
if type.nil?
|
|
340
|
+
# It's a method call
|
|
341
|
+
sig_scope = (node.type == :def ? :instance : :class)
|
|
342
|
+
type = api_map.infer_signature_type(start, ns_here, scope: sig_scope)
|
|
343
|
+
else
|
|
344
|
+
return nil if remainder.empty?
|
|
345
|
+
end
|
|
334
346
|
end
|
|
335
347
|
end
|
|
336
348
|
else
|
|
337
|
-
#
|
|
349
|
+
# Signature starts with a local variable
|
|
338
350
|
type = get_type_comment(var)
|
|
339
351
|
type = infer(var.children[1]) if type.nil?
|
|
340
|
-
skipdat = false
|
|
341
352
|
if type.nil?
|
|
342
353
|
vsig = resolve_node_signature(var.children[1])
|
|
343
|
-
|
|
344
|
-
fqns = @api_map.find_fully_qualified_namespace(vparts[0], ns_here)
|
|
345
|
-
if fqns.nil?
|
|
346
|
-
lvar = find_local_variable_node(vparts[0], scope)
|
|
347
|
-
if lvar.nil?
|
|
348
|
-
vtype = @api_map.infer_signature_type(vsig, ns_here, scope: :instance)
|
|
349
|
-
else
|
|
350
|
-
type = get_type_comment(lvar)
|
|
351
|
-
type = infer(lvar.children[1]) if type.nil?
|
|
352
|
-
if type.nil?
|
|
353
|
-
type = @api_map.infer_signature_type(resolve_node_signature(lvar.children[1]), ns_here, scope: :class)
|
|
354
|
-
end
|
|
355
|
-
unless type.nil?
|
|
356
|
-
signature = "#{vparts[1..-1].join('.')}"
|
|
357
|
-
skipdat = true
|
|
358
|
-
end
|
|
359
|
-
end
|
|
360
|
-
else
|
|
361
|
-
vtype = @api_map.infer_signature_type(vparts[1..-1].join('.'), fqns, scope: :class)
|
|
362
|
-
type = fqns
|
|
363
|
-
signature = parts[1..-1].join('.')
|
|
364
|
-
skipdat = true
|
|
365
|
-
end
|
|
366
|
-
unless skipdat
|
|
367
|
-
fqns = @api_map.find_fully_qualified_namespace(vtype, ns_here)
|
|
368
|
-
signature = parts[1..-1].join('.')
|
|
369
|
-
type = @api_map.infer_signature_type(signature, fqns, scope: :instance)
|
|
370
|
-
end
|
|
371
|
-
else
|
|
372
|
-
signature = signature.split('.')[1..-1].join('.')
|
|
373
|
-
end
|
|
374
|
-
unless type.nil?
|
|
375
|
-
lparts = signature.split('.')
|
|
376
|
-
if lparts.length >= 1
|
|
377
|
-
lsig = signature
|
|
378
|
-
ltype = @api_map.infer_signature_type(lsig, type, scope: :instance)
|
|
379
|
-
result.concat @api_map.get_instance_methods(ltype) unless ltype.nil?
|
|
380
|
-
else
|
|
381
|
-
result.concat @api_map.get_instance_methods(type)
|
|
382
|
-
end
|
|
354
|
+
type = infer_signature_from_node vsig, node
|
|
383
355
|
end
|
|
384
356
|
end
|
|
357
|
+
unless type.nil?
|
|
358
|
+
inferred = api_map.infer_signature_type(remainder.join('.'), type, scope: scope)
|
|
359
|
+
end
|
|
360
|
+
inferred
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
def suggest_for_signature_at index
|
|
364
|
+
result = []
|
|
365
|
+
type = infer_signature_at(index)
|
|
366
|
+
result.concat api_map.get_instance_methods(type) unless type.nil?
|
|
385
367
|
result
|
|
386
368
|
end
|
|
387
369
|
|
|
388
370
|
def get_type_comment node
|
|
389
371
|
obj = nil
|
|
390
|
-
cmnt =
|
|
372
|
+
cmnt = api_map.get_comment_for(node)
|
|
391
373
|
unless cmnt.nil?
|
|
392
374
|
tag = cmnt.tag(:type)
|
|
393
375
|
obj = tag.types[0] unless tag.nil? or tag.types.empty?
|
|
@@ -395,6 +377,17 @@ module Solargraph
|
|
|
395
377
|
obj
|
|
396
378
|
end
|
|
397
379
|
|
|
380
|
+
# Get the signature at the specified index.
|
|
381
|
+
# A signature is a method call that can start with a constant, method, or
|
|
382
|
+
# variable and does not include any method arguments. Examples:
|
|
383
|
+
#
|
|
384
|
+
# Code Signature
|
|
385
|
+
# -----------------------------------------
|
|
386
|
+
# String.new String.new
|
|
387
|
+
# @x.bar @x.bar
|
|
388
|
+
# y.split(', ').length y.split.length
|
|
389
|
+
#
|
|
390
|
+
# @return [String]
|
|
398
391
|
def get_signature_at index
|
|
399
392
|
brackets = 0
|
|
400
393
|
squares = 0
|
|
@@ -427,6 +420,10 @@ module Solargraph
|
|
|
427
420
|
signature
|
|
428
421
|
end
|
|
429
422
|
|
|
423
|
+
# Build a signature from the specified node. This method returns the node
|
|
424
|
+
# as an array of strings.
|
|
425
|
+
#
|
|
426
|
+
# @return [Array<String>]
|
|
430
427
|
def build_signature(node, parts)
|
|
431
428
|
if node.kind_of?(AST::Node)
|
|
432
429
|
if node.type == :send
|
|
@@ -457,22 +454,39 @@ module Solargraph
|
|
|
457
454
|
result
|
|
458
455
|
end
|
|
459
456
|
|
|
457
|
+
# Get an array of local variables and methods that can be accessed from
|
|
458
|
+
# the specified location in the code.
|
|
459
|
+
#
|
|
460
|
+
# @return [Array<Solargraph::Suggestion>]
|
|
460
461
|
def get_local_variables_and_methods_at(index)
|
|
461
462
|
result = []
|
|
462
463
|
local = parent_node_from(index, :class, :module, :def, :defs) || @node
|
|
463
464
|
result += get_local_variables_from(local)
|
|
464
465
|
scope = namespace_at(index) || @node
|
|
465
466
|
if local.type == :def
|
|
466
|
-
result +=
|
|
467
|
+
result += api_map.get_instance_methods(scope, visibility: [:public, :private, :protected])
|
|
467
468
|
else
|
|
468
|
-
result +=
|
|
469
|
+
result += api_map.get_methods(scope, visibility: [:public, :private, :protected])
|
|
470
|
+
end
|
|
471
|
+
if local.type == :def or local.type == :defs
|
|
472
|
+
result += get_method_arguments_from local
|
|
469
473
|
end
|
|
470
|
-
result +=
|
|
474
|
+
result += api_map.get_methods('Kernel')
|
|
471
475
|
result
|
|
472
476
|
end
|
|
473
477
|
|
|
474
478
|
private
|
|
475
479
|
|
|
480
|
+
def get_method_arguments_from node
|
|
481
|
+
result = []
|
|
482
|
+
args = node.children[1]
|
|
483
|
+
args.children.each do |arg|
|
|
484
|
+
name = arg.children[0].to_s
|
|
485
|
+
result.push Suggestion.new(name, kind: Suggestion::PROPERTY, insert: name)
|
|
486
|
+
end
|
|
487
|
+
result
|
|
488
|
+
end
|
|
489
|
+
|
|
476
490
|
def reduce_starting_with(suggestions, word)
|
|
477
491
|
suggestions.reject { |s|
|
|
478
492
|
!s.label.start_with?(word)
|
|
@@ -485,7 +499,7 @@ module Solargraph
|
|
|
485
499
|
node.children.each { |c|
|
|
486
500
|
if c.kind_of?(AST::Node)
|
|
487
501
|
if c.type == :lvasgn
|
|
488
|
-
arr.push Suggestion.new(c.children[0], kind: Suggestion::VARIABLE, documentation:
|
|
502
|
+
arr.push Suggestion.new(c.children[0], kind: Suggestion::VARIABLE, documentation: api_map.get_comment_for(c))
|
|
489
503
|
else
|
|
490
504
|
arr += get_local_variables_from(c) unless [:class, :module, :def, :defs].include?(c.type)
|
|
491
505
|
end
|
|
@@ -39,27 +39,6 @@ module Solargraph
|
|
|
39
39
|
end
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
-
def drill_signature node, signature
|
|
43
|
-
return signature unless node.kind_of?(AST::Node)
|
|
44
|
-
if node.type == :const or node.type == :cbase
|
|
45
|
-
unless node.children[0].nil?
|
|
46
|
-
signature += drill_signature(node.children[0], signature)
|
|
47
|
-
end
|
|
48
|
-
signature += '::' unless signature.empty?
|
|
49
|
-
signature += node.children[1].to_s
|
|
50
|
-
elsif node.type == :lvar
|
|
51
|
-
signature += '.' unless signature.empty?
|
|
52
|
-
signature += node.children[0].to_s
|
|
53
|
-
elsif node.type == :send
|
|
54
|
-
unless node.children[0].nil?
|
|
55
|
-
signature += drill_signature(node.children[0], signature)
|
|
56
|
-
end
|
|
57
|
-
signature += '.' unless signature.empty?
|
|
58
|
-
signature += node.children[1].to_s
|
|
59
|
-
end
|
|
60
|
-
signature
|
|
61
|
-
end
|
|
62
|
-
|
|
63
42
|
def infer node
|
|
64
43
|
if node.type == :str
|
|
65
44
|
return 'String'
|
|
@@ -127,5 +106,28 @@ module Solargraph
|
|
|
127
106
|
end
|
|
128
107
|
@yard_options
|
|
129
108
|
end
|
|
109
|
+
|
|
110
|
+
private
|
|
111
|
+
|
|
112
|
+
def drill_signature node, signature
|
|
113
|
+
return signature unless node.kind_of?(AST::Node)
|
|
114
|
+
if node.type == :const or node.type == :cbase
|
|
115
|
+
unless node.children[0].nil?
|
|
116
|
+
signature += drill_signature(node.children[0], signature)
|
|
117
|
+
end
|
|
118
|
+
signature += '::' unless signature.empty?
|
|
119
|
+
signature += node.children[1].to_s
|
|
120
|
+
elsif node.type == :lvar
|
|
121
|
+
signature += '.' unless signature.empty?
|
|
122
|
+
signature += node.children[0].to_s
|
|
123
|
+
elsif node.type == :send
|
|
124
|
+
unless node.children[0].nil?
|
|
125
|
+
signature += drill_signature(node.children[0], signature)
|
|
126
|
+
end
|
|
127
|
+
signature += '.' unless signature.empty?
|
|
128
|
+
signature += node.children[1].to_s
|
|
129
|
+
end
|
|
130
|
+
signature
|
|
131
|
+
end
|
|
130
132
|
end
|
|
131
133
|
end
|
data/lib/solargraph/server.rb
CHANGED
|
@@ -6,6 +6,7 @@ module Solargraph
|
|
|
6
6
|
class Server < Sinatra::Base
|
|
7
7
|
|
|
8
8
|
set :port, 7657
|
|
9
|
+
set :server, :webrick
|
|
9
10
|
|
|
10
11
|
@@api_hash = {}
|
|
11
12
|
@@semaphore = Mutex.new
|
|
@@ -19,12 +20,12 @@ module Solargraph
|
|
|
19
20
|
content_type :json
|
|
20
21
|
begin
|
|
21
22
|
sugg = []
|
|
22
|
-
workspace = params['workspace']
|
|
23
|
+
workspace = params['workspace']
|
|
23
24
|
Server.prepare_workspace workspace unless @@api_hash.has_key?(workspace)
|
|
24
25
|
@@semaphore.synchronize {
|
|
25
26
|
code_map = CodeMap.new(code: params['text'], filename: params['filename'], api_map: @@api_hash[workspace])
|
|
26
27
|
offset = code_map.get_offset(params['line'].to_i, params['column'].to_i)
|
|
27
|
-
sugg = code_map.suggest_at(offset, with_snippets: params['with_snippets'] == '1' ? true : false)
|
|
28
|
+
sugg = code_map.suggest_at(offset, with_snippets: params['with_snippets'] == '1' ? true : false, filtered: (params['filtered'] || false))
|
|
28
29
|
}
|
|
29
30
|
{ "status" => "ok", "suggestions" => sugg }.to_json
|
|
30
31
|
rescue Exception => e
|
|
@@ -8,11 +8,14 @@ module Solargraph
|
|
|
8
8
|
MODULE = 'Module'
|
|
9
9
|
METHOD = 'Method'
|
|
10
10
|
VARIABLE = 'Variable'
|
|
11
|
+
PROPERTY = 'Property'
|
|
12
|
+
FIELD = 'Field'
|
|
11
13
|
SNIPPET = 'Snippet'
|
|
12
14
|
|
|
13
15
|
attr_reader :label, :kind, :insert, :detail, :documentation, :code_object, :location, :arguments
|
|
14
16
|
|
|
15
17
|
def initialize label, kind: KEYWORD, insert: nil, detail: nil, documentation: nil, code_object: nil, location: nil, arguments: []
|
|
18
|
+
@helper = Server::Helpers.new
|
|
16
19
|
@label = label.to_s
|
|
17
20
|
@kind = kind
|
|
18
21
|
@insert = insert || @label
|
|
@@ -34,8 +37,14 @@ module Solargraph
|
|
|
34
37
|
def return_type
|
|
35
38
|
if code_object.nil?
|
|
36
39
|
unless documentation.nil?
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
if documentation.kind_of?(YARD::Docstring)
|
|
41
|
+
t = documentation.tag(:return)
|
|
42
|
+
return nil if t.nil?
|
|
43
|
+
return t.types[0]
|
|
44
|
+
else
|
|
45
|
+
match = documentation.match(/@return \[([a-z0-9:_]*)/i)
|
|
46
|
+
return match[1] unless match.nil?
|
|
47
|
+
end
|
|
39
48
|
end
|
|
40
49
|
else
|
|
41
50
|
o = code_object.tag(:overload)
|
|
@@ -49,6 +58,15 @@ module Solargraph
|
|
|
49
58
|
nil
|
|
50
59
|
end
|
|
51
60
|
|
|
61
|
+
def documentation
|
|
62
|
+
if @documentation.nil?
|
|
63
|
+
unless @code_object.nil?
|
|
64
|
+
@documentation = @code_object.docstring unless @code_object.docstring.nil?
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
@documentation
|
|
68
|
+
end
|
|
69
|
+
|
|
52
70
|
def to_json args={}
|
|
53
71
|
obj = {
|
|
54
72
|
label: @label,
|
|
@@ -57,13 +75,10 @@ module Solargraph
|
|
|
57
75
|
detail: @detail,
|
|
58
76
|
path: path,
|
|
59
77
|
location: (@location.nil? ? nil : @location.to_s),
|
|
60
|
-
arguments: @arguments
|
|
78
|
+
arguments: @arguments,
|
|
79
|
+
return_type: return_type,
|
|
80
|
+
documentation: @helper.html_markup_rdoc(documentation.to_s)
|
|
61
81
|
}
|
|
62
|
-
if @code_object.nil?
|
|
63
|
-
obj[:documentation] = @documentation.to_s unless @documentation.nil?
|
|
64
|
-
else
|
|
65
|
-
obj[:documentation] = @code_object.docstring.to_s unless @code_object.docstring.nil?
|
|
66
|
-
end
|
|
67
82
|
obj.to_json(args)
|
|
68
83
|
end
|
|
69
84
|
end
|
data/lib/solargraph/version.rb
CHANGED
data/lib/solargraph/yard_map.rb
CHANGED
|
@@ -148,7 +148,8 @@ module Solargraph
|
|
|
148
148
|
n = m.to_s.split(/[\.#]/).last.gsub(/=/, ' = ')
|
|
149
149
|
label = "#{n}"
|
|
150
150
|
args = get_method_args(m)
|
|
151
|
-
|
|
151
|
+
kind = (m.is_attribute? ? Suggestion::FIELD : Suggestion::METHOD)
|
|
152
|
+
meths.push Suggestion.new(label, insert: "#{n.gsub(/=/, ' = ')}", kind: kind, documentation: m.docstring, code_object: m, detail: "#{ns}", location: "#{m.file}:#{m.line}", arguments: args)
|
|
152
153
|
}
|
|
153
154
|
# Collect superclass methods
|
|
154
155
|
if ns.kind_of?(YARD::CodeObjects::ClassObject) and !ns.superclass.nil?
|
|
@@ -191,14 +192,19 @@ module Solargraph
|
|
|
191
192
|
if n.to_s.match(/^[a-z]/i) and (namespace == 'Kernel' or !m.to_s.start_with?('Kernel#')) and !m.docstring.to_s.include?(':nodoc:')
|
|
192
193
|
label = "#{n}"
|
|
193
194
|
args = get_method_args(m)
|
|
194
|
-
|
|
195
|
+
kind = (m.is_attribute? ? Suggestion::FIELD : Suggestion::METHOD)
|
|
196
|
+
meths.push Suggestion.new(label, insert: "#{n.gsub(/=/, ' = ')}", kind: kind, documentation: m.docstring, code_object: m, detail: m.namespace, location: "#{m.file}:#{m.line}", arguments: args)
|
|
195
197
|
end
|
|
196
198
|
}
|
|
197
199
|
if ns.kind_of?(YARD::CodeObjects::ClassObject) and namespace != 'Object'
|
|
198
|
-
if ns.superclass.kind_of?(YARD::CodeObjects::Proxy)
|
|
200
|
+
#if ns.superclass.kind_of?(YARD::CodeObjects::Proxy)
|
|
201
|
+
unless ns.nil?
|
|
199
202
|
meths += get_instance_methods(ns.superclass.to_s)
|
|
200
203
|
end
|
|
201
204
|
end
|
|
205
|
+
ns.instance_mixins.each do |m|
|
|
206
|
+
meths += get_instance_methods(m.to_s) unless m.to_s == 'Kernel'
|
|
207
|
+
end
|
|
202
208
|
end
|
|
203
209
|
end
|
|
204
210
|
}
|
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.8.
|
|
4
|
+
version: 0.8.6
|
|
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-
|
|
11
|
+
date: 2017-06-14 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: parser
|
|
@@ -137,7 +137,6 @@ files:
|
|
|
137
137
|
- lib/solargraph.rb
|
|
138
138
|
- lib/solargraph/api_map.rb
|
|
139
139
|
- lib/solargraph/code_map.rb
|
|
140
|
-
- lib/solargraph/live_parser.rb
|
|
141
140
|
- lib/solargraph/mapper.rb
|
|
142
141
|
- lib/solargraph/node_methods.rb
|
|
143
142
|
- lib/solargraph/server.rb
|
|
@@ -145,7 +144,6 @@ files:
|
|
|
145
144
|
- lib/solargraph/snippets.rb
|
|
146
145
|
- lib/solargraph/suggestion.rb
|
|
147
146
|
- lib/solargraph/version.rb
|
|
148
|
-
- lib/solargraph/views/foo.erb
|
|
149
147
|
- lib/solargraph/views/layout.erb
|
|
150
148
|
- lib/solargraph/views/search.erb
|
|
151
149
|
- lib/solargraph/yard_map.rb
|
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
# Solargraph Ruby Parser
|
|
2
|
-
# Copyright (c) 2015 by Fred Snyder for Castwide Technologies LLC
|
|
3
|
-
#
|
|
4
|
-
# Solargraph::Parser builds a code representation of existing Ruby interfaces
|
|
5
|
-
# for use in the Solargraph IDE.
|
|
6
|
-
#
|
|
7
|
-
# Example use:
|
|
8
|
-
#
|
|
9
|
-
# parser = Solargraph::Parser.new
|
|
10
|
-
# parser.parse #=> String with the entire Ruby interface
|
|
11
|
-
# parser.parse("Fixnum") #=> String with the Fixnum interface
|
|
12
|
-
#require 'yard'
|
|
13
|
-
#require 'yard/registry'
|
|
14
|
-
|
|
15
|
-
module Solargraph
|
|
16
|
-
class LiveParser
|
|
17
|
-
def get_yard_return(path)
|
|
18
|
-
objects = []
|
|
19
|
-
yardocs = ['yard/2.2.0/.yardoc', 'ruby/yard/2.2.0/.yardoc-stdlib']
|
|
20
|
-
yardocs.each { |y|
|
|
21
|
-
YARD::Registry.load!(y)
|
|
22
|
-
o = YARD::Registry.at(path)
|
|
23
|
-
if !o.nil?
|
|
24
|
-
objects.push o
|
|
25
|
-
end
|
|
26
|
-
}
|
|
27
|
-
result = nil
|
|
28
|
-
objects.each { |x|
|
|
29
|
-
meth = x
|
|
30
|
-
if !meth.tag(:return) and meth.tag(:overload) and meth.tag(:overload).tag(:return)
|
|
31
|
-
meth = meth.tag(:overload)
|
|
32
|
-
end
|
|
33
|
-
meth.tags(:return).each { |r|
|
|
34
|
-
result = "#{r.types[0]}"
|
|
35
|
-
break
|
|
36
|
-
}
|
|
37
|
-
break if !result.nil?
|
|
38
|
-
}
|
|
39
|
-
result
|
|
40
|
-
end
|
|
41
|
-
def initialize
|
|
42
|
-
|
|
43
|
-
end
|
|
44
|
-
def parse namespace = nil
|
|
45
|
-
#puts "Namespace: #{namespace}"
|
|
46
|
-
@parsed = []
|
|
47
|
-
code = ""
|
|
48
|
-
fqns = namespace
|
|
49
|
-
if fqns.nil?
|
|
50
|
-
#code += parse("BasicObject")
|
|
51
|
-
#code += parse("Object")
|
|
52
|
-
#code += parse("Kernel")
|
|
53
|
-
code += parse("Module")
|
|
54
|
-
return code
|
|
55
|
-
end
|
|
56
|
-
mod = eval("#{fqns}")
|
|
57
|
-
if !mod.nil?
|
|
58
|
-
if mod.instance_of?(Class)
|
|
59
|
-
#puts "Parsing class #{mod} to #{fqns}"
|
|
60
|
-
code += parse_class mod, fqns
|
|
61
|
-
elsif mod.instance_of?(Module)
|
|
62
|
-
#puts "Parsing module #{mod} to #{fqns}"
|
|
63
|
-
code += parse_module mod, fqns
|
|
64
|
-
else
|
|
65
|
-
#raise "I don't know what a #{fqns} is."
|
|
66
|
-
code += "#{fqns} = nil\n"
|
|
67
|
-
end
|
|
68
|
-
else
|
|
69
|
-
#puts "NIL!"
|
|
70
|
-
end
|
|
71
|
-
code
|
|
72
|
-
end
|
|
73
|
-
def self.parse n
|
|
74
|
-
LiveParser.new.parse(n)
|
|
75
|
-
end
|
|
76
|
-
private
|
|
77
|
-
def parse_class cls, rel_name
|
|
78
|
-
return "" if @parsed.include?(cls)
|
|
79
|
-
@parsed.push cls
|
|
80
|
-
code = ""
|
|
81
|
-
#code += "class #{rel_name}"
|
|
82
|
-
code += "class #{cls}"
|
|
83
|
-
if !cls.superclass.nil? && cls.superclass != cls
|
|
84
|
-
code += " < #{cls.superclass}"
|
|
85
|
-
end
|
|
86
|
-
code += "\n"
|
|
87
|
-
code += parse_class_internals(cls)
|
|
88
|
-
code += "end\n"
|
|
89
|
-
cls.constants().each { |c|
|
|
90
|
-
#obj = cls.class_eval(c.to_s)
|
|
91
|
-
begin
|
|
92
|
-
obj = cls.const_get(c)
|
|
93
|
-
if obj.kind_of?(Class)
|
|
94
|
-
code += parse_class(obj, c)
|
|
95
|
-
elsif obj.kind_of?(Module)
|
|
96
|
-
code += parse_module(obj, c)
|
|
97
|
-
else
|
|
98
|
-
#code += subparse(obj)
|
|
99
|
-
end
|
|
100
|
-
#rescue NameError => e
|
|
101
|
-
# #puts "NOPE! NOT #{c}"
|
|
102
|
-
#end
|
|
103
|
-
rescue Exception => e
|
|
104
|
-
# TODO: Ignoring all exceptions for now
|
|
105
|
-
end
|
|
106
|
-
}
|
|
107
|
-
code
|
|
108
|
-
end
|
|
109
|
-
def parse_module mod, rel_name
|
|
110
|
-
return "" if @parsed.include?(mod) or mod == Solargraph
|
|
111
|
-
@parsed.push mod
|
|
112
|
-
code = ""
|
|
113
|
-
#if (mod.to_s != "Kernel")
|
|
114
|
-
code = "module #{mod}\n"
|
|
115
|
-
#end
|
|
116
|
-
code += parse_module_internals(mod)
|
|
117
|
-
#if (mod.to_s != "Kernel")
|
|
118
|
-
code += "end\n"
|
|
119
|
-
#end
|
|
120
|
-
mod.constants().each { |c|
|
|
121
|
-
#obj = mod.class_eval(c.to_s)
|
|
122
|
-
begin
|
|
123
|
-
obj = mod.const_get(c)
|
|
124
|
-
rescue LoadError => e
|
|
125
|
-
code += "# @todo Failed to load #{c} from #{mod}\n"
|
|
126
|
-
end
|
|
127
|
-
if obj.kind_of?(Class)
|
|
128
|
-
code += parse_class(obj, c)
|
|
129
|
-
elsif obj.kind_of?(Module)
|
|
130
|
-
code += parse_module(obj, c)
|
|
131
|
-
else
|
|
132
|
-
#code += subparse(obj)
|
|
133
|
-
end
|
|
134
|
-
}
|
|
135
|
-
code
|
|
136
|
-
end
|
|
137
|
-
def parse_class_internals obj
|
|
138
|
-
code = ""
|
|
139
|
-
obj.included_modules.each { |inc|
|
|
140
|
-
#if (inc.to_s != "Kernel")
|
|
141
|
-
code += "include #{inc}\n"
|
|
142
|
-
#end
|
|
143
|
-
}
|
|
144
|
-
obj.public_methods(false).each { |m|
|
|
145
|
-
if !can_ignore?(obj, m)
|
|
146
|
-
args = build_args obj.method(m)
|
|
147
|
-
#ret = get_yard_return "#{obj}::#{m}"
|
|
148
|
-
#if !ret.nil?
|
|
149
|
-
# code += "# @return [#{ret}]\n"
|
|
150
|
-
#end
|
|
151
|
-
code += "def self.#{m}#{args};end\n"
|
|
152
|
-
end
|
|
153
|
-
}
|
|
154
|
-
alloc = obj
|
|
155
|
-
obj.singleton_methods(false).each { |m|
|
|
156
|
-
if !can_ignore?(obj, m)
|
|
157
|
-
args = build_args obj.method(m)
|
|
158
|
-
#ret = get_yard_return "#{obj}::#{m}"
|
|
159
|
-
#if !ret.nil?
|
|
160
|
-
# code += "# @return [#{ret}]\n"
|
|
161
|
-
#end
|
|
162
|
-
code += "def self.#{m}#{args};end\n"
|
|
163
|
-
end
|
|
164
|
-
}
|
|
165
|
-
obj.public_instance_methods(false).each { |m|
|
|
166
|
-
if !can_ignore?(obj, m)
|
|
167
|
-
begin
|
|
168
|
-
args = build_args obj.public_instance_method(m)
|
|
169
|
-
rescue TypeError => e
|
|
170
|
-
args = ""
|
|
171
|
-
end
|
|
172
|
-
#ret = get_yard_return "#{obj}##{m}"
|
|
173
|
-
#if !ret.nil?
|
|
174
|
-
# code += "# @return [#{ret}]\n"
|
|
175
|
-
#end
|
|
176
|
-
code += "def #{m}#{args};end\n"
|
|
177
|
-
end
|
|
178
|
-
}
|
|
179
|
-
code
|
|
180
|
-
end
|
|
181
|
-
def parse_module_internals obj
|
|
182
|
-
code = ""
|
|
183
|
-
obj.included_modules.each { |inc|
|
|
184
|
-
#if (inc.to_s != "Kernel")
|
|
185
|
-
code += "include #{inc}\n"
|
|
186
|
-
#end
|
|
187
|
-
}
|
|
188
|
-
obj.public_methods(false).each { |m|
|
|
189
|
-
if obj == Kernel #and obj.singleton_methods.include?(m)
|
|
190
|
-
next
|
|
191
|
-
end
|
|
192
|
-
if !can_ignore?(obj, m)
|
|
193
|
-
args = build_args obj.method(m)
|
|
194
|
-
#ret = get_yard_return "#{obj}##{m}"
|
|
195
|
-
#if !ret.nil?
|
|
196
|
-
# code += "# @return [#{ret}]\n"
|
|
197
|
-
#end
|
|
198
|
-
code += "def #{m}#{args};end\n"
|
|
199
|
-
end
|
|
200
|
-
}
|
|
201
|
-
obj.singleton_methods(false).each { |m|
|
|
202
|
-
if !can_ignore?(obj, m)
|
|
203
|
-
args = build_args obj.method(m)
|
|
204
|
-
#ret = get_yard_return "#{obj}::#{m}"
|
|
205
|
-
#if !ret.nil?
|
|
206
|
-
# code += "# @return [#{ret}]\n"
|
|
207
|
-
#end
|
|
208
|
-
code += "def self.#{m}#{args};end\n"
|
|
209
|
-
end
|
|
210
|
-
}
|
|
211
|
-
#obj.public_instance_methods(false).each { |m|
|
|
212
|
-
obj.public_instance_methods(false).each { |m|
|
|
213
|
-
#if !can_ignore?(obj, m)
|
|
214
|
-
args = build_args obj.public_instance_method(m)
|
|
215
|
-
#ret = get_yard_return "#{obj}##{m}"
|
|
216
|
-
#if !ret.nil?
|
|
217
|
-
# code += "# @return [#{ret}]\n"
|
|
218
|
-
#end
|
|
219
|
-
code += "def #{m}#{args};end\n"
|
|
220
|
-
#end
|
|
221
|
-
}
|
|
222
|
-
code
|
|
223
|
-
end
|
|
224
|
-
def can_ignore?(obj, sym)
|
|
225
|
-
#return false
|
|
226
|
-
basics = [Kernel, Module, Object, BasicObject]
|
|
227
|
-
return false if basics.include?(obj)
|
|
228
|
-
result = false
|
|
229
|
-
basics.each { |b|
|
|
230
|
-
if b.respond_to?(sym)
|
|
231
|
-
result = true
|
|
232
|
-
break
|
|
233
|
-
end
|
|
234
|
-
}
|
|
235
|
-
return result
|
|
236
|
-
end
|
|
237
|
-
def build_args method
|
|
238
|
-
args = ""
|
|
239
|
-
if (method.arity == -1)
|
|
240
|
-
args = "(*args)"
|
|
241
|
-
else
|
|
242
|
-
arr = []
|
|
243
|
-
num = 0
|
|
244
|
-
method.parameters.each { |p|
|
|
245
|
-
n = p[1]
|
|
246
|
-
if n.to_s == ""
|
|
247
|
-
n = "arg#{num}"
|
|
248
|
-
end
|
|
249
|
-
if p[0] == :req
|
|
250
|
-
arr.push "#{n}"
|
|
251
|
-
elsif p[0] == :opt
|
|
252
|
-
arr.push "#{n} = nil"
|
|
253
|
-
elsif p[0] == :rest
|
|
254
|
-
arr.push "*#{n}"
|
|
255
|
-
elsif p[0] == :block
|
|
256
|
-
arr.push "&#{n}"
|
|
257
|
-
end
|
|
258
|
-
num += 1
|
|
259
|
-
}
|
|
260
|
-
args = "(" + arr.join(", ") + ")"
|
|
261
|
-
end
|
|
262
|
-
args
|
|
263
|
-
end
|
|
264
|
-
end
|
|
265
|
-
end
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<p>Foo!</p>
|