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.

Files changed (121) hide show
  1. data/ChangeLog +585 -1
  2. data/README.md +10 -2
  3. data/benchmarks/yri_cache.rb +19 -0
  4. data/bin/yri +1 -26
  5. data/docs/WhatsNew.md +99 -0
  6. data/lib/rubygems_plugin.rb +2 -0
  7. data/lib/yard.rb +2 -2
  8. data/lib/yard/autoload.rb +9 -4
  9. data/lib/yard/cli/base.rb +26 -0
  10. data/lib/yard/cli/yard_graph.rb +2 -9
  11. data/lib/yard/cli/yardoc.rb +93 -33
  12. data/lib/yard/cli/yri.rb +128 -0
  13. data/lib/yard/code_objects/base.rb +16 -5
  14. data/lib/yard/code_objects/class_object.rb +11 -4
  15. data/lib/yard/code_objects/method_object.rb +11 -1
  16. data/lib/yard/code_objects/proxy.rb +5 -2
  17. data/lib/yard/code_objects/root_object.rb +1 -0
  18. data/lib/yard/core_ext/file.rb +1 -1
  19. data/lib/yard/core_ext/hash.rb +15 -0
  20. data/lib/yard/core_ext/module.rb +2 -2
  21. data/lib/yard/core_ext/string.rb +66 -0
  22. data/lib/yard/core_ext/symbol_hash.rb +1 -1
  23. data/lib/yard/docstring.rb +5 -5
  24. data/lib/yard/handlers/base.rb +10 -4
  25. data/lib/yard/handlers/processor.rb +3 -4
  26. data/lib/yard/handlers/ruby/attribute_handler.rb +3 -2
  27. data/lib/yard/handlers/ruby/legacy/attribute_handler.rb +2 -2
  28. data/lib/yard/handlers/ruby/legacy/method_handler.rb +7 -1
  29. data/lib/yard/handlers/ruby/method_handler.rb +7 -1
  30. data/lib/yard/logging.rb +11 -1
  31. data/lib/yard/parser/c_parser.rb +407 -0
  32. data/lib/yard/parser/ruby/ast_node.rb +2 -2
  33. data/lib/yard/parser/ruby/legacy/ruby_lex.rb +3 -4
  34. data/lib/yard/parser/source_parser.rb +18 -7
  35. data/lib/yard/rake/yardoc_task.rb +1 -1
  36. data/lib/yard/registry.rb +83 -29
  37. data/lib/yard/registry_store.rb +213 -0
  38. data/lib/yard/serializers/base.rb +1 -1
  39. data/lib/yard/serializers/yardoc_serializer.rb +113 -0
  40. data/lib/yard/tags/library.rb +4 -0
  41. data/lib/yard/tags/overload_tag.rb +16 -5
  42. data/lib/yard/tags/tag.rb +1 -2
  43. data/lib/yard/templates/engine.rb +3 -3
  44. data/lib/yard/templates/helpers/html_helper.rb +50 -16
  45. data/lib/yard/templates/helpers/html_syntax_highlight_helper.rb +1 -3
  46. data/lib/yard/templates/helpers/html_syntax_highlight_helper18.rb +1 -3
  47. data/lib/yard/templates/helpers/method_helper.rb +11 -4
  48. data/lib/yard/templates/helpers/text_helper.rb +24 -2
  49. data/lib/yard/verifier.rb +3 -3
  50. data/spec/cli/yardoc_spec.rb +33 -6
  51. data/spec/cli/yri_spec.rb +30 -0
  52. data/spec/code_objects/base_spec.rb +7 -0
  53. data/spec/code_objects/class_object_spec.rb +6 -1
  54. data/spec/code_objects/method_object_spec.rb +25 -0
  55. data/spec/core_ext/hash_spec.rb +10 -0
  56. data/spec/core_ext/module_spec.rb +1 -1
  57. data/spec/core_ext/string_spec.rb +50 -12
  58. data/spec/handlers/attribute_handler_spec.rb +4 -0
  59. data/spec/handlers/examples/method_handler_001.rb.txt +9 -0
  60. data/spec/handlers/method_handler_spec.rb +22 -4
  61. data/spec/parser/c_parser_spec.rb +22 -0
  62. data/spec/parser/examples/array.c.txt +3887 -0
  63. data/spec/parser/source_parser_spec.rb +29 -7
  64. data/spec/registry_spec.rb +93 -72
  65. data/spec/registry_store_spec.rb +184 -0
  66. data/spec/serializers/file_system_serializer_spec.rb +96 -75
  67. data/spec/spec_helper.rb +2 -2
  68. data/spec/tags/overload_tag_spec.rb +18 -0
  69. data/spec/templates/examples/class001.html +32 -30
  70. data/spec/templates/examples/method001.html +4 -1
  71. data/spec/templates/examples/method002.html +7 -2
  72. data/spec/templates/examples/method002.txt +1 -1
  73. data/spec/templates/examples/method003.html +30 -8
  74. data/spec/templates/examples/method003.txt +4 -4
  75. data/spec/templates/examples/method004.html +44 -0
  76. data/spec/templates/examples/method004.txt +10 -0
  77. data/spec/templates/examples/method005.html +99 -0
  78. data/spec/templates/examples/method005.txt +33 -0
  79. data/spec/templates/examples/module001.dot +1 -1
  80. data/spec/templates/examples/module001.html +391 -37
  81. data/spec/templates/examples/module001.txt +1 -1
  82. data/spec/templates/helpers/base_helper_spec.rb +2 -2
  83. data/spec/templates/helpers/html_helper_spec.rb +83 -0
  84. data/spec/templates/helpers/method_helper_spec.rb +47 -0
  85. data/spec/templates/helpers/shared_signature_examples.rb +102 -0
  86. data/spec/templates/helpers/text_helper_spec.rb +31 -0
  87. data/spec/templates/method_spec.rb +43 -18
  88. data/spec/templates/module_spec.rb +22 -1
  89. data/spec/templates/spec_helper.rb +10 -1
  90. data/spec/yard_spec.rb +4 -3
  91. data/templates/default/class/html/constructor_details.erb +1 -1
  92. data/templates/default/docstring/html/returns_void.erb +1 -0
  93. data/templates/default/docstring/setup.rb +9 -4
  94. data/templates/default/docstring/text/returns_void.erb +1 -0
  95. data/templates/default/fulldoc/html/css/style.css +4 -2
  96. data/templates/default/fulldoc/html/full_list.erb +2 -2
  97. data/templates/default/fulldoc/html/js/app.js +1 -1
  98. data/templates/default/fulldoc/html/setup.rb +14 -6
  99. data/templates/default/layout/dot/setup.rb +1 -1
  100. data/templates/default/layout/html/breadcrumb.erb +2 -2
  101. data/templates/default/layout/html/index.erb +2 -2
  102. data/templates/default/layout/html/setup.rb +5 -5
  103. data/templates/default/method/html/header.erb +6 -4
  104. data/templates/default/method_details/html/method_signature.erb +2 -1
  105. data/templates/default/method_details/html/source.erb +1 -1
  106. data/templates/default/method_details/setup.rb +2 -1
  107. data/templates/default/method_details/text/setup.rb +1 -1
  108. data/templates/default/module/html/attribute_details.erb +4 -4
  109. data/templates/default/module/html/attribute_summary.erb +3 -3
  110. data/templates/default/module/html/box_info.erb +2 -2
  111. data/templates/default/module/html/defines.erb +1 -1
  112. data/templates/default/module/html/inherited_constants.erb +1 -1
  113. data/templates/default/module/html/inherited_methods.erb +1 -1
  114. data/templates/default/module/html/item_summary.erb +13 -4
  115. data/templates/default/module/html/method_details_list.erb +5 -4
  116. data/templates/default/module/html/method_summary.erb +5 -4
  117. data/templates/default/module/html/methodmissing.erb +1 -1
  118. data/templates/default/module/setup.rb +14 -5
  119. data/templates/default/tags/html/overload.erb +3 -2
  120. data/templates/default/tags/setup.rb +4 -0
  121. metadata +23 -2
@@ -150,7 +150,7 @@ module YARD
150
150
 
151
151
  class << self
152
152
  # Clear all registered subclasses. Testing purposes only
153
- # @return [nil]
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 == Registry.root
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
- log.warn "JRuby does not implement Kernel#callcc and cannot load files in order. You must specify the correct order manually."
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 [nil]
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.error "Stack trace:" + e.backtrace[0..5].map {|x| "\n\t#{x}" }.join + "\n"
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 [nil]
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 +#{name}+\n@param value the value to set the attribute +#{name}+ to."
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 +#{name}+"
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 +#{name}+\n@param value the value to set the attribute +#{name}+ to."
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 +#{name}+"
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 +#{namespace.name}+", namespace.name.to_s))
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 +#{namespace.name}+", namespace.name.to_s))
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.level = INFO
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