rdoc 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rdoc might be problematic. Click here for more details.
- data/History.txt +13 -0
- data/Manifest.txt +61 -0
- data/README.txt +34 -0
- data/Rakefile +10 -0
- data/bin/rdoc +22 -0
- data/bin/ri +6 -0
- data/lib/rdoc.rb +277 -0
- data/lib/rdoc/code_objects.rb +776 -0
- data/lib/rdoc/diagram.rb +338 -0
- data/lib/rdoc/dot.rb +249 -0
- data/lib/rdoc/generator.rb +1048 -0
- data/lib/rdoc/generator/chm.rb +113 -0
- data/lib/rdoc/generator/chm/chm.rb +98 -0
- data/lib/rdoc/generator/html.rb +370 -0
- data/lib/rdoc/generator/html/hefss.rb +414 -0
- data/lib/rdoc/generator/html/html.rb +704 -0
- data/lib/rdoc/generator/html/kilmer.rb +418 -0
- data/lib/rdoc/generator/html/one_page_html.rb +121 -0
- data/lib/rdoc/generator/ri.rb +229 -0
- data/lib/rdoc/generator/xml.rb +120 -0
- data/lib/rdoc/generator/xml/rdf.rb +113 -0
- data/lib/rdoc/generator/xml/xml.rb +111 -0
- data/lib/rdoc/markup.rb +473 -0
- data/lib/rdoc/markup/attribute_manager.rb +274 -0
- data/lib/rdoc/markup/formatter.rb +14 -0
- data/lib/rdoc/markup/fragments.rb +337 -0
- data/lib/rdoc/markup/inline.rb +101 -0
- data/lib/rdoc/markup/lines.rb +152 -0
- data/lib/rdoc/markup/preprocess.rb +71 -0
- data/lib/rdoc/markup/to_flow.rb +185 -0
- data/lib/rdoc/markup/to_html.rb +353 -0
- data/lib/rdoc/markup/to_html_crossref.rb +86 -0
- data/lib/rdoc/markup/to_latex.rb +328 -0
- data/lib/rdoc/markup/to_test.rb +50 -0
- data/lib/rdoc/options.rb +616 -0
- data/lib/rdoc/parsers/parse_c.rb +775 -0
- data/lib/rdoc/parsers/parse_f95.rb +1841 -0
- data/lib/rdoc/parsers/parse_rb.rb +2584 -0
- data/lib/rdoc/parsers/parse_simple.rb +40 -0
- data/lib/rdoc/parsers/parserfactory.rb +99 -0
- data/lib/rdoc/rdoc.rb +277 -0
- data/lib/rdoc/ri.rb +4 -0
- data/lib/rdoc/ri/cache.rb +188 -0
- data/lib/rdoc/ri/descriptions.rb +150 -0
- data/lib/rdoc/ri/display.rb +274 -0
- data/lib/rdoc/ri/driver.rb +452 -0
- data/lib/rdoc/ri/formatter.rb +616 -0
- data/lib/rdoc/ri/paths.rb +102 -0
- data/lib/rdoc/ri/reader.rb +106 -0
- data/lib/rdoc/ri/util.rb +81 -0
- data/lib/rdoc/ri/writer.rb +68 -0
- data/lib/rdoc/stats.rb +25 -0
- data/lib/rdoc/template.rb +64 -0
- data/lib/rdoc/tokenstream.rb +33 -0
- data/test/test_rdoc_c_parser.rb +261 -0
- data/test/test_rdoc_markup.rb +613 -0
- data/test/test_rdoc_markup_attribute_manager.rb +224 -0
- data/test/test_rdoc_ri_attribute_formatter.rb +42 -0
- data/test/test_rdoc_ri_default_display.rb +295 -0
- data/test/test_rdoc_ri_formatter.rb +318 -0
- data/test/test_rdoc_ri_overstrike_formatter.rb +69 -0
- metadata +134 -0
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'rdoc'
|
2
|
+
require 'rdoc/code_objects'
|
3
|
+
require 'rdoc/markup/preprocess'
|
4
|
+
|
5
|
+
##
|
6
|
+
# Parse a non-source file. We basically take the whole thing as one big
|
7
|
+
# comment. If the first character in the file is '#', we strip leading pound
|
8
|
+
# signs.
|
9
|
+
|
10
|
+
class RDoc::SimpleParser
|
11
|
+
|
12
|
+
##
|
13
|
+
# Prepare to parse a plain file
|
14
|
+
|
15
|
+
def initialize(top_level, file_name, body, options, stats)
|
16
|
+
preprocess = RDoc::Markup::PreProcess.new(file_name, options.rdoc_include)
|
17
|
+
|
18
|
+
preprocess.handle(body) do |directive, param|
|
19
|
+
warn "Unrecognized directive '#{directive}' in #{file_name}"
|
20
|
+
end
|
21
|
+
|
22
|
+
@body = body
|
23
|
+
@options = options
|
24
|
+
@top_level = top_level
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# Extract the file contents and attach them to the toplevel as a comment
|
29
|
+
|
30
|
+
def scan
|
31
|
+
@top_level.comment = remove_private_comments(@body)
|
32
|
+
@top_level
|
33
|
+
end
|
34
|
+
|
35
|
+
def remove_private_comments(comment)
|
36
|
+
comment.gsub(/^--[^-].*?^\+\+/m, '').sub(/^--.*/m, '')
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require "rdoc/parsers/parse_simple"
|
2
|
+
|
3
|
+
module RDoc
|
4
|
+
|
5
|
+
# A parser is simple a class that implements
|
6
|
+
#
|
7
|
+
# #initialize(file_name, body, options)
|
8
|
+
#
|
9
|
+
# and
|
10
|
+
#
|
11
|
+
# #scan
|
12
|
+
#
|
13
|
+
# The initialize method takes a file name to be used, the body of the
|
14
|
+
# file, and an RDoc::Options object. The scan method is then called
|
15
|
+
# to return an appropriately parsed TopLevel code object.
|
16
|
+
#
|
17
|
+
# The ParseFactory is used to redirect to the correct parser given a filename
|
18
|
+
# extension. This magic works because individual parsers have to register
|
19
|
+
# themselves with us as they are loaded in. The do this using the following
|
20
|
+
# incantation
|
21
|
+
#
|
22
|
+
#
|
23
|
+
# require "rdoc/parsers/parsefactory"
|
24
|
+
#
|
25
|
+
# module RDoc
|
26
|
+
#
|
27
|
+
# class XyzParser
|
28
|
+
# extend ParseFactory <<<<
|
29
|
+
# parse_files_matching /\.xyz$/ <<<<
|
30
|
+
#
|
31
|
+
# def initialize(file_name, body, options)
|
32
|
+
# ...
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# def scan
|
36
|
+
# ...
|
37
|
+
# end
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# Just to make life interesting, if we suspect a plain text file, we
|
42
|
+
# also look for a shebang line just in case it's a potential
|
43
|
+
# shell script
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
module ParserFactory
|
48
|
+
|
49
|
+
@@parsers = []
|
50
|
+
|
51
|
+
Parsers = Struct.new(:regexp, :parser)
|
52
|
+
|
53
|
+
# Record the fact that a particular class parses files that
|
54
|
+
# match a given extension
|
55
|
+
|
56
|
+
def parse_files_matching(regexp)
|
57
|
+
@@parsers.unshift Parsers.new(regexp, self)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Return a parser that can handle a particular extension
|
61
|
+
|
62
|
+
def ParserFactory.can_parse(file_name)
|
63
|
+
@@parsers.find {|p| p.regexp.match(file_name) }
|
64
|
+
end
|
65
|
+
|
66
|
+
# Alias an extension to another extension. After this call,
|
67
|
+
# files ending "new_ext" will be parsed using the same parser
|
68
|
+
# as "old_ext"
|
69
|
+
|
70
|
+
def ParserFactory.alias_extension(old_ext, new_ext)
|
71
|
+
parser = ParserFactory.can_parse("xxx.#{old_ext}")
|
72
|
+
return false unless parser
|
73
|
+
@@parsers.unshift Parsers.new(Regexp.new("\\.#{new_ext}$"), parser.parser)
|
74
|
+
true
|
75
|
+
end
|
76
|
+
|
77
|
+
# Find the correct parser for a particular file name. Return a
|
78
|
+
# SimpleParser for ones that we don't know
|
79
|
+
|
80
|
+
def ParserFactory.parser_for(top_level, file_name, body, options, stats)
|
81
|
+
# If no extension, look for shebang
|
82
|
+
if file_name !~ /\.\w+$/ && body =~ %r{\A#!(.+)}
|
83
|
+
shebang = $1
|
84
|
+
case shebang
|
85
|
+
when %r{env\s+ruby}, %r{/ruby}
|
86
|
+
file_name = "dummy.rb"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
parser_description = can_parse(file_name)
|
90
|
+
if parser_description
|
91
|
+
parser = parser_description.parser
|
92
|
+
else
|
93
|
+
parser = SimpleParser
|
94
|
+
end
|
95
|
+
|
96
|
+
parser.new(top_level, file_name, body, options, stats)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/lib/rdoc/rdoc.rb
ADDED
@@ -0,0 +1,277 @@
|
|
1
|
+
require 'rdoc'
|
2
|
+
|
3
|
+
require 'rdoc/parsers/parse_rb.rb'
|
4
|
+
require 'rdoc/parsers/parse_c.rb'
|
5
|
+
require 'rdoc/parsers/parse_f95.rb'
|
6
|
+
require 'rdoc/parsers/parse_simple.rb'
|
7
|
+
|
8
|
+
require 'rdoc/stats'
|
9
|
+
require 'rdoc/options'
|
10
|
+
|
11
|
+
require 'rdoc/diagram'
|
12
|
+
|
13
|
+
require 'find'
|
14
|
+
require 'fileutils'
|
15
|
+
require 'time'
|
16
|
+
|
17
|
+
module RDoc
|
18
|
+
|
19
|
+
##
|
20
|
+
# Encapsulate the production of rdoc documentation. Basically
|
21
|
+
# you can use this as you would invoke rdoc from the command
|
22
|
+
# line:
|
23
|
+
#
|
24
|
+
# rdoc = RDoc::RDoc.new
|
25
|
+
# rdoc.document(args)
|
26
|
+
#
|
27
|
+
# where _args_ is an array of strings, each corresponding to
|
28
|
+
# an argument you'd give rdoc on the command line. See rdoc/rdoc.rb
|
29
|
+
# for details.
|
30
|
+
|
31
|
+
class RDoc
|
32
|
+
|
33
|
+
Generator = Struct.new(:file_name, :class_name, :key)
|
34
|
+
|
35
|
+
##
|
36
|
+
# This is the list of output generator that we support
|
37
|
+
|
38
|
+
GENERATORS = {}
|
39
|
+
|
40
|
+
$LOAD_PATH.collect do |d|
|
41
|
+
File.expand_path d
|
42
|
+
end.find_all do |d|
|
43
|
+
File.directory? "#{d}/rdoc/generator"
|
44
|
+
end.each do |dir|
|
45
|
+
Dir.entries("#{dir}/rdoc/generator").each do |gen|
|
46
|
+
next unless /(\w+)\.rb$/ =~ gen
|
47
|
+
type = $1
|
48
|
+
unless GENERATORS.has_key? type
|
49
|
+
GENERATORS[type] = Generator.new("rdoc/generator/#{gen}",
|
50
|
+
"#{type.upcase}".intern,
|
51
|
+
type)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def initialize
|
57
|
+
@stats = Stats.new
|
58
|
+
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# Report an error message and exit
|
62
|
+
|
63
|
+
def error(msg)
|
64
|
+
raise ::RDoc::Error, msg
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# Create an output dir if it doesn't exist. If it does exist, but doesn't
|
69
|
+
# contain the flag file <tt>created.rid</tt> then we refuse to use it, as
|
70
|
+
# we may clobber some manually generated documentation
|
71
|
+
|
72
|
+
def setup_output_dir(op_dir, force)
|
73
|
+
flag_file = output_flag_file(op_dir)
|
74
|
+
if File.exist?(op_dir)
|
75
|
+
unless File.directory?(op_dir)
|
76
|
+
error "'#{op_dir}' exists, and is not a directory"
|
77
|
+
end
|
78
|
+
begin
|
79
|
+
created = File.read(flag_file)
|
80
|
+
rescue SystemCallError
|
81
|
+
error "\nDirectory #{op_dir} already exists, but it looks like it\n" +
|
82
|
+
"isn't an RDoc directory. Because RDoc doesn't want to risk\n" +
|
83
|
+
"destroying any of your existing files, you'll need to\n" +
|
84
|
+
"specify a different output directory name (using the\n" +
|
85
|
+
"--op <dir> option).\n\n"
|
86
|
+
else
|
87
|
+
last = (Time.parse(created) unless force rescue nil)
|
88
|
+
end
|
89
|
+
else
|
90
|
+
FileUtils.mkdir_p(op_dir)
|
91
|
+
end
|
92
|
+
last
|
93
|
+
end
|
94
|
+
|
95
|
+
##
|
96
|
+
# Update the flag file in an output directory.
|
97
|
+
|
98
|
+
def update_output_dir(op_dir, time)
|
99
|
+
File.open(output_flag_file(op_dir), "w") {|f| f.puts time.rfc2822 }
|
100
|
+
end
|
101
|
+
|
102
|
+
##
|
103
|
+
# Return the path name of the flag file in an output directory.
|
104
|
+
|
105
|
+
def output_flag_file(op_dir)
|
106
|
+
File.join(op_dir, "created.rid")
|
107
|
+
end
|
108
|
+
|
109
|
+
##
|
110
|
+
# The .document file contains a list of file and directory name patterns,
|
111
|
+
# representing candidates for documentation. It may also contain comments
|
112
|
+
# (starting with '#')
|
113
|
+
|
114
|
+
def parse_dot_doc_file(in_dir, filename, options)
|
115
|
+
# read and strip comments
|
116
|
+
patterns = File.read(filename).gsub(/#.*/, '')
|
117
|
+
|
118
|
+
result = []
|
119
|
+
|
120
|
+
patterns.split.each do |patt|
|
121
|
+
candidates = Dir.glob(File.join(in_dir, patt))
|
122
|
+
result.concat(normalized_file_list(options, candidates))
|
123
|
+
end
|
124
|
+
result
|
125
|
+
end
|
126
|
+
|
127
|
+
##
|
128
|
+
# Given a list of files and directories, create a list of all the Ruby
|
129
|
+
# files they contain.
|
130
|
+
#
|
131
|
+
# If +force_doc+ is true we always add the given files, if false, only
|
132
|
+
# add files that we guarantee we can parse. It is true when looking at
|
133
|
+
# files given on the command line, false when recursing through
|
134
|
+
# subdirectories.
|
135
|
+
#
|
136
|
+
# The effect of this is that if you want a file with a non-standard
|
137
|
+
# extension parsed, you must name it explicity.
|
138
|
+
|
139
|
+
def normalized_file_list(options, relative_files, force_doc = false,
|
140
|
+
exclude_pattern = nil)
|
141
|
+
file_list = []
|
142
|
+
|
143
|
+
relative_files.each do |rel_file_name|
|
144
|
+
next if exclude_pattern && exclude_pattern =~ rel_file_name
|
145
|
+
stat = File.stat(rel_file_name)
|
146
|
+
case type = stat.ftype
|
147
|
+
when "file"
|
148
|
+
next if @last_created and stat.mtime < @last_created
|
149
|
+
file_list << rel_file_name.sub(/^\.\//, '') if force_doc || ParserFactory.can_parse(rel_file_name)
|
150
|
+
when "directory"
|
151
|
+
next if rel_file_name == "CVS" || rel_file_name == ".svn"
|
152
|
+
dot_doc = File.join(rel_file_name, DOT_DOC_FILENAME)
|
153
|
+
if File.file?(dot_doc)
|
154
|
+
file_list.concat(parse_dot_doc_file(rel_file_name, dot_doc, options))
|
155
|
+
else
|
156
|
+
file_list.concat(list_files_in_directory(rel_file_name, options))
|
157
|
+
end
|
158
|
+
else
|
159
|
+
raise RDoc::Error, "I can't deal with a #{type} #{rel_file_name}"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
file_list
|
164
|
+
end
|
165
|
+
|
166
|
+
##
|
167
|
+
# Return a list of the files to be processed in a directory. We know that
|
168
|
+
# this directory doesn't have a .document file, so we're looking for real
|
169
|
+
# files. However we may well contain subdirectories which must be tested
|
170
|
+
# for .document files.
|
171
|
+
|
172
|
+
def list_files_in_directory(dir, options)
|
173
|
+
files = Dir.glob File.join(dir, "*")
|
174
|
+
|
175
|
+
normalized_file_list options, files, false, options.exclude
|
176
|
+
end
|
177
|
+
|
178
|
+
##
|
179
|
+
# Parse each file on the command line, recursively entering directories.
|
180
|
+
|
181
|
+
def parse_files(options)
|
182
|
+
files = options.files
|
183
|
+
files = ["."] if files.empty?
|
184
|
+
|
185
|
+
file_list = normalized_file_list(options, files, true)
|
186
|
+
|
187
|
+
return [] if file_list.empty?
|
188
|
+
|
189
|
+
file_info = []
|
190
|
+
width = file_list.map { |name| name.length }.max + 1
|
191
|
+
|
192
|
+
file_list.each do |fn|
|
193
|
+
$stderr.printf("\n%*s: ", width, fn) unless options.quiet
|
194
|
+
|
195
|
+
content = if RUBY_VERSION >= '1.9' then
|
196
|
+
File.open(fn, "r:ascii-8bit") { |f| f.read }
|
197
|
+
else
|
198
|
+
File.read fn
|
199
|
+
end
|
200
|
+
|
201
|
+
if /coding:\s*(\S+)/ =~ content[/\A(?:.*\n){0,2}/]
|
202
|
+
if enc = Encoding.find($1)
|
203
|
+
content.force_encoding(enc)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
top_level = TopLevel.new(fn)
|
208
|
+
parser = ParserFactory.parser_for(top_level, fn, content, options, @stats)
|
209
|
+
file_info << parser.scan
|
210
|
+
@stats.num_files += 1
|
211
|
+
end
|
212
|
+
|
213
|
+
file_info
|
214
|
+
end
|
215
|
+
|
216
|
+
##
|
217
|
+
# Format up one or more files according to the given arguments.
|
218
|
+
#
|
219
|
+
# For simplicity, _argv_ is an array of strings, equivalent to the strings
|
220
|
+
# that would be passed on the command line. (This isn't a coincidence, as
|
221
|
+
# we _do_ pass in ARGV when running interactively). For a list of options,
|
222
|
+
# see rdoc/rdoc.rb. By default, output will be stored in a directory
|
223
|
+
# called +doc+ below the current directory, so make sure you're somewhere
|
224
|
+
# writable before invoking.
|
225
|
+
#
|
226
|
+
# Throws: RDoc::Error on error
|
227
|
+
|
228
|
+
def document(argv)
|
229
|
+
TopLevel::reset
|
230
|
+
|
231
|
+
@options = Options.new GENERATORS
|
232
|
+
@options.parse argv
|
233
|
+
|
234
|
+
@last_created = nil
|
235
|
+
|
236
|
+
unless @options.all_one_file then
|
237
|
+
@last_created = setup_output_dir @options.op_dir, @options.force_update
|
238
|
+
end
|
239
|
+
|
240
|
+
start_time = Time.now
|
241
|
+
|
242
|
+
file_info = parse_files @options
|
243
|
+
|
244
|
+
if file_info.empty?
|
245
|
+
$stderr.puts "\nNo newer files." unless @options.quiet
|
246
|
+
else
|
247
|
+
gen = @options.generator
|
248
|
+
|
249
|
+
$stderr.puts "\nGenerating #{gen.key.upcase}..." unless @options.quiet
|
250
|
+
|
251
|
+
require gen.file_name
|
252
|
+
|
253
|
+
gen_class = ::RDoc::Generator.const_get gen.class_name
|
254
|
+
gen = gen_class.for @options
|
255
|
+
|
256
|
+
pwd = Dir.pwd
|
257
|
+
|
258
|
+
Dir.chdir @options.op_dir unless @options.all_one_file
|
259
|
+
|
260
|
+
begin
|
261
|
+
Diagram.new(file_info, @options).draw if @options.diagram
|
262
|
+
gen.generate(file_info)
|
263
|
+
update_output_dir(".", start_time)
|
264
|
+
ensure
|
265
|
+
Dir.chdir(pwd)
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
unless @options.quiet
|
270
|
+
puts
|
271
|
+
@stats.print
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
end
|
277
|
+
|
data/lib/rdoc/ri.rb
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
require 'rdoc/ri'
|
2
|
+
|
3
|
+
class RDoc::RI::ClassEntry
|
4
|
+
|
5
|
+
attr_reader :name
|
6
|
+
attr_reader :path_names
|
7
|
+
|
8
|
+
def initialize(path_name, name, in_class)
|
9
|
+
@path_names = [ path_name ]
|
10
|
+
@name = name
|
11
|
+
@in_class = in_class
|
12
|
+
@class_methods = []
|
13
|
+
@instance_methods = []
|
14
|
+
@inferior_classes = []
|
15
|
+
end
|
16
|
+
|
17
|
+
# We found this class in more tha one place, so add
|
18
|
+
# in the name from there.
|
19
|
+
def add_path(path)
|
20
|
+
@path_names << path
|
21
|
+
end
|
22
|
+
|
23
|
+
# read in our methods and any classes
|
24
|
+
# and modules in our namespace. Methods are
|
25
|
+
# stored in files called name-c|i.yaml,
|
26
|
+
# where the 'name' portion is the external
|
27
|
+
# form of the method name and the c|i is a class|instance
|
28
|
+
# flag
|
29
|
+
|
30
|
+
def load_from(dir)
|
31
|
+
Dir.foreach(dir) do |name|
|
32
|
+
next if name =~ /^\./
|
33
|
+
|
34
|
+
# convert from external to internal form, and
|
35
|
+
# extract the instance/class flag
|
36
|
+
|
37
|
+
if name =~ /^(.*?)-(c|i).yaml$/
|
38
|
+
external_name = $1
|
39
|
+
is_class_method = $2 == "c"
|
40
|
+
internal_name = RiWriter.external_to_internal(external_name)
|
41
|
+
list = is_class_method ? @class_methods : @instance_methods
|
42
|
+
path = File.join(dir, name)
|
43
|
+
list << MethodEntry.new(path, internal_name, is_class_method, self)
|
44
|
+
else
|
45
|
+
full_name = File.join(dir, name)
|
46
|
+
if File.directory?(full_name)
|
47
|
+
inf_class = @inferior_classes.find {|c| c.name == name }
|
48
|
+
if inf_class
|
49
|
+
inf_class.add_path(full_name)
|
50
|
+
else
|
51
|
+
inf_class = ClassEntry.new(full_name, name, self)
|
52
|
+
@inferior_classes << inf_class
|
53
|
+
end
|
54
|
+
inf_class.load_from(full_name)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Return a list of any classes or modules that we contain
|
61
|
+
# that match a given string
|
62
|
+
|
63
|
+
def contained_modules_matching(name)
|
64
|
+
@inferior_classes.find_all {|c| c.name[name]}
|
65
|
+
end
|
66
|
+
|
67
|
+
def classes_and_modules
|
68
|
+
@inferior_classes
|
69
|
+
end
|
70
|
+
|
71
|
+
# Return an exact match to a particular name
|
72
|
+
def contained_class_named(name)
|
73
|
+
@inferior_classes.find {|c| c.name == name}
|
74
|
+
end
|
75
|
+
|
76
|
+
# return the list of local methods matching name
|
77
|
+
# We're split into two because we need distinct behavior
|
78
|
+
# when called from the _toplevel_
|
79
|
+
def methods_matching(name, is_class_method)
|
80
|
+
local_methods_matching(name, is_class_method)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Find methods matching 'name' in ourselves and in
|
84
|
+
# any classes we contain
|
85
|
+
def recursively_find_methods_matching(name, is_class_method)
|
86
|
+
res = local_methods_matching(name, is_class_method)
|
87
|
+
@inferior_classes.each do |c|
|
88
|
+
res.concat(c.recursively_find_methods_matching(name, is_class_method))
|
89
|
+
end
|
90
|
+
res
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
# Return our full name
|
95
|
+
def full_name
|
96
|
+
res = @in_class.full_name
|
97
|
+
res << "::" unless res.empty?
|
98
|
+
res << @name
|
99
|
+
end
|
100
|
+
|
101
|
+
# Return a list of all out method names
|
102
|
+
def all_method_names
|
103
|
+
res = @class_methods.map {|m| m.full_name }
|
104
|
+
@instance_methods.each {|m| res << m.full_name}
|
105
|
+
res
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
# Return a list of all our methods matching a given string.
|
111
|
+
# Is +is_class_methods+ if 'nil', we don't care if the method
|
112
|
+
# is a class method or not, otherwise we only return
|
113
|
+
# those methods that match
|
114
|
+
def local_methods_matching(name, is_class_method)
|
115
|
+
|
116
|
+
list = case is_class_method
|
117
|
+
when nil then @class_methods + @instance_methods
|
118
|
+
when true then @class_methods
|
119
|
+
when false then @instance_methods
|
120
|
+
else fail "Unknown is_class_method: #{is_class_method.inspect}"
|
121
|
+
end
|
122
|
+
|
123
|
+
list.find_all {|m| m.name; m.name[name]}
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
##
|
128
|
+
# A TopLevelEntry is like a class entry, but when asked to search for methods
|
129
|
+
# searches all classes, not just itself
|
130
|
+
|
131
|
+
class RDoc::RI::TopLevelEntry < RDoc::RI::ClassEntry
|
132
|
+
def methods_matching(name, is_class_method)
|
133
|
+
res = recursively_find_methods_matching(name, is_class_method)
|
134
|
+
end
|
135
|
+
|
136
|
+
def full_name
|
137
|
+
""
|
138
|
+
end
|
139
|
+
|
140
|
+
def module_named(name)
|
141
|
+
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
|
146
|
+
class RDoc::RI::MethodEntry
|
147
|
+
attr_reader :name
|
148
|
+
attr_reader :path_name
|
149
|
+
|
150
|
+
def initialize(path_name, name, is_class_method, in_class)
|
151
|
+
@path_name = path_name
|
152
|
+
@name = name
|
153
|
+
@is_class_method = is_class_method
|
154
|
+
@in_class = in_class
|
155
|
+
end
|
156
|
+
|
157
|
+
def full_name
|
158
|
+
res = @in_class.full_name
|
159
|
+
unless res.empty?
|
160
|
+
if @is_class_method
|
161
|
+
res << "::"
|
162
|
+
else
|
163
|
+
res << "#"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
res << @name
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
##
|
171
|
+
# We represent everything know about all 'ri' files accessible to this program
|
172
|
+
|
173
|
+
class RDoc::RI::Cache
|
174
|
+
|
175
|
+
attr_reader :toplevel
|
176
|
+
|
177
|
+
def initialize(dirs)
|
178
|
+
# At the top level we have a dummy module holding the
|
179
|
+
# overall namespace
|
180
|
+
@toplevel = RDoc::RI::TopLevelEntry.new('', '::', nil)
|
181
|
+
|
182
|
+
dirs.each do |dir|
|
183
|
+
@toplevel.load_from(dir)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
|