solargraph 0.1.0
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 +7 -0
- data/bin/solargraph +6 -0
- data/lib/solargraph.rb +39 -0
- data/lib/solargraph/api_map.rb +538 -0
- data/lib/solargraph/code_map.rb +404 -0
- data/lib/solargraph/live_parser.rb +265 -0
- data/lib/solargraph/mapper.rb +31 -0
- data/lib/solargraph/node_methods.rb +48 -0
- data/lib/solargraph/server.rb +21 -0
- data/lib/solargraph/shell.rb +84 -0
- data/lib/solargraph/snippets.rb +186 -0
- data/lib/solargraph/suggestion.rb +40 -0
- data/lib/solargraph/version.rb +3 -0
- data/lib/solargraph/yard_map.rb +119 -0
- data/yardoc/2.0.0.tar.gz +0 -0
- metadata +160 -0
@@ -0,0 +1,404 @@
|
|
1
|
+
require 'parser/current'
|
2
|
+
|
3
|
+
module Solargraph
|
4
|
+
class CodeMap
|
5
|
+
attr_accessor :node
|
6
|
+
attr_accessor :api_map
|
7
|
+
|
8
|
+
include NodeMethods
|
9
|
+
|
10
|
+
def initialize code: '', filename: nil
|
11
|
+
workspace = nil
|
12
|
+
unless filename.nil?
|
13
|
+
filename.gsub!(File::ALT_SEPARATOR, File::SEPARATOR) unless File::ALT_SEPARATOR.nil?
|
14
|
+
workspace = CodeMap.find_workspace(filename)
|
15
|
+
end
|
16
|
+
@api_map = ApiMap.new(workspace)
|
17
|
+
unless workspace.nil?
|
18
|
+
files = Dir[File.join workspace, '**', '*.rb']
|
19
|
+
files.each { |f|
|
20
|
+
unless filename == f
|
21
|
+
@api_map.append_file f
|
22
|
+
end
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
@code = code.gsub(/\r/, '')
|
27
|
+
tries = 0
|
28
|
+
# Hide incomplete code to avoid syntax errors
|
29
|
+
tmp = "#{code}\nX".gsub(/[\.@]([\s])/, '#\1').gsub(/([\A\s]?)def([\s]*?[\n\Z])/, '\1#ef\2')
|
30
|
+
#tmp = code
|
31
|
+
begin
|
32
|
+
@node, comments = Parser::CurrentRuby.parse_with_comments(tmp)
|
33
|
+
@api_map.append_node(@node, comments, filename)
|
34
|
+
rescue Parser::SyntaxError => e
|
35
|
+
if tries < 10
|
36
|
+
tries += 1
|
37
|
+
spot = e.diagnostic.location.begin_pos
|
38
|
+
if spot == tmp.length
|
39
|
+
puts e.message
|
40
|
+
tmp = tmp[0..-2] + '#'
|
41
|
+
else
|
42
|
+
tmp = tmp[0..spot] + '#' + tmp[spot+2..-1].to_s
|
43
|
+
end
|
44
|
+
retry
|
45
|
+
end
|
46
|
+
raise e
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.find_workspace filename
|
51
|
+
return nil if filename.nil?
|
52
|
+
dirname = filename
|
53
|
+
lastname = nil
|
54
|
+
result = nil
|
55
|
+
until dirname == lastname
|
56
|
+
if File.file?("#{dirname}/Gemfile")
|
57
|
+
result = dirname
|
58
|
+
break
|
59
|
+
end
|
60
|
+
lastname = dirname
|
61
|
+
dirname = File.dirname(dirname)
|
62
|
+
end
|
63
|
+
result ||= File.dirname(filename)
|
64
|
+
result.gsub!(File::ALT_SEPARATOR, File::SEPARATOR) unless File::ALT_SEPARATOR.nil?
|
65
|
+
result
|
66
|
+
end
|
67
|
+
|
68
|
+
def get_offset line, col
|
69
|
+
offset = 0
|
70
|
+
if line > 0
|
71
|
+
@code.lines[0..line - 1].each { |l|
|
72
|
+
offset += l.length
|
73
|
+
}
|
74
|
+
end
|
75
|
+
offset + col
|
76
|
+
end
|
77
|
+
|
78
|
+
def merge node
|
79
|
+
api_map.merge node
|
80
|
+
end
|
81
|
+
|
82
|
+
def tree_at(index)
|
83
|
+
arr = []
|
84
|
+
arr.push @node
|
85
|
+
inner_node_at(index, @node, arr)
|
86
|
+
arr
|
87
|
+
end
|
88
|
+
|
89
|
+
def node_at(index)
|
90
|
+
tree_at(index).first
|
91
|
+
end
|
92
|
+
|
93
|
+
def string_at?(index)
|
94
|
+
n = node_at(index)
|
95
|
+
n.kind_of?(AST::Node) and n.type == :str
|
96
|
+
end
|
97
|
+
|
98
|
+
def parent_node_from(index, *types)
|
99
|
+
arr = tree_at(index)
|
100
|
+
arr.each { |a|
|
101
|
+
if a.kind_of?(AST::Node) and (types.empty? or types.include?(a.type))
|
102
|
+
return a
|
103
|
+
end
|
104
|
+
}
|
105
|
+
@node
|
106
|
+
end
|
107
|
+
|
108
|
+
def namespace_at(index)
|
109
|
+
tree = tree_at(index)
|
110
|
+
return nil if tree.length == 0
|
111
|
+
node = parent_node_from(index, :module, :class)
|
112
|
+
slice = tree[(tree.index(node) || 0)..-1]
|
113
|
+
parts = []
|
114
|
+
slice.reverse.each { |n|
|
115
|
+
if n.type == :class or n.type == :module
|
116
|
+
parts.push unpack_name(n.children[0])
|
117
|
+
end
|
118
|
+
}
|
119
|
+
parts.join("::")
|
120
|
+
end
|
121
|
+
|
122
|
+
def phrase_at index
|
123
|
+
word = ''
|
124
|
+
cursor = index - 1
|
125
|
+
while cursor > -1
|
126
|
+
char = @code[cursor, 1]
|
127
|
+
break if char.nil? or char == ''
|
128
|
+
break unless char.match(/[\s;=\(\)\[\]\{\}]/).nil?
|
129
|
+
word = char + word
|
130
|
+
cursor -= 1
|
131
|
+
end
|
132
|
+
word
|
133
|
+
end
|
134
|
+
|
135
|
+
def word_at index
|
136
|
+
word = ''
|
137
|
+
cursor = index - 1
|
138
|
+
while cursor > -1
|
139
|
+
char = @code[cursor, 1]
|
140
|
+
break if char.nil? or char == ''
|
141
|
+
break unless char.match(/[a-z0-9_]/i)
|
142
|
+
word = char + word
|
143
|
+
cursor -= 1
|
144
|
+
end
|
145
|
+
word
|
146
|
+
end
|
147
|
+
|
148
|
+
def get_instance_variables_at(index)
|
149
|
+
node = parent_node_from(index, :def, :defs, :class, :module)
|
150
|
+
ns = namespace_at(index) || ''
|
151
|
+
@api_map.get_instance_variables(ns, (node.type == :def ? :instance : :class))
|
152
|
+
end
|
153
|
+
|
154
|
+
def suggest_at index, filtered: true, with_snippets: false
|
155
|
+
return [] if string_at?(index)
|
156
|
+
result = []
|
157
|
+
phrase = phrase_at(index)
|
158
|
+
if phrase.start_with?('@')
|
159
|
+
if phrase.include?('.')
|
160
|
+
result = []
|
161
|
+
# TODO: Temporarily assuming one period
|
162
|
+
var = phrase[0..phrase.index('.')-1]
|
163
|
+
ns = namespace_at(index)
|
164
|
+
obj = @api_map.infer_instance_variable(var, ns)
|
165
|
+
result = @api_map.get_instance_methods(obj) unless obj.nil?
|
166
|
+
else
|
167
|
+
result = get_instance_variables_at(index)
|
168
|
+
end
|
169
|
+
elsif phrase.start_with?('$')
|
170
|
+
result += @api_map.get_global_variables
|
171
|
+
elsif phrase.start_with?(':') and !phrase.start_with?('::')
|
172
|
+
# TODO: It's a symbol. Nothing to do for now.
|
173
|
+
return []
|
174
|
+
elsif phrase.include?('::')
|
175
|
+
parts = phrase.split('::', -1)
|
176
|
+
ns = parts[0..-2].join('::')
|
177
|
+
if parts.last.include?('.')
|
178
|
+
ns = parts[0..-2].join('::') + '::' + parts.last[0..parts.last.index('.')-1]
|
179
|
+
result = @api_map.get_methods(ns)
|
180
|
+
else
|
181
|
+
result = @api_map.namespaces_in(ns)
|
182
|
+
end
|
183
|
+
elsif phrase.include?('.')
|
184
|
+
# It's a method call
|
185
|
+
# TODO: For now we're assuming only one period. That's obviously a bad assumption.
|
186
|
+
#base = phrase[0..phrase.index('.')-1]
|
187
|
+
#ns_here = namespace_at(index)
|
188
|
+
#result = @api_map.get_methods(base, ns_here)
|
189
|
+
#scope = parent_node_from(index, :class, :module, :def, :defs) || @node
|
190
|
+
#var = find_local_variable_node(base, scope)
|
191
|
+
#unless var.nil?
|
192
|
+
# obj = infer(var.children[1])
|
193
|
+
# result = @api_map.get_instance_methods(obj) unless obj.nil?
|
194
|
+
#end
|
195
|
+
|
196
|
+
# TODO: Alternate version that resolves signature
|
197
|
+
result = resolve_signature_at index
|
198
|
+
else
|
199
|
+
current_namespace = namespace_at(index)
|
200
|
+
parts = current_namespace.to_s.split('::')
|
201
|
+
result += get_snippets_at(index) if with_snippets
|
202
|
+
result += get_local_variables_and_methods_at(index)
|
203
|
+
result += ApiMap.get_keywords(without_snippets: with_snippets)
|
204
|
+
while parts.length > 0
|
205
|
+
ns = parts.join('::')
|
206
|
+
result += @api_map.namespaces_in(ns)
|
207
|
+
parts.pop
|
208
|
+
end
|
209
|
+
result += @api_map.namespaces_in('')
|
210
|
+
end
|
211
|
+
result = reduce_starting_with(result, word_at(index)) if filtered
|
212
|
+
result
|
213
|
+
end
|
214
|
+
|
215
|
+
def resolve_signature_at index
|
216
|
+
signature = get_signature_at(index)
|
217
|
+
ns_here = namespace_at(index)
|
218
|
+
parts = signature.split('.')
|
219
|
+
first = parts.shift
|
220
|
+
scope = parent_node_from(index, :class, :module, :def, :defs) || @node
|
221
|
+
var = find_local_variable_node(first, scope)
|
222
|
+
if var.nil?
|
223
|
+
if parts.length == 0
|
224
|
+
return @api_map.get_methods(first, ns_here)
|
225
|
+
end
|
226
|
+
obj = get_method_return_value first, ns_here, parts.shift
|
227
|
+
while parts.length > 0
|
228
|
+
obj = get_instance_method_return_value obj, ns_here, parts.shift
|
229
|
+
end
|
230
|
+
return @api_map.get_instance_methods(obj) unless obj.nil?
|
231
|
+
else
|
232
|
+
obj = infer(var.children[1])
|
233
|
+
while parts.length > 0
|
234
|
+
obj = get_instance_method_return_value obj, ns_here, parts.shift
|
235
|
+
end
|
236
|
+
return @api_map.get_instance_methods(obj) unless obj.nil?
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def get_method_return_value namespace, root, method
|
241
|
+
meths = @api_map.get_methods(namespace, root).delete_if{ |m| m.label != method }
|
242
|
+
meths.each { |m|
|
243
|
+
unless m.documentation.nil?
|
244
|
+
match = m.documentation.all.match(/@return \[([a-z0-9:_]*)/i)
|
245
|
+
klass = match[1]
|
246
|
+
return klass unless klass.nil?
|
247
|
+
end
|
248
|
+
}
|
249
|
+
'Object'
|
250
|
+
end
|
251
|
+
|
252
|
+
def get_instance_method_return_value namespace, root, method
|
253
|
+
meths = @api_map.get_instance_methods(namespace, root).delete_if{ |m| m.label != method }
|
254
|
+
meths.each { |m|
|
255
|
+
unless m.documentation.nil?
|
256
|
+
match = m.documentation.all.match(/@return \[([a-z0-9:_]*)/i)
|
257
|
+
return match[1] unless match.nil?
|
258
|
+
end
|
259
|
+
}
|
260
|
+
'Object'
|
261
|
+
end
|
262
|
+
|
263
|
+
def get_signature_at index
|
264
|
+
#node = node_at(index - 1)
|
265
|
+
#return '' unless node.type == :send
|
266
|
+
#parts = []
|
267
|
+
#build_signature(node, parts)
|
268
|
+
#return parts.join('.')
|
269
|
+
|
270
|
+
# TODO: Alternate rough version
|
271
|
+
brackets = 0
|
272
|
+
squares = 0
|
273
|
+
parens = 0
|
274
|
+
signature = ''
|
275
|
+
index -=1
|
276
|
+
while index > 0
|
277
|
+
break if brackets > 0 or parens > 0 or squares > 0
|
278
|
+
char = @code[index, 1]
|
279
|
+
if char == ')'
|
280
|
+
parens -=1
|
281
|
+
elsif char == ']'
|
282
|
+
squares -=1
|
283
|
+
elsif char == '}'
|
284
|
+
brackets -= 1
|
285
|
+
elsif char == '('
|
286
|
+
parens += 1
|
287
|
+
elsif char == '{'
|
288
|
+
brackets += 1
|
289
|
+
elsif char == '['
|
290
|
+
squares += 1
|
291
|
+
end
|
292
|
+
if brackets == 0 and parens == 0 and squares == 0
|
293
|
+
break if ['"', "'", ',', ' ', "\t", "\n"].include?(char)
|
294
|
+
signature = char + signature if char.match(/[a-z0-9:\._]/i)
|
295
|
+
end
|
296
|
+
index -= 1
|
297
|
+
end
|
298
|
+
signature
|
299
|
+
end
|
300
|
+
|
301
|
+
def build_signature(node, parts)
|
302
|
+
if node.kind_of?(AST::Node)
|
303
|
+
if node.type == :send
|
304
|
+
parts.unshift node.children[1].to_s
|
305
|
+
elsif node.type == :const
|
306
|
+
parts.unshift unpack_name(node)
|
307
|
+
end
|
308
|
+
build_signature(node.children[0], parts)
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
def get_snippets_at(index)
|
313
|
+
result = []
|
314
|
+
Snippets.definitions.each_pair { |name, detail|
|
315
|
+
matched = false
|
316
|
+
prefix = detail['prefix']
|
317
|
+
while prefix.length > 0
|
318
|
+
if @code[index-prefix.length, prefix.length] == prefix
|
319
|
+
matched = true
|
320
|
+
break
|
321
|
+
end
|
322
|
+
prefix = prefix[0..-2]
|
323
|
+
end
|
324
|
+
if matched
|
325
|
+
result.push Suggestion.new(detail['prefix'], kind: Suggestion::KEYWORD, detail: name, insert: detail['body'].join("\r\n"))
|
326
|
+
end
|
327
|
+
}
|
328
|
+
result
|
329
|
+
end
|
330
|
+
|
331
|
+
def get_local_variables_and_methods_at(index)
|
332
|
+
result = []
|
333
|
+
local = parent_node_from(index, :class, :module, :def, :defs) || @node
|
334
|
+
result += get_local_variables_from(local)
|
335
|
+
scope = namespace_at(index) || @node
|
336
|
+
if local.type == :def
|
337
|
+
result += @api_map.get_instance_methods(scope)
|
338
|
+
else
|
339
|
+
result += @api_map.get_methods(scope)
|
340
|
+
end
|
341
|
+
result += @api_map.get_methods('Kernel')
|
342
|
+
result
|
343
|
+
end
|
344
|
+
|
345
|
+
private
|
346
|
+
|
347
|
+
def reduce_starting_with(suggestions, word)
|
348
|
+
suggestions.reject { |s|
|
349
|
+
!s.label.start_with?(word)
|
350
|
+
}
|
351
|
+
end
|
352
|
+
|
353
|
+
def get_local_variables_from(node)
|
354
|
+
node ||= @node
|
355
|
+
arr = []
|
356
|
+
node.children.each { |c|
|
357
|
+
if c.kind_of?(AST::Node)
|
358
|
+
if c.type == :lvasgn
|
359
|
+
arr.push Suggestion.new(c.children[0], kind: Suggestion::VARIABLE)
|
360
|
+
else
|
361
|
+
arr += get_local_variables_from(c) unless [:class, :module, :def, :defs].include?(c.type)
|
362
|
+
end
|
363
|
+
end
|
364
|
+
}
|
365
|
+
arr
|
366
|
+
end
|
367
|
+
|
368
|
+
def inner_node_at(index, node, arr)
|
369
|
+
node.children.each { |c|
|
370
|
+
if c.kind_of?(AST::Node)
|
371
|
+
unless c.loc.expression.nil?
|
372
|
+
if index >= c.loc.expression.begin_pos
|
373
|
+
if c.respond_to?(:end)
|
374
|
+
if index < c.end.end_pos
|
375
|
+
arr.unshift c
|
376
|
+
end
|
377
|
+
elsif index < c.loc.expression.end_pos
|
378
|
+
arr.unshift c
|
379
|
+
end
|
380
|
+
end
|
381
|
+
end
|
382
|
+
inner_node_at(index, c, arr)
|
383
|
+
end
|
384
|
+
}
|
385
|
+
end
|
386
|
+
|
387
|
+
def find_local_variable_node name, scope
|
388
|
+
scope.children.each { |c|
|
389
|
+
if c.kind_of?(AST::Node)
|
390
|
+
if c.type == :lvasgn and c.children[0].to_s == name
|
391
|
+
return c
|
392
|
+
else
|
393
|
+
unless [:class, :module, :def, :defs].include?(c.type)
|
394
|
+
sub = find_local_variable_node(name, c)
|
395
|
+
return sub unless sub.nil?
|
396
|
+
end
|
397
|
+
end
|
398
|
+
end
|
399
|
+
}
|
400
|
+
nil
|
401
|
+
end
|
402
|
+
|
403
|
+
end
|
404
|
+
end
|
@@ -0,0 +1,265 @@
|
|
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
|