mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Clean up namespacing of RI's classes
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@14953 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
929faf7906
commit
3e39ade457
20 changed files with 1362 additions and 1402 deletions
|
@ -1,3 +1,7 @@
|
||||||
|
Tue Jan 8 19:17:29 2008 Eric Hodel <drbrain@segment7.net>
|
||||||
|
|
||||||
|
* lib/rdoc/*: Clean up namespacing of RI's classes.
|
||||||
|
|
||||||
Tue Jan 8 18:05:35 2008 Eric Hodel <drbrain@segment7.net>
|
Tue Jan 8 18:05:35 2008 Eric Hodel <drbrain@segment7.net>
|
||||||
|
|
||||||
* bin/ri, lib/rdoc/ri/*: Replace with Ryan Davis' cached ri.
|
* bin/ri, lib/rdoc/ri/*: Replace with Ryan Davis' cached ri.
|
||||||
|
|
4
bin/ri
4
bin/ri
|
@ -1,6 +1,6 @@
|
||||||
#!/usr//bin/env ruby
|
#!/usr//bin/env ruby
|
||||||
|
|
||||||
require 'rdoc/ri/ri_driver'
|
require 'rdoc/ri/driver'
|
||||||
|
|
||||||
RDoc::RI::RiDriver.run ARGV
|
RDoc::RI::Driver.run ARGV
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
require 'rdoc/generators'
|
require 'rdoc/generators'
|
||||||
require 'rdoc/markup/simple_markup/to_flow'
|
require 'rdoc/markup/simple_markup/to_flow'
|
||||||
|
|
||||||
require 'rdoc/ri/ri_cache'
|
require 'rdoc/ri/cache'
|
||||||
require 'rdoc/ri/ri_reader'
|
require 'rdoc/ri/reader'
|
||||||
require 'rdoc/ri/ri_writer'
|
require 'rdoc/ri/writer'
|
||||||
require 'rdoc/ri/ri_descriptions'
|
require 'rdoc/ri/descriptions'
|
||||||
|
|
||||||
class RDoc::Generators::RIGenerator
|
class RDoc::Generators::RIGenerator
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ class RDoc::Generators::RIGenerator
|
||||||
|
|
||||||
def initialize(options) #:not-new:
|
def initialize(options) #:not-new:
|
||||||
@options = options
|
@options = options
|
||||||
@ri_writer = RI::RiWriter.new(".")
|
@ri_writer = RDoc::RI::Writer.new "."
|
||||||
@markup = SM::SimpleMarkup.new
|
@markup = SM::SimpleMarkup.new
|
||||||
@to_flow = SM::ToFlow.new
|
@to_flow = SM::ToFlow.new
|
||||||
|
|
||||||
|
@ -53,34 +53,35 @@ class RDoc::Generators::RIGenerator
|
||||||
|
|
||||||
def generate_class_info(cls)
|
def generate_class_info(cls)
|
||||||
if cls === RDoc::NormalModule
|
if cls === RDoc::NormalModule
|
||||||
cls_desc = RI::ModuleDescription.new
|
cls_desc = RDoc::RI::ModuleDescription.new
|
||||||
else
|
else
|
||||||
cls_desc = RI::ClassDescription.new
|
cls_desc = RDoc::RI::ClassDescription.new
|
||||||
cls_desc.superclass = cls.superclass
|
cls_desc.superclass = cls.superclass
|
||||||
end
|
end
|
||||||
cls_desc.name = cls.name
|
cls_desc.name = cls.name
|
||||||
cls_desc.full_name = cls.full_name
|
cls_desc.full_name = cls.full_name
|
||||||
cls_desc.comment = markup(cls.comment)
|
cls_desc.comment = markup(cls.comment)
|
||||||
|
|
||||||
cls_desc.attributes =cls.attributes.sort.map do |a|
|
cls_desc.attributes = cls.attributes.sort.map do |a|
|
||||||
RI::Attribute.new(a.name, a.rw, markup(a.comment))
|
RDoc::RI::Attribute.new(a.name, a.rw, markup(a.comment))
|
||||||
end
|
end
|
||||||
|
|
||||||
cls_desc.constants = cls.constants.map do |c|
|
cls_desc.constants = cls.constants.map do |c|
|
||||||
RI::Constant.new(c.name, c.value, markup(c.comment))
|
RDoc::RI::Constant.new(c.name, c.value, markup(c.comment))
|
||||||
end
|
end
|
||||||
|
|
||||||
cls_desc.includes = cls.includes.map do |i|
|
cls_desc.includes = cls.includes.map do |i|
|
||||||
RI::IncludedModule.new(i.name)
|
RDoc::RI::IncludedModule.new(i.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
class_methods, instance_methods = method_list(cls)
|
class_methods, instance_methods = method_list(cls)
|
||||||
|
|
||||||
cls_desc.class_methods = class_methods.map do |m|
|
cls_desc.class_methods = class_methods.map do |m|
|
||||||
RI::MethodSummary.new(m.name)
|
RDoc::RI::MethodSummary.new(m.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
cls_desc.instance_methods = instance_methods.map do |m|
|
cls_desc.instance_methods = instance_methods.map do |m|
|
||||||
RI::MethodSummary.new(m.name)
|
RDoc::RI::MethodSummary.new(m.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
update_or_replace(cls_desc)
|
update_or_replace(cls_desc)
|
||||||
|
@ -94,9 +95,8 @@ class RDoc::Generators::RIGenerator
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def generate_method_info(cls_desc, method)
|
def generate_method_info(cls_desc, method)
|
||||||
meth_desc = RI::MethodDescription.new
|
meth_desc = RDoc::RI::MethodDescription.new
|
||||||
meth_desc.name = method.name
|
meth_desc.name = method.name
|
||||||
meth_desc.full_name = cls_desc.full_name
|
meth_desc.full_name = cls_desc.full_name
|
||||||
if method.singleton
|
if method.singleton
|
||||||
|
@ -113,7 +113,7 @@ class RDoc::Generators::RIGenerator
|
||||||
meth_desc.block_params = method.block_params
|
meth_desc.block_params = method.block_params
|
||||||
|
|
||||||
meth_desc.aliases = method.aliases.map do |a|
|
meth_desc.aliases = method.aliases.map do |a|
|
||||||
RI::AliasName.new(a.name)
|
RDoc::RI::AliasName.new(a.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ri_writer.add_method(cls_desc, meth_desc)
|
@ri_writer.add_method(cls_desc, meth_desc)
|
||||||
|
@ -190,7 +190,7 @@ class RDoc::Generators::RIGenerator
|
||||||
old_cls = nil
|
old_cls = nil
|
||||||
|
|
||||||
if @options.merge
|
if @options.merge
|
||||||
rdr = RI::RiReader.new(RI::RiCache.new(@options.op_dir))
|
rdr = RDoc::RI::Reader.new RDoc::RI::Cache.new(@options.op_dir)
|
||||||
|
|
||||||
namespace = rdr.top_level_namespace
|
namespace = rdr.top_level_namespace
|
||||||
namespace = rdr.lookup_namespace_in(cls_desc.name, namespace)
|
namespace = rdr.lookup_namespace_in(cls_desc.name, namespace)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# We handle the parsing of options, and subsequently as a singleton
|
# We handle the parsing of options, and subsequently as a singleton
|
||||||
# object to be queried for option values
|
# object to be queried for option values
|
||||||
|
|
||||||
require "rdoc/ri/ri_paths"
|
require "rdoc/ri/paths"
|
||||||
require 'optparse'
|
require 'optparse'
|
||||||
|
|
||||||
class RDoc::Options
|
class RDoc::Options
|
||||||
|
@ -423,7 +423,7 @@ Usage: #{opt.program_name} [options] [names...]
|
||||||
"subsequent --op parameter, so no special",
|
"subsequent --op parameter, so no special",
|
||||||
"privileges are needed.") do |value|
|
"privileges are needed.") do |value|
|
||||||
@generator_name = "ri"
|
@generator_name = "ri"
|
||||||
@op_dir = RI::Paths::HOMEDIR
|
@op_dir = RDoc::RI::Paths::HOMEDIR
|
||||||
setup_generator
|
setup_generator
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -435,7 +435,7 @@ Usage: #{opt.program_name} [options] [names...]
|
||||||
"making them accessible to others, so",
|
"making them accessible to others, so",
|
||||||
"special privileges are needed.") do |value|
|
"special privileges are needed.") do |value|
|
||||||
@generator_name = "ri"
|
@generator_name = "ri"
|
||||||
@op_dir = RI::Paths::SITEDIR
|
@op_dir = RDoc::RI::Paths::SITEDIR
|
||||||
setup_generator
|
setup_generator
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -449,7 +449,7 @@ Usage: #{opt.program_name} [options] [names...]
|
||||||
"option is intended to be used during Ruby",
|
"option is intended to be used during Ruby",
|
||||||
"installation.") do |value|
|
"installation.") do |value|
|
||||||
@generator_name = "ri"
|
@generator_name = "ri"
|
||||||
@op_dir = RI::Paths::SYSDIR
|
@op_dir = RDoc::RI::Paths::SYSDIR
|
||||||
setup_generator
|
setup_generator
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ module RDoc
|
||||||
##
|
##
|
||||||
# Exception thrown by any rdoc error.
|
# Exception thrown by any rdoc error.
|
||||||
|
|
||||||
class Error < StandardError; end
|
class Error < RuntimeError; end
|
||||||
|
|
||||||
RDocError = Error # :nodoc:
|
RDocError = Error # :nodoc:
|
||||||
|
|
||||||
|
|
188
lib/rdoc/ri/cache.rb
Normal file
188
lib/rdoc/ri/cache.rb
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
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
|
||||||
|
|
147
lib/rdoc/ri/descriptions.rb
Normal file
147
lib/rdoc/ri/descriptions.rb
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
require 'yaml'
|
||||||
|
require 'rdoc/markup/simple_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::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::RDoc::RI::NamedThing; end
|
||||||
|
|
||||||
|
class RDoc::RI::Attribute < RDoc::RI::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 desscription 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
|
||||||
|
@comment << SM::Flow::RULE.new
|
||||||
|
@comment.concat old.comment
|
||||||
|
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
|
||||||
|
|
|
@ -1,54 +1,42 @@
|
||||||
# This is a kind of 'flag' module. If you want to write your
|
require 'rdoc/ri'
|
||||||
# own 'ri' display module (perhaps because you'r writing
|
|
||||||
# an IDE or somesuch beast), you simply write a class
|
##
|
||||||
# which implements the various 'display' methods in 'DefaultDisplay',
|
# This is a kind of 'flag' module. If you want to write your own 'ri' display
|
||||||
# and include the 'RiDisplay' module in that class.
|
# module (perhaps because you'r writing an IDE or somesuch beast), you simply
|
||||||
|
# write a class which implements the various 'display' methods in
|
||||||
|
# 'DefaultDisplay', and include the 'RiDisplay' module in that class.
|
||||||
#
|
#
|
||||||
# To access your class from the command line, you can do
|
# To access your class from the command line, you can do
|
||||||
#
|
#
|
||||||
# ruby -r <your source file> ../ri ....
|
# ruby -r <your source file> ../ri ....
|
||||||
#
|
|
||||||
# If folks _really_ want to do this from the command line,
|
|
||||||
# I'll build an option in
|
|
||||||
|
|
||||||
module RiDisplay
|
module RDoc::RI::Display
|
||||||
|
|
||||||
@@display_class = nil
|
@@display_class = nil
|
||||||
|
|
||||||
def RiDisplay.append_features(display_class)
|
def self.append_features(display_class)
|
||||||
@@display_class = display_class
|
@@display_class = display_class
|
||||||
end
|
end
|
||||||
|
|
||||||
def RiDisplay.new(*args)
|
def self.new(*args)
|
||||||
@@display_class.new(*args)
|
@@display_class.new(*args)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
######################################################################
|
##
|
||||||
#
|
# A paging display module. Uses the RDoc::RI::Formatter class to do the actual
|
||||||
# A paging display module. Uses the ri_formatter class to do the
|
# presentation
|
||||||
# actual presentation
|
|
||||||
#
|
|
||||||
|
|
||||||
class DefaultDisplay
|
class RDoc::RI::DefaultDisplay
|
||||||
|
|
||||||
include RiDisplay
|
include RDoc::RI::Display
|
||||||
|
|
||||||
def initialize(formatter, width, use_stdout)
|
def initialize(formatter, width, use_stdout)
|
||||||
@use_stdout = use_stdout
|
@use_stdout = use_stdout
|
||||||
@formatter = formatter.new width, " "
|
@formatter = formatter.new width, " "
|
||||||
end
|
end
|
||||||
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
def display_usage
|
|
||||||
page do
|
|
||||||
RI::Options::OptionList.usage(short_form=true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
def display_method_info(method)
|
def display_method_info(method)
|
||||||
page do
|
page do
|
||||||
@formatter.draw_line(method.full_name)
|
@formatter.draw_line(method.full_name)
|
||||||
|
@ -65,8 +53,6 @@ class DefaultDisplay
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
def display_class_info(klass, ri_reader)
|
def display_class_info(klass, ri_reader)
|
||||||
page do
|
page do
|
||||||
superclass = klass.superclass_string
|
superclass = klass.superclass_string
|
||||||
|
@ -145,8 +131,7 @@ class DefaultDisplay
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
######################################################################
|
##
|
||||||
|
|
||||||
# Display a list of method names
|
# Display a list of method names
|
||||||
|
|
||||||
def display_method_list(methods)
|
def display_method_list(methods)
|
||||||
|
@ -157,8 +142,6 @@ class DefaultDisplay
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
def display_class_list(namespaces)
|
def display_class_list(namespaces)
|
||||||
page do
|
page do
|
||||||
puts "More than one class or module matched your request. You can refine"
|
puts "More than one class or module matched your request. You can refine"
|
||||||
|
@ -167,8 +150,6 @@ class DefaultDisplay
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
def list_known_classes(classes)
|
def list_known_classes(classes)
|
||||||
if classes.empty?
|
if classes.empty?
|
||||||
warn_no_database
|
warn_no_database
|
||||||
|
@ -181,8 +162,6 @@ class DefaultDisplay
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
def list_known_names(names)
|
def list_known_names(names)
|
||||||
if names.empty?
|
if names.empty?
|
||||||
warn_no_database
|
warn_no_database
|
||||||
|
@ -193,12 +172,8 @@ class DefaultDisplay
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
def page
|
def page
|
||||||
if pager = setup_pager then
|
if pager = setup_pager then
|
||||||
begin
|
begin
|
||||||
|
@ -215,8 +190,6 @@ class DefaultDisplay
|
||||||
rescue Errno::EPIPE
|
rescue Errno::EPIPE
|
||||||
end
|
end
|
||||||
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
def setup_pager
|
def setup_pager
|
||||||
unless @use_stdout then
|
unless @use_stdout then
|
||||||
for pager in [ ENV['PAGER'], "less", "more", 'pager' ].compact.uniq
|
for pager in [ ENV['PAGER'], "less", "more", 'pager' ].compact.uniq
|
||||||
|
@ -227,8 +200,6 @@ class DefaultDisplay
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
def display_params(method)
|
def display_params(method)
|
||||||
params = method.params
|
params = method.params
|
||||||
|
|
||||||
|
@ -248,7 +219,6 @@ class DefaultDisplay
|
||||||
@formatter.wrap("Extension from #{method.source_path}")
|
@formatter.wrap("Extension from #{method.source_path}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
######################################################################
|
|
||||||
|
|
||||||
def display_flow(flow)
|
def display_flow(flow)
|
||||||
if !flow || flow.empty?
|
if !flow || flow.empty?
|
||||||
|
@ -258,8 +228,6 @@ class DefaultDisplay
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
def warn_no_database
|
def warn_no_database
|
||||||
puts "No ri data found"
|
puts "No ri data found"
|
||||||
puts
|
puts
|
||||||
|
@ -272,4 +240,5 @@ class DefaultDisplay
|
||||||
puts "If you installed Ruby from a packaging system, then you may need to"
|
puts "If you installed Ruby from a packaging system, then you may need to"
|
||||||
puts "install an additional package, or ask the packager to enable ri generation."
|
puts "install an additional package, or ask the packager to enable ri generation."
|
||||||
end
|
end
|
||||||
end # class RiDisplay
|
end
|
||||||
|
|
|
@ -2,20 +2,20 @@ require 'optparse'
|
||||||
require 'yaml'
|
require 'yaml'
|
||||||
|
|
||||||
require 'rdoc/ri'
|
require 'rdoc/ri'
|
||||||
require 'rdoc/ri/ri_paths'
|
require 'rdoc/ri/paths'
|
||||||
require 'rdoc/ri/ri_formatter'
|
require 'rdoc/ri/formatter'
|
||||||
require 'rdoc/ri/ri_display'
|
require 'rdoc/ri/display'
|
||||||
require 'fileutils'
|
require 'fileutils'
|
||||||
require 'rdoc/markup/simple_markup'
|
require 'rdoc/markup/simple_markup'
|
||||||
require 'rdoc/markup/simple_markup/to_flow'
|
require 'rdoc/markup/simple_markup/to_flow'
|
||||||
|
|
||||||
class RDoc::RI::RiDriver
|
class RDoc::RI::Driver
|
||||||
|
|
||||||
def self.process_args(argv)
|
def self.process_args(argv)
|
||||||
options = {}
|
options = {}
|
||||||
options[:use_stdout] = !$stdout.tty?
|
options[:use_stdout] = !$stdout.tty?
|
||||||
options[:width] = 72
|
options[:width] = 72
|
||||||
options[:formatter] = RI::TextFormatter.for 'plain'
|
options[:formatter] = RDoc::RI::Formatter.for 'plain'
|
||||||
options[:list_classes] = false
|
options[:list_classes] = false
|
||||||
options[:list_names] = false
|
options[:list_names] = false
|
||||||
|
|
||||||
|
@ -33,12 +33,12 @@ class RDoc::RI::RiDriver
|
||||||
opt.summary_indent = ' ' * 4
|
opt.summary_indent = ' ' * 4
|
||||||
|
|
||||||
directories = [
|
directories = [
|
||||||
RI::Paths::SYSDIR,
|
RDoc::RI::Paths::SYSDIR,
|
||||||
RI::Paths::SITEDIR,
|
RDoc::RI::Paths::SITEDIR,
|
||||||
RI::Paths::HOMEDIR
|
RDoc::RI::Paths::HOMEDIR
|
||||||
]
|
]
|
||||||
|
|
||||||
if RI::Paths::GEMDIRS then
|
if RDoc::RI::Paths::GEMDIRS then
|
||||||
Gem.path.each do |dir|
|
Gem.path.each do |dir|
|
||||||
directories << "#{dir}/doc/*/ri"
|
directories << "#{dir}/doc/*/ri"
|
||||||
end
|
end
|
||||||
|
@ -109,19 +109,19 @@ Options may also be set in the 'RI' environment variable.
|
||||||
opt.separator nil
|
opt.separator nil
|
||||||
|
|
||||||
opt.on("--fmt=FORMAT", "--format=FORMAT", "-f",
|
opt.on("--fmt=FORMAT", "--format=FORMAT", "-f",
|
||||||
RI::TextFormatter.list.split(', '), # HACK
|
RDoc::RI::Formatter::FORMATTERS.keys,
|
||||||
"Format to use when displaying output:",
|
"Format to use when displaying output:",
|
||||||
" #{RI::TextFormatter.list}",
|
" #{RDoc::RI::Formatter.list}",
|
||||||
"Use 'bs' (backspace) with most pager",
|
"Use 'bs' (backspace) with most pager",
|
||||||
"programs. To use ANSI, either disable the",
|
"programs. To use ANSI, either disable the",
|
||||||
"pager or tell the pager to allow control",
|
"pager or tell the pager to allow control",
|
||||||
"characters.") do |value|
|
"characters.") do |value|
|
||||||
options[:formatter] = RI::TextFormatter.for value
|
options[:formatter] = RDoc::RI::Formatter.for value
|
||||||
end
|
end
|
||||||
|
|
||||||
opt.separator nil
|
opt.separator nil
|
||||||
|
|
||||||
if RI::Paths::GEMDIRS then
|
unless RDoc::RI::Paths::GEMDIRS.empty? then
|
||||||
opt.on("--[no-]gems",
|
opt.on("--[no-]gems",
|
||||||
"Include documentation from RubyGems.") do |value|
|
"Include documentation from RubyGems.") do |value|
|
||||||
use_gems = value
|
use_gems = value
|
||||||
|
@ -180,10 +180,10 @@ Options may also be set in the 'RI' environment variable.
|
||||||
|
|
||||||
options[:names] = argv
|
options[:names] = argv
|
||||||
|
|
||||||
options[:path] = RI::Paths.path(use_system, use_site, use_home, use_gems,
|
options[:path] = RDoc::RI::Paths.path(use_system, use_site, use_home,
|
||||||
*doc_dirs)
|
use_gems, *doc_dirs)
|
||||||
options[:raw_path] = RI::Paths.raw_path(use_system, use_site, use_home,
|
options[:raw_path] = RDoc::RI::Paths.raw_path(use_system, use_site,
|
||||||
use_gems, *doc_dirs)
|
use_home, use_gems, *doc_dirs)
|
||||||
|
|
||||||
options
|
options
|
||||||
|
|
||||||
|
@ -204,17 +204,18 @@ Options may also be set in the 'RI' environment variable.
|
||||||
@names = options[:names]
|
@names = options[:names]
|
||||||
|
|
||||||
@class_cache_name = 'classes'
|
@class_cache_name = 'classes'
|
||||||
@all_dirs = RI::Paths.path(true, true, true, true)
|
@all_dirs = RDoc::RI::Paths.path(true, true, true, true)
|
||||||
@homepath = RI::Paths.raw_path(false, false, true, false).first
|
@homepath = RDoc::RI::Paths.raw_path(false, false, true, false).first
|
||||||
@homepath = @homepath.sub(/\.rdoc/, '.ri')
|
@homepath = @homepath.sub(/\.rdoc/, '.ri')
|
||||||
@sys_dirs = RI::Paths.raw_path(true, false, false, false)
|
@sys_dirs = RDoc::RI::Paths.raw_path(true, false, false, false)
|
||||||
|
|
||||||
FileUtils.mkdir_p cache_file_path unless File.directory? cache_file_path
|
FileUtils.mkdir_p cache_file_path unless File.directory? cache_file_path
|
||||||
|
|
||||||
@class_cache = nil
|
@class_cache = nil
|
||||||
|
|
||||||
@display = DefaultDisplay.new(options[:formatter], options[:width],
|
@display = RDoc::RI::DefaultDisplay.new(options[:formatter],
|
||||||
options[:use_stdout])
|
options[:width],
|
||||||
|
options[:use_stdout])
|
||||||
end
|
end
|
||||||
|
|
||||||
def class_cache
|
def class_cache
|
||||||
|
@ -386,17 +387,6 @@ Options may also be set in the 'RI' environment variable.
|
||||||
cache
|
cache
|
||||||
end
|
end
|
||||||
|
|
||||||
# Couldn't find documentation in +path+, so tell the user what to do
|
|
||||||
|
|
||||||
def report_missing_documentation(path)
|
|
||||||
STDERR.puts "No ri documentation found in:"
|
|
||||||
path.each do |d|
|
|
||||||
STDERR.puts " #{d}"
|
|
||||||
end
|
|
||||||
STDERR.puts "\nWas rdoc run to create documentation?\n\n"
|
|
||||||
RDoc::usage("Installing Documentation")
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
class Hash
|
class Hash
|
662
lib/rdoc/ri/formatter.rb
Normal file
662
lib/rdoc/ri/formatter.rb
Normal file
|
@ -0,0 +1,662 @@
|
||||||
|
require 'rdoc/ri'
|
||||||
|
|
||||||
|
class RDoc::RI::Formatter
|
||||||
|
|
||||||
|
attr_reader :indent
|
||||||
|
|
||||||
|
def initialize(width, indent)
|
||||||
|
@width = width
|
||||||
|
@indent = indent
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
def draw_line(label=nil)
|
||||||
|
len = @width
|
||||||
|
len -= (label.size+1) if label
|
||||||
|
print "-"*len
|
||||||
|
if label
|
||||||
|
print(" ")
|
||||||
|
bold_print(label)
|
||||||
|
end
|
||||||
|
puts
|
||||||
|
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?
|
||||||
|
puts(prefix + res.join("\n" + next_prefix))
|
||||||
|
end
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
def blankline
|
||||||
|
puts
|
||||||
|
end
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
# called when we want to ensure a nbew '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)
|
||||||
|
print txt
|
||||||
|
end
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
def raw_print_line(txt)
|
||||||
|
puts txt
|
||||||
|
end
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
# convert HTML entities back to ASCII
|
||||||
|
def conv_html(txt)
|
||||||
|
txt.
|
||||||
|
gsub(/>/, '>').
|
||||||
|
gsub(/</, '<').
|
||||||
|
gsub(/"/, '"').
|
||||||
|
gsub(/&/, '&')
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
# convert markup into display form
|
||||||
|
def conv_markup(txt)
|
||||||
|
txt.
|
||||||
|
gsub(%r{<tt>(.*?)</tt>}) { "+#$1+" } .
|
||||||
|
gsub(%r{<code>(.*?)</code>}) { "+#$1+" } .
|
||||||
|
gsub(%r{<b>(.*?)</b>}) { "*#$1*" } .
|
||||||
|
gsub(%r{<em>(.*?)</em>}) { "_#$1_" }
|
||||||
|
end
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
def display_list(list)
|
||||||
|
case list.type
|
||||||
|
|
||||||
|
when SM::ListBase::BULLET
|
||||||
|
prefixer = proc { |ignored| @indent + "* " }
|
||||||
|
|
||||||
|
when SM::ListBase::NUMBER,
|
||||||
|
SM::ListBase::UPPERALPHA,
|
||||||
|
SM::ListBase::LOWERALPHA
|
||||||
|
|
||||||
|
start = case list.type
|
||||||
|
when SM::ListBase::NUMBER then 1
|
||||||
|
when SM::ListBase::UPPERALPHA then 'A'
|
||||||
|
when SM::ListBase::LOWERALPHA then 'a'
|
||||||
|
end
|
||||||
|
prefixer = proc do |ignored|
|
||||||
|
res = @indent + "#{start}.".ljust(4)
|
||||||
|
start = start.succ
|
||||||
|
res
|
||||||
|
end
|
||||||
|
|
||||||
|
when SM::ListBase::LABELED
|
||||||
|
prefixer = proc do |li|
|
||||||
|
li.label
|
||||||
|
end
|
||||||
|
|
||||||
|
when SM::ListBase::NOTE
|
||||||
|
longest = 0
|
||||||
|
list.contents.each do |item|
|
||||||
|
if item.kind_of?(SM::Flow::LI) && item.label.length > longest
|
||||||
|
longest = item.label.length
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
prefixer = proc do |li|
|
||||||
|
@indent + li.label.ljust(longest+1)
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
fail "unknown list type"
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
list.contents.each do |item|
|
||||||
|
if item.kind_of? SM::Flow::LI
|
||||||
|
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 SM::Flow::P, SM::Flow::LI
|
||||||
|
wrap(conv_html(item.body), prefix)
|
||||||
|
blankline
|
||||||
|
|
||||||
|
when SM::Flow::LIST
|
||||||
|
display_list(item)
|
||||||
|
|
||||||
|
when SM::Flow::VERB
|
||||||
|
display_verbatim_flow_item(item, @indent)
|
||||||
|
|
||||||
|
when SM::Flow::H
|
||||||
|
display_heading(conv_html(item.text), item.level, @indent)
|
||||||
|
|
||||||
|
when SM::Flow::RULE
|
||||||
|
draw_line
|
||||||
|
|
||||||
|
else
|
||||||
|
fail "Unknown flow element: #{item.class}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
def display_verbatim_flow_item(item, prefix=@indent)
|
||||||
|
item.body.split(/\n/).each do |line|
|
||||||
|
print @indent, conv_html(line), "\n"
|
||||||
|
end
|
||||||
|
blankline
|
||||||
|
end
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
def display_heading(text, level, indent)
|
||||||
|
text = strip_attributes(text)
|
||||||
|
case level
|
||||||
|
when 1
|
||||||
|
ul = "=" * text.length
|
||||||
|
puts
|
||||||
|
puts text.upcase
|
||||||
|
puts ul
|
||||||
|
# puts
|
||||||
|
|
||||||
|
when 2
|
||||||
|
ul = "-" * text.length
|
||||||
|
puts
|
||||||
|
puts text
|
||||||
|
puts ul
|
||||||
|
# puts
|
||||||
|
else
|
||||||
|
print indent, text, "\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def display_flow(flow)
|
||||||
|
flow.each do |f|
|
||||||
|
display_flow_item(f)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def strip_attributes(txt)
|
||||||
|
tokens = txt.split(%r{(</?(?:b|code|em|i|tt)>)})
|
||||||
|
text = []
|
||||||
|
attributes = 0
|
||||||
|
tokens.each do |tok|
|
||||||
|
case tok
|
||||||
|
when %r{^</(\w+)>$}, %r{^<(\w+)>$}
|
||||||
|
;
|
||||||
|
else
|
||||||
|
text << tok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
text.join
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
# TODO: struct?
|
||||||
|
class AttrChar
|
||||||
|
attr_reader :char
|
||||||
|
attr_reader :attr
|
||||||
|
|
||||||
|
def initialize(char, attr)
|
||||||
|
@char = char
|
||||||
|
@attr = attr
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
##
|
||||||
|
# overridden in specific formatters
|
||||||
|
|
||||||
|
def write_attribute_text(prefix, line)
|
||||||
|
print prefix
|
||||||
|
line.each do |achar|
|
||||||
|
print achar.char
|
||||||
|
end
|
||||||
|
puts
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# again, overridden
|
||||||
|
|
||||||
|
def bold_print(txt)
|
||||||
|
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)
|
||||||
|
print prefix
|
||||||
|
line.each do |achar|
|
||||||
|
attr = achar.attr
|
||||||
|
if (attr & (ITALIC+CODE)) != 0
|
||||||
|
print "_", BS
|
||||||
|
end
|
||||||
|
if (attr & BOLD) != 0
|
||||||
|
print achar.char, BS
|
||||||
|
end
|
||||||
|
print achar.char
|
||||||
|
end
|
||||||
|
puts
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# draw a string in bold
|
||||||
|
|
||||||
|
def bold_print(text)
|
||||||
|
text.split(//).each do |ch|
|
||||||
|
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)
|
||||||
|
print "\033[0m"
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_attribute_text(prefix, line)
|
||||||
|
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
|
||||||
|
print achar.char
|
||||||
|
end
|
||||||
|
update_attributes(0) unless curr_attr.zero?
|
||||||
|
puts
|
||||||
|
end
|
||||||
|
|
||||||
|
def bold_print(txt)
|
||||||
|
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]
|
||||||
|
print indent
|
||||||
|
print heading[0]
|
||||||
|
print strip_attributes(text)
|
||||||
|
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
|
||||||
|
print str, "m"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# This formatter uses HTML.
|
||||||
|
|
||||||
|
class RDoc::RI::HtmlFormatter < RDoc::RI::AttributeFormatter
|
||||||
|
|
||||||
|
def initialize(*args)
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
puts("<hr>")
|
||||||
|
end
|
||||||
|
|
||||||
|
def bold_print(txt)
|
||||||
|
tag("b") { txt }
|
||||||
|
end
|
||||||
|
|
||||||
|
def blankline()
|
||||||
|
puts("<p>")
|
||||||
|
end
|
||||||
|
|
||||||
|
def break_to_newline
|
||||||
|
puts("<br>")
|
||||||
|
end
|
||||||
|
|
||||||
|
def display_heading(text, level, indent)
|
||||||
|
level = 4 if level > 4
|
||||||
|
tag("h#{level}") { text }
|
||||||
|
puts
|
||||||
|
end
|
||||||
|
|
||||||
|
def display_list(list)
|
||||||
|
case list.type
|
||||||
|
when SM::ListBase::BULLET
|
||||||
|
list_type = "ul"
|
||||||
|
prefixer = proc { |ignored| "<li>" }
|
||||||
|
|
||||||
|
when SM::ListBase::NUMBER,
|
||||||
|
SM::ListBase::UPPERALPHA,
|
||||||
|
SM::ListBase::LOWERALPHA
|
||||||
|
list_type = "ol"
|
||||||
|
prefixer = proc { |ignored| "<li>" }
|
||||||
|
|
||||||
|
when SM::ListBase::LABELED
|
||||||
|
list_type = "dl"
|
||||||
|
prefixer = proc do |li|
|
||||||
|
"<dt><b>" + escape(li.label) + "</b><dd>"
|
||||||
|
end
|
||||||
|
|
||||||
|
when SM::ListBase::NOTE
|
||||||
|
list_type = "table"
|
||||||
|
prefixer = proc do |li|
|
||||||
|
%{<tr valign="top"><td>#{li.label.gsub(/ /, ' ')}</td><td>}
|
||||||
|
end
|
||||||
|
else
|
||||||
|
fail "unknown list type"
|
||||||
|
end
|
||||||
|
|
||||||
|
print "<#{list_type}>"
|
||||||
|
list.contents.each do |item|
|
||||||
|
if item.kind_of? SM::Flow::LI
|
||||||
|
prefix = prefixer.call(item)
|
||||||
|
print prefix
|
||||||
|
display_flow_item(item, prefix)
|
||||||
|
else
|
||||||
|
display_flow_item(item)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
print "</#{list_type}>"
|
||||||
|
end
|
||||||
|
|
||||||
|
def display_verbatim_flow_item(item, prefix=@indent)
|
||||||
|
print("<pre>")
|
||||||
|
item.body.split(/\n/).each do |line|
|
||||||
|
puts conv_html(line)
|
||||||
|
end
|
||||||
|
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
|
||||||
|
print str
|
||||||
|
end
|
||||||
|
|
||||||
|
def tag(code)
|
||||||
|
print("<#{code}>")
|
||||||
|
print(yield)
|
||||||
|
print("</#{code}>")
|
||||||
|
end
|
||||||
|
|
||||||
|
def escape(str)
|
||||||
|
str.
|
||||||
|
gsub(/&/n, '&').
|
||||||
|
gsub(/\"/n, '"').
|
||||||
|
gsub(/>/n, '>').
|
||||||
|
gsub(/</n, '<')
|
||||||
|
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)
|
||||||
|
puts
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Place heading level indicators inline with heading.
|
||||||
|
|
||||||
|
def display_heading(text, level, indent)
|
||||||
|
text = strip_attributes(text)
|
||||||
|
case level
|
||||||
|
when 1
|
||||||
|
puts "= " + text.upcase
|
||||||
|
when 2
|
||||||
|
puts "-- " + text
|
||||||
|
else
|
||||||
|
print indent, text, "\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
# Finally, fill in the list of known formatters
|
||||||
|
|
||||||
|
class RDoc::RI::Formatter
|
||||||
|
|
||||||
|
FORMATTERS = {
|
||||||
|
"plain" => RDoc::RI::Formatter,
|
||||||
|
"simple" => RDoc::RI::SimpleFormatter,
|
||||||
|
"bs" => RDoc::RI::OverstrikeFormatter,
|
||||||
|
"ansi" => RDoc::RI::AnsiFormatter,
|
||||||
|
"html" => RDoc::RI::HtmlFormatter,
|
||||||
|
}
|
||||||
|
|
||||||
|
def self.list
|
||||||
|
FORMATTERS.keys.sort.join(", ")
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.for(name)
|
||||||
|
FORMATTERS[name.downcase]
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
97
lib/rdoc/ri/paths.rb
Normal file
97
lib/rdoc/ri/paths.rb
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
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)}
|
||||||
|
|
||||||
|
require 'rubygems' unless defined?(Gem) 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 }
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
106
lib/rdoc/ri/reader.rb
Normal file
106
lib/rdoc/ri/reader.rb
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
require 'rdoc/ri'
|
||||||
|
require 'rdoc/ri/descriptions'
|
||||||
|
require 'rdoc/ri/writer'
|
||||||
|
require 'rdoc/markup/simple_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
|
||||||
|
|
|
@ -1,187 +0,0 @@
|
||||||
module RI
|
|
||||||
|
|
||||||
class 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 TopLevelEntry < 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 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 RiCache
|
|
||||||
|
|
||||||
attr_reader :toplevel
|
|
||||||
|
|
||||||
def initialize(dirs)
|
|
||||||
# At the top level we have a dummy module holding the
|
|
||||||
# overall namespace
|
|
||||||
@toplevel = TopLevelEntry.new('', '::', nil)
|
|
||||||
|
|
||||||
dirs.each do |dir|
|
|
||||||
@toplevel.load_from(dir)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,154 +0,0 @@
|
||||||
require 'yaml'
|
|
||||||
require 'rdoc/markup/simple_markup/fragments'
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
module RI
|
|
||||||
class 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
|
|
||||||
|
|
||||||
# Alias = Struct.new(:old_name, :new_name)
|
|
||||||
|
|
||||||
class AliasName < NamedThing
|
|
||||||
end
|
|
||||||
|
|
||||||
class Attribute < NamedThing
|
|
||||||
attr_reader :rw, :comment
|
|
||||||
def initialize(name, rw, comment)
|
|
||||||
super(name)
|
|
||||||
@rw = rw
|
|
||||||
@comment = comment
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Constant < NamedThing
|
|
||||||
attr_reader :value, :comment
|
|
||||||
def initialize(name, value, comment)
|
|
||||||
super(name)
|
|
||||||
@value = value
|
|
||||||
@comment = comment
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class IncludedModule < NamedThing
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
class MethodSummary < NamedThing
|
|
||||||
def initialize(name="")
|
|
||||||
super
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Description
|
|
||||||
attr_accessor :name
|
|
||||||
attr_accessor :full_name
|
|
||||||
attr_accessor :comment
|
|
||||||
|
|
||||||
def serialize
|
|
||||||
self.to_yaml
|
|
||||||
end
|
|
||||||
|
|
||||||
def Description.deserialize(from)
|
|
||||||
YAML.load(from)
|
|
||||||
end
|
|
||||||
|
|
||||||
def <=>(other)
|
|
||||||
@name <=> other.name
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class ModuleDescription < Description
|
|
||||||
|
|
||||||
attr_accessor :class_methods
|
|
||||||
attr_accessor :instance_methods
|
|
||||||
attr_accessor :attributes
|
|
||||||
attr_accessor :constants
|
|
||||||
attr_accessor :includes
|
|
||||||
|
|
||||||
# merge in another class desscription 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
|
|
||||||
@comment << SM::Flow::RULE.new
|
|
||||||
@comment.concat old.comment
|
|
||||||
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 ClassDescription < ModuleDescription
|
|
||||||
attr_accessor :superclass
|
|
||||||
|
|
||||||
def display_name
|
|
||||||
"Class"
|
|
||||||
end
|
|
||||||
|
|
||||||
def superclass_string
|
|
||||||
if @superclass && @superclass != "Object"
|
|
||||||
@superclass
|
|
||||||
else
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
class MethodDescription < 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
|
|
||||||
|
|
||||||
end
|
|
|
@ -1,673 +0,0 @@
|
||||||
module RI
|
|
||||||
class TextFormatter
|
|
||||||
|
|
||||||
attr_reader :indent
|
|
||||||
|
|
||||||
def initialize(width, indent)
|
|
||||||
@width = width
|
|
||||||
@indent = indent
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
def draw_line(label=nil)
|
|
||||||
len = @width
|
|
||||||
len -= (label.size+1) if label
|
|
||||||
print "-"*len
|
|
||||||
if label
|
|
||||||
print(" ")
|
|
||||||
bold_print(label)
|
|
||||||
end
|
|
||||||
puts
|
|
||||||
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?
|
|
||||||
puts(prefix + res.join("\n" + next_prefix))
|
|
||||||
end
|
|
||||||
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
def blankline
|
|
||||||
puts
|
|
||||||
end
|
|
||||||
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
# called when we want to ensure a nbew '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)
|
|
||||||
print txt
|
|
||||||
end
|
|
||||||
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
def raw_print_line(txt)
|
|
||||||
puts txt
|
|
||||||
end
|
|
||||||
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
# convert HTML entities back to ASCII
|
|
||||||
def conv_html(txt)
|
|
||||||
txt.
|
|
||||||
gsub(/>/, '>').
|
|
||||||
gsub(/</, '<').
|
|
||||||
gsub(/"/, '"').
|
|
||||||
gsub(/&/, '&')
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
# convert markup into display form
|
|
||||||
def conv_markup(txt)
|
|
||||||
txt.
|
|
||||||
gsub(%r{<tt>(.*?)</tt>}) { "+#$1+" } .
|
|
||||||
gsub(%r{<code>(.*?)</code>}) { "+#$1+" } .
|
|
||||||
gsub(%r{<b>(.*?)</b>}) { "*#$1*" } .
|
|
||||||
gsub(%r{<em>(.*?)</em>}) { "_#$1_" }
|
|
||||||
end
|
|
||||||
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
def display_list(list)
|
|
||||||
case list.type
|
|
||||||
|
|
||||||
when SM::ListBase::BULLET
|
|
||||||
prefixer = proc { |ignored| @indent + "* " }
|
|
||||||
|
|
||||||
when SM::ListBase::NUMBER,
|
|
||||||
SM::ListBase::UPPERALPHA,
|
|
||||||
SM::ListBase::LOWERALPHA
|
|
||||||
|
|
||||||
start = case list.type
|
|
||||||
when SM::ListBase::NUMBER then 1
|
|
||||||
when SM::ListBase::UPPERALPHA then 'A'
|
|
||||||
when SM::ListBase::LOWERALPHA then 'a'
|
|
||||||
end
|
|
||||||
prefixer = proc do |ignored|
|
|
||||||
res = @indent + "#{start}.".ljust(4)
|
|
||||||
start = start.succ
|
|
||||||
res
|
|
||||||
end
|
|
||||||
|
|
||||||
when SM::ListBase::LABELED
|
|
||||||
prefixer = proc do |li|
|
|
||||||
li.label
|
|
||||||
end
|
|
||||||
|
|
||||||
when SM::ListBase::NOTE
|
|
||||||
longest = 0
|
|
||||||
list.contents.each do |item|
|
|
||||||
if item.kind_of?(SM::Flow::LI) && item.label.length > longest
|
|
||||||
longest = item.label.length
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
prefixer = proc do |li|
|
|
||||||
@indent + li.label.ljust(longest+1)
|
|
||||||
end
|
|
||||||
|
|
||||||
else
|
|
||||||
fail "unknown list type"
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
list.contents.each do |item|
|
|
||||||
if item.kind_of? SM::Flow::LI
|
|
||||||
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 SM::Flow::P, SM::Flow::LI
|
|
||||||
wrap(conv_html(item.body), prefix)
|
|
||||||
blankline
|
|
||||||
|
|
||||||
when SM::Flow::LIST
|
|
||||||
display_list(item)
|
|
||||||
|
|
||||||
when SM::Flow::VERB
|
|
||||||
display_verbatim_flow_item(item, @indent)
|
|
||||||
|
|
||||||
when SM::Flow::H
|
|
||||||
display_heading(conv_html(item.text), item.level, @indent)
|
|
||||||
|
|
||||||
when SM::Flow::RULE
|
|
||||||
draw_line
|
|
||||||
|
|
||||||
else
|
|
||||||
fail "Unknown flow element: #{item.class}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
def display_verbatim_flow_item(item, prefix=@indent)
|
|
||||||
item.body.split(/\n/).each do |line|
|
|
||||||
print @indent, conv_html(line), "\n"
|
|
||||||
end
|
|
||||||
blankline
|
|
||||||
end
|
|
||||||
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
def display_heading(text, level, indent)
|
|
||||||
text = strip_attributes(text)
|
|
||||||
case level
|
|
||||||
when 1
|
|
||||||
ul = "=" * text.length
|
|
||||||
puts
|
|
||||||
puts text.upcase
|
|
||||||
puts ul
|
|
||||||
# puts
|
|
||||||
|
|
||||||
when 2
|
|
||||||
ul = "-" * text.length
|
|
||||||
puts
|
|
||||||
puts text
|
|
||||||
puts ul
|
|
||||||
# puts
|
|
||||||
else
|
|
||||||
print indent, text, "\n"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def display_flow(flow)
|
|
||||||
flow.each do |f|
|
|
||||||
display_flow_item(f)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def strip_attributes(txt)
|
|
||||||
tokens = txt.split(%r{(</?(?:b|code|em|i|tt)>)})
|
|
||||||
text = []
|
|
||||||
attributes = 0
|
|
||||||
tokens.each do |tok|
|
|
||||||
case tok
|
|
||||||
when %r{^</(\w+)>$}, %r{^<(\w+)>$}
|
|
||||||
;
|
|
||||||
else
|
|
||||||
text << tok
|
|
||||||
end
|
|
||||||
end
|
|
||||||
text.join
|
|
||||||
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 AttributeFormatter < TextFormatter
|
|
||||||
|
|
||||||
BOLD = 1
|
|
||||||
ITALIC = 2
|
|
||||||
CODE = 4
|
|
||||||
|
|
||||||
ATTR_MAP = {
|
|
||||||
"b" => BOLD,
|
|
||||||
"code" => CODE,
|
|
||||||
"em" => ITALIC,
|
|
||||||
"i" => ITALIC,
|
|
||||||
"tt" => CODE
|
|
||||||
}
|
|
||||||
|
|
||||||
# TODO: struct?
|
|
||||||
class AttrChar
|
|
||||||
attr_reader :char
|
|
||||||
attr_reader :attr
|
|
||||||
|
|
||||||
def initialize(char, attr)
|
|
||||||
@char = char
|
|
||||||
@attr = attr
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
# overridden in specific formatters
|
|
||||||
|
|
||||||
def write_attribute_text(prefix, line)
|
|
||||||
print prefix
|
|
||||||
line.each do |achar|
|
|
||||||
print achar.char
|
|
||||||
end
|
|
||||||
puts
|
|
||||||
end
|
|
||||||
|
|
||||||
# again, overridden
|
|
||||||
|
|
||||||
def bold_print(txt)
|
|
||||||
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 OverstrikeFormatter < AttributeFormatter
|
|
||||||
|
|
||||||
BS = "\C-h"
|
|
||||||
|
|
||||||
def write_attribute_text(prefix, line)
|
|
||||||
print prefix
|
|
||||||
line.each do |achar|
|
|
||||||
attr = achar.attr
|
|
||||||
if (attr & (ITALIC+CODE)) != 0
|
|
||||||
print "_", BS
|
|
||||||
end
|
|
||||||
if (attr & BOLD) != 0
|
|
||||||
print achar.char, BS
|
|
||||||
end
|
|
||||||
print achar.char
|
|
||||||
end
|
|
||||||
puts
|
|
||||||
end
|
|
||||||
|
|
||||||
# draw a string in bold
|
|
||||||
def bold_print(text)
|
|
||||||
text.split(//).each do |ch|
|
|
||||||
print ch, BS, ch
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##################################################
|
|
||||||
|
|
||||||
# This formatter uses ANSI escape sequences
|
|
||||||
# to colorize stuff
|
|
||||||
# works with pages such as man and less.
|
|
||||||
|
|
||||||
class AnsiFormatter < AttributeFormatter
|
|
||||||
|
|
||||||
def initialize(*args)
|
|
||||||
print "\033[0m"
|
|
||||||
super
|
|
||||||
end
|
|
||||||
|
|
||||||
def write_attribute_text(prefix, line)
|
|
||||||
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
|
|
||||||
print achar.char
|
|
||||||
end
|
|
||||||
update_attributes(0) unless curr_attr.zero?
|
|
||||||
puts
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def bold_print(txt)
|
|
||||||
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]
|
|
||||||
print indent
|
|
||||||
print heading[0]
|
|
||||||
print strip_attributes(text)
|
|
||||||
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
|
|
||||||
print str, "m"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##################################################
|
|
||||||
|
|
||||||
# This formatter uses HTML.
|
|
||||||
|
|
||||||
class HtmlFormatter < AttributeFormatter
|
|
||||||
|
|
||||||
def initialize(*args)
|
|
||||||
super
|
|
||||||
end
|
|
||||||
|
|
||||||
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
|
|
||||||
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
|
|
||||||
puts("<hr>")
|
|
||||||
end
|
|
||||||
|
|
||||||
def bold_print(txt)
|
|
||||||
tag("b") { txt }
|
|
||||||
end
|
|
||||||
|
|
||||||
def blankline()
|
|
||||||
puts("<p>")
|
|
||||||
end
|
|
||||||
|
|
||||||
def break_to_newline
|
|
||||||
puts("<br>")
|
|
||||||
end
|
|
||||||
|
|
||||||
def display_heading(text, level, indent)
|
|
||||||
level = 4 if level > 4
|
|
||||||
tag("h#{level}") { text }
|
|
||||||
puts
|
|
||||||
end
|
|
||||||
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
def display_list(list)
|
|
||||||
|
|
||||||
case list.type
|
|
||||||
when SM::ListBase::BULLET
|
|
||||||
list_type = "ul"
|
|
||||||
prefixer = proc { |ignored| "<li>" }
|
|
||||||
|
|
||||||
when SM::ListBase::NUMBER,
|
|
||||||
SM::ListBase::UPPERALPHA,
|
|
||||||
SM::ListBase::LOWERALPHA
|
|
||||||
list_type = "ol"
|
|
||||||
prefixer = proc { |ignored| "<li>" }
|
|
||||||
|
|
||||||
when SM::ListBase::LABELED
|
|
||||||
list_type = "dl"
|
|
||||||
prefixer = proc do |li|
|
|
||||||
"<dt><b>" + escape(li.label) + "</b><dd>"
|
|
||||||
end
|
|
||||||
|
|
||||||
when SM::ListBase::NOTE
|
|
||||||
list_type = "table"
|
|
||||||
prefixer = proc do |li|
|
|
||||||
%{<tr valign="top"><td>#{li.label.gsub(/ /, ' ')}</td><td>}
|
|
||||||
end
|
|
||||||
else
|
|
||||||
fail "unknown list type"
|
|
||||||
end
|
|
||||||
|
|
||||||
print "<#{list_type}>"
|
|
||||||
list.contents.each do |item|
|
|
||||||
if item.kind_of? SM::Flow::LI
|
|
||||||
prefix = prefixer.call(item)
|
|
||||||
print prefix
|
|
||||||
display_flow_item(item, prefix)
|
|
||||||
else
|
|
||||||
display_flow_item(item)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
print "</#{list_type}>"
|
|
||||||
end
|
|
||||||
|
|
||||||
def display_verbatim_flow_item(item, prefix=@indent)
|
|
||||||
print("<pre>")
|
|
||||||
item.body.split(/\n/).each do |line|
|
|
||||||
puts conv_html(line)
|
|
||||||
end
|
|
||||||
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
|
|
||||||
print str
|
|
||||||
end
|
|
||||||
|
|
||||||
def tag(code)
|
|
||||||
print("<#{code}>")
|
|
||||||
print(yield)
|
|
||||||
print("</#{code}>")
|
|
||||||
end
|
|
||||||
|
|
||||||
def escape(str)
|
|
||||||
str.
|
|
||||||
gsub(/&/n, '&').
|
|
||||||
gsub(/\"/n, '"').
|
|
||||||
gsub(/>/n, '>').
|
|
||||||
gsub(/</n, '<')
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
##################################################
|
|
||||||
|
|
||||||
# This formatter reduces extra lines for a simpler output.
|
|
||||||
# It improves way output looks for tools like IRC bots.
|
|
||||||
|
|
||||||
class SimpleFormatter < TextFormatter
|
|
||||||
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
# No extra blank lines
|
|
||||||
|
|
||||||
def blankline
|
|
||||||
end
|
|
||||||
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
# Display labels only, no lines
|
|
||||||
|
|
||||||
def draw_line(label=nil)
|
|
||||||
unless label.nil? then
|
|
||||||
bold_print(label)
|
|
||||||
puts
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
# Place heading level indicators inline with heading.
|
|
||||||
|
|
||||||
def display_heading(text, level, indent)
|
|
||||||
text = strip_attributes(text)
|
|
||||||
case level
|
|
||||||
when 1
|
|
||||||
puts "= " + text.upcase
|
|
||||||
when 2
|
|
||||||
puts "-- " + text
|
|
||||||
else
|
|
||||||
print indent, text, "\n"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# Finally, fill in the list of known formatters
|
|
||||||
|
|
||||||
class TextFormatter
|
|
||||||
|
|
||||||
FORMATTERS = {
|
|
||||||
"ansi" => AnsiFormatter,
|
|
||||||
"bs" => OverstrikeFormatter,
|
|
||||||
"html" => HtmlFormatter,
|
|
||||||
"plain" => TextFormatter,
|
|
||||||
"simple" => SimpleFormatter,
|
|
||||||
}
|
|
||||||
|
|
||||||
def TextFormatter.list
|
|
||||||
FORMATTERS.keys.sort.join(", ")
|
|
||||||
end
|
|
||||||
|
|
||||||
def TextFormatter.for(name)
|
|
||||||
FORMATTERS[name.downcase]
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
|
@ -1,97 +0,0 @@
|
||||||
module 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 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
|
|
||||||
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)}
|
|
||||||
|
|
||||||
require 'rubygems' unless defined?(Gem) 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| RI::Paths::PATH << dir }
|
|
||||||
|
|
||||||
# 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 << RI::Paths::SYSDIR if use_system
|
|
||||||
path << RI::Paths::SITEDIR if use_site
|
|
||||||
path << RI::Paths::HOMEDIR if use_home
|
|
||||||
path << RI::Paths::GEMDIRS if use_gems
|
|
||||||
|
|
||||||
return path.flatten.compact
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,100 +0,0 @@
|
||||||
require 'rdoc/ri/ri_descriptions'
|
|
||||||
require 'rdoc/ri/ri_writer'
|
|
||||||
require 'rdoc/markup/simple_markup/to_flow'
|
|
||||||
|
|
||||||
module RI
|
|
||||||
class RiReader
|
|
||||||
|
|
||||||
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
|
|
||||||
end
|
|
|
@ -1,62 +0,0 @@
|
||||||
require 'fileutils'
|
|
||||||
|
|
||||||
module RI
|
|
||||||
class RiWriter
|
|
||||||
|
|
||||||
def RiWriter.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 RiWriter.internal_to_external(name)
|
|
||||||
name.gsub(/\W/) { "%%%02x" % $&[0].ord }
|
|
||||||
end
|
|
||||||
|
|
||||||
# And the reverse operation
|
|
||||||
def RiWriter.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 = RiWriter.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 = RiWriter.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
|
|
||||||
end
|
|
|
@ -1,29 +1,34 @@
|
||||||
######################################################################
|
require 'rdoc/ri'
|
||||||
|
|
||||||
class RiError < Exception; end
|
class RDoc::RI::Error < RuntimeError; end
|
||||||
#
|
|
||||||
|
##
|
||||||
# Break argument into its constituent class or module names, an
|
# Break argument into its constituent class or module names, an
|
||||||
# optional method type, and a method name
|
# optional method type, and a method name
|
||||||
|
|
||||||
class NameDescriptor
|
class RDoc::RI::NameDescriptor
|
||||||
|
|
||||||
attr_reader :class_names
|
attr_reader :class_names
|
||||||
attr_reader :method_name
|
attr_reader :method_name
|
||||||
|
|
||||||
|
##
|
||||||
# true and false have the obvious meaning. nil means we don't care
|
# true and false have the obvious meaning. nil means we don't care
|
||||||
|
|
||||||
attr_reader :is_class_method
|
attr_reader :is_class_method
|
||||||
|
|
||||||
# arg may be
|
##
|
||||||
# 1. a class or module name (optionally qualified with other class
|
# +arg+ may be
|
||||||
# or module names (Kernel, File::Stat etc)
|
#
|
||||||
# 2. a method name
|
# 1. A class or module name (optionally qualified with other class or module
|
||||||
# 3. a method name qualified by a optionally fully qualified class
|
# names (Kernel, File::Stat etc)
|
||||||
# or module name
|
# 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,
|
# We're fairly casual about delimiters: folks can say Kernel::puts,
|
||||||
# Kernel.puts, or Kernel\#puts for example. There's one exception:
|
# Kernel.puts, or Kernel\#puts for example. There's one exception: if you
|
||||||
# if you say IO::read, we look for a class method, but if you
|
# say IO::read, we look for a class method, but if you say IO.read, we look
|
||||||
# say IO.read, we look for an instance method
|
# for an instance method
|
||||||
|
|
||||||
def initialize(arg)
|
def initialize(arg)
|
||||||
@class_names = []
|
@class_names = []
|
||||||
|
@ -47,8 +52,7 @@ class NameDescriptor
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Now must have a single token, the method name, or an empty
|
# Now must have a single token, the method name, or an empty array
|
||||||
# array
|
|
||||||
unless tokens.empty?
|
unless tokens.empty?
|
||||||
@method_name = tokens.shift
|
@method_name = tokens.shift
|
||||||
# We may now have a trailing !, ?, or = to roll into
|
# We may now have a trailing !, ?, or = to roll into
|
||||||
|
@ -66,10 +70,12 @@ class NameDescriptor
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Return the full class name (with '::' between the components)
|
# Return the full class name (with '::' between the components) or "" if
|
||||||
# or "" if there's no class name
|
# there's no class name
|
||||||
|
|
||||||
def full_class_name
|
def full_class_name
|
||||||
@class_names.join("::")
|
@class_names.join("::")
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
64
lib/rdoc/ri/writer.rb
Normal file
64
lib/rdoc/ri/writer.rb
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
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)
|
||||||
|
name.gsub(/\W/) { "%%%02x" % $&[0].ord }
|
||||||
|
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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue