diff options
author | yugui <yugui@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2008-08-25 15:13:14 +0000 |
---|---|---|
committer | yugui <yugui@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2008-08-25 15:13:14 +0000 |
commit | d0233291bc8a5068e52c69c210e5979e5324b5bc (patch) | |
tree | 7d9459449c33792c63eeb7baa071e76352e0baab /trunk/lib/rdoc | |
parent | 0dc342de848a642ecce8db697b8fecd83a63e117 (diff) | |
parent | 72eaacaa15256ab95c3b52ea386f88586fb9da40 (diff) |
re-adding tag v1_9_0_4 as an alias of trunk@18848v1_9_0_4
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/tags/v1_9_0_4@18849 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'trunk/lib/rdoc')
56 files changed, 0 insertions, 18060 deletions
diff --git a/trunk/lib/rdoc/README b/trunk/lib/rdoc/README deleted file mode 100644 index f183c61f8d..0000000000 --- a/trunk/lib/rdoc/README +++ /dev/null @@ -1,232 +0,0 @@ -= RDOC - Ruby Documentation System - -This package contains RDoc and RDoc::Markup. RDoc is an application that -produces documentation for one or more Ruby source files. We work similarly to -JavaDoc, parsing the source, and extracting the definition for classes, -modules, and methods (along with includes and requires). We associate with -these optional documentation contained in the immediately preceding comment -block, and then render the result using a pluggable output formatter. -RDoc::Markup is a library that converts plain text into various output formats. -The markup library is used to interpret the comment blocks that RDoc uses to -document methods, classes, and so on. - -== Roadmap - -* If you want to use RDoc to create documentation for your Ruby source files, - read on. -* If you want to include extensions written in C, see RDoc::C_Parser -* For information on the various markups available in comment blocks, see - RDoc::Markup. -* If you want to drive RDoc programmatically, see RDoc::RDoc. -* If you want to use the library to format text blocks into HTML, have a look - at RDoc::Markup. -* If you want to try writing your own HTML output template, see - RDoc::Generator::HTML - -== Summary - -Once installed, you can create documentation using the 'rdoc' command -(the command is 'rdoc.bat' under Windows) - - % rdoc [options] [names...] - -Type "rdoc --help" for an up-to-date option summary. - -A typical use might be to generate documentation for a package of Ruby -source (such as rdoc itself). - - % rdoc - -This command generates documentation for all the Ruby and C source -files in and below the current directory. These will be stored in a -documentation tree starting in the subdirectory 'doc'. - -You can make this slightly more useful for your readers by having the -index page contain the documentation for the primary file. In our -case, we could type - - % rdoc --main rdoc.rb - -You'll find information on the various formatting tricks you can use -in comment blocks in the documentation this generates. - -RDoc uses file extensions to determine how to process each file. File names -ending +.rb+ and <tt>.rbw</tt> are assumed to be Ruby source. Files -ending +.c+ are parsed as C files. All other files are assumed to -contain just Markup-style markup (with or without leading '#' comment markers). -If directory names are passed to RDoc, they are scanned recursively for C and -Ruby source files only. - -= Markup - -For information on how to make lists, hyperlinks, & etc. with RDoc, see -RDoc::Markup. - -Comment blocks can be written fairly naturally, either using '#' on successive -lines of the comment, or by including the comment in an =begin/=end block. If -you use the latter form, the =begin line must be flagged with an RDoc tag: - - =begin rdoc - Documentation to be processed by RDoc. - - ... - =end - -RDoc stops processing comments if it finds a comment line containing '+#--+'. -This can be used to separate external from internal comments, or to stop a -comment being associated with a method, class, or module. Commenting can be -turned back on with a line that starts '+#+++'. - - ## - # Extract the age and calculate the date-of-birth. - #-- - # FIXME: fails if the birthday falls on February 29th - #++ - # The DOB is returned as a Time object. - - def get_dob(person) - # ... - end - -Names of classes, source files, and any method names containing an underscore -or preceded by a hash character are automatically hyperlinked from comment text -to their description. - -Method parameter lists are extracted and displayed with the method description. -If a method calls +yield+, then the parameters passed to yield will also be -displayed: - - def fred - ... - yield line, address - -This will get documented as: - - fred() { |line, address| ... } - -You can override this using a comment containing ':yields: ...' immediately -after the method definition - - def fred # :yields: index, position - # ... - - yield line, address - -which will get documented as - - fred() { |index, position| ... } - -+:yields:+ is an example of a documentation directive. These appear immediately -after the start of the document element they are modifying. - -== Directives - -[+:nodoc:+ / +:nodoc:+ all] - Don't include this element in the documentation. For classes - and modules, the methods, aliases, constants, and attributes - directly within the affected class or module will also be - omitted. By default, though, modules and classes within that - class of module _will_ be documented. This is turned off by - adding the +all+ modifier. - - module MyModule # :nodoc: - class Input - end - end - - module OtherModule # :nodoc: all - class Output - end - end - - In the above code, only class +MyModule::Input+ will be documented. - -[+:doc:+] - Force a method or attribute to be documented even if it wouldn't otherwise - be. Useful if, for example, you want to include documentation of a - particular private method. - -[+:notnew:+] - Only applicable to the +initialize+ instance method. Normally RDoc assumes - that the documentation and parameters for #initialize are actually for the - ::new method, and so fakes out a ::new for the class. The :notnew: modifier - stops this. Remember that #initialize is protected, so you won't see the - documentation unless you use the -a command line option. - -Comment blocks can contain other directives: - -[+:section: title+] - Starts a new section in the output. The title following +:section:+ is used - as the section heading, and the remainder of the comment containing the - section is used as introductory text. Subsequent methods, aliases, - attributes, and classes will be documented in this section. A :section: - comment block may have one or more lines before the :section: directive. - These will be removed, and any identical lines at the end of the block are - also removed. This allows you to add visual cues such as: - - # ---------------------------------------- - # :section: My Section - # This is the section that I wrote. - # See it glisten in the noon-day sun. - # ---------------------------------------- - -[+:call-seq:+] - Lines up to the next blank line in the comment are treated as the method's - calling sequence, overriding the default parsing of method parameters and - yield arguments. - -[+:include:+ _filename_] - Include the contents of the named file at this point. The file will be - searched for in the directories listed by the +--include+ option, or in the - current directory by default. The contents of the file will be shifted to - have the same indentation as the ':' at the start of the :include: directive. - -[+:title:+ _text_] - Sets the title for the document. Equivalent to the --title command line - parameter. (The command line parameter overrides any :title: directive in - the source). - -[+:enddoc:+] - Document nothing further at the current level. - -[+:main:+ _name_] - Equivalent to the --main command line parameter. - -[+:stopdoc:+ / +:startdoc:+] - Stop and start adding new documentation elements to the current container. - For example, if a class has a number of constants that you don't want to - document, put a +:stopdoc:+ before the first, and a +:startdoc:+ after the - last. If you don't specify a +:startdoc:+ by the end of the container, - disables documentation for the entire class or module. - -= Other stuff - -Author:: Dave Thomas <dave@pragmaticprogrammer.com> - -== Credits - -* The Ruby parser in rdoc/parse.rb is based heavily on the outstanding - work of Keiju ISHITSUKA of Nippon Rational Inc, who produced the Ruby - parser for irb and the rtags package. - -* Code to diagram classes and modules was written by Sergey A Yanovitsky - (Jah) of Enticla. - -* Charset patch from MoonWolf. - -* Rich Kilmer wrote the kilmer.rb output template. - -* Dan Brickley led the design of the RDF format. - -== License - -RDoc is Copyright (c) 2001-2003 Dave Thomas, The Pragmatic Programmers. It -is free software, and may be redistributed under the terms specified -in the README file of the Ruby distribution. - -== Warranty - -This software is provided "as is" and without any express or implied -warranties, including, without limitation, the implied warranties of -merchantibility and fitness for a particular purpose. - diff --git a/trunk/lib/rdoc/code_objects.rb b/trunk/lib/rdoc/code_objects.rb deleted file mode 100644 index fbdb612b92..0000000000 --- a/trunk/lib/rdoc/code_objects.rb +++ /dev/null @@ -1,995 +0,0 @@ -# We represent the various high-level code constructs that appear -# in Ruby programs: classes, modules, methods, and so on. - -require 'rdoc/tokenstream' - -module RDoc - - ## - # We contain the common stuff for contexts (which are containers) and other - # elements (methods, attributes and so on) - - class CodeObject - - attr_accessor :parent - - # We are the model of the code, but we know that at some point - # we will be worked on by viewers. By implementing the Viewable - # protocol, viewers can associated themselves with these objects. - - attr_accessor :viewer - - # are we done documenting (ie, did we come across a :enddoc:)? - - attr_accessor :done_documenting - - # Which section are we in - - attr_accessor :section - - # do we document ourselves? - - attr_reader :document_self - - def initialize - @document_self = true - @document_children = true - @force_documentation = false - @done_documenting = false - end - - def document_self=(val) - @document_self = val - if !val - remove_methods_etc - end - end - - # set and cleared by :startdoc: and :enddoc:, this is used to toggle - # the capturing of documentation - def start_doc - @document_self = true - @document_children = true - end - - def stop_doc - @document_self = false - @document_children = false - end - - # do we document ourselves and our children - - attr_reader :document_children - - def document_children=(val) - @document_children = val - if !val - remove_classes_and_modules - end - end - - # Do we _force_ documentation, even is we wouldn't normally show the entity - attr_accessor :force_documentation - - def parent_file_name - @parent ? @parent.file_base_name : '(unknown)' - end - - def parent_name - @parent ? @parent.name : '(unknown)' - end - - # Default callbacks to nothing, but this is overridden for classes - # and modules - def remove_classes_and_modules - end - - def remove_methods_etc - end - - # Access the code object's comment - attr_reader :comment - - # Update the comment, but don't overwrite a real comment with an empty one - def comment=(comment) - @comment = comment unless comment.empty? - end - - # There's a wee trick we pull. Comment blocks can have directives that - # override the stuff we extract during the parse. So, we have a special - # class method, attr_overridable, that lets code objects list - # those directives. Wehn a comment is assigned, we then extract - # out any matching directives and update our object - - def self.attr_overridable(name, *aliases) - @overridables ||= {} - - attr_accessor name - - aliases.unshift name - aliases.each do |directive_name| - @overridables[directive_name.to_s] = name - end - end - - end - - ## - # A Context is something that can hold modules, classes, methods, - # attributes, aliases, requires, and includes. Classes, modules, and files - # are all Contexts. - - class Context < CodeObject - - attr_reader :aliases - attr_reader :attributes - attr_reader :constants - attr_reader :current_section - attr_reader :in_files - attr_reader :includes - attr_reader :method_list - attr_reader :name - attr_reader :requires - attr_reader :sections - attr_reader :visibility - - class Section - attr_reader :title, :comment, :sequence - - @@sequence = "SEC00000" - - def initialize(title, comment) - @title = title - @@sequence.succ! - @sequence = @@sequence.dup - @comment = nil - set_comment(comment) - end - - def ==(other) - self.class === other and @sequence == other.sequence - end - - def inspect - "#<%s:0x%x %s %p>" % [ - self.class, object_id, - @sequence, title - ] - end - - ## - # Set the comment for this section from the original comment block If - # the first line contains :section:, strip it and use the rest. - # Otherwise remove lines up to the line containing :section:, and look - # for those lines again at the end and remove them. This lets us write - # - # # --------------------- - # # :SECTION: The title - # # The body - # # --------------------- - - def set_comment(comment) - return unless comment - - if comment =~ /^#[ \t]*:section:.*\n/ - start = $` - rest = $' - - if start.empty? - @comment = rest - else - @comment = rest.sub(/#{start.chomp}\Z/, '') - end - else - @comment = comment - end - @comment = nil if @comment.empty? - end - - end - - def initialize - super - - @in_files = [] - - @name ||= "unknown" - @comment ||= "" - @parent = nil - @visibility = :public - - @current_section = Section.new(nil, nil) - @sections = [ @current_section ] - - initialize_methods_etc - initialize_classes_and_modules - end - - ## - # map the class hash to an array externally - - def classes - @classes.values - end - - ## - # map the module hash to an array externally - - def modules - @modules.values - end - - ## - # Change the default visibility for new methods - - def ongoing_visibility=(vis) - @visibility = vis - end - - ## - # Yields Method and Attr entries matching the list of names in +methods+. - # Attributes are only returned when +singleton+ is false. - - def methods_matching(methods, singleton = false) - count = 0 - - @method_list.each do |m| - if methods.include? m.name and m.singleton == singleton then - yield m - count += 1 - end - end - - return if count == methods.size || singleton - - # perhaps we need to look at attributes - - @attributes.each do |a| - yield a if methods.include? a.name - end - end - - ## - # Given an array +methods+ of method names, set the visibility of the - # corresponding AnyMethod object - - def set_visibility_for(methods, vis, singleton = false) - methods_matching methods, singleton do |m| - m.visibility = vis - end - end - - ## - # Record the file that we happen to find it in - - def record_location(toplevel) - @in_files << toplevel unless @in_files.include?(toplevel) - end - - # Return true if at least part of this thing was defined in +file+ - def defined_in?(file) - @in_files.include?(file) - end - - def add_class(class_type, name, superclass) - add_class_or_module @classes, class_type, name, superclass - end - - def add_module(class_type, name) - add_class_or_module(@modules, class_type, name, nil) - end - - def add_method(a_method) - a_method.visibility = @visibility - add_to(@method_list, a_method) - end - - def add_attribute(an_attribute) - add_to(@attributes, an_attribute) - end - - def add_alias(an_alias) - meth = find_instance_method_named(an_alias.old_name) - - if meth then - new_meth = AnyMethod.new(an_alias.text, an_alias.new_name) - new_meth.is_alias_for = meth - new_meth.singleton = meth.singleton - new_meth.params = meth.params - new_meth.comment = "Alias for \##{meth.name}" - meth.add_alias(new_meth) - add_method(new_meth) - else - add_to(@aliases, an_alias) - end - - an_alias - end - - def add_include(an_include) - add_to(@includes, an_include) - end - - def add_constant(const) - add_to(@constants, const) - end - - # Requires always get added to the top-level (file) context - def add_require(a_require) - if TopLevel === self then - add_to @requires, a_require - else - parent.add_require a_require - end - end - - def add_class_or_module(collection, class_type, name, superclass=nil) - cls = collection[name] - - if cls then - cls.superclass = superclass unless cls.module? - puts "Reusing class/module #{name}" if $DEBUG_RDOC - else - cls = class_type.new(name, superclass) -# collection[name] = cls if @document_self && !@done_documenting - collection[name] = cls if !@done_documenting - cls.parent = self - cls.section = @current_section - end - cls - end - - def add_to(array, thing) - array << thing if @document_self and not @done_documenting - thing.parent = self - thing.section = @current_section - end - - # If a class's documentation is turned off after we've started - # collecting methods etc., we need to remove the ones - # we have - - def remove_methods_etc - initialize_methods_etc - end - - def initialize_methods_etc - @method_list = [] - @attributes = [] - @aliases = [] - @requires = [] - @includes = [] - @constants = [] - end - - # and remove classes and modules when we see a :nodoc: all - def remove_classes_and_modules - initialize_classes_and_modules - end - - def initialize_classes_and_modules - @classes = {} - @modules = {} - end - - # Find a named module - def find_module_named(name) - return self if self.name == name - res = @modules[name] || @classes[name] - return res if res - find_enclosing_module_named(name) - end - - # find a module at a higher scope - def find_enclosing_module_named(name) - parent && parent.find_module_named(name) - end - - # Iterate over all the classes and modules in - # this object - - def each_classmodule - @modules.each_value {|m| yield m} - @classes.each_value {|c| yield c} - end - - def each_method - @method_list.each {|m| yield m} - end - - def each_attribute - @attributes.each {|a| yield a} - end - - def each_constant - @constants.each {|c| yield c} - end - - # Return the toplevel that owns us - - def toplevel - return @toplevel if defined? @toplevel - @toplevel = self - @toplevel = @toplevel.parent until TopLevel === @toplevel - @toplevel - end - - # allow us to sort modules by name - def <=>(other) - name <=> other.name - end - - ## - # Look up +symbol+. If +method+ is non-nil, then we assume the symbol - # references a module that contains that method. - - def find_symbol(symbol, method = nil) - result = nil - - case symbol - when /^::(.*)/ then - result = toplevel.find_symbol($1) - when /::/ then - modules = symbol.split(/::/) - - unless modules.empty? then - module_name = modules.shift - result = find_module_named(module_name) - if result then - modules.each do |name| - result = result.find_module_named(name) - break unless result - end - end - end - - else - # if a method is specified, then we're definitely looking for - # a module, otherwise it could be any symbol - if method - result = find_module_named(symbol) - else - result = find_local_symbol(symbol) - if result.nil? - if symbol =~ /^[A-Z]/ - result = parent - while result && result.name != symbol - result = result.parent - end - end - end - end - end - - if result and method then - fail unless result.respond_to? :find_local_symbol - result = result.find_local_symbol(method) - end - - result - end - - def find_local_symbol(symbol) - res = find_method_named(symbol) || - find_constant_named(symbol) || - find_attribute_named(symbol) || - find_module_named(symbol) || - find_file_named(symbol) - end - - # Handle sections - - def set_current_section(title, comment) - @current_section = Section.new(title, comment) - @sections << @current_section - end - - private - - # Find a named method, or return nil - def find_method_named(name) - @method_list.find {|meth| meth.name == name} - end - - # Find a named instance method, or return nil - def find_instance_method_named(name) - @method_list.find {|meth| meth.name == name && !meth.singleton} - end - - # Find a named constant, or return nil - def find_constant_named(name) - @constants.find {|m| m.name == name} - end - - # Find a named attribute, or return nil - def find_attribute_named(name) - @attributes.find {|m| m.name == name} - end - - ## - # Find a named file, or return nil - - def find_file_named(name) - toplevel.class.find_file_named(name) - end - - end - - ## - # A TopLevel context is a source file - - class TopLevel < Context - attr_accessor :file_stat - attr_accessor :file_relative_name - attr_accessor :file_absolute_name - attr_accessor :diagram - - @@all_classes = {} - @@all_modules = {} - @@all_files = {} - - def self.reset - @@all_classes = {} - @@all_modules = {} - @@all_files = {} - end - - def initialize(file_name) - super() - @name = "TopLevel" - @file_relative_name = file_name - @file_absolute_name = file_name - @file_stat = File.stat(file_name) - @diagram = nil - @@all_files[file_name] = self - end - - def file_base_name - File.basename @file_absolute_name - end - - def full_name - nil - end - - ## - # Adding a class or module to a TopLevel is special, as we only want one - # copy of a particular top-level class. For example, if both file A and - # file B implement class C, we only want one ClassModule object for C. - # This code arranges to share classes and modules between files. - - def add_class_or_module(collection, class_type, name, superclass) - cls = collection[name] - - if cls then - cls.superclass = superclass unless cls.module? - puts "Reusing class/module #{cls.full_name}" if $DEBUG_RDOC - else - if class_type == NormalModule then - all = @@all_modules - else - all = @@all_classes - end - - cls = all[name] - - unless cls then - cls = class_type.new name, superclass - all[name] = cls unless @done_documenting - end - - collection[name] = cls unless @done_documenting - - cls.parent = self - end - - cls - end - - def self.all_classes_and_modules - @@all_classes.values + @@all_modules.values - end - - def self.find_class_named(name) - @@all_classes.each_value do |c| - res = c.find_class_named(name) - return res if res - end - nil - end - - def self.find_file_named(name) - @@all_files[name] - end - - def find_local_symbol(symbol) - find_class_or_module_named(symbol) || super - end - - def find_class_or_module_named(symbol) - @@all_classes.each_value {|c| return c if c.name == symbol} - @@all_modules.each_value {|m| return m if m.name == symbol} - nil - end - - ## - # Find a named module - - def find_module_named(name) - find_class_or_module_named(name) || find_enclosing_module_named(name) - end - - def inspect - "#<%s:0x%x %p modules: %p classes: %p>" % [ - self.class, object_id, - file_base_name, - @modules.map { |n,m| m }, - @classes.map { |n,c| c } - ] - end - - end - - ## - # ClassModule is the base class for objects representing either a class or a - # module. - - class ClassModule < Context - - attr_accessor :diagram - - def initialize(name, superclass = nil) - @name = name - @diagram = nil - @superclass = superclass - @comment = "" - super() - end - - def find_class_named(name) - return self if full_name == name - @classes.each_value {|c| return c if c.find_class_named(name) } - nil - end - - ## - # Return the fully qualified name of this class or module - - def full_name - if @parent && @parent.full_name - @parent.full_name + "::" + @name - else - @name - end - end - - def http_url(prefix) - path = full_name.split("::") - File.join(prefix, *path) + ".html" - end - - ## - # Does this object represent a module? - - def module? - false - end - - ## - # Get the superclass of this class. Attempts to retrieve the superclass' - # real name by following module nesting. - - def superclass - raise NoMethodError, "#{full_name} is a module" if module? - - scope = self - - begin - superclass = scope.classes.find { |c| c.name == @superclass } - - return superclass.full_name if superclass - scope = scope.parent - end until scope.nil? or TopLevel === scope - - @superclass - end - - ## - # Set the superclass of this class - - def superclass=(superclass) - raise NoMethodError, "#{full_name} is a module" if module? - - if @superclass.nil? or @superclass == 'Object' then - @superclass = superclass - end - end - - def to_s - "#{self.class}: #{@name} #{@comment} #{super}" - end - - end - - ## - # Anonymous classes - - class AnonClass < ClassModule - end - - ## - # Normal classes - - class NormalClass < ClassModule - - def inspect - superclass = @superclass ? " < #{@superclass}" : nil - "<%s:0x%x class %s%s includes: %p attributes: %p methods: %p aliases: %p>" % [ - self.class, object_id, - @name, superclass, @includes, @attributes, @method_list, @aliases - ] - end - - end - - ## - # Singleton classes - - class SingleClass < ClassModule - end - - ## - # Module - - class NormalModule < ClassModule - - def comment=(comment) - return if comment.empty? - comment = @comment << "# ---\n" << comment unless @comment.empty? - - super - end - - def inspect - "#<%s:0x%x module %s includes: %p attributes: %p methods: %p aliases: %p>" % [ - self.class, object_id, - @name, @includes, @attributes, @method_list, @aliases - ] - end - - def module? - true - end - - end - - ## - # AnyMethod is the base class for objects representing methods - - class AnyMethod < CodeObject - - attr_accessor :name - attr_accessor :visibility - attr_accessor :block_params - attr_accessor :dont_rename_initialize - attr_accessor :singleton - attr_reader :text - - # list of other names for this method - attr_reader :aliases - - # method we're aliasing - attr_accessor :is_alias_for - - attr_overridable :params, :param, :parameters, :parameter - - attr_accessor :call_seq - - include TokenStream - - def initialize(text, name) - super() - @text = text - @name = name - @token_stream = nil - @visibility = :public - @dont_rename_initialize = false - @block_params = nil - @aliases = [] - @is_alias_for = nil - @comment = "" - @call_seq = nil - end - - def <=>(other) - @name <=> other.name - end - - def add_alias(method) - @aliases << method - end - - def inspect - alias_for = @is_alias_for ? " (alias for #{@is_alias_for.name})" : nil - "#<%s:0x%x %s%s%s (%s)%s>" % [ - self.class, object_id, - parent_name, - singleton ? '::' : '#', - name, - visibility, - alias_for, - ] - end - - def param_seq - params = params.gsub(/\s*\#.*/, '') - params = params.tr("\n", " ").squeeze(" ") - params = "(#{params})" unless p[0] == ?( - - if block = block_params then # yes, = - # If this method has explicit block parameters, remove any explicit - # &block - params.sub!(/,?\s*&\w+/) - - block.gsub!(/\s*\#.*/, '') - block = block.tr("\n", " ").squeeze(" ") - if block[0] == ?( - block.sub!(/^\(/, '').sub!(/\)/, '') - end - params << " { |#{block}| ... }" - end - - params - end - - def to_s - res = self.class.name + ": " + @name + " (" + @text + ")\n" - res << @comment.to_s - res - end - - end - - ## - # GhostMethod represents a method referenced only by a comment - - class GhostMethod < AnyMethod - end - - ## - # MetaMethod represents a meta-programmed method - - class MetaMethod < AnyMethod - end - - ## - # Represent an alias, which is an old_name/ new_name pair associated with a - # particular context - - class Alias < CodeObject - - attr_accessor :text, :old_name, :new_name, :comment - - def initialize(text, old_name, new_name, comment) - super() - @text = text - @old_name = old_name - @new_name = new_name - self.comment = comment - end - - def inspect - "#<%s:0x%x %s.alias_method %s, %s>" % [ - self.class, object_id, - parent.name, @old_name, @new_name, - ] - end - - def to_s - "alias: #{self.old_name} -> #{self.new_name}\n#{self.comment}" - end - - end - - ## - # Represent a constant - - class Constant < CodeObject - attr_accessor :name, :value - - def initialize(name, value, comment) - super() - @name = name - @value = value - self.comment = comment - end - end - - ## - # Represent attributes - - class Attr < CodeObject - attr_accessor :text, :name, :rw, :visibility - - def initialize(text, name, rw, comment) - super() - @text = text - @name = name - @rw = rw - @visibility = :public - self.comment = comment - end - - def <=>(other) - self.name <=> other.name - end - - def inspect - attr = case rw - when 'RW' then :attr_accessor - when 'R' then :attr_reader - when 'W' then :attr_writer - else - " (#{rw})" - end - - "#<%s:0x%x %s.%s :%s>" % [ - self.class, object_id, - parent_name, attr, @name, - ] - end - - def to_s - "attr: #{self.name} #{self.rw}\n#{self.comment}" - end - - end - - ## - # A required file - - class Require < CodeObject - attr_accessor :name - - def initialize(name, comment) - super() - @name = name.gsub(/'|"/, "") #' - self.comment = comment - end - - def inspect - "#<%s:0x%x require '%s' in %s>" % [ - self.class, - object_id, - @name, - parent_file_name, - ] - end - - end - - ## - # An included module - - class Include < CodeObject - - attr_accessor :name - - def initialize(name, comment) - super() - @name = name - self.comment = comment - - end - - def inspect - "#<%s:0x%x %s.include %s>" % [ - self.class, - object_id, - parent_name, @name, - ] - end - - end - -end diff --git a/trunk/lib/rdoc/diagram.rb b/trunk/lib/rdoc/diagram.rb deleted file mode 100644 index e235e043dc..0000000000 --- a/trunk/lib/rdoc/diagram.rb +++ /dev/null @@ -1,338 +0,0 @@ -# A wonderful hack by to draw package diagrams using the dot package. -# Originally written by Jah, team Enticla. -# -# You must have the V1.7 or later in your path -# http://www.research.att.com/sw/tools/graphviz/ - -require 'rdoc/dot' - -module RDoc - - ## - # Draw a set of diagrams representing the modules and classes in the - # system. We draw one diagram for each file, and one for each toplevel - # class or module. This means there will be overlap. However, it also - # means that you'll get better context for objects. - # - # To use, simply - # - # d = Diagram.new(info) # pass in collection of top level infos - # d.draw - # - # The results will be written to the +dot+ subdirectory. The process - # also sets the +diagram+ attribute in each object it graphs to - # the name of the file containing the image. This can be used - # by output generators to insert images. - - class Diagram - - FONT = "Arial" - - DOT_PATH = "dot" - - ## - # Pass in the set of top level objects. The method also creates the - # subdirectory to hold the images - - def initialize(info, options) - @info = info - @options = options - @counter = 0 - FileUtils.mkdir_p(DOT_PATH) - @diagram_cache = {} - end - - ## - # Draw the diagrams. We traverse the files, drawing a diagram for each. We - # also traverse each top-level class and module in that file drawing a - # diagram for these too. - - def draw - unless @options.quiet - $stderr.print "Diagrams: " - $stderr.flush - end - - @info.each_with_index do |i, file_count| - @done_modules = {} - @local_names = find_names(i) - @global_names = [] - @global_graph = graph = DOT::Digraph.new('name' => 'TopLevel', - 'fontname' => FONT, - 'fontsize' => '8', - 'bgcolor' => 'lightcyan1', - 'compound' => 'true') - - # it's a little hack %) i'm too lazy to create a separate class - # for default node - graph << DOT::Node.new('name' => 'node', - 'fontname' => FONT, - 'color' => 'black', - 'fontsize' => 8) - - i.modules.each do |mod| - draw_module(mod, graph, true, i.file_relative_name) - end - add_classes(i, graph, i.file_relative_name) - - i.diagram = convert_to_png("f_#{file_count}", graph) - - # now go through and document each top level class and - # module independently - i.modules.each_with_index do |mod, count| - @done_modules = {} - @local_names = find_names(mod) - @global_names = [] - - @global_graph = graph = DOT::Digraph.new('name' => 'TopLevel', - 'fontname' => FONT, - 'fontsize' => '8', - 'bgcolor' => 'lightcyan1', - 'compound' => 'true') - - graph << DOT::Node.new('name' => 'node', - 'fontname' => FONT, - 'color' => 'black', - 'fontsize' => 8) - draw_module(mod, graph, true) - mod.diagram = convert_to_png("m_#{file_count}_#{count}", - graph) - end - end - $stderr.puts unless @options.quiet - end - - private - - def find_names(mod) - return [mod.full_name] + mod.classes.collect{|cl| cl.full_name} + - mod.modules.collect{|m| find_names(m)}.flatten - end - - def find_full_name(name, mod) - full_name = name.dup - return full_name if @local_names.include?(full_name) - mod_path = mod.full_name.split('::')[0..-2] - unless mod_path.nil? - until mod_path.empty? - full_name = mod_path.pop + '::' + full_name - return full_name if @local_names.include?(full_name) - end - end - return name - end - - def draw_module(mod, graph, toplevel = false, file = nil) - return if @done_modules[mod.full_name] and not toplevel - - @counter += 1 - url = mod.http_url("classes") - m = DOT::Subgraph.new('name' => "cluster_#{mod.full_name.gsub( /:/,'_' )}", - 'label' => mod.name, - 'fontname' => FONT, - 'color' => 'blue', - 'style' => 'filled', - 'URL' => %{"#{url}"}, - 'fillcolor' => toplevel ? 'palegreen1' : 'palegreen3') - - @done_modules[mod.full_name] = m - add_classes(mod, m, file) - graph << m - - unless mod.includes.empty? - mod.includes.each do |inc| - m_full_name = find_full_name(inc.name, mod) - if @local_names.include?(m_full_name) - @global_graph << DOT::Edge.new('from' => "#{m_full_name.gsub( /:/,'_' )}", - 'to' => "#{mod.full_name.gsub( /:/,'_' )}", - 'ltail' => "cluster_#{m_full_name.gsub( /:/,'_' )}", - 'lhead' => "cluster_#{mod.full_name.gsub( /:/,'_' )}") - else - unless @global_names.include?(m_full_name) - path = m_full_name.split("::") - url = File.join('classes', *path) + ".html" - @global_graph << DOT::Node.new('name' => "#{m_full_name.gsub( /:/,'_' )}", - 'shape' => 'box', - 'label' => "#{m_full_name}", - 'URL' => %{"#{url}"}) - @global_names << m_full_name - end - @global_graph << DOT::Edge.new('from' => "#{m_full_name.gsub( /:/,'_' )}", - 'to' => "#{mod.full_name.gsub( /:/,'_' )}", - 'lhead' => "cluster_#{mod.full_name.gsub( /:/,'_' )}") - end - end - end - end - - def add_classes(container, graph, file = nil ) - - use_fileboxes = @options.fileboxes - - files = {} - - # create dummy node (needed if empty and for module includes) - if container.full_name - graph << DOT::Node.new('name' => "#{container.full_name.gsub( /:/,'_' )}", - 'label' => "", - 'width' => (container.classes.empty? and - container.modules.empty?) ? - '0.75' : '0.01', - 'height' => '0.01', - 'shape' => 'plaintext') - end - - container.classes.each_with_index do |cl, cl_index| - last_file = cl.in_files[-1].file_relative_name - - if use_fileboxes && !files.include?(last_file) - @counter += 1 - files[last_file] = - DOT::Subgraph.new('name' => "cluster_#{@counter}", - 'label' => "#{last_file}", - 'fontname' => FONT, - 'color'=> - last_file == file ? 'red' : 'black') - end - - next if cl.name == 'Object' || cl.name[0,2] == "<<" - - url = cl.http_url("classes") - - label = cl.name.dup - if use_fileboxes && cl.in_files.length > 1 - label << '\n[' + - cl.in_files.collect {|i| - i.file_relative_name - }.sort.join( '\n' ) + - ']' - end - - attrs = { - 'name' => "#{cl.full_name.gsub( /:/, '_' )}", - 'fontcolor' => 'black', - 'style'=>'filled', - 'color'=>'palegoldenrod', - 'label' => label, - 'shape' => 'ellipse', - 'URL' => %{"#{url}"} - } - - c = DOT::Node.new(attrs) - - if use_fileboxes - files[last_file].push c - else - graph << c - end - end - - if use_fileboxes - files.each_value do |val| - graph << val - end - end - - unless container.classes.empty? - container.classes.each_with_index do |cl, cl_index| - cl.includes.each do |m| - m_full_name = find_full_name(m.name, cl) - if @local_names.include?(m_full_name) - @global_graph << DOT::Edge.new('from' => "#{m_full_name.gsub( /:/,'_' )}", - 'to' => "#{cl.full_name.gsub( /:/,'_' )}", - 'ltail' => "cluster_#{m_full_name.gsub( /:/,'_' )}") - else - unless @global_names.include?(m_full_name) - path = m_full_name.split("::") - url = File.join('classes', *path) + ".html" - @global_graph << DOT::Node.new('name' => "#{m_full_name.gsub( /:/,'_' )}", - 'shape' => 'box', - 'label' => "#{m_full_name}", - 'URL' => %{"#{url}"}) - @global_names << m_full_name - end - @global_graph << DOT::Edge.new('from' => "#{m_full_name.gsub( /:/,'_' )}", - 'to' => "#{cl.full_name.gsub( /:/, '_')}") - end - end - - sclass = cl.superclass - next if sclass.nil? || sclass == 'Object' - sclass_full_name = find_full_name(sclass,cl) - unless @local_names.include?(sclass_full_name) or @global_names.include?(sclass_full_name) - path = sclass_full_name.split("::") - url = File.join('classes', *path) + ".html" - @global_graph << DOT::Node.new('name' => "#{sclass_full_name.gsub( /:/, '_' )}", - 'label' => sclass_full_name, - 'URL' => %{"#{url}"}) - @global_names << sclass_full_name - end - @global_graph << DOT::Edge.new('from' => "#{sclass_full_name.gsub( /:/,'_' )}", - 'to' => "#{cl.full_name.gsub( /:/, '_')}") - end - end - - container.modules.each do |submod| - draw_module(submod, graph) - end - - end - - def convert_to_png(file_base, graph) - str = graph.to_s - return @diagram_cache[str] if @diagram_cache[str] - op_type = @options.image_format - dotfile = File.join(DOT_PATH, file_base) - src = dotfile + ".dot" - dot = dotfile + "." + op_type - - unless @options.quiet - $stderr.print "." - $stderr.flush - end - - File.open(src, 'w+' ) do |f| - f << str << "\n" - end - - system "dot", "-T#{op_type}", src, "-o", dot - - # Now construct the imagemap wrapper around - # that png - - ret = wrap_in_image_map(src, dot) - @diagram_cache[str] = ret - return ret - end - - ## - # Extract the client-side image map from dot, and use it to generate the - # imagemap proper. Return the whole <map>..<img> combination, suitable for - # inclusion on the page - - def wrap_in_image_map(src, dot) - res = %{<map id="map" name="map">\n} - dot_map = `dot -Tismap #{src}` - dot_map.split($/).each do |area| - unless area =~ /^rectangle \((\d+),(\d+)\) \((\d+),(\d+)\) ([\/\w.]+)\s*(.*)/ - $stderr.puts "Unexpected output from dot:\n#{area}" - return nil - end - - xs, ys = [$1.to_i, $3.to_i], [$2.to_i, $4.to_i] - url, area_name = $5, $6 - - res << %{ <area shape="rect" coords="#{xs.min},#{ys.min},#{xs.max},#{ys.max}" } - res << %{ href="#{url}" alt="#{area_name}" />\n} - end - res << "</map>\n" -# map_file = src.sub(/.dot/, '.map') -# system("dot -Timap #{src} -o #{map_file}") - res << %{<img src="#{dot}" usemap="#map" border="0" alt="#{dot}">} - return res - end - - end - -end - diff --git a/trunk/lib/rdoc/dot.rb b/trunk/lib/rdoc/dot.rb deleted file mode 100644 index fbd2cfba02..0000000000 --- a/trunk/lib/rdoc/dot.rb +++ /dev/null @@ -1,249 +0,0 @@ -module RDoc; end - -module RDoc::DOT - - TAB = ' ' - TAB2 = TAB * 2 - - # options for node declaration - NODE_OPTS = [ - 'bgcolor', - 'color', - 'fontcolor', - 'fontname', - 'fontsize', - 'height', - 'width', - 'label', - 'layer', - 'rank', - 'shape', - 'shapefile', - 'style', - 'URL', - ] - - # options for edge declaration - EDGE_OPTS = [ - 'color', - 'decorate', - 'dir', - 'fontcolor', - 'fontname', - 'fontsize', - 'id', - 'label', - 'layer', - 'lhead', - 'ltail', - 'minlen', - 'style', - 'weight' - ] - - # options for graph declaration - GRAPH_OPTS = [ - 'bgcolor', - 'center', - 'clusterrank', - 'color', - 'compound', - 'concentrate', - 'fillcolor', - 'fontcolor', - 'fontname', - 'fontsize', - 'label', - 'layerseq', - 'margin', - 'mclimit', - 'nodesep', - 'nslimit', - 'ordering', - 'orientation', - 'page', - 'rank', - 'rankdir', - 'ranksep', - 'ratio', - 'size', - 'style', - 'URL' - ] - - # a root class for any element in dot notation - class SimpleElement - attr_accessor :name - - def initialize( params = {} ) - @label = params['name'] ? params['name'] : '' - end - - def to_s - @name - end - end - - # an element that has options ( node, edge or graph ) - class Element < SimpleElement - #attr_reader :parent - attr_accessor :name, :options - - def initialize( params = {}, option_list = [] ) - super( params ) - @name = params['name'] ? params['name'] : nil - @parent = params['parent'] ? params['parent'] : nil - @options = {} - option_list.each{ |i| - @options[i] = params[i] if params[i] - } - @options['label'] ||= @name if @name != 'node' - end - - def each_option - @options.each{ |i| yield i } - end - - def each_option_pair - @options.each_pair{ |key, val| yield key, val } - end - - #def parent=( thing ) - # @parent.delete( self ) if defined?( @parent ) and @parent - # @parent = thing - #end - end - - - # this is used when we build nodes that have shape=record - # ports don't have options :) - class Port < SimpleElement - attr_accessor :label - - def initialize( params = {} ) - super( params ) - @name = params['label'] ? params['label'] : '' - end - def to_s - ( @name && @name != "" ? "<#{@name}>" : "" ) + "#{@label}" - end - end - - # node element - class Node < Element - - def initialize( params = {}, option_list = NODE_OPTS ) - super( params, option_list ) - @ports = params['ports'] ? params['ports'] : [] - end - - def each_port - @ports.each{ |i| yield i } - end - - def << ( thing ) - @ports << thing - end - - def push ( thing ) - @ports.push( thing ) - end - - def pop - @ports.pop - end - - def to_s( t = '' ) - - label = @options['shape'] != 'record' && @ports.length == 0 ? - @options['label'] ? - t + TAB + "label = \"#{@options['label']}\"\n" : - '' : - t + TAB + 'label = "' + " \\\n" + - t + TAB2 + "#{@options['label']}| \\\n" + - @ports.collect{ |i| - t + TAB2 + i.to_s - }.join( "| \\\n" ) + " \\\n" + - t + TAB + '"' + "\n" - - t + "#{@name} [\n" + - @options.to_a.collect{ |i| - i[1] && i[0] != 'label' ? - t + TAB + "#{i[0]} = #{i[1]}" : nil - }.compact.join( ",\n" ) + ( label != '' ? ",\n" : "\n" ) + - label + - t + "]\n" - end - end - - # subgraph element is the same to graph, but has another header in dot - # notation - class Subgraph < Element - - def initialize( params = {}, option_list = GRAPH_OPTS ) - super( params, option_list ) - @nodes = params['nodes'] ? params['nodes'] : [] - @dot_string = 'subgraph' - end - - def each_node - @nodes.each{ |i| yield i } - end - - def << ( thing ) - @nodes << thing - end - - def push( thing ) - @nodes.push( thing ) - end - - def pop - @nodes.pop - end - - def to_s( t = '' ) - hdr = t + "#{@dot_string} #{@name} {\n" - - options = @options.to_a.collect{ |name, val| - val && name != 'label' ? - t + TAB + "#{name} = #{val}" : - name ? t + TAB + "#{name} = \"#{val}\"" : nil - }.compact.join( "\n" ) + "\n" - - nodes = @nodes.collect{ |i| - i.to_s( t + TAB ) - }.join( "\n" ) + "\n" - hdr + options + nodes + t + "}\n" - end - end - - # this is graph - class Digraph < Subgraph - def initialize( params = {}, option_list = GRAPH_OPTS ) - super( params, option_list ) - @dot_string = 'digraph' - end - end - - # this is edge - class Edge < Element - attr_accessor :from, :to - def initialize( params = {}, option_list = EDGE_OPTS ) - super( params, option_list ) - @from = params['from'] ? params['from'] : nil - @to = params['to'] ? params['to'] : nil - end - - def to_s( t = '' ) - t + "#{@from} -> #{to} [\n" + - @options.to_a.collect{ |i| - i[1] && i[0] != 'label' ? - t + TAB + "#{i[0]} = #{i[1]}" : - i[1] ? t + TAB + "#{i[0]} = \"#{i[1]}\"" : nil - }.compact.join( "\n" ) + "\n" + t + "]\n" - end - end - -end - diff --git a/trunk/lib/rdoc/generator.rb b/trunk/lib/rdoc/generator.rb deleted file mode 100644 index fbc08c4e20..0000000000 --- a/trunk/lib/rdoc/generator.rb +++ /dev/null @@ -1,1076 +0,0 @@ -require 'cgi' -require 'rdoc' -require 'rdoc/options' -require 'rdoc/markup/to_html_crossref' -require 'rdoc/template' - -module RDoc::Generator - - ## - # Name of sub-directory that holds file descriptions - - FILE_DIR = "files" - - ## - # Name of sub-directory that holds class descriptions - - CLASS_DIR = "classes" - - ## - # Name of the RDoc CSS file - - CSS_NAME = "rdoc-style.css" - - ## - # Build a hash of all items that can be cross-referenced. This is used when - # we output required and included names: if the names appear in this hash, - # we can generate an html cross reference to the appropriate description. - # We also use this when parsing comment blocks: any decorated words matching - # an entry in this list are hyperlinked. - - class AllReferences - @@refs = {} - - def AllReferences::reset - @@refs = {} - end - - def AllReferences.add(name, html_class) - @@refs[name] = html_class - end - - def AllReferences.[](name) - @@refs[name] - end - - def AllReferences.keys - @@refs.keys - end - end - - ## - # Handle common markup tasks for the various Context subclasses - - module MarkUp - - ## - # Convert a string in markup format into HTML. - - def markup(str, remove_para = false) - return '' unless str - - # Convert leading comment markers to spaces, but only if all non-blank - # lines have them - if str =~ /^(?>\s*)[^\#]/ then - content = str - else - content = str.gsub(/^\s*(#+)/) { $1.tr '#', ' ' } - end - - res = formatter.convert content - - if remove_para then - res.sub!(/^<p>/, '') - res.sub!(/<\/p>$/, '') - end - - res - end - - ## - # Qualify a stylesheet URL; if if +css_name+ does not begin with '/' or - # 'http[s]://', prepend a prefix relative to +path+. Otherwise, return it - # unmodified. - - def style_url(path, css_name=nil) -# $stderr.puts "style_url( #{path.inspect}, #{css_name.inspect} )" - css_name ||= CSS_NAME - if %r{^(https?:/)?/} =~ css_name - css_name - else - RDoc::Markup::ToHtml.gen_relative_url path, css_name - end - end - - ## - # Build a webcvs URL with the given 'url' argument. URLs with a '%s' in them - # get the file's path sprintfed into them; otherwise they're just catenated - # together. - - def cvs_url(url, full_path) - if /%s/ =~ url - return sprintf( url, full_path ) - else - return url + full_path - end - end - - end - - ## - # A Context is built by the parser to represent a container: contexts hold - # classes, modules, methods, require lists and include lists. ClassModule - # and TopLevel are the context objects we process here - - class Context - - include MarkUp - - attr_reader :context - - ## - # Generate: - # - # * a list of RDoc::Generator::File objects for each TopLevel object - # * a list of RDoc::Generator::Class objects for each first level class or - # module in the TopLevel objects - # * a complete list of all hyperlinkable terms (file, class, module, and - # method names) - - def self.build_indicies(toplevels, options) - files = [] - classes = [] - - toplevels.each do |toplevel| - files << RDoc::Generator::File.new(toplevel, options, - RDoc::Generator::FILE_DIR) - end - - RDoc::TopLevel.all_classes_and_modules.each do |cls| - build_class_list(classes, options, cls, files[0], - RDoc::Generator::CLASS_DIR) - end - - return files, classes - end - - def self.build_class_list(classes, options, from, html_file, class_dir) - classes << RDoc::Generator::Class.new(from, html_file, class_dir, options) - - from.each_classmodule do |mod| - build_class_list(classes, options, mod, html_file, class_dir) - end - end - - def initialize(context, options) - @context = context - @options = options - - # HACK ugly - @template = options.template_class - end - - def formatter - @formatter ||= @options.formatter || - RDoc::Markup::ToHtmlCrossref.new(path, self, @options.show_hash) - end - - ## - # convenience method to build a hyperlink - - def href(link, cls, name) - %{<a href="#{link}" class="#{cls}">#{name}</a>} #" - end - - ## - # Returns a reference to outselves to be used as an href= the form depends - # on whether we're all in one file or in multiple files - - def as_href(from_path) - if @options.all_one_file - "#" + path - else - RDoc::Markup::ToHtml.gen_relative_url from_path, path - end - end - - ## - # Create a list of Method objects for each method in the corresponding - # context object. If the @options.show_all variable is set (corresponding - # to the <tt>--all</tt> option, we include all methods, otherwise just the - # public ones. - - def collect_methods - list = @context.method_list - - unless @options.show_all then - list = list.select do |m| - m.visibility == :public or - m.visibility == :protected or - m.force_documentation - end - end - - @methods = list.collect do |m| - RDoc::Generator::Method.new m, self, @options - end - end - - ## - # Build a summary list of all the methods in this context - - def build_method_summary_list(path_prefix = "") - collect_methods unless @methods - - @methods.sort.map do |meth| - { - "name" => CGI.escapeHTML(meth.name), - "aref" => "#{path_prefix}\##{meth.aref}" - } - end - end - - ## - # Build a list of aliases for which we couldn't find a - # corresponding method - - def build_alias_summary_list(section) - @context.aliases.map do |al| - next unless al.section == section - - res = { - 'old_name' => al.old_name, - 'new_name' => al.new_name, - } - - if al.comment and not al.comment.empty? then - res['desc'] = markup al.comment, true - end - - res - end.compact - end - - ## - # Build a list of constants - - def build_constants_summary_list(section) - @context.constants.map do |co| - next unless co.section == section - - res = { - 'name' => co.name, - 'value' => CGI.escapeHTML(co.value) - } - - if co.comment and not co.comment.empty? then - res['desc'] = markup co.comment, true - end - - res - end.compact - end - - def build_requires_list(context) - potentially_referenced_list(context.requires) {|fn| [fn + ".rb"] } - end - - def build_include_list(context) - potentially_referenced_list(context.includes) - end - - ## - # Build a list from an array of Context items. Look up each in the - # AllReferences hash: if we find a corresponding entry, we generate a - # hyperlink to it, otherwise just output the name. However, some names - # potentially need massaging. For example, you may require a Ruby file - # without the .rb extension, but the file names we know about may have it. - # To deal with this, we pass in a block which performs the massaging, - # returning an array of alternative names to match - - def potentially_referenced_list(array) - res = [] - array.each do |i| - ref = AllReferences[i.name] -# if !ref -# container = @context.parent -# while !ref && container -# name = container.name + "::" + i.name -# ref = AllReferences[name] -# container = container.parent -# end -# end - - ref = @context.find_symbol(i.name) - ref = ref.viewer if ref - - if !ref && block_given? - possibles = yield(i.name) - while !ref and !possibles.empty? - ref = AllReferences[possibles.shift] - end - end - h_name = CGI.escapeHTML(i.name) - if ref and ref.document_self - path = url(ref.path) - res << { "name" => h_name, "aref" => path } - else - res << { "name" => h_name } - end - end - res - end - - ## - # Build an array of arrays of method details. The outer array has up - # to six entries, public, private, and protected for both class - # methods, the other for instance methods. The inner arrays contain - # a hash for each method - - def build_method_detail_list(section) - outer = [] - - methods = @methods.sort.select do |m| - m.document_self and m.section == section - end - - for singleton in [true, false] - for vis in [ :public, :protected, :private ] - res = [] - methods.each do |m| - next unless m.visibility == vis and m.singleton == singleton - - row = {} - - if m.call_seq then - row["callseq"] = m.call_seq.gsub(/->/, '→') - else - row["name"] = CGI.escapeHTML(m.name) - row["params"] = m.params - end - - desc = m.description.strip - row["m_desc"] = desc unless desc.empty? - row["aref"] = m.aref - row["visibility"] = m.visibility.to_s - - alias_names = [] - - m.aliases.each do |other| - if other.viewer then # won't be if the alias is private - alias_names << { - 'name' => other.name, - 'aref' => other.viewer.as_href(path) - } - end - end - - row["aka"] = alias_names unless alias_names.empty? - - if @options.inline_source then - code = m.source_code - row["sourcecode"] = code if code - else - code = m.src_url - if code then - row["codeurl"] = code - row["imgurl"] = m.img_url - end - end - - res << row - end - - if res.size > 0 then - outer << { - "type" => vis.to_s.capitalize, - "category" => singleton ? "Class" : "Instance", - "methods" => res - } - end - end - end - - outer - end - - ## - # Build the structured list of classes and modules contained - # in this context. - - def build_class_list(level, from, section, infile=nil) - prefix = ' ::' * level; - res = '' - - from.modules.sort.each do |mod| - next unless mod.section == section - next if infile && !mod.defined_in?(infile) - if mod.document_self - res << - prefix << - 'Module ' << - href(url(mod.viewer.path), 'link', mod.full_name) << - "<br />\n" << - build_class_list(level + 1, mod, section, infile) - end - end - - from.classes.sort.each do |cls| - next unless cls.section == section - next if infile and not cls.defined_in?(infile) - - if cls.document_self - res << - prefix << - 'Class ' << - href(url(cls.viewer.path), 'link', cls.full_name) << - "<br />\n" << - build_class_list(level + 1, cls, section, infile) - end - end - - res - end - - def url(target) - RDoc::Markup::ToHtml.gen_relative_url path, target - end - - def aref_to(target) - if @options.all_one_file - "#" + target - else - url(target) - end - end - - def document_self - @context.document_self - end - - def diagram_reference(diagram) - res = diagram.gsub(/((?:src|href)=")(.*?)"/) { - $1 + url($2) + '"' - } - res - end - - ## - # Find a symbol in ourselves or our parent - - def find_symbol(symbol, method=nil) - res = @context.find_symbol(symbol, method) - if res - res = res.viewer - end - res - end - - ## - # create table of contents if we contain sections - - def add_table_of_sections - toc = [] - @context.sections.each do |section| - if section.title then - toc << { - 'secname' => section.title, - 'href' => section.sequence - } - end - end - - @values['toc'] = toc unless toc.empty? - end - - end - - ## - # Wrap a ClassModule context - - class Class < Context - - attr_reader :methods - attr_reader :path - attr_reader :values - - def initialize(context, html_file, prefix, options) - super context, options - - @html_file = html_file - @html_class = self - @is_module = context.module? - @values = {} - - context.viewer = self - - if options.all_one_file - @path = context.full_name - else - @path = http_url(context.full_name, prefix) - end - - collect_methods - - AllReferences.add(name, self) - end - - ## - # Returns the relative file name to store this class in, which is also its - # url - - def http_url(full_name, prefix) - path = full_name.dup - - path.gsub!(/<<\s*(\w*)/, 'from-\1') if path['<<'] - - ::File.join(prefix, path.split("::")) + ".html" - end - - def name - @context.full_name - end - - def parent_name - @context.parent.full_name - end - - def index_name - name - end - - def write_on(f, file_list, class_list, method_list, overrides = {}) - value_hash - - @values['file_list'] = file_list - @values['class_list'] = class_list - @values['method_list'] = method_list - - @values.update overrides - - template = RDoc::TemplatePage.new(@template::BODY, - @template::CLASS_PAGE, - @template::METHOD_LIST) - - template.write_html_on(f, @values) - end - - def value_hash - class_attribute_values - add_table_of_sections - - @values["charset"] = @options.charset - @values["style_url"] = style_url(path, @options.css) - - d = markup(@context.comment) - @values["description"] = d unless d.empty? - - ml = build_method_summary_list @path - @values["methods"] = ml unless ml.empty? - - il = build_include_list @context - @values["includes"] = il unless il.empty? - - @values["sections"] = @context.sections.map do |section| - secdata = { - "sectitle" => section.title, - "secsequence" => section.sequence, - "seccomment" => markup(section.comment), - } - - al = build_alias_summary_list section - secdata["aliases"] = al unless al.empty? - - co = build_constants_summary_list section - secdata["constants"] = co unless co.empty? - - al = build_attribute_list section - secdata["attributes"] = al unless al.empty? - - cl = build_class_list 0, @context, section - secdata["classlist"] = cl unless cl.empty? - - mdl = build_method_detail_list section - secdata["method_list"] = mdl unless mdl.empty? - - secdata - end - - @values - end - - def build_attribute_list(section) - @context.attributes.sort.map do |att| - next unless att.section == section - - if att.visibility == :public or att.visibility == :protected or - @options.show_all then - - entry = { - "name" => CGI.escapeHTML(att.name), - "rw" => att.rw, - "a_desc" => markup(att.comment, true) - } - - unless att.visibility == :public or att.visibility == :protected then - entry["rw"] << "-" - end - - entry - end - end.compact - end - - def class_attribute_values - h_name = CGI.escapeHTML(name) - - @values["path"] = @path - @values["classmod"] = @is_module ? "Module" : "Class" - @values["title"] = "#{@values['classmod']}: #{h_name}" - - c = @context - c = c.parent while c and not c.diagram - - if c and c.diagram then - @values["diagram"] = diagram_reference(c.diagram) - end - - @values["full_name"] = h_name - - if not @context.module? and @context.superclass then - parent_class = @context.superclass - @values["parent"] = CGI.escapeHTML(parent_class) - - if parent_name - lookup = parent_name + "::" + parent_class - else - lookup = parent_class - end - - parent_url = AllReferences[lookup] || AllReferences[parent_class] - - if parent_url and parent_url.document_self - @values["par_url"] = aref_to(parent_url.path) - end - end - - files = [] - @context.in_files.each do |f| - res = {} - full_path = CGI.escapeHTML(f.file_absolute_name) - - res["full_path"] = full_path - res["full_path_url"] = aref_to(f.viewer.path) if f.document_self - - if @options.webcvs - res["cvsurl"] = cvs_url( @options.webcvs, full_path ) - end - - files << res - end - - @values['infiles'] = files - end - - def <=>(other) - self.name <=> other.name - end - - end - - ## - # Handles the mapping of a file's information to HTML. In reality, a file - # corresponds to a +TopLevel+ object, containing modules, classes, and - # top-level methods. In theory it _could_ contain attributes and aliases, - # but we ignore these for now. - - class File < Context - - attr_reader :path - attr_reader :name - attr_reader :values - - def initialize(context, options, file_dir) - super context, options - - @values = {} - - if options.all_one_file - @path = filename_to_label - else - @path = http_url(file_dir) - end - - @name = @context.file_relative_name - - collect_methods - AllReferences.add(name, self) - context.viewer = self - end - - def http_url(file_dir) - ::File.join file_dir, "#{@context.file_relative_name.tr '.', '_'}.html" - end - - def filename_to_label - @context.file_relative_name.gsub(/%|\/|\?|\#/) do - '%%%x' % $&[0].unpack('C') - end - end - - def index_name - name - end - - def parent_name - nil - end - - def value_hash - file_attribute_values - add_table_of_sections - - @values["charset"] = @options.charset - @values["href"] = path - @values["style_url"] = style_url(path, @options.css) - - if @context.comment - d = markup(@context.comment) - @values["description"] = d if d.size > 0 - end - - ml = build_method_summary_list - @values["methods"] = ml unless ml.empty? - - il = build_include_list(@context) - @values["includes"] = il unless il.empty? - - rl = build_requires_list(@context) - @values["requires"] = rl unless rl.empty? - - if @options.promiscuous - file_context = nil - else - file_context = @context - end - - - @values["sections"] = @context.sections.map do |section| - - secdata = { - "sectitle" => section.title, - "secsequence" => section.sequence, - "seccomment" => markup(section.comment) - } - - cl = build_class_list(0, @context, section, file_context) - secdata["classlist"] = cl unless cl.empty? - - mdl = build_method_detail_list(section) - secdata["method_list"] = mdl unless mdl.empty? - - al = build_alias_summary_list(section) - secdata["aliases"] = al unless al.empty? - - co = build_constants_summary_list(section) - secdata["constants"] = co unless co.empty? - - secdata - end - - @values - end - - def write_on(f, file_list, class_list, method_list, overrides = {}) - value_hash - - @values['file_list'] = file_list - @values['class_list'] = class_list - @values['method_list'] = method_list - - @values.update overrides - - template = RDoc::TemplatePage.new(@template::BODY, - @template::FILE_PAGE, - @template::METHOD_LIST) - - template.write_html_on(f, @values) - end - - def file_attribute_values - full_path = @context.file_absolute_name - short_name = ::File.basename full_path - - @values["title"] = CGI.escapeHTML("File: #{short_name}") - - if @context.diagram then - @values["diagram"] = diagram_reference(@context.diagram) - end - - @values["short_name"] = CGI.escapeHTML(short_name) - @values["full_path"] = CGI.escapeHTML(full_path) - @values["dtm_modified"] = @context.file_stat.mtime.to_s - - if @options.webcvs then - @values["cvsurl"] = cvs_url @options.webcvs, @values["full_path"] - end - end - - def <=>(other) - self.name <=> other.name - end - - end - - class Method - - include MarkUp - - attr_reader :context - attr_reader :src_url - attr_reader :img_url - attr_reader :source_code - - @@seq = "M000000" - - @@all_methods = [] - - def self.all_methods - @@all_methods - end - - def self.reset - @@all_methods = [] - end - - def initialize(context, html_class, options) - # TODO: rethink the class hierarchy here... - @context = context - @html_class = html_class - @options = options - - @@seq = @@seq.succ - @seq = @@seq - - # HACK ugly - @template = options.template_class - - @@all_methods << self - - context.viewer = self - - if (ts = @context.token_stream) - @source_code = markup_code(ts) - unless @options.inline_source - @src_url = create_source_code_file(@source_code) - @img_url = RDoc::Markup::ToHtml.gen_relative_url path, 'source.png' - end - end - - AllReferences.add(name, self) - end - - ## - # Returns a reference to outselves to be used as an href= the form depends - # on whether we're all in one file or in multiple files - - def as_href(from_path) - if @options.all_one_file - "#" + path - else - RDoc::Markup::ToHtml.gen_relative_url from_path, path - end - end - - def formatter - @formatter ||= @options.formatter || - RDoc::Markup::ToHtmlCrossref.new(path, self, @options.show_hash) - end - - def inspect - alias_for = if @context.is_alias_for then - " (alias_for #{@context.is_alias_for})" - else - nil - end - - "#<%s:0x%x %s%s%s (%s)%s>" % [ - self.class, object_id, - @context.parent.name, - @context.singleton ? '::' : '#', - name, - @context.visibility, - alias_for - ] - end - - def name - @context.name - end - - def section - @context.section - end - - def index_name - "#{@context.name} (#{@html_class.name})" - end - - def parent_name - if @context.parent.parent - @context.parent.parent.full_name - else - nil - end - end - - def aref - @seq - end - - def path - if @options.all_one_file - aref - else - @html_class.path + "#" + aref - end - end - - def description - markup(@context.comment) - end - - def visibility - @context.visibility - end - - def singleton - @context.singleton - end - - def call_seq - cs = @context.call_seq - if cs - cs.gsub(/\n/, "<br />\n") - else - nil - end - end - - def params - # params coming from a call-seq in 'C' will start with the - # method name - params = @context.params - if params !~ /^\w/ - params = @context.params.gsub(/\s*\#.*/, '') - params = params.tr("\n", " ").squeeze(" ") - params = "(" + params + ")" unless params[0] == ?( - - if (block = @context.block_params) - # If this method has explicit block parameters, remove any - # explicit &block - - params.sub!(/,?\s*&\w+/, '') - - block.gsub!(/\s*\#.*/, '') - block = block.tr("\n", " ").squeeze(" ") - if block[0] == ?( - block.sub!(/^\(/, '').sub!(/\)/, '') - end - params << " {|#{block.strip}| ...}" - end - end - CGI.escapeHTML(params) - end - - def create_source_code_file(code_body) - meth_path = @html_class.path.sub(/\.html$/, '.src') - FileUtils.mkdir_p(meth_path) - file_path = ::File.join meth_path, "#{@seq}.html" - - template = RDoc::TemplatePage.new(@template::SRC_PAGE) - - open file_path, 'w' do |f| - values = { - 'title' => CGI.escapeHTML(index_name), - 'code' => code_body, - 'style_url' => style_url(file_path, @options.css), - 'charset' => @options.charset - } - template.write_html_on(f, values) - end - - RDoc::Markup::ToHtml.gen_relative_url path, file_path - end - - def <=>(other) - @context <=> other.context - end - - ## - # Given a sequence of source tokens, mark up the source code - # to make it look purty. - - def markup_code(tokens) - src = "" - tokens.each do |t| - next unless t -# style = STYLE_MAP[t.class] - style = case t - when RDoc::RubyToken::TkCONSTANT then "ruby-constant" - when RDoc::RubyToken::TkKW then "ruby-keyword kw" - when RDoc::RubyToken::TkIVAR then "ruby-ivar" - when RDoc::RubyToken::TkOp then "ruby-operator" - when RDoc::RubyToken::TkId then "ruby-identifier" - when RDoc::RubyToken::TkNode then "ruby-node" - when RDoc::RubyToken::TkCOMMENT then "ruby-comment cmt" - when RDoc::RubyToken::TkREGEXP then "ruby-regexp re" - when RDoc::RubyToken::TkSTRING then "ruby-value str" - when RDoc::RubyToken::TkVal then "ruby-value" - else - nil - end - - text = CGI.escapeHTML(t.text) - - if style - src << "<span class=\"#{style}\">#{text}</span>" - else - src << text - end - end - - add_line_numbers(src) if @options.include_line_numbers - src - end - - ## - # We rely on the fact that the first line of a source code listing has - # # File xxxxx, line dddd - - def add_line_numbers(src) - if src =~ /\A.*, line (\d+)/ - first = $1.to_i - 1 - last = first + src.count("\n") - size = last.to_s.length - real_fmt = "%#{size}d: " - fmt = " " * (size+2) - src.gsub!(/^/) do - res = sprintf(fmt, first) - first += 1 - fmt = real_fmt - res - end - end - end - - def document_self - @context.document_self - end - - def aliases - @context.aliases - end - - def find_symbol(symbol, method=nil) - res = @context.parent.find_symbol(symbol, method) - if res - res = res.viewer - end - res - end - - end - -end - diff --git a/trunk/lib/rdoc/generator/chm.rb b/trunk/lib/rdoc/generator/chm.rb deleted file mode 100644 index 7537365842..0000000000 --- a/trunk/lib/rdoc/generator/chm.rb +++ /dev/null @@ -1,113 +0,0 @@ -require 'rdoc/generator/html' - -class RDoc::Generator::CHM < RDoc::Generator::HTML - - HHC_PATH = "c:/Program Files/HTML Help Workshop/hhc.exe" - - ## - # Standard generator factory - - def self.for(options) - new(options) - end - - def initialize(*args) - super - @op_name = @options.op_name || "rdoc" - check_for_html_help_workshop - end - - def check_for_html_help_workshop - stat = File.stat(HHC_PATH) - rescue - $stderr << - "\n.chm output generation requires that Microsoft's Html Help\n" << - "Workshop is installed. RDoc looks for it in:\n\n " << - HHC_PATH << - "\n\nYou can download a copy for free from:\n\n" << - " http://msdn.microsoft.com/library/default.asp?" << - "url=/library/en-us/htmlhelp/html/hwMicrosoftHTMLHelpDownloads.asp\n\n" - end - - ## - # Generate the html as normal, then wrap it in a help project - - def generate(info) - super - @project_name = @op_name + ".hhp" - create_help_project - end - - ## - # The project contains the project file, a table of contents and an index - - def create_help_project - create_project_file - create_contents_and_index - compile_project - end - - ## - # The project file links together all the various - # files that go to make up the help. - - def create_project_file - template = RDoc::TemplatePage.new @template::HPP_FILE - values = { "title" => @options.title, "opname" => @op_name } - files = [] - @files.each do |f| - files << { "html_file_name" => f.path } - end - - values['all_html_files'] = files - - File.open(@project_name, "w") do |f| - template.write_html_on(f, values) - end - end - - ## - # The contents is a list of all files and modules. - # For each we include as sub-entries the list - # of methods they contain. As we build the contents - # we also build an index file - - def create_contents_and_index - contents = [] - index = [] - - (@files+@classes).sort.each do |entry| - content_entry = { "c_name" => entry.name, "ref" => entry.path } - index << { "name" => entry.name, "aref" => entry.path } - - internals = [] - - methods = entry.build_method_summary_list(entry.path) - - content_entry["methods"] = methods unless methods.empty? - contents << content_entry - index.concat methods - end - - values = { "contents" => contents } - template = RDoc::TemplatePage.new @template::CONTENTS - File.open("contents.hhc", "w") do |f| - template.write_html_on(f, values) - end - - values = { "index" => index } - template = RDoc::TemplatePage.new @template::CHM_INDEX - File.open("index.hhk", "w") do |f| - template.write_html_on(f, values) - end - end - - ## - # Invoke the windows help compiler to compiler the project - - def compile_project - system(HHC_PATH, @project_name) - end - -end - diff --git a/trunk/lib/rdoc/generator/chm/chm.rb b/trunk/lib/rdoc/generator/chm/chm.rb deleted file mode 100644 index 0a17a9e1ea..0000000000 --- a/trunk/lib/rdoc/generator/chm/chm.rb +++ /dev/null @@ -1,98 +0,0 @@ -require 'rdoc/generator/chm' -require 'rdoc/generator/html/html' - -module RDoc::Generator::CHM::CHM - - HTML = RDoc::Generator::HTML::HTML - - INDEX = HTML::INDEX - - CLASS_INDEX = HTML::CLASS_INDEX - CLASS_PAGE = HTML::CLASS_PAGE - FILE_INDEX = HTML::FILE_INDEX - FILE_PAGE = HTML::FILE_PAGE - METHOD_INDEX = HTML::METHOD_INDEX - METHOD_LIST = HTML::METHOD_LIST - - FR_INDEX_BODY = HTML::FR_INDEX_BODY - - # This is a nasty little hack, but hhc doesn't support the <?xml tag, so... - BODY = HTML::BODY.sub!(/<\?xml.*\?>/, '') - SRC_PAGE = HTML::SRC_PAGE.sub!(/<\?xml.*\?>/, '') - - HPP_FILE = <<-EOF -[OPTIONS] -Auto Index = Yes -Compatibility=1.1 or later -Compiled file=<%= values["opname"] %>.chm -Contents file=contents.hhc -Full-text search=Yes -Index file=index.hhk -Language=0x409 English(United States) -Title=<%= values["title"] %> - -[FILES] -<% values["all_html_files"].each do |all_html_files| %> -<%= all_html_files["html_file_name"] %> -<% end # values["all_html_files"] %> - EOF - - CONTENTS = <<-EOF -<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> -<HTML> -<HEAD> -<meta name="GENERATOR" content="Microsoft® HTML Help Workshop 4.1"> -<!-- Sitemap 1.0 --> -</HEAD><BODY> -<OBJECT type="text/site properties"> - <param name="Foreground" value="0x80"> - <param name="Window Styles" value="0x800025"> - <param name="ImageType" value="Folder"> -</OBJECT> -<UL> -<% values["contents"].each do |contents| %> - <LI> <OBJECT type="text/sitemap"> - <param name="Name" value="<%= contents["c_name"] %>"> - <param name="Local" value="<%= contents["ref"] %>"> - </OBJECT> -<% if contents["methods"] then %> -<ul> -<% contents["methods"].each do |methods| %> - <LI> <OBJECT type="text/sitemap"> - <param name="Name" value="<%= methods["name"] %>"> - <param name="Local" value="<%= methods["aref"] %>"> - </OBJECT> -<% end # contents["methods"] %> -</ul> -<% end %> - </LI> -<% end # values["contents"] %> -</UL> -</BODY></HTML> - EOF - - CHM_INDEX = <<-EOF -<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> -<HTML> -<HEAD> -<meta name="GENERATOR" content="Microsoft® HTML Help Workshop 4.1"> -<!-- Sitemap 1.0 --> -</HEAD><BODY> -<OBJECT type="text/site properties"> - <param name="Foreground" value="0x80"> - <param name="Window Styles" value="0x800025"> - <param name="ImageType" value="Folder"> -</OBJECT> -<UL> -<% values["index"].each do |index| %> - <LI> <OBJECT type="text/sitemap"> - <param name="Name" value="<%= index["name"] %>"> - <param name="Local" value="<%= index["aref"] %>"> - </OBJECT> -<% end # values["index"] %> -</UL> -</BODY></HTML> - EOF - -end - diff --git a/trunk/lib/rdoc/generator/html.rb b/trunk/lib/rdoc/generator/html.rb deleted file mode 100644 index a9e030a896..0000000000 --- a/trunk/lib/rdoc/generator/html.rb +++ /dev/null @@ -1,397 +0,0 @@ -require 'fileutils' - -require 'rdoc/generator' -require 'rdoc/markup/to_html' - -## -# We're responsible for generating all the HTML files from the object tree -# defined in code_objects.rb. We generate: -# -# [files] an html file for each input file given. These -# input files appear as objects of class -# TopLevel -# -# [classes] an html file for each class or module encountered. -# These classes are not grouped by file: if a file -# contains four classes, we'll generate an html -# file for the file itself, and four html files -# for the individual classes. -# -# [indices] we generate three indices for files, classes, -# and methods. These are displayed in a browser -# like window with three index panes across the -# top and the selected description below -# -# Method descriptions appear in whatever entity (file, class, or module) that -# contains them. -# -# We generate files in a structure below a specified subdirectory, normally -# +doc+. -# -# opdir -# | -# |___ files -# | |__ per file summaries -# | -# |___ classes -# |__ per class/module descriptions -# -# HTML is generated using the Template class. - -class RDoc::Generator::HTML - - include RDoc::Generator::MarkUp - - ## - # Generator may need to return specific subclasses depending on the - # options they are passed. Because of this we create them using a factory - - def self.for(options) - RDoc::Generator::AllReferences.reset - RDoc::Generator::Method.reset - - if options.all_one_file - RDoc::Generator::HTMLInOne.new options - else - new options - end - end - - class << self - protected :new - end - - ## - # Set up a new HTML generator. Basically all we do here is load up the - # correct output temlate - - def initialize(options) #:not-new: - @options = options - load_html_template - @main_page_path = nil - end - - ## - # Build the initial indices and output objects - # based on an array of TopLevel objects containing - # the extracted information. - - def generate(toplevels) - @toplevels = toplevels - @files = [] - @classes = [] - - write_style_sheet - gen_sub_directories - build_indices - generate_html - end - - private - - ## - # Load up the HTML template specified in the options. - # If the template name contains a slash, use it literally - - def load_html_template - template = @options.template - - unless template =~ %r{/|\\} then - template = File.join('rdoc', 'generator', @options.generator.key, - template) - end - - require template - - @template = self.class.const_get @options.template.upcase - @options.template_class = @template - - rescue LoadError - $stderr.puts "Could not find HTML template '#{template}'" - exit 99 - end - - ## - # Write out the style sheet used by the main frames - - def write_style_sheet - return unless @template.constants.include? :STYLE or - @template.constants.include? 'STYLE' - - template = RDoc::TemplatePage.new @template::STYLE - - unless @options.css then - open RDoc::Generator::CSS_NAME, 'w' do |f| - values = {} - - if @template.constants.include? :FONTS or - @template.constants.include? 'FONTS' then - values["fonts"] = @template::FONTS - end - - template.write_html_on(f, values) - end - end - end - - ## - # See the comments at the top for a description of the directory structure - - def gen_sub_directories - FileUtils.mkdir_p RDoc::Generator::FILE_DIR - FileUtils.mkdir_p RDoc::Generator::CLASS_DIR - rescue - $stderr.puts $!.message - exit 1 - end - - def build_indices - @files, @classes = RDoc::Generator::Context.build_indicies(@toplevels, - @options) - end - - ## - # Generate all the HTML - - def generate_html - # the individual descriptions for files and classes - gen_into(@files) - gen_into(@classes) - - # and the index files - gen_file_index - gen_class_index - gen_method_index - gen_main_index - - # this method is defined in the template file - write_extra_pages if defined? write_extra_pages - end - - def gen_into(list) - @file_list ||= index_to_links @files - @class_list ||= index_to_links @classes - @method_list ||= index_to_links RDoc::Generator::Method.all_methods - - list.each do |item| - next unless item.document_self - - op_file = item.path - - FileUtils.mkdir_p File.dirname(op_file) - - open op_file, 'w' do |io| - item.write_on io, @file_list, @class_list, @method_list - end - end - end - - def gen_file_index - gen_an_index @files, 'Files', @template::FILE_INDEX, "fr_file_index.html" - end - - def gen_class_index - gen_an_index(@classes, 'Classes', @template::CLASS_INDEX, - "fr_class_index.html") - end - - def gen_method_index - gen_an_index(RDoc::Generator::Method.all_methods, 'Methods', - @template::METHOD_INDEX, "fr_method_index.html") - end - - def gen_an_index(collection, title, template, filename) - template = RDoc::TemplatePage.new @template::FR_INDEX_BODY, template - res = [] - collection.sort.each do |f| - if f.document_self - res << { "href" => f.path, "name" => f.index_name } - end - end - - values = { - "entries" => res, - 'list_title' => CGI.escapeHTML(title), - 'index_url' => main_url, - 'charset' => @options.charset, - 'style_url' => style_url('', @options.css), - } - - open filename, 'w' do |f| - template.write_html_on(f, values) - end - end - - ## - # The main index page is mostly a template frameset, but includes the - # initial page. If the <tt>--main</tt> option was given, we use this as - # our main page, otherwise we use the first file specified on the command - # line. - - def gen_main_index - if @template.const_defined? :FRAMELESS then - main = @files.find do |file| - @main_page == file.name - end - - if main.nil? then - main = @classes.find do |klass| - main_page == klass.context.full_name - end - end - else - main = RDoc::TemplatePage.new @template::INDEX - end - - open 'index.html', 'w' do |f| - style_url = style_url '', @options.css - - classes = @classes.sort.map { |klass| klass.value_hash } - - values = { - 'main_page' => @main_page, - 'initial_page' => main_url, - 'style_url' => style_url('', @options.css), - 'title' => CGI.escapeHTML(@options.title), - 'charset' => @options.charset, - 'classes' => classes, - } - - values['inline_source'] = @options.inline_source - - if main.respond_to? :write_on then - main.write_on f, @file_list, @class_list, @method_list, values - else - main.write_html_on f, values - end - end - end - - def index_to_links(collection) - collection.sort.map do |f| - next unless f.document_self - { "href" => f.path, "name" => f.index_name } - end.compact - end - - ## - # Returns the url of the main page - - def main_url - @main_page = @options.main_page - @main_page_ref = nil - - if @main_page then - @main_page_ref = RDoc::Generator::AllReferences[@main_page] - - if @main_page_ref then - @main_page_path = @main_page_ref.path - else - $stderr.puts "Could not find main page #{@main_page}" - end - end - - unless @main_page_path then - file = @files.find { |context| context.document_self } - @main_page_path = file.path if file - end - - unless @main_page_path then - $stderr.puts "Couldn't find anything to document" - $stderr.puts "Perhaps you've used :stopdoc: in all classes" - exit 1 - end - - @main_page_path - end - -end - -class RDoc::Generator::HTMLInOne < RDoc::Generator::HTML - - def initialize(*args) - super - end - - ## - # Build the initial indices and output objects - # based on an array of TopLevel objects containing - # the extracted information. - - def generate(info) - @toplevels = info - @hyperlinks = {} - - build_indices - generate_xml - end - - ## - # Generate: - # - # * a list of RDoc::Generator::File objects for each TopLevel object. - # * a list of RDoc::Generator::Class objects for each first level - # class or module in the TopLevel objects - # * a complete list of all hyperlinkable terms (file, - # class, module, and method names) - - def build_indices - @files, @classes = RDoc::Generator::Context.build_indices(@toplevels, - @options) - end - - ## - # Generate all the HTML. For the one-file case, we generate - # all the information in to one big hash - - def generate_xml - values = { - 'charset' => @options.charset, - 'files' => gen_into(@files), - 'classes' => gen_into(@classes), - 'title' => CGI.escapeHTML(@options.title), - } - - # this method is defined in the template file - write_extra_pages if defined? write_extra_pages - - template = RDoc::TemplatePage.new @template::ONE_PAGE - - if @options.op_name - opfile = open @options.op_name, 'w' - else - opfile = $stdout - end - template.write_html_on(opfile, values) - end - - def gen_into(list) - res = [] - list.each do |item| - res << item.value_hash - end - res - end - - def gen_file_index - gen_an_index(@files, 'Files') - end - - def gen_class_index - gen_an_index(@classes, 'Classes') - end - - def gen_method_index - gen_an_index(RDoc::Generator::Method.all_methods, 'Methods') - end - - def gen_an_index(collection, title) - return { - "entries" => index_to_links(collection), - 'list_title' => title, - 'index_url' => main_url, - } - end - -end - diff --git a/trunk/lib/rdoc/generator/html/frameless.rb b/trunk/lib/rdoc/generator/html/frameless.rb deleted file mode 100644 index 2af890ce04..0000000000 --- a/trunk/lib/rdoc/generator/html/frameless.rb +++ /dev/null @@ -1,795 +0,0 @@ -require 'rdoc/generator/html' -require 'rdoc/generator/html/one_page_html' - -## -# = CSS2 RDoc HTML template -# -# This is a template for RDoc that uses XHTML 1.0 Transitional and dictates a -# bit more of the appearance of the output to cascading stylesheets than the -# default. It was designed for clean inline code display, and uses DHTMl to -# toggle the visbility of each method's source with each click on the '[source]' -# link. -# -# == Authors -# -# * Michael Granger <ged@FaerieMUD.org> -# -# Copyright (c) 2002, 2003 The FaerieMUD Consortium. Some rights reserved. -# -# This work is licensed under the Creative Commons Attribution License. To view -# a copy of this license, visit http://creativecommons.org/licenses/by/1.0/ or -# send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California -# 94305, USA. - -module RDoc::Generator::HTML::FRAMELESS - - FRAMELESS = true - - FONTS = "Verdana,Arial,Helvetica,sans-serif" - - STYLE = <<-EOF -body { - font-family: #{FONTS}; - font-size: 90%; - margin: 0; - margin-left: 40px; - padding: 0; - background: white; -} - -h1, h2, h3, h4 { - margin: 0; - color: #efefef; - background: transparent; -} - -h1 { - font-size: 150%; -} - -h2,h3,h4 { - margin-top: 1em; -} - -:link, :visited { - background: #eef; - color: #039; - text-decoration: none; -} - -:link:hover, :visited:hover { - background: #039; - color: #eef; -} - -/* Override the base stylesheet's Anchor inside a table cell */ -td > :link, td > :visited { - background: transparent; - color: #039; - text-decoration: none; -} - -/* and inside a section title */ -.section-title > :link, .section-title > :visited { - background: transparent; - color: #eee; - text-decoration: none; -} - -/* === Structural elements =================================== */ - -.index { - margin: 0; - margin-left: -40px; - padding: 0; - font-size: 90%; -} - -.index :link, .index :visited { - margin-left: 0.7em; -} - -.index .section-bar { - margin-left: 0px; - padding-left: 0.7em; - background: #ccc; - font-size: small; -} - -#classHeader, #fileHeader { - width: auto; - color: white; - padding: 0.5em 1.5em 0.5em 1.5em; - margin: 0; - margin-left: -40px; - border-bottom: 3px solid #006; -} - -#classHeader :link, #fileHeader :link, -#classHeader :visited, #fileHeader :visited { - background: inherit; - color: white; -} - -#classHeader td, #fileHeader td { - background: inherit; - color: white; -} - -#fileHeader { - background: #057; -} - -#classHeader { - background: #048; -} - -.class-name-in-header { - font-size: 180%; - font-weight: bold; -} - -#bodyContent { - padding: 0 1.5em 0 1.5em; -} - -#description { - padding: 0.5em 1.5em; - background: #efefef; - border: 1px dotted #999; -} - -#description h1, #description h2, #description h3, -#description h4, #description h5, #description h6 { - color: #125; - background: transparent; -} - -#copyright { - color: #333; - background: #efefef; - font: 0.75em sans-serif; - margin-top: 5em; - margin-bottom: 0; - padding: 0.5em 2em; -} - -/* === Classes =================================== */ - -table.header-table { - color: white; - font-size: small; -} - -.type-note { - font-size: small; - color: #dedede; -} - -.xxsection-bar { - background: #eee; - color: #333; - padding: 3px; -} - -.section-bar { - color: #333; - border-bottom: 1px solid #999; - margin-left: -20px; -} - -.section-title { - background: #79a; - color: #eee; - padding: 3px; - margin-top: 2em; - margin-left: -30px; - border: 1px solid #999; -} - -.top-aligned-row { - vertical-align: top -} - -.bottom-aligned-row { - vertical-align: bottom -} - -/* --- Context section classes ----------------------- */ - -.context-row { } - -.context-item-name { - font-family: monospace; - font-weight: bold; - color: black; -} - -.context-item-value { - font-size: small; - color: #448; -} - -.context-item-desc { - color: #333; - padding-left: 2em; -} - -/* --- Method classes -------------------------- */ - -.method-detail { - background: #efefef; - padding: 0; - margin-top: 0.5em; - margin-bottom: 1em; - border: 1px dotted #ccc; -} - -.method-heading { - color: black; - background: #ccc; - border-bottom: 1px solid #666; - padding: 0.2em 0.5em 0 0.5em; -} - -.method-signature { - color: black; - background: inherit; -} - -.method-name { - font-weight: bold; -} - -.method-args { - font-style: italic; -} - -.method-description { - padding: 0 0.5em 0 0.5em; -} - -/* --- Source code sections -------------------- */ - -:link.source-toggle, :visited.source-toggle { - font-size: 90%; -} - -div.method-source-code { - background: #262626; - color: #ffdead; - margin: 1em; - padding: 0.5em; - border: 1px dashed #999; - overflow: hidden; -} - -div.method-source-code pre { - color: #ffdead; - overflow: hidden; -} - -/* --- Ruby keyword styles --------------------- */ - -.standalone-code { - background: #221111; - color: #ffdead; - overflow: hidden; -} - -.ruby-constant { - color: #7fffd4; - background: transparent; -} - -.ruby-keyword { - color: #00ffff; - background: transparent; -} - -.ruby-ivar { - color: #eedd82; - background: transparent; -} - -.ruby-operator { - color: #00ffee; - background: transparent; -} - -.ruby-identifier { - color: #ffdead; - background: transparent; -} - -.ruby-node { - color: #ffa07a; - background: transparent; -} - -.ruby-comment { - color: #b22222; - font-weight: bold; - background: transparent; -} - -.ruby-regexp { - color: #ffa07a; - background: transparent; -} - -.ruby-value { - color: #7fffd4; - background: transparent; -} - -EOF - - ## - # Header template - - XHTML_PREAMBLE = <<-EOF -<?xml version="1.0" encoding="<%= values["charset"] %>"?> -<!DOCTYPE html - PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> - EOF - - HEADER = XHTML_PREAMBLE + <<-EOF -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> -<head> - <title><%= values["title"] %></title> - <meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" /> - <meta http-equiv="Content-Script-Type" content="text/javascript" /> - <link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" media="screen" /> - <script type="text/javascript"> - // <![CDATA[ - - function popupCode( url ) { - window.open(url, "Code", "resizable=yes,scrollbars=yes,toolbar=no,status=no,height=150,width=400") - } - - function toggleCode( id ) { - if ( document.getElementById ) - elem = document.getElementById( id ); - else if ( document.all ) - elem = eval( "document.all." + id ); - else - return false; - - elemStyle = elem.style; - - if ( elemStyle.display != "block" ) { - elemStyle.display = "block" - } else { - elemStyle.display = "none" - } - - return true; - } - - // Make codeblocks hidden by default - document.writeln( "<style type=\\"text/css\\">div.method-source-code { display: none }</style>" ) - - // ]]> - </script> - -</head> -<body> -EOF - - ## - # Context content template - - CONTEXT_CONTENT = %{ -} - - ## - # Footer template - - FOOTER = <<-EOF - <div id="popupmenu" class="index"> - <ul> - <li class="index-entries section-bar">Classes - <ul> -<% values["class_list"].each do |klass| %> - <li><a href="<%= klass["href"] %>"><%= klass["name"] %></a> -<% end %> - </ul> - </li> - - <li class="index-entries section-bar">Methods - <ul> -<% values["method_list"].each do |file| %> - <li><a href="<%= file["href"] %>"><%= file["name"] %></a> -<% end %> - </ul> - </li> - - <li class="index-entries section-bar">Files - <ul> -<% values["file_list"].each do |file| %> - <li><a href="<%= file["href"] %>"><%= file["name"] %></a> -<% end %> - </ul> - </li> - </ul> - </li> - -</body> -</html> - EOF - - ## - # File page header template - - FILE_PAGE = <<-EOF - <div id="fileHeader"> - <h1><%= values["short_name"] %></h1> - - <table class="header-table"> - <tr class="top-aligned-row"> - <td><strong>Path:</strong></td> - <td><%= values["full_path"] %> -<% if values["cvsurl"] then %> - (<a href="<%= values["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>) -<% end %> - </td> - </tr> - - <tr class="top-aligned-row"> - <td><strong>Last Update:</strong></td> - <td><%= values["dtm_modified"] %></td> - </tr> - </table> - </div> - EOF - - ## - # Class page header template - - CLASS_PAGE = <<-EOF - <div id="classHeader"> - <table class="header-table"> - <tr class="top-aligned-row"> - <td><strong><%= values["classmod"] %></strong></td> - <td class="class-name-in-header"><%= values["full_name"] %></td> - </tr> - - <tr class="top-aligned-row"> - <td><strong>In:</strong></td> - <td> -<% values["infiles"].each do |infiles| %> -<% if infiles["full_path_url"] then %> - <a href="<%= infiles["full_path_url"] %>"> -<% end %> - <%= infiles["full_path"] %> -<% if infiles["full_path_url"] then %> - </a> -<% end %> -<% if infiles["cvsurl"] then %> - (<a href="<%= infiles["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>) -<% end %> - <br /> -<% end %><%# values["infiles"] %> - </td> - </tr> - -<% if values["parent"] then %> - <tr class="top-aligned-row"> - <td><strong>Parent:</strong></td> - <td> -<% if values["par_url"] then %> - <a href="<%= values["par_url"] %>"> -<% end %> - <%= values["parent"] %> -<% if values["par_url"] then %> - </a> -<% end %> - </td> - </tr> -<% end %> - </table> - </div> - EOF - - ## - # Method list template - - METHOD_LIST = <<-EOF - - <div id="contextContent"> -<% if values["diagram"] then %> - <div id="diagram"> - <%= values["diagram"] %> - </div> -<% end %> - -<% if values["description"] then %> - <div id="description"> - <%= values["description"] %> - </div> -<% end %> - -<% if values["requires"] then %> - <div id="requires-list"> - <h3 class="section-bar">Required files</h3> - - <div class="name-list"> -<% values["requires"].each do |requires| %> - <%= href requires["aref"], requires["name"] %> -<% end %><%# values["requires"] %> - </div> - </div> -<% end %> - -<% if values["toc"] then %> - <div id="contents-list"> - <h3 class="section-bar">Contents</h3> - <ul> -<% values["toc"].each do |toc| %> - <li><a href="#<%= values["href"] %>"><%= values["secname"] %></a></li> -<% end %><%# values["toc"] %> - </ul> -<% end %> - </div> - -<% if values["methods"] then %> - <div id="method-list"> - <h3 class="section-bar">Methods</h3> - - <div class="name-list"> -<% values["methods"].each do |methods| %> - <%= href methods["aref"], methods["name"] %> -<% end %><%# values["methods"] %> - </div> - </div> -<% end %> - - </div> - - - <!-- if includes --> -<% if values["includes"] then %> - <div id="includes"> - <h3 class="section-bar">Included Modules</h3> - - <div id="includes-list"> -<% values["includes"].each do |includes| %> - <span class="include-name"><%= href includes["aref"], includes["name"] %></span> -<% end %><%# values["includes"] %> - </div> - </div> -<% end %> - -<% values["sections"].each do |sections| %> - <div id="section"> -<% if sections["sectitle"] then %> - <h2 class="section-title"><a name="<%= sections["secsequence"] %>"><%= sections["sectitle"] %></a></h2> -<% if sections["seccomment"] then %> - <div class="section-comment"> - <%= sections["seccomment"] %> - </div> -<% end %> -<% end %> - -<% if values["classlist"] then %> - <div id="class-list"> - <h3 class="section-bar">Classes and Modules</h3> - - <%= values["classlist"] %> - </div> -<% end %> - -<% if values["constants"] then %> - <div id="constants-list"> - <h3 class="section-bar">Constants</h3> - - <div class="name-list"> - <table summary="Constants"> -<% values["constants"].each do |constants| %> - <tr class="top-aligned-row context-row"> - <td class="context-item-name"><%= constants["name"] %></td> - <td>=</td> - <td class="context-item-value"><%= constants["value"] %></td> -<% if values["desc"] then %> - <td width="3em"> </td> - <td class="context-item-desc"><%= constants["desc"] %></td> -<% end %> - </tr> -<% end %><%# values["constants"] %> - </table> - </div> - </div> -<% end %> - -<% if values["aliases"] then %> - <div id="aliases-list"> - <h3 class="section-bar">External Aliases</h3> - - <div class="name-list"> - <table summary="aliases"> -<% values["aliases"].each do |aliases| $stderr.puts({ :aliases => aliases }.inspect) %> - <tr class="top-aligned-row context-row"> - <td class="context-item-name"><%= values["old_name"] %></td> - <td>-></td> - <td class="context-item-value"><%= values["new_name"] %></td> - </tr> -<% if values["desc"] then %> - <tr class="top-aligned-row context-row"> - <td> </td> - <td colspan="2" class="context-item-desc"><%= values["desc"] %></td> - </tr> -<% end %> -<% end %><%# values["aliases"] %> - </table> - </div> - </div> -<% end %> - - -<% if values["attributes"] then %> - <div id="attribute-list"> - <h3 class="section-bar">Attributes</h3> - - <div class="name-list"> - <table> -<% values["attributes"].each do |attributes| $stderr.puts({ :attributes => attributes }.inspect) %> - <tr class="top-aligned-row context-row"> - <td class="context-item-name"><%= values["name"] %></td> -<% if values["rw"] then %> - <td class="context-item-value"> [<%= values["rw"] %>] </td> -<% end %> -<% unless values["rw"] then %> - <td class="context-item-value"> </td> -<% end %> - <td class="context-item-desc"><%= values["a_desc"] %></td> - </tr> -<% end %><%# values["attributes"] %> - </table> - </div> - </div> -<% end %> - - <!-- if method_list --> -<% if sections["method_list"] then %> - <div id="methods"> -<% sections["method_list"].each do |method_list| %> -<% if method_list["methods"] then %> - <h3 class="section-bar"><%= method_list["type"] %> <%= method_list["category"] %> methods</h3> - -<% method_list["methods"].each do |methods| %> - <div id="method-<%= methods["aref"] %>" class="method-detail"> - <a name="<%= methods["aref"] %>"></a> - - <div class="method-heading"> -<% if methods["codeurl"] then %> - <a href="<%= methods["codeurl"] %>" target="Code" class="method-signature" - onclick="popupCode('<%= methods["codeurl"] %>');return false;"> -<% end %> -<% if methods["sourcecode"] then %> - <a href="#<%= methods["aref"] %>" class="method-signature"> -<% end %> -<% if methods["callseq"] then %> - <span class="method-name"><%= methods["callseq"] %></span> -<% end %> -<% unless methods["callseq"] then %> - <span class="method-name"><%= methods["name"] %></span><span class="method-args"><%= methods["params"] %></span> -<% end %> -<% if methods["codeurl"] then %> - </a> -<% end %> -<% if methods["sourcecode"] then %> - </a> -<% end %> - </div> - - <div class="method-description"> -<% if methods["m_desc"] then %> - <%= methods["m_desc"] %> -<% end %> -<% if methods["sourcecode"] then %> - <p><a class="source-toggle" href="#" - onclick="toggleCode('<%= methods["aref"] %>-source');return false;">[Source]</a></p> - <div class="method-source-code" id="<%= methods["aref"] %>-source"> -<pre> -<%= methods["sourcecode"] %> -</pre> - </div> -<% end %> - </div> - </div> - -<% end %><%# method_list["methods"] %> -<% end %> -<% end %><%# sections["method_list"] %> - - </div> -<% end %> -<% end %><%# values["sections"] %> - EOF - - ## - # Body template - - BODY = HEADER + %{ - -<%= template_include %> <!-- banner header --> - - <div id="bodyContent"> - -} + METHOD_LIST + %{ - - </div> - -} + FOOTER - - ## - # Source code template - - SRC_PAGE = XHTML_PREAMBLE + <<-EOF -<html> -<head> - <title><%= values["title"] %></title> - <meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" /> - <link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" media="screen" /> -</head> -<body class="standalone-code"> - <pre><%= values["code"] %></pre> -</body> -</html> - EOF - - ## - # Index file templates - - FR_INDEX_BODY = %{ -<%= template_include %> -} - - FILE_INDEX = XHTML_PREAMBLE + <<-EOF -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> -<head> - <title><%= values["list_title"] %></title> - <meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" /> - <link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" /> - <base target="docwin" /> -</head> -<body> -<div class="index"> - <h1 class="section-bar"><%= values["list_title"] %></h1> - <div class="index-entries"> -<% values["entries"].each do |entries| %> - <a href="<%= entries["href"] %>"><%= entries["name"] %></a><br /> -<% end %><%# values["entries"] %> - </div> -</div> -</body> -</html> - EOF - - CLASS_INDEX = FILE_INDEX - METHOD_INDEX = FILE_INDEX - - INDEX = <<-EOF -<?xml version="1.0" encoding="<%= values["charset"] %>"?> -<!DOCTYPE html - PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> -<head> - <title><%= values["title"] %></title> - <meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" /> -</head> -<frameset rows="20%, 80%"> - <frameset cols="45%,55%"> - <frame src="fr_class_index.html" name="Classes" /> - <frame src="fr_method_index.html" name="Methods" /> - </frameset> - <frame src="<%= values["initial_page"] %>" name="docwin" /> -</frameset> -</html> - EOF - -end - diff --git a/trunk/lib/rdoc/generator/html/hefss.rb b/trunk/lib/rdoc/generator/html/hefss.rb deleted file mode 100644 index e186a40384..0000000000 --- a/trunk/lib/rdoc/generator/html/hefss.rb +++ /dev/null @@ -1,414 +0,0 @@ -require 'rdoc/generator/html' -require 'rdoc/generator/html/html' - -module RDoc::Generator::HTML::HEFSS - - FONTS = "Verdana, Arial, Helvetica, sans-serif" - -STYLE = <<-EOF -body,p { font-family: Verdana, Arial, Helvetica, sans-serif; - color: #000040; background: #BBBBBB; -} - -td { font-family: Verdana, Arial, Helvetica, sans-serif; - color: #000040; -} - -.attr-rw { font-size: small; color: #444488 } - -.title-row {color: #eeeeff; - background: #BBBBDD; -} - -.big-title-font { color: white; - font-family: Verdana, Arial, Helvetica, sans-serif; - font-size: large; - height: 50px} - -.small-title-font { color: purple; - font-family: Verdana, Arial, Helvetica, sans-serif; - font-size: small; } - -.aqua { color: purple } - -.method-name, attr-name { - font-family: monospace; font-weight: bold; -} - -.tablesubtitle { - width: 100%; - margin-top: 1ex; - margin-bottom: .5ex; - padding: 5px 0px 5px 20px; - font-size: large; - color: purple; - background: #BBBBCC; -} - -.tablesubsubtitle { - width: 100%; - margin-top: 1ex; - margin-bottom: .5ex; - padding: 5px 0px 5px 20px; - font-size: medium; - color: white; - background: #BBBBCC; -} - -.name-list { - font-family: monospace; - margin-left: 40px; - margin-bottom: 2ex; - line-height: 140%; -} - -.description { - margin-left: 40px; - margin-bottom: 2ex; - line-height: 140%; -} - -.methodtitle { - font-size: medium; - text_decoration: none; - padding: 3px 3px 3px 20px; - color: #0000AA; -} - -.column-title { - font-size: medium; - font-weight: bold; - text_decoration: none; - padding: 3px 3px 3px 20px; - color: #3333CC; - } - -.variable-name { - font-family: monospace; - font-size: medium; - text_decoration: none; - padding: 3px 3px 3px 20px; - color: #0000AA; -} - -.row-name { - font-size: medium; - font-weight: medium; - font-family: monospace; - text_decoration: none; - padding: 3px 3px 3px 20px; -} - -.paramsig { - font-size: small; -} - -.srcbut { float: right } - - EOF - - BODY = <<-EOF -<html><head> - <title><%= values["title"] %></title> - <meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>"> - <link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" media="screen" /> - <script type="text/javascript" language="JavaScript"> - <!-- - function popCode(url) { - parent.frames.source.location = url - } - //--> - </script> -</head> -<body bgcolor="#BBBBBB"> - -<%= template_include %> <!-- banner header --> - -<% if values["diagram"] then %> -<table width="100%"><tr><td align="center"> -<%= values["diagram"] %> -</td></tr></table> -<% end %> - -<% if values["description"] then %> -<div class="description"><%= values["description"] %></div> -<% end %> - -<% if values["requires"] then %> -<table cellpadding="5" width="100%"> -<tr><td class="tablesubtitle">Required files</td></tr> -</table><br /> -<div class="name-list"> -<% values["requires"].each do |requires| %> -<%= href requires["aref"], requires["name"] %> -<% end %><%# values["requires"] %> -<% end %> -</div> - -<% if values["sections"] then %> -<% values["sections"].each do |sections| %> -<% if sections["method_list"] then %> -<% sections["method_list"].each do |method_list| %> -<% if method_list["methods"] then %> -<table cellpadding="5" width="100%"> -<tr><td class="tablesubtitle">Subroutines and Functions</td></tr> -</table><br /> -<div class="name-list"> -<% method_list["methods"].each do |methods| %> -<a href="<%= methods["codeurl"] %>" target="source"><%= methods["name"] %></a> -<% end %><%# values["methods"] %> -</div> -<% end %> -<% end %><%# values["method_list"] %> -<% end %> - -<% if sections["attributes"] then %> -<table cellpadding="5" width="100%"> -<tr><td class="tablesubtitle">Arguments</td></tr> -</table><br /> -<table cellspacing="5"> -<% sections["attributes"].each do |attributes| %> - <tr valign="top"> -<% if attributes["rw"] then %> - <td align="center" class="attr-rw"> [<%= attributes["rw"] %>] </td> -<% end %> -<% unless attributes["rw"] then %> - <td></td> -<% end %> - <td class="attr-name"><%= attributes["name"] %></td> - <td><%= attributes["a_desc"] %></td> - </tr> -<% end %><%# values["attributes"] %> -</table> -<% end %> -<% end %><%# values["sections"] %> -<% end %> - -<% if values["classlist"] then %> -<table cellpadding="5" width="100%"> -<tr><td class="tablesubtitle">Modules</td></tr> -</table><br /> -<%= values["classlist"] %><br /> -<% end %> - - <%= template_include %> <!-- method descriptions --> - -</body> -</html> - EOF - - FILE_PAGE = <<-EOF -<table width="100%"> - <tr class="title-row"> - <td><table width="100%"><tr> - <td class="big-title-font" colspan="2"><font size="-3"><b>File</b><br /></font><%= values["short_name"] %></td> - <td align="right"><table cellspacing="0" cellpadding="2"> - <tr> - <td class="small-title-font">Path:</td> - <td class="small-title-font"><%= values["full_path"] %> -<% if values["cvsurl"] then %> - (<a href="<%= values["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>) -<% end %> - </td> - </tr> - <tr> - <td class="small-title-font">Modified:</td> - <td class="small-title-font"><%= values["dtm_modified"] %></td> - </tr> - </table> - </td></tr></table></td> - </tr> -</table><br /> - EOF - - CLASS_PAGE = <<-EOF -<table width="100%" border="0" cellspacing="0"> - <tr class="title-row"> - <td class="big-title-font"> - <font size="-3"><b><%= values["classmod"] %></b><br /></font><%= values["full_name"] %> - </td> - <td align="right"> - <table cellspacing="0" cellpadding="2"> - <tr valign="top"> - <td class="small-title-font">In:</td> - <td class="small-title-font"> -<% values["infiles"].each do |infiles| %> -<%= href infiles["full_path_url"], infiles["full_path"] %> -<% if infiles["cvsurl"] then %> - (<a href="<%= infiles["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>) -<% end %> -<% end %><%# values["infiles"] %> - </td> - </tr> -<% if values["parent"] then %> - <tr> - <td class="small-title-font">Parent:</td> - <td class="small-title-font"> -<% if values["par_url"] then %> - <a href="<%= values["par_url"] %>" class="cyan"> -<% end %> -<%= values["parent"] %> -<% if values["par_url"] then %> - </a> -<% end %> - </td> - </tr> -<% end %> - </table> - </td> - </tr> -</table><br /> - EOF - - METHOD_LIST = <<-EOF -<% if values["includes"] then %> -<div class="tablesubsubtitle">Uses</div><br /> -<div class="name-list"> -<% values["includes"].each do |includes| %> - <span class="method-name"><%= href includes["aref"], includes["name"] %></span> -<% end %><%# values["includes"] %> -</div> -<% end %> - -<% if values["sections"] then %> -<% values["sections"].each do |sections| %> -<% if sections["method_list"] then %> -<% sections["method_list"].each do |method_list| %> -<% if method_list["methods"] then %> -<table cellpadding="5" width="100%"> -<tr><td class="tablesubtitle"><%= method_list["type"] %> <%= method_list["category"] %> methods</td></tr> -</table> -<% method_list["methods"].each do |methods| %> -<table width="100%" cellspacing="0" cellpadding="5" border="0"> -<tr><td class="methodtitle"> -<a name="<%= methods["aref"] %>"> -<b><%= methods["name"] %></b><%= methods["params"] %> -<% if methods["codeurl"] then %> -<a href="<%= methods["codeurl"] %>" target="source" class="srclink">src</a> -<% end %> -</a></td></tr> -</table> -<% if method_list["m_desc"] then %> -<div class="description"> -<%= method_list["m_desc"] %> -</div> -<% end %> -<% end %><%# method_list["methods"] %> -<% end %> -<% end %><%# sections["method_list"] %> -<% end %> -<% end %><%# values["sections"] %> -<% end %> - EOF - - SRC_PAGE = <<-EOF -<html> -<head><title><%= values["title"] %></title> -<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>"> -<style type="text/css"> - .kw { color: #3333FF; font-weight: bold } - .cmt { color: green; font-style: italic } - .str { color: #662222; font-style: italic } - .re { color: #662222; } -.ruby-comment { color: green; font-style: italic } -.ruby-constant { color: #4433aa; font-weight: bold; } -.ruby-identifier { color: #222222; } -.ruby-ivar { color: #2233dd; } -.ruby-keyword { color: #3333FF; font-weight: bold } -.ruby-node { color: #777777; } -.ruby-operator { color: #111111; } -.ruby-regexp { color: #662222; } -.ruby-value { color: #662222; font-style: italic } -</style> -</head> -<body bgcolor="#BBBBBB"> -<pre><%= values["code"] %></pre> -</body> -</html> - EOF - - FR_INDEX_BODY = %{ -<%= template_include %> -} - - FILE_INDEX = <<-EOF -<html> -<head> -<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>"> -<style type="text/css"> -<!-- - body { -background-color: #bbbbbb; - font-family: #{FONTS}; - font-size: 11px; - font-style: normal; - line-height: 14px; - color: #000040; - } -div.banner { - background: #bbbbcc; - color: white; - padding: 1; - margin: 0; - font-size: 90%; - font-weight: bold; - line-height: 1.1; - text-align: center; - width: 100%; -} - ---> -</style> -<base target="docwin"> -</head> -<body> -<div class="banner"><%= values["list_title"] %></div> -<% values["entries"].each do |entries| %> -<a href="<%= entries["href"] %>"><%= entries["name"] %></a><br /> -<% end %><%# values["entries"] %> -</body></html> - EOF - - CLASS_INDEX = FILE_INDEX - METHOD_INDEX = FILE_INDEX - - INDEX = <<-EOF -<html> -<head> - <title><%= values["title"] %></title> - <meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>"> -</head> - -<frameset cols="20%,*"> - <frameset rows="15%,35%,50%"> - <frame src="fr_file_index.html" title="Files" name="Files"> - <frame src="fr_class_index.html" name="Modules"> - <frame src="fr_method_index.html" name="Subroutines and Functions"> - </frameset> - <frameset rows="80%,20%"> - <frame src="<%= values["initial_page"] %>" name="docwin"> - <frame src="blank.html" name="source"> - </frameset> - <noframes> - <body bgcolor="#BBBBBB"> - Click <a href="html/index.html">here</a> for a non-frames - version of this page. - </body> - </noframes> -</frameset> - -</html> - EOF - - # Blank page to use as a target - BLANK = %{ -<html><body bgcolor="#BBBBBB"></body></html> -} - - def write_extra_pages - template = TemplatePage.new(BLANK) - File.open("blank.html", "w") { |f| template.write_html_on(f, {}) } - end - -end - diff --git a/trunk/lib/rdoc/generator/html/html.rb b/trunk/lib/rdoc/generator/html/html.rb deleted file mode 100644 index 1ab90c6264..0000000000 --- a/trunk/lib/rdoc/generator/html/html.rb +++ /dev/null @@ -1,698 +0,0 @@ -require 'rdoc/generator/html' -require 'rdoc/generator/html/one_page_html' - -## -# = CSS2 RDoc HTML template -# -# This is a template for RDoc that uses XHTML 1.0 Transitional and dictates a -# bit more of the appearance of the output to cascading stylesheets than the -# default. It was designed for clean inline code display, and uses DHTMl to -# toggle the visibility of each method's source with each click on the -# '[source]' link. -# -# == Authors -# -# * Michael Granger <ged@FaerieMUD.org> -# -# Copyright (c) 2002, 2003 The FaerieMUD Consortium. Some rights reserved. -# -# This work is licensed under the Creative Commons Attribution License. To -# view a copy of this license, visit -# http://creativecommons.org/licenses/by/1.0/ or send a letter to Creative -# Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. - -module RDoc::Generator::HTML::HTML - - FONTS = "Verdana,Arial,Helvetica,sans-serif" - - STYLE = <<-EOF -body { - font-family: Verdana,Arial,Helvetica,sans-serif; - font-size: 90%; - margin: 0; - margin-left: 40px; - padding: 0; - background: white; -} - -h1,h2,h3,h4 { margin: 0; color: #efefef; background: transparent; } -h1 { font-size: 150%; } -h2,h3,h4 { margin-top: 1em; } - -a { background: #eef; color: #039; text-decoration: none; } -a:hover { background: #039; color: #eef; } - -/* Override the base stylesheet's Anchor inside a table cell */ -td > a { - background: transparent; - color: #039; - text-decoration: none; -} - -/* and inside a section title */ -.section-title > a { - background: transparent; - color: #eee; - text-decoration: none; -} - -/* === Structural elements =================================== */ - -div#index { - margin: 0; - margin-left: -40px; - padding: 0; - font-size: 90%; -} - - -div#index a { - margin-left: 0.7em; -} - -div#index .section-bar { - margin-left: 0px; - padding-left: 0.7em; - background: #ccc; - font-size: small; -} - - -div#classHeader, div#fileHeader { - width: auto; - color: white; - padding: 0.5em 1.5em 0.5em 1.5em; - margin: 0; - margin-left: -40px; - border-bottom: 3px solid #006; -} - -div#classHeader a, div#fileHeader a { - background: inherit; - color: white; -} - -div#classHeader td, div#fileHeader td { - background: inherit; - color: white; -} - - -div#fileHeader { - background: #057; -} - -div#classHeader { - background: #048; -} - - -.class-name-in-header { - font-size: 180%; - font-weight: bold; -} - - -div#bodyContent { - padding: 0 1.5em 0 1.5em; -} - -div#description { - padding: 0.5em 1.5em; - background: #efefef; - border: 1px dotted #999; -} - -div#description h1,h2,h3,h4,h5,h6 { - color: #125;; - background: transparent; -} - -div#validator-badges { - text-align: center; -} -div#validator-badges img { border: 0; } - -div#copyright { - color: #333; - background: #efefef; - font: 0.75em sans-serif; - margin-top: 5em; - margin-bottom: 0; - padding: 0.5em 2em; -} - - -/* === Classes =================================== */ - -table.header-table { - color: white; - font-size: small; -} - -.type-note { - font-size: small; - color: #DEDEDE; -} - -.xxsection-bar { - background: #eee; - color: #333; - padding: 3px; -} - -.section-bar { - color: #333; - border-bottom: 1px solid #999; - margin-left: -20px; -} - - -.section-title { - background: #79a; - color: #eee; - padding: 3px; - margin-top: 2em; - margin-left: -30px; - border: 1px solid #999; -} - -.top-aligned-row { vertical-align: top } -.bottom-aligned-row { vertical-align: bottom } - -/* --- Context section classes ----------------------- */ - -.context-row { } -.context-item-name { font-family: monospace; font-weight: bold; color: black; } -.context-item-value { font-size: small; color: #448; } -.context-item-desc { color: #333; padding-left: 2em; } - -/* --- Method classes -------------------------- */ -.method-detail { - background: #efefef; - padding: 0; - margin-top: 0.5em; - margin-bottom: 1em; - border: 1px dotted #ccc; -} -.method-heading { - color: black; - background: #ccc; - border-bottom: 1px solid #666; - padding: 0.2em 0.5em 0 0.5em; -} -.method-signature { color: black; background: inherit; } -.method-name { font-weight: bold; } -.method-args { font-style: italic; } -.method-description { padding: 0 0.5em 0 0.5em; } - -/* --- Source code sections -------------------- */ - -a.source-toggle { font-size: 90%; } -div.method-source-code { - background: #262626; - color: #ffdead; - margin: 1em; - padding: 0.5em; - border: 1px dashed #999; - overflow: hidden; -} - -div.method-source-code pre { color: #ffdead; overflow: hidden; } - -/* --- Ruby keyword styles --------------------- */ - -.standalone-code { background: #221111; color: #ffdead; overflow: hidden; } - -.ruby-constant { color: #7fffd4; background: transparent; } -.ruby-keyword { color: #00ffff; background: transparent; } -.ruby-ivar { color: #eedd82; background: transparent; } -.ruby-operator { color: #00ffee; background: transparent; } -.ruby-identifier { color: #ffdead; background: transparent; } -.ruby-node { color: #ffa07a; background: transparent; } -.ruby-comment { color: #b22222; font-weight: bold; background: transparent; } -.ruby-regexp { color: #ffa07a; background: transparent; } -.ruby-value { color: #7fffd4; background: transparent; } -EOF - - -##################################################################### -### H E A D E R T E M P L A T E -##################################################################### - - XHTML_PREAMBLE = <<-EOF -<?xml version="1.0" encoding="<%= values["charset"] %>"?> -<!DOCTYPE html - PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> - EOF - - HEADER = XHTML_PREAMBLE + <<-EOF -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> -<head> - <title><%= values["title"] %></title> - <meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" /> - <meta http-equiv="Content-Script-Type" content="text/javascript" /> - <link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" media="screen" /> - <script type="text/javascript"> - // <![CDATA[ - - function popupCode( url ) { - window.open(url, "Code", "resizable=yes,scrollbars=yes,toolbar=no,status=no,height=150,width=400") - } - - function toggleCode( id ) { - if ( document.getElementById ) - elem = document.getElementById( id ); - else if ( document.all ) - elem = eval( "document.all." + id ); - else - return false; - - elemStyle = elem.style; - - if ( elemStyle.display != "block" ) { - elemStyle.display = "block" - } else { - elemStyle.display = "none" - } - - return true; - } - - // Make codeblocks hidden by default - document.writeln( "<style type=\\"text/css\\">div.method-source-code { display: none }</style>" ) - - // ]]> - </script> - -</head> -<body> -EOF - -##################################################################### -### C O N T E X T C O N T E N T T E M P L A T E -##################################################################### - - CONTEXT_CONTENT = %{ -} - -##################################################################### -### F O O T E R T E M P L A T E -##################################################################### - - FOOTER = <<-EOF -<div id="validator-badges"> - <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p> -</div> - -</body> -</html> - EOF - - -##################################################################### -### F I L E P A G E H E A D E R T E M P L A T E -##################################################################### - - FILE_PAGE = <<-EOF - <div id="fileHeader"> - <h1><%= values["short_name"] %></h1> - <table class="header-table"> - <tr class="top-aligned-row"> - <td><strong>Path:</strong></td> - <td><%= values["full_path"] %> -<% if values["cvsurl"] then %> - (<a href="<%= values["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>) -<% end %> - </td> - </tr> - <tr class="top-aligned-row"> - <td><strong>Last Update:</strong></td> - <td><%= values["dtm_modified"] %></td> - </tr> - </table> - </div> - EOF - -##################################################################### -### C L A S S P A G E H E A D E R T E M P L A T E -##################################################################### - - CLASS_PAGE = <<-EOF - <div id="classHeader"> - <table class="header-table"> - <tr class="top-aligned-row"> - <td><strong><%= values["classmod"] %></strong></td> - <td class="class-name-in-header"><%= values["full_name"] %></td> - </tr> - <tr class="top-aligned-row"> - <td><strong>In:</strong></td> - <td> -<% values["infiles"].each do |infiles| %> -<% if infiles["full_path_url"] then %> - <a href="<%= infiles["full_path_url"] %>"> -<% end %> - <%= infiles["full_path"] %> -<% if infiles["full_path_url"] then %> - </a> -<% end %> -<% if infiles["cvsurl"] then %> - (<a href="<%= infiles["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>) -<% end %> - <br /> -<% end %><%# values["infiles"] %> - </td> - </tr> - -<% if values["parent"] then %> - <tr class="top-aligned-row"> - <td><strong>Parent:</strong></td> - <td> -<% if values["par_url"] then %> - <a href="<%= values["par_url"] %>"> -<% end %> - <%= values["parent"] %> -<% if values["par_url"] then %> - </a> -<% end %> - </td> - </tr> -<% end %> - </table> - </div> - EOF - -##################################################################### -### M E T H O D L I S T T E M P L A T E -##################################################################### - - METHOD_LIST = <<-EOF - <div id="contextContent"> -<% if values["diagram"] then %> - <div id="diagram"> - <%= values["diagram"] %> - </div> -<% end - - if values["description"] then %> - <div id="description"> - <%= values["description"] %> - </div> -<% end - - if values["requires"] then %> - <div id="requires-list"> - <h3 class="section-bar">Required files</h3> - - <div class="name-list"> -<% values["requires"].each do |requires| %> - <%= href requires["aref"], requires["name"] %> -<% end %><%# values["requires"] %> - </div> - </div> -<% end - - if values["toc"] then %> - <div id="contents-list"> - <h3 class="section-bar">Contents</h3> - <ul> -<% values["toc"].each do |toc| %> - <li><a href="#<%= toc["href"] %>"><%= toc["secname"] %></a></li> -<% end %><%# values["toc"] %> - </ul> -<% end %> - </div> - -<% if values["methods"] then %> - <div id="method-list"> - <h3 class="section-bar">Methods</h3> - - <div class="name-list"> -<% values["methods"].each do |methods| %> - <%= href methods["aref"], methods["name"] %> -<% end %><%# values["methods"] %> - </div> - </div> -<% end %> - </div> - - <!-- if includes --> -<% if values["includes"] then %> - <div id="includes"> - <h3 class="section-bar">Included Modules</h3> - - <div id="includes-list"> -<% values["includes"].each do |includes| %> - <span class="include-name"><%= href includes["aref"], includes["name"] %></span> -<% end %><%# values["includes"] %> - </div> - </div> -<% end - - values["sections"].each do |sections| %> - <div id="section"> -<% if sections["sectitle"] then %> - <h2 class="section-title"><a name="<%= sections["secsequence"] %>"><%= sections["sectitle"] %></a></h2> -<% if sections["seccomment"] then %> - <div class="section-comment"> - <%= sections["seccomment"] %> - </div> -<% end - end - - if sections["classlist"] then %> - <div id="class-list"> - <h3 class="section-bar">Classes and Modules</h3> - - <%= sections["classlist"] %> - </div> -<% end - - if sections["constants"] then %> - <div id="constants-list"> - <h3 class="section-bar">Constants</h3> - - <div class="name-list"> - <table summary="Constants"> -<% sections["constants"].each do |constants| %> - <tr class="top-aligned-row context-row"> - <td class="context-item-name"><%= constants["name"] %></td> - <td>=</td> - <td class="context-item-value"><%= constants["value"] %></td> -<% if sections["desc"] then %> - <td width="3em"> </td> - <td class="context-item-desc"><%= constants["desc"] %></td> -<% end %> - </tr> -<% end %><%# sections["constants"] %> - </table> - </div> - </div> -<% end - - if sections["aliases"] then %> - <div id="aliases-list"> - <h3 class="section-bar">External Aliases</h3> - - <div class="name-list"> - <table summary="aliases"> -<% sections["aliases"].each do |aliases| %> - <tr class="top-aligned-row context-row"> - <td class="context-item-name"><%= aliases["old_name"] %></td> - <td>-></td> - <td class="context-item-value"><%= aliases["new_name"] %></td> - </tr> -<% if aliases["desc"] then %> - <tr class="top-aligned-row context-row"> - <td> </td> - <td colspan="2" class="context-item-desc"><%= aliases["desc"] %></td> - </tr> -<% end - end %><%# sections["aliases"] %> - </table> - </div> - </div> -<% end %> - -<% if sections["attributes"] then %> - <div id="attribute-list"> - <h3 class="section-bar">Attributes</h3> - - <div class="name-list"> - <table> -<% sections["attributes"].each do |attribute| %> - <tr class="top-aligned-row context-row"> - <td class="context-item-name"><%= attribute["name"] %></td> -<% if attribute["rw"] then %> - <td class="context-item-value"> [<%= attribute["rw"] %>] </td> -<% end - unless attribute["rw"] then %> - <td class="context-item-value"> </td> -<% end %> - <td class="context-item-desc"><%= attribute["a_desc"] %></td> - </tr> -<% end %><%# sections["attributes"] %> - </table> - </div> - </div> -<% end %> - - <!-- if method_list --> -<% if sections["method_list"] then %> - <div id="methods"> -<% sections["method_list"].each do |method_list| - if method_list["methods"] then %> - <h3 class="section-bar"><%= method_list["type"] %> <%= method_list["category"] %> methods</h3> - -<% method_list["methods"].each do |methods| %> - <div id="method-<%= methods["aref"] %>" class="method-detail"> - <a name="<%= methods["aref"] %>"></a> - - <div class="method-heading"> -<% if methods["codeurl"] then %> - <a href="<%= methods["codeurl"] %>" target="Code" class="method-signature" - onclick="popupCode('<%= methods["codeurl"] %>');return false;"> -<% end - if methods["sourcecode"] then %> - <a href="#<%= methods["aref"] %>" class="method-signature"> -<% end - if methods["callseq"] then %> - <span class="method-name"><%= methods["callseq"] %></span> -<% end - unless methods["callseq"] then %> - <span class="method-name"><%= methods["name"] %></span><span class="method-args"><%= methods["params"] %></span> -<% end - if methods["codeurl"] then %> - </a> -<% end - if methods["sourcecode"] then %> - </a> -<% end %> - </div> - - <div class="method-description"> -<% if methods["m_desc"] then %> - <%= methods["m_desc"] %> -<% end - if methods["sourcecode"] then %> - <p><a class="source-toggle" href="#" - onclick="toggleCode('<%= methods["aref"] %>-source');return false;">[Source]</a></p> - <div class="method-source-code" id="<%= methods["aref"] %>-source"> -<pre> -<%= methods["sourcecode"] %> -</pre> - </div> -<% end %> - </div> - </div> - -<% end %><%# method_list["methods"] %><% - end - end %><%# sections["method_list"] %> - - </div> -<% end %> -<% end %><%# values["sections"] %> - EOF - -##################################################################### -### B O D Y T E M P L A T E -##################################################################### - - BODY = HEADER + %{ - -<%= template_include %> <!-- banner header --> - - <div id="bodyContent"> - -} + METHOD_LIST + %{ - - </div> - -} + FOOTER - -##################################################################### -### S O U R C E C O D E T E M P L A T E -##################################################################### - - SRC_PAGE = XHTML_PREAMBLE + <<-EOF -<html> -<head> - <title><%= values["title"] %></title> - <meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" /> - <link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" media="screen" /> -</head> -<body class="standalone-code"> - <pre><%= values["code"] %></pre> -</body> -</html> - EOF - - -##################################################################### -### I N D E X F I L E T E M P L A T E S -##################################################################### - - FR_INDEX_BODY = %{ -<%= template_include %> -} - - FILE_INDEX = XHTML_PREAMBLE + <<-EOF -<!-- - - <%= values["list_title"] %> - - --> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> -<head> - <title><%= values["list_title"] %></title> - <meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" /> - <link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" /> - <base target="docwin" /> -</head> -<body> -<div id="index"> - <h1 class="section-bar"><%= values["list_title"] %></h1> - <div id="index-entries"> -<% values["entries"].each do |entries| %> - <a href="<%= entries["href"] %>"><%= entries["name"] %></a><br /> -<% end %><%# values["entries"] %> - </div> -</div> -</body> -</html> - EOF - - CLASS_INDEX = FILE_INDEX - METHOD_INDEX = FILE_INDEX - - INDEX = <<-EOF -<?xml version="1.0" encoding="<%= values["charset"] %>"?> -<!DOCTYPE html - PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"> - -<!-- - - <%= values["title"] %> - - --> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> -<head> - <title><%= values["title"] %></title> - <meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" /> -</head> -<frameset rows="20%, 80%"> - <frameset cols="25%,35%,45%"> - <frame src="fr_file_index.html" title="Files" name="Files" /> - <frame src="fr_class_index.html" name="Classes" /> - <frame src="fr_method_index.html" name="Methods" /> - </frameset> - <frame src="<%= values["initial_page"] %>" name="docwin" /> -</frameset> -</html> - EOF - -end - diff --git a/trunk/lib/rdoc/generator/html/kilmer.rb b/trunk/lib/rdoc/generator/html/kilmer.rb deleted file mode 100644 index 6479abaf8b..0000000000 --- a/trunk/lib/rdoc/generator/html/kilmer.rb +++ /dev/null @@ -1,418 +0,0 @@ -require 'rdoc/generator/html' - -module RDoc::Generator::HTML::KILMER - - FONTS = "Verdana, Arial, Helvetica, sans-serif" - - STYLE = <<-EOF -body,td,p { font-family: <%= values["fonts"] %>; - color: #000040; -} - -.attr-rw { font-size: xx-small; color: #444488 } - -.title-row { background-color: #CCCCFF; - color: #000010; -} - -.big-title-font { - color: black; - font-weight: bold; - font-family: <%= values["fonts"] %>; - font-size: large; - height: 60px; - padding: 10px 3px 10px 3px; -} - -.small-title-font { color: black; - font-family: <%= values["fonts"] %>; - font-size:10; } - -.aqua { color: black } - -.method-name, .attr-name { - font-family: font-family: <%= values["fonts"] %>; - font-weight: bold; - font-size: small; - margin-left: 20px; - color: #000033; -} - -.tablesubtitle, .tablesubsubtitle { - width: 100%; - margin-top: 1ex; - margin-bottom: .5ex; - padding: 5px 0px 5px 3px; - font-size: large; - color: black; - background-color: #CCCCFF; - border: thin; -} - -.name-list { - margin-left: 5px; - margin-bottom: 2ex; - line-height: 105%; -} - -.description { - margin-left: 5px; - margin-bottom: 2ex; - line-height: 105%; - font-size: small; -} - -.methodtitle { - font-size: small; - font-weight: bold; - text-decoration: none; - color: #000033; - background-color: white; -} - -.srclink { - font-size: small; - font-weight: bold; - text-decoration: none; - color: #0000DD; - background-color: white; -} - -.paramsig { - font-size: small; -} - -.srcbut { float: right } - EOF - - BODY = <<-EOF -<html><head> - <title><%= values["title"] %></title> - <meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>"> - <link rel="stylesheet" href="<%= values["style_url"] %>" type="text/css" media="screen" /> - <script type="text/javascript" language="JavaScript"> - <!-- - function popCode(url) { - parent.frames.source.location = url - } - //--> - </script> -</head> -<body bgcolor="white"> - -<%= template_include %> <!-- banner header --> - -<% if values["diagram"] then %> -<table width="100%"><tr><td align="center"> -<%= values["diagram"] %> -</td></tr></table> -<% end %> - -<% if values["description"] then %> -<div class="description"><%= values["description"] %></div> -<% end %> - -<% if values["requires"] then %> -<table cellpadding="5" width="100%"> -<tr><td class="tablesubtitle">Required files</td></tr> -</table><br /> -<div class="name-list"> -<% values["requires"].each do |requires| %> -<%= href requires["aref"], requires["name"] %> -<% end %><%# values["requires"] %> -<% end %> -</div> - -<% if values["methods"] then %> -<table cellpadding="5" width="100%"> -<tr><td class="tablesubtitle">Methods</td></tr> -</table><br /> -<div class="name-list"> -<% values["methods"].each do |methods| %> -<%= href methods["aref"], methods["name"] %>, -<% end %><%# values["methods"] %> -</div> -<% end %> - - -<% values["sections"].each do |sections| %> - <div id="section"> -<% if sections["sectitle"] then %> - <h2 class="section-title"><a name="<%= sections["secsequence"] %>"><%= sections["sectitle"] %></a></h2> -<% if sections["seccomment"] then %> - <div class="section-comment"> - <%= sections["seccomment"] %> - </div> -<% end %> -<% end %> - -<% if sections["attributes"] then %> -<table cellpadding="5" width="100%"> -<tr><td class="tablesubtitle">Attributes</td></tr> -</table><br /> -<table cellspacing="5"> -<% sections["attributes"].each do |attributes| %> - <tr valign="top"> -<% if attributes["rw"] then %> - <td align="center" class="attr-rw"> [<%= attributes["rw"] %>] </td> -<% end %> -<% unless attributes["rw"] then %> - <td></td> -<% end %> - <td class="attr-name"><%= attributes["name"] %></td> - <td><%= attributes["a_desc"] %></td> - </tr> -<% end %><%# sections["attributes"] %> -</table> -<% end %> - -<% if sections["classlist"] then %> -<table cellpadding="5" width="100%"> -<tr><td class="tablesubtitle">Classes and Modules</td></tr> -</table><br /> -<%= sections["classlist"] %><br /> -<% end %> - - <%= template_include %> <!-- method descriptions --> - -<% end %><%# values["sections"] %> - -</body> -</html> - EOF - - FILE_PAGE = <<-EOF -<table width="100%"> - <tr class="title-row"> - <td><table width="100%"><tr> - <td class="big-title-font" colspan="2"><font size="-3"><b>File</b><br /></font><%= values["short_name"] %></td> - <td align="right"><table cellspacing="0" cellpadding="2"> - <tr> - <td class="small-title-font">Path:</td> - <td class="small-title-font"><%= values["full_path"] %> -<% if values["cvsurl"] then %> - (<a href="<%= values["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>) -<% end %> - </td> - </tr> - <tr> - <td class="small-title-font">Modified:</td> - <td class="small-title-font"><%= values["dtm_modified"] %></td> - </tr> - </table> - </td></tr></table></td> - </tr> -</table><br /> - EOF - - CLASS_PAGE = <<-EOF -<table width="100%" border="0" cellspacing="0"> - <tr class="title-row"> - <td class="big-title-font"> - <font size="-3"><b><%= values["classmod"] %></b><br /></font><%= values["full_name"] %> - </td> - <td align="right"> - <table cellspacing="0" cellpadding="2"> - <tr valign="top"> - <td class="small-title-font">In:</td> - <td class="small-title-font"> -<% values["infiles"].each do |infiles| %> -<%= href infiles["full_path_url"], infiles["full_path"] %> -<% if infiles["cvsurl"] then %> - (<a href="<%= infiles["cvsurl"] %>"><acronym title="Concurrent Versioning System">CVS</acronym></a>) -<% end %> -<% end %><%# values["infiles"] %> - </td> - </tr> -<% if values["parent"] then %> - <tr> - <td class="small-title-font">Parent:</td> - <td class="small-title-font"> -<% if values["par_url"] then %> - <a href="<%= values["par_url"] %>" class="cyan"> -<% end %> -<%= values["parent"] %> -<% if values["par_url"] then %> - </a> -<% end %> - </td> - </tr> -<% end %> - </table> - </td> - </tr> -</table><br /> - EOF - - METHOD_LIST = <<-EOF -<% if values["includes"] then %> -<div class="tablesubsubtitle">Included modules</div><br /> -<div class="name-list"> -<% values["includes"].each do |includes| %> - <span class="method-name"><%= href includes["aref"], includes["name"] %></span> -<% end %><%# values["includes"] %> -</div> -<% end %> - -<% if values["method_list"] then %> -<% values["method_list"].each do |method_list| $stderr.puts({ :method_list => method_list }.inspect) %> -<% if values["methods"] then %> -<table cellpadding=5 width="100%"> -<tr><td class="tablesubtitle"><%= values["type"] %> <%= values["category"] %> methods</td></tr> -</table> -<% values["methods"].each do |methods| $stderr.puts({ :methods => methods }.inspect) %> -<table width="100%" cellspacing="0" cellpadding="5" border="0"> -<tr><td class="methodtitle"> -<a name="<%= values["aref"] %>"> -<% if values["callseq"] then %> -<b><%= values["callseq"] %></b> -<% end %> -<% unless values["callseq"] then %> - <b><%= values["name"] %></b><%= values["params"] %> -<% end %> -<% if values["codeurl"] then %> -<a href="<%= values["codeurl"] %>" target="source" class="srclink">src</a> -<% end %> -</a></td></tr> -</table> -<% if values["m_desc"] then %> -<div class="description"> -<%= values["m_desc"] %> -</div> -<% end %> -<% if values["aka"] then %> -<div class="aka"> -This method is also aliased as -<% values["aka"].each do |aka| $stderr.puts({ :aka => aka }.inspect) %> -<a href="<%= values["aref"] %>"><%= values["name"] %></a> -<% end %><%# values["aka"] %> -</div> -<% end %> -<% if values["sourcecode"] then %> -<pre class="source"> -<%= values["sourcecode"] %> -</pre> -<% end %> -<% end %><%# values["methods"] %> -<% end %> -<% end %><%# values["method_list"] %> -<% end %> - EOF - - SRC_PAGE = <<-EOF -<html> -<head><title><%= values["title"] %></title> -<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>"> -<style type="text/css"> -.ruby-comment { color: green; font-style: italic } -.ruby-constant { color: #4433aa; font-weight: bold; } -.ruby-identifier { color: #222222; } -.ruby-ivar { color: #2233dd; } -.ruby-keyword { color: #3333FF; font-weight: bold } -.ruby-node { color: #777777; } -.ruby-operator { color: #111111; } -.ruby-regexp { color: #662222; } -.ruby-value { color: #662222; font-style: italic } - .kw { color: #3333FF; font-weight: bold } - .cmt { color: green; font-style: italic } - .str { color: #662222; font-style: italic } - .re { color: #662222; } -</style> -</head> -<body bgcolor="white"> -<pre><%= values["code"] %></pre> -</body> -</html> - EOF - - FR_INDEX_BODY = %{ -<%= template_include %> -} - - FILE_INDEX = <<-EOF -<html> -<head> -<meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>"> -<style> -<!-- -body { - background-color: #ddddff; - font-family: #{FONTS}; - font-size: 11px; - font-style: normal; - line-height: 14px; - color: #000040; -} - -div.banner { - background: #0000aa; - color: white; - padding: 1; - margin: 0; - font-size: 90%; - font-weight: bold; - line-height: 1.1; - text-align: center; - width: 100%; -} - ---> -</style> -<base target="docwin"> -</head> -<body> -<div class="banner"><%= values["list_title"] %></div> -<% values["entries"].each do |entries| %> -<a href="<%= entries["href"] %>"><%= entries["name"] %></a><br /> -<% end %><%# values["entries"] %> -</body></html> - EOF - - CLASS_INDEX = FILE_INDEX - METHOD_INDEX = FILE_INDEX - - INDEX = <<-EOF -<html> -<head> - <title><%= values["title"] %></title> - <meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>"> -</head> - -<frameset cols="20%,*"> - <frameset rows="15%,35%,50%"> - <frame src="fr_file_index.html" title="Files" name="Files"> - <frame src="fr_class_index.html" name="Classes"> - <frame src="fr_method_index.html" name="Methods"> - </frameset> -<% if values["inline_source"] then %> - <frame src="<%= values["initial_page"] %>" name="docwin"> -<% end %> -<% unless values["inline_source"] then %> - <frameset rows="80%,20%"> - <frame src="<%= values["initial_page"] %>" name="docwin"> - <frame src="blank.html" name="source"> - </frameset> -<% end %> - <noframes> - <body bgcolor="white"> - Click <a href="html/index.html">here</a> for a non-frames - version of this page. - </body> - </noframes> -</frameset> - -</html> - EOF - - # A blank page to use as a target - BLANK = %{ -<html><body bgcolor="white"></body></html> -} - - def write_extra_pages - template = TemplatePage.new(BLANK) - File.open("blank.html", "w") { |f| template.write_html_on(f, {}) } - end - -end - diff --git a/trunk/lib/rdoc/generator/html/one_page_html.rb b/trunk/lib/rdoc/generator/html/one_page_html.rb deleted file mode 100644 index c4dd95529d..0000000000 --- a/trunk/lib/rdoc/generator/html/one_page_html.rb +++ /dev/null @@ -1,121 +0,0 @@ -require 'rdoc/generator/html' - -module RDoc::Generator::HTML::ONE_PAGE_HTML - - CONTENTS_XML = <<-EOF -<% if defined? classes and classes["description"] then %> -<%= classes["description"] %> -<% end %> - -<% if defined? files and files["requires"] then %> -<h4>Requires:</h4> -<ul> -<% files["requires"].each do |requires| %> -<% if requires["aref"] then %> -<li><a href="<%= requires["aref"] %>"><%= requires["name"] %></a></li> -<% end %> -<% unless requires["aref"] then %> -<li><%= requires["name"] %></li> -<% end %> -<% end %><%# files["requires"] %> -</ul> -<% end %> - -<% if defined? classes and classes["includes"] then %> -<h4>Includes</h4> -<ul> -<% classes["includes"].each do |includes| %> -<% if includes["aref"] then %> -<li><a href="<%= includes["aref"] %>"><%= includes["name"] %></a></li> -<% end %> -<% unless includes["aref"] then %> -<li><%= includes["name"] %></li> -<% end %> -<% end %><%# classes["includes"] %> -</ul> -<% end %> - -<% if defined? classes and classes["sections"] then %> -<% classes["sections"].each do |sections| %> -<% if sections["attributes"] then %> -<h4>Attributes</h4> -<table> -<% sections["attributes"].each do |attributes| %> -<tr><td><%= attributes["name"] %></td><td><%= attributes["rw"] %></td><td><%= attributes["a_desc"] %></td></tr> -<% end %><%# sections["attributes"] %> -</table> -<% end %> - -<% if sections["method_list"] then %> -<h3>Methods</h3> -<% sections["method_list"].each do |method_list| %> -<% if method_list["methods"] then %> -<% method_list["methods"].each do |methods| %> -<h4><%= methods["type"] %> <%= methods["category"] %> method: -<% if methods["callseq"] then %> -<a name="<%= methods["aref"] %>"><%= methods["callseq"] %></a> -<% end %> -<% unless methods["callseq"] then %> -<a name="<%= methods["aref"] %>"><%= methods["name"] %><%= methods["params"] %></a></h4> -<% end %> - -<% if methods["m_desc"] then %> -<%= methods["m_desc"] %> -<% end %> - -<% if methods["sourcecode"] then %> -<blockquote><pre> -<%= methods["sourcecode"] %> -</pre></blockquote> -<% end %> -<% end %><%# method_list["methods"] %> -<% end %> -<% end %><%# sections["method_list"] %> -<% end %> -<% end %><%# classes["sections"] %> -<% end %> - EOF - - ONE_PAGE = %{ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<html> -<head> - <title><%= values["title"] %></title> - <meta http-equiv="Content-Type" content="text/html; charset=<%= values["charset"] %>" /> -</head> -<body> -<% values["files"].each do |files| %> -<h2>File: <%= files["short_name"] %></h2> -<table> - <tr><td>Path:</td><td><%= files["full_path"] %></td></tr> - <tr><td>Modified:</td><td><%= files["dtm_modified"] %></td></tr> -</table> -} + CONTENTS_XML + %{ -<% end %><%# values["files"] %> - -<% if values["classes"] then %> -<h2>Classes</h2> -<% values["classes"].each do |classes| %> -<% if classes["parent"] then %> -<h3><%= classes["classmod"] %> <%= classes["full_name"] %> < <%= href classes["par_url"], classes["parent"] %></h3> -<% end %> -<% unless classes["parent"] then %> -<h3><%= classes["classmod"] %> <%= classes["full_name"] %></h3> -<% end %> - -<% if classes["infiles"] then %> -(in files -<% classes["infiles"].each do |infiles| %> -<%= href infiles["full_path_url"], infiles["full_path"] %> -<% end %><%# classes["infiles"] %> -) -<% end %> -} + CONTENTS_XML + %{ -<% end %><%# values["classes"] %> -<% end %> -</body> -</html> -} - -end - diff --git a/trunk/lib/rdoc/generator/ri.rb b/trunk/lib/rdoc/generator/ri.rb deleted file mode 100644 index 6b7a5932f8..0000000000 --- a/trunk/lib/rdoc/generator/ri.rb +++ /dev/null @@ -1,226 +0,0 @@ -require 'rdoc/generator' -require 'rdoc/markup/to_flow' - -require 'rdoc/ri/cache' -require 'rdoc/ri/reader' -require 'rdoc/ri/writer' -require 'rdoc/ri/descriptions' - -class RDoc::Generator::RI - - ## - # Generator may need to return specific subclasses depending on the - # options they are passed. Because of this we create them using a factory - - def self.for(options) - new(options) - end - - ## - # Set up a new ri generator - - def initialize(options) #:not-new: - @options = options - @ri_writer = RDoc::RI::Writer.new "." - @markup = RDoc::Markup.new - @to_flow = RDoc::Markup::ToFlow.new - - @generated = {} - end - - ## - # Build the initial indices and output objects based on an array of - # TopLevel objects containing the extracted information. - - def generate(toplevels) - RDoc::TopLevel.all_classes_and_modules.each do |cls| - process_class cls - end - end - - def process_class(from_class) - generate_class_info(from_class) - - # now recurse into this class' constituent classes - from_class.each_classmodule do |mod| - process_class(mod) - end - end - - def generate_class_info(cls) - case cls - when RDoc::NormalModule then - cls_desc = RDoc::RI::ModuleDescription.new - else - cls_desc = RDoc::RI::ClassDescription.new - cls_desc.superclass = cls.superclass - end - - cls_desc.name = cls.name - cls_desc.full_name = cls.full_name - cls_desc.comment = markup(cls.comment) - - cls_desc.attributes = cls.attributes.sort.map do |a| - RDoc::RI::Attribute.new(a.name, a.rw, markup(a.comment)) - end - - cls_desc.constants = cls.constants.map do |c| - RDoc::RI::Constant.new(c.name, c.value, markup(c.comment)) - end - - cls_desc.includes = cls.includes.map do |i| - RDoc::RI::IncludedModule.new(i.name) - end - - class_methods, instance_methods = method_list(cls) - - cls_desc.class_methods = class_methods.map do |m| - RDoc::RI::MethodSummary.new(m.name) - end - - cls_desc.instance_methods = instance_methods.map do |m| - RDoc::RI::MethodSummary.new(m.name) - end - - update_or_replace(cls_desc) - - class_methods.each do |m| - generate_method_info(cls_desc, m) - end - - instance_methods.each do |m| - generate_method_info(cls_desc, m) - end - end - - def generate_method_info(cls_desc, method) - meth_desc = RDoc::RI::MethodDescription.new - meth_desc.name = method.name - meth_desc.full_name = cls_desc.full_name - if method.singleton - meth_desc.full_name += "::" - else - meth_desc.full_name += "#" - end - meth_desc.full_name << method.name - - meth_desc.comment = markup(method.comment) - meth_desc.params = params_of(method) - meth_desc.visibility = method.visibility.to_s - meth_desc.is_singleton = method.singleton - meth_desc.block_params = method.block_params - - meth_desc.aliases = method.aliases.map do |a| - RDoc::RI::AliasName.new(a.name) - end - - @ri_writer.add_method(cls_desc, meth_desc) - end - - private - - ## - # Returns a list of class and instance methods that we'll be documenting - - def method_list(cls) - list = cls.method_list - unless @options.show_all - list = list.find_all do |m| - m.visibility == :public || m.visibility == :protected || m.force_documentation - end - end - - c = [] - i = [] - list.sort.each do |m| - if m.singleton - c << m - else - i << m - end - end - return c,i - end - - def params_of(method) - if method.call_seq - method.call_seq - else - params = method.params || "" - - p = params.gsub(/\s*\#.*/, '') - p = p.tr("\n", " ").squeeze(" ") - p = "(" + p + ")" unless p[0] == ?( - - if (block = method.block_params) - block.gsub!(/\s*\#.*/, '') - block = block.tr("\n", " ").squeeze(" ") - if block[0] == ?( - block.sub!(/^\(/, '').sub!(/\)/, '') - end - p << " {|#{block.strip}| ...}" - end - p - end - end - - def markup(comment) - return nil if !comment || comment.empty? - - # Convert leading comment markers to spaces, but only - # if all non-blank lines have them - - if comment =~ /^(?>\s*)[^\#]/ - content = comment - else - content = comment.gsub(/^\s*(#+)/) { $1.tr('#',' ') } - end - @markup.convert(content, @to_flow) - end - - ## - # By default we replace existing classes with the same name. If the - # --merge option was given, we instead merge this definition into an - # existing class. We add our methods, aliases, etc to that class, but do - # not change the class's description. - - def update_or_replace(cls_desc) - old_cls = nil - - if @options.merge - rdr = RDoc::RI::Reader.new RDoc::RI::Cache.new(@options.op_dir) - - namespace = rdr.top_level_namespace - namespace = rdr.lookup_namespace_in(cls_desc.name, namespace) - if namespace.empty? - $stderr.puts "You asked me to merge this source into existing " - $stderr.puts "documentation. This file references a class or " - $stderr.puts "module called #{cls_desc.name} which I don't" - $stderr.puts "have existing documentation for." - $stderr.puts - $stderr.puts "Perhaps you need to generate its documentation first" - exit 1 - else - old_cls = namespace[0] - end - end - - prev_cls = @generated[cls_desc.full_name] - - if old_cls and not prev_cls then - old_desc = rdr.get_class old_cls - cls_desc.merge_in old_desc - end - - if prev_cls then - cls_desc.merge_in prev_cls - end - - @generated[cls_desc.full_name] = cls_desc - - @ri_writer.remove_class cls_desc - @ri_writer.add_class cls_desc - end - -end - diff --git a/trunk/lib/rdoc/generator/texinfo.rb b/trunk/lib/rdoc/generator/texinfo.rb deleted file mode 100644 index 0b79820228..0000000000 --- a/trunk/lib/rdoc/generator/texinfo.rb +++ /dev/null @@ -1,84 +0,0 @@ -require 'rdoc/rdoc' -require 'rdoc/generator' -require 'rdoc/markup/to_texinfo' - -module RDoc - RDoc::GENERATORS['texinfo'] = RDoc::Generator.new("rdoc/generator/texinfo", - :Texinfo, - 'texinfo') - module Generator - # This generates Texinfo files for viewing with GNU Info or Emacs - # from RDoc extracted from Ruby source files. - class Texinfo - # What should the .info file be named by default? - DEFAULT_INFO_FILENAME = 'rdoc.info' - - include Generator::MarkUp - - # Accept some options - def initialize(options) - @options = options - @options.inline_source = true - @options.op_name ||= 'rdoc.texinfo' - @options.formatter = ::RDoc::Markup::ToTexInfo.new - end - - # Generate the +texinfo+ files - def generate(toplevels) - @toplevels = toplevels - @files, @classes = ::RDoc::Generator::Context.build_indicies(@toplevels, - @options) - - (@files + @classes).each { |x| x.value_hash } - - open(@options.op_name, 'w') do |f| - f.puts TexinfoTemplate.new('files' => @files, - 'classes' => @classes, - 'filename' => @options.op_name.gsub(/texinfo/, 'info'), - 'title' => @options.title).render - end - # TODO: create info files and install? - end - - class << self - # Factory? We don't need no stinkin' factory! - alias_method :for, :new - end - end - - # Basically just a wrapper around ERB. - # Should probably use RDoc::TemplatePage instead - class TexinfoTemplate - BASE_DIR = ::File.expand_path(::File.dirname(__FILE__)) # have to calculate this when the file's loaded. - - def initialize(values, file = 'texinfo.erb') - @v, @file = [values, file] - end - - def template - ::File.read(::File.join(BASE_DIR, 'texinfo', @file)) - end - - # Go! - def render - ERB.new(template).result binding - end - - def href(location, text) - text # TODO: how does texinfo do hyperlinks? - end - - def target(name, text) - text # TODO: how do hyperlink targets work? - end - - # TODO: this is probably implemented elsewhere? - def method_prefix(section) - { 'Class' => '.', - 'Module' => '::', - 'Instance' => '#', - }[section['category']] - end - end - end -end diff --git a/trunk/lib/rdoc/generator/texinfo/class.texinfo.erb b/trunk/lib/rdoc/generator/texinfo/class.texinfo.erb deleted file mode 100644 index 07f17eaef2..0000000000 --- a/trunk/lib/rdoc/generator/texinfo/class.texinfo.erb +++ /dev/null @@ -1,44 +0,0 @@ -@node <%= @v['class']['full_name'].gsub(/::/, '-') %> -@chapter <%= @v['class']["classmod"] %> <%= @v['class']['full_name'] %> - -<% if @v['class']["parent"] and @v['class']['par_url'] %> -Inherits <%= href @v['class']["par_url"], @v['class']["parent"] %><% end %> - -<%= @v['class']["description"] %> - -<% if @v['class']["includes"] %> -Includes -<% @v['class']["includes"].each do |include| %> -* <%= href include["aref"], include["name"] %> -<% end # @v['class']["includes"] %> -<% end %> - -<% if @v['class']["sections"] %> -<% @v['class']["sections"].each do |section| %> -<% if section["attributes"] %> -Attributes -<% section["attributes"].each do |attributes| %> -* <%= attributes["name"] %> <%= attributes["rw"] %> <%= attributes["a_desc"] %> -<% end # section["attributes"] %> -<% end %> -<% end %> - -<% @v['class']["sections"].each do |section| %> -<% if section["method_list"] %> -Methods -@menu -<% section["method_list"].each_with_index do |method_list, i| %> -<%= i %> -<% (method_list["methods"] || []).each do |method| %> -* <%= @v['class']['full_name'].gsub(/::/, '-') %><%= method_prefix method_list %><%= method['name'] %>::<% end %> -<% end %> -@end menu - -<% section["method_list"].each do |method_list| %> -<% (method_list["methods"] || []).uniq.each do |method| %> -<%= TexinfoTemplate.new(@v.merge({'method' => method, 'list' => method_list}), - 'method.texinfo.erb').render %><% end %> -<% end # section["method_list"] %> -<% end %> -<% end # @v['class']["sections"] %> -<% end %> diff --git a/trunk/lib/rdoc/generator/texinfo/file.texinfo.erb b/trunk/lib/rdoc/generator/texinfo/file.texinfo.erb deleted file mode 100644 index b619b94bd2..0000000000 --- a/trunk/lib/rdoc/generator/texinfo/file.texinfo.erb +++ /dev/null @@ -1,6 +0,0 @@ -<% if false %> -<h2>File: <%= @v['file']["short_name"] %></h2> -Path: <%= @v['file']["full_path"] %> - -<%= TexinfoTemplate.new(@v, 'content.texinfo.erb').render %> -<% end %> diff --git a/trunk/lib/rdoc/generator/texinfo/method.texinfo.erb b/trunk/lib/rdoc/generator/texinfo/method.texinfo.erb deleted file mode 100644 index f5c2b73a4b..0000000000 --- a/trunk/lib/rdoc/generator/texinfo/method.texinfo.erb +++ /dev/null @@ -1,6 +0,0 @@ -@node <%= @v['class']['full_name'].gsub(/::/, '-') %><%= method_prefix @v['list'] %><%= @v['method']['name'] %> -@section <%= @v['class']["classmod"] %> <%= @v['class']['full_name'] %><%= method_prefix @v['list'] %><%= @v['method']['name'] %> -<%= @v['method']["type"] %> <%= @v['method']["category"] %> method: -<%= target @v['method']["aref"], @v['method']['callseq'] || - @v['method']["name"] + @v['method']["params"] %> -<%= @v['method']["m_desc"] %> diff --git a/trunk/lib/rdoc/generator/texinfo/texinfo.erb b/trunk/lib/rdoc/generator/texinfo/texinfo.erb deleted file mode 100644 index 235f63d73c..0000000000 --- a/trunk/lib/rdoc/generator/texinfo/texinfo.erb +++ /dev/null @@ -1,28 +0,0 @@ -\input texinfo @c -*-texinfo-*- -@c %**start of header -@setfilename <%= @v['filename'] %> -@settitle <%= @v['title'] %> -@c %**end of header - -@contents @c TODO: whitespace is a mess... =\ - -@ifnottex -@node Top - -@top <%= @v['title'] %> -@end ifnottex - -<% if @f = @v['files'].detect { |f| f.name =~ /Readme/i } %> -<%= @f.values['description'] %><% end %> - -@menu -<% @v['classes'].each do |klass| %> -* <%= klass.name.gsub(/::/, '-') %>::<% end %> -@c TODO: add files -@end menu - -<% (@v['classes'] || []).each_with_index do |klass, i| %> -<%= TexinfoTemplate.new(@v.merge('class' => klass.values), - 'class.texinfo.erb').render %><% end %> - -@bye diff --git a/trunk/lib/rdoc/generator/xml.rb b/trunk/lib/rdoc/generator/xml.rb deleted file mode 100644 index 3335f2ce7c..0000000000 --- a/trunk/lib/rdoc/generator/xml.rb +++ /dev/null @@ -1,120 +0,0 @@ -require 'rdoc/generator/html' - -## -# Generate XML output as one big file - -class RDoc::Generator::XML < RDoc::Generator::HTML - - ## - # Standard generator factory - - def self.for(options) - new(options) - end - - def initialize(*args) - super - end - - ## - # Build the initial indices and output objects - # based on an array of TopLevel objects containing - # the extracted information. - - def generate(info) - @info = info - @files = [] - @classes = [] - @hyperlinks = {} - - build_indices - generate_xml - end - - ## - # Generate: - # - # * a list of HtmlFile objects for each TopLevel object. - # * a list of HtmlClass objects for each first level - # class or module in the TopLevel objects - # * a complete list of all hyperlinkable terms (file, - # class, module, and method names) - - def build_indices - @info.each do |toplevel| - @files << RDoc::Generator::HtmlFile.new(toplevel, @options, RDoc::Generator::FILE_DIR) - end - - RDoc::TopLevel.all_classes_and_modules.each do |cls| - build_class_list(cls, @files[0], RDoc::Generator::CLASS_DIR) - end - end - - def build_class_list(from, html_file, class_dir) - @classes << RDoc::Generator::HtmlClass.new(from, html_file, class_dir, @options) - from.each_classmodule do |mod| - build_class_list(mod, html_file, class_dir) - end - end - - ## - # Generate all the HTML. For the one-file case, we generate - # all the information in to one big hash - - def generate_xml - values = { - 'charset' => @options.charset, - 'files' => gen_into(@files), - 'classes' => gen_into(@classes) - } - - # this method is defined in the template file - write_extra_pages if defined? write_extra_pages - - template = RDoc::TemplatePage.new @template::ONE_PAGE - - if @options.op_name - opfile = File.open(@options.op_name, "w") - else - opfile = $stdout - end - template.write_html_on(opfile, values) - end - - def gen_into(list) - res = [] - list.each do |item| - res << item.value_hash - end - res - end - - def gen_file_index - gen_an_index(@files, 'Files') - end - - def gen_class_index - gen_an_index(@classes, 'Classes') - end - - def gen_method_index - gen_an_index(RDoc::Generator::HtmlMethod.all_methods, 'Methods') - end - - def gen_an_index(collection, title) - res = [] - collection.sort.each do |f| - if f.document_self - res << { "href" => f.path, "name" => f.index_name } - end - end - - return { - "entries" => res, - 'list_title' => title, - 'index_url' => main_url, - } - end - -end - diff --git a/trunk/lib/rdoc/generator/xml/rdf.rb b/trunk/lib/rdoc/generator/xml/rdf.rb deleted file mode 100644 index 7b15c69a18..0000000000 --- a/trunk/lib/rdoc/generator/xml/rdf.rb +++ /dev/null @@ -1,113 +0,0 @@ -require 'rdoc/generator/xml' - -module RDoc::Generator::XML::RDF - - CONTENTS_RDF = <<-EOF -<% if defined? classes and classes["description"] then %> - <description rd:parseType="Literal"> -<%= classes["description"] %> - </description> -<% end %> - -<% if defined? files and files["requires"] then %> -<% files["requires"].each do |requires| %> - <rd:required-file rd:name="<%= requires["name"] %>" /> -<% end # files["requires"] %> -<% end %> - -<% if defined? classes and classes["includes"] then %> - <IncludedModuleList> -<% classes["includes"].each do |includes| %> - <included-module rd:name="<%= includes["name"] %>" /> -<% end # includes["includes"] %> - </IncludedModuleList> -<% end %> - -<% if defined? classes and classes["sections"] then %> -<% classes["sections"].each do |sections| %> -<% if sections["attributes"] then %> -<% sections["attributes"].each do |attributes| %> - <contents> - <Attribute rd:name="<%= attributes["name"] %>"> -<% if attributes["rw"] then %> - <attribute-rw><%= attributes["rw"] %></attribute-rw> -<% end %> - <description rdf:parseType="Literal"><%= attributes["a_desc"] %></description> - </Attribute> - </contents> -<% end # sections["attributes"] %> -<% end %> - -<% if sections["method_list"] then %> -<% sections["method_list"].each do |method_list| %> -<% if method_list["methods"] then %> -<% method_list["methods"].each do |methods| %> - <contents> - <Method rd:name="<%= methods["name"] %>" rd:visibility="<%= methods["type"] %>" - rd:category="<%= methods["category"] %>" rd:id="<%= methods["aref"] %>"> - <parameters><%= methods["params"] %></parameters> -<% if methods["m_desc"] then %> - <description rdf:parseType="Literal"> -<%= methods["m_desc"] %> - </description> -<% end %> -<% if methods["sourcecode"] then %> - <source-code-listing rdf:parseType="Literal"> -<%= methods["sourcecode"] %> - </source-code-listing> -<% end %> - </Method> - </contents> -<% end # method_list["methods"] %> -<% end %> -<% end # sections["method_list"] %> -<% end %> - <!-- end method list --> -<% end # classes["sections"] %> -<% end %> - EOF - -######################################################################## - - ONE_PAGE = %{<?xml version="1.0" encoding="utf-8"?> -<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns="http://pragprog.com/rdoc/rdoc.rdf#" - xmlns:rd="http://pragprog.com/rdoc/rdoc.rdf#"> - -<!-- RDoc --> -<% values["files"].each do |files| %> - <rd:File rd:name="<%= files["short_name"] %>" rd:id="<%= files["href"] %>"> - <path><%= files["full_path"] %></path> - <dtm-modified><%= files["dtm_modified"] %></dtm-modified> -} + CONTENTS_RDF + %{ - </rd:File> -<% end # values["files"] %> -<% values["classes"].each do |classes| %> - <<%= values["classmod"] %> rd:name="<%= classes["full_name"] %>" rd:id="<%= classes["full_name"] %>"> - <classmod-info> -<% if classes["infiles"] then %> - <InFiles> -<% classes["infiles"].each do |infiles| %> - <infile> - <File rd:name="<%= infiles["full_path"] %>" -<% if infiles["full_path_url"] then %> - rdf:about="<%= infiles["full_path_url"] %>" -<% end %> - /> - </infile> -<% end # classes["infiles"] %> - </InFiles> -<% end %> -<% if classes["parent"] then %> - <superclass><%= href classes["par_url"], classes["parent"] %></superclass> -<% end %> - </classmod-info> -} + CONTENTS_RDF + %{ - </<%= classes["classmod"] %>> -<% end # values["classes"] %> -<!-- /RDoc --> -</rdf:RDF> -} - -end - diff --git a/trunk/lib/rdoc/generator/xml/xml.rb b/trunk/lib/rdoc/generator/xml/xml.rb deleted file mode 100644 index ffb1329c4e..0000000000 --- a/trunk/lib/rdoc/generator/xml/xml.rb +++ /dev/null @@ -1,111 +0,0 @@ -require 'rdoc/generator/xml' - -module RDoc::Generator::XML::XML - - CONTENTS_XML = <<-EOF -<% if defined? classes and classes["description"] then %> - <description> -<%= classes["description"] %> - </description> -<% end %> - <contents> -<% if defined? files and files["requires"] then %> - <required-file-list> -<% files["requires"].each do |requires| %> - <required-file name="<%= requires["name"] %>" -<% if requires["aref"] then %> - href="<%= requires["aref"] %>" -<% end %> - /> -<% end # files["requires"] %> - </required-file-list> -<% end %> -<% if defined? classes and classes["sections"] then %> -<% classes["sections"].each do |sections| %> -<% if sections["attributes"] then %> - <attribute-list> -<% sections["attributes"].each do |attributes| %> - <attribute name="<%= attributes["name"] %>"> -<% if attributes["rw"] then %> - <attribute-rw><%= attributes["rw"] %></attribute-rw> -<% end %> - <description><%= attributes["a_desc"] %></description> - </attribute> -<% end # sections["attributes"] %> - </attribute-list> -<% end %> -<% if sections["method_list"] then %> - <method-list> -<% sections["method_list"].each do |method_list| %> -<% if method_list["methods"] then %> -<% method_list["methods"].each do |methods| %> - <method name="<%= methods["name"] %>" type="<%= methods["type"] %>" category="<%= methods["category"] %>" id="<%= methods["aref"] %>"> - <parameters><%= methods["params"] %></parameters> -<% if methods["m_desc"] then %> - <description> -<%= methods["m_desc"] %> - </description> -<% end %> -<% if methods["sourcecode"] then %> - <source-code-listing> -<%= methods["sourcecode"] %> - </source-code-listing> -<% end %> - </method> -<% end # method_list["methods"] %> -<% end %> -<% end # sections["method_list"] %> - </method-list> -<% end %> -<% end # classes["sections"] %> -<% end %> -<% if defined? classes and classes["includes"] then %> - <included-module-list> -<% classes["includes"].each do |includes| %> - <included-module name="<%= includes["name"] %>" -<% if includes["aref"] then %> - href="<%= includes["aref"] %>" -<% end %> - /> -<% end # classes["includes"] %> - </included-module-list> -<% end %> - </contents> - EOF - - ONE_PAGE = %{<?xml version="1.0" encoding="utf-8"?> -<rdoc> -<file-list> -<% values["files"].each do |files| %> - <file name="<%= files["short_name"] %>" id="<%= files["href"] %>"> - <file-info> - <path><%= files["full_path"] %></path> - <dtm-modified><%= files["dtm_modified"] %></dtm-modified> - </file-info> -} + CONTENTS_XML + %{ - </file> -<% end # values["files"] %> -</file-list> -<class-module-list> -<% values["classes"].each do |classes| %> - <<%= classes["classmod"] %> name="<%= classes["full_name"] %>" id="<%= classes["full_name"] %>"> - <classmod-info> -<% if classes["infiles"] then %> - <infiles> -<% classes["infiles"].each do |infiles| %> - <infile><%= href infiles["full_path_url"], infiles["full_path"] %></infile> -<% end # classes["infiles"] %> - </infiles> -<% end %> -<% if classes["parent"] then %> - <superclass><%= href classes["par_url"], classes["parent"] %></superclass> -<% end %> - </classmod-info> -} + CONTENTS_XML + %{ - </<%= classes["classmod"] %>> -<% end # values["classes"] %> -</class-module-list> -</rdoc> -} - -end diff --git a/trunk/lib/rdoc/known_classes.rb b/trunk/lib/rdoc/known_classes.rb deleted file mode 100644 index 4c52f58ad2..0000000000 --- a/trunk/lib/rdoc/known_classes.rb +++ /dev/null @@ -1,69 +0,0 @@ -module RDoc - - ## - # Ruby's built-in classes, modules and exceptions - - KNOWN_CLASSES = { - "rb_cArray" => "Array", - "rb_cBignum" => "Bignum", - "rb_cClass" => "Class", - "rb_cData" => "Data", - "rb_cDir" => "Dir", - "rb_cFalseClass" => "FalseClass", - "rb_cFile" => "File", - "rb_cFixnum" => "Fixnum", - "rb_cFloat" => "Float", - "rb_cHash" => "Hash", - "rb_cIO" => "IO", - "rb_cInteger" => "Integer", - "rb_cModule" => "Module", - "rb_cNilClass" => "NilClass", - "rb_cNumeric" => "Numeric", - "rb_cObject" => "Object", - "rb_cProc" => "Proc", - "rb_cRange" => "Range", - "rb_cRegexp" => "Regexp", - "rb_cRubyVM" => "RubyVM", - "rb_cString" => "String", - "rb_cStruct" => "Struct", - "rb_cSymbol" => "Symbol", - "rb_cThread" => "Thread", - "rb_cTime" => "Time", - "rb_cTrueClass" => "TrueClass", - - "rb_eArgError" => "ArgError", - "rb_eEOFError" => "EOFError", - "rb_eException" => "Exception", - "rb_eFatal" => "Fatal", - "rb_eFloatDomainError" => "FloatDomainError", - "rb_eIOError" => "IOError", - "rb_eIndexError" => "IndexError", - "rb_eInterrupt" => "Interrupt", - "rb_eLoadError" => "LoadError", - "rb_eNameError" => "NameError", - "rb_eNoMemError" => "NoMemError", - "rb_eNotImpError" => "NotImpError", - "rb_eRangeError" => "RangeError", - "rb_eRuntimeError" => "RuntimeError", - "rb_eScriptError" => "ScriptError", - "rb_eSecurityError" => "SecurityError", - "rb_eSignal" => "Signal", - "rb_eStandardError" => "StandardError", - "rb_eSyntaxError" => "SyntaxError", - "rb_eSystemCallError" => "SystemCallError", - "rb_eSystemExit" => "SystemExit", - "rb_eTypeError" => "TypeError", - "rb_eZeroDivError" => "ZeroDivError", - - "rb_mComparable" => "Comparable", - "rb_mEnumerable" => "Enumerable", - "rb_mErrno" => "Errno", - "rb_mFileTest" => "FileTest", - "rb_mGC" => "GC", - "rb_mKernel" => "Kernel", - "rb_mMath" => "Math", - "rb_mPrecision" => "Precision", - "rb_mProcess" => "Process" - } - -end diff --git a/trunk/lib/rdoc/markup.rb b/trunk/lib/rdoc/markup.rb deleted file mode 100644 index 0e1b596255..0000000000 --- a/trunk/lib/rdoc/markup.rb +++ /dev/null @@ -1,473 +0,0 @@ -require 'rdoc' - -## -# RDoc::Markup parses plain text documents and attempts to decompose them into -# their constituent parts. Some of these parts are high-level: paragraphs, -# chunks of verbatim text, list entries and the like. Other parts happen at -# the character level: a piece of bold text, a word in code font. This markup -# is similar in spirit to that used on WikiWiki webs, where folks create web -# pages using a simple set of formatting rules. -# -# RDoc::Markup itself does no output formatting: this is left to a different -# set of classes. -# -# RDoc::Markup is extendable at runtime: you can add \new markup elements to -# be recognised in the documents that RDoc::Markup parses. -# -# RDoc::Markup is intended to be the basis for a family of tools which share -# the common requirement that simple, plain-text should be rendered in a -# variety of different output formats and media. It is envisaged that -# RDoc::Markup could be the basis for formatting RDoc style comment blocks, -# Wiki entries, and online FAQs. -# -# = Basic Formatting -# -# * RDoc::Markup looks for a document's natural left margin. This is -# used as the initial margin for the document. -# -# * Consecutive lines starting at this margin are considered to be a -# paragraph. -# -# * If a paragraph starts with a "*", "-", or with "<digit>.", then it is -# taken to be the start of a list. The margin in increased to be the first -# non-space following the list start flag. Subsequent lines should be -# indented to this \new margin until the list ends. For example: -# -# * this is a list with three paragraphs in -# the first item. This is the first paragraph. -# -# And this is the second paragraph. -# -# 1. This is an indented, numbered list. -# 2. This is the second item in that list -# -# This is the third conventional paragraph in the -# first list item. -# -# * This is the second item in the original list -# -# * You can also construct labeled lists, sometimes called description -# or definition lists. Do this by putting the label in square brackets -# and indenting the list body: -# -# [cat] a small furry mammal -# that seems to sleep a lot -# -# [ant] a little insect that is known -# to enjoy picnics -# -# A minor variation on labeled lists uses two colons to separate the -# label from the list body: -# -# cat:: a small furry mammal -# that seems to sleep a lot -# -# ant:: a little insect that is known -# to enjoy picnics -# -# This latter style guarantees that the list bodies' left margins are -# aligned: think of them as a two column table. -# -# * Any line that starts to the right of the current margin is treated -# as verbatim text. This is useful for code listings. The example of a -# list above is also verbatim text. -# -# * A line starting with an equals sign (=) is treated as a -# heading. Level one headings have one equals sign, level two headings -# have two,and so on. -# -# * A line starting with three or more hyphens (at the current indent) -# generates a horizontal rule. The more hyphens, the thicker the rule -# (within reason, and if supported by the output device) -# -# * You can use markup within text (except verbatim) to change the -# appearance of parts of that text. Out of the box, RDoc::Markup -# supports word-based and general markup. -# -# Word-based markup uses flag characters around individual words: -# -# [\*word*] displays word in a *bold* font -# [\_word_] displays word in an _emphasized_ font -# [\+word+] displays word in a +code+ font -# -# General markup affects text between a start delimiter and and end -# delimiter. Not surprisingly, these delimiters look like HTML markup. -# -# [\<b>text...</b>] displays word in a *bold* font -# [\<em>text...</em>] displays word in an _emphasized_ font -# [\<i>text...</i>] displays word in an _emphasized_ font -# [\<tt>text...</tt>] displays word in a +code+ font -# -# Unlike conventional Wiki markup, general markup can cross line -# boundaries. You can turn off the interpretation of markup by -# preceding the first character with a backslash, so \\\<b>bold -# text</b> and \\\*bold* produce \<b>bold text</b> and \*bold* -# respectively. -# -# * Hyperlinks to the web starting http:, mailto:, ftp:, or www. are -# recognized. An HTTP url that references an external image file is -# converted into an inline <IMG..>. Hyperlinks starting 'link:' are -# assumed to refer to local files whose path is relative to the --op -# directory. -# -# Hyperlinks can also be of the form <tt>label</tt>[url], in which -# case the label is used in the displayed text, and <tt>url</tt> is -# used as the target. If <tt>label</tt> contains multiple words, -# put it in braces: <em>{multi word label}[</em>url<em>]</em>. -# -# == Synopsis -# -# This code converts +input_string+ to HTML. The conversion takes place in -# the +convert+ method, so you can use the same RDoc::Markup converter to -# convert multiple input strings. -# -# require 'rdoc/markup/to_html' -# -# h = RDoc::Markup::ToHtml.new -# -# puts h.convert(input_string) -# -# You can extend the RDoc::Markup parser to recognise new markup -# sequences, and to add special processing for text that matches a -# regular expression. Here we make WikiWords significant to the parser, -# and also make the sequences {word} and \<no>text...</no> signify -# strike-through text. When then subclass the HTML output class to deal -# with these: -# -# require 'rdoc/markup' -# require 'rdoc/markup/to_html' -# -# class WikiHtml < RDoc::Markup::ToHtml -# def handle_special_WIKIWORD(special) -# "<font color=red>" + special.text + "</font>" -# end -# end -# -# m = RDoc::Markup.new -# m.add_word_pair("{", "}", :STRIKE) -# m.add_html("no", :STRIKE) -# -# m.add_special(/\b([A-Z][a-z]+[A-Z]\w+)/, :WIKIWORD) -# -# wh = WikiHtml.new -# wh.add_tag(:STRIKE, "<strike>", "</strike>") -# -# puts "<body>#{wh.convert ARGF.read}</body>" -# -#-- -# Author:: Dave Thomas, dave@pragmaticprogrammer.com -# License:: Ruby license - -class RDoc::Markup - - SPACE = ?\s - - # List entries look like: - # * text - # 1. text - # [label] text - # label:: text - # - # Flag it as a list entry, and work out the indent for subsequent lines - - SIMPLE_LIST_RE = /^( - ( \* (?# bullet) - |- (?# bullet) - |\d+\. (?# numbered ) - |[A-Za-z]\. (?# alphabetically numbered ) - ) - \s+ - )\S/x - - LABEL_LIST_RE = /^( - ( \[.*?\] (?# labeled ) - |\S.*:: (?# note ) - )(?:\s+|$) - )/x - - ## - # Take a block of text and use various heuristics to determine it's - # structure (paragraphs, lists, and so on). Invoke an event handler as we - # identify significant chunks. - - def initialize - @am = RDoc::Markup::AttributeManager.new - @output = nil - end - - ## - # Add to the sequences used to add formatting to an individual word (such - # as *bold*). Matching entries will generate attributes that the output - # formatters can recognize by their +name+. - - def add_word_pair(start, stop, name) - @am.add_word_pair(start, stop, name) - end - - ## - # Add to the sequences recognized as general markup. - - def add_html(tag, name) - @am.add_html(tag, name) - end - - ## - # Add to other inline sequences. For example, we could add WikiWords using - # something like: - # - # parser.add_special(/\b([A-Z][a-z]+[A-Z]\w+)/, :WIKIWORD) - # - # Each wiki word will be presented to the output formatter via the - # accept_special method. - - def add_special(pattern, name) - @am.add_special(pattern, name) - end - - ## - # We take a string, split it into lines, work out the type of each line, - # and from there deduce groups of lines (for example all lines in a - # paragraph). We then invoke the output formatter using a Visitor to - # display the result. - - def convert(str, op) - lines = str.split(/\r?\n/).map { |line| Line.new line } - @lines = Lines.new lines - - return "" if @lines.empty? - @lines.normalize - assign_types_to_lines - group = group_lines - # call the output formatter to handle the result - #group.each { |line| p line } - group.accept @am, op - end - - private - - ## - # Look through the text at line indentation. We flag each line as being - # Blank, a paragraph, a list element, or verbatim text. - - def assign_types_to_lines(margin = 0, level = 0) - while line = @lines.next - if line.blank? then - line.stamp :BLANK, level - next - end - - # if a line contains non-blanks before the margin, then it must belong - # to an outer level - - text = line.text - - for i in 0...margin - if text[i] != SPACE - @lines.unget - return - end - end - - active_line = text[margin..-1] - - # Rules (horizontal lines) look like - # - # --- (three or more hyphens) - # - # The more hyphens, the thicker the rule - # - - if /^(---+)\s*$/ =~ active_line - line.stamp :RULE, level, $1.length-2 - next - end - - # Then look for list entries. First the ones that have to have - # text following them (* xxx, - xxx, and dd. xxx) - - if SIMPLE_LIST_RE =~ active_line - offset = margin + $1.length - prefix = $2 - prefix_length = prefix.length - - flag = case prefix - when "*","-" then :BULLET - when /^\d/ then :NUMBER - when /^[A-Z]/ then :UPPERALPHA - when /^[a-z]/ then :LOWERALPHA - else raise "Invalid List Type: #{self.inspect}" - end - - line.stamp :LIST, level+1, prefix, flag - text[margin, prefix_length] = " " * prefix_length - assign_types_to_lines(offset, level + 1) - next - end - - if LABEL_LIST_RE =~ active_line - offset = margin + $1.length - prefix = $2 - prefix_length = prefix.length - - next if handled_labeled_list(line, level, margin, offset, prefix) - end - - # Headings look like - # = Main heading - # == Second level - # === Third - # - # Headings reset the level to 0 - - if active_line[0] == ?= and active_line =~ /^(=+)\s*(.*)/ - prefix_length = $1.length - prefix_length = 6 if prefix_length > 6 - line.stamp :HEADING, 0, prefix_length - line.strip_leading(margin + prefix_length) - next - end - - # If the character's a space, then we have verbatim text, - # otherwise - - if active_line[0] == SPACE - line.strip_leading(margin) if margin > 0 - line.stamp :VERBATIM, level - else - line.stamp :PARAGRAPH, level - end - end - end - - ## - # Handle labeled list entries, We have a special case to deal with. - # Because the labels can be long, they force the remaining block of text - # over the to right: - # - # this is a long label that I wrote:: and here is the - # block of text with - # a silly margin - # - # So we allow the special case. If the label is followed by nothing, and - # if the following line is indented, then we take the indent of that line - # as the new margin. - # - # this is a long label that I wrote:: - # here is a more reasonably indented block which - # will be attached to the label. - # - - def handled_labeled_list(line, level, margin, offset, prefix) - prefix_length = prefix.length - text = line.text - flag = nil - - case prefix - when /^\[/ then - flag = :LABELED - prefix = prefix[1, prefix.length-2] - when /:$/ then - flag = :NOTE - prefix.chop! - else - raise "Invalid List Type: #{self.inspect}" - end - - # body is on the next line - if text.length <= offset then - original_line = line - line = @lines.next - return false unless line - text = line.text - - for i in 0..margin - if text[i] != SPACE - @lines.unget - return false - end - end - - i = margin - i += 1 while text[i] == SPACE - - if i >= text.length then - @lines.unget - return false - else - offset = i - prefix_length = 0 - - if text[offset..-1] =~ SIMPLE_LIST_RE then - @lines.unget - line = original_line - line.text = '' - else - @lines.delete original_line - end - end - end - - line.stamp :LIST, level+1, prefix, flag - text[margin, prefix_length] = " " * prefix_length - assign_types_to_lines(offset, level + 1) - return true - end - - ## - # Return a block consisting of fragments which are paragraphs, list - # entries or verbatim text. We merge consecutive lines of the same type - # and level together. We are also slightly tricky with lists: the lines - # following a list introduction look like paragraph lines at the next - # level, and we remap them into list entries instead. - - def group_lines - @lines.rewind - - in_list = false - wanted_type = wanted_level = nil - - block = LineCollection.new - group = nil - - while line = @lines.next - if line.level == wanted_level and line.type == wanted_type - group.add_text(line.text) - else - group = block.fragment_for(line) - block.add(group) - - if line.type == :LIST - wanted_type = :PARAGRAPH - else - wanted_type = line.type - end - - wanted_level = line.type == :HEADING ? line.param : line.level - end - end - - block.normalize - block - end - - ## - # For debugging, we allow access to our line contents as text. - - def content - @lines.as_text - end - public :content - - ## - # For debugging, return the list of line types. - - def get_line_types - @lines.line_types - end - public :get_line_types - -end - -require 'rdoc/markup/fragments' -require 'rdoc/markup/inline' -require 'rdoc/markup/lines' diff --git a/trunk/lib/rdoc/markup/attribute_manager.rb b/trunk/lib/rdoc/markup/attribute_manager.rb deleted file mode 100644 index d13b79376c..0000000000 --- a/trunk/lib/rdoc/markup/attribute_manager.rb +++ /dev/null @@ -1,265 +0,0 @@ -require 'rdoc/markup/inline' - -class RDoc::Markup::AttributeManager - - NULL = "\000".freeze - - ## - # We work by substituting non-printing characters in to the text. For now - # I'm assuming that I can substitute a character in the range 0..8 for a 7 - # bit character without damaging the encoded string, but this might be - # optimistic - - A_PROTECT = 004 - PROTECT_ATTR = A_PROTECT.chr - - ## - # This maps delimiters that occur around words (such as *bold* or +tt+) - # where the start and end delimiters and the same. This lets us optimize - # the regexp - - MATCHING_WORD_PAIRS = {} - - ## - # And this is used when the delimiters aren't the same. In this case the - # hash maps a pattern to the attribute character - - WORD_PAIR_MAP = {} - - ## - # This maps HTML tags to the corresponding attribute char - - HTML_TAGS = {} - - ## - # And this maps _special_ sequences to a name. A special sequence is - # something like a WikiWord - - SPECIAL = {} - - ## - # Return an attribute object with the given turn_on and turn_off bits set - - def attribute(turn_on, turn_off) - RDoc::Markup::AttrChanger.new turn_on, turn_off - end - - def change_attribute(current, new) - diff = current ^ new - attribute(new & diff, current & diff) - end - - def changed_attribute_by_name(current_set, new_set) - current = new = 0 - current_set.each do |name| - current |= RDoc::Markup::Attribute.bitmap_for(name) - end - - new_set.each do |name| - new |= RDoc::Markup::Attribute.bitmap_for(name) - end - - change_attribute(current, new) - end - - def copy_string(start_pos, end_pos) - res = @str[start_pos...end_pos] - res.gsub!(/\000/, '') - res - end - - ## - # Map attributes like <b>text</b>to the sequence - # \001\002<char>\001\003<char>, where <char> is a per-attribute specific - # character - - def convert_attrs(str, attrs) - # first do matching ones - tags = MATCHING_WORD_PAIRS.keys.join("") - - re = /(^|\W)([#{tags}])([#:\\]?[\w.\/-]+?\S?)\2(\W|$)/ - - 1 while str.gsub!(re) do - attr = MATCHING_WORD_PAIRS[$2] - attrs.set_attrs($`.length + $1.length + $2.length, $3.length, attr) - $1 + NULL * $2.length + $3 + NULL * $2.length + $4 - end - - # then non-matching - unless WORD_PAIR_MAP.empty? then - WORD_PAIR_MAP.each do |regexp, attr| - str.gsub!(regexp) { - attrs.set_attrs($`.length + $1.length, $2.length, attr) - NULL * $1.length + $2 + NULL * $3.length - } - end - end - end - - def convert_html(str, attrs) - tags = HTML_TAGS.keys.join '|' - - 1 while str.gsub!(/<(#{tags})>(.*?)<\/\1>/i) { - attr = HTML_TAGS[$1.downcase] - html_length = $1.length + 2 - seq = NULL * html_length - attrs.set_attrs($`.length + html_length, $2.length, attr) - seq + $2 + seq + NULL - } - end - - def convert_specials(str, attrs) - unless SPECIAL.empty? - SPECIAL.each do |regexp, attr| - str.scan(regexp) do - attrs.set_attrs($`.length, $&.length, - attr | RDoc::Markup::Attribute::SPECIAL) - end - end - end - end - - ## - # A \ in front of a character that would normally be processed turns off - # processing. We do this by turning \< into <#{PROTECT} - - PROTECTABLE = %w[<\\] - - def mask_protected_sequences - protect_pattern = Regexp.new("\\\\([#{Regexp.escape(PROTECTABLE.join(''))}])") - @str.gsub!(protect_pattern, "\\1#{PROTECT_ATTR}") - end - - def unmask_protected_sequences - @str.gsub!(/(.)#{PROTECT_ATTR}/, "\\1\000") - end - - def initialize - add_word_pair("*", "*", :BOLD) - add_word_pair("_", "_", :EM) - add_word_pair("+", "+", :TT) - - add_html("em", :EM) - add_html("i", :EM) - add_html("b", :BOLD) - add_html("tt", :TT) - add_html("code", :TT) - end - - def add_word_pair(start, stop, name) - raise ArgumentError, "Word flags may not start with '<'" if - start[0,1] == '<' - - bitmap = RDoc::Markup::Attribute.bitmap_for name - - if start == stop then - MATCHING_WORD_PAIRS[start] = bitmap - else - pattern = /(#{Regexp.escape start})(\S+)(#{Regexp.escape stop})/ - WORD_PAIR_MAP[pattern] = bitmap - end - - PROTECTABLE << start[0,1] - PROTECTABLE.uniq! - end - - def add_html(tag, name) - HTML_TAGS[tag.downcase] = RDoc::Markup::Attribute.bitmap_for name - end - - def add_special(pattern, name) - SPECIAL[pattern] = RDoc::Markup::Attribute.bitmap_for name - end - - def flow(str) - @str = str - - mask_protected_sequences - - @attrs = RDoc::Markup::AttrSpan.new @str.length - - convert_attrs(@str, @attrs) - convert_html(@str, @attrs) - convert_specials(str, @attrs) - - unmask_protected_sequences - - return split_into_flow - end - - def display_attributes - puts - puts @str.tr(NULL, "!") - bit = 1 - 16.times do |bno| - line = "" - @str.length.times do |i| - if (@attrs[i] & bit) == 0 - line << " " - else - if bno.zero? - line << "S" - else - line << ("%d" % (bno+1)) - end - end - end - puts(line) unless line =~ /^ *$/ - bit <<= 1 - end - end - - def split_into_flow - res = [] - current_attr = 0 - str = "" - - str_len = @str.length - - # skip leading invisible text - i = 0 - i += 1 while i < str_len and @str[i].chr == "\0" - start_pos = i - - # then scan the string, chunking it on attribute changes - while i < str_len - new_attr = @attrs[i] - if new_attr != current_attr - if i > start_pos - res << copy_string(start_pos, i) - start_pos = i - end - - res << change_attribute(current_attr, new_attr) - current_attr = new_attr - - if (current_attr & RDoc::Markup::Attribute::SPECIAL) != 0 then - i += 1 while - i < str_len and (@attrs[i] & RDoc::Markup::Attribute::SPECIAL) != 0 - - res << RDoc::Markup::Special.new(current_attr, - copy_string(start_pos, i)) - start_pos = i - next - end - end - - # move on, skipping any invisible characters - begin - i += 1 - end while i < str_len and @str[i].chr == "\0" - end - - # tidy up trailing text - if start_pos < str_len - res << copy_string(start_pos, str_len) - end - - # and reset to all attributes off - res << change_attribute(current_attr, 0) if current_attr != 0 - - return res - end - -end - diff --git a/trunk/lib/rdoc/markup/formatter.rb b/trunk/lib/rdoc/markup/formatter.rb deleted file mode 100644 index 14cbae59f9..0000000000 --- a/trunk/lib/rdoc/markup/formatter.rb +++ /dev/null @@ -1,14 +0,0 @@ -require 'rdoc/markup' - -class RDoc::Markup::Formatter - - def initialize - @markup = RDoc::Markup.new - end - - def convert(content) - @markup.convert content, self - end - -end - diff --git a/trunk/lib/rdoc/markup/fragments.rb b/trunk/lib/rdoc/markup/fragments.rb deleted file mode 100644 index b7f9b605c8..0000000000 --- a/trunk/lib/rdoc/markup/fragments.rb +++ /dev/null @@ -1,337 +0,0 @@ -require 'rdoc/markup' -require 'rdoc/markup/lines' - -class RDoc::Markup - - ## - # A Fragment is a chunk of text, subclassed as a paragraph, a list - # entry, or verbatim text. - - class Fragment - attr_reader :level, :param, :txt - attr_accessor :type - - ## - # This is a simple factory system that lets us associate fragement - # types (a string) with a subclass of fragment - - TYPE_MAP = {} - - def self.type_name(name) - TYPE_MAP[name] = self - end - - def self.for(line) - klass = TYPE_MAP[line.type] || - raise("Unknown line type: '#{line.type.inspect}:' '#{line.text}'") - return klass.new(line.level, line.param, line.flag, line.text) - end - - def initialize(level, param, type, txt) - @level = level - @param = param - @type = type - @txt = "" - add_text(txt) if txt - end - - def add_text(txt) - @txt << " " if @txt.length > 0 - @txt << txt.tr_s("\n ", " ").strip - end - - def to_s - "L#@level: #{self.class.name.split('::')[-1]}\n#@txt" - end - - end - - ## - # A paragraph is a fragment which gets wrapped to fit. We remove all - # newlines when we're created, and have them put back on output. - - class Paragraph < Fragment - type_name :PARAGRAPH - end - - class BlankLine < Paragraph - type_name :BLANK - end - - class Heading < Paragraph - type_name :HEADING - - def head_level - @param.to_i - end - end - - ## - # A List is a fragment with some kind of label - - class ListBase < Paragraph - LIST_TYPES = [ - :BULLET, - :NUMBER, - :UPPERALPHA, - :LOWERALPHA, - :LABELED, - :NOTE, - ] - end - - class ListItem < ListBase - type_name :LIST - - def to_s - text = if [:NOTE, :LABELED].include? type then - "#{@param}: #{@txt}" - else - @txt - end - - "L#@level: #{type} #{self.class.name.split('::')[-1]}\n#{text}" - end - - end - - class ListStart < ListBase - def initialize(level, param, type) - super(level, param, type, nil) - end - end - - class ListEnd < ListBase - def initialize(level, type) - super(level, "", type, nil) - end - end - - ## - # Verbatim code contains lines that don't get wrapped. - - class Verbatim < Fragment - type_name :VERBATIM - - def add_text(txt) - @txt << txt.chomp << "\n" - end - - end - - ## - # A horizontal rule - - class Rule < Fragment - type_name :RULE - end - - ## - # Collect groups of lines together. Each group will end up containing a flow - # of text. - - class LineCollection - - def initialize - @fragments = [] - end - - def add(fragment) - @fragments << fragment - end - - def each(&b) - @fragments.each(&b) - end - - def to_a # :nodoc: - @fragments.map {|fragment| fragment.to_s} - end - - ## - # Factory for different fragment types - - def fragment_for(*args) - Fragment.for(*args) - end - - ## - # Tidy up at the end - - def normalize - change_verbatim_blank_lines - add_list_start_and_ends - add_list_breaks - tidy_blank_lines - end - - def to_s - @fragments.join("\n----\n") - end - - def accept(am, visitor) - visitor.start_accepting - - @fragments.each do |fragment| - case fragment - when Verbatim - visitor.accept_verbatim(am, fragment) - when Rule - visitor.accept_rule(am, fragment) - when ListStart - visitor.accept_list_start(am, fragment) - when ListEnd - visitor.accept_list_end(am, fragment) - when ListItem - visitor.accept_list_item(am, fragment) - when BlankLine - visitor.accept_blank_line(am, fragment) - when Heading - visitor.accept_heading(am, fragment) - when Paragraph - visitor.accept_paragraph(am, fragment) - end - end - - visitor.end_accepting - end - - private - - # If you have: - # - # normal paragraph text. - # - # this is code - # - # and more code - # - # You'll end up with the fragments Paragraph, BlankLine, Verbatim, - # BlankLine, Verbatim, BlankLine, etc. - # - # The BlankLine in the middle of the verbatim chunk needs to be changed to - # a real verbatim newline, and the two verbatim blocks merged - - def change_verbatim_blank_lines - frag_block = nil - blank_count = 0 - @fragments.each_with_index do |frag, i| - if frag_block.nil? - frag_block = frag if Verbatim === frag - else - case frag - when Verbatim - blank_count.times { frag_block.add_text("\n") } - blank_count = 0 - frag_block.add_text(frag.txt) - @fragments[i] = nil # remove out current fragment - when BlankLine - if frag_block - blank_count += 1 - @fragments[i] = nil - end - else - frag_block = nil - blank_count = 0 - end - end - end - @fragments.compact! - end - - ## - # List nesting is implicit given the level of indentation. Make it - # explicit, just to make life a tad easier for the output processors - - def add_list_start_and_ends - level = 0 - res = [] - type_stack = [] - - @fragments.each do |fragment| - # $stderr.puts "#{level} : #{fragment.class.name} : #{fragment.level}" - new_level = fragment.level - while (level < new_level) - level += 1 - type = fragment.type - res << ListStart.new(level, fragment.param, type) if type - type_stack.push type - # $stderr.puts "Start: #{level}" - end - - while level > new_level - type = type_stack.pop - res << ListEnd.new(level, type) if type - level -= 1 - # $stderr.puts "End: #{level}, #{type}" - end - - res << fragment - level = fragment.level - end - level.downto(1) do |i| - type = type_stack.pop - res << ListEnd.new(i, type) if type - end - - @fragments = res - end - - ## - # Inserts start/ends between list entries at the same level that have - # different element types - - def add_list_breaks - res = @fragments - - @fragments = [] - list_stack = [] - - res.each do |fragment| - case fragment - when ListStart - list_stack.push fragment - when ListEnd - start = list_stack.pop - fragment.type = start.type - when ListItem - l = list_stack.last - if fragment.type != l.type - @fragments << ListEnd.new(l.level, l.type) - start = ListStart.new(l.level, fragment.param, fragment.type) - @fragments << start - list_stack.pop - list_stack.push start - end - else - ; - end - @fragments << fragment - end - end - - ## - # Tidy up the blank lines: - # * change Blank/ListEnd into ListEnd/Blank - # * remove blank lines at the front - - def tidy_blank_lines - (@fragments.size - 1).times do |i| - if BlankLine === @fragments[i] and ListEnd === @fragments[i+1] then - @fragments[i], @fragments[i+1] = @fragments[i+1], @fragments[i] - end - end - - # remove leading blanks - @fragments.each_with_index do |f, i| - break unless f.kind_of? BlankLine - @fragments[i] = nil - end - - @fragments.compact! - end - - end - -end - diff --git a/trunk/lib/rdoc/markup/inline.rb b/trunk/lib/rdoc/markup/inline.rb deleted file mode 100644 index ee77679a11..0000000000 --- a/trunk/lib/rdoc/markup/inline.rb +++ /dev/null @@ -1,101 +0,0 @@ -require 'rdoc/markup' - -class RDoc::Markup - - ## - # We manage a set of attributes. Each attribute has a symbol name and a bit - # value. - - class Attribute - SPECIAL = 1 - - @@name_to_bitmap = { :_SPECIAL_ => SPECIAL } - @@next_bitmap = 2 - - def self.bitmap_for(name) - bitmap = @@name_to_bitmap[name] - unless bitmap then - bitmap = @@next_bitmap - @@next_bitmap <<= 1 - @@name_to_bitmap[name] = bitmap - end - bitmap - end - - def self.as_string(bitmap) - return "none" if bitmap.zero? - res = [] - @@name_to_bitmap.each do |name, bit| - res << name if (bitmap & bit) != 0 - end - res.join(",") - end - - def self.each_name_of(bitmap) - @@name_to_bitmap.each do |name, bit| - next if bit == SPECIAL - yield name.to_s if (bitmap & bit) != 0 - end - end - end - - AttrChanger = Struct.new(:turn_on, :turn_off) - - ## - # An AttrChanger records a change in attributes. It contains a bitmap of the - # attributes to turn on, and a bitmap of those to turn off. - - class AttrChanger - def to_s - "Attr: +#{Attribute.as_string(@turn_on)}/-#{Attribute.as_string(@turn_on)}" - end - end - - ## - # An array of attributes which parallels the characters in a string. - - class AttrSpan - def initialize(length) - @attrs = Array.new(length, 0) - end - - def set_attrs(start, length, bits) - for i in start ... (start+length) - @attrs[i] |= bits - end - end - - def [](n) - @attrs[n] - end - end - - ## - # Hold details of a special sequence - - class Special - attr_reader :type - attr_accessor :text - - def initialize(type, text) - @type, @text = type, text - end - - def ==(o) - self.text == o.text && self.type == o.type - end - - def inspect - "#<RDoc::Markup::Special:0x%x @type=%p, name=%p @text=%p>" % [ - object_id, @type, RDoc::Markup::Attribute.as_string(type), text.dump] - end - - def to_s - "Special: type=#{type}, name=#{RDoc::Markup::Attribute.as_string type}, text=#{text.dump}" - end - - end - -end - -require 'rdoc/markup/attribute_manager' diff --git a/trunk/lib/rdoc/markup/lines.rb b/trunk/lib/rdoc/markup/lines.rb deleted file mode 100644 index 069492122f..0000000000 --- a/trunk/lib/rdoc/markup/lines.rb +++ /dev/null @@ -1,152 +0,0 @@ -class RDoc::Markup - - ## - # We store the lines we're working on as objects of class Line. These - # contain the text of the line, along with a flag indicating the line type, - # and an indentation level. - - class Line - INFINITY = 9999 - - LINE_TYPES = [ - :BLANK, - :HEADING, - :LIST, - :PARAGRAPH, - :RULE, - :VERBATIM, - ] - - # line type - attr_accessor :type - - # The indentation nesting level - attr_accessor :level - - # The contents - attr_accessor :text - - # A prefix or parameter. For LIST lines, this is - # the text that introduced the list item (the label) - attr_accessor :param - - # A flag. For list lines, this is the type of the list - attr_accessor :flag - - # the number of leading spaces - attr_accessor :leading_spaces - - # true if this line has been deleted from the list of lines - attr_accessor :deleted - - def initialize(text) - @text = text.dup - @deleted = false - - # expand tabs - 1 while @text.gsub!(/\t+/) { ' ' * (8*$&.length - $`.length % 8)} && $~ #` - - # Strip trailing whitespace - @text.sub!(/\s+$/, '') - - # and look for leading whitespace - if @text.length > 0 - @text =~ /^(\s*)/ - @leading_spaces = $1.length - else - @leading_spaces = INFINITY - end - end - - # Return true if this line is blank - def blank? - @text.empty? - end - - # stamp a line with a type, a level, a prefix, and a flag - def stamp(type, level, param="", flag=nil) - @type, @level, @param, @flag = type, level, param, flag - end - - ## - # Strip off the leading margin - - def strip_leading(size) - if @text.size > size - @text[0,size] = "" - else - @text = "" - end - end - - def to_s - "#@type#@level: #@text" - end - end - - ## - # A container for all the lines. - - class Lines - - include Enumerable - - attr_reader :lines # :nodoc: - - def initialize(lines) - @lines = lines - rewind - end - - def empty? - @lines.size.zero? - end - - def each - @lines.each do |line| - yield line unless line.deleted - end - end - -# def [](index) -# @lines[index] -# end - - def rewind - @nextline = 0 - end - - def next - begin - res = @lines[@nextline] - @nextline += 1 if @nextline < @lines.size - end while res and res.deleted and @nextline < @lines.size - res - end - - def unget - @nextline -= 1 - end - - def delete(a_line) - a_line.deleted = true - end - - def normalize - margin = @lines.collect{|l| l.leading_spaces}.min - margin = 0 if margin == :INFINITY - @lines.each {|line| line.strip_leading(margin) } if margin > 0 - end - - def as_text - @lines.map {|l| l.text}.join("\n") - end - - def line_types - @lines.map {|l| l.type } - end - - end - -end - diff --git a/trunk/lib/rdoc/markup/preprocess.rb b/trunk/lib/rdoc/markup/preprocess.rb deleted file mode 100644 index 00dd4be4ad..0000000000 --- a/trunk/lib/rdoc/markup/preprocess.rb +++ /dev/null @@ -1,75 +0,0 @@ -require 'rdoc/markup' - -## -# Handle common directives that can occur in a block of text: -# -# : include : filename - -class RDoc::Markup::PreProcess - - def initialize(input_file_name, include_path) - @input_file_name = input_file_name - @include_path = include_path - end - - ## - # Look for common options in a chunk of text. Options that we don't handle - # are yielded to the caller. - - def handle(text) - text.gsub!(/^([ \t]*#?[ \t]*):(\w+):([ \t]*)(.+)?\n/) do - next $& if $3.empty? and $4 and $4[0, 1] == ':' - - prefix = $1 - directive = $2.downcase - param = $4 - - case directive - when 'include' then - filename = param.split[0] - include_file filename, prefix - - else - result = yield directive, param - result = "#{prefix}:#{directive}: #{param}\n" unless result - result - end - end - end - - private - - ## - # Include a file, indenting it correctly. - - def include_file(name, indent) - if full_name = find_include_file(name) then - content = File.open(full_name) {|f| f.read} - # strip leading '#'s, but only if all lines start with them - if content =~ /^[^#]/ - content.gsub(/^/, indent) - else - content.gsub(/^#?/, indent) - end - else - $stderr.puts "Couldn't find file to include: '#{name}'" - '' - end - end - - ## - # Look for the given file in the directory containing the current file, - # and then in each of the directories specified in the RDOC_INCLUDE path - - def find_include_file(name) - to_search = [ File.dirname(@input_file_name) ].concat @include_path - to_search.each do |dir| - full_name = File.join(dir, name) - stat = File.stat(full_name) rescue next - return full_name if stat.readable? - end - nil - end - -end - diff --git a/trunk/lib/rdoc/markup/to_flow.rb b/trunk/lib/rdoc/markup/to_flow.rb deleted file mode 100644 index 3d87b3e9c3..0000000000 --- a/trunk/lib/rdoc/markup/to_flow.rb +++ /dev/null @@ -1,185 +0,0 @@ -require 'rdoc/markup/formatter' -require 'rdoc/markup/fragments' -require 'rdoc/markup/inline' -require 'cgi' - -class RDoc::Markup - - module Flow - P = Struct.new(:body) - VERB = Struct.new(:body) - RULE = Struct.new(:width) - class LIST - attr_reader :type, :contents - def initialize(type) - @type = type - @contents = [] - end - def <<(stuff) - @contents << stuff - end - end - LI = Struct.new(:label, :body) - H = Struct.new(:level, :text) - end - - class ToFlow < RDoc::Markup::Formatter - LIST_TYPE_TO_HTML = { - :BULLET => [ "<ul>", "</ul>" ], - :NUMBER => [ "<ol>", "</ol>" ], - :UPPERALPHA => [ "<ol>", "</ol>" ], - :LOWERALPHA => [ "<ol>", "</ol>" ], - :LABELED => [ "<dl>", "</dl>" ], - :NOTE => [ "<table>", "</table>" ], - } - - InlineTag = Struct.new(:bit, :on, :off) - - def initialize - super - - init_tags - end - - ## - # Set up the standard mapping of attributes to HTML tags - - def init_tags - @attr_tags = [ - InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:BOLD), "<b>", "</b>"), - InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:TT), "<tt>", "</tt>"), - InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:EM), "<em>", "</em>"), - ] - end - - ## - # Add a new set of HTML tags for an attribute. We allow separate start and - # end tags for flexibility - - def add_tag(name, start, stop) - @attr_tags << InlineTag.new(RDoc::Markup::Attribute.bitmap_for(name), start, stop) - end - - ## - # Given an HTML tag, decorate it with class information and the like if - # required. This is a no-op in the base class, but is overridden in HTML - # output classes that implement style sheets - - def annotate(tag) - tag - end - - ## - # Here's the client side of the visitor pattern - - def start_accepting - @res = [] - @list_stack = [] - end - - def end_accepting - @res - end - - def accept_paragraph(am, fragment) - @res << Flow::P.new((convert_flow(am.flow(fragment.txt)))) - end - - def accept_verbatim(am, fragment) - @res << Flow::VERB.new((convert_flow(am.flow(fragment.txt)))) - end - - def accept_rule(am, fragment) - size = fragment.param - size = 10 if size > 10 - @res << Flow::RULE.new(size) - end - - def accept_list_start(am, fragment) - @list_stack.push(@res) - list = Flow::LIST.new(fragment.type) - @res << list - @res = list - end - - def accept_list_end(am, fragment) - @res = @list_stack.pop - end - - def accept_list_item(am, fragment) - @res << Flow::LI.new(fragment.param, convert_flow(am.flow(fragment.txt))) - end - - def accept_blank_line(am, fragment) - # @res << annotate("<p />") << "\n" - end - - def accept_heading(am, fragment) - @res << Flow::H.new(fragment.head_level, convert_flow(am.flow(fragment.txt))) - end - - private - - def on_tags(res, item) - attr_mask = item.turn_on - return if attr_mask.zero? - - @attr_tags.each do |tag| - if attr_mask & tag.bit != 0 - res << annotate(tag.on) - end - end - end - - def off_tags(res, item) - attr_mask = item.turn_off - return if attr_mask.zero? - - @attr_tags.reverse_each do |tag| - if attr_mask & tag.bit != 0 - res << annotate(tag.off) - end - end - end - - def convert_flow(flow) - res = "" - flow.each do |item| - case item - when String - res << convert_string(item) - when AttrChanger - off_tags(res, item) - on_tags(res, item) - when Special - res << convert_special(item) - else - raise "Unknown flow element: #{item.inspect}" - end - end - res - end - - def convert_string(item) - CGI.escapeHTML(item) - end - - def convert_special(special) - handled = false - Attribute.each_name_of(special.type) do |name| - method_name = "handle_special_#{name}" - if self.respond_to? method_name - special.text = send(method_name, special) - handled = true - end - end - - raise "Unhandled special: #{special}" unless handled - - special.text - end - - end - -end - diff --git a/trunk/lib/rdoc/markup/to_html.rb b/trunk/lib/rdoc/markup/to_html.rb deleted file mode 100644 index ca29373db1..0000000000 --- a/trunk/lib/rdoc/markup/to_html.rb +++ /dev/null @@ -1,400 +0,0 @@ -require 'rdoc/markup/formatter' -require 'rdoc/markup/fragments' -require 'rdoc/markup/inline' - -require 'cgi' - -class RDoc::Markup::ToHtml < RDoc::Markup::Formatter - - LIST_TYPE_TO_HTML = { - :BULLET => %w[<ul> </ul>], - :NUMBER => %w[<ol> </ol>], - :UPPERALPHA => %w[<ol> </ol>], - :LOWERALPHA => %w[<ol> </ol>], - :LABELED => %w[<dl> </dl>], - :NOTE => %w[<table> </table>], - } - - InlineTag = Struct.new(:bit, :on, :off) - - def initialize - super - - # @in_tt - tt nested levels count - # @tt_bit - cache - @in_tt = 0 - @tt_bit = RDoc::Markup::Attribute.bitmap_for :TT - - # external hyperlinks - @markup.add_special(/((link:|https?:|mailto:|ftp:|www\.)\S+\w)/, :HYPERLINK) - - # and links of the form <text>[<url>] - @markup.add_special(/(((\{.*?\})|\b\S+?)\[\S+?\.\S+?\])/, :TIDYLINK) - - init_tags - end - - ## - # Converts a target url to one that is relative to a given path - - def self.gen_relative_url(path, target) - from = File.dirname path - to, to_file = File.split target - - from = from.split "/" - to = to.split "/" - - while from.size > 0 and to.size > 0 and from[0] == to[0] do - from.shift - to.shift - end - - from.fill ".." - from.concat to - from << to_file - File.join(*from) - end - - ## - # Generate a hyperlink for url, labeled with text. Handle the - # special cases for img: and link: described under handle_special_HYPEDLINK - - def gen_url(url, text) - if url =~ /([A-Za-z]+):(.*)/ then - type = $1 - path = $2 - else - type = "http" - path = url - url = "http://#{url}" - end - - if type == "link" then - url = if path[0, 1] == '#' then # is this meaningful? - path - else - self.class.gen_relative_url @from_path, path - end - end - - if (type == "http" or type == "link") and - url =~ /\.(gif|png|jpg|jpeg|bmp)$/ then - "<img src=\"#{url}\" />" - else - "<a href=\"#{url}\">#{text.sub(%r{^#{type}:/*}, '')}</a>" - end - end - - ## - # And we're invoked with a potential external hyperlink mailto: - # just gets inserted. http: links are checked to see if they - # reference an image. If so, that image gets inserted using an - # <img> tag. Otherwise a conventional <a href> is used. We also - # support a special type of hyperlink, link:, which is a reference - # to a local file whose path is relative to the --op directory. - - def handle_special_HYPERLINK(special) - url = special.text - gen_url url, url - end - - ## - # Here's a hypedlink where the label is different to the URL - # <label>[url] or {long label}[url] - - def handle_special_TIDYLINK(special) - text = special.text - - return text unless text =~ /\{(.*?)\}\[(.*?)\]/ or text =~ /(\S+)\[(.*?)\]/ - - label = $1 - url = $2 - gen_url url, label - end - - ## - # are we currently inside <tt> tags? - - def in_tt? - @in_tt > 0 - end - - ## - # is +tag+ a <tt> tag? - - def tt?(tag) - tag.bit == @tt_bit - end - - ## - # Set up the standard mapping of attributes to HTML tags - - def init_tags - @attr_tags = [ - InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:BOLD), "<b>", "</b>"), - InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:TT), "<tt>", "</tt>"), - InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:EM), "<em>", "</em>"), - ] - end - - ## - # Add a new set of HTML tags for an attribute. We allow separate start and - # end tags for flexibility. - - def add_tag(name, start, stop) - @attr_tags << InlineTag.new(RDoc::Markup::Attribute.bitmap_for(name), start, stop) - end - - ## - # Given an HTML tag, decorate it with class information and the like if - # required. This is a no-op in the base class, but is overridden in HTML - # output classes that implement style sheets. - - def annotate(tag) - tag - end - - ## - # Here's the client side of the visitor pattern - - def start_accepting - @res = "" - @in_list_entry = [] - end - - def end_accepting - @res - end - - def accept_paragraph(am, fragment) - @res << annotate("<p>") + "\n" - @res << wrap(convert_flow(am.flow(fragment.txt))) - @res << annotate("</p>") + "\n" - end - - def accept_verbatim(am, fragment) - @res << annotate("<pre>") + "\n" - @res << CGI.escapeHTML(fragment.txt) - @res << annotate("</pre>") << "\n" - end - - def accept_rule(am, fragment) - size = fragment.param - size = 10 if size > 10 - @res << "<hr size=\"#{size}\"></hr>" - end - - def accept_list_start(am, fragment) - @res << html_list_name(fragment.type, true) << "\n" - @in_list_entry.push false - end - - def accept_list_end(am, fragment) - if tag = @in_list_entry.pop - @res << annotate(tag) << "\n" - end - @res << html_list_name(fragment.type, false) << "\n" - end - - def accept_list_item(am, fragment) - if tag = @in_list_entry.last - @res << annotate(tag) << "\n" - end - - @res << list_item_start(am, fragment) - - @res << wrap(convert_flow(am.flow(fragment.txt))) << "\n" - - @in_list_entry[-1] = list_end_for(fragment.type) - end - - def accept_blank_line(am, fragment) - # @res << annotate("<p />") << "\n" - end - - def accept_heading(am, fragment) - @res << convert_heading(fragment.head_level, am.flow(fragment.txt)) - end - - ## - # This is a higher speed (if messier) version of wrap - - def wrap(txt, line_len = 76) - res = "" - sp = 0 - ep = txt.length - while sp < ep - # scan back for a space - p = sp + line_len - 1 - if p >= ep - p = ep - else - while p > sp and txt[p] != ?\s - p -= 1 - end - if p <= sp - p = sp + line_len - while p < ep and txt[p] != ?\s - p += 1 - end - end - end - res << txt[sp...p] << "\n" - sp = p - sp += 1 while sp < ep and txt[sp] == ?\s - end - res - end - - private - - def on_tags(res, item) - attr_mask = item.turn_on - return if attr_mask.zero? - - @attr_tags.each do |tag| - if attr_mask & tag.bit != 0 - res << annotate(tag.on) - @in_tt += 1 if tt?(tag) - end - end - end - - def off_tags(res, item) - attr_mask = item.turn_off - return if attr_mask.zero? - - @attr_tags.reverse_each do |tag| - if attr_mask & tag.bit != 0 - @in_tt -= 1 if tt?(tag) - res << annotate(tag.off) - end - end - end - - def convert_flow(flow) - res = "" - - flow.each do |item| - case item - when String - res << convert_string(item) - when RDoc::Markup::AttrChanger - off_tags(res, item) - on_tags(res, item) - when RDoc::Markup::Special - res << convert_special(item) - else - raise "Unknown flow element: #{item.inspect}" - end - end - - res - end - - def convert_string(item) - in_tt? ? convert_string_simple(item) : convert_string_fancy(item) - end - - def convert_string_simple(item) - CGI.escapeHTML item - end - - ## - # some of these patterns are taken from SmartyPants... - - def convert_string_fancy(item) - # convert -- to em-dash, (-- to en-dash) - item.gsub(/---?/, '—'). #gsub(/--/, '–'). - - # convert ... to elipsis (and make sure .... becomes .<elipsis>) - gsub(/\.\.\.\./, '.…').gsub(/\.\.\./, '…'). - - # convert single closing quote - gsub(%r{([^ \t\r\n\[\{\(])\'}, '\1’'). # } - gsub(%r{\'(?=\W|s\b)}, '’'). - - # convert single opening quote - gsub(/'/, '‘'). - - # convert double closing quote - gsub(%r{([^ \t\r\n\[\{\(])\'(?=\W)}, '\1”'). # } - - # convert double opening quote - gsub(/'/, '“'). - - # convert copyright - gsub(/\(c\)/, '©'). - - # convert and registered trademark - gsub(/\(r\)/, '®') - end - - def convert_special(special) - handled = false - RDoc::Markup::Attribute.each_name_of(special.type) do |name| - method_name = "handle_special_#{name}" - if self.respond_to? method_name - special.text = send(method_name, special) - handled = true - end - end - raise "Unhandled special: #{special}" unless handled - special.text - end - - def convert_heading(level, flow) - res = - annotate("<h#{level}>") + - convert_flow(flow) + - annotate("</h#{level}>\n") - end - - def html_list_name(list_type, is_open_tag) - tags = LIST_TYPE_TO_HTML[list_type] || raise("Invalid list type: #{list_type.inspect}") - annotate(tags[ is_open_tag ? 0 : 1]) - end - - def list_item_start(am, fragment) - case fragment.type - when :BULLET, :NUMBER then - annotate("<li>") - - when :UPPERALPHA then - annotate("<li type=\"A\">") - - when :LOWERALPHA then - annotate("<li type=\"a\">") - - when :LABELED then - annotate("<dt>") + - convert_flow(am.flow(fragment.param)) + - annotate("</dt>") + - annotate("<dd>") - - when :NOTE then - annotate("<tr>") + - annotate("<td valign=\"top\">") + - convert_flow(am.flow(fragment.param)) + - annotate("</td>") + - annotate("<td>") - else - raise "Invalid list type" - end - end - - def list_end_for(fragment_type) - case fragment_type - when :BULLET, :NUMBER, :UPPERALPHA, :LOWERALPHA then - "</li>" - when :LABELED then - "</dd>" - when :NOTE then - "</td></tr>" - else - raise "Invalid list type" - end - end - -end - diff --git a/trunk/lib/rdoc/markup/to_html_crossref.rb b/trunk/lib/rdoc/markup/to_html_crossref.rb deleted file mode 100644 index a6f29c5c2c..0000000000 --- a/trunk/lib/rdoc/markup/to_html_crossref.rb +++ /dev/null @@ -1,102 +0,0 @@ -require 'rdoc/markup/to_html' - -## -# Subclass of the RDoc::Markup::ToHtml class that supports looking up words in -# the AllReferences list. Those that are found (like AllReferences in this -# comment) will be hyperlinked - -class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml - - attr_accessor :context - - ## - # We need to record the html path of our caller so we can generate - # correct relative paths for any hyperlinks that we find - - def initialize(from_path, context, show_hash) - raise ArgumentError, 'from_path cannot be nil' if from_path.nil? - super() - - # class names, variable names, or instance variables - @markup.add_special(/( - # A::B.meth(**) (for operator in Fortran95) - \w+(::\w+)*[.\#]\w+(\([\.\w+\*\/\+\-\=\<\>]+\))? - # meth(**) (for operator in Fortran95) - | \#\w+(\([.\w\*\/\+\-\=\<\>]+\))? - | \b([A-Z]\w*(::\w+)*[.\#]\w+) # A::B.meth - | \b([A-Z]\w+(::\w+)*) # A::B - | \#\w+[!?=]? # #meth_name - | \\?\b\w+([_\/\.]+\w+)*[!?=]? # meth_name - )/x, - :CROSSREF) - - @from_path = from_path - @context = context - @show_hash = show_hash - - @seen = {} - end - - ## - # We're invoked when any text matches the CROSSREF pattern - # (defined in MarkUp). If we fine the corresponding reference, - # generate a hyperlink. If the name we're looking for contains - # no punctuation, we look for it up the module/class chain. For - # example, HyperlinkHtml is found, even without the Generator:: - # prefix, because we look for it in module Generator first. - - def handle_special_CROSSREF(special) - name = special.text - - return name if name =~ /\A[a-z]*\z/ - - return @seen[name] if @seen.include? name - - if name[0, 1] == '#' then - lookup = name[1..-1] - name = lookup unless @show_hash - else - lookup = name - end - - - # Find class, module, or method in class or module. - # - # Do not, however, use an if/elsif/else chain to do so. Instead, test - # each possible pattern until one matches. The reason for this is that a - # string like "YAML.txt" could be the txt() class method of class YAML (in - # which case it would match the first pattern, which splits the string - # into container and method components and looks up both) or a filename - # (in which case it would match the last pattern, which just checks - # whether the string as a whole is a known symbol). - - if /([A-Z][\w:]*)[.\#](\w+[!?=]?)/ =~ lookup then - container = $1 - method = $2 - ref = @context.find_symbol container, method - end - - if !ref and - /([A-Za-z][\w:]*)[.\#](\w+(\([\.\w+\*\/\+\-\=\<\>]+\))?)/ =~ lookup then - container = $1 - method = $2 - ref = @context.find_symbol container, method - end - - ref = @context.find_symbol lookup unless ref - - out = if lookup =~ /^\\/ then - $' - elsif ref and ref.document_self then - "<a href=\"#{ref.as_href(@from_path)}\">#{name}</a>" - else - name - end - - @seen[name] = out - - out - end - -end - diff --git a/trunk/lib/rdoc/markup/to_latex.rb b/trunk/lib/rdoc/markup/to_latex.rb deleted file mode 100644 index bbf958f2ed..0000000000 --- a/trunk/lib/rdoc/markup/to_latex.rb +++ /dev/null @@ -1,328 +0,0 @@ -require 'rdoc/markup/formatter' -require 'rdoc/markup/fragments' -require 'rdoc/markup/inline' - -require 'cgi' - -## -# Convert SimpleMarkup to basic LaTeX report format. - -class RDoc::Markup::ToLaTeX < RDoc::Markup::Formatter - - BS = "\020" # \ - OB = "\021" # { - CB = "\022" # } - DL = "\023" # Dollar - - BACKSLASH = "#{BS}symbol#{OB}92#{CB}" - HAT = "#{BS}symbol#{OB}94#{CB}" - BACKQUOTE = "#{BS}symbol#{OB}0#{CB}" - TILDE = "#{DL}#{BS}sim#{DL}" - LESSTHAN = "#{DL}<#{DL}" - GREATERTHAN = "#{DL}>#{DL}" - - def self.l(str) - str.tr('\\', BS).tr('{', OB).tr('}', CB).tr('$', DL) - end - - def l(arg) - RDoc::Markup::ToLaTeX.l(arg) - end - - LIST_TYPE_TO_LATEX = { - :BULLET => [ l("\\begin{itemize}"), l("\\end{itemize}") ], - :NUMBER => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\arabic" ], - :UPPERALPHA => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\Alph" ], - :LOWERALPHA => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\alph" ], - :LABELED => [ l("\\begin{description}"), l("\\end{description}") ], - :NOTE => [ - l("\\begin{tabularx}{\\linewidth}{@{} l X @{}}"), - l("\\end{tabularx}") ], - } - - InlineTag = Struct.new(:bit, :on, :off) - - def initialize - init_tags - @list_depth = 0 - @prev_list_types = [] - end - - ## - # Set up the standard mapping of attributes to LaTeX - - def init_tags - @attr_tags = [ - InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:BOLD), l("\\textbf{"), l("}")), - InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:TT), l("\\texttt{"), l("}")), - InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:EM), l("\\emph{"), l("}")), - ] - end - - ## - # Escape a LaTeX string - - def escape(str) - $stderr.print "FE: ", str if $DEBUG_RDOC - s = str. - sub(/\s+$/, ''). - gsub(/([_\${}&%#])/, "#{BS}\\1"). - gsub(/\\/, BACKSLASH). - gsub(/\^/, HAT). - gsub(/~/, TILDE). - gsub(/</, LESSTHAN). - gsub(/>/, GREATERTHAN). - gsub(/,,/, ",{},"). - gsub(/\`/, BACKQUOTE) - $stderr.print "-> ", s, "\n" if $DEBUG_RDOC - s - end - - ## - # Add a new set of LaTeX tags for an attribute. We allow - # separate start and end tags for flexibility - - def add_tag(name, start, stop) - @attr_tags << InlineTag.new(RDoc::Markup::Attribute.bitmap_for(name), start, stop) - end - - ## - # Here's the client side of the visitor pattern - - def start_accepting - @res = "" - @in_list_entry = [] - end - - def end_accepting - @res.tr(BS, '\\').tr(OB, '{').tr(CB, '}').tr(DL, '$') - end - - def accept_paragraph(am, fragment) - @res << wrap(convert_flow(am.flow(fragment.txt))) - @res << "\n" - end - - def accept_verbatim(am, fragment) - @res << "\n\\begin{code}\n" - @res << fragment.txt.sub(/[\n\s]+\Z/, '') - @res << "\n\\end{code}\n\n" - end - - def accept_rule(am, fragment) - size = fragment.param - size = 10 if size > 10 - @res << "\n\n\\rule{\\linewidth}{#{size}pt}\n\n" - end - - def accept_list_start(am, fragment) - @res << list_name(fragment.type, true) << "\n" - @in_list_entry.push false - end - - def accept_list_end(am, fragment) - if tag = @in_list_entry.pop - @res << tag << "\n" - end - @res << list_name(fragment.type, false) << "\n" - end - - def accept_list_item(am, fragment) - if tag = @in_list_entry.last - @res << tag << "\n" - end - @res << list_item_start(am, fragment) - @res << wrap(convert_flow(am.flow(fragment.txt))) << "\n" - @in_list_entry[-1] = list_end_for(fragment.type) - end - - def accept_blank_line(am, fragment) - # @res << "\n" - end - - def accept_heading(am, fragment) - @res << convert_heading(fragment.head_level, am.flow(fragment.txt)) - end - - ## - # This is a higher speed (if messier) version of wrap - - def wrap(txt, line_len = 76) - res = "" - sp = 0 - ep = txt.length - while sp < ep - # scan back for a space - p = sp + line_len - 1 - if p >= ep - p = ep - else - while p > sp and txt[p] != ?\s - p -= 1 - end - if p <= sp - p = sp + line_len - while p < ep and txt[p] != ?\s - p += 1 - end - end - end - res << txt[sp...p] << "\n" - sp = p - sp += 1 while sp < ep and txt[sp] == ?\s - end - res - end - - private - - def on_tags(res, item) - attr_mask = item.turn_on - return if attr_mask.zero? - - @attr_tags.each do |tag| - if attr_mask & tag.bit != 0 - res << tag.on - end - end - end - - def off_tags(res, item) - attr_mask = item.turn_off - return if attr_mask.zero? - - @attr_tags.reverse_each do |tag| - if attr_mask & tag.bit != 0 - res << tag.off - end - end - end - - def convert_flow(flow) - res = "" - flow.each do |item| - case item - when String - $stderr.puts "Converting '#{item}'" if $DEBUG_RDOC - res << convert_string(item) - when AttrChanger - off_tags(res, item) - on_tags(res, item) - when Special - res << convert_special(item) - else - raise "Unknown flow element: #{item.inspect}" - end - end - res - end - - ## - # some of these patterns are taken from SmartyPants... - - def convert_string(item) - escape(item). - - # convert ... to elipsis (and make sure .... becomes .<elipsis>) - gsub(/\.\.\.\./, '.\ldots{}').gsub(/\.\.\./, '\ldots{}'). - - # convert single closing quote - gsub(%r{([^ \t\r\n\[\{\(])\'}, '\1\''). - gsub(%r{\'(?=\W|s\b)}, "'" ). - - # convert single opening quote - gsub(/'/, '`'). - - # convert double closing quote - gsub(%r{([^ \t\r\n\[\{\(])\"(?=\W)}, "\\1''"). - - # convert double opening quote - gsub(/"/, "``"). - - # convert copyright - gsub(/\(c\)/, '\copyright{}') - - end - - def convert_special(special) - handled = false - Attribute.each_name_of(special.type) do |name| - method_name = "handle_special_#{name}" - if self.respond_to? method_name - special.text = send(method_name, special) - handled = true - end - end - raise "Unhandled special: #{special}" unless handled - special.text - end - - def convert_heading(level, flow) - res = - case level - when 1 then "\\chapter{" - when 2 then "\\section{" - when 3 then "\\subsection{" - when 4 then "\\subsubsection{" - else "\\paragraph{" - end + - convert_flow(flow) + - "}\n" - end - - def list_name(list_type, is_open_tag) - tags = LIST_TYPE_TO_LATEX[list_type] || raise("Invalid list type: #{list_type.inspect}") - if tags[2] # enumerate - if is_open_tag - @list_depth += 1 - if @prev_list_types[@list_depth] != tags[2] - case @list_depth - when 1 - roman = "i" - when 2 - roman = "ii" - when 3 - roman = "iii" - when 4 - roman = "iv" - else - raise("Too deep list: level #{@list_depth}") - end - @prev_list_types[@list_depth] = tags[2] - return l("\\renewcommand{\\labelenum#{roman}}{#{tags[2]}{enum#{roman}}}") + "\n" + tags[0] - end - else - @list_depth -= 1 - end - end - tags[ is_open_tag ? 0 : 1] - end - - def list_item_start(am, fragment) - case fragment.type - when :BULLET, :NUMBER, :UPPERALPHA, :LOWERALPHA then - "\\item " - - when :LABELED then - "\\item[" + convert_flow(am.flow(fragment.param)) + "] " - - when :NOTE then - convert_flow(am.flow(fragment.param)) + " & " - else - raise "Invalid list type" - end - end - - def list_end_for(fragment_type) - case fragment_type - when :BULLET, :NUMBER, :UPPERALPHA, :LOWERALPHA, :LABELED then - "" - when :NOTE - "\\\\\n" - else - raise "Invalid list type" - end - end - -end - diff --git a/trunk/lib/rdoc/markup/to_test.rb b/trunk/lib/rdoc/markup/to_test.rb deleted file mode 100644 index ce6aff6e9a..0000000000 --- a/trunk/lib/rdoc/markup/to_test.rb +++ /dev/null @@ -1,50 +0,0 @@ -require 'rdoc/markup' -require 'rdoc/markup/formatter' - -## -# This Markup outputter is used for testing purposes. - -class RDoc::Markup::ToTest < RDoc::Markup::Formatter - - def start_accepting - @res = [] - end - - def end_accepting - @res - end - - def accept_paragraph(am, fragment) - @res << fragment.to_s - end - - def accept_verbatim(am, fragment) - @res << fragment.to_s - end - - def accept_list_start(am, fragment) - @res << fragment.to_s - end - - def accept_list_end(am, fragment) - @res << fragment.to_s - end - - def accept_list_item(am, fragment) - @res << fragment.to_s - end - - def accept_blank_line(am, fragment) - @res << fragment.to_s - end - - def accept_heading(am, fragment) - @res << fragment.to_s - end - - def accept_rule(am, fragment) - @res << fragment.to_s - end - -end - diff --git a/trunk/lib/rdoc/markup/to_texinfo.rb b/trunk/lib/rdoc/markup/to_texinfo.rb deleted file mode 100644 index 533d3e34a0..0000000000 --- a/trunk/lib/rdoc/markup/to_texinfo.rb +++ /dev/null @@ -1,69 +0,0 @@ -require 'rdoc/markup/formatter' -require 'rdoc/markup/fragments' -require 'rdoc/markup/inline' - -require 'rdoc/markup' -require 'rdoc/markup/formatter' - -## -# Convert SimpleMarkup to basic TexInfo format -# -# TODO: WTF is AttributeManager for? -# -class RDoc::Markup::ToTexInfo < RDoc::Markup::Formatter - - def start_accepting - @text = [] - end - - def end_accepting - @text.join("\n") - end - - def accept_paragraph(attributes, text) - @text << format(text) - end - - def accept_verbatim(attributes, text) - @text << "@verb{|#{format(text)}|}" - end - - def accept_heading(attributes, text) - heading = ['@majorheading', '@chapheading'][text.head_level - 1] || '@heading' - @text << "#{heading}{#{format(text)}}" - end - - def accept_list_start(attributes, text) - @text << '@itemize @bullet' - end - - def accept_list_end(attributes, text) - @text << '@end itemize' - end - - def accept_list_item(attributes, text) - @text << "@item\n#{format(text)}" - end - - def accept_blank_line(attributes, text) - @text << "\n" - end - - def accept_rule(attributes, text) - @text << '-----' - end - - def format(text) - text.txt. - gsub(/@/, "@@"). - gsub(/\{/, "@{"). - gsub(/\}/, "@}"). - # gsub(/,/, "@,"). # technically only required in cross-refs - gsub(/\+([\w]+)\+/, "@code{\\1}"). - gsub(/\<tt\>([^<]+)\<\/tt\>/, "@code{\\1}"). - gsub(/\*([\w]+)\*/, "@strong{\\1}"). - gsub(/\<b\>([^<]+)\<\/b\>/, "@strong{\\1}"). - gsub(/_([\w]+)_/, "@emph{\\1}"). - gsub(/\<em\>([^<]+)\<\/em\>/, "@emph{\\1}") - end -end diff --git a/trunk/lib/rdoc/options.rb b/trunk/lib/rdoc/options.rb deleted file mode 100644 index d683a14022..0000000000 --- a/trunk/lib/rdoc/options.rb +++ /dev/null @@ -1,639 +0,0 @@ -# We handle the parsing of options, and subsequently as a singleton -# object to be queried for option values - -require "rdoc/ri/paths" -require 'optparse' - -class RDoc::Options - - ## - # Should the output be placed into a single file - - attr_reader :all_one_file - - ## - # Character-set - - attr_reader :charset - - ## - # URL of stylesheet - - attr_reader :css - - ## - # Should diagrams be drawn - - attr_reader :diagram - - ## - # Files matching this pattern will be excluded - - attr_accessor :exclude - - ## - # Additional attr_... style method flags - - attr_reader :extra_accessor_flags - - ## - # Pattern for additional attr_... style methods - - attr_accessor :extra_accessors - - ## - # Should we draw fileboxes in diagrams - - attr_reader :fileboxes - - ## - # The list of files to be processed - - attr_accessor :files - - ## - # Scan newer sources than the flag file if true. - - attr_reader :force_update - - ## - # Description of the output generator (set with the <tt>-fmt</tt> option) - - attr_accessor :generator - - ## - # Formatter to mark up text with - - attr_accessor :formatter - - ## - # image format for diagrams - - attr_reader :image_format - - ## - # Include line numbers in the source listings - - attr_reader :include_line_numbers - - ## - # Should source code be included inline, or displayed in a popup - - attr_accessor :inline_source - - ## - # Name of the file, class or module to display in the initial index page (if - # not specified the first file we encounter is used) - - attr_accessor :main_page - - ## - # Merge into classes of the same name when generating ri - - attr_reader :merge - - ## - # The name of the output directory - - attr_accessor :op_dir - - ## - # The name to use for the output - - attr_accessor :op_name - - ## - # Are we promiscuous about showing module contents across multiple files - - attr_reader :promiscuous - - ## - # Array of directories to search for files to satisfy an :include: - - attr_reader :rdoc_include - - ## - # Include private and protected methods in the output - - attr_accessor :show_all - - ## - # Include the '#' at the front of hyperlinked instance method names - - attr_reader :show_hash - - ## - # The number of columns in a tab - - attr_reader :tab_width - - ## - # template to be used when generating output - - attr_reader :template - - ## - # Template class for file generation - #-- - # HACK around dependencies in lib/rdoc/generator/html.rb - - attr_accessor :template_class # :nodoc: - - ## - # Documentation title - - attr_reader :title - - ## - # Verbosity, zero means quiet - - attr_accessor :verbosity - - ## - # URL of web cvs frontend - - attr_reader :webcvs - - def initialize(generators = {}) # :nodoc: - @op_dir = "doc" - @op_name = nil - @show_all = false - @main_page = nil - @merge = false - @exclude = [] - @generators = generators - @generator_name = 'html' - @generator = @generators[@generator_name] - @rdoc_include = [] - @title = nil - @template = nil - @template_class = nil - @diagram = false - @fileboxes = false - @show_hash = false - @image_format = 'png' - @inline_source = false - @all_one_file = false - @tab_width = 8 - @include_line_numbers = false - @extra_accessor_flags = {} - @promiscuous = false - @force_update = false - @verbosity = 1 - - @css = nil - @webcvs = nil - - @charset = 'iso-8859-1' - end - - ## - # Parse command line options. - - def parse(argv) - accessors = [] - - opts = OptionParser.new do |opt| - opt.program_name = File.basename $0 - opt.version = RDoc::VERSION - opt.summary_indent = ' ' * 4 - opt.banner = <<-EOF -Usage: #{opt.program_name} [options] [names...] - - Files are parsed, and the information they contain collected, before any - output is produced. This allows cross references between all files to be - resolved. If a name is a directory, it is traversed. If no names are - specified, all Ruby files in the current directory (and subdirectories) are - processed. - - How RDoc generates output depends on the output formatter being used, and on - the options you give. - - - HTML output is normally produced into a number of separate files - (one per class, module, and file, along with various indices). - These files will appear in the directory given by the --op - option (doc/ by default). - - - XML output by default is written to standard output. If a - --opname option is given, the output will instead be written - to a file with that name in the output directory. - - - .chm files (Windows help files) are written in the --op directory. - If an --opname parameter is present, that name is used, otherwise - the file will be called rdoc.chm. - EOF - - opt.separator nil - opt.separator "Options:" - opt.separator nil - - opt.on("--accessor=ACCESSORS", "-A", Array, - "A comma separated list of additional class", - "methods that should be treated like", - "'attr_reader' and friends.", - " ", - "Option may be repeated.", - " ", - "Each accessorname may have '=text'", - "appended, in which case that text appears", - "where the r/w/rw appears for normal.", - "accessors") do |value| - value.each do |accessor| - if accessor =~ /^(\w+)(=(.*))?$/ - accessors << $1 - @extra_accessor_flags[$1] = $3 - end - end - end - - opt.separator nil - - opt.on("--all", "-a", - "Include all methods (not just public) in", - "the output.") do |value| - @show_all = value - end - - opt.separator nil - - opt.on("--charset=CHARSET", "-c", - "Specifies the HTML character-set.") do |value| - @charset = value - end - - opt.separator nil - - opt.on("--debug", "-D", - "Displays lots on internal stuff.") do |value| - $DEBUG_RDOC = value - end - - opt.separator nil - - opt.on("--diagram", "-d", - "Generate diagrams showing modules and", - "classes. You need dot V1.8.6 or later to", - "use the --diagram option correctly. Dot is", - "available from http://graphviz.org") do |value| - check_diagram - @diagram = true - end - - opt.separator nil - - opt.on("--exclude=PATTERN", "-x", Regexp, - "Do not process files or directories", - "matching PATTERN. Files given explicitly", - "on the command line will never be", - "excluded.") do |value| - @exclude << value - end - - opt.separator nil - - opt.on("--extension=NEW=OLD", "-E", - "Treat files ending with .new as if they", - "ended with .old. Using '-E cgi=rb' will", - "cause xxx.cgi to be parsed as a Ruby file.") do |value| - new, old = value.split(/=/, 2) - - unless new and old then - raise OptionParser::InvalidArgument, "Invalid parameter to '-E'" - end - - unless RDoc::ParserFactory.alias_extension old, new then - raise OptionParser::InvalidArgument, "Unknown extension .#{old} to -E" - end - end - - opt.separator nil - - opt.on("--fileboxes", "-F", - "Classes are put in boxes which represents", - "files, where these classes reside. Classes", - "shared between more than one file are", - "shown with list of files that are sharing", - "them. Silently discarded if --diagram is", - "not given.") do |value| - @fileboxes = value - end - - opt.separator nil - - opt.on("--force-update", "-U", - "Forces rdoc to scan all sources even if", - "newer than the flag file.") do |value| - @force_update = value - end - - opt.separator nil - - opt.on("--fmt=FORMAT", "--format=FORMAT", "-f", @generators.keys, - "Set the output formatter.") do |value| - @generator_name = value.downcase - setup_generator - end - - opt.separator nil - - image_formats = %w[gif png jpg jpeg] - opt.on("--image-format=FORMAT", "-I", image_formats, - "Sets output image format for diagrams. Can", - "be #{image_formats.join ', '}. If this option", - "is omitted, png is used. Requires", - "diagrams.") do |value| - @image_format = value - end - - opt.separator nil - - opt.on("--include=DIRECTORIES", "-i", Array, - "set (or add to) the list of directories to", - "be searched when satisfying :include:", - "requests. Can be used more than once.") do |value| - @rdoc_include.concat value.map { |dir| dir.strip } - end - - opt.separator nil - - opt.on("--inline-source", "-S", - "Show method source code inline, rather than", - "via a popup link.") do |value| - @inline_source = value - end - - opt.separator nil - - opt.on("--line-numbers", "-N", - "Include line numbers in the source code.") do |value| - @include_line_numbers = value - end - - opt.separator nil - - opt.on("--main=NAME", "-m", - "NAME will be the initial page displayed.") do |value| - @main_page = value - end - - opt.separator nil - - opt.on("--merge", "-M", - "When creating ri output, merge previously", - "processed classes into previously", - "documented classes of the same name.") do |value| - @merge = value - end - - opt.separator nil - - opt.on("--one-file", "-1", - "Put all the output into a single file.") do |value| - @all_one_file = value - @inline_source = value if value - @template = 'one_page_html' - end - - opt.separator nil - - opt.on("--op=DIR", "-o", - "Set the output directory.") do |value| - @op_dir = value - end - - opt.separator nil - - opt.on("--opname=NAME", "-n", - "Set the NAME of the output. Has no effect", - "for HTML.") do |value| - @op_name = value - end - - opt.separator nil - - opt.on("--promiscuous", "-p", - "When documenting a file that contains a", - "module or class also defined in other", - "files, show all stuff for that module or", - "class in each files page. By default, only", - "show stuff defined in that particular file.") do |value| - @promiscuous = value - end - - opt.separator nil - - opt.on("--quiet", "-q", - "Don't show progress as we parse.") do |value| - @verbosity = 0 - end - - opt.on("--verbose", "-v", - "Display extra progress as we parse.") do |value| - @verbosity = 2 - end - - - opt.separator nil - - opt.on("--ri", "-r", - "Generate output for use by `ri`. The files", - "are stored in the '.rdoc' directory under", - "your home directory unless overridden by a", - "subsequent --op parameter, so no special", - "privileges are needed.") do |value| - @generator_name = "ri" - @op_dir = RDoc::RI::Paths::HOMEDIR - setup_generator - end - - opt.separator nil - - opt.on("--ri-site", "-R", - "Generate output for use by `ri`. The files", - "are stored in a site-wide directory,", - "making them accessible to others, so", - "special privileges are needed.") do |value| - @generator_name = "ri" - @op_dir = RDoc::RI::Paths::SITEDIR - setup_generator - end - - opt.separator nil - - opt.on("--ri-system", "-Y", - "Generate output for use by `ri`. The files", - "are stored in a site-wide directory,", - "making them accessible to others, so", - "special privileges are needed. This", - "option is intended to be used during Ruby", - "installation.") do |value| - @generator_name = "ri" - @op_dir = RDoc::RI::Paths::SYSDIR - setup_generator - end - - opt.separator nil - - opt.on("--show-hash", "-H", - "A name of the form #name in a comment is a", - "possible hyperlink to an instance method", - "name. When displayed, the '#' is removed", - "unless this option is specified.") do |value| - @show_hash = value - end - - opt.separator nil - - opt.on("--style=URL", "-s", - "Specifies the URL of a separate stylesheet.") do |value| - @css = value - end - - opt.separator nil - - opt.on("--tab-width=WIDTH", "-w", OptionParser::DecimalInteger, - "Set the width of tab characters.") do |value| - @tab_width = value - end - - opt.separator nil - - opt.on("--template=NAME", "-T", - "Set the template used when generating", - "output.") do |value| - @template = value - end - - opt.separator nil - - opt.on("--title=TITLE", "-t", - "Set TITLE as the title for HTML output.") do |value| - @title = value - end - - opt.separator nil - - opt.on("--webcvs=URL", "-W", - "Specify a URL for linking to a web frontend", - "to CVS. If the URL contains a '\%s', the", - "name of the current file will be", - "substituted; if the URL doesn't contain a", - "'\%s', the filename will be appended to it.") do |value| - @webcvs = value - end - end - - argv.insert(0, *ENV['RDOCOPT'].split) if ENV['RDOCOPT'] - - opts.parse! argv - - @files = argv.dup - - @rdoc_include << "." if @rdoc_include.empty? - - if @exclude.empty? then - @exclude = nil - else - @exclude = Regexp.new(@exclude.join("|")) - end - - check_files - - # If no template was specified, use the default template for the output - # formatter - - @template ||= @generator_name - - # Generate a regexp from the accessors - unless accessors.empty? then - re = '^(' + accessors.map { |a| Regexp.quote a }.join('|') + ')$' - @extra_accessors = Regexp.new re - end - - rescue OptionParser::InvalidArgument, OptionParser::InvalidOption => e - puts opts - puts - puts e - exit 1 - end - - ## - # Set the title, but only if not already set. This means that a title set - # from the command line trumps one set in a source file - - def title=(string) - @title ||= string - end - - ## - # Don't display progress as we process the files - - def quiet - @verbosity.zero? - end - - def quiet=(bool) - @verbosity = bool ? 0 : 1 - end - - private - - ## - # Set up an output generator for the format in @generator_name - - def setup_generator - @generator = @generators[@generator_name] - - unless @generator then - raise OptionParser::InvalidArgument, "Invalid output formatter" - end - - if @generator_name == "xml" then - @all_one_file = true - @inline_source = true - end - end - - # Check that the right version of 'dot' is available. Unfortunately this - # doesn't work correctly under Windows NT, so we'll bypass the test under - # Windows. - - def check_diagram - return if RUBY_PLATFORM =~ /mswin|cygwin|mingw|bccwin/ - - ok = false - ver = nil - - IO.popen "dot -V 2>&1" do |io| - ver = io.read - if ver =~ /dot.+version(?:\s+gviz)?\s+(\d+)\.(\d+)/ then - ok = ($1.to_i > 1) || ($1.to_i == 1 && $2.to_i >= 8) - end - end - - unless ok then - if ver =~ /^dot.+version/ then - $stderr.puts "Warning: You may need dot V1.8.6 or later to use\n", - "the --diagram option correctly. You have:\n\n ", - ver, - "\nDiagrams might have strange background colors.\n\n" - else - $stderr.puts "You need the 'dot' program to produce diagrams.", - "(see http://www.research.att.com/sw/tools/graphviz/)\n\n" - exit - end - end - end - - ## - # Check that the files on the command line exist - - def check_files - @files.each do |f| - stat = File.stat f - raise RDoc::Error, "file '#{f}' not readable" unless stat.readable? - end - end - -end - diff --git a/trunk/lib/rdoc/parser.rb b/trunk/lib/rdoc/parser.rb deleted file mode 100644 index 794fad00e9..0000000000 --- a/trunk/lib/rdoc/parser.rb +++ /dev/null @@ -1,109 +0,0 @@ -require 'rdoc' -require 'rdoc/code_objects' -require 'rdoc/markup/preprocess' -require 'rdoc/stats' - -## -# A parser is simple a class that implements -# -# #initialize(file_name, body, options) -# -# and -# -# #scan -# -# The initialize method takes a file name to be used, the body of the file, -# and an RDoc::Options object. The scan method is then called to return an -# appropriately parsed TopLevel code object. -# -# The ParseFactory is used to redirect to the correct parser given a -# filename extension. This magic works because individual parsers have to -# register themselves with us as they are loaded in. The do this using the -# following incantation -# -# require "rdoc/parser" -# -# class RDoc::Parser::Xyz < RDoc::Parser -# parse_files_matching /\.xyz$/ # <<<< -# -# def initialize(file_name, body, options) -# ... -# end -# -# def scan -# ... -# end -# end -# -# Just to make life interesting, if we suspect a plain text file, we also -# look for a shebang line just in case it's a potential shell script - -class RDoc::Parser - - @parsers = [] - - class << self - attr_reader :parsers - end - - ## - # Alias an extension to another extension. After this call, files ending - # "new_ext" will be parsed using the same parser as "old_ext" - - def self.alias_extension(old_ext, new_ext) - old_ext = old_ext.sub(/^\.(.*)/, '\1') - new_ext = new_ext.sub(/^\.(.*)/, '\1') - - parser = can_parse "xxx.#{old_ext}" - return false unless parser - - RDoc::Parser.parsers.unshift [/\.#{new_ext}$/, parser] - - true - end - - ## - # Return a parser that can handle a particular extension - - def self.can_parse(file_name) - RDoc::Parser.parsers.find { |regexp, parser| regexp =~ file_name }.last - end - - ## - # Find the correct parser for a particular file name. Return a SimpleParser - # for ones that we don't know - - def self.for(top_level, file_name, body, options, stats) - # If no extension, look for shebang - if file_name !~ /\.\w+$/ && body =~ %r{\A#!(.+)} then - shebang = $1 - case shebang - when %r{env\s+ruby}, %r{/ruby} - file_name = "dummy.rb" - end - end - - parser = can_parse file_name - - parser.new top_level, file_name, body, options, stats - end - - ## - # Record which file types this parser can understand. - - def self.parse_files_matching(regexp) - RDoc::Parser.parsers.unshift [regexp, self] - end - - def initialize(top_level, file_name, content, options, stats) - @top_level = top_level - @file_name = file_name - @content = content - @options = options - @stats = stats - end - -end - -require 'rdoc/parser/simple' - diff --git a/trunk/lib/rdoc/parser/c.rb b/trunk/lib/rdoc/parser/c.rb deleted file mode 100644 index 43bb767da9..0000000000 --- a/trunk/lib/rdoc/parser/c.rb +++ /dev/null @@ -1,656 +0,0 @@ -require 'rdoc/parser' -require 'rdoc/known_classes' - -## -# We attempt to parse C extension files. Basically we look for -# the standard patterns that you find in extensions: <tt>rb_define_class, -# rb_define_method</tt> and so on. We also try 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 -# <tt>rb_define_method</tt> 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(ary) -# VALUE ary; -# { -# ary = rb_obj_dup(ary); -# rb_ary_flatten_bang(ary); -# return ary; -# } -# -# ... -# -# void -# Init_Array() -# { -# ... -# 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. -# -# C classes can be diagrammed (see /tc/dl/ruby/ruby/error.c), and RDoc -# integrates C and Ruby source into one tree -# -# The comment blocks may include special directives: -# -# [Document-class: <i>name</i>] -# This comment block is documentation for the given class. Use this -# when the <tt>Init_xxx</tt> method is not named after the class. -# -# [Document-method: <i>name</i>] -# This comment documents the named method. Use when RDoc cannot -# automatically find the method from it's declaration -# -# [call-seq: <i>text up to an empty line</i>] -# Because C source doesn't give descripive 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. -# */ - -class RDoc::Parser::C < RDoc::Parser - - parse_files_matching(/\.(?:([CcHh])\1?|c([+xp])\2|y)\z/) - - @@enclosure_classes = {} - @@known_bodies = {} - - ## - # Prepare to parse a C file - - def initialize(top_level, file_name, content, options, stats) - super - - @known_classes = RDoc::KNOWN_CLASSES.dup - @content = handle_tab_width handle_ifdefs_in(@content) - @classes = Hash.new - @file_dir = File.dirname(@file_name) - end - - def do_aliases - @content.scan(%r{rb_define_alias\s*\(\s*(\w+),\s*"([^"]+)",\s*"([^"]+)"\s*\)}m) do - |var_name, new_name, old_name| - class_name = @known_classes[var_name] || var_name - class_obj = find_class(var_name, class_name) - - as = class_obj.add_alias RDoc::Alias.new("", old_name, new_name, "") - - @stats.add_alias as - end - end - - def do_classes - @content.scan(/(\w+)\s* = \s*rb_define_module\s*\(\s*"(\w+)"\s*\)/mx) do - |var_name, class_name| - handle_class_module(var_name, "module", class_name, nil, nil) - end - - # The '.' lets us handle SWIG-generated files - @content.scan(/([\w\.]+)\s* = \s*rb_define_class\s* - \( - \s*"(\w+)", - \s*(\w+)\s* - \)/mx) do |var_name, class_name, parent| - handle_class_module(var_name, "class", class_name, parent, nil) - end - - @content.scan(/(\w+)\s*=\s*boot_defclass\s*\(\s*"(\w+?)",\s*(\w+?)\s*\)/) do - |var_name, class_name, parent| - parent = nil if parent == "0" - handle_class_module(var_name, "class", class_name, parent, nil) - end - - @content.scan(/(\w+)\s* = \s*rb_define_module_under\s* - \( - \s*(\w+), - \s*"(\w+)" - \s*\)/mx) do |var_name, in_module, class_name| - handle_class_module(var_name, "module", class_name, nil, in_module) - end - - @content.scan(/([\w\.]+)\s* = \s*rb_define_class_under\s* - \( - \s*(\w+), - \s*"(\w+)", - \s*(\w+)\s* - \s*\)/mx) do |var_name, in_module, class_name, parent| - handle_class_module(var_name, "class", class_name, parent, in_module) - end - end - - 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 - end - - ## - # Look for includes of the form: - # - # rb_include_module(rb_cArray, rb_mEnumerable); - - def do_includes - @content.scan(/rb_include_module\s*\(\s*(\w+?),\s*(\w+?)\s*\)/) do |c,m| - if cls = @classes[c] - m = @known_classes[m] || m - cls.add_include RDoc::Include.new(m, "") - end - end - end - - def do_methods - @content.scan(%r{rb_define_ - ( - singleton_method | - method | - module_function | - private_method - ) - \s*\(\s*([\w\.]+), - \s*"([^"]+)", - \s*(?:RUBY_METHOD_FUNC\(|VALUEFUNC\()?(\w+)\)?, - \s*(-?\w+)\s*\) - (?:;\s*/[*/]\s+in\s+(\w+?\.[cy]))? - }xm) do - |type, var_name, meth_name, meth_body, 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" - next if var_name == "envtbl" - next if var_name == "argf" # it'd be nice to handle this one - - var_name = "rb_cObject" if var_name == "rb_mKernel" - handle_method(type, var_name, meth_name, - meth_body, param_count, source_file) - end - - @content.scan(%r{rb_define_attr\( - \s*([\w\.]+), - \s*"([^"]+)", - \s*(\d+), - \s*(\d+)\s*\); - }xm) do |var_name, attr_name, attr_reader, attr_writer| - #var_name = "rb_cObject" if var_name == "rb_mKernel" - handle_attr(var_name, attr_name, - attr_reader.to_i != 0, - attr_writer.to_i != 0) - 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, meth_body, param_count, source_file| - handle_method("method", "rb_mKernel", meth_name, - meth_body, 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, meth_body, param_count| - - handle_method("method", "rb_mFileTest", meth_name, meth_body, param_count) - handle_method("singleton_method", "rb_cFile", meth_name, meth_body, param_count) - end - end - - def find_attr_comment(attr_name) - if @content =~ %r{((?>/\*.*?\*/\s+)) - rb_define_attr\((?:\s*(\w+),)?\s*"#{attr_name}"\s*,.*?\)\s*;}xmi - $1 - elsif @content =~ %r{Document-attr:\s#{attr_name}\s*?\n((?>.*?\*/))}m - $1 - else - '' - end - end - - ## - # Find the C code corresponding to a Ruby method - - def find_body(meth_name, meth_obj, body, quiet = false) - case body - when %r"((?>/\*.*?\*/\s*))(?:static\s+)?VALUE\s+#{meth_name} - \s*(\([^)]*\))\s*\{.*?^\}"xm - comment, params = $1, $2 - body_text = $& - - remove_private_comments(comment) if comment - - # see if we can find the whole body - - re = Regexp.escape(body_text) + '[^(]*^\{.*?^\}' - if Regexp.new(re, Regexp::MULTILINE).match(body) - body_text = $& - end - - # 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(meth_obj.name) - comment = override_comment if override_comment - - find_modifiers(comment, meth_obj) if comment - -# meth_obj.params = params - meth_obj.start_collecting_tokens - meth_obj.add_token(RDoc::RubyToken::Token.new(1,1).set_text(body_text)) - meth_obj.comment = mangle_comment(comment) - when %r{((?>/\*.*?\*/\s*))^\s*\#\s*define\s+#{meth_name}\s+(\w+)}m - comment = $1 - find_body($2, meth_obj, body, true) - find_modifiers(comment, meth_obj) - meth_obj.comment = mangle_comment(comment) + meth_obj.comment - when %r{^\s*\#\s*define\s+#{meth_name}\s+(\w+)}m - unless find_body($1, meth_obj, body, true) - warn "No definition for #{meth_name}" unless @options.quiet - return false - end - else - - # No body, but might still have an override comment - comment = find_override_comment(meth_obj.name) - - if comment - find_modifiers(comment, meth_obj) - meth_obj.comment = mangle_comment(comment) - else - warn "No definition for #{meth_name}" unless @options.quiet - return false - end - end - true - end - - def find_class(raw_name, name) - 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, nil - end - - container.record_location @top_level - @classes[raw_name] = container - end - @classes[raw_name] - end - - ## - # 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); - - def find_class_comment(class_name, class_meth) - comment = nil - if @content =~ %r{((?>/\*.*?\*/\s+)) - (static\s+)?void\s+Init_#{class_name}\s*(?:_\(\s*)?\(\s*(?:void\s*)\)}xmi - comment = $1 - elsif @content =~ %r{Document-(class|module):\s#{class_name}\s*?\n((?>.*?\*/))}m - comment = $2 - else - if @content =~ /rb_define_(class|module)/m then - class_name = class_name.split("::").last - comments = [] - @content.split(/(\/\*.*?\*\/)\s*?\n/m).each_with_index do |chunk, index| - comments[index] = chunk - if chunk =~ /rb_define_(class|module).*?"(#{class_name})"/m then - comment = comments[index-1] - break - end - end - end - end - class_meth.comment = mangle_comment(comment) if comment - end - - ## - # Finds a comment matching +type+ and +const_name+ either above the - # comment or in the matching Document- section. - - def find_const_comment(type, const_name) - if @content =~ %r{((?>^\s*/\*.*?\*/\s+)) - rb_define_#{type}\((?:\s*(\w+),)?\s*"#{const_name}"\s*,.*?\)\s*;}xmi - $1 - elsif @content =~ %r{Document-(?:const|global|variable):\s#{const_name}\s*?\n((?>.*?\*/))}m - $1 - else - '' - end - end - - ## - # If the comment block contains a section that looks like: - # - # call-seq: - # Array.new - # Array.new(10) - # - # use it for the parameters. - - def find_modifiers(comment, meth_obj) - if comment.sub!(/:nodoc:\s*^\s*\*?\s*$/m, '') or - comment.sub!(/\A\/\*\s*:nodoc:\s*\*\/\Z/, '') - meth_obj.document_self = false - end - if comment.sub!(/call-seq:(.*?)^\s*\*?\s*$/m, '') or - comment.sub!(/\A\/\*\s*call-seq:(.*?)\*\/\Z/, '') - seq = $1 - seq.gsub!(/^\s*\*\s*/, '') - meth_obj.call_seq = seq - end - end - - def find_override_comment(meth_name) - name = Regexp.escape(meth_name) - if @content =~ %r{Document-method:\s#{name}\s*?\n((?>.*?\*/))}m - $1 - end - end - - def handle_attr(var_name, attr_name, reader, writer) - rw = '' - if reader - #@stats.num_methods += 1 - rw << 'R' - end - if writer - #@stats.num_methods += 1 - rw << 'W' - end - - class_name = @known_classes[var_name] - - return unless class_name - - class_obj = find_class(var_name, class_name) - - if class_obj - comment = find_attr_comment(attr_name) - unless comment.empty? - comment = mangle_comment(comment) - end - att = RDoc::Attr.new '', attr_name, rw, comment - class_obj.add_attribute(att) - end - end - - def handle_class_module(var_name, class_mod, class_name, parent, in_module) - parent_name = @known_classes[parent] || parent - - if in_module - enclosure = @classes[in_module] || @@enclosure_classes[in_module] - unless enclosure - if enclosure = @known_classes[in_module] - handle_class_module(in_module, (/^rb_m/ =~ in_module ? "module" : "class"), - enclosure, nil, nil) - enclosure = @classes[in_module] - end - end - unless enclosure - warn("Enclosing class/module '#{in_module}' for " + - "#{class_mod} #{class_name} not known") - return - end - else - enclosure = @top_level - end - - if class_mod == "class" then - cm = enclosure.add_class RDoc::NormalClass, class_name, parent_name - @stats.add_class cm - else - cm = enclosure.add_module RDoc::NormalModule, class_name - @stats.add_module cm - end - - cm.record_location(enclosure.toplevel) - - find_class_comment(cm.full_name, cm) - @classes[var_name] = cm - @@enclosure_classes[var_name] = cm - @known_classes[var_name] = cm.full_name - end - - ## - # Adds constant comments. By providing some_value: at the start ofthe - # 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 (\:). - - def handle_constants(type, var_name, const_name, definition) - #@stats.num_constants += 1 - class_name = @known_classes[var_name] - - return unless class_name - - class_obj = find_class(var_name, class_name) - - unless class_obj - warn("Enclosing class/module '#{const_name}' for not known") - return - end - - comment = find_const_comment(type, const_name) - - # 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 - elements = mangle_comment(comment).split(':') - if elements.nil? or elements.empty? then - con = RDoc::Constant.new(const_name, definition, - mangle_comment(comment)) - else - new_definition = elements[0..-2].join(':') - if new_definition.empty? then # Default to literal C definition - new_definition = definition - else - new_definition.gsub!("\:", ":") - new_definition.gsub!("\\", '\\') - end - new_definition.sub!(/\A(\s+)/, '') - new_comment = $1.nil? ? elements.last : "#{$1}#{elements.last.lstrip}" - con = RDoc::Constant.new(const_name, new_definition, - mangle_comment(new_comment)) - end - else - con = RDoc::Constant.new const_name, definition, mangle_comment(comment) - end - - class_obj.add_constant(con) - end - - ## - # Removes #ifdefs that would otherwise confuse us - - def handle_ifdefs_in(body) - body.gsub(/^#ifdef HAVE_PROTOTYPES.*?#else.*?\n(.*?)#endif.*?\n/m, '\1') - end - - def handle_method(type, var_name, meth_name, meth_body, param_count, - source_file = nil) - class_name = @known_classes[var_name] - - return unless class_name - - class_obj = find_class(var_name, class_name) - - if class_obj - if meth_name == "initialize" - meth_name = "new" - type = "singleton_method" - end - meth_obj = RDoc::AnyMethod.new("", meth_name) - meth_obj.singleton = - %w{singleton_method module_function}.include?(type) - - @stats.add_method meth_obj - - p_count = (Integer(param_count) rescue -1) - - if p_count < 0 - meth_obj.params = "(...)" - elsif p_count == 0 - meth_obj.params = "()" - else - meth_obj.params = "(" + (1..p_count).map{|i| "p#{i}"}.join(", ") + ")" - end - - if source_file - file_name = File.join(@file_dir, source_file) - body = (@@known_bodies[source_file] ||= File.read(file_name)) - else - body = @content - end - if find_body(meth_body, meth_obj, body) and meth_obj.document_self - class_obj.add_method(meth_obj) - end - end - end - - def handle_tab_width(body) - if /\t/ =~ body - tab_width = @options.tab_width - body.split(/\n/).map do |line| - 1 while line.gsub!(/\t+/) { ' ' * (tab_width*$&.length - $`.length % tab_width)} && $~ #` - line - end .join("\n") - else - body - end - end - - ## - # Remove the /*'s and leading asterisks from C comments - - def mangle_comment(comment) - comment.sub!(%r{/\*+}) { " " * $&.length } - comment.sub!(%r{\*+/}) { " " * $&.length } - comment.gsub!(/^[ \t]*\*/m) { " " * $&.length } - comment - end - - ## - # Removes lines that are commented out that might otherwise get picked up - # when scanning for classes and methods - - def remove_commented_out_lines - @content.gsub!(%r{//.*rb_define_}, '//') - end - - def remove_private_comments(comment) - comment.gsub!(/\/?\*--(.*?)\/?\*\+\+/m, '') - comment.sub!(/\/?\*--.*/m, '') - end - - ## - # Extract the classes/modules and methods from a C file and return the - # corresponding top-level object - - def scan - remove_commented_out_lines - do_classes - do_constants - do_methods - do_includes - do_aliases - @top_level - end - - def warn(msg) - $stderr.puts - $stderr.puts msg - $stderr.flush - end - -end - diff --git a/trunk/lib/rdoc/parser/f95.rb b/trunk/lib/rdoc/parser/f95.rb deleted file mode 100644 index fd372b098b..0000000000 --- a/trunk/lib/rdoc/parser/f95.rb +++ /dev/null @@ -1,1835 +0,0 @@ -require 'rdoc/parser' - -## -# = Fortran95 RDoc Parser -# -# == Overview -# -# This parser parses Fortran95 files with suffixes "f90", "F90", "f95" and -# "F95". Fortran95 files are expected to be conformed to Fortran95 standards. -# -# == Rules -# -# Fundamental rules are same as that of the Ruby parser. But comment markers -# are '!' not '#'. -# -# === Correspondence between RDoc documentation and Fortran95 programs -# -# F95 parses main programs, modules, subroutines, functions, derived-types, -# public variables, public constants, defined operators and defined -# assignments. These components are described in items of RDoc documentation, -# as follows. -# -# Files :: Files (same as Ruby) -# Classes:: Modules -# Methods:: Subroutines, functions, variables, constants, derived-types, -# defined operators, defined assignments -# Required files:: Files in which imported modules, external subroutines and -# external functions are defined. -# Included Modules:: List of imported modules -# Attributes:: List of derived-types, List of imported modules all of whose -# components are published again -# -# Components listed in 'Methods' (subroutines, functions, ...) defined in -# modules are described in the item of 'Classes'. On the other hand, -# components defined in main programs or as external procedures are described -# in the item of 'Files'. -# -# === Components parsed by default -# -# By default, documentation on public components (subroutines, functions, -# variables, constants, derived-types, defined operators, defined assignments) -# are generated. -# -# With "--all" option, documentation on all components are generated (almost -# same as the Ruby parser). -# -# === Information parsed automatically -# -# The following information is automatically parsed. -# -# * Types of arguments -# * Types of variables and constants -# * Types of variables in the derived types, and initial values -# * NAMELISTs and types of variables in them, and initial values -# -# Aliases by interface statement are described in the item of 'Methods'. -# -# Components which are imported from other modules and published again are -# described in the item of 'Methods'. -# -# === Format of comment blocks -# -# Comment blocks should be written as follows. -# -# Comment blocks are considered to be ended when the line without '!' appears. -# -# The indentation is not necessary. -# -# ! (Top of file) -# ! -# ! Comment blocks for the files. -# ! -# !-- -# ! The comment described in the part enclosed by -# ! "!--" and "!++" is ignored. -# !++ -# ! -# module hogehoge -# ! -# ! Comment blocks for the modules (or the programs). -# ! -# -# private -# -# logical :: a ! a private variable -# real, public :: b ! a public variable -# integer, parameter :: c = 0 ! a public constant -# -# public :: c -# public :: MULTI_ARRAY -# public :: hoge, foo -# -# type MULTI_ARRAY -# ! -# ! Comment blocks for the derived-types. -# ! -# real, pointer :: var(:) =>null() ! Comments block for the variables. -# integer :: num = 0 -# end type MULTI_ARRAY -# -# contains -# -# subroutine hoge( in, & ! Comment blocks between continuation lines are ignored. -# & out ) -# ! -# ! Comment blocks for the subroutines or functions -# ! -# character(*),intent(in):: in ! Comment blocks for the arguments. -# character(*),intent(out),allocatable,target :: in -# ! Comment blocks can be -# ! written under Fortran statements. -# -# character(32) :: file ! This comment parsed as a variable in below NAMELIST. -# integer :: id -# -# namelist /varinfo_nml/ file, id -# ! -# ! Comment blocks for the NAMELISTs. -# ! Information about variables are described above. -# ! -# -# .... -# -# end subroutine hoge -# -# integer function foo( in ) -# ! -# ! This part is considered as comment block. -# -# ! Comment blocks under blank lines are ignored. -# ! -# integer, intent(in):: inA ! This part is considered as comment block. -# -# ! This part is ignored. -# -# end function foo -# -# subroutine hide( in, & -# & out ) !:nodoc: -# ! -# ! If "!:nodoc:" is described at end-of-line in subroutine -# ! statement as above, the subroutine is ignored. -# ! This assignment can be used to modules, subroutines, -# ! functions, variables, constants, derived-types, -# ! defined operators, defined assignments, -# ! list of imported modules ("use" statement). -# ! -# -# .... -# -# end subroutine hide -# -# end module hogehoge - -class RDoc::Parser::F95 < RDoc::Parser - - parse_files_matching(/\.((f|F)9(0|5)|F)$/) - - class Token - - NO_TEXT = "??".freeze - - def initialize(line_no, char_no) - @line_no = line_no - @char_no = char_no - @text = NO_TEXT - end - # Because we're used in contexts that expect to return a token, - # we set the text string and then return ourselves - def set_text(text) - @text = text - self - end - - attr_reader :line_no, :char_no, :text - - end - - @@external_aliases = [] - @@public_methods = [] - - ## - # "false":: Comments are below source code - # "true" :: Comments are upper source code - - COMMENTS_ARE_UPPER = false - - ## - # Internal alias message - - INTERNAL_ALIAS_MES = "Alias for" - - ## - # External alias message - - EXTERNAL_ALIAS_MES = "The entity is" - - ## - # Define code constructs - - def scan - # remove private comment - remaining_code = remove_private_comments(@content) - - # continuation lines are united to one line - remaining_code = united_to_one_line(remaining_code) - - # semicolons are replaced to line feed - remaining_code = semicolon_to_linefeed(remaining_code) - - # collect comment for file entity - whole_comment, remaining_code = collect_first_comment(remaining_code) - @top_level.comment = whole_comment - - # String "remaining_code" is converted to Array "remaining_lines" - remaining_lines = remaining_code.split("\n") - - # "module" or "program" parts are parsed (new) - # - level_depth = 0 - block_searching_flag = nil - block_searching_lines = [] - pre_comment = [] - module_program_trailing = "" - module_program_name = "" - other_block_level_depth = 0 - other_block_searching_flag = nil - remaining_lines.collect!{|line| - if !block_searching_flag && !other_block_searching_flag - if line =~ /^\s*?module\s+(\w+)\s*?(!.*?)?$/i - block_searching_flag = :module - block_searching_lines << line - module_program_name = $1 - module_program_trailing = find_comments($2) - next false - elsif line =~ /^\s*?program\s+(\w+)\s*?(!.*?)?$/i || - line =~ /^\s*?\w/ && !block_start?(line) - block_searching_flag = :program - block_searching_lines << line - module_program_name = $1 || "" - module_program_trailing = find_comments($2) - next false - - elsif block_start?(line) - other_block_searching_flag = true - next line - - elsif line =~ /^\s*?!\s?(.*)/ - pre_comment << line - next line - else - pre_comment = [] - next line - end - elsif other_block_searching_flag - other_block_level_depth += 1 if block_start?(line) - other_block_level_depth -= 1 if block_end?(line) - if other_block_level_depth < 0 - other_block_level_depth = 0 - other_block_searching_flag = nil - end - next line - end - - block_searching_lines << line - level_depth += 1 if block_start?(line) - level_depth -= 1 if block_end?(line) - if level_depth >= 0 - next false - end - - # "module_program_code" is formatted. - # ":nodoc:" flag is checked. - # - module_program_code = block_searching_lines.join("\n") - module_program_code = remove_empty_head_lines(module_program_code) - if module_program_trailing =~ /^:nodoc:/ - # next loop to search next block - level_depth = 0 - block_searching_flag = false - block_searching_lines = [] - pre_comment = [] - next false - end - - # NormalClass is created, and added to @top_level - # - if block_searching_flag == :module - module_name = module_program_name - module_code = module_program_code - module_trailing = module_program_trailing - - f9x_module = @top_level.add_module NormalClass, module_name - f9x_module.record_location @top_level - - @stats.add_module f9x_module - - f9x_comment = COMMENTS_ARE_UPPER ? - find_comments(pre_comment.join("\n")) + "\n" + module_trailing : - module_trailing + "\n" + find_comments(module_code.sub(/^.*$\n/i, '')) - f9x_module.comment = f9x_comment - parse_program_or_module(f9x_module, module_code) - - TopLevel.all_files.each do |name, toplevel| - if toplevel.include_includes?(module_name, @options.ignore_case) - if !toplevel.include_requires?(@file_name, @options.ignore_case) - toplevel.add_require(Require.new(@file_name, "")) - end - end - toplevel.each_classmodule{|m| - if m.include_includes?(module_name, @options.ignore_case) - if !m.include_requires?(@file_name, @options.ignore_case) - m.add_require(Require.new(@file_name, "")) - end - end - } - end - elsif block_searching_flag == :program - program_name = module_program_name - program_code = module_program_code - program_trailing = module_program_trailing - # progress "p" # HACK what stats thingy does this correspond to? - program_comment = COMMENTS_ARE_UPPER ? - find_comments(pre_comment.join("\n")) + "\n" + program_trailing : - program_trailing + "\n" + find_comments(program_code.sub(/^.*$\n/i, '')) - program_comment = "\n\n= <i>Program</i> <tt>#{program_name}</tt>\n\n" \ - + program_comment - @top_level.comment << program_comment - parse_program_or_module(@top_level, program_code, :private) - end - - # next loop to search next block - level_depth = 0 - block_searching_flag = false - block_searching_lines = [] - pre_comment = [] - next false - } - - remaining_lines.delete_if{ |line| - line == false - } - - # External subprograms and functions are parsed - # - parse_program_or_module(@top_level, remaining_lines.join("\n"), - :public, true) - - @top_level - end # End of scan - - private - - def parse_program_or_module(container, code, - visibility=:public, external=nil) - return unless container - return unless code - remaining_lines = code.split("\n") - remaining_code = "#{code}" - - # - # Parse variables before "contains" in module - # - level_depth = 0 - before_contains_lines = [] - before_contains_code = nil - before_contains_flag = nil - remaining_lines.each{ |line| - if !before_contains_flag - if line =~ /^\s*?module\s+\w+\s*?(!.*?)?$/i - before_contains_flag = true - end - else - break if line =~ /^\s*?contains\s*?(!.*?)?$/i - level_depth += 1 if block_start?(line) - level_depth -= 1 if block_end?(line) - break if level_depth < 0 - before_contains_lines << line - end - } - before_contains_code = before_contains_lines.join("\n") - if before_contains_code - before_contains_code.gsub!(/^\s*?interface\s+.*?\s+end\s+interface.*?$/im, "") - before_contains_code.gsub!(/^\s*?type[\s\,]+.*?\s+end\s+type.*?$/im, "") - end - - # - # Parse global "use" - # - use_check_code = "#{before_contains_code}" - cascaded_modules_list = [] - while use_check_code =~ /^\s*?use\s+(\w+)(.*?)(!.*?)?$/i - use_check_code = $~.pre_match - use_check_code << $~.post_match - used_mod_name = $1.strip.chomp - used_list = $2 || "" - used_trailing = $3 || "" - next if used_trailing =~ /!:nodoc:/ - if !container.include_includes?(used_mod_name, @options.ignore_case) - # progress "." # HACK what stats thingy does this correspond to? - container.add_include Include.new(used_mod_name, "") - end - if ! (used_list =~ /\,\s*?only\s*?:/i ) - cascaded_modules_list << "\#" + used_mod_name - end - end - - # - # Parse public and private, and store information. - # This information is used when "add_method" and - # "set_visibility_for" are called. - # - visibility_default, visibility_info = - parse_visibility(remaining_lines.join("\n"), visibility, container) - @@public_methods.concat visibility_info - if visibility_default == :public - if !cascaded_modules_list.empty? - cascaded_modules = - Attr.new("Cascaded Modules", - "Imported modules all of whose components are published again", - "", - cascaded_modules_list.join(", ")) - container.add_attribute(cascaded_modules) - end - end - - # - # Check rename elements - # - use_check_code = "#{before_contains_code}" - while use_check_code =~ /^\s*?use\s+(\w+)\s*?\,(.+)$/i - use_check_code = $~.pre_match - use_check_code << $~.post_match - used_mod_name = $1.strip.chomp - used_elements = $2.sub(/\s*?only\s*?:\s*?/i, '') - used_elements.split(",").each{ |used| - if /\s*?(\w+)\s*?=>\s*?(\w+)\s*?/ =~ used - local = $1 - org = $2 - @@public_methods.collect!{ |pub_meth| - if local == pub_meth["name"] || - local.upcase == pub_meth["name"].upcase && - @options.ignore_case - pub_meth["name"] = org - pub_meth["local_name"] = local - end - pub_meth - } - end - } - end - - # - # Parse private "use" - # - use_check_code = remaining_lines.join("\n") - while use_check_code =~ /^\s*?use\s+(\w+)(.*?)(!.*?)?$/i - use_check_code = $~.pre_match - use_check_code << $~.post_match - used_mod_name = $1.strip.chomp - used_trailing = $3 || "" - next if used_trailing =~ /!:nodoc:/ - if !container.include_includes?(used_mod_name, @options.ignore_case) - # progress "." # HACK what stats thingy does this correspond to? - container.add_include Include.new(used_mod_name, "") - end - end - - container.each_includes{ |inc| - TopLevel.all_files.each do |name, toplevel| - indicated_mod = toplevel.find_symbol(inc.name, - nil, @options.ignore_case) - if indicated_mod - indicated_name = indicated_mod.parent.file_relative_name - if !container.include_requires?(indicated_name, @options.ignore_case) - container.add_require(Require.new(indicated_name, "")) - end - break - end - end - } - - # - # Parse derived-types definitions - # - derived_types_comment = "" - remaining_code = remaining_lines.join("\n") - while remaining_code =~ /^\s*? - type[\s\,]+(public|private)?\s*?(::)?\s*? - (\w+)\s*?(!.*?)?$ - (.*?) - ^\s*?end\s+type.*?$ - /imx - remaining_code = $~.pre_match - remaining_code << $~.post_match - typename = $3.chomp.strip - type_elements = $5 || "" - type_code = remove_empty_head_lines($&) - type_trailing = find_comments($4) - next if type_trailing =~ /^:nodoc:/ - type_visibility = $1 - type_comment = COMMENTS_ARE_UPPER ? - find_comments($~.pre_match) + "\n" + type_trailing : - type_trailing + "\n" + find_comments(type_code.sub(/^.*$\n/i, '')) - type_element_visibility_public = true - type_code.split("\n").each{ |line| - if /^\s*?private\s*?$/ =~ line - type_element_visibility_public = nil - break - end - } if type_code - - args_comment = "" - type_args_info = nil - - if @options.show_all - args_comment = find_arguments(nil, type_code, true) - else - type_public_args_list = [] - type_args_info = definition_info(type_code) - type_args_info.each{ |arg| - arg_is_public = type_element_visibility_public - arg_is_public = true if arg.include_attr?("public") - arg_is_public = nil if arg.include_attr?("private") - type_public_args_list << arg.varname if arg_is_public - } - args_comment = find_arguments(type_public_args_list, type_code) - end - - type = AnyMethod.new("type #{typename}", typename) - type.singleton = false - type.params = "" - type.comment = "<b><em> Derived Type </em></b> :: <tt></tt>\n" - type.comment << args_comment if args_comment - type.comment << type_comment if type_comment - - @stats.add_method type - - container.add_method type - - set_visibility(container, typename, visibility_default, @@public_methods) - - if type_visibility - type_visibility.gsub!(/\s/,'') - type_visibility.gsub!(/\,/,'') - type_visibility.gsub!(/:/,'') - type_visibility.downcase! - if type_visibility == "public" - container.set_visibility_for([typename], :public) - elsif type_visibility == "private" - container.set_visibility_for([typename], :private) - end - end - - check_public_methods(type, container.name) - - if @options.show_all - derived_types_comment << ", " unless derived_types_comment.empty? - derived_types_comment << typename - else - if type.visibility == :public - derived_types_comment << ", " unless derived_types_comment.empty? - derived_types_comment << typename - end - end - - end - - if !derived_types_comment.empty? - derived_types_table = - Attr.new("Derived Types", "Derived_Types", "", - derived_types_comment) - container.add_attribute(derived_types_table) - end - - # - # move interface scope - # - interface_code = "" - while remaining_code =~ /^\s*? - interface( - \s+\w+ | - \s+operator\s*?\(.*?\) | - \s+assignment\s*?\(\s*?=\s*?\) - )?\s*?$ - (.*?) - ^\s*?end\s+interface.*?$ - /imx - interface_code << remove_empty_head_lines($&) + "\n" - remaining_code = $~.pre_match - remaining_code << $~.post_match - end - - # - # Parse global constants or variables in modules - # - const_var_defs = definition_info(before_contains_code) - const_var_defs.each{|defitem| - next if defitem.nodoc - const_or_var_type = "Variable" - const_or_var_progress = "v" - if defitem.include_attr?("parameter") - const_or_var_type = "Constant" - const_or_var_progress = "c" - end - const_or_var = AnyMethod.new(const_or_var_type, defitem.varname) - const_or_var.singleton = false - const_or_var.params = "" - self_comment = find_arguments([defitem.varname], before_contains_code) - const_or_var.comment = "<b><em>" + const_or_var_type + "</em></b> :: <tt></tt>\n" - const_or_var.comment << self_comment if self_comment - - @stats.add_method const_or_var_progress - - container.add_method const_or_var - - set_visibility(container, defitem.varname, visibility_default, @@public_methods) - - if defitem.include_attr?("public") - container.set_visibility_for([defitem.varname], :public) - elsif defitem.include_attr?("private") - container.set_visibility_for([defitem.varname], :private) - end - - check_public_methods(const_or_var, container.name) - - } if const_var_defs - - remaining_lines = remaining_code.split("\n") - - # "subroutine" or "function" parts are parsed (new) - # - level_depth = 0 - block_searching_flag = nil - block_searching_lines = [] - pre_comment = [] - procedure_trailing = "" - procedure_name = "" - procedure_params = "" - procedure_prefix = "" - procedure_result_arg = "" - procedure_type = "" - contains_lines = [] - contains_flag = nil - remaining_lines.collect!{|line| - if !block_searching_flag - # subroutine - if line =~ /^\s*? - (recursive|pure|elemental)?\s*? - subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$ - /ix - block_searching_flag = :subroutine - block_searching_lines << line - - procedure_name = $2.chomp.strip - procedure_params = $3 || "" - procedure_prefix = $1 || "" - procedure_trailing = $4 || "!" - next false - - # function - elsif line =~ /^\s*? - (recursive|pure|elemental)?\s*? - ( - character\s*?(\([\w\s\=\(\)\*]+?\))?\s+ - | type\s*?\([\w\s]+?\)\s+ - | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+ - | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+ - | double\s+precision\s+ - | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+ - | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+ - )? - function\s+(\w+)\s*? - (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$ - /ix - block_searching_flag = :function - block_searching_lines << line - - procedure_prefix = $1 || "" - procedure_type = $2 ? $2.chomp.strip : nil - procedure_name = $8.chomp.strip - procedure_params = $9 || "" - procedure_result_arg = $11 ? $11.chomp.strip : procedure_name - procedure_trailing = $12 || "!" - next false - elsif line =~ /^\s*?!\s?(.*)/ - pre_comment << line - next line - else - pre_comment = [] - next line - end - end - contains_flag = true if line =~ /^\s*?contains\s*?(!.*?)?$/ - block_searching_lines << line - contains_lines << line if contains_flag - - level_depth += 1 if block_start?(line) - level_depth -= 1 if block_end?(line) - if level_depth >= 0 - next false - end - - # "procedure_code" is formatted. - # ":nodoc:" flag is checked. - # - procedure_code = block_searching_lines.join("\n") - procedure_code = remove_empty_head_lines(procedure_code) - if procedure_trailing =~ /^!:nodoc:/ - # next loop to search next block - level_depth = 0 - block_searching_flag = nil - block_searching_lines = [] - pre_comment = [] - procedure_trailing = "" - procedure_name = "" - procedure_params = "" - procedure_prefix = "" - procedure_result_arg = "" - procedure_type = "" - contains_lines = [] - contains_flag = nil - next false - end - - # AnyMethod is created, and added to container - # - subroutine_function = nil - if block_searching_flag == :subroutine - subroutine_prefix = procedure_prefix - subroutine_name = procedure_name - subroutine_params = procedure_params - subroutine_trailing = procedure_trailing - subroutine_code = procedure_code - - subroutine_comment = COMMENTS_ARE_UPPER ? - pre_comment.join("\n") + "\n" + subroutine_trailing : - subroutine_trailing + "\n" + subroutine_code.sub(/^.*$\n/i, '') - subroutine = AnyMethod.new("subroutine", subroutine_name) - parse_subprogram(subroutine, subroutine_params, - subroutine_comment, subroutine_code, - before_contains_code, nil, subroutine_prefix) - - @stats.add_method subroutine - - container.add_method subroutine - subroutine_function = subroutine - - elsif block_searching_flag == :function - function_prefix = procedure_prefix - function_type = procedure_type - function_name = procedure_name - function_params_org = procedure_params - function_result_arg = procedure_result_arg - function_trailing = procedure_trailing - function_code_org = procedure_code - - function_comment = COMMENTS_ARE_UPPER ? - pre_comment.join("\n") + "\n" + function_trailing : - function_trailing + "\n " + function_code_org.sub(/^.*$\n/i, '') - - function_code = "#{function_code_org}" - if function_type - function_code << "\n" + function_type + " :: " + function_result_arg - end - - function_params = - function_params_org.sub(/^\(/, "\(#{function_result_arg}, ") - - function = AnyMethod.new("function", function_name) - parse_subprogram(function, function_params, - function_comment, function_code, - before_contains_code, true, function_prefix) - - # Specific modification due to function - function.params.sub!(/\(\s*?#{function_result_arg}\s*?,\s*?/, "\( ") - function.params << " result(" + function_result_arg + ")" - function.start_collecting_tokens - function.add_token Token.new(1,1).set_text(function_code_org) - - @stats.add_method function - - container.add_method function - subroutine_function = function - - end - - # The visibility of procedure is specified - # - set_visibility(container, procedure_name, - visibility_default, @@public_methods) - - # The alias for this procedure from external modules - # - check_external_aliases(procedure_name, - subroutine_function.params, - subroutine_function.comment, subroutine_function) if external - check_public_methods(subroutine_function, container.name) - - - # contains_lines are parsed as private procedures - if contains_flag - parse_program_or_module(container, - contains_lines.join("\n"), :private) - end - - # next loop to search next block - level_depth = 0 - block_searching_flag = nil - block_searching_lines = [] - pre_comment = [] - procedure_trailing = "" - procedure_name = "" - procedure_params = "" - procedure_prefix = "" - procedure_result_arg = "" - contains_lines = [] - contains_flag = nil - next false - } # End of remaining_lines.collect!{|line| - - # Array remains_lines is converted to String remains_code again - # - remaining_code = remaining_lines.join("\n") - - # - # Parse interface - # - interface_scope = false - generic_name = "" - interface_code.split("\n").each{ |line| - if /^\s*? - interface( - \s+\w+| - \s+operator\s*?\(.*?\)| - \s+assignment\s*?\(\s*?=\s*?\) - )? - \s*?(!.*?)?$ - /ix =~ line - generic_name = $1 ? $1.strip.chomp : nil - interface_trailing = $2 || "!" - interface_scope = true - interface_scope = false if interface_trailing =~ /!:nodoc:/ -# if generic_name =~ /operator\s*?\((.*?)\)/i -# operator_name = $1 -# if operator_name && !operator_name.empty? -# generic_name = "#{operator_name}" -# end -# end -# if generic_name =~ /assignment\s*?\((.*?)\)/i -# assignment_name = $1 -# if assignment_name && !assignment_name.empty? -# generic_name = "#{assignment_name}" -# end -# end - end - if /^\s*?end\s+interface/i =~ line - interface_scope = false - generic_name = nil - end - # internal alias - if interface_scope && /^\s*?module\s+procedure\s+(.*?)(!.*?)?$/i =~ line - procedures = $1.strip.chomp - procedures_trailing = $2 || "!" - next if procedures_trailing =~ /!:nodoc:/ - procedures.split(",").each{ |proc| - proc.strip! - proc.chomp! - next if generic_name == proc || !generic_name - old_meth = container.find_symbol(proc, nil, @options.ignore_case) - next if !old_meth - nolink = old_meth.visibility == :private ? true : nil - nolink = nil if @options.show_all - new_meth = - initialize_external_method(generic_name, proc, - old_meth.params, nil, - old_meth.comment, - old_meth.clone.token_stream[0].text, - true, nolink) - new_meth.singleton = old_meth.singleton - - @stats.add_method new_meth - - container.add_method new_meth - - set_visibility(container, generic_name, visibility_default, @@public_methods) - - check_public_methods(new_meth, container.name) - - } - end - - # external aliases - if interface_scope - # subroutine - proc = nil - params = nil - procedures_trailing = nil - if line =~ /^\s*? - (recursive|pure|elemental)?\s*? - subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$ - /ix - proc = $2.chomp.strip - generic_name = proc unless generic_name - params = $3 || "" - procedures_trailing = $4 || "!" - - # function - elsif line =~ /^\s*? - (recursive|pure|elemental)?\s*? - ( - character\s*?(\([\w\s\=\(\)\*]+?\))?\s+ - | type\s*?\([\w\s]+?\)\s+ - | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+ - | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+ - | double\s+precision\s+ - | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+ - | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+ - )? - function\s+(\w+)\s*? - (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$ - /ix - proc = $8.chomp.strip - generic_name = proc unless generic_name - params = $9 || "" - procedures_trailing = $12 || "!" - else - next - end - next if procedures_trailing =~ /!:nodoc:/ - indicated_method = nil - indicated_file = nil - TopLevel.all_files.each do |name, toplevel| - indicated_method = toplevel.find_local_symbol(proc, @options.ignore_case) - indicated_file = name - break if indicated_method - end - - if indicated_method - external_method = - initialize_external_method(generic_name, proc, - indicated_method.params, - indicated_file, - indicated_method.comment) - - @stats.add_method external_method - - container.add_method external_method - set_visibility(container, generic_name, visibility_default, @@public_methods) - if !container.include_requires?(indicated_file, @options.ignore_case) - container.add_require(Require.new(indicated_file, "")) - end - check_public_methods(external_method, container.name) - - else - @@external_aliases << { - "new_name" => generic_name, - "old_name" => proc, - "file_or_module" => container, - "visibility" => find_visibility(container, generic_name, @@public_methods) || visibility_default - } - end - end - - } if interface_code # End of interface_code.split("\n").each ... - - # - # Already imported methods are removed from @@public_methods. - # Remainders are assumed to be imported from other modules. - # - @@public_methods.delete_if{ |method| method["entity_is_discovered"]} - - @@public_methods.each{ |pub_meth| - next unless pub_meth["file_or_module"].name == container.name - pub_meth["used_modules"].each{ |used_mod| - TopLevel.all_classes_and_modules.each{ |modules| - if modules.name == used_mod || - modules.name.upcase == used_mod.upcase && - @options.ignore_case - modules.method_list.each{ |meth| - if meth.name == pub_meth["name"] || - meth.name.upcase == pub_meth["name"].upcase && - @options.ignore_case - new_meth = initialize_public_method(meth, - modules.name) - if pub_meth["local_name"] - new_meth.name = pub_meth["local_name"] - end - - @stats.add_method new_meth - - container.add_method new_meth - end - } - end - } - } - } - - container - end # End of parse_program_or_module - - ## - # Parse arguments, comment, code of subroutine and function. Return - # AnyMethod object. - - def parse_subprogram(subprogram, params, comment, code, - before_contains=nil, function=nil, prefix=nil) - subprogram.singleton = false - prefix = "" if !prefix - arguments = params.sub(/\(/, "").sub(/\)/, "").split(",") if params - args_comment, params_opt = - find_arguments(arguments, code.sub(/^s*?contains\s*?(!.*?)?$.*/im, ""), - nil, nil, true) - params_opt = "( " + params_opt + " ) " if params_opt - subprogram.params = params_opt || "" - namelist_comment = find_namelists(code, before_contains) - - block_comment = find_comments comment - if function - subprogram.comment = "<b><em> Function </em></b> :: <em>#{prefix}</em>\n" - else - subprogram.comment = "<b><em> Subroutine </em></b> :: <em>#{prefix}</em>\n" - end - subprogram.comment << args_comment if args_comment - subprogram.comment << block_comment if block_comment - subprogram.comment << namelist_comment if namelist_comment - - # For output source code - subprogram.start_collecting_tokens - subprogram.add_token Token.new(1,1).set_text(code) - - subprogram - end - - ## - # Collect comment for file entity - - def collect_first_comment(body) - comment = "" - not_comment = "" - comment_start = false - comment_end = false - body.split("\n").each{ |line| - if comment_end - not_comment << line - not_comment << "\n" - elsif /^\s*?!\s?(.*)$/i =~ line - comment_start = true - comment << $1 - comment << "\n" - elsif /^\s*?$/i =~ line - comment_end = true if comment_start && COMMENTS_ARE_UPPER - else - comment_end = true - not_comment << line - not_comment << "\n" - end - } - return comment, not_comment - end - - - ## - # Return comments of definitions of arguments - # - # If "all" argument is true, information of all arguments are returned. - # - # If "modified_params" is true, list of arguments are decorated, for - # example, optional arguments are parenthetic as "[arg]". - - def find_arguments(args, text, all=nil, indent=nil, modified_params=nil) - return unless args || all - indent = "" unless indent - args = ["all"] if all - params = "" if modified_params - comma = "" - return unless text - args_rdocforms = "\n" - remaining_lines = "#{text}" - definitions = definition_info(remaining_lines) - args.each{ |arg| - arg.strip! - arg.chomp! - definitions.each { |defitem| - if arg == defitem.varname.strip.chomp || all - args_rdocforms << <<-"EOF" - -#{indent}<tt><b>#{defitem.varname.chomp.strip}#{defitem.arraysuffix}</b> #{defitem.inivalue}</tt> :: -#{indent} <tt>#{defitem.types.chomp.strip}</tt> -EOF - if !defitem.comment.chomp.strip.empty? - comment = "" - defitem.comment.split("\n").each{ |line| - comment << " " + line + "\n" - } - args_rdocforms << <<-"EOF" - -#{indent} <tt></tt> :: -#{indent} <tt></tt> -#{indent} #{comment.chomp.strip} -EOF - end - - if modified_params - if defitem.include_attr?("optional") - params << "#{comma}[#{arg}]" - else - params << "#{comma}#{arg}" - end - comma = ", " - end - end - } - } - if modified_params - return args_rdocforms, params - else - return args_rdocforms - end - end - - ## - # Return comments of definitions of namelists - - def find_namelists(text, before_contains=nil) - return nil if !text - result = "" - lines = "#{text}" - before_contains = "" if !before_contains - while lines =~ /^\s*?namelist\s+\/\s*?(\w+)\s*?\/([\s\w\,]+)$/i - lines = $~.post_match - nml_comment = COMMENTS_ARE_UPPER ? - find_comments($~.pre_match) : find_comments($~.post_match) - nml_name = $1 - nml_args = $2.split(",") - result << "\n\n=== NAMELIST <tt><b>" + nml_name + "</tt></b>\n\n" - result << nml_comment + "\n" if nml_comment - if lines.split("\n")[0] =~ /^\//i - lines = "namelist " + lines - end - result << find_arguments(nml_args, "#{text}" + "\n" + before_contains) - end - return result - end - - ## - # Comments just after module or subprogram, or arguments are returned. If - # "COMMENTS_ARE_UPPER" is true, comments just before modules or subprograms - # are returnd - - def find_comments text - return "" unless text - lines = text.split("\n") - lines.reverse! if COMMENTS_ARE_UPPER - comment_block = Array.new - lines.each do |line| - break if line =~ /^\s*?\w/ || line =~ /^\s*?$/ - if COMMENTS_ARE_UPPER - comment_block.unshift line.sub(/^\s*?!\s?/,"") - else - comment_block.push line.sub(/^\s*?!\s?/,"") - end - end - nice_lines = comment_block.join("\n").split "\n\s*?\n" - nice_lines[0] ||= "" - nice_lines.shift - end - - ## - # Create method for internal alias - - def initialize_public_method(method, parent) - return if !method || !parent - - new_meth = AnyMethod.new("External Alias for module", method.name) - new_meth.singleton = method.singleton - new_meth.params = method.params.clone - new_meth.comment = remove_trailing_alias(method.comment.clone) - new_meth.comment << "\n\n#{EXTERNAL_ALIAS_MES} #{parent.strip.chomp}\##{method.name}" - - return new_meth - end - - ## - # Create method for external alias - # - # If argument "internal" is true, file is ignored. - - def initialize_external_method(new, old, params, file, comment, token=nil, - internal=nil, nolink=nil) - return nil unless new || old - - if internal - external_alias_header = "#{INTERNAL_ALIAS_MES} " - external_alias_text = external_alias_header + old - elsif file - external_alias_header = "#{EXTERNAL_ALIAS_MES} " - external_alias_text = external_alias_header + file + "#" + old - else - return nil - end - external_meth = AnyMethod.new(external_alias_text, new) - external_meth.singleton = false - external_meth.params = params - external_comment = remove_trailing_alias(comment) + "\n\n" if comment - external_meth.comment = external_comment || "" - if nolink && token - external_meth.start_collecting_tokens - external_meth.add_token Token.new(1,1).set_text(token) - else - external_meth.comment << external_alias_text - end - - return external_meth - end - - ## - # Parse visibility - - def parse_visibility(code, default, container) - result = [] - visibility_default = default || :public - - used_modules = [] - container.includes.each{|i| used_modules << i.name} if container - - remaining_code = code.gsub(/^\s*?type[\s\,]+.*?\s+end\s+type.*?$/im, "") - remaining_code.split("\n").each{ |line| - if /^\s*?private\s*?$/ =~ line - visibility_default = :private - break - end - } if remaining_code - - remaining_code.split("\n").each{ |line| - if /^\s*?private\s*?(::)?\s+(.*)\s*?(!.*?)?/i =~ line - methods = $2.sub(/!.*$/, '') - methods.split(",").each{ |meth| - meth.sub!(/!.*$/, '') - meth.gsub!(/:/, '') - result << { - "name" => meth.chomp.strip, - "visibility" => :private, - "used_modules" => used_modules.clone, - "file_or_module" => container, - "entity_is_discovered" => nil, - "local_name" => nil - } - } - elsif /^\s*?public\s*?(::)?\s+(.*)\s*?(!.*?)?/i =~ line - methods = $2.sub(/!.*$/, '') - methods.split(",").each{ |meth| - meth.sub!(/!.*$/, '') - meth.gsub!(/:/, '') - result << { - "name" => meth.chomp.strip, - "visibility" => :public, - "used_modules" => used_modules.clone, - "file_or_module" => container, - "entity_is_discovered" => nil, - "local_name" => nil - } - } - end - } if remaining_code - - if container - result.each{ |vis_info| - vis_info["parent"] = container.name - } - end - - return visibility_default, result - end - - ## - # Set visibility - # - # "subname" element of "visibility_info" is deleted. - - def set_visibility(container, subname, visibility_default, visibility_info) - return unless container || subname || visibility_default || visibility_info - not_found = true - visibility_info.collect!{ |info| - if info["name"] == subname || - @options.ignore_case && info["name"].upcase == subname.upcase - if info["file_or_module"].name == container.name - container.set_visibility_for([subname], info["visibility"]) - info["entity_is_discovered"] = true - not_found = false - end - end - info - } - if not_found - return container.set_visibility_for([subname], visibility_default) - else - return container - end - end - - ## - # Find visibility - - def find_visibility(container, subname, visibility_info) - return nil if !subname || !visibility_info - visibility_info.each{ |info| - if info["name"] == subname || - @options.ignore_case && info["name"].upcase == subname.upcase - if info["parent"] == container.name - return info["visibility"] - end - end - } - return nil - end - - ## - # Check external aliases - - def check_external_aliases(subname, params, comment, test=nil) - @@external_aliases.each{ |alias_item| - if subname == alias_item["old_name"] || - subname.upcase == alias_item["old_name"].upcase && - @options.ignore_case - - new_meth = initialize_external_method(alias_item["new_name"], - subname, params, @file_name, - comment) - new_meth.visibility = alias_item["visibility"] - - @stats.add_method new_meth - - alias_item["file_or_module"].add_method(new_meth) - - if !alias_item["file_or_module"].include_requires?(@file_name, @options.ignore_case) - alias_item["file_or_module"].add_require(Require.new(@file_name, "")) - end - end - } - end - - ## - # Check public_methods - - def check_public_methods(method, parent) - return if !method || !parent - @@public_methods.each{ |alias_item| - parent_is_used_module = nil - alias_item["used_modules"].each{ |used_module| - if used_module == parent || - used_module.upcase == parent.upcase && - @options.ignore_case - parent_is_used_module = true - end - } - next if !parent_is_used_module - - if method.name == alias_item["name"] || - method.name.upcase == alias_item["name"].upcase && - @options.ignore_case - - new_meth = initialize_public_method(method, parent) - if alias_item["local_name"] - new_meth.name = alias_item["local_name"] - end - - @stats.add_method new_meth - - alias_item["file_or_module"].add_method new_meth - end - } - end - - ## - # Continuous lines are united. - # - # Comments in continuous lines are removed. - - def united_to_one_line(f90src) - return "" unless f90src - lines = f90src.split("\n") - previous_continuing = false - now_continuing = false - body = "" - lines.each{ |line| - words = line.split("") - next if words.empty? && previous_continuing - commentout = false - brank_flag = true ; brank_char = "" - squote = false ; dquote = false - ignore = false - words.collect! { |char| - if previous_continuing && brank_flag - now_continuing = true - ignore = true - case char - when "!" ; break - when " " ; brank_char << char ; next "" - when "&" - brank_flag = false - now_continuing = false - next "" - else - brank_flag = false - now_continuing = false - ignore = false - next brank_char + char - end - end - ignore = false - - if now_continuing - next "" - elsif !(squote) && !(dquote) && !(commentout) - case char - when "!" ; commentout = true ; next char - when "\""; dquote = true ; next char - when "\'"; squote = true ; next char - when "&" ; now_continuing = true ; next "" - else next char - end - elsif commentout - next char - elsif squote - case char - when "\'"; squote = false ; next char - else next char - end - elsif dquote - case char - when "\""; dquote = false ; next char - else next char - end - end - } - if !ignore && !previous_continuing || !brank_flag - if previous_continuing - body << words.join("") - else - body << "\n" + words.join("") - end - end - previous_continuing = now_continuing ? true : nil - now_continuing = nil - } - return body - end - - - ## - # Continuous line checker - - def continuous_line?(line) - continuous = false - if /&\s*?(!.*)?$/ =~ line - continuous = true - if comment_out?($~.pre_match) - continuous = false - end - end - return continuous - end - - ## - # Comment out checker - - def comment_out?(line) - return nil unless line - commentout = false - squote = false ; dquote = false - line.split("").each { |char| - if !(squote) && !(dquote) - case char - when "!" ; commentout = true ; break - when "\""; dquote = true - when "\'"; squote = true - else next - end - elsif squote - case char - when "\'"; squote = false - else next - end - elsif dquote - case char - when "\""; dquote = false - else next - end - end - } - return commentout - end - - ## - # Semicolons are replaced to line feed. - - def semicolon_to_linefeed(text) - return "" unless text - lines = text.split("\n") - lines.collect!{ |line| - words = line.split("") - commentout = false - squote = false ; dquote = false - words.collect! { |char| - if !(squote) && !(dquote) && !(commentout) - case char - when "!" ; commentout = true ; next char - when "\""; dquote = true ; next char - when "\'"; squote = true ; next char - when ";" ; "\n" - else next char - end - elsif commentout - next char - elsif squote - case char - when "\'"; squote = false ; next char - else next char - end - elsif dquote - case char - when "\""; dquote = false ; next char - else next char - end - end - } - words.join("") - } - return lines.join("\n") - end - - ## - # Which "line" is start of block (module, program, block data, subroutine, - # function) statement ? - - def block_start?(line) - return nil if !line - - if line =~ /^\s*?module\s+(\w+)\s*?(!.*?)?$/i || - line =~ /^\s*?program\s+(\w+)\s*?(!.*?)?$/i || - line =~ /^\s*?block\s+data(\s+\w+)?\s*?(!.*?)?$/i || - line =~ \ - /^\s*? - (recursive|pure|elemental)?\s*? - subroutine\s+(\w+)\s*?(\(.*?\))?\s*?(!.*?)?$ - /ix || - line =~ \ - /^\s*? - (recursive|pure|elemental)?\s*? - ( - character\s*?(\([\w\s\=\(\)\*]+?\))?\s+ - | type\s*?\([\w\s]+?\)\s+ - | integer\s*?(\([\w\s\=\(\)\*]+?\))?\s+ - | real\s*?(\([\w\s\=\(\)\*]+?\))?\s+ - | double\s+precision\s+ - | logical\s*?(\([\w\s\=\(\)\*]+?\))?\s+ - | complex\s*?(\([\w\s\=\(\)\*]+?\))?\s+ - )? - function\s+(\w+)\s*? - (\(.*?\))?(\s+result\((.*?)\))?\s*?(!.*?)?$ - /ix - return true - end - - return nil - end - - ## - # Which "line" is end of block (module, program, block data, subroutine, - # function) statement ? - - def block_end?(line) - return nil if !line - - if line =~ /^\s*?end\s*?(!.*?)?$/i || - line =~ /^\s*?end\s+module(\s+\w+)?\s*?(!.*?)?$/i || - line =~ /^\s*?end\s+program(\s+\w+)?\s*?(!.*?)?$/i || - line =~ /^\s*?end\s+block\s+data(\s+\w+)?\s*?(!.*?)?$/i || - line =~ /^\s*?end\s+subroutine(\s+\w+)?\s*?(!.*?)?$/i || - line =~ /^\s*?end\s+function(\s+\w+)?\s*?(!.*?)?$/i - return true - end - - return nil - end - - ## - # Remove "Alias for" in end of comments - - def remove_trailing_alias(text) - return "" if !text - lines = text.split("\n").reverse - comment_block = Array.new - checked = false - lines.each do |line| - if !checked - if /^\s?#{INTERNAL_ALIAS_MES}/ =~ line || - /^\s?#{EXTERNAL_ALIAS_MES}/ =~ line - checked = true - next - end - end - comment_block.unshift line - end - nice_lines = comment_block.join("\n") - nice_lines ||= "" - return nice_lines - end - - ## - # Empty lines in header are removed - - def remove_empty_head_lines(text) - return "" unless text - lines = text.split("\n") - header = true - lines.delete_if{ |line| - header = false if /\S/ =~ line - header && /^\s*?$/ =~ line - } - lines.join("\n") - end - - ## - # header marker "=", "==", ... are removed - - def remove_header_marker(text) - return text.gsub(/^\s?(=+)/, '<tt></tt>\1') - end - - def remove_private_comments(body) - body.gsub!(/^\s*!--\s*?$.*?^\s*!\+\+\s*?$/m, '') - return body - end - - ## - # Information of arguments of subroutines and functions in Fortran95 - - class Fortran95Definition - - # Name of variable - # - attr_reader :varname - - # Types of variable - # - attr_reader :types - - # Initial Value - # - attr_reader :inivalue - - # Suffix of array - # - attr_reader :arraysuffix - - # Comments - # - attr_accessor :comment - - # Flag of non documentation - # - attr_accessor :nodoc - - def initialize(varname, types, inivalue, arraysuffix, comment, - nodoc=false) - @varname = varname - @types = types - @inivalue = inivalue - @arraysuffix = arraysuffix - @comment = comment - @nodoc = nodoc - end - - def to_s - return <<-EOF -<Fortran95Definition: -varname=#{@varname}, types=#{types}, -inivalue=#{@inivalue}, arraysuffix=#{@arraysuffix}, nodoc=#{@nodoc}, -comment= -#{@comment} -> -EOF - end - - # - # If attr is included, true is returned - # - def include_attr?(attr) - return if !attr - @types.split(",").each{ |type| - return true if type.strip.chomp.upcase == attr.strip.chomp.upcase - } - return nil - end - - end # End of Fortran95Definition - - ## - # Parse string argument "text", and Return Array of Fortran95Definition - # object - - def definition_info(text) - return nil unless text - lines = "#{text}" - defs = Array.new - comment = "" - trailing_comment = "" - under_comment_valid = false - lines.split("\n").each{ |line| - if /^\s*?!\s?(.*)/ =~ line - if COMMENTS_ARE_UPPER - comment << remove_header_marker($1) - comment << "\n" - elsif defs[-1] && under_comment_valid - defs[-1].comment << "\n" - defs[-1].comment << remove_header_marker($1) - end - next - elsif /^\s*?$/ =~ line - comment = "" - under_comment_valid = false - next - end - type = "" - characters = "" - if line =~ /^\s*? - ( - character\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]* - | type\s*?\([\w\s]+?\)[\s\,]* - | integer\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]* - | real\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]* - | double\s+precision[\s\,]* - | logical\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]* - | complex\s*?(\([\w\s\=\(\)\*]+?\))?[\s\,]* - ) - (.*?::)? - (.+)$ - /ix - characters = $8 - type = $1 - type << $7.gsub(/::/, '').gsub(/^\s*?\,/, '') if $7 - else - under_comment_valid = false - next - end - squote = false ; dquote = false ; bracket = 0 - iniflag = false; commentflag = false - varname = "" ; arraysuffix = "" ; inivalue = "" - start_pos = defs.size - characters.split("").each { |char| - if !(squote) && !(dquote) && bracket <= 0 && !(iniflag) && !(commentflag) - case char - when "!" ; commentflag = true - when "(" ; bracket += 1 ; arraysuffix = char - when "\""; dquote = true - when "\'"; squote = true - when "=" ; iniflag = true ; inivalue << char - when "," - defs << Fortran95Definition.new(varname, type, inivalue, arraysuffix, comment) - varname = "" ; arraysuffix = "" ; inivalue = "" - under_comment_valid = true - when " " ; next - else ; varname << char - end - elsif commentflag - comment << remove_header_marker(char) - trailing_comment << remove_header_marker(char) - elsif iniflag - if dquote - case char - when "\"" ; dquote = false ; inivalue << char - else ; inivalue << char - end - elsif squote - case char - when "\'" ; squote = false ; inivalue << char - else ; inivalue << char - end - elsif bracket > 0 - case char - when "(" ; bracket += 1 ; inivalue << char - when ")" ; bracket -= 1 ; inivalue << char - else ; inivalue << char - end - else - case char - when "," - defs << Fortran95Definition.new(varname, type, inivalue, arraysuffix, comment) - varname = "" ; arraysuffix = "" ; inivalue = "" - iniflag = false - under_comment_valid = true - when "(" ; bracket += 1 ; inivalue << char - when "\""; dquote = true ; inivalue << char - when "\'"; squote = true ; inivalue << char - when "!" ; commentflag = true - else ; inivalue << char - end - end - elsif !(squote) && !(dquote) && bracket > 0 - case char - when "(" ; bracket += 1 ; arraysuffix << char - when ")" ; bracket -= 1 ; arraysuffix << char - else ; arraysuffix << char - end - elsif squote - case char - when "\'"; squote = false ; inivalue << char - else ; inivalue << char - end - elsif dquote - case char - when "\""; dquote = false ; inivalue << char - else ; inivalue << char - end - end - } - defs << Fortran95Definition.new(varname, type, inivalue, arraysuffix, comment) - if trailing_comment =~ /^:nodoc:/ - defs[start_pos..-1].collect!{ |defitem| - defitem.nodoc = true - } - end - varname = "" ; arraysuffix = "" ; inivalue = "" - comment = "" - under_comment_valid = true - trailing_comment = "" - } - return defs - end - -end - diff --git a/trunk/lib/rdoc/parser/ruby.rb b/trunk/lib/rdoc/parser/ruby.rb deleted file mode 100644 index abbc85bde7..0000000000 --- a/trunk/lib/rdoc/parser/ruby.rb +++ /dev/null @@ -1,2829 +0,0 @@ -## -# This file contains stuff stolen outright from: -# -# rtags.rb - -# ruby-lex.rb - ruby lexcal analyzer -# ruby-token.rb - ruby tokens -# by Keiju ISHITSUKA (Nippon Rational Inc.) -# - -require 'e2mmap' -require 'irb/slex' - -require 'rdoc/code_objects' -require 'rdoc/tokenstream' -require 'rdoc/markup/preprocess' -require 'rdoc/parser' - -$TOKEN_DEBUG ||= nil -#$TOKEN_DEBUG = $DEBUG_RDOC - -## -# Definitions of all tokens involved in the lexical analysis - -module RDoc::RubyToken - - EXPR_BEG = :EXPR_BEG - EXPR_MID = :EXPR_MID - EXPR_END = :EXPR_END - EXPR_ARG = :EXPR_ARG - EXPR_FNAME = :EXPR_FNAME - EXPR_DOT = :EXPR_DOT - EXPR_CLASS = :EXPR_CLASS - - class Token - NO_TEXT = "??".freeze - - attr_accessor :text - attr_reader :line_no - attr_reader :char_no - - def initialize(line_no, char_no) - @line_no = line_no - @char_no = char_no - @text = NO_TEXT - end - - def ==(other) - self.class == other.class and - other.line_no == @line_no and - other.char_no == @char_no and - other.text == @text - end - - ## - # Because we're used in contexts that expect to return a token, we set the - # text string and then return ourselves - - def set_text(text) - @text = text - self - end - - end - - class TkNode < Token - attr :node - end - - class TkId < Token - def initialize(line_no, char_no, name) - super(line_no, char_no) - @name = name - end - attr :name - end - - class TkKW < TkId - end - - class TkVal < Token - def initialize(line_no, char_no, value = nil) - super(line_no, char_no) - set_text(value) - end - end - - class TkOp < Token - def name - self.class.op_name - end - end - - class TkOPASGN < TkOp - def initialize(line_no, char_no, op) - super(line_no, char_no) - op = TkReading2Token[op] unless Symbol === op - @op = op - end - attr :op - end - - class TkUnknownChar < Token - def initialize(line_no, char_no, id) - super(line_no, char_no) - @name = char_no.chr - end - attr :name - end - - class TkError < Token - end - - def set_token_position(line, char) - @prev_line_no = line - @prev_char_no = char - end - - def Token(token, value = nil) - tk = nil - case token - when String, Symbol - source = String === token ? TkReading2Token : TkSymbol2Token - raise TkReading2TokenNoKey, token if (tk = source[token]).nil? - tk = Token(tk[0], value) - else - tk = if (token.ancestors & [TkId, TkVal, TkOPASGN, TkUnknownChar]).empty? - token.new(@prev_line_no, @prev_char_no) - else - token.new(@prev_line_no, @prev_char_no, value) - end - end - tk - end - - TokenDefinitions = [ - [:TkCLASS, TkKW, "class", EXPR_CLASS], - [:TkMODULE, TkKW, "module", EXPR_BEG], - [:TkDEF, TkKW, "def", EXPR_FNAME], - [:TkUNDEF, TkKW, "undef", EXPR_FNAME], - [:TkBEGIN, TkKW, "begin", EXPR_BEG], - [:TkRESCUE, TkKW, "rescue", EXPR_MID], - [:TkENSURE, TkKW, "ensure", EXPR_BEG], - [:TkEND, TkKW, "end", EXPR_END], - [:TkIF, TkKW, "if", EXPR_BEG, :TkIF_MOD], - [:TkUNLESS, TkKW, "unless", EXPR_BEG, :TkUNLESS_MOD], - [:TkTHEN, TkKW, "then", EXPR_BEG], - [:TkELSIF, TkKW, "elsif", EXPR_BEG], - [:TkELSE, TkKW, "else", EXPR_BEG], - [:TkCASE, TkKW, "case", EXPR_BEG], - [:TkWHEN, TkKW, "when", EXPR_BEG], - [:TkWHILE, TkKW, "while", EXPR_BEG, :TkWHILE_MOD], - [:TkUNTIL, TkKW, "until", EXPR_BEG, :TkUNTIL_MOD], - [:TkFOR, TkKW, "for", EXPR_BEG], - [:TkBREAK, TkKW, "break", EXPR_END], - [:TkNEXT, TkKW, "next", EXPR_END], - [:TkREDO, TkKW, "redo", EXPR_END], - [:TkRETRY, TkKW, "retry", EXPR_END], - [:TkIN, TkKW, "in", EXPR_BEG], - [:TkDO, TkKW, "do", EXPR_BEG], - [:TkRETURN, TkKW, "return", EXPR_MID], - [:TkYIELD, TkKW, "yield", EXPR_END], - [:TkSUPER, TkKW, "super", EXPR_END], - [:TkSELF, TkKW, "self", EXPR_END], - [:TkNIL, TkKW, "nil", EXPR_END], - [:TkTRUE, TkKW, "true", EXPR_END], - [:TkFALSE, TkKW, "false", EXPR_END], - [:TkAND, TkKW, "and", EXPR_BEG], - [:TkOR, TkKW, "or", EXPR_BEG], - [:TkNOT, TkKW, "not", EXPR_BEG], - [:TkIF_MOD, TkKW], - [:TkUNLESS_MOD, TkKW], - [:TkWHILE_MOD, TkKW], - [:TkUNTIL_MOD, TkKW], - [:TkALIAS, TkKW, "alias", EXPR_FNAME], - [:TkDEFINED, TkKW, "defined?", EXPR_END], - [:TklBEGIN, TkKW, "BEGIN", EXPR_END], - [:TklEND, TkKW, "END", EXPR_END], - [:Tk__LINE__, TkKW, "__LINE__", EXPR_END], - [:Tk__FILE__, TkKW, "__FILE__", EXPR_END], - - [:TkIDENTIFIER, TkId], - [:TkFID, TkId], - [:TkGVAR, TkId], - [:TkIVAR, TkId], - [:TkCONSTANT, TkId], - - [:TkINTEGER, TkVal], - [:TkFLOAT, TkVal], - [:TkSTRING, TkVal], - [:TkXSTRING, TkVal], - [:TkREGEXP, TkVal], - [:TkCOMMENT, TkVal], - - [:TkDSTRING, TkNode], - [:TkDXSTRING, TkNode], - [:TkDREGEXP, TkNode], - [:TkNTH_REF, TkId], - [:TkBACK_REF, TkId], - - [:TkUPLUS, TkOp, "+@"], - [:TkUMINUS, TkOp, "-@"], - [:TkPOW, TkOp, "**"], - [:TkCMP, TkOp, "<=>"], - [:TkEQ, TkOp, "=="], - [:TkEQQ, TkOp, "==="], - [:TkNEQ, TkOp, "!="], - [:TkGEQ, TkOp, ">="], - [:TkLEQ, TkOp, "<="], - [:TkANDOP, TkOp, "&&"], - [:TkOROP, TkOp, "||"], - [:TkMATCH, TkOp, "=~"], - [:TkNMATCH, TkOp, "!~"], - [:TkDOT2, TkOp, ".."], - [:TkDOT3, TkOp, "..."], - [:TkAREF, TkOp, "[]"], - [:TkASET, TkOp, "[]="], - [:TkLSHFT, TkOp, "<<"], - [:TkRSHFT, TkOp, ">>"], - [:TkCOLON2, TkOp], - [:TkCOLON3, TkOp], -# [:OPASGN, TkOp], # +=, -= etc. # - [:TkASSOC, TkOp, "=>"], - [:TkQUESTION, TkOp, "?"], #? - [:TkCOLON, TkOp, ":"], #: - - [:TkfLPAREN], # func( # - [:TkfLBRACK], # func[ # - [:TkfLBRACE], # func{ # - [:TkSTAR], # *arg - [:TkAMPER], # &arg # - [:TkSYMBOL, TkId], # :SYMBOL - [:TkSYMBEG, TkId], - [:TkGT, TkOp, ">"], - [:TkLT, TkOp, "<"], - [:TkPLUS, TkOp, "+"], - [:TkMINUS, TkOp, "-"], - [:TkMULT, TkOp, "*"], - [:TkDIV, TkOp, "/"], - [:TkMOD, TkOp, "%"], - [:TkBITOR, TkOp, "|"], - [:TkBITXOR, TkOp, "^"], - [:TkBITAND, TkOp, "&"], - [:TkBITNOT, TkOp, "~"], - [:TkNOTOP, TkOp, "!"], - - [:TkBACKQUOTE, TkOp, "`"], - - [:TkASSIGN, Token, "="], - [:TkDOT, Token, "."], - [:TkLPAREN, Token, "("], #(exp) - [:TkLBRACK, Token, "["], #[arry] - [:TkLBRACE, Token, "{"], #{hash} - [:TkRPAREN, Token, ")"], - [:TkRBRACK, Token, "]"], - [:TkRBRACE, Token, "}"], - [:TkCOMMA, Token, ","], - [:TkSEMICOLON, Token, ";"], - - [:TkRD_COMMENT], - [:TkSPACE], - [:TkNL], - [:TkEND_OF_SCRIPT], - - [:TkBACKSLASH, TkUnknownChar, "\\"], - [:TkAT, TkUnknownChar, "@"], - [:TkDOLLAR, TkUnknownChar, "\$"], #" - ] - - # {reading => token_class} - # {reading => [token_class, *opt]} - TkReading2Token = {} - TkSymbol2Token = {} - - def self.def_token(token_n, super_token = Token, reading = nil, *opts) - token_n = token_n.id2name unless String === token_n - - fail AlreadyDefinedToken, token_n if const_defined?(token_n) - - token_c = Class.new super_token - const_set token_n, token_c -# token_c.inspect - - if reading - if TkReading2Token[reading] - fail TkReading2TokenDuplicateError, token_n, reading - end - if opts.empty? - TkReading2Token[reading] = [token_c] - else - TkReading2Token[reading] = [token_c].concat(opts) - end - end - TkSymbol2Token[token_n.intern] = token_c - - if token_c <= TkOp - token_c.class_eval %{ - def self.op_name; "#{reading}"; end - } - end - end - - for defs in TokenDefinitions - def_token(*defs) - end - - NEWLINE_TOKEN = TkNL.new(0,0) - NEWLINE_TOKEN.set_text("\n") - -end - -## -# Lexical analyzer for Ruby source - -class RDoc::RubyLex - - ## - # Read an input stream character by character. We allow for unlimited - # ungetting of characters just read. - # - # We simplify the implementation greatly by reading the entire input - # into a buffer initially, and then simply traversing it using - # pointers. - # - # We also have to allow for the <i>here document diversion</i>. This - # little gem comes about when the lexer encounters a here - # document. At this point we effectively need to split the input - # stream into two parts: one to read the body of the here document, - # the other to read the rest of the input line where the here - # document was initially encountered. For example, we might have - # - # do_something(<<-A, <<-B) - # stuff - # for - # A - # stuff - # for - # B - # - # When the lexer encounters the <<A, it reads until the end of the - # line, and keeps it around for later. It then reads the body of the - # here document. Once complete, it needs to read the rest of the - # original line, but then skip the here document body. - # - - class BufferedReader - - attr_reader :line_num - - def initialize(content, options) - @options = options - - if /\t/ =~ content - tab_width = @options.tab_width - content = content.split(/\n/).map do |line| - 1 while line.gsub!(/\t+/) { ' ' * (tab_width*$&.length - $`.length % tab_width)} && $~ #` - line - end .join("\n") - end - @content = content - @content << "\n" unless @content[-1,1] == "\n" - @size = @content.size - @offset = 0 - @hwm = 0 - @line_num = 1 - @read_back_offset = 0 - @last_newline = 0 - @newline_pending = false - end - - def column - @offset - @last_newline - end - - def getc - return nil if @offset >= @size - ch = @content[@offset, 1] - - @offset += 1 - @hwm = @offset if @hwm < @offset - - if @newline_pending - @line_num += 1 - @last_newline = @offset - 1 - @newline_pending = false - end - - if ch == "\n" - @newline_pending = true - end - ch - end - - def getc_already_read - getc - end - - def ungetc(ch) - raise "unget past beginning of file" if @offset <= 0 - @offset -= 1 - if @content[@offset] == ?\n - @newline_pending = false - end - end - - def get_read - res = @content[@read_back_offset...@offset] - @read_back_offset = @offset - res - end - - def peek(at) - pos = @offset + at - if pos >= @size - nil - else - @content[pos, 1] - end - end - - def peek_equal(str) - @content[@offset, str.length] == str - end - - def divert_read_from(reserve) - @content[@offset, 0] = reserve - @size = @content.size - end - end - - # end of nested class BufferedReader - - extend Exception2MessageMapper - def_exception(:AlreadyDefinedToken, "Already defined token(%s)") - def_exception(:TkReading2TokenNoKey, "key nothing(key='%s')") - def_exception(:TkSymbol2TokenNoKey, "key nothing(key='%s')") - def_exception(:TkReading2TokenDuplicateError, - "key duplicate(token_n='%s', key='%s')") - def_exception(:SyntaxError, "%s") - - include RDoc::RubyToken - include IRB - - attr_reader :continue - attr_reader :lex_state - - def self.debug? - false - end - - def initialize(content, options) - lex_init - - @options = options - - @reader = BufferedReader.new content, @options - - @exp_line_no = @line_no = 1 - @base_char_no = 0 - @indent = 0 - - @ltype = nil - @quoted = nil - @lex_state = EXPR_BEG - @space_seen = false - - @continue = false - @line = "" - - @skip_space = false - @read_auto_clean_up = false - @exception_on_syntax_error = true - end - - attr_accessor :skip_space - attr_accessor :read_auto_clean_up - attr_accessor :exception_on_syntax_error - attr_reader :indent - - # io functions - def line_no - @reader.line_num - end - - def char_no - @reader.column - end - - def get_read - @reader.get_read - end - - def getc - @reader.getc - end - - def getc_of_rests - @reader.getc_already_read - end - - def gets - c = getc or return - l = "" - begin - l.concat c unless c == "\r" - break if c == "\n" - end while c = getc - l - end - - - def ungetc(c = nil) - @reader.ungetc(c) - end - - def peek_equal?(str) - @reader.peek_equal(str) - end - - def peek(i = 0) - @reader.peek(i) - end - - def lex - until (TkNL === (tk = token) or TkEND_OF_SCRIPT === tk) and - not @continue or tk.nil? - end - - line = get_read - - if line == "" and TkEND_OF_SCRIPT === tk or tk.nil? then - nil - else - line - end - end - - def token - set_token_position(line_no, char_no) - begin - begin - tk = @OP.match(self) - @space_seen = TkSPACE === tk - rescue SyntaxError => e - raise RDoc::Error, "syntax error: #{e.message}" if - @exception_on_syntax_error - - tk = TkError.new(line_no, char_no) - end - end while @skip_space and TkSPACE === tk - if @read_auto_clean_up - get_read - end -# throw :eof unless tk - tk - end - - ENINDENT_CLAUSE = [ - "case", "class", "def", "do", "for", "if", - "module", "unless", "until", "while", "begin" #, "when" - ] - DEINDENT_CLAUSE = ["end" #, "when" - ] - - PERCENT_LTYPE = { - "q" => "\'", - "Q" => "\"", - "x" => "\`", - "r" => "/", - "w" => "]" - } - - PERCENT_PAREN = { - "{" => "}", - "[" => "]", - "<" => ">", - "(" => ")" - } - - Ltype2Token = { - "\'" => TkSTRING, - "\"" => TkSTRING, - "\`" => TkXSTRING, - "/" => TkREGEXP, - "]" => TkDSTRING - } - Ltype2Token.default = TkSTRING - - DLtype2Token = { - "\"" => TkDSTRING, - "\`" => TkDXSTRING, - "/" => TkDREGEXP, - } - - def lex_init() - @OP = IRB::SLex.new - @OP.def_rules("\0", "\004", "\032") do |chars, io| - Token(TkEND_OF_SCRIPT).set_text(chars) - end - - @OP.def_rules(" ", "\t", "\f", "\r", "\13") do |chars, io| - @space_seen = TRUE - while (ch = getc) =~ /[ \t\f\r\13]/ - chars << ch - end - ungetc - Token(TkSPACE).set_text(chars) - end - - @OP.def_rule("#") do - |op, io| - identify_comment - end - - @OP.def_rule("=begin", proc{@prev_char_no == 0 && peek(0) =~ /\s/}) do - |op, io| - str = op - @ltype = "=" - - - begin - line = "" - begin - ch = getc - line << ch - end until ch == "\n" - str << line - end until line =~ /^=end/ - - ungetc - - @ltype = nil - - if str =~ /\A=begin\s+rdoc/i - str.sub!(/\A=begin.*\n/, '') - str.sub!(/^=end.*/m, '') - Token(TkCOMMENT).set_text(str) - else - Token(TkRD_COMMENT)#.set_text(str) - end - end - - @OP.def_rule("\n") do - print "\\n\n" if RDoc::RubyLex.debug? - case @lex_state - when EXPR_BEG, EXPR_FNAME, EXPR_DOT - @continue = TRUE - else - @continue = FALSE - @lex_state = EXPR_BEG - end - Token(TkNL).set_text("\n") - end - - @OP.def_rules("*", "**", - "!", "!=", "!~", - "=", "==", "===", - "=~", "<=>", - "<", "<=", - ">", ">=", ">>") do - |op, io| - @lex_state = EXPR_BEG - Token(op).set_text(op) - end - - @OP.def_rules("<<") do - |op, io| - tk = nil - if @lex_state != EXPR_END && @lex_state != EXPR_CLASS && - (@lex_state != EXPR_ARG || @space_seen) - c = peek(0) - if /[-\w_\"\'\`]/ =~ c - tk = identify_here_document - end - end - if !tk - @lex_state = EXPR_BEG - tk = Token(op).set_text(op) - end - tk - end - - @OP.def_rules("'", '"') do - |op, io| - identify_string(op) - end - - @OP.def_rules("`") do - |op, io| - if @lex_state == EXPR_FNAME - Token(op).set_text(op) - else - identify_string(op) - end - end - - @OP.def_rules('?') do - |op, io| - if @lex_state == EXPR_END - @lex_state = EXPR_BEG - Token(TkQUESTION).set_text(op) - else - ch = getc - if @lex_state == EXPR_ARG && ch !~ /\s/ - ungetc - @lex_state = EXPR_BEG - Token(TkQUESTION).set_text(op) - else - str = op - str << ch - if (ch == '\\') #' - str << read_escape - end - @lex_state = EXPR_END - Token(TkINTEGER).set_text(str) - end - end - end - - @OP.def_rules("&", "&&", "|", "||") do - |op, io| - @lex_state = EXPR_BEG - Token(op).set_text(op) - end - - @OP.def_rules("+=", "-=", "*=", "**=", - "&=", "|=", "^=", "<<=", ">>=", "||=", "&&=") do - |op, io| - @lex_state = EXPR_BEG - op =~ /^(.*)=$/ - Token(TkOPASGN, $1).set_text(op) - end - - @OP.def_rule("+@", proc{@lex_state == EXPR_FNAME}) do |op, io| - Token(TkUPLUS).set_text(op) - end - - @OP.def_rule("-@", proc{@lex_state == EXPR_FNAME}) do |op, io| - Token(TkUMINUS).set_text(op) - end - - @OP.def_rules("+", "-") do - |op, io| - catch(:RET) do - if @lex_state == EXPR_ARG - if @space_seen and peek(0) =~ /[0-9]/ - throw :RET, identify_number(op) - else - @lex_state = EXPR_BEG - end - elsif @lex_state != EXPR_END and peek(0) =~ /[0-9]/ - throw :RET, identify_number(op) - else - @lex_state = EXPR_BEG - end - Token(op).set_text(op) - end - end - - @OP.def_rule(".") do - @lex_state = EXPR_BEG - if peek(0) =~ /[0-9]/ - ungetc - identify_number("") - else - # for obj.if - @lex_state = EXPR_DOT - Token(TkDOT).set_text(".") - end - end - - @OP.def_rules("..", "...") do - |op, io| - @lex_state = EXPR_BEG - Token(op).set_text(op) - end - - lex_int2 - end - - def lex_int2 - @OP.def_rules("]", "}", ")") do - |op, io| - @lex_state = EXPR_END - @indent -= 1 - Token(op).set_text(op) - end - - @OP.def_rule(":") do - if @lex_state == EXPR_END || peek(0) =~ /\s/ - @lex_state = EXPR_BEG - tk = Token(TkCOLON) - else - @lex_state = EXPR_FNAME - tk = Token(TkSYMBEG) - end - tk.set_text(":") - end - - @OP.def_rule("::") do - if @lex_state == EXPR_BEG or @lex_state == EXPR_ARG && @space_seen - @lex_state = EXPR_BEG - tk = Token(TkCOLON3) - else - @lex_state = EXPR_DOT - tk = Token(TkCOLON2) - end - tk.set_text("::") - end - - @OP.def_rule("/") do - |op, io| - if @lex_state == EXPR_BEG || @lex_state == EXPR_MID - identify_string(op) - elsif peek(0) == '=' - getc - @lex_state = EXPR_BEG - Token(TkOPASGN, :/).set_text("/=") #") - elsif @lex_state == EXPR_ARG and @space_seen and peek(0) !~ /\s/ - identify_string(op) - else - @lex_state = EXPR_BEG - Token("/").set_text(op) - end - end - - @OP.def_rules("^") do - @lex_state = EXPR_BEG - Token("^").set_text("^") - end - - @OP.def_rules(",", ";") do - |op, io| - @lex_state = EXPR_BEG - Token(op).set_text(op) - end - - @OP.def_rule("~") do - @lex_state = EXPR_BEG - Token("~").set_text("~") - end - - @OP.def_rule("~@", proc{@lex_state = EXPR_FNAME}) do - @lex_state = EXPR_BEG - Token("~").set_text("~@") - end - - @OP.def_rule("(") do - @indent += 1 - if @lex_state == EXPR_BEG || @lex_state == EXPR_MID - @lex_state = EXPR_BEG - tk = Token(TkfLPAREN) - else - @lex_state = EXPR_BEG - tk = Token(TkLPAREN) - end - tk.set_text("(") - end - - @OP.def_rule("[]", proc{@lex_state == EXPR_FNAME}) do - Token("[]").set_text("[]") - end - - @OP.def_rule("[]=", proc{@lex_state == EXPR_FNAME}) do - Token("[]=").set_text("[]=") - end - - @OP.def_rule("[") do - @indent += 1 - if @lex_state == EXPR_FNAME - t = Token(TkfLBRACK) - else - if @lex_state == EXPR_BEG || @lex_state == EXPR_MID - t = Token(TkLBRACK) - elsif @lex_state == EXPR_ARG && @space_seen - t = Token(TkLBRACK) - else - t = Token(TkfLBRACK) - end - @lex_state = EXPR_BEG - end - t.set_text("[") - end - - @OP.def_rule("{") do - @indent += 1 - if @lex_state != EXPR_END && @lex_state != EXPR_ARG - t = Token(TkLBRACE) - else - t = Token(TkfLBRACE) - end - @lex_state = EXPR_BEG - t.set_text("{") - end - - @OP.def_rule('\\') do #' - if getc == "\n" - @space_seen = true - @continue = true - Token(TkSPACE).set_text("\\\n") - else - ungetc - Token("\\").set_text("\\") #" - end - end - - @OP.def_rule('%') do - |op, io| - if @lex_state == EXPR_BEG || @lex_state == EXPR_MID - identify_quotation('%') - elsif peek(0) == '=' - getc - Token(TkOPASGN, "%").set_text("%=") - elsif @lex_state == EXPR_ARG and @space_seen and peek(0) !~ /\s/ - identify_quotation('%') - else - @lex_state = EXPR_BEG - Token("%").set_text("%") - end - end - - @OP.def_rule('$') do #' - identify_gvar - end - - @OP.def_rule('@') do - if peek(0) =~ /[@\w_]/ - ungetc - identify_identifier - else - Token("@").set_text("@") - end - end - - @OP.def_rule("__END__", proc{@prev_char_no == 0 && peek(0) =~ /[\r\n]/}) do - throw :eof - end - - @OP.def_rule("") do - |op, io| - printf "MATCH: start %s: %s\n", op, io.inspect if RDoc::RubyLex.debug? - if peek(0) =~ /[0-9]/ - t = identify_number("") - elsif peek(0) =~ /[\w_]/ - t = identify_identifier - end - printf "MATCH: end %s: %s\n", op, io.inspect if RDoc::RubyLex.debug? - t - end - end - - def identify_gvar - @lex_state = EXPR_END - str = "$" - - tk = case ch = getc - when /[~_*$?!@\/\\;,=:<>".]/ #" - str << ch - Token(TkGVAR, str) - - when "-" - str << "-" << getc - Token(TkGVAR, str) - - when "&", "`", "'", "+" - str << ch - Token(TkBACK_REF, str) - - when /[1-9]/ - str << ch - while (ch = getc) =~ /[0-9]/ - str << ch - end - ungetc - Token(TkNTH_REF) - when /\w/ - ungetc - ungetc - return identify_identifier - else - ungetc - Token("$") - end - tk.set_text(str) - end - - def identify_identifier - token = "" - token.concat getc if peek(0) =~ /[$@]/ - token.concat getc if peek(0) == "@" - - while (ch = getc) =~ /\w|_/ - print ":", ch, ":" if RDoc::RubyLex.debug? - token.concat ch - end - ungetc - - if ch == "!" or ch == "?" - token.concat getc - end - # fix token - - # $stderr.puts "identifier - #{token}, state = #@lex_state" - - case token - when /^\$/ - return Token(TkGVAR, token).set_text(token) - when /^\@/ - @lex_state = EXPR_END - return Token(TkIVAR, token).set_text(token) - end - - if @lex_state != EXPR_DOT - print token, "\n" if RDoc::RubyLex.debug? - - token_c, *trans = TkReading2Token[token] - if token_c - # reserved word? - - if (@lex_state != EXPR_BEG && - @lex_state != EXPR_FNAME && - trans[1]) - # modifiers - token_c = TkSymbol2Token[trans[1]] - @lex_state = trans[0] - else - if @lex_state != EXPR_FNAME - if ENINDENT_CLAUSE.include?(token) - @indent += 1 - elsif DEINDENT_CLAUSE.include?(token) - @indent -= 1 - end - @lex_state = trans[0] - else - @lex_state = EXPR_END - end - end - return Token(token_c, token).set_text(token) - end - end - - if @lex_state == EXPR_FNAME - @lex_state = EXPR_END - if peek(0) == '=' - token.concat getc - end - elsif @lex_state == EXPR_BEG || @lex_state == EXPR_DOT - @lex_state = EXPR_ARG - else - @lex_state = EXPR_END - end - - if token[0, 1] =~ /[A-Z]/ - return Token(TkCONSTANT, token).set_text(token) - elsif token[token.size - 1, 1] =~ /[!?]/ - return Token(TkFID, token).set_text(token) - else - return Token(TkIDENTIFIER, token).set_text(token) - end - end - - def identify_here_document - ch = getc - if ch == "-" - ch = getc - indent = true - end - if /['"`]/ =~ ch # ' - lt = ch - quoted = "" - while (c = getc) && c != lt - quoted.concat c - end - else - lt = '"' - quoted = ch.dup - while (c = getc) && c =~ /\w/ - quoted.concat c - end - ungetc - end - - ltback, @ltype = @ltype, lt - reserve = "" - - while ch = getc - reserve << ch - if ch == "\\" #" - ch = getc - reserve << ch - elsif ch == "\n" - break - end - end - - str = "" - while (l = gets) - l.chomp! - l.strip! if indent - break if l == quoted - str << l.chomp << "\n" - end - - @reader.divert_read_from(reserve) - - @ltype = ltback - @lex_state = EXPR_END - Token(Ltype2Token[lt], str).set_text(str.dump) - end - - def identify_quotation(initial_char) - ch = getc - if lt = PERCENT_LTYPE[ch] - initial_char += ch - ch = getc - elsif ch =~ /\W/ - lt = "\"" - else - fail SyntaxError, "unknown type of %string ('#{ch}')" - end -# if ch !~ /\W/ -# ungetc -# next -# end - #@ltype = lt - @quoted = ch unless @quoted = PERCENT_PAREN[ch] - identify_string(lt, @quoted, ch, initial_char) - end - - def identify_number(start) - str = start.dup - - if start == "+" or start == "-" or start == "" - start = getc - str << start - end - - @lex_state = EXPR_END - - if start == "0" - if peek(0) == "x" - ch = getc - str << ch - match = /[0-9a-f_]/ - else - match = /[0-7_]/ - end - while ch = getc - if ch !~ match - ungetc - break - else - str << ch - end - end - return Token(TkINTEGER).set_text(str) - end - - type = TkINTEGER - allow_point = TRUE - allow_e = TRUE - while ch = getc - case ch - when /[0-9_]/ - str << ch - - when allow_point && "." - type = TkFLOAT - if peek(0) !~ /[0-9]/ - ungetc - break - end - str << ch - allow_point = false - - when allow_e && "e", allow_e && "E" - str << ch - type = TkFLOAT - if peek(0) =~ /[+-]/ - str << getc - end - allow_e = false - allow_point = false - else - ungetc - break - end - end - Token(type).set_text(str) - end - - def identify_string(ltype, quoted = ltype, opener=nil, initial_char = nil) - @ltype = ltype - @quoted = quoted - subtype = nil - - str = "" - str << initial_char if initial_char - str << (opener||quoted) - - nest = 0 - begin - while ch = getc - str << ch - if @quoted == ch - if nest == 0 - break - else - nest -= 1 - end - elsif opener == ch - nest += 1 - elsif @ltype != "'" && @ltype != "]" and ch == "#" - ch = getc - if ch == "{" - subtype = true - str << ch << skip_inner_expression - else - ungetc(ch) - end - elsif ch == '\\' #' - str << read_escape - end - end - if @ltype == "/" - if peek(0) =~ /i|o|n|e|s/ - str << getc - end - end - if subtype - Token(DLtype2Token[ltype], str) - else - Token(Ltype2Token[ltype], str) - end.set_text(str) - ensure - @ltype = nil - @quoted = nil - @lex_state = EXPR_END - end - end - - def skip_inner_expression - res = "" - nest = 0 - while (ch = getc) - res << ch - if ch == '}' - break if nest.zero? - nest -= 1 - elsif ch == '{' - nest += 1 - end - end - res - end - - def identify_comment - @ltype = "#" - comment = "#" - while ch = getc - if ch == "\\" - ch = getc - if ch == "\n" - ch = " " - else - comment << "\\" - end - else - if ch == "\n" - @ltype = nil - ungetc - break - end - end - comment << ch - end - return Token(TkCOMMENT).set_text(comment) - end - - def read_escape - res = "" - case ch = getc - when /[0-7]/ - ungetc ch - 3.times do - case ch = getc - when /[0-7]/ - when nil - break - else - ungetc - break - end - res << ch - end - - when "x" - res << ch - 2.times do - case ch = getc - when /[0-9a-fA-F]/ - when nil - break - else - ungetc - break - end - res << ch - end - - when "M" - res << ch - if (ch = getc) != '-' - ungetc - else - res << ch - if (ch = getc) == "\\" #" - res << ch - res << read_escape - else - res << ch - end - end - - when "C", "c" #, "^" - res << ch - if ch == "C" and (ch = getc) != "-" - ungetc - else - res << ch - if (ch = getc) == "\\" #" - res << ch - res << read_escape - else - res << ch - end - end - else - res << ch - end - res - end -end - -## -# Extracts code elements from a source file returning a TopLevel object -# containing the constituent file elements. -# -# This file is based on rtags -# -# RubyParser understands how to document: -# * classes -# * modules -# * methods -# * constants -# * aliases -# * private, public, protected -# * private_class_function, public_class_function -# * module_function -# * attr, attr_reader, attr_writer, attr_accessor -# * extra accessors given on the command line -# * metaprogrammed methods -# * require -# * include -# -# == Method Arguments -# -#-- -# NOTE: I don't think this works, needs tests, remove the paragraph following -# this block when known to work -# -# The parser extracts the arguments from the method definition. You can -# override this with a custom argument definition using the :args: directive: -# -# ## -# # This method tries over and over until it is tired -# -# def go_go_go(thing_to_try, tries = 10) # :args: thing_to_try -# puts thing_to_try -# go_go_go thing_to_try, tries - 1 -# end -# -# If you have a more-complex set of overrides you can use the :call-seq: -# directive: -#++ -# -# The parser extracts the arguments from the method definition. You can -# override this with a custom argument definition using the :call-seq: -# directive: -# -# ## -# # This method can be called with a range or an offset and length -# # -# # :call-seq: -# # my_method(Range) -# # my_method(offset, length) -# -# def my_method(*args) -# end -# -# The parser extracts +yield+ expressions from method bodies to gather the -# yielded argument names. If your method manually calls a block instead of -# yielding or you want to override the discovered argument names use -# the :yields: directive: -# -# ## -# # My method is awesome -# -# def my_method(&block) # :yields: happy, times -# block.call 1, 2 -# end -# -# == Metaprogrammed Methods -# -# To pick up a metaprogrammed method, the parser looks for a comment starting -# with '##' before an identifier: -# -# ## -# # This is a meta-programmed method! -# -# add_my_method :meta_method, :arg1, :arg2 -# -# The parser looks at the token after the identifier to determine the name, in -# this example, :meta_method. If a name cannot be found, a warning is printed -# and 'unknown is used. -# -# You can force the name of a method using the :method: directive: -# -# ## -# # :method: woo_hoo! -# -# By default, meta-methods are instance methods. To indicate that a method is -# a singleton method instead use the :singleton-method: directive: -# -# ## -# # :singleton-method: -# -# You can also use the :singleton-method: directive with a name: -# -# ## -# # :singleton-method: woo_hoo! -# -# == Hidden methods -# -# You can provide documentation for methods that don't appear using -# the :method: and :singleton-method: directives: -# -# ## -# # :method: ghost_method -# # There is a method here, but you can't see it! -# -# ## -# # this is a comment for a regular method -# -# def regular_method() end -# -# Note that by default, the :method: directive will be ignored if there is a -# standard rdocable item following it. - -class RDoc::Parser::Ruby < RDoc::Parser - - parse_files_matching(/\.rbw?$/) - - include RDoc::RubyToken - include RDoc::TokenStream - - NORMAL = "::" - SINGLE = "<<" - - def initialize(top_level, file_name, content, options, stats) - super - - @size = 0 - @token_listeners = nil - @scanner = RDoc::RubyLex.new content, @options - @scanner.exception_on_syntax_error = false - - reset - end - - def add_token_listener(obj) - @token_listeners ||= [] - @token_listeners << obj - end - - ## - # Look for the first comment in a file that isn't a shebang line. - - def collect_first_comment - skip_tkspace - res = '' - first_line = true - - tk = get_tk - - while TkCOMMENT === tk - if first_line and tk.text =~ /\A#!/ then - skip_tkspace - tk = get_tk - elsif first_line and tk.text =~ /\A#\s*-\*-/ then - first_line = false - skip_tkspace - tk = get_tk - else - first_line = false - res << tk.text << "\n" - tk = get_tk - - if TkNL === tk then - skip_tkspace false - tk = get_tk - end - end - end - - unget_tk tk - - res - end - - def error(msg) - msg = make_message msg - $stderr.puts msg - exit(1) - end - - ## - # Look for a 'call-seq' in the comment, and override the normal parameter - # stuff - - def extract_call_seq(comment, meth) - if comment.sub!(/:?call-seq:(.*?)^\s*\#?\s*$/m, '') then - seq = $1 - seq.gsub!(/^\s*\#\s*/, '') - meth.call_seq = seq - end - - meth - end - - def get_bool - skip_tkspace - tk = get_tk - case tk - when TkTRUE - true - when TkFALSE, TkNIL - false - else - unget_tk tk - true - end - end - - ## - # Look for the name of a class of module (optionally with a leading :: or - # with :: separated named) and return the ultimate name and container - - def get_class_or_module(container) - skip_tkspace - name_t = get_tk - - # class ::A -> A is in the top level - if TkCOLON2 === name_t then - name_t = get_tk - container = @top_level - end - - skip_tkspace(false) - - while TkCOLON2 === peek_tk do - prev_container = container - container = container.find_module_named(name_t.name) - if !container -# warn("Couldn't find module #{name_t.name}") - container = prev_container.add_module RDoc::NormalModule, name_t.name - end - get_tk - name_t = get_tk - end - skip_tkspace(false) - return [container, name_t] - end - - ## - # Return a superclass, which can be either a constant of an expression - - def get_class_specification - tk = get_tk - return "self" if TkSELF === tk - - res = "" - while TkCOLON2 === tk or TkCOLON3 === tk or TkCONSTANT === tk do - res += tk.text - tk = get_tk - end - - unget_tk(tk) - skip_tkspace(false) - - get_tkread # empty out read buffer - - tk = get_tk - - case tk - when TkNL, TkCOMMENT, TkSEMICOLON then - unget_tk(tk) - return res - end - - res += parse_call_parameters(tk) - res - end - - ## - # Parse a constant, which might be qualified by one or more class or module - # names - - def get_constant - res = "" - skip_tkspace(false) - tk = get_tk - - while TkCOLON2 === tk or TkCOLON3 === tk or TkCONSTANT === tk do - res += tk.text - tk = get_tk - end - -# if res.empty? -# warn("Unexpected token #{tk} in constant") -# end - unget_tk(tk) - res - end - - ## - # Get a constant that may be surrounded by parens - - def get_constant_with_optional_parens - skip_tkspace(false) - nest = 0 - while TkLPAREN === (tk = peek_tk) or TkfLPAREN === tk do - get_tk - skip_tkspace(true) - nest += 1 - end - - name = get_constant - - while nest > 0 - skip_tkspace(true) - tk = get_tk - nest -= 1 if TkRPAREN === tk - end - name - end - - def get_symbol_or_name - tk = get_tk - case tk - when TkSYMBOL - tk.text.sub(/^:/, '') - when TkId, TkOp - tk.name - when TkSTRING - tk.text - else - raise "Name or symbol expected (got #{tk})" - end - end - - def get_tk - tk = nil - if @tokens.empty? - tk = @scanner.token - @read.push @scanner.get_read - puts "get_tk1 => #{tk.inspect}" if $TOKEN_DEBUG - else - @read.push @unget_read.shift - tk = @tokens.shift - puts "get_tk2 => #{tk.inspect}" if $TOKEN_DEBUG - end - - if TkSYMBEG === tk then - set_token_position(tk.line_no, tk.char_no) - tk1 = get_tk - if TkId === tk1 or TkOp === tk1 or TkSTRING === tk1 then - if tk1.respond_to?(:name) - tk = Token(TkSYMBOL).set_text(":" + tk1.name) - else - tk = Token(TkSYMBOL).set_text(":" + tk1.text) - end - # remove the identifier we just read (we're about to - # replace it with a symbol) - @token_listeners.each do |obj| - obj.pop_token - end if @token_listeners - else - warn("':' not followed by identifier or operator") - tk = tk1 - end - end - - # inform any listeners of our shiny new token - @token_listeners.each do |obj| - obj.add_token(tk) - end if @token_listeners - - tk - end - - def get_tkread - read = @read.join("") - @read = [] - read - end - - ## - # Look for directives in a normal comment block: - # - # #-- - don't display comment from this point forward - # - # This routine modifies it's parameter - - def look_for_directives_in(context, comment) - preprocess = RDoc::Markup::PreProcess.new(@file_name, - @options.rdoc_include) - - preprocess.handle(comment) do |directive, param| - case directive - when 'enddoc' then - throw :enddoc - when 'main' then - @options.main_page = param - '' - when 'method', 'singleton-method' then - false # ignore - when 'section' then - context.set_current_section(param, comment) - comment.replace '' - break - when 'startdoc' then - context.start_doc - context.force_documentation = true - '' - when 'stopdoc' then - context.stop_doc - '' - when 'title' then - @options.title = param - '' - else - warn "Unrecognized directive '#{directive}'" - false - end - end - - remove_private_comments(comment) - end - - def make_message(msg) - prefix = "\n" + @file_name + ":" - if @scanner - prefix << "#{@scanner.line_no}:#{@scanner.char_no}: " - end - return prefix + msg - end - - def parse_attr(context, single, tk, comment) - args = parse_symbol_arg(1) - if args.size > 0 - name = args[0] - rw = "R" - skip_tkspace(false) - tk = get_tk - if TkCOMMA === tk then - rw = "RW" if get_bool - else - unget_tk tk - end - att = RDoc::Attr.new get_tkread, name, rw, comment - read_documentation_modifiers att, RDoc::ATTR_MODIFIERS - if att.document_self - context.add_attribute(att) - end - else - warn("'attr' ignored - looks like a variable") - end - end - - def parse_attr_accessor(context, single, tk, comment) - args = parse_symbol_arg - read = get_tkread - rw = "?" - - # If nodoc is given, don't document any of them - - tmp = RDoc::CodeObject.new - read_documentation_modifiers tmp, RDoc::ATTR_MODIFIERS - return unless tmp.document_self - - case tk.name - when "attr_reader" then rw = "R" - when "attr_writer" then rw = "W" - when "attr_accessor" then rw = "RW" - else - rw = @options.extra_accessor_flags[tk.name] - rw = '?' if rw.nil? - end - - for name in args - att = RDoc::Attr.new get_tkread, name, rw, comment - context.add_attribute att - end - end - - def parse_alias(context, single, tk, comment) - skip_tkspace - if TkLPAREN === peek_tk then - get_tk - skip_tkspace - end - new_name = get_symbol_or_name - @scanner.instance_eval{@lex_state = EXPR_FNAME} - skip_tkspace - if TkCOMMA === peek_tk then - get_tk - skip_tkspace - end - old_name = get_symbol_or_name - - al = RDoc::Alias.new get_tkread, old_name, new_name, comment - read_documentation_modifiers al, RDoc::ATTR_MODIFIERS - if al.document_self - context.add_alias(al) - end - end - - def parse_call_parameters(tk) - end_token = case tk - when TkLPAREN, TkfLPAREN - TkRPAREN - when TkRPAREN - return "" - else - TkNL - end - nest = 0 - - loop do - case tk - when TkSEMICOLON - break - when TkLPAREN, TkfLPAREN - nest += 1 - when end_token - if end_token == TkRPAREN - nest -= 1 - break if @scanner.lex_state == EXPR_END and nest <= 0 - else - break unless @scanner.continue - end - when TkCOMMENT - unget_tk(tk) - break - end - tk = get_tk - end - res = get_tkread.tr("\n", " ").strip - res = "" if res == ";" - res - end - - def parse_class(container, single, tk, comment) - container, name_t = get_class_or_module(container) - - case name_t - when TkCONSTANT - name = name_t.name - superclass = "Object" - - if TkLT === peek_tk then - get_tk - skip_tkspace(true) - superclass = get_class_specification - superclass = "<unknown>" if superclass.empty? - end - - cls_type = single == SINGLE ? RDoc::SingleClass : RDoc::NormalClass - cls = container.add_class cls_type, name, superclass - - @stats.add_class cls - - read_documentation_modifiers cls, RDoc::CLASS_MODIFIERS - cls.record_location @top_level - - parse_statements cls - cls.comment = comment - - when TkLSHFT - case name = get_class_specification - when "self", container.name - parse_statements(container, SINGLE) - else - other = RDoc::TopLevel.find_class_named(name) - unless other - # other = @top_level.add_class(NormalClass, name, nil) - # other.record_location(@top_level) - # other.comment = comment - other = RDoc::NormalClass.new "Dummy", nil - end - - @stats.add_class other - - read_documentation_modifiers other, RDoc::CLASS_MODIFIERS - parse_statements(other, SINGLE) - end - - else - warn("Expected class name or '<<'. Got #{name_t.class}: #{name_t.text.inspect}") - end - end - - def parse_constant(container, single, tk, comment) - name = tk.name - skip_tkspace(false) - eq_tk = get_tk - - unless TkASSIGN === eq_tk then - unget_tk(eq_tk) - return - end - - - nest = 0 - get_tkread - - tk = get_tk - if TkGT === tk then - unget_tk(tk) - unget_tk(eq_tk) - return - end - - loop do - case tk - when TkSEMICOLON - break - when TkLPAREN, TkfLPAREN - nest += 1 - when TkRPAREN - nest -= 1 - when TkCOMMENT - if nest <= 0 && @scanner.lex_state == EXPR_END - unget_tk(tk) - break - end - when TkNL - if (@scanner.lex_state == EXPR_END and nest <= 0) || !@scanner.continue - unget_tk(tk) - break - end - end - tk = get_tk - end - - res = get_tkread.tr("\n", " ").strip - res = "" if res == ";" - - con = RDoc::Constant.new name, res, comment - read_documentation_modifiers con, RDoc::CONSTANT_MODIFIERS - - if con.document_self - container.add_constant(con) - end - end - - def parse_comment(container, tk, comment) - line_no = tk.line_no - column = tk.char_no - - singleton = !!comment.sub!(/(^# +:?)(singleton-)(method:)/, '\1\3') - - if comment.sub!(/^# +:?method: *(\S*).*?\n/i, '') then - name = $1 unless $1.empty? - else - return nil - end - - meth = RDoc::GhostMethod.new get_tkread, name - meth.singleton = singleton - - @stats.add_method meth - - meth.start_collecting_tokens - indent = TkSPACE.new 1, 1 - indent.set_text " " * column - - position_comment = TkCOMMENT.new(line_no, 1, "# File #{@top_level.file_absolute_name}, line #{line_no}") - meth.add_tokens [position_comment, NEWLINE_TOKEN, indent] - - meth.params = '' - - extract_call_seq comment, meth - - container.add_method meth if meth.document_self - - meth.comment = comment - end - - def parse_include(context, comment) - loop do - skip_tkspace_comment - - name = get_constant_with_optional_parens - context.add_include RDoc::Include.new(name, comment) unless name.empty? - - return unless TkCOMMA === peek_tk - get_tk - end - end - - ## - # Parses a meta-programmed method - - def parse_meta_method(container, single, tk, comment) - line_no = tk.line_no - column = tk.char_no - - start_collecting_tokens - add_token tk - add_token_listener self - - skip_tkspace false - - singleton = !!comment.sub!(/(^# +:?)(singleton-)(method:)/, '\1\3') - - if comment.sub!(/^# +:?method: *(\S*).*?\n/i, '') then - name = $1 unless $1.empty? - end - - if name.nil? then - name_t = get_tk - case name_t - when TkSYMBOL then - name = name_t.text[1..-1] - when TkSTRING then - name = name_t.text[1..-2] - else - warn "#{container.top_level.file_relative_name}:#{name_t.line_no} unknown name token #{name_t.inspect} for meta-method" - name = 'unknown' - end - end - - meth = RDoc::MetaMethod.new get_tkread, name - meth.singleton = singleton - - @stats.add_method meth - - remove_token_listener self - - meth.start_collecting_tokens - indent = TkSPACE.new 1, 1 - indent.set_text " " * column - - position_comment = TkCOMMENT.new(line_no, 1, "# File #{@top_level.file_absolute_name}, line #{line_no}") - meth.add_tokens [position_comment, NEWLINE_TOKEN, indent] - meth.add_tokens @token_stream - - add_token_listener meth - - meth.params = '' - - extract_call_seq comment, meth - - container.add_method meth if meth.document_self - - last_tk = tk - - while tk = get_tk do - case tk - when TkSEMICOLON then - break - when TkNL then - break unless last_tk and TkCOMMA === last_tk - when TkSPACE then - # expression continues - else - last_tk = tk - end - end - - remove_token_listener meth - - meth.comment = comment - end - - ## - # Parses a method - - def parse_method(container, single, tk, comment) - line_no = tk.line_no - column = tk.char_no - - start_collecting_tokens - add_token(tk) - add_token_listener(self) - - @scanner.instance_eval do @lex_state = EXPR_FNAME end - - skip_tkspace(false) - name_t = get_tk - back_tk = skip_tkspace - meth = nil - added_container = false - - dot = get_tk - if TkDOT === dot or TkCOLON2 === dot then - @scanner.instance_eval do @lex_state = EXPR_FNAME end - skip_tkspace - name_t2 = get_tk - - case name_t - when TkSELF then - name = name_t2.name - when TkCONSTANT then - name = name_t2.name - prev_container = container - container = container.find_module_named(name_t.name) - unless container then - added_container = true - obj = name_t.name.split("::").inject(Object) do |state, item| - state.const_get(item) - end rescue nil - - type = obj.class == Class ? RDoc::NormalClass : RDoc::NormalModule - - unless [Class, Module].include?(obj.class) then - warn("Couldn't find #{name_t.name}. Assuming it's a module") - end - - if type == RDoc::NormalClass then - container = prev_container.add_class(type, name_t.name, obj.superclass.name) - else - container = prev_container.add_module(type, name_t.name) - end - - container.record_location @top_level - end - else - # warn("Unexpected token '#{name_t2.inspect}'") - # break - skip_method(container) - return - end - - meth = RDoc::AnyMethod.new(get_tkread, name) - meth.singleton = true - else - unget_tk dot - back_tk.reverse_each do |token| - unget_tk token - end - name = name_t.name - - meth = RDoc::AnyMethod.new get_tkread, name - meth.singleton = (single == SINGLE) - end - - @stats.add_method meth - - remove_token_listener self - - meth.start_collecting_tokens - indent = TkSPACE.new 1, 1 - indent.set_text " " * column - - token = TkCOMMENT.new(line_no, 1, "# File #{@top_level.file_absolute_name}, line #{line_no}") - meth.add_tokens [token, NEWLINE_TOKEN, indent] - meth.add_tokens @token_stream - - add_token_listener meth - - @scanner.instance_eval do @continue = false end - parse_method_parameters meth - - if meth.document_self then - container.add_method meth - elsif added_container then - container.document_self = false - end - - # Having now read the method parameters and documentation modifiers, we - # now know whether we have to rename #initialize to ::new - - if name == "initialize" && !meth.singleton then - if meth.dont_rename_initialize then - meth.visibility = :protected - else - meth.singleton = true - meth.name = "new" - meth.visibility = :public - end - end - - parse_statements(container, single, meth) - - remove_token_listener(meth) - - extract_call_seq comment, meth - - meth.comment = comment - end - - def parse_method_or_yield_parameters(method = nil, - modifiers = RDoc::METHOD_MODIFIERS) - skip_tkspace(false) - tk = get_tk - - # Little hack going on here. In the statement - # f = 2*(1+yield) - # We see the RPAREN as the next token, so we need - # to exit early. This still won't catch all cases - # (such as "a = yield + 1" - end_token = case tk - when TkLPAREN, TkfLPAREN - TkRPAREN - when TkRPAREN - return "" - else - TkNL - end - nest = 0 - - loop do - case tk - when TkSEMICOLON - break - when TkLBRACE - nest += 1 - when TkRBRACE - # we might have a.each {|i| yield i } - unget_tk(tk) if nest.zero? - nest -= 1 - break if nest <= 0 - when TkLPAREN, TkfLPAREN - nest += 1 - when end_token - if end_token == TkRPAREN - nest -= 1 - break if @scanner.lex_state == EXPR_END and nest <= 0 - else - break unless @scanner.continue - end - when method && method.block_params.nil? && TkCOMMENT - unget_tk(tk) - read_documentation_modifiers(method, modifiers) - end - tk = get_tk - end - res = get_tkread.tr("\n", " ").strip - res = "" if res == ";" - res - end - - ## - # Capture the method's parameters. Along the way, look for a comment - # containing: - # - # # yields: .... - # - # and add this as the block_params for the method - - def parse_method_parameters(method) - res = parse_method_or_yield_parameters(method) - res = "(" + res + ")" unless res[0] == ?( - method.params = res unless method.params - if method.block_params.nil? - skip_tkspace(false) - read_documentation_modifiers method, RDoc::METHOD_MODIFIERS - end - end - - def parse_module(container, single, tk, comment) - container, name_t = get_class_or_module(container) - - name = name_t.name - - mod = container.add_module RDoc::NormalModule, name - mod.record_location @top_level - - @stats.add_module mod - - read_documentation_modifiers mod, RDoc::CLASS_MODIFIERS - parse_statements(mod) - mod.comment = comment - end - - def parse_require(context, comment) - skip_tkspace_comment - tk = get_tk - if TkLPAREN === tk then - skip_tkspace_comment - tk = get_tk - end - - name = nil - case tk - when TkSTRING - name = tk.text - # when TkCONSTANT, TkIDENTIFIER, TkIVAR, TkGVAR - # name = tk.name - when TkDSTRING - warn "Skipping require of dynamic string: #{tk.text}" - # else - # warn "'require' used as variable" - end - if name - context.add_require RDoc::Require.new(name, comment) - else - unget_tk(tk) - end - end - - def parse_statements(container, single = NORMAL, current_method = nil, - comment = '') - nest = 1 - save_visibility = container.visibility - - non_comment_seen = true - - while tk = get_tk do - keep_comment = false - - non_comment_seen = true unless TkCOMMENT === tk - - case tk - when TkNL then - skip_tkspace true # Skip blanks and newlines - tk = get_tk - - if TkCOMMENT === tk then - if non_comment_seen then - # Look for RDoc in a comment about to be thrown away - parse_comment container, tk, comment unless comment.empty? - - comment = '' - non_comment_seen = false - end - - while TkCOMMENT === tk do - comment << tk.text << "\n" - tk = get_tk # this is the newline - skip_tkspace(false) # leading spaces - tk = get_tk - end - - unless comment.empty? then - look_for_directives_in container, comment - - if container.done_documenting then - container.ongoing_visibility = save_visibility - end - end - - keep_comment = true - else - non_comment_seen = true - end - - unget_tk tk - keep_comment = true - - when TkCLASS then - if container.document_children then - parse_class container, single, tk, comment - else - nest += 1 - end - - when TkMODULE then - if container.document_children then - parse_module container, single, tk, comment - else - nest += 1 - end - - when TkDEF then - if container.document_self then - parse_method container, single, tk, comment - else - nest += 1 - end - - when TkCONSTANT then - if container.document_self then - parse_constant container, single, tk, comment - end - - when TkALIAS then - if container.document_self then - parse_alias container, single, tk, comment - end - - when TkYIELD then - if current_method.nil? then - warn "Warning: yield outside of method" if container.document_self - else - parse_yield container, single, tk, current_method - end - - # Until and While can have a 'do', which shouldn't increase the nesting. - # We can't solve the general case, but we can handle most occurrences by - # ignoring a do at the end of a line. - - when TkUNTIL, TkWHILE then - nest += 1 - skip_optional_do_after_expression - - # 'for' is trickier - when TkFOR then - nest += 1 - skip_for_variable - skip_optional_do_after_expression - - when TkCASE, TkDO, TkIF, TkUNLESS, TkBEGIN then - nest += 1 - - when TkIDENTIFIER then - if nest == 1 and current_method.nil? then - case tk.name - when 'private', 'protected', 'public', 'private_class_method', - 'public_class_method', 'module_function' then - parse_visibility container, single, tk - keep_comment = true - when 'attr' then - parse_attr container, single, tk, comment - when /^attr_(reader|writer|accessor)$/, @options.extra_accessors then - parse_attr_accessor container, single, tk, comment - when 'alias_method' then - if container.document_self then - parse_alias container, single, tk, comment - end - else - if container.document_self and comment =~ /\A#\#$/ then - parse_meta_method container, single, tk, comment - end - end - end - - case tk.name - when "require" then - parse_require container, comment - when "include" then - parse_include container, comment - end - - when TkEND then - nest -= 1 - if nest == 0 then - read_documentation_modifiers container, RDoc::CLASS_MODIFIERS - container.ongoing_visibility = save_visibility - return - end - - end - - comment = '' unless keep_comment - - begin - get_tkread - skip_tkspace(false) - end while peek_tk == TkNL - end - end - - def parse_symbol_arg(no = nil) - args = [] - skip_tkspace_comment - case tk = get_tk - when TkLPAREN - loop do - skip_tkspace_comment - if tk1 = parse_symbol_in_arg - args.push tk1 - break if no and args.size >= no - end - - skip_tkspace_comment - case tk2 = get_tk - when TkRPAREN - break - when TkCOMMA - else - warn("unexpected token: '#{tk2.inspect}'") if $DEBUG_RDOC - break - end - end - else - unget_tk tk - if tk = parse_symbol_in_arg - args.push tk - return args if no and args.size >= no - end - - loop do - skip_tkspace(false) - - tk1 = get_tk - unless TkCOMMA === tk1 then - unget_tk tk1 - break - end - - skip_tkspace_comment - if tk = parse_symbol_in_arg - args.push tk - break if no and args.size >= no - end - end - end - args - end - - def parse_symbol_in_arg - case tk = get_tk - when TkSYMBOL - tk.text.sub(/^:/, '') - when TkSTRING - eval @read[-1] - else - warn("Expected symbol or string, got #{tk.inspect}") if $DEBUG_RDOC - nil - end - end - - def parse_toplevel_statements(container) - comment = collect_first_comment - look_for_directives_in(container, comment) - container.comment = comment unless comment.empty? - parse_statements container, NORMAL, nil, comment - end - - def parse_visibility(container, single, tk) - singleton = (single == SINGLE) - - vis_type = tk.name - - vis = case vis_type - when 'private' then :private - when 'protected' then :protected - when 'public' then :public - when 'private_class_method' then - singleton = true - :private - when 'public_class_method' then - singleton = true - :public - when 'module_function' then - singleton = true - :public - else - raise "Invalid visibility: #{tk.name}" - end - - skip_tkspace_comment false - - case peek_tk - # Ryan Davis suggested the extension to ignore modifiers, because he - # often writes - # - # protected unless $TESTING - # - when TkNL, TkUNLESS_MOD, TkIF_MOD, TkSEMICOLON then - container.ongoing_visibility = vis - else - if vis_type == 'module_function' then - args = parse_symbol_arg - container.set_visibility_for args, :private, false - - module_functions = [] - - container.methods_matching args do |m| - s_m = m.dup - s_m.singleton = true if RDoc::AnyMethod === s_m - s_m.visibility = :public - module_functions << s_m - end - - module_functions.each do |s_m| - case s_m - when RDoc::AnyMethod then - container.add_method s_m - when RDoc::Attr then - container.add_attribute s_m - end - end - else - args = parse_symbol_arg - container.set_visibility_for args, vis, singleton - end - end - end - - def parse_yield_parameters - parse_method_or_yield_parameters - end - - def parse_yield(context, single, tk, method) - if method.block_params.nil? - get_tkread - @scanner.instance_eval{@continue = false} - method.block_params = parse_yield_parameters - end - end - - def peek_read - @read.join('') - end - - ## - # Peek at the next token, but don't remove it from the stream - - def peek_tk - unget_tk(tk = get_tk) - tk - end - - ## - # Directives are modifier comments that can appear after class, module, or - # method names. For example: - # - # def fred # :yields: a, b - # - # or: - # - # class MyClass # :nodoc: - # - # We return the directive name and any parameters as a two element array - - def read_directive(allowed) - tk = get_tk - result = nil - if TkCOMMENT === tk - if tk.text =~ /\s*:?(\w+):\s*(.*)/ - directive = $1.downcase - if allowed.include?(directive) - result = [directive, $2] - end - end - else - unget_tk(tk) - end - result - end - - def read_documentation_modifiers(context, allow) - dir = read_directive(allow) - - case dir[0] - when "notnew", "not_new", "not-new" then - context.dont_rename_initialize = true - - when "nodoc" then - context.document_self = false - if dir[1].downcase == "all" - context.document_children = false - end - - when "doc" then - context.document_self = true - context.force_documentation = true - - when "yield", "yields" then - unless context.params.nil? - context.params.sub!(/(,|)\s*&\w+/,'') # remove parameter &proc - end - - context.block_params = dir[1] - - when "arg", "args" then - context.params = dir[1] - end if dir - end - - def remove_private_comments(comment) - comment.gsub!(/^#--.*?^#\+\+/m, '') - comment.sub!(/^#--.*/m, '') - end - - def remove_token_listener(obj) - @token_listeners.delete(obj) - end - - def reset - @tokens = [] - @unget_read = [] - @read = [] - end - - def scan - reset - - catch(:eof) do - catch(:enddoc) do - begin - parse_toplevel_statements(@top_level) - rescue Exception => e - $stderr.puts <<-EOF - - -RDoc failure in #{@file_name} at or around line #{@scanner.line_no} column -#{@scanner.char_no} - -Before reporting this, could you check that the file you're documenting -compiles cleanly--RDoc is not a full Ruby parser, and gets confused easily if -fed invalid programs. - -The internal error was: - - EOF - - e.set_backtrace(e.backtrace[0,4]) - raise - end - end - end - - @top_level - end - - ## - # while, until, and for have an optional do - - def skip_optional_do_after_expression - skip_tkspace(false) - tk = get_tk - case tk - when TkLPAREN, TkfLPAREN - end_token = TkRPAREN - else - end_token = TkNL - end - - nest = 0 - @scanner.instance_eval{@continue = false} - - loop do - case tk - when TkSEMICOLON - break - when TkLPAREN, TkfLPAREN - nest += 1 - when TkDO - break if nest.zero? - when end_token - if end_token == TkRPAREN - nest -= 1 - break if @scanner.lex_state == EXPR_END and nest.zero? - else - break unless @scanner.continue - end - end - tk = get_tk - end - skip_tkspace(false) - - get_tk if TkDO === peek_tk - end - - ## - # skip the var [in] part of a 'for' statement - - def skip_for_variable - skip_tkspace(false) - tk = get_tk - skip_tkspace(false) - tk = get_tk - unget_tk(tk) unless TkIN === tk - end - - def skip_method(container) - meth = RDoc::AnyMethod.new "", "anon" - parse_method_parameters(meth) - parse_statements(container, false, meth) - end - - ## - # Skip spaces - - def skip_tkspace(skip_nl = true) - tokens = [] - - while TkSPACE === (tk = get_tk) or (skip_nl and TkNL === tk) do - tokens.push tk - end - - unget_tk(tk) - tokens - end - - ## - # Skip spaces until a comment is found - - def skip_tkspace_comment(skip_nl = true) - loop do - skip_tkspace(skip_nl) - return unless TkCOMMENT === peek_tk - get_tk - end - end - - def unget_tk(tk) - @tokens.unshift tk - @unget_read.unshift @read.pop - - # Remove this token from any listeners - @token_listeners.each do |obj| - obj.pop_token - end if @token_listeners - end - - def warn(msg) - return if @options.quiet - msg = make_message msg - $stderr.puts msg - end - -end - diff --git a/trunk/lib/rdoc/parser/simple.rb b/trunk/lib/rdoc/parser/simple.rb deleted file mode 100644 index 6e123a4655..0000000000 --- a/trunk/lib/rdoc/parser/simple.rb +++ /dev/null @@ -1,38 +0,0 @@ -require 'rdoc/parser' - -## -# Parse a non-source file. We basically take the whole thing as one big -# comment. If the first character in the file is '#', we strip leading pound -# signs. - -class RDoc::Parser::Simple < RDoc::Parser - - parse_files_matching(//) - - ## - # Prepare to parse a plain file - - def initialize(top_level, file_name, content, options, stats) - super - - preprocess = RDoc::Markup::PreProcess.new @file_name, @options.rdoc_include - - preprocess.handle @content do |directive, param| - warn "Unrecognized directive '#{directive}' in #{@file_name}" - end - end - - ## - # Extract the file contents and attach them to the toplevel as a comment - - def scan - @top_level.comment = remove_private_comments(@content) - @top_level - end - - def remove_private_comments(comment) - comment.gsub(/^--[^-].*?^\+\+/m, '').sub(/^--.*/m, '') - end - -end - diff --git a/trunk/lib/rdoc/rdoc.rb b/trunk/lib/rdoc/rdoc.rb deleted file mode 100644 index bc0a32f407..0000000000 --- a/trunk/lib/rdoc/rdoc.rb +++ /dev/null @@ -1,293 +0,0 @@ -require 'rdoc' - -require 'rdoc/parser' - -# Simple must come first -require 'rdoc/parser/simple' -require 'rdoc/parser/ruby' -require 'rdoc/parser/c' -require 'rdoc/parser/f95' - -require 'rdoc/stats' -require 'rdoc/options' - -require 'rdoc/diagram' - -require 'find' -require 'fileutils' -require 'time' - -module RDoc - - ## - # Encapsulate the production of rdoc documentation. Basically you can use - # this as you would invoke rdoc from the command line: - # - # rdoc = RDoc::RDoc.new - # rdoc.document(args) - # - # Where +args+ is an array of strings, each corresponding to an argument - # you'd give rdoc on the command line. See rdoc/rdoc.rb for details. - - class RDoc - - Generator = Struct.new(:file_name, :class_name, :key) - - ## - # Accessor for statistics. Available after each call to parse_files - - attr_reader :stats - - ## - # This is the list of output generator that we support - - GENERATORS = {} - - $LOAD_PATH.collect do |d| - File.expand_path d - end.find_all do |d| - File.directory? "#{d}/rdoc/generator" - end.each do |dir| - Dir.entries("#{dir}/rdoc/generator").each do |gen| - next unless /(\w+)\.rb$/ =~ gen - type = $1 - unless GENERATORS.has_key? type - GENERATORS[type] = Generator.new("rdoc/generator/#{gen}", - "#{type.upcase}".intern, - type) - end - end - end - - def initialize - @stats = nil - end - - ## - # Report an error message and exit - - def error(msg) - raise ::RDoc::Error, msg - end - - ## - # Create an output dir if it doesn't exist. If it does exist, but doesn't - # contain the flag file <tt>created.rid</tt> then we refuse to use it, as - # we may clobber some manually generated documentation - - def setup_output_dir(op_dir, force) - flag_file = output_flag_file(op_dir) - if File.exist?(op_dir) - unless File.directory?(op_dir) - error "'#{op_dir}' exists, and is not a directory" - end - begin - created = File.read(flag_file) - rescue SystemCallError - error "\nDirectory #{op_dir} already exists, but it looks like it\n" + - "isn't an RDoc directory. Because RDoc doesn't want to risk\n" + - "destroying any of your existing files, you'll need to\n" + - "specify a different output directory name (using the\n" + - "--op <dir> option).\n\n" - else - last = (Time.parse(created) unless force rescue nil) - end - else - FileUtils.mkdir_p(op_dir) - end - last - end - - ## - # Update the flag file in an output directory. - - def update_output_dir(op_dir, time) - File.open(output_flag_file(op_dir), "w") {|f| f.puts time.rfc2822 } - end - - ## - # Return the path name of the flag file in an output directory. - - def output_flag_file(op_dir) - File.join(op_dir, "created.rid") - end - - ## - # The .document file contains a list of file and directory name patterns, - # representing candidates for documentation. It may also contain comments - # (starting with '#') - - def parse_dot_doc_file(in_dir, filename, options) - # read and strip comments - patterns = File.read(filename).gsub(/#.*/, '') - - result = [] - - patterns.split.each do |patt| - candidates = Dir.glob(File.join(in_dir, patt)) - result.concat(normalized_file_list(options, candidates)) - end - result - end - - ## - # Given a list of files and directories, create a list of all the Ruby - # files they contain. - # - # If +force_doc+ is true we always add the given files, if false, only - # add files that we guarantee we can parse. It is true when looking at - # files given on the command line, false when recursing through - # subdirectories. - # - # The effect of this is that if you want a file with a non-standard - # extension parsed, you must name it explicitly. - - def normalized_file_list(options, relative_files, force_doc = false, - exclude_pattern = nil) - file_list = [] - - relative_files.each do |rel_file_name| - next if exclude_pattern && exclude_pattern =~ rel_file_name - stat = File.stat(rel_file_name) - case type = stat.ftype - when "file" - next if @last_created and stat.mtime < @last_created - - if force_doc or ::RDoc::Parser.can_parse(rel_file_name) then - file_list << rel_file_name.sub(/^\.\//, '') - end - when "directory" - next if rel_file_name == "CVS" || rel_file_name == ".svn" - dot_doc = File.join(rel_file_name, DOT_DOC_FILENAME) - if File.file?(dot_doc) - file_list.concat(parse_dot_doc_file(rel_file_name, dot_doc, options)) - else - file_list.concat(list_files_in_directory(rel_file_name, options)) - end - else - raise RDoc::Error, "I can't deal with a #{type} #{rel_file_name}" - end - end - - file_list - end - - ## - # Return a list of the files to be processed in a directory. We know that - # this directory doesn't have a .document file, so we're looking for real - # files. However we may well contain subdirectories which must be tested - # for .document files. - - def list_files_in_directory(dir, options) - files = Dir.glob File.join(dir, "*") - - normalized_file_list options, files, false, options.exclude - end - - ## - # Parse each file on the command line, recursively entering directories. - - def parse_files(options) - @stats = Stats.new options.verbosity - - files = options.files - files = ["."] if files.empty? - - file_list = normalized_file_list(options, files, true) - - return [] if file_list.empty? - - file_info = [] - - file_list.each do |filename| - @stats.add_file filename - - content = if RUBY_VERSION >= '1.9' then - File.open(filename, "r:ascii-8bit") { |f| f.read } - else - File.read filename - end - - if defined? Encoding then - if /coding:\s*(\S+)/ =~ content[/\A(?:.*\n){0,2}/] - if enc = ::Encoding.find($1) - content.force_encoding(enc) - end - end - end - - top_level = ::RDoc::TopLevel.new filename - - parser = ::RDoc::Parser.for top_level, filename, content, options, - @stats - - file_info << parser.scan - end - - file_info - end - - ## - # Format up one or more files according to the given arguments. - # - # For simplicity, _argv_ is an array of strings, equivalent to the strings - # that would be passed on the command line. (This isn't a coincidence, as - # we _do_ pass in ARGV when running interactively). For a list of options, - # see rdoc/rdoc.rb. By default, output will be stored in a directory - # called +doc+ below the current directory, so make sure you're somewhere - # writable before invoking. - # - # Throws: RDoc::Error on error - - def document(argv) - TopLevel::reset - - @options = Options.new GENERATORS - @options.parse argv - - @last_created = nil - - unless @options.all_one_file then - @last_created = setup_output_dir @options.op_dir, @options.force_update - end - - start_time = Time.now - - file_info = parse_files @options - - @options.title = "RDoc Documentation" - - if file_info.empty? - $stderr.puts "\nNo newer files." unless @options.quiet - else - @gen = @options.generator - - $stderr.puts "\nGenerating #{@gen.key.upcase}..." unless @options.quiet - - require @gen.file_name - - gen_class = ::RDoc::Generator.const_get @gen.class_name - @gen = gen_class.for @options - - pwd = Dir.pwd - - Dir.chdir @options.op_dir unless @options.all_one_file - - begin - Diagram.new(file_info, @options).draw if @options.diagram - @gen.generate(file_info) - update_output_dir(".", start_time) - ensure - Dir.chdir(pwd) - end - end - - unless @options.quiet - puts - @stats.print - end - end - end - -end - diff --git a/trunk/lib/rdoc/ri.rb b/trunk/lib/rdoc/ri.rb deleted file mode 100644 index a3a858e673..0000000000 --- a/trunk/lib/rdoc/ri.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'rdoc' - -module RDoc::RI - - class Error < RDoc::Error; end - -end - diff --git a/trunk/lib/rdoc/ri/cache.rb b/trunk/lib/rdoc/ri/cache.rb deleted file mode 100644 index 2e267d95fb..0000000000 --- a/trunk/lib/rdoc/ri/cache.rb +++ /dev/null @@ -1,188 +0,0 @@ -require 'rdoc/ri' - -class RDoc::RI::ClassEntry - - attr_reader :name - attr_reader :path_names - - def initialize(path_name, name, in_class) - @path_names = [ path_name ] - @name = name - @in_class = in_class - @class_methods = [] - @instance_methods = [] - @inferior_classes = [] - end - - # We found this class in more tha one place, so add - # in the name from there. - def add_path(path) - @path_names << path - end - - # read in our methods and any classes - # and modules in our namespace. Methods are - # stored in files called name-c|i.yaml, - # where the 'name' portion is the external - # form of the method name and the c|i is a class|instance - # flag - - def load_from(dir) - Dir.foreach(dir) do |name| - next if name =~ /^\./ - - # convert from external to internal form, and - # extract the instance/class flag - - if name =~ /^(.*?)-(c|i).yaml$/ - external_name = $1 - is_class_method = $2 == "c" - internal_name = RiWriter.external_to_internal(external_name) - list = is_class_method ? @class_methods : @instance_methods - path = File.join(dir, name) - list << MethodEntry.new(path, internal_name, is_class_method, self) - else - full_name = File.join(dir, name) - if File.directory?(full_name) - inf_class = @inferior_classes.find {|c| c.name == name } - if inf_class - inf_class.add_path(full_name) - else - inf_class = ClassEntry.new(full_name, name, self) - @inferior_classes << inf_class - end - inf_class.load_from(full_name) - end - end - end - end - - # Return a list of any classes or modules that we contain - # that match a given string - - def contained_modules_matching(name) - @inferior_classes.find_all {|c| c.name[name]} - end - - def classes_and_modules - @inferior_classes - end - - # Return an exact match to a particular name - def contained_class_named(name) - @inferior_classes.find {|c| c.name == name} - end - - # return the list of local methods matching name - # We're split into two because we need distinct behavior - # when called from the _toplevel_ - def methods_matching(name, is_class_method) - local_methods_matching(name, is_class_method) - end - - # Find methods matching 'name' in ourselves and in - # any classes we contain - def recursively_find_methods_matching(name, is_class_method) - res = local_methods_matching(name, is_class_method) - @inferior_classes.each do |c| - res.concat(c.recursively_find_methods_matching(name, is_class_method)) - end - res - end - - - # Return our full name - def full_name - res = @in_class.full_name - res << "::" unless res.empty? - res << @name - end - - # Return a list of all out method names - def all_method_names - res = @class_methods.map {|m| m.full_name } - @instance_methods.each {|m| res << m.full_name} - res - end - - private - - # Return a list of all our methods matching a given string. - # Is +is_class_methods+ if 'nil', we don't care if the method - # is a class method or not, otherwise we only return - # those methods that match - def local_methods_matching(name, is_class_method) - - list = case is_class_method - when nil then @class_methods + @instance_methods - when true then @class_methods - when false then @instance_methods - else fail "Unknown is_class_method: #{is_class_method.inspect}" - end - - list.find_all {|m| m.name; m.name[name]} - end -end - -## -# A TopLevelEntry is like a class entry, but when asked to search for methods -# searches all classes, not just itself - -class RDoc::RI::TopLevelEntry < RDoc::RI::ClassEntry - def methods_matching(name, is_class_method) - res = recursively_find_methods_matching(name, is_class_method) - end - - def full_name - "" - end - - def module_named(name) - - end - -end - -class RDoc::RI::MethodEntry - attr_reader :name - attr_reader :path_name - - def initialize(path_name, name, is_class_method, in_class) - @path_name = path_name - @name = name - @is_class_method = is_class_method - @in_class = in_class - end - - def full_name - res = @in_class.full_name - unless res.empty? - if @is_class_method - res << "::" - else - res << "#" - end - end - res << @name - end -end - -## -# We represent everything know about all 'ri' files accessible to this program - -class RDoc::RI::Cache - - attr_reader :toplevel - - def initialize(dirs) - # At the top level we have a dummy module holding the - # overall namespace - @toplevel = RDoc::RI::TopLevelEntry.new('', '::', nil) - - dirs.each do |dir| - @toplevel.load_from(dir) - end - end - -end - diff --git a/trunk/lib/rdoc/ri/descriptions.rb b/trunk/lib/rdoc/ri/descriptions.rb deleted file mode 100644 index 0d8560323a..0000000000 --- a/trunk/lib/rdoc/ri/descriptions.rb +++ /dev/null @@ -1,153 +0,0 @@ -require 'yaml' -require 'rdoc/markup/fragments' -require 'rdoc/ri' - -## -# Descriptions are created by RDoc (in ri_generator) and written out in -# serialized form into the documentation tree. ri then reads these to generate -# the documentation - -class RDoc::RI::NamedThing - attr_reader :name - def initialize(name) - @name = name - end - - def <=>(other) - @name <=> other.name - end - - def hash - @name.hash - end - - def eql?(other) - @name.eql?(other) - end -end - -class RDoc::RI::AliasName < RDoc::RI::NamedThing; end - -class RDoc::RI::Attribute < RDoc::RI::NamedThing - attr_reader :rw, :comment - - def initialize(name, rw, comment) - super(name) - @rw = rw - @comment = comment - end -end - -class RDoc::RI::Constant < RDoc::RI::NamedThing - attr_reader :value, :comment - - def initialize(name, value, comment) - super(name) - @value = value - @comment = comment - end -end - -class RDoc::RI::IncludedModule < RDoc::RI::NamedThing; end - -class RDoc::RI::MethodSummary < RDoc::RI::NamedThing - def initialize(name="") - super - end -end - -class RDoc::RI::Description - attr_accessor :name - attr_accessor :full_name - attr_accessor :comment - - def serialize - self.to_yaml - end - - def self.deserialize(from) - YAML.load(from) - end - - def <=>(other) - @name <=> other.name - end -end - -class RDoc::RI::ModuleDescription < RDoc::RI::Description - - attr_accessor :class_methods - attr_accessor :instance_methods - attr_accessor :attributes - attr_accessor :constants - attr_accessor :includes - - # merge in another class description into this one - def merge_in(old) - merge(@class_methods, old.class_methods) - merge(@instance_methods, old.instance_methods) - merge(@attributes, old.attributes) - merge(@constants, old.constants) - merge(@includes, old.includes) - if @comment.nil? || @comment.empty? - @comment = old.comment - else - unless old.comment.nil? or old.comment.empty? then - if @comment.nil? or @comment.empty? then - @comment = old.comment - else - @comment << RDoc::Markup::Flow::RULE.new - @comment.concat old.comment - end - end - end - end - - def display_name - "Module" - end - - # the 'ClassDescription' subclass overrides this - # to format up the name of a parent - def superclass_string - nil - end - - private - - def merge(into, from) - names = {} - into.each {|i| names[i.name] = i } - from.each {|i| names[i.name] = i } - into.replace(names.keys.sort.map {|n| names[n]}) - end -end - -class RDoc::RI::ClassDescription < RDoc::RI::ModuleDescription - attr_accessor :superclass - - def display_name - "Class" - end - - def superclass_string - if @superclass && @superclass != "Object" - @superclass - else - nil - end - end -end - -class RDoc::RI::MethodDescription < RDoc::RI::Description - - attr_accessor :is_class_method - attr_accessor :visibility - attr_accessor :block_params - attr_accessor :is_singleton - attr_accessor :aliases - attr_accessor :is_alias_for - attr_accessor :params - -end - diff --git a/trunk/lib/rdoc/ri/display.rb b/trunk/lib/rdoc/ri/display.rb deleted file mode 100644 index 379cef11b3..0000000000 --- a/trunk/lib/rdoc/ri/display.rb +++ /dev/null @@ -1,274 +0,0 @@ -require 'rdoc/ri' - -## -# This is a kind of 'flag' module. If you want to write your own 'ri' display -# module (perhaps because you're writing an IDE), you write a class which -# implements the various 'display' methods in RDoc::RI::DefaultDisplay, and -# include the RDoc::RI::Display module in that class. -# -# To access your class from the command line, you can do -# -# ruby -r <your source file> ../ri .... - -module RDoc::RI::Display - - @@display_class = nil - - def self.append_features(display_class) - @@display_class = display_class - end - - def self.new(*args) - @@display_class.new(*args) - end - -end - -## -# A paging display module. Uses the RDoc::RI::Formatter class to do the actual -# presentation. - -class RDoc::RI::DefaultDisplay - - include RDoc::RI::Display - - def initialize(formatter, width, use_stdout, output = $stdout) - @use_stdout = use_stdout - @formatter = formatter.new output, width, " " - end - - ## - # Display information about +klass+. Fetches additional information from - # +ri_reader+ as necessary. - - def display_class_info(klass, ri_reader) - page do - superclass = klass.superclass_string - - if superclass - superclass = " < " + superclass - else - superclass = "" - end - - @formatter.draw_line(klass.display_name + ": " + - klass.full_name + superclass) - - display_flow(klass.comment) - @formatter.draw_line - - unless klass.includes.empty? - @formatter.blankline - @formatter.display_heading("Includes:", 2, "") - incs = [] - klass.includes.each do |inc| - inc_desc = ri_reader.find_class_by_name(inc.name) - if inc_desc - str = inc.name + "(" - str << inc_desc.instance_methods.map{|m| m.name}.join(", ") - str << ")" - incs << str - else - incs << inc.name - end - end - @formatter.wrap(incs.sort.join(', ')) - end - - unless klass.constants.empty? - @formatter.blankline - @formatter.display_heading("Constants:", 2, "") - - constants = klass.constants.sort_by { |constant| constant.name } - - constants.each do |constant| - if constant.comment then - @formatter.wrap "#{constant.name}:" - - @formatter.indent do - @formatter.display_flow constant.comment - end - else - @formatter.wrap constant.name - end - end - end - - class_data = [ - :class_methods, - :class_method_extensions, - :instance_methods, - :instance_method_extensions, - ] - - class_data.each do |data_type| - data = klass.send data_type - - unless data.empty? then - @formatter.blankline - - heading = data_type.to_s.split('_').join(' ').capitalize << ':' - @formatter.display_heading heading, 2, '' - - data = data.map { |item| item.name }.sort.join ', ' - @formatter.wrap data - end - end - - unless klass.attributes.empty? then - @formatter.blankline - - @formatter.display_heading 'Attributes:', 2, '' - - attributes = klass.attributes.sort_by { |attribute| attribute.name } - - attributes.each do |attribute| - if attribute.comment then - @formatter.wrap "#{attribute.name} (#{attribute.rw}):" - @formatter.indent do - @formatter.display_flow attribute.comment - end - else - @formatter.wrap "#{attribute.name} (#{attribute.rw})" - end - end - end - end - end - - ## - # Display an Array of RDoc::Markup::Flow objects, +flow+. - - def display_flow(flow) - if flow and not flow.empty? then - @formatter.display_flow flow - else - @formatter.wrap '[no description]' - end - end - - ## - # Display information about +method+. - - def display_method_info(method) - page do - @formatter.draw_line(method.full_name) - display_params(method) - - @formatter.draw_line - display_flow(method.comment) - - if method.aliases and not method.aliases.empty? then - @formatter.blankline - aka = "(also known as #{method.aliases.map { |a| a.name }.join(', ')})" - @formatter.wrap aka - end - end - end - - ## - # Display the list of +methods+. - - def display_method_list(methods) - page do - @formatter.wrap "More than one method matched your request. You can refine your search by asking for information on one of:" - - @formatter.blankline - - @formatter.wrap methods.map { |m| m.full_name }.join(", ") - end - end - - ## - # Display the params for +method+. - - def display_params(method) - params = method.params - - if params[0,1] == "(" then - if method.is_singleton - params = method.full_name + params - else - params = method.name + params - end - end - - params.split(/\n/).each do |param| - @formatter.wrap param - @formatter.break_to_newline - end - - if method.source_path then - @formatter.blankline - @formatter.wrap("Extension from #{method.source_path}") - end - end - - ## - # List the classes in +classes+. - - def list_known_classes(classes) - if classes.empty? - warn_no_database - else - page do - @formatter.draw_line "Known classes and modules" - @formatter.blankline - - @formatter.wrap classes.sort.join(', ') - end - end - end - - ## - # Paginates output through a pager program. - - def page - if pager = setup_pager then - begin - orig_output = @formatter.output - @formatter.output = pager - yield - ensure - @formatter.output = orig_output - pager.close - end - else - yield - end - rescue Errno::EPIPE - end - - ## - # Sets up a pager program to pass output through. - - def setup_pager - unless @use_stdout then - for pager in [ ENV['PAGER'], "less", "more", 'pager' ].compact.uniq - return IO.popen(pager, "w") rescue nil - end - @use_stdout = true - nil - end - end - - ## - # Displays a message that describes how to build RI data. - - def warn_no_database - output = @formatter.output - - output.puts "No ri data found" - output.puts - output.puts "If you've installed Ruby yourself, you need to generate documentation using:" - output.puts - output.puts " make install-doc" - output.puts - output.puts "from the same place you ran `make` to build ruby." - output.puts - output.puts "If you installed Ruby from a packaging system, then you may need to" - output.puts "install an additional package, or ask the packager to enable ri generation." - end - -end - diff --git a/trunk/lib/rdoc/ri/driver.rb b/trunk/lib/rdoc/ri/driver.rb deleted file mode 100644 index dfc5f2f98a..0000000000 --- a/trunk/lib/rdoc/ri/driver.rb +++ /dev/null @@ -1,551 +0,0 @@ -require 'optparse' -require 'yaml' - -require 'rdoc/ri' -require 'rdoc/ri/paths' -require 'rdoc/ri/formatter' -require 'rdoc/ri/display' -require 'fileutils' -require 'rdoc/markup' -require 'rdoc/markup/to_flow' - -class RDoc::RI::Driver - - class Hash < ::Hash - def self.convert(hash) - hash = new.update hash - - hash.each do |key, value| - hash[key] = case value - when ::Hash then - convert value - when Array then - value = value.map do |v| - ::Hash === v ? convert(v) : v - end - value - else - value - end - end - - hash - end - - def method_missing method, *args - self[method.to_s] - end - - def merge_enums(other) - other.each do |k, v| - if self[k] then - case v - when Array then - # HACK dunno - if String === self[k] and self[k].empty? then - self[k] = v - else - self[k] += v - end - when Hash then - self[k].update v - else - # do nothing - end - else - self[k] = v - end - end - end - end - - class Error < RDoc::RI::Error; end - - class NotFoundError < Error - def message - "Nothing known about #{super}" - end - end - - attr_accessor :homepath # :nodoc: - - def self.process_args(argv) - options = {} - options[:use_stdout] = !$stdout.tty? - options[:width] = 72 - options[:formatter] = RDoc::RI::Formatter.for 'plain' - options[:list_classes] = false - options[:list_names] = false - - # By default all paths are used. If any of these are true, only those - # directories are used. - use_system = false - use_site = false - use_home = false - use_gems = false - doc_dirs = [] - - opts = OptionParser.new do |opt| - opt.program_name = File.basename $0 - opt.version = RDoc::VERSION - opt.summary_indent = ' ' * 4 - - directories = [ - RDoc::RI::Paths::SYSDIR, - RDoc::RI::Paths::SITEDIR, - RDoc::RI::Paths::HOMEDIR - ] - - if RDoc::RI::Paths::GEMDIRS then - Gem.path.each do |dir| - directories << "#{dir}/doc/*/ri" - end - end - - opt.banner = <<-EOT -Usage: #{opt.program_name} [options] [names...] - -Where name can be: - - Class | Class::method | Class#method | Class.method | method - -All class names may be abbreviated to their minimum unambiguous form. If a name -is ambiguous, all valid options will be listed. - -The form '.' method matches either class or instance methods, while #method -matches only instance and ::method matches only class methods. - -For example: - - #{opt.program_name} Fil - #{opt.program_name} File - #{opt.program_name} File.new - #{opt.program_name} zip - -Note that shell quoting may be required for method names containing -punctuation: - - #{opt.program_name} 'Array.[]' - #{opt.program_name} compact\\! - -By default ri searches for documentation in the following directories: - - #{directories.join "\n "} - -Specifying the --system, --site, --home, --gems or --doc-dir options will -limit ri to searching only the specified directories. - -Options may also be set in the 'RI' environment variable. - EOT - - opt.separator nil - opt.separator "Options:" - opt.separator nil - - opt.on("--classes", "-c", - "Display the names of classes and modules we", - "know about.") do |value| - options[:list_classes] = value - end - - opt.separator nil - - opt.on("--doc-dir=DIRNAME", "-d", Array, - "List of directories to search for", - "documentation. If not specified, we search", - "the standard rdoc/ri directories. May be", - "repeated.") do |value| - value.each do |dir| - unless File.directory? dir then - raise OptionParser::InvalidArgument, "#{dir} is not a directory" - end - end - - doc_dirs.concat value - end - - opt.separator nil - - opt.on("--fmt=FORMAT", "--format=FORMAT", "-f", - RDoc::RI::Formatter::FORMATTERS.keys, - "Format to use when displaying output:", - " #{RDoc::RI::Formatter.list}", - "Use 'bs' (backspace) with most pager", - "programs. To use ANSI, either disable the", - "pager or tell the pager to allow control", - "characters.") do |value| - options[:formatter] = RDoc::RI::Formatter.for value - end - - opt.separator nil - - unless RDoc::RI::Paths::GEMDIRS.empty? then - opt.on("--[no-]gems", - "Include documentation from RubyGems.") do |value| - use_gems = value - end - end - - opt.separator nil - - opt.on("--[no-]home", - "Include documentation stored in ~/.rdoc.") do |value| - use_home = value - end - - opt.separator nil - - opt.on("--[no-]list-names", "-l", - "List all the names known to RDoc, one per", - "line.") do |value| - options[:list_names] = value - end - - opt.separator nil - - opt.on("--no-pager", "-T", - "Send output directly to stdout.") do |value| - options[:use_stdout] = !value - end - - opt.separator nil - - opt.on("--[no-]site", - "Include documentation from libraries", - "installed in site_lib.") do |value| - use_site = value - end - - opt.separator nil - - opt.on("--[no-]system", - "Include documentation from Ruby's standard", - "library.") do |value| - use_system = value - end - - opt.separator nil - - opt.on("--width=WIDTH", "-w", OptionParser::DecimalInteger, - "Set the width of the output.") do |value| - options[:width] = value - end - end - - argv = ENV['RI'].to_s.split.concat argv - - opts.parse! argv - - options[:names] = argv - - options[:path] = RDoc::RI::Paths.path(use_system, use_site, use_home, - use_gems, *doc_dirs) - options[:raw_path] = RDoc::RI::Paths.raw_path(use_system, use_site, - use_home, use_gems, *doc_dirs) - - options - - rescue OptionParser::InvalidArgument, OptionParser::InvalidOption => e - puts opts - puts - puts e - exit 1 - end - - def self.run(argv = ARGV) - options = process_args argv - ri = new options - ri.run - end - - def initialize(options={}) - options[:formatter] ||= RDoc::RI::Formatter.for('plain') - options[:use_stdout] ||= !$stdout.tty? - options[:width] ||= 72 - @names = options[:names] - - @class_cache_name = 'classes' - @all_dirs = RDoc::RI::Paths.path(true, true, true, true) - @homepath = RDoc::RI::Paths.raw_path(false, false, true, false).first - @homepath = @homepath.sub(/\.rdoc/, '.ri') - @sys_dirs = RDoc::RI::Paths.raw_path(true, false, false, false) - - FileUtils.mkdir_p cache_file_path unless File.directory? cache_file_path - - @class_cache = nil - - @display = RDoc::RI::DefaultDisplay.new(options[:formatter], - options[:width], - options[:use_stdout]) - end - - def class_cache - return @class_cache if @class_cache - - newest = map_dirs('created.rid', :all) do |f| - File.mtime f if test ?f, f - end.max - - up_to_date = (File.exist?(class_cache_file_path) and - newest and newest < File.mtime(class_cache_file_path)) - - @class_cache = if up_to_date then - load_cache_for @class_cache_name - else - class_cache = RDoc::RI::Driver::Hash.new - - classes = map_dirs('**/cdesc*.yaml', :sys) { |f| Dir[f] } - populate_class_cache class_cache, classes - - classes = map_dirs('**/cdesc*.yaml') { |f| Dir[f] } - warn "Updating class cache with #{classes.size} classes..." - - populate_class_cache class_cache, classes, true - write_cache class_cache, class_cache_file_path - end - - @class_cache = RDoc::RI::Driver::Hash.convert @class_cache - @class_cache - end - - def class_cache_file_path - File.join cache_file_path, @class_cache_name - end - - def cache_file_for(klassname) - File.join cache_file_path, klassname.gsub(/:+/, "-") - end - - def cache_file_path - File.join @homepath, 'cache' - end - - def display_class(name) - klass = class_cache[name] - klass = RDoc::RI::Driver::Hash.convert klass - @display.display_class_info klass, class_cache - end - - def get_info_for(arg) - @names = [arg] - run - end - - def load_cache_for(klassname) - path = cache_file_for klassname - - cache = nil - - if File.exist? path and - File.mtime(path) >= File.mtime(class_cache_file_path) then - open path, 'rb' do |fp| - cache = Marshal.load fp.read - end - else - class_cache = nil - - open class_cache_file_path, 'rb' do |fp| - class_cache = Marshal.load fp.read - end - - klass = class_cache[klassname] - return nil unless klass - - method_files = klass["sources"] - cache = RDoc::RI::Driver::Hash.new - - sys_dir = @sys_dirs.first - method_files.each do |f| - system_file = f.index(sys_dir) == 0 - Dir[File.join(File.dirname(f), "*")].each do |yaml| - next unless yaml =~ /yaml$/ - next if yaml =~ /cdesc-[^\/]+yaml$/ - method = read_yaml yaml - name = method["full_name"] - ext_path = f - ext_path = "gem #{$1}" if f =~ %r%gems/[\d.]+/doc/([^/]+)% - method["source_path"] = ext_path unless system_file - cache[name] = RDoc::RI::Driver::Hash.convert method - end - end - - write_cache cache, path - end - - RDoc::RI::Driver::Hash.convert cache - end - - ## - # Finds the next ancestor of +orig_klass+ after +klass+. - - def lookup_ancestor(klass, orig_klass) - cache = class_cache[orig_klass] - - return nil unless cache - - ancestors = [orig_klass] - ancestors.push(*cache.includes.map { |inc| inc['name'] }) - ancestors << cache.superclass - - ancestor = ancestors[ancestors.index(klass) + 1] - - return ancestor if ancestor - - lookup_ancestor klass, cache.superclass - end - - ## - # Finds the method - - def lookup_method(name, klass) - cache = load_cache_for klass - return nil unless cache - - method = cache[name.gsub('.', '#')] - method = cache[name.gsub('.', '::')] unless method - method - end - - def map_dirs(file_name, system=false) - dirs = if system == :all then - @all_dirs - else - if system then - @sys_dirs - else - @all_dirs - @sys_dirs - end - end - - dirs.map { |dir| yield File.join(dir, file_name) }.flatten.compact - end - - ## - # Extract the class and method name parts from +name+ like Foo::Bar#baz - - def parse_name(name) - parts = name.split(/(::|\#|\.)/) - - if parts[-2] != '::' or parts.last !~ /^[A-Z]/ then - meth = parts.pop - parts.pop - end - - klass = parts.join - - [klass, meth] - end - - def populate_class_cache(class_cache, classes, extension = false) - classes.each do |cdesc| - desc = read_yaml cdesc - klassname = desc["full_name"] - - unless class_cache.has_key? klassname then - desc["display_name"] = "Class" - desc["sources"] = [cdesc] - desc["instance_method_extensions"] = [] - desc["class_method_extensions"] = [] - class_cache[klassname] = desc - else - klass = class_cache[klassname] - - if extension then - desc["instance_method_extensions"] = desc.delete "instance_methods" - desc["class_method_extensions"] = desc.delete "class_methods" - end - - klass = RDoc::RI::Driver::Hash.convert klass - - klass.merge_enums desc - klass["sources"] << cdesc - end - end - end - - def read_yaml(path) - data = File.read path - data = data.gsub(/ \!ruby\/(object|struct):(RDoc::RI|RI).*/, '') - data = data.gsub(/ \!ruby\/(object|struct):SM::(\S+)/, - ' !ruby/\1:RDoc::Markup::\2') - YAML.load data - end - - def run - if @names.empty? then - @display.list_known_classes class_cache.keys.sort - else - @names.each do |name| - case name - when /::|\#|\./ then - if class_cache.key? name then - display_class name - else - klass, = parse_name name - - orig_klass = klass - orig_name = name - - until klass == 'Kernel' do - method = lookup_method name, klass - - break method if method - - ancestor = lookup_ancestor klass, orig_klass - - break unless ancestor - - name = name.sub klass, ancestor - klass = ancestor - end - - raise NotFoundError, orig_name unless method - - @display.display_method_info method - end - else - if class_cache.key? name then - display_class name - else - methods = select_methods(/^#{name}/) - - if methods.size == 0 - raise NotFoundError, name - elsif methods.size == 1 - @display.display_method_info methods.first - else - @display.display_method_list methods - end - end - end - end - end - rescue NotFoundError => e - abort e.message - end - - def select_methods(pattern) - methods = [] - class_cache.keys.sort.each do |klass| - class_cache[klass]["instance_methods"].map{|h|h["name"]}.grep(pattern) do |name| - method = load_cache_for(klass)[klass+'#'+name] - methods << method if method - end - class_cache[klass]["class_methods"].map{|h|h["name"]}.grep(pattern) do |name| - method = load_cache_for(klass)[klass+'::'+name] - methods << method if method - end - end - methods - end - - def write_cache(cache, path) - File.open path, "wb" do |cache_file| - Marshal.dump cache, cache_file - end - - cache - end - -end - diff --git a/trunk/lib/rdoc/ri/formatter.rb b/trunk/lib/rdoc/ri/formatter.rb deleted file mode 100644 index 0a0c3f7380..0000000000 --- a/trunk/lib/rdoc/ri/formatter.rb +++ /dev/null @@ -1,616 +0,0 @@ -require 'rdoc/ri' -require 'rdoc/markup' - -class RDoc::RI::Formatter - - attr_writer :indent - attr_accessor :output - - FORMATTERS = { } - - def self.for(name) - FORMATTERS[name.downcase] - end - - def self.list - FORMATTERS.keys.sort.join ", " - end - - def initialize(output, width, indent) - @output = output - @width = width - @indent = indent - @original_indent = indent.dup - end - - def draw_line(label=nil) - len = @width - len -= (label.size + 1) if label - - if len > 0 then - @output.print '-' * len - if label - @output.print ' ' - bold_print label - end - - @output.puts - else - @output.print '-' * @width - @output.puts - - @output.puts label - end - end - - def indent - return @indent unless block_given? - - begin - indent = @indent.dup - @indent += @original_indent - yield - ensure - @indent = indent - end - end - - def wrap(txt, prefix=@indent, linelen=@width) - return unless txt && !txt.empty? - - work = conv_markup(txt) - textLen = linelen - prefix.length - patt = Regexp.new("^(.{0,#{textLen}})[ \n]") - next_prefix = prefix.tr("^ ", " ") - - res = [] - - while work.length > textLen - if work =~ patt - res << $1 - work.slice!(0, $&.length) - else - res << work.slice!(0, textLen) - end - end - res << work if work.length.nonzero? - @output.puts(prefix + res.join("\n" + next_prefix)) - end - - def blankline - @output.puts - end - - ## - # Called when we want to ensure a new 'wrap' starts on a newline. Only - # needed for HtmlFormatter, because the rest do their own line breaking. - - def break_to_newline - end - - def bold_print(txt) - @output.print txt - end - - def raw_print_line(txt) - @output.puts txt - end - - ## - # Convert HTML entities back to ASCII - - def conv_html(txt) - txt = txt.gsub(/>/, '>') - txt.gsub!(/</, '<') - txt.gsub!(/"/, '"') - txt.gsub!(/&/, '&') - txt - end - - ## - # Convert markup into display form - - def conv_markup(txt) - txt = txt.gsub(%r{<tt>(.*?)</tt>}, '+\1+') - txt.gsub!(%r{<code>(.*?)</code>}, '+\1+') - txt.gsub!(%r{<b>(.*?)</b>}, '*\1*') - txt.gsub!(%r{<em>(.*?)</em>}, '_\1_') - txt - end - - def display_list(list) - case list.type - when :BULLET - prefixer = proc { |ignored| @indent + "* " } - - when :NUMBER, :UPPERALPHA, :LOWERALPHA then - start = case list.type - when :NUMBER then 1 - when :UPPERALPHA then 'A' - when :LOWERALPHA then 'a' - end - - prefixer = proc do |ignored| - res = @indent + "#{start}.".ljust(4) - start = start.succ - res - end - - when :LABELED, :NOTE then - longest = 0 - - list.contents.each do |item| - if RDoc::Markup::Flow::LI === item and item.label.length > longest then - longest = item.label.length - end - end - - longest += 1 - - prefixer = proc { |li| @indent + li.label.ljust(longest) } - - else - raise ArgumentError, "unknown list type #{list.type}" - end - - list.contents.each do |item| - if RDoc::Markup::Flow::LI === item then - prefix = prefixer.call item - display_flow_item item, prefix - else - display_flow_item item - end - end - end - - def display_flow_item(item, prefix = @indent) - case item - when RDoc::Markup::Flow::P, RDoc::Markup::Flow::LI - wrap(conv_html(item.body), prefix) - blankline - - when RDoc::Markup::Flow::LIST - display_list(item) - - when RDoc::Markup::Flow::VERB - display_verbatim_flow_item(item, @indent) - - when RDoc::Markup::Flow::H - display_heading(conv_html(item.text), item.level, @indent) - - when RDoc::Markup::Flow::RULE - draw_line - - else - raise RDoc::Error, "Unknown flow element: #{item.class}" - end - end - - def display_verbatim_flow_item(item, prefix=@indent) - item.body.split(/\n/).each do |line| - @output.print @indent, conv_html(line), "\n" - end - blankline - end - - def display_heading(text, level, indent) - text = strip_attributes text - - case level - when 1 then - ul = "=" * text.length - @output.puts - @output.puts text.upcase - @output.puts ul - - when 2 then - ul = "-" * text.length - @output.puts - @output.puts text - @output.puts ul - else - @output.print indent, text, "\n" - end - - @output.puts - end - - def display_flow(flow) - flow.each do |f| - display_flow_item(f) - end - end - - def strip_attributes(text) - text.gsub(/(<\/?(?:b|code|em|i|tt)>)/, '') - end - -end - -## -# Handle text with attributes. We're a base class: there are different -# presentation classes (one, for example, uses overstrikes to handle bold and -# underlining, while another using ANSI escape sequences. - -class RDoc::RI::AttributeFormatter < RDoc::RI::Formatter - - BOLD = 1 - ITALIC = 2 - CODE = 4 - - ATTR_MAP = { - "b" => BOLD, - "code" => CODE, - "em" => ITALIC, - "i" => ITALIC, - "tt" => CODE - } - - AttrChar = Struct.new :char, :attr - - class AttributeString - attr_reader :txt - - def initialize - @txt = [] - @optr = 0 - end - - def <<(char) - @txt << char - end - - def empty? - @optr >= @txt.length - end - - # accept non space, then all following spaces - def next_word - start = @optr - len = @txt.length - - while @optr < len && @txt[@optr].char != " " - @optr += 1 - end - - while @optr < len && @txt[@optr].char == " " - @optr += 1 - end - - @txt[start...@optr] - end - end - - ## - # Overrides base class. Looks for <tt>...</tt> etc sequences and generates - # an array of AttrChars. This array is then used as the basis for the - # split. - - def wrap(txt, prefix=@indent, linelen=@width) - return unless txt && !txt.empty? - - txt = add_attributes_to(txt) - next_prefix = prefix.tr("^ ", " ") - linelen -= prefix.size - - line = [] - - until txt.empty? - word = txt.next_word - if word.size + line.size > linelen - write_attribute_text(prefix, line) - prefix = next_prefix - line = [] - end - line.concat(word) - end - - write_attribute_text(prefix, line) if line.length > 0 - end - - protected - - def write_attribute_text(prefix, line) - @output.print prefix - line.each do |achar| - @output.print achar.char - end - @output.puts - end - - def bold_print(txt) - @output.print txt - end - - private - - def add_attributes_to(txt) - tokens = txt.split(%r{(</?(?:b|code|em|i|tt)>)}) - text = AttributeString.new - attributes = 0 - tokens.each do |tok| - case tok - when %r{^</(\w+)>$} then attributes &= ~(ATTR_MAP[$1]||0) - when %r{^<(\w+)>$} then attributes |= (ATTR_MAP[$1]||0) - else - tok.split(//).each {|ch| text << AttrChar.new(ch, attributes)} - end - end - text - end - -end - -## -# This formatter generates overstrike-style formatting, which works with -# pagers such as man and less. - -class RDoc::RI::OverstrikeFormatter < RDoc::RI::AttributeFormatter - - BS = "\C-h" - - def write_attribute_text(prefix, line) - @output.print prefix - - line.each do |achar| - attr = achar.attr - @output.print "_", BS if (attr & (ITALIC + CODE)) != 0 - @output.print achar.char, BS if (attr & BOLD) != 0 - @output.print achar.char - end - - @output.puts - end - - ## - # Draw a string in bold - - def bold_print(text) - text.split(//).each do |ch| - @output.print ch, BS, ch - end - end - -end - -## -# This formatter uses ANSI escape sequences to colorize stuff works with -# pagers such as man and less. - -class RDoc::RI::AnsiFormatter < RDoc::RI::AttributeFormatter - - def initialize(*args) - super - @output.print "\033[0m" - end - - def write_attribute_text(prefix, line) - @output.print prefix - curr_attr = 0 - line.each do |achar| - attr = achar.attr - if achar.attr != curr_attr - update_attributes(achar.attr) - curr_attr = achar.attr - end - @output.print achar.char - end - update_attributes(0) unless curr_attr.zero? - @output.puts - end - - def bold_print(txt) - @output.print "\033[1m#{txt}\033[m" - end - - HEADINGS = { - 1 => ["\033[1;32m", "\033[m"], - 2 => ["\033[4;32m", "\033[m"], - 3 => ["\033[32m", "\033[m"], - } - - def display_heading(text, level, indent) - level = 3 if level > 3 - heading = HEADINGS[level] - @output.print indent - @output.print heading[0] - @output.print strip_attributes(text) - @output.puts heading[1] - end - - private - - ATTR_MAP = { - BOLD => "1", - ITALIC => "33", - CODE => "36" - } - - def update_attributes(attr) - str = "\033[" - for quality in [ BOLD, ITALIC, CODE] - unless (attr & quality).zero? - str << ATTR_MAP[quality] - end - end - @output.print str, "m" - end - -end - -## -# This formatter uses HTML. - -class RDoc::RI::HtmlFormatter < RDoc::RI::AttributeFormatter - - def write_attribute_text(prefix, line) - curr_attr = 0 - line.each do |achar| - attr = achar.attr - if achar.attr != curr_attr - update_attributes(curr_attr, achar.attr) - curr_attr = achar.attr - end - @output.print(escape(achar.char)) - end - update_attributes(curr_attr, 0) unless curr_attr.zero? - end - - def draw_line(label=nil) - if label != nil - bold_print(label) - end - @output.puts("<hr>") - end - - def bold_print(txt) - tag("b") { txt } - end - - def blankline() - @output.puts("<p>") - end - - def break_to_newline - @output.puts("<br>") - end - - def display_heading(text, level, indent) - level = 4 if level > 4 - tag("h#{level}") { text } - @output.puts - end - - def display_list(list) - case list.type - when :BULLET then - list_type = "ul" - prefixer = proc { |ignored| "<li>" } - - when :NUMBER, :UPPERALPHA, :LOWERALPHA then - list_type = "ol" - prefixer = proc { |ignored| "<li>" } - - when :LABELED then - list_type = "dl" - prefixer = proc do |li| - "<dt><b>" + escape(li.label) + "</b><dd>" - end - - when :NOTE then - list_type = "table" - prefixer = proc do |li| - %{<tr valign="top"><td>#{li.label.gsub(/ /, ' ')}</td><td>} - end - else - fail "unknown list type" - end - - @output.print "<#{list_type}>" - list.contents.each do |item| - if item.kind_of? RDoc::Markup::Flow::LI - prefix = prefixer.call(item) - @output.print prefix - display_flow_item(item, prefix) - else - display_flow_item(item) - end - end - @output.print "</#{list_type}>" - end - - def display_verbatim_flow_item(item, prefix=@indent) - @output.print("<pre>") - item.body.split(/\n/).each do |line| - @output.puts conv_html(line) - end - @output.puts("</pre>") - end - - private - - ATTR_MAP = { - BOLD => "b>", - ITALIC => "i>", - CODE => "tt>" - } - - def update_attributes(current, wanted) - str = "" - # first turn off unwanted ones - off = current & ~wanted - for quality in [ BOLD, ITALIC, CODE] - if (off & quality) > 0 - str << "</" + ATTR_MAP[quality] - end - end - - # now turn on wanted - for quality in [ BOLD, ITALIC, CODE] - unless (wanted & quality).zero? - str << "<" << ATTR_MAP[quality] - end - end - @output.print str - end - - def tag(code) - @output.print("<#{code}>") - @output.print(yield) - @output.print("</#{code}>") - end - - def escape(str) - str = str.gsub(/&/n, '&') - str.gsub!(/\"/n, '"') - str.gsub!(/>/n, '>') - str.gsub!(/</n, '<') - str - end - -end - -## -# This formatter reduces extra lines for a simpler output. It improves way -# output looks for tools like IRC bots. - -class RDoc::RI::SimpleFormatter < RDoc::RI::Formatter - - ## - # No extra blank lines - - def blankline - end - - ## - # Display labels only, no lines - - def draw_line(label=nil) - unless label.nil? then - bold_print(label) - @output.puts - end - end - - ## - # Place heading level indicators inline with heading. - - def display_heading(text, level, indent) - text = strip_attributes(text) - case level - when 1 - @output.puts "= " + text.upcase - when 2 - @output.puts "-- " + text - else - @output.print indent, text, "\n" - end - end - -end - -RDoc::RI::Formatter::FORMATTERS['plain'] = RDoc::RI::Formatter -RDoc::RI::Formatter::FORMATTERS['simple'] = RDoc::RI::SimpleFormatter -RDoc::RI::Formatter::FORMATTERS['bs'] = RDoc::RI::OverstrikeFormatter -RDoc::RI::Formatter::FORMATTERS['ansi'] = RDoc::RI::AnsiFormatter -RDoc::RI::Formatter::FORMATTERS['html'] = RDoc::RI::HtmlFormatter diff --git a/trunk/lib/rdoc/ri/paths.rb b/trunk/lib/rdoc/ri/paths.rb deleted file mode 100644 index b4b6c64925..0000000000 --- a/trunk/lib/rdoc/ri/paths.rb +++ /dev/null @@ -1,102 +0,0 @@ -require 'rdoc/ri' - -## -# Encapsulate all the strangeness to do with finding out where to find RDoc -# files -# -# We basically deal with three directories: -# -# 1. The 'system' documentation directory, which holds the documentation -# distributed with Ruby, and which is managed by the Ruby install process -# 2. The 'site' directory, which contains site-wide documentation added -# locally. -# 3. The 'user' documentation directory, stored under the user's own home -# directory. -# -# There's contention about all this, but for now: -# -# system:: $datadir/ri/<ver>/system/... -# site:: $datadir/ri/<ver>/site/... -# user:: ~/.rdoc - -module RDoc::RI::Paths - - #:stopdoc: - require 'rbconfig' - - DOC_DIR = "doc/rdoc" - - version = RbConfig::CONFIG['ruby_version'] - - base = File.join(RbConfig::CONFIG['datadir'], "ri", version) - SYSDIR = File.join(base, "system") - SITEDIR = File.join(base, "site") - homedir = ENV['HOME'] || ENV['USERPROFILE'] || ENV['HOMEPATH'] - - if homedir then - HOMEDIR = File.join(homedir, ".rdoc") - else - HOMEDIR = nil - end - - # This is the search path for 'ri' - PATH = [ SYSDIR, SITEDIR, HOMEDIR ].find_all {|p| p && File.directory?(p)} - - begin - require 'rubygems' unless defined?(Gem) and defined?(Gem::Enable) and - Gem::Enable - - # HACK dup'd from Gem.latest_partials and friends - all_paths = [] - - all_paths = Gem.path.map do |dir| - Dir[File.join(dir, 'doc', '*', 'ri')] - end.flatten - - ri_paths = {} - - all_paths.each do |dir| - base = File.basename File.dirname(dir) - if base =~ /(.*)-((\d+\.)*\d+)/ then - name, version = $1, $2 - ver = Gem::Version.new version - if ri_paths[name].nil? or ver > ri_paths[name][0] then - ri_paths[name] = [ver, dir] - end - end - end - - GEMDIRS = ri_paths.map { |k,v| v.last }.sort - GEMDIRS.each { |dir| PATH << dir } - rescue LoadError - GEMDIRS = [] - end - - # Returns the selected documentation directories as an Array, or PATH if no - # overriding directories were given. - - def self.path(use_system, use_site, use_home, use_gems, *extra_dirs) - path = raw_path(use_system, use_site, use_home, use_gems, *extra_dirs) - return path.select { |directory| File.directory? directory } - end - - # Returns the selected documentation directories including nonexistent - # directories. Used to print out what paths were searched if no ri was - # found. - - def self.raw_path(use_system, use_site, use_home, use_gems, *extra_dirs) - return PATH unless use_system or use_site or use_home or use_gems or - not extra_dirs.empty? - - path = [] - path << extra_dirs unless extra_dirs.empty? - path << SYSDIR if use_system - path << SITEDIR if use_site - path << HOMEDIR if use_home - path << GEMDIRS if use_gems - - return path.flatten.compact - end - -end - diff --git a/trunk/lib/rdoc/ri/reader.rb b/trunk/lib/rdoc/ri/reader.rb deleted file mode 100644 index 986bb75954..0000000000 --- a/trunk/lib/rdoc/ri/reader.rb +++ /dev/null @@ -1,106 +0,0 @@ -require 'rdoc/ri' -require 'rdoc/ri/descriptions' -require 'rdoc/ri/writer' -require 'rdoc/markup/to_flow' - -class RDoc::RI::Reader - - def initialize(ri_cache) - @cache = ri_cache - end - - def top_level_namespace - [ @cache.toplevel ] - end - - def lookup_namespace_in(target, namespaces) - result = [] - for n in namespaces - result.concat(n.contained_modules_matching(target)) - end - result - end - - def find_class_by_name(full_name) - names = full_name.split(/::/) - ns = @cache.toplevel - for name in names - ns = ns.contained_class_named(name) - return nil if ns.nil? - end - get_class(ns) - end - - def find_methods(name, is_class_method, namespaces) - result = [] - namespaces.each do |ns| - result.concat ns.methods_matching(name, is_class_method) - end - result - end - - ## - # Return the MethodDescription for a given MethodEntry by deserializing the - # YAML - - def get_method(method_entry) - path = method_entry.path_name - File.open(path) { |f| RI::Description.deserialize(f) } - end - - ## - # Return a class description - - def get_class(class_entry) - result = nil - for path in class_entry.path_names - path = RiWriter.class_desc_path(path, class_entry) - desc = File.open(path) {|f| RI::Description.deserialize(f) } - if result - result.merge_in(desc) - else - result = desc - end - end - result - end - - ## - # Return the names of all classes and modules - - def full_class_names - res = [] - find_classes_in(res, @cache.toplevel) - end - - ## - # Return a list of all classes, modules, and methods - - def all_names - res = [] - find_names_in(res, @cache.toplevel) - end - - private - - def find_classes_in(res, klass) - classes = klass.classes_and_modules - for c in classes - res << c.full_name - find_classes_in(res, c) - end - res - end - - def find_names_in(res, klass) - classes = klass.classes_and_modules - for c in classes - res << c.full_name - res.concat c.all_method_names - find_names_in(res, c) - end - res - end - -end - diff --git a/trunk/lib/rdoc/ri/util.rb b/trunk/lib/rdoc/ri/util.rb deleted file mode 100644 index 34277f2594..0000000000 --- a/trunk/lib/rdoc/ri/util.rb +++ /dev/null @@ -1,81 +0,0 @@ -require 'rdoc/ri' - -class RDoc::RI::Error < RuntimeError; end - -## -# Break argument into its constituent class or module names, an -# optional method type, and a method name - -class RDoc::RI::NameDescriptor - - attr_reader :class_names - attr_reader :method_name - - ## - # true and false have the obvious meaning. nil means we don't care - - attr_reader :is_class_method - - ## - # +arg+ may be - # - # 1. A class or module name (optionally qualified with other class or module - # names (Kernel, File::Stat etc) - # 2. A method name - # 3. A method name qualified by a optionally fully qualified class or module - # name - # - # We're fairly casual about delimiters: folks can say Kernel::puts, - # Kernel.puts, or Kernel\#puts for example. There's one exception: if you - # say IO::read, we look for a class method, but if you say IO.read, we look - # for an instance method - - def initialize(arg) - @class_names = [] - separator = nil - - tokens = arg.split(/(\.|::|#)/) - - # Skip leading '::', '#' or '.', but remember it might - # be a method name qualifier - separator = tokens.shift if tokens[0] =~ /^(\.|::|#)/ - - # Skip leading '::', but remember we potentially have an inst - - # leading stuff must be class names - - while tokens[0] =~ /^[A-Z]/ - @class_names << tokens.shift - unless tokens.empty? - separator = tokens.shift - break unless separator == "::" - end - end - - # Now must have a single token, the method name, or an empty array - unless tokens.empty? - @method_name = tokens.shift - # We may now have a trailing !, ?, or = to roll into - # the method name - if !tokens.empty? && tokens[0] =~ /^[!?=]$/ - @method_name << tokens.shift - end - - if @method_name =~ /::|\.|#/ or !tokens.empty? - raise RDoc::RI::Error.new("Bad argument: #{arg}") - end - if separator && separator != '.' - @is_class_method = separator == "::" - end - end - end - - # Return the full class name (with '::' between the components) or "" if - # there's no class name - - def full_class_name - @class_names.join("::") - end - -end - diff --git a/trunk/lib/rdoc/ri/writer.rb b/trunk/lib/rdoc/ri/writer.rb deleted file mode 100644 index 92aaa1c2da..0000000000 --- a/trunk/lib/rdoc/ri/writer.rb +++ /dev/null @@ -1,68 +0,0 @@ -require 'fileutils' -require 'rdoc/ri' - -class RDoc::RI::Writer - - def self.class_desc_path(dir, class_desc) - File.join(dir, "cdesc-" + class_desc.name + ".yaml") - end - - ## - # Convert a name from internal form (containing punctuation) to an external - # form (where punctuation is replaced by %xx) - - def self.internal_to_external(name) - if ''.respond_to? :ord then - name.gsub(/\W/) { "%%%02x" % $&[0].ord } - else - name.gsub(/\W/) { "%%%02x" % $&[0] } - end - end - - ## - # And the reverse operation - - def self.external_to_internal(name) - name.gsub(/%([0-9a-f]{2,2})/) { $1.to_i(16).chr } - end - - def initialize(base_dir) - @base_dir = base_dir - end - - def remove_class(class_desc) - FileUtils.rm_rf(path_to_dir(class_desc.full_name)) - end - - def add_class(class_desc) - dir = path_to_dir(class_desc.full_name) - FileUtils.mkdir_p(dir) - class_file_name = self.class.class_desc_path(dir, class_desc) - File.open(class_file_name, "w") do |f| - f.write(class_desc.serialize) - end - end - - def add_method(class_desc, method_desc) - dir = path_to_dir(class_desc.full_name) - file_name = self.class.internal_to_external(method_desc.name) - meth_file_name = File.join(dir, file_name) - if method_desc.is_singleton - meth_file_name += "-c.yaml" - else - meth_file_name += "-i.yaml" - end - - File.open(meth_file_name, "w") do |f| - f.write(method_desc.serialize) - end - end - - private - - def path_to_dir(class_name) - File.join(@base_dir, *class_name.split('::')) - end - -end - diff --git a/trunk/lib/rdoc/stats.rb b/trunk/lib/rdoc/stats.rb deleted file mode 100644 index e18e3c23d7..0000000000 --- a/trunk/lib/rdoc/stats.rb +++ /dev/null @@ -1,115 +0,0 @@ -require 'rdoc' - -## -# Simple stats collector - -class RDoc::Stats - - attr_reader :num_classes - attr_reader :num_files - attr_reader :num_methods - attr_reader :num_modules - - def initialize(verbosity = 1) - @num_classes = 0 - @num_files = 0 - @num_methods = 0 - @num_modules = 0 - - @start = Time.now - - @display = case verbosity - when 0 then Quiet.new - when 1 then Normal.new - else Verbose.new - end - end - - def add_alias(as) - @display.print_alias as - @num_methods += 1 - end - - def add_class(klass) - @display.print_class klass - @num_classes += 1 - end - - def add_file(file) - @display.print_file file - @num_files += 1 - end - - def add_method(method) - @display.print_method method - @num_methods += 1 - end - - def add_module(mod) - @display.print_module mod - @num_modules += 1 - end - - def print - puts "Files: #@num_files" - puts "Classes: #@num_classes" - puts "Modules: #@num_modules" - puts "Methods: #@num_methods" - puts "Elapsed: " + sprintf("%0.1fs", Time.now - @start) - end - - class Quiet - def print_alias(*) end - def print_class(*) end - def print_file(*) end - def print_method(*) end - def print_module(*) end - end - - class Normal - def print_alias(as) - print 'a' - end - - def print_class(klass) - print 'C' - end - - def print_file(file) - print "\n#{file}: " - end - - def print_method(method) - print 'm' - end - - def print_module(mod) - print 'M' - end - end - - class Verbose - def print_alias(as) - puts "\t\talias #{as.new_name} #{as.old_name}" - end - - def print_class(klass) - puts "\tclass #{klass.full_name}" - end - - def print_file(file) - puts file - end - - def print_method(method) - puts "\t\t#{method.singleton ? '::' : '#'}#{method.name}" - end - - def print_module(mod) - puts "\tmodule #{mod.full_name}" - end - end - -end - - diff --git a/trunk/lib/rdoc/template.rb b/trunk/lib/rdoc/template.rb deleted file mode 100644 index 53d0e3ce68..0000000000 --- a/trunk/lib/rdoc/template.rb +++ /dev/null @@ -1,64 +0,0 @@ -require 'erb' - -module RDoc; end - -## -# An ERb wrapper that allows nesting of one ERb template inside another. -# -# This TemplatePage operates similarly to RDoc 1.x's TemplatePage, but uses -# ERb instead of a custom template language. -# -# Converting from a RDoc 1.x template to an RDoc 2.x template is fairly easy. -# -# * %blah% becomes <%= values["blah"] %> -# * !INCLUDE! becomes <%= template_include %> -# * HREF:aref:name becomes <%= href values["aref"], values["name"] %> -# * IF:blah becomes <% if values["blah"] then %> -# * IFNOT:blah becomes <% unless values["blah"] then %> -# * ENDIF:blah becomes <% end %> -# * START:blah becomes <% values["blah"].each do |blah| %> -# * END:blah becomes <% end %> -# -# To make nested loops easier to convert, start by converting START statements -# to: -# -# <% values["blah"].each do |blah| $stderr.puts blah.keys %> -# -# So you can see what is being used inside which loop. - -class RDoc::TemplatePage - - ## - # Create a new TemplatePage that will use +templates+. - - def initialize(*templates) - @templates = templates - end - - ## - # Returns "<a href=\"#{ref}\">#{name}</a>" - - def href(ref, name) - if ref then - "<a href=\"#{ref}\">#{name}</a>" - else - name - end - end - - ## - # Process the template using +values+, writing the result to +io+. - - def write_html_on(io, values) - b = binding - template_include = "" - - @templates.reverse_each do |template| - template_include = ERB.new(template).result b - end - - io.write template_include - end - -end - diff --git a/trunk/lib/rdoc/tokenstream.rb b/trunk/lib/rdoc/tokenstream.rb deleted file mode 100644 index 0a1eb9130b..0000000000 --- a/trunk/lib/rdoc/tokenstream.rb +++ /dev/null @@ -1,33 +0,0 @@ -module RDoc; end - -## -# A TokenStream is a list of tokens, gathered during the parse of some entity -# (say a method). Entities populate these streams by being registered with the -# lexer. Any class can collect tokens by including TokenStream. From the -# outside, you use such an object by calling the start_collecting_tokens -# method, followed by calls to add_token and pop_token. - -module RDoc::TokenStream - - def token_stream - @token_stream - end - - def start_collecting_tokens - @token_stream = [] - end - - def add_token(tk) - @token_stream << tk - end - - def add_tokens(tks) - tks.each {|tk| add_token(tk)} - end - - def pop_token - @token_stream.pop - end - -end - |