RDoc::Parser::C
attempts to parse C
extension files. It looks for the standard patterns that you find in extensions: rb_define_class
, rb_define_method
and so on. It tries to find the corresponding C
source for the methods and extract comments, but if we fail we don’t worry too much.
The comments associated with a Ruby method are extracted from the C
comment block associated with the routine that implements that method, that is to say the method whose name is given in the rb_define_method
call. For example, you might write:
/*
* Returns a new array that is a one-dimensional flattening of this
* array (recursively). That is, for every element that is an array,
* extract its elements into the new array.
*
* s = [ 1, 2, 3 ] #=> [1, 2, 3]
* t = [ 4, 5, 6, [7, 8] ] #=> [4, 5, 6, [7, 8]]
* a = [ s, t, 9, 10 ] #=> [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10]
* a.flatten #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
*/
static VALUE
rb_ary_flatten(VALUE ary)
{
ary = rb_obj_dup(ary);
rb_ary_flatten_bang(ary);
return ary;
}
...
void
Init_Array(void)
{
...
rb_define_method(rb_cArray, "flatten", rb_ary_flatten, 0);
Here RDoc
will determine from the rb_define_method
line that there’s a method called “flatten” in class Array
, and will look for the implementation in the method rb_ary_flatten
. It will then use the comment from that method in the HTML output. This method must be in the same source file as the rb_define_method
.
The comment blocks may include special directives:
- Document-class:
name
-
Documentation for the named class.
- Document-module:
name
-
Documentation for the named module.
- Document-const:
name
-
Documentation for the named
rb_define_const
.Constant values can be supplied on the first line of the comment like so:
/* 300: The highest possible score in bowling */ rb_define_const(cFoo, "PERFECT", INT2FIX(300));
The value can contain internal colons so long as they are escaped with a \
- Document-global:
name
-
Documentation for the named
rb_define_global_const
- Document-variable:
name
-
Documentation for the named
rb_define_variable
- Document-method:
method_name
-
Documentation for the named method. Use this when the method name is unambiguous.
- Document-method:
ClassName::method_name
-
Documentation for a singleton method in the given class. Use this when the method name alone is ambiguous.
- Document-method:
ClassName#method_name
-
Documentation for a instance method in the given class. Use this when the method name alone is ambiguous.
- Document-attr:
name
-
Documentation for the named attribute.
- call-seq: text up to an empty line
-
Because
C
source doesn’t give descriptive names to Ruby-level parameters, you need to document the calling sequence explicitly
In addition, RDoc
assumes by default that the C
method implementing a Ruby function is in the same source file as the rb_define_method call. If this isn’t the case, add the comment:
rb_define_method(....); // in filename
As an example, we might have an extension that defines multiple classes in its Init_xxx method. We could document them using
/*
* Document-class: MyClass
*
* Encapsulate the writing and reading of the configuration
* file. ...
*/
/*
* Document-method: read_value
*
* call-seq:
* cfg.read_value(key) -> value
* cfg.read_value(key} { |key| } -> value
*
* Return the value corresponding to +key+ from the configuration.
* In the second form, if the key isn't found, invoke the
* block and return its value.
*/
- A
- D
- F
- G
- H
- L
- N
- R
- S
Attributes
[R] | classes | Maps |
[RW] | content |
|
[R] | enclosure_dependencies | Dependencies from a missing enclosing class to the classes in |
[R] | known_classes | Maps |
[R] | missing_dependencies | Classes found while parsing the |
[R] | singleton_classes | Maps |
[R] | top_level | The TopLevel items in the parsed file belong to |
Class Public methods
new(top_level, file_name, content, options, stats) Link
Prepares for parsing a C
file. See RDoc::Parser#initialize for details on the arguments.
# File ruby/lib/rdoc/parser/c.rb, line 171 def initialize top_level, file_name, content, options, stats super @known_classes = RDoc::KNOWN_CLASSES.dup @content = handle_tab_width handle_ifdefs_in @content @file_dir = File.dirname @file_name @classes = load_variable_map :c_class_variables @singleton_classes = load_variable_map :c_singleton_class_variables @markup = @options.markup # class_variable => { function => [method, ...] } @methods = Hash.new { |h, f| h[f] = Hash.new { |i, m| i[m] = [] } } # missing variable => [handle_class_module arguments] @missing_dependencies = {} # missing enclosure variable => [dependent handle_class_module arguments] @enclosure_dependencies = Hash.new { |h, k| h[k] = [] } @enclosure_dependencies.instance_variable_set :@missing_dependencies, @missing_dependencies @enclosure_dependencies.extend TSort def @enclosure_dependencies.tsort_each_node &block each_key(&block) rescue TSort::Cyclic => e cycle_vars = e.message.scan(/"(.*?)"/).flatten cycle = cycle_vars.sort.map do |var_name| delete var_name var_name, type, mod_name, = @missing_dependencies[var_name] "#{type} #{mod_name} (#{var_name})" end.join ', ' warn "Unable to create #{cycle} due to a cyclic class or module creation" retry end def @enclosure_dependencies.tsort_each_child node, &block fetch(node, []).each(&block) end end
Instance Public methods
add_alias(var_name, class_obj, old_name, new_name, comment) Link
Add alias, either from a direct alias definition, or from two method that reference the same function.
# File ruby/lib/rdoc/parser/c.rb, line 250 def add_alias(var_name, class_obj, old_name, new_name, comment) al = RDoc::Alias.new '', old_name, new_name, '' al.singleton = @singleton_classes.key? var_name al.comment = comment al.record_location @top_level class_obj.add_alias al @stats.add_alias al al end
do_aliases() Link
Scans content
for rb_define_alias
# File ruby/lib/rdoc/parser/c.rb, line 222 def do_aliases @content.scan(/rb_define_alias\s*\( \s*(\w+), \s*"(.+?)", \s*"(.+?)" \s*\)/xm) do |var_name, new_name, old_name| class_name = @known_classes[var_name] unless class_name then @options.warn "Enclosing class or module %p for alias %s %s is not known" % [ var_name, new_name, old_name] next end class_obj = find_class var_name, class_name comment = find_alias_comment var_name, new_name, old_name comment.normalize if comment.to_s.empty? and existing_method = class_obj.method_list.find { |m| m.name == old_name} comment = existing_method.comment end add_alias(var_name, class_obj, old_name, new_name, comment) end end
do_attrs() Link
Scans content
for rb_attr and rb_define_attr
# File ruby/lib/rdoc/parser/c.rb, line 263 def do_attrs @content.scan(/rb_attr\s*\( \s*(\w+), \s*([\w"()]+), #{BOOL_ARG_PATTERN}, #{BOOL_ARG_PATTERN}, \s*\w+\);/xmo) do |var_name, attr_name, read, write| handle_attr var_name, attr_name, read, write end @content.scan(%r%rb_define_attr\( \s*([\w\.]+), \s*"([^"]+)", #{BOOL_ARG_PATTERN}, #{BOOL_ARG_PATTERN}\); %xmo) do |var_name, attr_name, read, write| handle_attr var_name, attr_name, read, write end end
do_classes_and_modules() Link
Scans content
for rb_define_class, boot_defclass, rb_define_class_under and rb_singleton_class
# File ruby/lib/rdoc/parser/c.rb, line 298 def do_classes_and_modules do_boot_defclass if @file_name == "class.c" @content.scan( %r( (?<open>\s*\(\s*) {0} (?<close>\s*\)\s*) {0} (?<name>\s*"(?<class_name>\w+)") {0} (?<parent>\s*(?: (?<parent_name>[\w\*\s\(\)\.\->]+) | rb_path2class\s*\(\s*"(?<path>[\w:]+)"\s*\) )) {0} (?<under>\w+) {0} (?<var_name>[\w\.]+)\s* = \s*rb_(?: define_(?: class(?: # rb_define_class(name, parent_name) \(\s* \g<name>, \g<parent> \s*\) | _under\g<open> # rb_define_class_under(under, name, parent_name...) \g<under>, \g<name>, \g<parent> \g<close> ) | (?<module>) module(?: # rb_define_module(name) \g<open> \g<name> \g<close> | _under\g<open> # rb_define_module_under(under, name) \g<under>, \g<name> \g<close> ) ) | (?<attributes>(?:\s*"\w+",)*\s*NULL\s*) {0} struct_define(?: \g<open> # rb_struct_define(name, ...) \g<name>, | _under\g<open> # rb_struct_define_under(under, name, ...) \g<under>, \g<name>, | _without_accessor(?: \g<open> # rb_struct_define_without_accessor(name, parent_name, ...) | _under\g<open> # rb_struct_define_without_accessor_under(under, name, parent_name, ...) \g<under>, ) \g<name>, \g<parent>, \s*\w+, # Allocation function ) \g<attributes> \g<close> | singleton_class\g<open> # rb_singleton_class(target_class_name) (?<target_class_name>\w+) \g<close> ) )mx ) do if target_class_name = $~[:target_class_name] # rb_singleton_class(target_class_name) handle_singleton $~[:var_name], target_class_name next end var_name = $~[:var_name] type = $~[:module] ? :module : :class class_name = $~[:class_name] parent_name = $~[:parent_name] || $~[:path] under = $~[:under] attributes = $~[:attributes] handle_class_module(var_name, type, class_name, parent_name, under) if attributes and !parent_name # rb_struct_define *not* without_accessor true_flag = 'Qtrue' attributes.scan(/"\K\w+(?=")/) do |attr_name| handle_attr var_name, attr_name, true_flag, true_flag end end end end
do_constants() Link
Scans content
for rb_define_variable, rb_define_readonly_variable, rb_define_const and rb_define_global_const
# File ruby/lib/rdoc/parser/c.rb, line 396 def do_constants @content.scan(%r%\Wrb_define_ ( variable | readonly_variable | const | global_const ) \s*\( (?:\s*(\w+),)? \s*"(\w+)", \s*(.*?)\s*\)\s*; %xm) do |type, var_name, const_name, definition| var_name = "rb_cObject" if !var_name or var_name == "rb_mKernel" handle_constants type, var_name, const_name, definition end @content.scan(%r% \Wrb_curses_define_const \s*\( \s* (\w+) \s* \) \s*;%xm) do |consts| const = consts.first handle_constants 'const', 'mCurses', const, "UINT2NUM(#{const})" end @content.scan(%r% \Wrb_file_const \s*\( \s* "([^"]+)", \s* (.*?) \s* \) \s*;%xm) do |name, value| handle_constants 'const', 'rb_mFConst', name, value end end
do_includes() Link
Scans content
for rb_include_module
# File ruby/lib/rdoc/parser/c.rb, line 442 def do_includes @content.scan(/rb_include_module\s*\(\s*(\w+?),\s*(\w+?)\s*\)/) do |c,m| next unless cls = @classes[c] m = @known_classes[m] || m comment = new_comment '', @top_level, :c incl = cls.add_include RDoc::Include.new(m, comment) incl.record_location @top_level end end
do_methods() Link
Scans content
for rb_define_method, rb_define_singleton_method, rb_define_module_function, rb_define_private_method, rb_define_global_function and define_filetest_function
# File ruby/lib/rdoc/parser/c.rb, line 458 def do_methods @content.scan(%r%rb_define_ ( singleton_method | method | module_function | private_method ) \s*\(\s*([\w\.]+), \s*"([^"]+)", \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\(|\(METHOD\))?(\w+)\)?, \s*(-?\w+)\s*\) (?:;\s*/[*/]\s+in\s+(\w+?\.(?:cpp|c|y)))? %xm) do |type, var_name, meth_name, function, param_count, source_file| # Ignore top-object and weird struct.c dynamic stuff next if var_name == "ruby_top_self" next if var_name == "nstr" var_name = "rb_cObject" if var_name == "rb_mKernel" handle_method(type, var_name, meth_name, function, param_count, source_file) end @content.scan(%r%rb_define_global_function\s*\( \s*"([^"]+)", \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?, \s*(-?\w+)\s*\) (?:;\s*/[*/]\s+in\s+(\w+?\.[cy]))? %xm) do |meth_name, function, param_count, source_file| handle_method("method", "rb_mKernel", meth_name, function, param_count, source_file) end @content.scan(/define_filetest_function\s*\( \s*"([^"]+)", \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?, \s*(-?\w+)\s*\)/xm) do |meth_name, function, param_count| handle_method("method", "rb_mFileTest", meth_name, function, param_count) handle_method("singleton_method", "rb_cFile", meth_name, function, param_count) end end
do_missing() Link
Creates classes and module that were missing were defined due to the file order being different than the declaration order.
# File ruby/lib/rdoc/parser/c.rb, line 507 def do_missing return if @missing_dependencies.empty? @enclosure_dependencies.tsort.each do |in_module| arguments = @missing_dependencies.delete in_module next unless arguments # dependency on existing class handle_class_module(*arguments) end end
find_alias_comment(class_name, new_name, old_name) Link
Finds the comment for an alias on class_name
from new_name
to old_name
# File ruby/lib/rdoc/parser/c.rb, line 523 def find_alias_comment class_name, new_name, old_name content =~ %r%((?>/\*.*?\*/\s+)) rb_define_alias\(\s*#{Regexp.escape class_name}\s*, \s*"#{Regexp.escape new_name}"\s*, \s*"#{Regexp.escape old_name}"\s*\);%xm new_comment($1 || '', @top_level, :c) end
find_attr_comment(var_name, attr_name, read = nil, write = nil) Link
Finds a comment for rb_define_attr, rb_attr or Document-attr.
var_name
is the C
class variable the attribute is defined on. attr_name
is the attribute’s name.
read
and write
are the read/write flags (‘1’ or ‘0’). Either both or neither must be provided.
# File ruby/lib/rdoc/parser/c.rb, line 541 def find_attr_comment var_name, attr_name, read = nil, write = nil attr_name = Regexp.escape attr_name rw = if read and write then /\s*#{read}\s*,\s*#{write}\s*/xm else /.*?/m end comment = if @content =~ %r%((?>/\*.*?\*/\s+)) rb_define_attr\((?:\s*#{var_name},)?\s* "#{attr_name}"\s*, #{rw}\)\s*;%xm then $1 elsif @content =~ %r%((?>/\*.*?\*/\s+)) rb_attr\(\s*#{var_name}\s*, \s*#{attr_name}\s*, #{rw},.*?\)\s*;%xm then $1 elsif @content =~ %r%(/\*.*?(?:\s*\*\s*)?) Document-attr:\s#{attr_name}\s*?\n ((?>(.|\n)*?\*/))%x then "#{$1}\n#{$2}" else '' end new_comment comment, @top_level, :c end
find_body(class_name, meth_name, meth_obj, file_content, quiet = false) Link
# File ruby/lib/rdoc/parser/c.rb, line 598 def find_body class_name, meth_name, meth_obj, file_content, quiet = false if file_content @body_table ||= {} @body_table[file_content] ||= gen_body_table file_content type, *args = @body_table[file_content][meth_name] end case type when :func_def comment = new_comment args[0], @top_level, :c body = args[1] offset, = args[2] comment.remove_private if comment # try to find the whole body body = $& if /#{Regexp.escape body}[^(]*?\{.*?^\}/m =~ file_content # The comment block may have been overridden with a 'Document-method' # block. This happens in the interpreter when multiple methods are # vectored through to the same C method but those methods are logically # distinct (for example Kernel.hash and Kernel.object_id share the same # implementation override_comment = find_override_comment class_name, meth_obj comment = override_comment if override_comment comment.normalize find_modifiers comment, meth_obj if comment #meth_obj.params = params meth_obj.start_collecting_tokens tk = { :line_no => 1, :char_no => 1, :text => body } meth_obj.add_token tk meth_obj.comment = comment meth_obj.line = file_content[0, offset].count("\n") + 1 body when :macro_def comment = new_comment args[0], @top_level, :c body = args[1] offset, = args[2] find_body class_name, args[3], meth_obj, file_content, true comment.normalize find_modifiers comment, meth_obj meth_obj.start_collecting_tokens tk = { :line_no => 1, :char_no => 1, :text => body } meth_obj.add_token tk meth_obj.comment = comment meth_obj.line = file_content[0, offset].count("\n") + 1 body when :macro_alias # with no comment we hope the aliased definition has it and use it's # definition body = find_body(class_name, args[0], meth_obj, file_content, true) return body if body @options.warn "No definition for #{meth_name}" false else # No body, but might still have an override comment comment = find_override_comment class_name, meth_obj if comment then comment.normalize find_modifiers comment, meth_obj meth_obj.comment = comment '' else @options.warn "No definition for #{meth_name}" false end end end
find_class(raw_name, name, base_name = nil) Link
Finds a RDoc::NormalClass
or RDoc::NormalModule
for raw_name
# File ruby/lib/rdoc/parser/c.rb, line 682 def find_class(raw_name, name, base_name = nil) unless @classes[raw_name] if raw_name =~ /^rb_m/ container = @top_level.add_module RDoc::NormalModule, name else container = @top_level.add_class RDoc::NormalClass, name end container.name = base_name if base_name container.record_location @top_level @classes[raw_name] = container end @classes[raw_name] end
find_class_comment(class_name, class_mod) Link
Look for class or module documentation above Init_class_name
(void), in a Document-class class_name
(or module) comment or above an rb_define_class (or module). If a comment is supplied above a matching Init_ and a rb_define_class the Init_ comment is used.
/*
* This is a comment for Foo
*/
Init_Foo(void) {
VALUE cFoo = rb_define_class("Foo", rb_cObject);
}
/*
* Document-class: Foo
* This is a comment for Foo
*/
Init_foo(void) {
VALUE cFoo = rb_define_class("Foo", rb_cObject);
}
/*
* This is a comment for Foo
*/
VALUE cFoo = rb_define_class("Foo", rb_cObject);
# File ruby/lib/rdoc/parser/c.rb, line 723 def find_class_comment class_name, class_mod comment = nil if @content =~ %r% ((?>/\*.*?\*/\s+)) (static\s+)? void\s+ Init(?:VM)?_(?i:#{class_name})\s*(?:_\(\s*)?\(\s*(?:void\s*)?\)%xm then comment = $1.sub(%r%Document-(?:class|module):\s+#{class_name}%, '') elsif @content =~ %r%Document-(?:class|module):\s+#{class_name}\s*? (?:<\s+[:,\w]+)?\n((?>.*?\*/))%xm then comment = "/*\n#{$1}" elsif @content =~ %r%((?>/\*.*?\*/\s+)) ([\w\.\s]+\s* = \s+)?rb_define_(class|module)[\t (]*?"(#{class_name})"%xm then comment = $1 elsif @content =~ %r%((?>/\*.*?\*/\s+)) ([\w\. \t]+ = \s+)?rb_define_(class|module)_under[\t\w, (]*?"(#{class_name.split('::').last})"%xm then comment = $1 else comment = '' end comment = new_comment comment, @top_level, :c comment.normalize look_for_directives_in class_mod, comment class_mod.add_comment comment, @top_level end
find_const_comment(type, const_name, class_name = nil) Link
Finds a comment matching type
and const_name
either above the comment or in the matching Document- section.
# File ruby/lib/rdoc/parser/c.rb, line 779 def find_const_comment(type, const_name, class_name = nil) @const_table ||= {} @const_table[@content] ||= gen_const_table @content table = @const_table[@content] comment = table[[type, const_name]] || (class_name && table[class_name + "::" + const_name]) || table[const_name] || '' new_comment comment, @top_level, :c end
find_modifiers(comment, meth_obj) Link
Handles modifiers in comment
and updates meth_obj
as appropriate.
find_override_comment(class_name, meth_obj) Link
Finds a Document-method
override for meth_obj
on class_name
# File ruby/lib/rdoc/parser/c.rb, line 806 def find_override_comment class_name, meth_obj name = Regexp.escape meth_obj.name prefix = Regexp.escape meth_obj.name_prefix comment = if @content =~ %r%Document-method: \s+#{class_name}#{prefix}#{name} \s*?\n((?>.*?\*/))%xm then "/*#{$1}" elsif @content =~ %r%Document-method: \s#{name}\s*?\n((?>.*?\*/))%xm then "/*#{$1}" end return unless comment new_comment comment, @top_level, :c end
gen_body_table(file_content) Link
Generate a Ruby-method table
# File ruby/lib/rdoc/parser/c.rb, line 574 def gen_body_table file_content table = {} file_content.scan(%r{ ((?>/\*.*?\*/\s*)?) ((?:\w+\s+){0,2} VALUE\s+(\w+) \s*(?:\([^\)]*\))(?:[^\);]|$)) | ((?>/\*.*?\*/\s*))^\s*(\#\s*define\s+(\w+)\s+(\w+)) | ^\s*\#\s*define\s+(\w+)\s+(\w+) }xm) do case when name = $3 table[name] = [:func_def, $1, $2, $~.offset(2)] if !(t = table[name]) || t[0] != :func_def when name = $6 table[name] = [:macro_def, $4, $5, $~.offset(5), $7] if !(t = table[name]) || t[0] == :macro_alias when name = $8 table[name] ||= [:macro_alias, $9] end end table end
gen_const_table(file_content) Link
Generate a const table
# File ruby/lib/rdoc/parser/c.rb, line 756 def gen_const_table file_content table = {} @content.scan(%r{ ((?>^\s*/\*.*?\*/\s+)) rb_define_(\w+)\((?:\s*(?:\w+),)?\s* "(\w+)"\s*, .*?\)\s*; | Document-(?:const|global|variable):\s ((?:\w+::)*\w+) \s*?\n((?>.*?\*/)) }mxi) do case when $1 then table[[$2, $3]] = $1 when $4 then table[$4] = "/*\n" + $5 end end table end
handle_attr(var_name, attr_name, read, write) Link
Creates a new RDoc::Attr
attr_name
on class var_name
that is either read
, write
or both
# File ruby/lib/rdoc/parser/c.rb, line 828 def handle_attr(var_name, attr_name, read, write) rw = '' rw += 'R' if TRUE_VALUES.include?(read) rw += 'W' if TRUE_VALUES.include?(write) class_name = @known_classes[var_name] return unless class_name class_obj = find_class var_name, class_name return unless class_obj comment = find_attr_comment var_name, attr_name comment.normalize name = attr_name.gsub(/rb_intern(?:_const)?\("([^"]+)"\)/, '\1') attr = RDoc::Attr.new '', name, rw, comment attr.record_location @top_level class_obj.add_attribute attr @stats.add_attribute attr end
handle_class_module(var_name, type, class_name, parent, in_module) Link
Creates a new RDoc::NormalClass
or RDoc::NormalModule
based on type
named class_name
in parent
which was assigned to the C
var_name
.
# File ruby/lib/rdoc/parser/c.rb, line 857 def handle_class_module(var_name, type, class_name, parent, in_module) parent_name = @known_classes[parent] || parent if in_module then enclosure = @classes[in_module] || @store.find_c_enclosure(in_module) if enclosure.nil? and enclosure = @known_classes[in_module] then enc_type = /^rb_m/ =~ in_module ? :module : :class handle_class_module in_module, enc_type, enclosure, nil, nil enclosure = @classes[in_module] end unless enclosure then @enclosure_dependencies[in_module] << var_name @missing_dependencies[var_name] = [var_name, type, class_name, parent, in_module] return end else enclosure = @top_level end if type == :class then full_name = if RDoc::ClassModule === enclosure then enclosure.full_name + "::#{class_name}" else class_name end if @content =~ %r%Document-class:\s+#{full_name}\s*<\s+([:,\w]+)% then parent_name = $1 end cm = enclosure.add_class RDoc::NormalClass, class_name, parent_name else cm = enclosure.add_module RDoc::NormalModule, class_name end cm.record_location enclosure.top_level find_class_comment cm.full_name, cm case cm when RDoc::NormalClass @stats.add_class cm when RDoc::NormalModule @stats.add_module cm end @classes[var_name] = cm @known_classes[var_name] = cm.full_name @store.add_c_enclosure var_name, cm end
handle_constants(type, var_name, const_name, definition) Link
Adds constants. By providing some_value: at the start of the comment you can override the C
value of the comment to give a friendly definition.
/* 300: The perfect score in bowling */
rb_define_const(cFoo, "PERFECT", INT2FIX(300));
Will override INT2FIX(300)
with the value 300
in the output RDoc
. Values may include quotes and escaped colons (:).
# File ruby/lib/rdoc/parser/c.rb, line 922 def handle_constants(type, var_name, const_name, definition) class_name = @known_classes[var_name] return unless class_name class_obj = find_class var_name, class_name, class_name[/::\K[^:]+\z/] unless class_obj then @options.warn 'Enclosing class or module %p is not known' % [const_name] return end comment = find_const_comment type, const_name, class_name comment.normalize # In the case of rb_define_const, the definition and comment are in # "/* definition: comment */" form. The literal ':' and '\' characters # can be escaped with a backslash. if type.downcase == 'const' then if /\A(.+?)?:(?!\S)/ =~ comment.text new_definition, new_comment = $1, $' if !new_definition # Default to literal C definition new_definition = definition else new_definition = new_definition.gsub(/\\([\\:])/, '\1') end new_definition.sub!(/\A(\s+)/, '') new_comment = "#{$1}#{new_comment.lstrip}" new_comment = self.new_comment(new_comment, @top_level, :c) con = RDoc::Constant.new const_name, new_definition, new_comment else con = RDoc::Constant.new const_name, definition, comment end else con = RDoc::Constant.new const_name, definition, comment end con.record_location @top_level @stats.add_constant con class_obj.add_constant con end
handle_ifdefs_in(body) Link
Removes ifdefs that would otherwise confuse us
handle_method(type, var_name, meth_name, function, param_count, source_file = nil) Link
Adds an RDoc::AnyMethod
meth_name
defined on a class or module assigned to var_name
. type
is the type of method definition function used. singleton_method
and module_function
create a singleton method.
# File ruby/lib/rdoc/parser/c.rb, line 981 def handle_method(type, var_name, meth_name, function, param_count, source_file = nil) class_name = @known_classes[var_name] singleton = @singleton_classes.key? var_name @methods[var_name][function] << meth_name return unless class_name class_obj = find_class var_name, class_name if existing_method = class_obj.method_list.find { |m| m.c_function == function } add_alias(var_name, class_obj, existing_method.name, meth_name, existing_method.comment) end if class_obj then if meth_name == 'initialize' then meth_name = 'new' singleton = true type = 'method' # force public end meth_obj = RDoc::AnyMethod.new '', meth_name meth_obj.c_function = function meth_obj.singleton = singleton || %w[singleton_method module_function].include?(type) p_count = Integer(param_count) rescue -1 if source_file then file_name = File.join @file_dir, source_file if File.exist? file_name then file_content = File.read file_name else @options.warn "unknown source #{source_file} for #{meth_name} in #{@file_name}" end else file_content = @content end body = find_body class_name, function, meth_obj, file_content if body and meth_obj.document_self then meth_obj.params = if p_count < -1 then # -2 is Array '(*args)' elsif p_count == -1 then # argc, argv rb_scan_args body else args = (1..p_count).map { |i| "p#{i}" } "(#{args.join ', '})" end meth_obj.record_location @top_level if meth_obj.section_title class_obj.temporary_section = class_obj.add_section(meth_obj.section_title) end class_obj.add_method meth_obj @stats.add_method meth_obj meth_obj.visibility = :private if 'private_method' == type end end end
handle_singleton(sclass_var, class_var) Link
Registers a singleton class sclass_var
as a singleton of class_var
load_variable_map(map_name) Link
Loads the variable map with the given name
from the RDoc::Store
, if present.
# File ruby/lib/rdoc/parser/c.rb, line 1062 def load_variable_map map_name return {} unless files = @store.cache[map_name] return {} unless name_map = files[@file_name] class_map = {} name_map.each do |variable, name| next unless mod = @store.find_class_or_module(name) class_map[variable] = if map_name == :c_class_variables then mod else name end @known_classes[variable] = name end class_map end
look_for_directives_in(context, comment) Link
Look for directives in a normal comment block:
/*
* :title: My Awesome Project
*/
This method modifies the comment
# File ruby/lib/rdoc/parser/c.rb, line 1091 def look_for_directives_in context, comment @preprocess.handle comment, context do |directive, param| case directive when 'main' then @options.main_page = param '' when 'title' then @options.default_title = param if @options.respond_to? :default_title= '' end end comment end
new_comment(text = nil, location = nil, language = nil) Link
Creates a RDoc::Comment
instance.
rb_scan_args(method_body) Link
Extracts parameters from the method_body
and returns a method parameter string. Follows 1.9.3dev’s scan-arg-spec, see README.EXT
# File ruby/lib/rdoc/parser/c.rb, line 1110 def rb_scan_args method_body method_body =~ /rb_scan_args\((.*?)\)/m return '(*args)' unless $1 $1.split(/,/)[2] =~ /"(.*?)"/ # format argument format = $1.split(//) lead = opt = trail = 0 if format.first =~ /\d/ then lead = $&.to_i format.shift if format.first =~ /\d/ then opt = $&.to_i format.shift if format.first =~ /\d/ then trail = $&.to_i format.shift block_arg = true end end end if format.first == '*' and not block_arg then var = true format.shift if format.first =~ /\d/ then trail = $&.to_i format.shift end end if format.first == ':' then hash = true format.shift end if format.first == '&' then block = true format.shift end # if the format string is not empty there's a bug in the C code, ignore it args = [] position = 1 (1...(position + lead)).each do |index| args << "p#{index}" end position += lead (position...(position + opt)).each do |index| args << "p#{index} = v#{index}" end position += opt if var then args << '*args' position += 1 end (position...(position + trail)).each do |index| args << "p#{index}" end position += trail if hash then args << "p#{position} = {}" end args << '&block' if block "(#{args.join ', '})" end
remove_commented_out_lines() Link
Removes lines that are commented out that might otherwise get picked up when scanning for classes and methods
scan() Link
Extracts the classes, modules, methods, attributes, constants and aliases from a C
file and returns an RDoc::TopLevel
for this file