yard 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of yard might be problematic. Click here for more details.
- data/ChangeLog +585 -1
- data/README.md +10 -2
- data/benchmarks/yri_cache.rb +19 -0
- data/bin/yri +1 -26
- data/docs/WhatsNew.md +99 -0
- data/lib/rubygems_plugin.rb +2 -0
- data/lib/yard.rb +2 -2
- data/lib/yard/autoload.rb +9 -4
- data/lib/yard/cli/base.rb +26 -0
- data/lib/yard/cli/yard_graph.rb +2 -9
- data/lib/yard/cli/yardoc.rb +93 -33
- data/lib/yard/cli/yri.rb +128 -0
- data/lib/yard/code_objects/base.rb +16 -5
- data/lib/yard/code_objects/class_object.rb +11 -4
- data/lib/yard/code_objects/method_object.rb +11 -1
- data/lib/yard/code_objects/proxy.rb +5 -2
- data/lib/yard/code_objects/root_object.rb +1 -0
- data/lib/yard/core_ext/file.rb +1 -1
- data/lib/yard/core_ext/hash.rb +15 -0
- data/lib/yard/core_ext/module.rb +2 -2
- data/lib/yard/core_ext/string.rb +66 -0
- data/lib/yard/core_ext/symbol_hash.rb +1 -1
- data/lib/yard/docstring.rb +5 -5
- data/lib/yard/handlers/base.rb +10 -4
- data/lib/yard/handlers/processor.rb +3 -4
- data/lib/yard/handlers/ruby/attribute_handler.rb +3 -2
- data/lib/yard/handlers/ruby/legacy/attribute_handler.rb +2 -2
- data/lib/yard/handlers/ruby/legacy/method_handler.rb +7 -1
- data/lib/yard/handlers/ruby/method_handler.rb +7 -1
- data/lib/yard/logging.rb +11 -1
- data/lib/yard/parser/c_parser.rb +407 -0
- data/lib/yard/parser/ruby/ast_node.rb +2 -2
- data/lib/yard/parser/ruby/legacy/ruby_lex.rb +3 -4
- data/lib/yard/parser/source_parser.rb +18 -7
- data/lib/yard/rake/yardoc_task.rb +1 -1
- data/lib/yard/registry.rb +83 -29
- data/lib/yard/registry_store.rb +213 -0
- data/lib/yard/serializers/base.rb +1 -1
- data/lib/yard/serializers/yardoc_serializer.rb +113 -0
- data/lib/yard/tags/library.rb +4 -0
- data/lib/yard/tags/overload_tag.rb +16 -5
- data/lib/yard/tags/tag.rb +1 -2
- data/lib/yard/templates/engine.rb +3 -3
- data/lib/yard/templates/helpers/html_helper.rb +50 -16
- data/lib/yard/templates/helpers/html_syntax_highlight_helper.rb +1 -3
- data/lib/yard/templates/helpers/html_syntax_highlight_helper18.rb +1 -3
- data/lib/yard/templates/helpers/method_helper.rb +11 -4
- data/lib/yard/templates/helpers/text_helper.rb +24 -2
- data/lib/yard/verifier.rb +3 -3
- data/spec/cli/yardoc_spec.rb +33 -6
- data/spec/cli/yri_spec.rb +30 -0
- data/spec/code_objects/base_spec.rb +7 -0
- data/spec/code_objects/class_object_spec.rb +6 -1
- data/spec/code_objects/method_object_spec.rb +25 -0
- data/spec/core_ext/hash_spec.rb +10 -0
- data/spec/core_ext/module_spec.rb +1 -1
- data/spec/core_ext/string_spec.rb +50 -12
- data/spec/handlers/attribute_handler_spec.rb +4 -0
- data/spec/handlers/examples/method_handler_001.rb.txt +9 -0
- data/spec/handlers/method_handler_spec.rb +22 -4
- data/spec/parser/c_parser_spec.rb +22 -0
- data/spec/parser/examples/array.c.txt +3887 -0
- data/spec/parser/source_parser_spec.rb +29 -7
- data/spec/registry_spec.rb +93 -72
- data/spec/registry_store_spec.rb +184 -0
- data/spec/serializers/file_system_serializer_spec.rb +96 -75
- data/spec/spec_helper.rb +2 -2
- data/spec/tags/overload_tag_spec.rb +18 -0
- data/spec/templates/examples/class001.html +32 -30
- data/spec/templates/examples/method001.html +4 -1
- data/spec/templates/examples/method002.html +7 -2
- data/spec/templates/examples/method002.txt +1 -1
- data/spec/templates/examples/method003.html +30 -8
- data/spec/templates/examples/method003.txt +4 -4
- data/spec/templates/examples/method004.html +44 -0
- data/spec/templates/examples/method004.txt +10 -0
- data/spec/templates/examples/method005.html +99 -0
- data/spec/templates/examples/method005.txt +33 -0
- data/spec/templates/examples/module001.dot +1 -1
- data/spec/templates/examples/module001.html +391 -37
- data/spec/templates/examples/module001.txt +1 -1
- data/spec/templates/helpers/base_helper_spec.rb +2 -2
- data/spec/templates/helpers/html_helper_spec.rb +83 -0
- data/spec/templates/helpers/method_helper_spec.rb +47 -0
- data/spec/templates/helpers/shared_signature_examples.rb +102 -0
- data/spec/templates/helpers/text_helper_spec.rb +31 -0
- data/spec/templates/method_spec.rb +43 -18
- data/spec/templates/module_spec.rb +22 -1
- data/spec/templates/spec_helper.rb +10 -1
- data/spec/yard_spec.rb +4 -3
- data/templates/default/class/html/constructor_details.erb +1 -1
- data/templates/default/docstring/html/returns_void.erb +1 -0
- data/templates/default/docstring/setup.rb +9 -4
- data/templates/default/docstring/text/returns_void.erb +1 -0
- data/templates/default/fulldoc/html/css/style.css +4 -2
- data/templates/default/fulldoc/html/full_list.erb +2 -2
- data/templates/default/fulldoc/html/js/app.js +1 -1
- data/templates/default/fulldoc/html/setup.rb +14 -6
- data/templates/default/layout/dot/setup.rb +1 -1
- data/templates/default/layout/html/breadcrumb.erb +2 -2
- data/templates/default/layout/html/index.erb +2 -2
- data/templates/default/layout/html/setup.rb +5 -5
- data/templates/default/method/html/header.erb +6 -4
- data/templates/default/method_details/html/method_signature.erb +2 -1
- data/templates/default/method_details/html/source.erb +1 -1
- data/templates/default/method_details/setup.rb +2 -1
- data/templates/default/method_details/text/setup.rb +1 -1
- data/templates/default/module/html/attribute_details.erb +4 -4
- data/templates/default/module/html/attribute_summary.erb +3 -3
- data/templates/default/module/html/box_info.erb +2 -2
- data/templates/default/module/html/defines.erb +1 -1
- data/templates/default/module/html/inherited_constants.erb +1 -1
- data/templates/default/module/html/inherited_methods.erb +1 -1
- data/templates/default/module/html/item_summary.erb +13 -4
- data/templates/default/module/html/method_details_list.erb +5 -4
- data/templates/default/module/html/method_summary.erb +5 -4
- data/templates/default/module/html/methodmissing.erb +1 -1
- data/templates/default/module/setup.rb +14 -5
- data/templates/default/tags/html/overload.erb +3 -2
- data/templates/default/tags/setup.rb +4 -0
- metadata +23 -2
data/lib/yard/handlers/base.rb
CHANGED
@@ -150,7 +150,7 @@ module YARD
|
|
150
150
|
|
151
151
|
class << self
|
152
152
|
# Clear all registered subclasses. Testing purposes only
|
153
|
-
# @return [
|
153
|
+
# @return [void]
|
154
154
|
def clear_subclasses
|
155
155
|
@@subclasses = []
|
156
156
|
end
|
@@ -238,6 +238,8 @@ module YARD
|
|
238
238
|
|
239
239
|
attr_reader :parser, :statement
|
240
240
|
attr_accessor :owner, :namespace, :visibility, :scope
|
241
|
+
undef owner, owner=, namespace, namespace=
|
242
|
+
undef visibility, visibility=, scope, scope=
|
241
243
|
|
242
244
|
def owner; parser.owner end
|
243
245
|
def owner=(v) parser.owner=(v) end
|
@@ -322,7 +324,7 @@ module YARD
|
|
322
324
|
end
|
323
325
|
|
324
326
|
def ensure_loaded!(object, max_retries = 1)
|
325
|
-
return if object
|
327
|
+
return if object.root?
|
326
328
|
unless parser.load_order_errors
|
327
329
|
if object.is_a?(Proxy)
|
328
330
|
raise NamespaceMissingError, object
|
@@ -331,8 +333,12 @@ module YARD
|
|
331
333
|
end
|
332
334
|
end
|
333
335
|
|
334
|
-
if RUBY_PLATFORM =~ /java/
|
335
|
-
|
336
|
+
if RUBY_PLATFORM =~ /java/ || defined?(::Rubinius)
|
337
|
+
unless $NO_CONTINUATION_WARNING
|
338
|
+
$NO_CONTINUATION_WARNING = true
|
339
|
+
log.warn "JRuby/Rubinius do not implement Kernel#callcc and cannot " +
|
340
|
+
"load files in order. You must specify the correct order manually."
|
341
|
+
end
|
336
342
|
raise NamespaceMissingError, object
|
337
343
|
end
|
338
344
|
|
@@ -63,7 +63,7 @@ module YARD
|
|
63
63
|
# one.
|
64
64
|
#
|
65
65
|
# @param [Array] statements a list of statements
|
66
|
-
# @return [
|
66
|
+
# @return [void]
|
67
67
|
def process(statements)
|
68
68
|
statements.each_with_index do |stmt, index|
|
69
69
|
find_handlers(stmt).each do |handler|
|
@@ -81,9 +81,8 @@ module YARD
|
|
81
81
|
log.warn "\tin file '#{file}':#{stmt.line}:\n\n" + stmt.show + "\n"
|
82
82
|
rescue => e
|
83
83
|
log.error "Unhandled exception in #{handler.to_s}:"
|
84
|
-
log.error "#{e.class.class_name}: #{e.message}"
|
85
84
|
log.error " in `#{file}`:#{stmt.line}:\n\n#{stmt.show}\n"
|
86
|
-
log.
|
85
|
+
log.backtrace(e)
|
87
86
|
end
|
88
87
|
end
|
89
88
|
end
|
@@ -123,7 +122,7 @@ module YARD
|
|
123
122
|
# Loads handlers from {#handler_base_namespace}. This ensures that
|
124
123
|
# Ruby1.9 handlers are never loaded into 1.8; also lowers the amount
|
125
124
|
# of modules that are loaded
|
126
|
-
# @return [
|
125
|
+
# @return [void]
|
127
126
|
def load_handlers
|
128
127
|
return if @handlers_loaded[parser_type]
|
129
128
|
handler_base_namespace.constants.each {|c| handler_base_namespace.const_get(c) }
|
@@ -5,6 +5,7 @@ class YARD::Handlers::Ruby::AttributeHandler < YARD::Handlers::Ruby::Base
|
|
5
5
|
handles method_call(:attr_accessor)
|
6
6
|
|
7
7
|
def process
|
8
|
+
return if statement.type == :var_ref
|
8
9
|
read, write = true, false
|
9
10
|
params = statement.parameters(false).dup
|
10
11
|
|
@@ -35,11 +36,11 @@ class YARD::Handlers::Ruby::AttributeHandler < YARD::Handlers::Ruby::Base
|
|
35
36
|
if type == :write
|
36
37
|
src = "def #{meth}(value)"
|
37
38
|
full_src = "#{src}\n @#{name} = value\nend"
|
38
|
-
doc = "Sets the attribute
|
39
|
+
doc = "Sets the attribute #{name}\n@param value the value to set the attribute #{name} to."
|
39
40
|
else
|
40
41
|
src = "def #{meth}"
|
41
42
|
full_src = "#{src}\n @#{name}\nend"
|
42
|
-
doc = "Returns the value of attribute
|
43
|
+
doc = "Returns the value of attribute #{name}"
|
43
44
|
end
|
44
45
|
o.source ||= full_src
|
45
46
|
o.signature ||= src
|
@@ -35,11 +35,11 @@ class YARD::Handlers::Ruby::Legacy::AttributeHandler < YARD::Handlers::Ruby::Leg
|
|
35
35
|
if type == :write
|
36
36
|
src = "def #{meth}(value)"
|
37
37
|
full_src = "#{src}\n @#{name} = value\nend"
|
38
|
-
doc = "Sets the attribute
|
38
|
+
doc = "Sets the attribute #{name}\n@param value the value to set the attribute #{name} to."
|
39
39
|
else
|
40
40
|
src = "def #{meth}"
|
41
41
|
full_src = "#{src}\n @#{name}\nend"
|
42
|
-
doc = "Returns the value of attribute
|
42
|
+
doc = "Returns the value of attribute #{name}"
|
43
43
|
end
|
44
44
|
o.source ||= full_src
|
45
45
|
o.signature ||= src
|
@@ -29,11 +29,17 @@ class YARD::Handlers::Ruby::Legacy::MethodHandler < YARD::Handlers::Ruby::Legacy
|
|
29
29
|
if mscope == :instance && meth == "initialize"
|
30
30
|
unless obj.has_tag?(:return)
|
31
31
|
obj.docstring.add_tag(YARD::Tags::Tag.new(:return,
|
32
|
-
"a new instance of
|
32
|
+
"a new instance of #{namespace.name}", namespace.name.to_s))
|
33
33
|
end
|
34
34
|
elsif mscope == :class && obj.docstring.blank? && %w(inherited included
|
35
35
|
extended method_added method_removed method_undefined).include?(meth)
|
36
36
|
obj.docstring.add_tag(YARD::Tags::Tag.new(:private, nil))
|
37
|
+
elsif meth.to_s =~ /\?$/
|
38
|
+
if obj.tag(:return) && (obj.tag(:return).types || []).empty?
|
39
|
+
obj.tag(:return).types = ['Boolean']
|
40
|
+
elsif obj.tag(:return).nil?
|
41
|
+
obj.docstring.add_tag(YARD::Tags::Tag.new(:return, "", "Boolean"))
|
42
|
+
end
|
37
43
|
end
|
38
44
|
|
39
45
|
parse_block(:owner => obj) # mainly for yield/exceptions
|
@@ -27,11 +27,17 @@ class YARD::Handlers::Ruby::MethodHandler < YARD::Handlers::Ruby::Base
|
|
27
27
|
if mscope == :instance && meth == "initialize"
|
28
28
|
unless obj.has_tag?(:return)
|
29
29
|
obj.docstring.add_tag(YARD::Tags::Tag.new(:return,
|
30
|
-
"a new instance of
|
30
|
+
"a new instance of #{namespace.name}", namespace.name.to_s))
|
31
31
|
end
|
32
32
|
elsif mscope == :class && obj.docstring.blank? && %w(inherited included
|
33
33
|
extended method_added method_removed method_undefined).include?(meth)
|
34
34
|
obj.docstring.add_tag(YARD::Tags::Tag.new(:private, nil))
|
35
|
+
elsif meth.to_s =~ /\?$/
|
36
|
+
if obj.tag(:return) && (obj.tag(:return).types || []).empty?
|
37
|
+
obj.tag(:return).types = ['Boolean']
|
38
|
+
elsif obj.tag(:return).nil?
|
39
|
+
obj.docstring.add_tag(YARD::Tags::Tag.new(:return, "", "Boolean"))
|
40
|
+
end
|
35
41
|
end
|
36
42
|
|
37
43
|
parse_block(blk, :owner => obj) # mainly for yield/exceptions
|
data/lib/yard/logging.rb
CHANGED
@@ -4,6 +4,8 @@ module YARD
|
|
4
4
|
# Handles console logging for info, warnings and errors.
|
5
5
|
# Uses the stdlib Logger class in Ruby for all the backend logic.
|
6
6
|
class Logger < ::Logger
|
7
|
+
attr_accessor :show_backtraces
|
8
|
+
|
7
9
|
# The logger instance
|
8
10
|
# @return [Logger] the logger instance
|
9
11
|
def self.instance(pipe = STDERR)
|
@@ -13,7 +15,8 @@ module YARD
|
|
13
15
|
# Creates a new logger
|
14
16
|
def initialize(*args)
|
15
17
|
super
|
16
|
-
self.
|
18
|
+
self.show_backtraces = false
|
19
|
+
self.level = WARN
|
17
20
|
self.formatter = method(:format_log)
|
18
21
|
end
|
19
22
|
|
@@ -24,6 +27,13 @@ module YARD
|
|
24
27
|
super
|
25
28
|
end
|
26
29
|
|
30
|
+
def backtrace(exc)
|
31
|
+
return unless show_backtraces
|
32
|
+
error "#{exc.class.class_name}: #{exc.message}"
|
33
|
+
error "Stack trace:" +
|
34
|
+
exc.backtrace[0..5].map {|x| "\n\t#{x}" }.join + "\n"
|
35
|
+
end
|
36
|
+
|
27
37
|
# Sets the logger level for the duration of the block
|
28
38
|
#
|
29
39
|
# @example
|
@@ -0,0 +1,407 @@
|
|
1
|
+
# Parts of this source were borrowed from `rdoc/parser/c.rb`
|
2
|
+
# RDoc's license is packaged along with Ruby.
|
3
|
+
|
4
|
+
|
5
|
+
module YARD
|
6
|
+
module Parser
|
7
|
+
class CParser
|
8
|
+
def initialize(source, file = '(stdin)')
|
9
|
+
@file = file
|
10
|
+
@namespaces = {}
|
11
|
+
@content = clean_source(source)
|
12
|
+
end
|
13
|
+
|
14
|
+
def parse
|
15
|
+
parse_modules
|
16
|
+
parse_classes
|
17
|
+
parse_methods
|
18
|
+
parse_includes
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
|
23
|
+
def ensure_loaded!(object, max_retries = 1)
|
24
|
+
return if object.is_a?(CodeObjects::RootObject)
|
25
|
+
if RUBY_PLATFORM =~ /java/ || defined?(::Rubinius)
|
26
|
+
unless $NO_CONTINUATION_WARNING
|
27
|
+
$NO_CONTINUATION_WARNING = true
|
28
|
+
log.warn "JRuby/Rubinius do not implement Kernel#callcc and cannot " +
|
29
|
+
"load files in order. You must specify the correct order manually."
|
30
|
+
end
|
31
|
+
raise NamespaceMissingError, object
|
32
|
+
end
|
33
|
+
|
34
|
+
retries = 0
|
35
|
+
context = callcc {|c| c }
|
36
|
+
retries += 1
|
37
|
+
|
38
|
+
if object.is_a?(CodeObjects::Proxy)
|
39
|
+
if retries <= max_retries
|
40
|
+
log.debug "Missing object #{object} in file `#{@file}', moving it to the back of the line."
|
41
|
+
raise Parser::LoadOrderError, context
|
42
|
+
end
|
43
|
+
end
|
44
|
+
object
|
45
|
+
end
|
46
|
+
|
47
|
+
def handle_module(var_name, mod_name, in_module = nil)
|
48
|
+
namespace = @namespaces[in_module] || (in_module ? P(in_module.gsub(/^rb_[mc]/, '')) : :root)
|
49
|
+
ensure_loaded!(namespace)
|
50
|
+
obj = CodeObjects::ModuleObject.new(namespace, mod_name)
|
51
|
+
obj.add_file(@file)
|
52
|
+
find_namespace_docstring(obj)
|
53
|
+
@namespaces[var_name] = obj
|
54
|
+
end
|
55
|
+
|
56
|
+
def handle_class(var_name, class_name, parent, in_module = nil)
|
57
|
+
parent = nil if parent == "0"
|
58
|
+
namespace = @namespaces[in_module] || (in_module ? P(in_module.gsub(/^rb_[mc]/, '')) : :root)
|
59
|
+
ensure_loaded!(namespace)
|
60
|
+
obj = CodeObjects::ClassObject.new(namespace, class_name)
|
61
|
+
obj.superclass = @namespaces[parent] || parent.gsub(/^rb_[mc]/, '') if parent
|
62
|
+
obj.add_file(@file)
|
63
|
+
find_namespace_docstring(obj)
|
64
|
+
@namespaces[var_name] = obj
|
65
|
+
end
|
66
|
+
|
67
|
+
# @todo Handle +source_file+
|
68
|
+
def handle_method(scope, var_name, name, func_name, source_file = nil)
|
69
|
+
case scope
|
70
|
+
when "singleton_method", "module_function"; scope = :class
|
71
|
+
else; scope = :instance
|
72
|
+
end
|
73
|
+
|
74
|
+
namespace = @namespaces[var_name] || P(var_name.gsub(/^rb_[mc]/, ''))
|
75
|
+
ensure_loaded!(namespace)
|
76
|
+
obj = CodeObjects::MethodObject.new(namespace, name, scope)
|
77
|
+
obj.add_file(@file)
|
78
|
+
obj.parameters = []
|
79
|
+
obj.docstring.add_tag(YARD::Tags::Tag.new(:return, '', 'Boolean')) if name =~ /\?$/
|
80
|
+
obj.source_type = :c
|
81
|
+
find_method_body(obj, func_name)
|
82
|
+
end
|
83
|
+
|
84
|
+
def handle_constants(type, var_name, const_name, definition)
|
85
|
+
namespace = @namespaces[var_name]
|
86
|
+
obj = CodeObjects::ConstantObject.new(namespace, const_name)
|
87
|
+
comment = find_constant_docstring(type, const_name)
|
88
|
+
|
89
|
+
# In the case of rb_define_const, the definition and comment are in
|
90
|
+
# "/* definition: comment */" form. The literal ':' and '\' characters
|
91
|
+
# can be escaped with a backslash.
|
92
|
+
if type.downcase == 'const'
|
93
|
+
elements = comment.split(':')
|
94
|
+
new_definition = elements[0..-2].join(':')
|
95
|
+
if new_definition.empty? then # Default to literal C definition
|
96
|
+
new_definition = definition
|
97
|
+
else
|
98
|
+
new_definition.gsub!("\:", ":")
|
99
|
+
new_definition.gsub!("\\", '\\')
|
100
|
+
end
|
101
|
+
new_definition.sub!(/\A(\s+)/, '')
|
102
|
+
comment = $1.nil? ? elements.last : "#{$1}#{elements.last.lstrip}"
|
103
|
+
end
|
104
|
+
|
105
|
+
obj.docstring = comment
|
106
|
+
end
|
107
|
+
|
108
|
+
def find_namespace_docstring(object)
|
109
|
+
comment = nil
|
110
|
+
if @content =~ %r{((?>/\*.*?\*/\s+))
|
111
|
+
(static\s+)?void\s+Init_#{object.name}\s*(?:_\(\s*)?\(\s*(?:void\s*)\)}xmi then
|
112
|
+
comment = $1
|
113
|
+
elsif @content =~ %r{Document-(?:class|module):\s#{object.path}\s*?(?:<\s+[:,\w]+)?\n((?>.*?\*/))}m
|
114
|
+
comment = $1
|
115
|
+
else
|
116
|
+
if @content =~ /rb_define_(class|module)/m then
|
117
|
+
comments = []
|
118
|
+
@content.split(/(\/\*.*?\*\/)\s*?\n/m).each_with_index do |chunk, index|
|
119
|
+
comments[index] = chunk
|
120
|
+
if chunk =~ /rb_define_(class|module).*?"(#{object.name})"/m then
|
121
|
+
comment = comments[index-1]
|
122
|
+
break
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
object.docstring = parse_comments(object, comment) if comment
|
128
|
+
end
|
129
|
+
|
130
|
+
def find_constant_docstring(type, const_name)
|
131
|
+
comments = if @content =~ %r{((?>^\s*/\*.*?\*/\s+))
|
132
|
+
rb_define_#{type}\((?:\s*(\w+),)?\s*"#{const_name}"\s*,.*?\)\s*;}xmi
|
133
|
+
$1
|
134
|
+
elsif @content =~ %r{Document-(?:const|global|variable):\s#{const_name}\s*?\n((?>.*?\*/))}m
|
135
|
+
$1
|
136
|
+
else
|
137
|
+
''
|
138
|
+
end
|
139
|
+
parse_comments(object, comments)
|
140
|
+
end
|
141
|
+
|
142
|
+
def find_method_body(object, func_name, content = @content)
|
143
|
+
case content
|
144
|
+
when %r"((?>/\*.*?\*/\s*))(?:(?:static|SWIGINTERN)\s+)?(?:intern\s+)?VALUE\s+#{func_name}
|
145
|
+
\s*(\([^)]*\))([^;]|$)"xm
|
146
|
+
comment, params = $1, $2
|
147
|
+
body_text = $&
|
148
|
+
|
149
|
+
remove_private_comments(comment) if comment
|
150
|
+
|
151
|
+
# see if we can find the whole body
|
152
|
+
|
153
|
+
re = Regexp.escape(body_text) + '[^(]*^\{.*?^\}'
|
154
|
+
body_text = $& if /#{re}/m =~ content
|
155
|
+
|
156
|
+
# The comment block may have been overridden with a 'Document-method'
|
157
|
+
# block. This happens in the interpreter when multiple methods are
|
158
|
+
# vectored through to the same C method but those methods are logically
|
159
|
+
# distinct (for example Kernel.hash and Kernel.object_id share the same
|
160
|
+
# implementation
|
161
|
+
|
162
|
+
# override_comment = find_override_comment(object)
|
163
|
+
# comment = override_comment if override_comment
|
164
|
+
|
165
|
+
object.docstring = parse_comments(object, comment) if comment
|
166
|
+
object.source = body_text
|
167
|
+
when %r{((?>/\*.*?\*/\s*))^\s*\#\s*define\s+#{func_name}\s+(\w+)}m
|
168
|
+
comment = $1
|
169
|
+
find_method_body(object, $2, content)
|
170
|
+
else
|
171
|
+
# No body, but might still have an override comment
|
172
|
+
# comment = find_override_comment(object)
|
173
|
+
comment = nil
|
174
|
+
object.docstring = parse_comments(object, comment) if comment
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def parse_comments(object, comments)
|
179
|
+
spaces = nil
|
180
|
+
comments = remove_private_comments(comments)
|
181
|
+
comments = comments.split(/\r?\n/).map do |line|
|
182
|
+
line.gsub!(/^\s*\/?\*\/?/, '')
|
183
|
+
line.gsub!(/\*\/\s*$/, '')
|
184
|
+
if line =~ /^\s*$/
|
185
|
+
next if spaces.nil?
|
186
|
+
next ""
|
187
|
+
end
|
188
|
+
spaces = (line[/^(\s+)/, 1] || "").size if spaces.nil?
|
189
|
+
line.gsub(/^\s{0,#{spaces}}/, '').rstrip
|
190
|
+
end.compact
|
191
|
+
|
192
|
+
comments.shift if comments.first =~ /^\s*Document-method:/
|
193
|
+
comments = parse_callseq(object, comments)
|
194
|
+
comments.join("\n")
|
195
|
+
end
|
196
|
+
|
197
|
+
def parse_callseq(object, comments)
|
198
|
+
return comments unless comments[0] =~ /\Acall-seq:\s*(\S.+)?/
|
199
|
+
if $1
|
200
|
+
comments[0] = " #{$1}"
|
201
|
+
else
|
202
|
+
comments.shift
|
203
|
+
end
|
204
|
+
overloads = []
|
205
|
+
while comments.first =~ /^\s+(\S.+)/ || comments.first =~ /^\s*$/
|
206
|
+
line = comments.shift.strip
|
207
|
+
next if line.empty?
|
208
|
+
line.sub!(/^\w+[\.#]/, '')
|
209
|
+
signature, types = *line.split(/ [-=]> /)
|
210
|
+
types = parse_types(object, types)
|
211
|
+
if signature.sub!(/\[?\s*(\{(?:\s*\|(.+?)\|)?.*\})\s*\]?\s*$/, '') && $1
|
212
|
+
blk, blkparams = $1, $2
|
213
|
+
else
|
214
|
+
blk, blkparams = nil, nil
|
215
|
+
end
|
216
|
+
case signature
|
217
|
+
when /^(\w+)\s*=\s+(\w+)/
|
218
|
+
signature = "#{$1}=(#{$2})"
|
219
|
+
when /^\w+\s+\S/
|
220
|
+
signature = signature.split(/\s+/)
|
221
|
+
signature = "#{signature[1]}#{signature[2] ? '(' + signature[2..-1].join(' ') + ')' : ''}"
|
222
|
+
when /^\w+\[(.+?)\]\s*(=)?/
|
223
|
+
signature = "[]#{$2}(#{$1})"
|
224
|
+
when /^\w+\s+(#{CodeObjects::METHODMATCH})\s+(\w+)/
|
225
|
+
signature = "#{$1}(#{$2})"
|
226
|
+
end
|
227
|
+
next unless signature =~ /^#{CodeObjects::METHODNAMEMATCH}/
|
228
|
+
signature = signature.rstrip
|
229
|
+
overloads << "@overload #{signature}"
|
230
|
+
overloads << " @yield [#{blkparams}]" if blk
|
231
|
+
overloads << " @return [#{types.join(', ')}]" unless types.empty?
|
232
|
+
end
|
233
|
+
|
234
|
+
comments + [""] + overloads
|
235
|
+
end
|
236
|
+
|
237
|
+
def parse_types(object, types)
|
238
|
+
if types =~ /true or false/
|
239
|
+
["Boolean"]
|
240
|
+
else
|
241
|
+
(types||"").split(/,| or /).map do |t|
|
242
|
+
case t.strip.gsub(/^an?_/, '')
|
243
|
+
when "class"; "Class"
|
244
|
+
when "obj", "object", "anObject"; "Object"
|
245
|
+
when "arr", "array", "anArray", /^\[/; "Array"
|
246
|
+
when "str", "string", "new_str"; "String"
|
247
|
+
when "enum", "anEnumerator"; "Enumerator"
|
248
|
+
when "exc", "exception"; "Exception"
|
249
|
+
when "proc", "proc_obj", "prc"; "Proc"
|
250
|
+
when "binding"; "Binding"
|
251
|
+
when "hsh", "hash", "aHash"; "Hash"
|
252
|
+
when "ios", "io"; "IO"
|
253
|
+
when "file"; "File"
|
254
|
+
when "float"; "Float"
|
255
|
+
when "time", "new_time"; "Time"
|
256
|
+
when "dir", "aDir"; "Dir"
|
257
|
+
when "regexp", "new_regexp"; "Regexp"
|
258
|
+
when "matchdata"; "MatchData"
|
259
|
+
when "encoding"; "Encoding"
|
260
|
+
when "fixnum", "fix"; "Fixnum"
|
261
|
+
when "int", "integer", "Integer"; "Integer"
|
262
|
+
when "num", "numeric", "Numeric", "number"; "Numeric"
|
263
|
+
when "aBignum"; "Bignum"
|
264
|
+
when "nil"; "nil"
|
265
|
+
when "true"; "true"
|
266
|
+
when "false"; "false"
|
267
|
+
when "boolean", "Boolean"; "Boolean"
|
268
|
+
when "self"; object.namespace.name.to_s
|
269
|
+
when /^[-+]?\d/; t
|
270
|
+
end
|
271
|
+
end.compact
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
def parse_modules
|
276
|
+
@content.scan(/(\w+)\s* = \s*rb_define_module\s*
|
277
|
+
\(\s*"(\w+)"\s*\)/mx) do |var_name, class_name|
|
278
|
+
handle_module(var_name, class_name)
|
279
|
+
end
|
280
|
+
|
281
|
+
@content.scan(/(\w+)\s* = \s*rb_define_module_under\s*
|
282
|
+
\(
|
283
|
+
\s*(\w+),
|
284
|
+
\s*"(\w+)"
|
285
|
+
\s*\)/mx) do |var_name, in_module, class_name|
|
286
|
+
handle_module(var_name, class_name, in_module)
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def parse_classes
|
291
|
+
# The '.' lets us handle SWIG-generated files
|
292
|
+
@content.scan(/([\w\.]+)\s* = \s*(?:rb_define_class|boot_defclass)\s*
|
293
|
+
\(
|
294
|
+
\s*"(\w+)",
|
295
|
+
\s*(\w+|0)\s*
|
296
|
+
\)/mx) do |var_name, class_name, parent|
|
297
|
+
handle_class(var_name, class_name, parent)
|
298
|
+
end
|
299
|
+
|
300
|
+
@content.scan(/([\w\.]+)\s* = \s*rb_define_class_under\s*
|
301
|
+
\(
|
302
|
+
\s*(\w+),
|
303
|
+
\s*"(\w+)",
|
304
|
+
\s*([\w\*\s\(\)\.\->]+)\s* # for SWIG
|
305
|
+
\s*\)/mx) do |var_name, in_module, class_name, parent|
|
306
|
+
handle_class(var_name, class_name, parent, in_module)
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
def parse_methods
|
311
|
+
@content.scan(%r{rb_define_
|
312
|
+
(
|
313
|
+
singleton_method |
|
314
|
+
method |
|
315
|
+
module_function |
|
316
|
+
private_method
|
317
|
+
)
|
318
|
+
\s*\(\s*([\w\.]+),
|
319
|
+
\s*"([^"]+)",
|
320
|
+
\s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
|
321
|
+
\s*(-?\w+)\s*\)
|
322
|
+
(?:;\s*/[*/]\s+in\s+(\w+?\.[cy]))?
|
323
|
+
}xm) do |type, var_name, name, func_name, param_count, source_file|
|
324
|
+
|
325
|
+
# Ignore top-object and weird struct.c dynamic stuff
|
326
|
+
next if var_name == "ruby_top_self"
|
327
|
+
next if var_name == "nstr"
|
328
|
+
next if var_name == "envtbl"
|
329
|
+
|
330
|
+
var_name = "rb_cObject" if var_name == "rb_mKernel"
|
331
|
+
handle_method(type, var_name, name, func_name, source_file)
|
332
|
+
end
|
333
|
+
|
334
|
+
@content.scan(%r{rb_define_global_function\s*\(
|
335
|
+
\s*"([^"]+)",
|
336
|
+
\s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?,
|
337
|
+
\s*(-?\w+)\s*\)
|
338
|
+
(?:;\s*/[*/]\s+in\s+(\w+?\.[cy]))?
|
339
|
+
}xm) do |name, func_name, param_count, source_file|
|
340
|
+
handle_method("method", "rb_mKernel", name, func_name, source_file)
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
def parse_includes
|
345
|
+
@content.scan(/rb_include_module\s*\(\s*(\w+?),\s*(\w+?)\s*\)/) do |klass, mod|
|
346
|
+
if klass = @namespaces[klass]
|
347
|
+
mod = @namespaces[mod] || P(mod.gsub(/^rb_[mc]/, ''))
|
348
|
+
klass.mixins(:instance) << mod
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
def parse_constants
|
354
|
+
@content.scan(%r{\Wrb_define_
|
355
|
+
(
|
356
|
+
variable |
|
357
|
+
readonly_variable |
|
358
|
+
const |
|
359
|
+
global_const |
|
360
|
+
)
|
361
|
+
\s*\(
|
362
|
+
(?:\s*(\w+),)?
|
363
|
+
\s*"(\w+)",
|
364
|
+
\s*(.*?)\s*\)\s*;
|
365
|
+
}xm) do |type, var_name, const_name, definition|
|
366
|
+
var_name = "rb_cObject" if !var_name or var_name == "rb_mKernel"
|
367
|
+
handle_constants(type, var_name, const_name, definition)
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
private
|
372
|
+
|
373
|
+
def clean_source(source)
|
374
|
+
source = handle_ifdefs_in(source)
|
375
|
+
source = handle_tab_width(source)
|
376
|
+
source = remove_commented_out_lines(source)
|
377
|
+
source
|
378
|
+
end
|
379
|
+
|
380
|
+
def handle_ifdefs_in(body)
|
381
|
+
body.gsub(/^#ifdef HAVE_PROTOTYPES.*?#else.*?\n(.*?)#endif.*?\n/m, '\1')
|
382
|
+
end
|
383
|
+
|
384
|
+
def handle_tab_width(body)
|
385
|
+
if /\t/ =~ body
|
386
|
+
tab_width = 4
|
387
|
+
body.split(/\n/).map do |line|
|
388
|
+
1 while line.gsub!(/\t+/) { ' ' * (tab_width*$&.length - $`.length % tab_width)} && $~ #`
|
389
|
+
line
|
390
|
+
end .join("\n")
|
391
|
+
else
|
392
|
+
body
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
def remove_commented_out_lines(body)
|
397
|
+
body.gsub(%r{//.*rb_define_}, '//')
|
398
|
+
end
|
399
|
+
|
400
|
+
def remove_private_comments(comment)
|
401
|
+
comment = comment.gsub(/\/?\*--\n(.*?)\/?\*\+\+/m, '')
|
402
|
+
comment = comment.sub(/\/?\*--\n.*/m, '')
|
403
|
+
comment
|
404
|
+
end
|
405
|
+
end
|
406
|
+
end
|
407
|
+
end
|