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,150 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'rdoc/markup/fragments'
|
3
|
+
require 'rdoc/ri'
|
4
|
+
|
5
|
+
#--
|
6
|
+
# Descriptions are created by RDoc (in ri_generator) and written out in
|
7
|
+
# serialized form into the documentation tree. ri then reads these to generate
|
8
|
+
# the documentation
|
9
|
+
#++
|
10
|
+
|
11
|
+
class RDoc::RI::NamedThing
|
12
|
+
attr_reader :name
|
13
|
+
def initialize(name)
|
14
|
+
@name = name
|
15
|
+
end
|
16
|
+
|
17
|
+
def <=>(other)
|
18
|
+
@name <=> other.name
|
19
|
+
end
|
20
|
+
|
21
|
+
def hash
|
22
|
+
@name.hash
|
23
|
+
end
|
24
|
+
|
25
|
+
def eql?(other)
|
26
|
+
@name.eql?(other)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class RDoc::RI::AliasName < RDoc::RI::NamedThing; end
|
31
|
+
|
32
|
+
class RDoc::RI::Attribute < RDoc::RI::NamedThing
|
33
|
+
attr_reader :rw, :comment
|
34
|
+
|
35
|
+
def initialize(name, rw, comment)
|
36
|
+
super(name)
|
37
|
+
@rw = rw
|
38
|
+
@comment = comment
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class RDoc::RI::Constant < RDoc::RI::NamedThing
|
43
|
+
attr_reader :value, :comment
|
44
|
+
|
45
|
+
def initialize(name, value, comment)
|
46
|
+
super(name)
|
47
|
+
@value = value
|
48
|
+
@comment = comment
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class RDoc::RI::IncludedModule < RDoc::RI::NamedThing; end
|
53
|
+
|
54
|
+
class RDoc::RI::MethodSummary < RDoc::RI::NamedThing
|
55
|
+
def initialize(name="")
|
56
|
+
super
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class RDoc::RI::Description
|
61
|
+
attr_accessor :name
|
62
|
+
attr_accessor :full_name
|
63
|
+
attr_accessor :comment
|
64
|
+
|
65
|
+
def serialize
|
66
|
+
self.to_yaml
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.deserialize(from)
|
70
|
+
YAML.load(from)
|
71
|
+
end
|
72
|
+
|
73
|
+
def <=>(other)
|
74
|
+
@name <=> other.name
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class RDoc::RI::ModuleDescription < RDoc::RI::Description
|
79
|
+
|
80
|
+
attr_accessor :class_methods
|
81
|
+
attr_accessor :instance_methods
|
82
|
+
attr_accessor :attributes
|
83
|
+
attr_accessor :constants
|
84
|
+
attr_accessor :includes
|
85
|
+
|
86
|
+
# merge in another class desscription into this one
|
87
|
+
def merge_in(old)
|
88
|
+
merge(@class_methods, old.class_methods)
|
89
|
+
merge(@instance_methods, old.instance_methods)
|
90
|
+
merge(@attributes, old.attributes)
|
91
|
+
merge(@constants, old.constants)
|
92
|
+
merge(@includes, old.includes)
|
93
|
+
if @comment.nil? || @comment.empty?
|
94
|
+
@comment = old.comment
|
95
|
+
else
|
96
|
+
unless old.comment.nil? or old.comment.empty? then
|
97
|
+
@comment << RDoc::Markup::Flow::RULE.new
|
98
|
+
@comment.concat old.comment
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def display_name
|
104
|
+
"Module"
|
105
|
+
end
|
106
|
+
|
107
|
+
# the 'ClassDescription' subclass overrides this
|
108
|
+
# to format up the name of a parent
|
109
|
+
def superclass_string
|
110
|
+
nil
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def merge(into, from)
|
116
|
+
names = {}
|
117
|
+
into.each {|i| names[i.name] = i }
|
118
|
+
from.each {|i| names[i.name] = i }
|
119
|
+
into.replace(names.keys.sort.map {|n| names[n]})
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
class RDoc::RI::ClassDescription < RDoc::RI::ModuleDescription
|
124
|
+
attr_accessor :superclass
|
125
|
+
|
126
|
+
def display_name
|
127
|
+
"Class"
|
128
|
+
end
|
129
|
+
|
130
|
+
def superclass_string
|
131
|
+
if @superclass && @superclass != "Object"
|
132
|
+
@superclass
|
133
|
+
else
|
134
|
+
nil
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
class RDoc::RI::MethodDescription < RDoc::RI::Description
|
140
|
+
|
141
|
+
attr_accessor :is_class_method
|
142
|
+
attr_accessor :visibility
|
143
|
+
attr_accessor :block_params
|
144
|
+
attr_accessor :is_singleton
|
145
|
+
attr_accessor :aliases
|
146
|
+
attr_accessor :is_alias_for
|
147
|
+
attr_accessor :params
|
148
|
+
|
149
|
+
end
|
150
|
+
|
@@ -0,0 +1,274 @@
|
|
1
|
+
require 'rdoc/ri'
|
2
|
+
|
3
|
+
##
|
4
|
+
# This is a kind of 'flag' module. If you want to write your own 'ri' display
|
5
|
+
# module (perhaps because you're writing an IDE), you write a class which
|
6
|
+
# implements the various 'display' methods in RDoc::RI::DefaultDisplay, and
|
7
|
+
# include the RDoc::RI::Display module in that class.
|
8
|
+
#
|
9
|
+
# To access your class from the command line, you can do
|
10
|
+
#
|
11
|
+
# ruby -r <your source file> ../ri ....
|
12
|
+
|
13
|
+
module RDoc::RI::Display
|
14
|
+
|
15
|
+
@@display_class = nil
|
16
|
+
|
17
|
+
def self.append_features(display_class)
|
18
|
+
@@display_class = display_class
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.new(*args)
|
22
|
+
@@display_class.new(*args)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# A paging display module. Uses the RDoc::RI::Formatter class to do the actual
|
29
|
+
# presentation.
|
30
|
+
|
31
|
+
class RDoc::RI::DefaultDisplay
|
32
|
+
|
33
|
+
include RDoc::RI::Display
|
34
|
+
|
35
|
+
def initialize(formatter, width, use_stdout, output = $stdout)
|
36
|
+
@use_stdout = use_stdout
|
37
|
+
@formatter = formatter.new output, width, " "
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# Display information about +klass+. Fetches additional information from
|
42
|
+
# +ri_reader+ as necessary.
|
43
|
+
|
44
|
+
def display_class_info(klass, ri_reader)
|
45
|
+
page do
|
46
|
+
superclass = klass.superclass_string
|
47
|
+
|
48
|
+
if superclass
|
49
|
+
superclass = " < " + superclass
|
50
|
+
else
|
51
|
+
superclass = ""
|
52
|
+
end
|
53
|
+
|
54
|
+
@formatter.draw_line(klass.display_name + ": " +
|
55
|
+
klass.full_name + superclass)
|
56
|
+
|
57
|
+
display_flow(klass.comment)
|
58
|
+
@formatter.draw_line
|
59
|
+
|
60
|
+
unless klass.includes.empty?
|
61
|
+
@formatter.blankline
|
62
|
+
@formatter.display_heading("Includes:", 2, "")
|
63
|
+
incs = []
|
64
|
+
klass.includes.each do |inc|
|
65
|
+
inc_desc = ri_reader.find_class_by_name(inc.name)
|
66
|
+
if inc_desc
|
67
|
+
str = inc.name + "("
|
68
|
+
str << inc_desc.instance_methods.map{|m| m.name}.join(", ")
|
69
|
+
str << ")"
|
70
|
+
incs << str
|
71
|
+
else
|
72
|
+
incs << inc.name
|
73
|
+
end
|
74
|
+
end
|
75
|
+
@formatter.wrap(incs.sort.join(', '))
|
76
|
+
end
|
77
|
+
|
78
|
+
unless klass.constants.empty?
|
79
|
+
@formatter.blankline
|
80
|
+
@formatter.display_heading("Constants:", 2, "")
|
81
|
+
|
82
|
+
constants = klass.constants.sort_by { |constant| constant.name }
|
83
|
+
|
84
|
+
constants.each do |constant|
|
85
|
+
if constant.comment then
|
86
|
+
@formatter.wrap "#{constant.name}:"
|
87
|
+
|
88
|
+
@formatter.indent do
|
89
|
+
@formatter.display_flow constant.comment
|
90
|
+
end
|
91
|
+
else
|
92
|
+
@formatter.wrap constant.name
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class_data = [
|
98
|
+
:class_methods,
|
99
|
+
:class_method_extensions,
|
100
|
+
:instance_methods,
|
101
|
+
:instance_method_extensions,
|
102
|
+
]
|
103
|
+
|
104
|
+
class_data.each do |data_type|
|
105
|
+
data = klass.send data_type
|
106
|
+
|
107
|
+
unless data.empty? then
|
108
|
+
@formatter.blankline
|
109
|
+
|
110
|
+
heading = data_type.to_s.split('_').join(' ').capitalize << ':'
|
111
|
+
@formatter.display_heading heading, 2, ''
|
112
|
+
|
113
|
+
data = data.map { |item| item.name }.sort.join ', '
|
114
|
+
@formatter.wrap data
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
unless klass.attributes.empty? then
|
119
|
+
@formatter.blankline
|
120
|
+
|
121
|
+
@formatter.display_heading 'Attributes:', 2, ''
|
122
|
+
|
123
|
+
attributes = klass.attributes.sort_by { |attribute| attribute.name }
|
124
|
+
|
125
|
+
attributes.each do |attribute|
|
126
|
+
if attribute.comment then
|
127
|
+
@formatter.wrap "#{attribute.name} (#{attribute.rw}):"
|
128
|
+
@formatter.indent do
|
129
|
+
@formatter.display_flow attribute.comment
|
130
|
+
end
|
131
|
+
else
|
132
|
+
@formatter.wrap "#{attribute.name} (#{attribute.rw})"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
##
|
140
|
+
# Display an Array of RDoc::Markup::Flow objects, +flow+.
|
141
|
+
|
142
|
+
def display_flow(flow)
|
143
|
+
if flow and not flow.empty? then
|
144
|
+
@formatter.display_flow flow
|
145
|
+
else
|
146
|
+
@formatter.wrap '[no description]'
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
##
|
151
|
+
# Display information about +method+.
|
152
|
+
|
153
|
+
def display_method_info(method)
|
154
|
+
page do
|
155
|
+
@formatter.draw_line(method.full_name)
|
156
|
+
display_params(method)
|
157
|
+
|
158
|
+
@formatter.draw_line
|
159
|
+
display_flow(method.comment)
|
160
|
+
|
161
|
+
if method.aliases and not method.aliases.empty? then
|
162
|
+
@formatter.blankline
|
163
|
+
aka = "(also known as #{method.aliases.map { |a| a.name }.join(', ')})"
|
164
|
+
@formatter.wrap aka
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
##
|
170
|
+
# Display the list of +methods+.
|
171
|
+
|
172
|
+
def display_method_list(methods)
|
173
|
+
page do
|
174
|
+
@formatter.wrap "More than one method matched your request. You can refine your search by asking for information on one of:"
|
175
|
+
|
176
|
+
@formatter.blankline
|
177
|
+
|
178
|
+
@formatter.wrap methods.map { |m| m.full_name }.join(", ")
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
##
|
183
|
+
# Display the params for +method+.
|
184
|
+
|
185
|
+
def display_params(method)
|
186
|
+
params = method.params
|
187
|
+
|
188
|
+
if params[0,1] == "(" then
|
189
|
+
if method.is_singleton
|
190
|
+
params = method.full_name + params
|
191
|
+
else
|
192
|
+
params = method.name + params
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
params.split(/\n/).each do |param|
|
197
|
+
@formatter.wrap param
|
198
|
+
@formatter.break_to_newline
|
199
|
+
end
|
200
|
+
|
201
|
+
if method.source_path then
|
202
|
+
@formatter.blankline
|
203
|
+
@formatter.wrap("Extension from #{method.source_path}")
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
##
|
208
|
+
# List the classes in +classes+.
|
209
|
+
|
210
|
+
def list_known_classes(classes)
|
211
|
+
if classes.empty?
|
212
|
+
warn_no_database
|
213
|
+
else
|
214
|
+
page do
|
215
|
+
@formatter.draw_line "Known classes and modules"
|
216
|
+
@formatter.blankline
|
217
|
+
|
218
|
+
@formatter.wrap classes.sort.join(', ')
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
##
|
224
|
+
# Paginates output through a pager program.
|
225
|
+
|
226
|
+
def page
|
227
|
+
if pager = setup_pager then
|
228
|
+
begin
|
229
|
+
orig_output = @formatter.output
|
230
|
+
@formatter.output = pager
|
231
|
+
yield
|
232
|
+
ensure
|
233
|
+
@formatter.output = orig_output
|
234
|
+
pager.close
|
235
|
+
end
|
236
|
+
else
|
237
|
+
yield
|
238
|
+
end
|
239
|
+
rescue Errno::EPIPE
|
240
|
+
end
|
241
|
+
|
242
|
+
##
|
243
|
+
# Sets up a pager program to pass output through.
|
244
|
+
|
245
|
+
def setup_pager
|
246
|
+
unless @use_stdout then
|
247
|
+
for pager in [ ENV['PAGER'], "less", "more", 'pager' ].compact.uniq
|
248
|
+
return IO.popen(pager, "w") rescue nil
|
249
|
+
end
|
250
|
+
@use_stdout = true
|
251
|
+
nil
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
##
|
256
|
+
# Displays a message that describes how to build RI data.
|
257
|
+
|
258
|
+
def warn_no_database
|
259
|
+
output = @formatter.output
|
260
|
+
|
261
|
+
output.puts "No ri data found"
|
262
|
+
output.puts
|
263
|
+
output.puts "If you've installed Ruby yourself, you need to generate documentation using:"
|
264
|
+
output.puts
|
265
|
+
output.puts " make install-doc"
|
266
|
+
output.puts
|
267
|
+
output.puts "from the same place you ran `make` to build ruby."
|
268
|
+
output.puts
|
269
|
+
output.puts "If you installed Ruby from a packaging system, then you may need to"
|
270
|
+
output.puts "install an additional package, or ask the packager to enable ri generation."
|
271
|
+
end
|
272
|
+
|
273
|
+
end
|
274
|
+
|
@@ -0,0 +1,452 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
require 'rdoc/ri'
|
5
|
+
require 'rdoc/ri/paths'
|
6
|
+
require 'rdoc/ri/formatter'
|
7
|
+
require 'rdoc/ri/display'
|
8
|
+
require 'fileutils'
|
9
|
+
require 'rdoc/markup'
|
10
|
+
require 'rdoc/markup/to_flow'
|
11
|
+
|
12
|
+
class RDoc::RI::Driver
|
13
|
+
|
14
|
+
def self.process_args(argv)
|
15
|
+
options = {}
|
16
|
+
options[:use_stdout] = !$stdout.tty?
|
17
|
+
options[:width] = 72
|
18
|
+
options[:formatter] = RDoc::RI::Formatter.for 'plain'
|
19
|
+
options[:list_classes] = false
|
20
|
+
options[:list_names] = false
|
21
|
+
|
22
|
+
# By default all paths are used. If any of these are true, only those
|
23
|
+
# directories are used.
|
24
|
+
use_system = false
|
25
|
+
use_site = false
|
26
|
+
use_home = false
|
27
|
+
use_gems = false
|
28
|
+
doc_dirs = []
|
29
|
+
|
30
|
+
opts = OptionParser.new do |opt|
|
31
|
+
opt.program_name = File.basename $0
|
32
|
+
opt.version = RDoc::VERSION
|
33
|
+
opt.summary_indent = ' ' * 4
|
34
|
+
|
35
|
+
directories = [
|
36
|
+
RDoc::RI::Paths::SYSDIR,
|
37
|
+
RDoc::RI::Paths::SITEDIR,
|
38
|
+
RDoc::RI::Paths::HOMEDIR
|
39
|
+
]
|
40
|
+
|
41
|
+
if RDoc::RI::Paths::GEMDIRS then
|
42
|
+
Gem.path.each do |dir|
|
43
|
+
directories << "#{dir}/doc/*/ri"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
opt.banner = <<-EOT
|
48
|
+
Usage: #{opt.program_name} [options] [names...]
|
49
|
+
|
50
|
+
Where name can be:
|
51
|
+
|
52
|
+
Class | Class::method | Class#method | Class.method | method
|
53
|
+
|
54
|
+
All class names may be abbreviated to their minimum unambiguous form. If a name
|
55
|
+
is ambiguous, all valid options will be listed.
|
56
|
+
|
57
|
+
The form '.' method matches either class or instance methods, while #method
|
58
|
+
matches only instance and ::method matches only class methods.
|
59
|
+
|
60
|
+
For example:
|
61
|
+
|
62
|
+
#{opt.program_name} Fil
|
63
|
+
#{opt.program_name} File
|
64
|
+
#{opt.program_name} File.new
|
65
|
+
#{opt.program_name} zip
|
66
|
+
|
67
|
+
Note that shell quoting may be required for method names containing
|
68
|
+
punctuation:
|
69
|
+
|
70
|
+
#{opt.program_name} 'Array.[]'
|
71
|
+
#{opt.program_name} compact\\!
|
72
|
+
|
73
|
+
By default ri searches for documentation in the following directories:
|
74
|
+
|
75
|
+
#{directories.join "\n "}
|
76
|
+
|
77
|
+
Specifying the --system, --site, --home, --gems or --doc-dir options will
|
78
|
+
limit ri to searching only the specified directories.
|
79
|
+
|
80
|
+
Options may also be set in the 'RI' environment variable.
|
81
|
+
EOT
|
82
|
+
|
83
|
+
opt.separator nil
|
84
|
+
opt.separator "Options:"
|
85
|
+
opt.separator nil
|
86
|
+
|
87
|
+
opt.on("--classes", "-c",
|
88
|
+
"Display the names of classes and modules we",
|
89
|
+
"know about.") do |value|
|
90
|
+
options[:list_classes] = value
|
91
|
+
end
|
92
|
+
|
93
|
+
opt.separator nil
|
94
|
+
|
95
|
+
opt.on("--doc-dir=DIRNAME", "-d", Array,
|
96
|
+
"List of directories to search for",
|
97
|
+
"documentation. If not specified, we search",
|
98
|
+
"the standard rdoc/ri directories. May be",
|
99
|
+
"repeated.") do |value|
|
100
|
+
value.each do |dir|
|
101
|
+
unless File.directory? dir then
|
102
|
+
raise OptionParser::InvalidArgument, "#{dir} is not a directory"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
doc_dirs.concat value
|
107
|
+
end
|
108
|
+
|
109
|
+
opt.separator nil
|
110
|
+
|
111
|
+
opt.on("--fmt=FORMAT", "--format=FORMAT", "-f",
|
112
|
+
RDoc::RI::Formatter::FORMATTERS.keys,
|
113
|
+
"Format to use when displaying output:",
|
114
|
+
" #{RDoc::RI::Formatter.list}",
|
115
|
+
"Use 'bs' (backspace) with most pager",
|
116
|
+
"programs. To use ANSI, either disable the",
|
117
|
+
"pager or tell the pager to allow control",
|
118
|
+
"characters.") do |value|
|
119
|
+
options[:formatter] = RDoc::RI::Formatter.for value
|
120
|
+
end
|
121
|
+
|
122
|
+
opt.separator nil
|
123
|
+
|
124
|
+
unless RDoc::RI::Paths::GEMDIRS.empty? then
|
125
|
+
opt.on("--[no-]gems",
|
126
|
+
"Include documentation from RubyGems.") do |value|
|
127
|
+
use_gems = value
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
opt.separator nil
|
132
|
+
|
133
|
+
opt.on("--[no-]home",
|
134
|
+
"Include documentation stored in ~/.rdoc.") do |value|
|
135
|
+
use_home = value
|
136
|
+
end
|
137
|
+
|
138
|
+
opt.separator nil
|
139
|
+
|
140
|
+
opt.on("--[no-]list-names", "-l",
|
141
|
+
"List all the names known to RDoc, one per",
|
142
|
+
"line.") do |value|
|
143
|
+
options[:list_names] = value
|
144
|
+
end
|
145
|
+
|
146
|
+
opt.separator nil
|
147
|
+
|
148
|
+
opt.on("--no-pager", "-T",
|
149
|
+
"Send output directly to stdout.") do |value|
|
150
|
+
options[:use_stdout] = !value
|
151
|
+
end
|
152
|
+
|
153
|
+
opt.separator nil
|
154
|
+
|
155
|
+
opt.on("--[no-]site",
|
156
|
+
"Include documentation from libraries",
|
157
|
+
"installed in site_lib.") do |value|
|
158
|
+
use_site = value
|
159
|
+
end
|
160
|
+
|
161
|
+
opt.separator nil
|
162
|
+
|
163
|
+
opt.on("--[no-]system",
|
164
|
+
"Include documentation from Ruby's standard",
|
165
|
+
"library.") do |value|
|
166
|
+
use_system = value
|
167
|
+
end
|
168
|
+
|
169
|
+
opt.separator nil
|
170
|
+
|
171
|
+
opt.on("--width=WIDTH", "-w", OptionParser::DecimalInteger,
|
172
|
+
"Set the width of the output.") do |value|
|
173
|
+
options[:width] = value
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
argv = ENV['RI'].to_s.split.concat argv
|
178
|
+
|
179
|
+
opts.parse! argv
|
180
|
+
|
181
|
+
options[:names] = argv
|
182
|
+
|
183
|
+
options[:path] = RDoc::RI::Paths.path(use_system, use_site, use_home,
|
184
|
+
use_gems, *doc_dirs)
|
185
|
+
options[:raw_path] = RDoc::RI::Paths.raw_path(use_system, use_site,
|
186
|
+
use_home, use_gems, *doc_dirs)
|
187
|
+
|
188
|
+
options
|
189
|
+
|
190
|
+
rescue OptionParser::InvalidArgument, OptionParser::InvalidOption => e
|
191
|
+
puts opts
|
192
|
+
puts
|
193
|
+
puts e
|
194
|
+
exit 1
|
195
|
+
end
|
196
|
+
|
197
|
+
def self.run(argv = ARGV)
|
198
|
+
options = process_args argv
|
199
|
+
ri = new options
|
200
|
+
ri.run
|
201
|
+
end
|
202
|
+
|
203
|
+
def initialize(options={})
|
204
|
+
options[:formatter] ||= RDoc::RI::Formatter.for('plain')
|
205
|
+
options[:use_stdout] ||= !$stdout.tty?
|
206
|
+
options[:width] ||= 72
|
207
|
+
@names = options[:names]
|
208
|
+
|
209
|
+
@class_cache_name = 'classes'
|
210
|
+
@all_dirs = RDoc::RI::Paths.path(true, true, true, true)
|
211
|
+
@homepath = RDoc::RI::Paths.raw_path(false, false, true, false).first
|
212
|
+
@homepath = @homepath.sub(/\.rdoc/, '.ri')
|
213
|
+
@sys_dirs = RDoc::RI::Paths.raw_path(true, false, false, false)
|
214
|
+
|
215
|
+
FileUtils.mkdir_p cache_file_path unless File.directory? cache_file_path
|
216
|
+
|
217
|
+
@class_cache = nil
|
218
|
+
|
219
|
+
@display = RDoc::RI::DefaultDisplay.new(options[:formatter],
|
220
|
+
options[:width],
|
221
|
+
options[:use_stdout])
|
222
|
+
end
|
223
|
+
|
224
|
+
def class_cache
|
225
|
+
return @class_cache if @class_cache
|
226
|
+
|
227
|
+
newest = map_dirs('created.rid', :all) do |f|
|
228
|
+
File.mtime f if test ?f, f
|
229
|
+
end.max
|
230
|
+
|
231
|
+
up_to_date = (File.exist?(class_cache_file_path) and
|
232
|
+
newest and newest < File.mtime(class_cache_file_path))
|
233
|
+
|
234
|
+
@class_cache = if up_to_date then
|
235
|
+
load_cache_for @class_cache_name
|
236
|
+
else
|
237
|
+
class_cache = {}
|
238
|
+
|
239
|
+
classes = map_dirs('**/cdesc*.yaml', :sys) { |f| Dir[f] }
|
240
|
+
populate_class_cache class_cache, classes
|
241
|
+
|
242
|
+
classes = map_dirs('**/cdesc*.yaml') { |f| Dir[f] }
|
243
|
+
warn "Updating class cache with #{classes.size} classes..."
|
244
|
+
|
245
|
+
populate_class_cache class_cache, classes, true
|
246
|
+
write_cache class_cache, class_cache_file_path
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def class_cache_file_path
|
251
|
+
File.join cache_file_path, @class_cache_name
|
252
|
+
end
|
253
|
+
|
254
|
+
def cache_file_for(klassname)
|
255
|
+
File.join cache_file_path, klassname.gsub(/:+/, "-")
|
256
|
+
end
|
257
|
+
|
258
|
+
def cache_file_path
|
259
|
+
File.join @homepath, 'cache'
|
260
|
+
end
|
261
|
+
|
262
|
+
def display_class(name)
|
263
|
+
klass = class_cache[name]
|
264
|
+
@display.display_class_info klass, class_cache
|
265
|
+
end
|
266
|
+
|
267
|
+
def load_cache_for(klassname)
|
268
|
+
path = cache_file_for klassname
|
269
|
+
|
270
|
+
if File.exist? path and
|
271
|
+
File.mtime(path) >= File.mtime(class_cache_file_path) then
|
272
|
+
File.open path, 'rb' do |fp|
|
273
|
+
Marshal.load fp.read
|
274
|
+
end
|
275
|
+
else
|
276
|
+
class_cache = nil
|
277
|
+
|
278
|
+
File.open class_cache_file_path, 'rb' do |fp|
|
279
|
+
class_cache = Marshal.load fp.read
|
280
|
+
end
|
281
|
+
|
282
|
+
klass = class_cache[klassname]
|
283
|
+
return nil unless klass
|
284
|
+
|
285
|
+
method_files = klass["sources"]
|
286
|
+
cache = {}
|
287
|
+
|
288
|
+
sys_dir = @sys_dirs.first
|
289
|
+
method_files.each do |f|
|
290
|
+
system_file = f.index(sys_dir) == 0
|
291
|
+
Dir[File.join(File.dirname(f), "*")].each do |yaml|
|
292
|
+
next unless yaml =~ /yaml$/
|
293
|
+
next if yaml =~ /cdesc-[^\/]+yaml$/
|
294
|
+
method = read_yaml yaml
|
295
|
+
name = method["full_name"]
|
296
|
+
ext_path = f
|
297
|
+
ext_path = "gem #{$1}" if f =~ %r%gems/[\d.]+/doc/([^/]+)%
|
298
|
+
method["source_path"] = ext_path unless system_file
|
299
|
+
cache[name] = method
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
write_cache cache, path
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
def map_dirs(file_name, system=false)
|
308
|
+
dirs = if system == :all then
|
309
|
+
@all_dirs
|
310
|
+
else
|
311
|
+
if system then
|
312
|
+
@sys_dirs
|
313
|
+
else
|
314
|
+
@all_dirs - @sys_dirs
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
dirs.map { |dir| yield File.join(dir, file_name) }.flatten.compact
|
319
|
+
end
|
320
|
+
|
321
|
+
def populate_class_cache(class_cache, classes, extension = false)
|
322
|
+
classes.each do |cdesc|
|
323
|
+
desc = read_yaml cdesc
|
324
|
+
klassname = desc["full_name"]
|
325
|
+
|
326
|
+
unless class_cache.has_key? klassname then
|
327
|
+
desc["display_name"] = "Class"
|
328
|
+
desc["sources"] = [cdesc]
|
329
|
+
desc["instance_method_extensions"] = []
|
330
|
+
desc["class_method_extensions"] = []
|
331
|
+
class_cache[klassname] = desc
|
332
|
+
else
|
333
|
+
klass = class_cache[klassname]
|
334
|
+
|
335
|
+
if extension then
|
336
|
+
desc["instance_method_extensions"] = desc.delete "instance_methods"
|
337
|
+
desc["class_method_extensions"] = desc.delete "class_methods"
|
338
|
+
end
|
339
|
+
|
340
|
+
klass.merge_enums desc
|
341
|
+
klass["sources"] << cdesc
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
def read_yaml(path)
|
347
|
+
data = File.read path
|
348
|
+
data = data.gsub(/ \!ruby\/(object|struct):(RDoc::RI|RI).*/, '')
|
349
|
+
data = data.gsub(/ \!ruby\/(object|struct):SM::(\S+)/,
|
350
|
+
' !ruby/\1:RDoc::Markup::\2')
|
351
|
+
YAML.load data
|
352
|
+
end
|
353
|
+
|
354
|
+
def get_info_for(arg)
|
355
|
+
@names = [arg]
|
356
|
+
run
|
357
|
+
end
|
358
|
+
|
359
|
+
def run
|
360
|
+
if @names.empty? then
|
361
|
+
@display.list_known_classes class_cache.keys.sort
|
362
|
+
else
|
363
|
+
@names.each do |name|
|
364
|
+
case name
|
365
|
+
when /::|\#|\./ then
|
366
|
+
if class_cache.key? name then
|
367
|
+
display_class name
|
368
|
+
else
|
369
|
+
meth = nil
|
370
|
+
|
371
|
+
parts = name.split(/::|\#|\./)
|
372
|
+
meth = parts.pop unless parts.last =~ /^[A-Z]/
|
373
|
+
klass = parts.join '::'
|
374
|
+
|
375
|
+
cache = load_cache_for klass
|
376
|
+
# HACK Does not support F.n
|
377
|
+
abort "Nothing known about #{name}" unless cache
|
378
|
+
method = cache[name.gsub(/\./, '#')]
|
379
|
+
abort "Nothing known about #{name}" unless method
|
380
|
+
@display.display_method_info method
|
381
|
+
end
|
382
|
+
else
|
383
|
+
if class_cache.key? name then
|
384
|
+
display_class name
|
385
|
+
else
|
386
|
+
methods = select_methods(/^#{name}/)
|
387
|
+
if methods.size == 0
|
388
|
+
abort "Nothing known about #{name}"
|
389
|
+
elsif methods.size == 1
|
390
|
+
@display.display_method_info methods.first
|
391
|
+
else
|
392
|
+
@display.display_method_list methods
|
393
|
+
end
|
394
|
+
end
|
395
|
+
end
|
396
|
+
end
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
def select_methods(pattern)
|
401
|
+
methods = []
|
402
|
+
class_cache.keys.sort.each do |klass|
|
403
|
+
class_cache[klass]["instance_methods"].map{|h|h["name"]}.grep(pattern) do |name|
|
404
|
+
method = load_cache_for(klass)[klass+'#'+name]
|
405
|
+
methods << method if method
|
406
|
+
end
|
407
|
+
class_cache[klass]["class_methods"].map{|h|h["name"]}.grep(pattern) do |name|
|
408
|
+
method = load_cache_for(klass)[klass+'::'+name]
|
409
|
+
methods << method if method
|
410
|
+
end
|
411
|
+
end
|
412
|
+
methods
|
413
|
+
end
|
414
|
+
|
415
|
+
def write_cache(cache, path)
|
416
|
+
File.open path, "wb" do |cache_file|
|
417
|
+
Marshal.dump cache, cache_file
|
418
|
+
end
|
419
|
+
|
420
|
+
cache
|
421
|
+
end
|
422
|
+
|
423
|
+
end
|
424
|
+
|
425
|
+
class Hash # HACK don't add stuff to Hash.
|
426
|
+
def method_missing method, *args
|
427
|
+
self[method.to_s]
|
428
|
+
end
|
429
|
+
|
430
|
+
def merge_enums(other)
|
431
|
+
other.each do |k,v|
|
432
|
+
if self[k] then
|
433
|
+
case v
|
434
|
+
when Array then
|
435
|
+
# HACK dunno
|
436
|
+
if String === self[k] and self[k].empty? then
|
437
|
+
self[k] = v
|
438
|
+
else
|
439
|
+
self[k] += v
|
440
|
+
end
|
441
|
+
when Hash then
|
442
|
+
self[k].merge! v
|
443
|
+
else
|
444
|
+
# do nothing
|
445
|
+
end
|
446
|
+
else
|
447
|
+
self[k] = v
|
448
|
+
end
|
449
|
+
end
|
450
|
+
end
|
451
|
+
end
|
452
|
+
|