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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a80ad72c101042120c5ec349dd3fb19a98e794e7
4
- data.tar.gz: 99d562e48985d5fa6e3d8dbaf93edafd165490c4
3
+ metadata.gz: 02c54193138513541a17e784338430260259b223
4
+ data.tar.gz: f464a3a964e88b6f82d250e065d92e0062c3a135
5
5
  SHA512:
6
- metadata.gz: ee8a6a7d1d7cc1ec77727a81e7cd4db7e3da8c0d03baee7424ac2063d281bbc1ceb1863c37c489219f65735cc3adbb64f78b05b7a1ed29a9dfc2b73eb776c6ee
7
- data.tar.gz: 34a309dcdb41c54a99ced46a8c51fe3b4372d8dc51a2620647f0b7d3f4331662f7fd3a263f4a99d6e45f3bfe899b965212e38adfb1e578daa89667914be50b98
6
+ metadata.gz: b548477ca4dc01db1785654d68d779a1ae3e29fb29c7702efc006ce2abfd8101df8735395328bb11db9a19abfc568d93896fadc392c8eb9af4126450fb7df162
7
+ data.tar.gz: dd27df9547f7dddfff89b98af23cf61d9dfaeb5937f790885c30607b7ccc7f3d5151132fb1affa8183b4f766d257731129b504a1dbcc43c1791deec07f719a7c
@@ -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 #unless yardoc_has_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 == :kwoptarg
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
- Dir.chdir(workspace) do
467
- Thread.new {
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
- end
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
- meths.push Suggestion.new(label, insert: c.children[1].to_s.gsub(/=/, ' = '), kind: Suggestion::METHOD, detail: 'Method', documentation: docstring, arguments: args) if c.children[1].to_s[0].match(/[a-z_]/i) and c.children[1] != :def
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 += inner_get_methods(i, root, skip) unless i == 'Kernel'
535
+ meths.concat inner_get_methods(i, root, skip) unless i == 'Kernel'
518
536
  else
519
- meths += inner_get_methods_from_node(c, root, skip)
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::METHOD) if x.type == :sym
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::METHOD) if x.type == :sym
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::METHOD) if x.type == :sym
565
- meths.push Suggestion.new("#{x.children[0]}=", kind: Suggestion::METHOD) if x.type == :sym
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
- @required.push(node.children[2].children[0]) if node.children[2].children[0].kind_of?(String)
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
@@ -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
- unless workspace.nil?
14
- workspace = workspace.gsub(File::ALT_SEPARATOR, File::SEPARATOR) unless File::ALT_SEPARATOR.nil?
15
- end
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
- node, comments = Parser::CurrentRuby.parse_with_comments(tmp)
30
- @node = @api_map.append_node(node, comments, filename)
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
- spot = e.diagnostic.location.begin_pos
40
- repl = '_'
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
- # Stub the whole line
50
- spot = beginning_of_line_from(tmp, spot)
51
- repl = '#'
52
- if tmp[spot+1..-1].rstrip == 'end'
53
- repl= 'end;end'
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
- def self.find_workspace filename
64
- return nil if filename.nil?
65
- dirname = filename
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
- @api_map.get_instance_variables(ns, (node.type == :def ? :instance : :class))
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
- node = node_at(index - 2)
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
- if (@code[index - 1] == '.')
185
+ namespace = namespace_at(index)
186
+ if signature.include?('.')
187
+ # Check for literals first
174
188
  type = infer(node_at(index - 2))
175
- return @api_map.get_instance_methods(type) unless type.nil?
176
- end
177
- if signature.start_with?('@')
178
- parts = signature.split('.')
179
- var = parts.shift
180
- if parts.length > 0 or signature.end_with?('.')
181
- result = []
182
- ns = namespace_at(index)
183
- scope = :class
184
- node = parent_node_from(index, :def, :defs, :class, :module)
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 = get_instance_variables_at(index)
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 += @api_map.get_global_variables
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 = @api_map.get_methods(ns)
211
+ result = api_map.get_methods(ns)
203
212
  else
204
- result = @api_map.namespaces_in(ns)
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 += @api_map.namespaces_in(ns)
223
+ result += api_map.namespaces_in(ns, namespace)
217
224
  parts.pop
218
225
  end
219
- result += @api_map.namespaces_in('')
220
- result += @api_map.get_instance_methods('Kernel')
221
- unless @filename.nil? or @api_map.yardoc_has_file?(@filename)
222
- m = @code.match(/# +@bind \[([a-z0-9_:]*)/i)
223
- unless m.nil?
224
- @api_map.get_instance_methods(m[1])
225
- end
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
- scope = parent_node_from(index, :class, :module, :def, :defs) || @node
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
- beginner = signature
256
- ender = nil
257
- end
258
- var = find_local_variable_node(parts[0], scope)
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 = beginner
276
- path += "##{ender}" unless ender.nil?
277
- return @api_map.yard_map.objects(path, ns_here)
271
+ path = signature
278
272
  end
279
- else
280
- # It's a local variable. Get the type from the node
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
- # Find the signature at the specified index and get suggestions based
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
- ns_here = namespace_at(index)
318
- scope = parent_node_from(index, :class, :module, :def, :defs) || @node
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
- var = find_local_variable_node(parts[0], scope)
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
- # It's not a local variable
323
- fqns = @api_map.find_fully_qualified_namespace(signature, ns_here)
324
- if fqns.nil?
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 fqns == ns_here
331
- result.concat @api_map.get_methods(fqns, '', visibility: [:private, :protected, :public])
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
- result.concat @api_map.get_methods(fqns)
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
- # It's a local variable. Get the type from the node
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
- vparts = vsig.split('.')
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 = @api_map.get_comment_for(node)
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 += @api_map.get_instance_methods(scope, visibility: [:public, :private, :protected])
467
+ result += api_map.get_instance_methods(scope, visibility: [:public, :private, :protected])
467
468
  else
468
- result += @api_map.get_methods(scope, visibility: [:public, :private, :protected])
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 += @api_map.get_methods('Kernel')
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: @api_map.get_comment_for(c))
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
@@ -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'] || CodeMap.find_workspace(params['filename'])
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
- match = documentation.all.match(/@return \[([a-z0-9:_]*)/i)
38
- return match[1]
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
@@ -1,3 +1,3 @@
1
1
  module Solargraph
2
- VERSION = '0.8.0'
2
+ VERSION = '0.8.6'
3
3
  end
@@ -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
- meths.push Suggestion.new(label, insert: "#{n.gsub(/=/, ' = ')}", kind: Suggestion::METHOD, documentation: m.docstring, code_object: m, detail: "#{ns}", location: "#{m.file}:#{m.line}", arguments: args)
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
- meths.push Suggestion.new(label, insert: "#{n.gsub(/=/, ' = ')}", kind: Suggestion::METHOD, documentation: m.docstring, code_object: m, detail: m.namespace, location: "#{m.file}:#{m.line}", arguments: args)
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.0
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-05-30 00:00:00.000000000 Z
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>