voloko-sdoc 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. data/rdoc/History.txt +254 -0
  2. data/rdoc/Manifest.txt +126 -0
  3. data/rdoc/README.txt +47 -0
  4. data/rdoc/RI.txt +58 -0
  5. data/rdoc/Rakefile +70 -0
  6. data/rdoc/bin/rdoc +35 -0
  7. data/rdoc/bin/ri +5 -0
  8. data/rdoc/lib/rdoc/alias.rb +54 -0
  9. data/rdoc/lib/rdoc/anon_class.rb +10 -0
  10. data/rdoc/lib/rdoc/any_method.rb +190 -0
  11. data/rdoc/lib/rdoc/attr.rb +79 -0
  12. data/rdoc/lib/rdoc/cache.rb +41 -0
  13. data/rdoc/lib/rdoc/class_module.rb +87 -0
  14. data/rdoc/lib/rdoc/code_object.rb +152 -0
  15. data/rdoc/lib/rdoc/code_objects.rb +23 -0
  16. data/rdoc/lib/rdoc/constant.rb +36 -0
  17. data/rdoc/lib/rdoc/context.rb +712 -0
  18. data/rdoc/lib/rdoc/diagram.rb +340 -0
  19. data/rdoc/lib/rdoc/dot.rb +249 -0
  20. data/rdoc/lib/rdoc/generator/darkfish.rb +455 -0
  21. data/rdoc/lib/rdoc/generator/markup.rb +194 -0
  22. data/rdoc/lib/rdoc/generator/ri.rb +230 -0
  23. data/rdoc/lib/rdoc/generator/template/darkfish/classpage.rhtml +281 -0
  24. data/rdoc/lib/rdoc/generator/template/darkfish/filepage.rhtml +112 -0
  25. data/rdoc/lib/rdoc/generator/template/darkfish/images/brick.png +0 -0
  26. data/rdoc/lib/rdoc/generator/template/darkfish/images/brick_link.png +0 -0
  27. data/rdoc/lib/rdoc/generator/template/darkfish/images/bug.png +0 -0
  28. data/rdoc/lib/rdoc/generator/template/darkfish/images/bullet_black.png +0 -0
  29. data/rdoc/lib/rdoc/generator/template/darkfish/images/bullet_toggle_minus.png +0 -0
  30. data/rdoc/lib/rdoc/generator/template/darkfish/images/bullet_toggle_plus.png +0 -0
  31. data/rdoc/lib/rdoc/generator/template/darkfish/images/date.png +0 -0
  32. data/rdoc/lib/rdoc/generator/template/darkfish/images/find.png +0 -0
  33. data/rdoc/lib/rdoc/generator/template/darkfish/images/loadingAnimation.gif +0 -0
  34. data/rdoc/lib/rdoc/generator/template/darkfish/images/macFFBgHack.png +0 -0
  35. data/rdoc/lib/rdoc/generator/template/darkfish/images/package.png +0 -0
  36. data/rdoc/lib/rdoc/generator/template/darkfish/images/page_green.png +0 -0
  37. data/rdoc/lib/rdoc/generator/template/darkfish/images/page_white_text.png +0 -0
  38. data/rdoc/lib/rdoc/generator/template/darkfish/images/page_white_width.png +0 -0
  39. data/rdoc/lib/rdoc/generator/template/darkfish/images/plugin.png +0 -0
  40. data/rdoc/lib/rdoc/generator/template/darkfish/images/ruby.png +0 -0
  41. data/rdoc/lib/rdoc/generator/template/darkfish/images/tag_green.png +0 -0
  42. data/rdoc/lib/rdoc/generator/template/darkfish/images/wrench.png +0 -0
  43. data/rdoc/lib/rdoc/generator/template/darkfish/images/wrench_orange.png +0 -0
  44. data/rdoc/lib/rdoc/generator/template/darkfish/images/zoom.png +0 -0
  45. data/rdoc/lib/rdoc/generator/template/darkfish/index.rhtml +64 -0
  46. data/rdoc/lib/rdoc/generator/template/darkfish/js/darkfish.js +116 -0
  47. data/rdoc/lib/rdoc/generator/template/darkfish/js/jquery.js +32 -0
  48. data/rdoc/lib/rdoc/generator/template/darkfish/js/quicksearch.js +114 -0
  49. data/rdoc/lib/rdoc/generator/template/darkfish/js/thickbox-compressed.js +10 -0
  50. data/rdoc/lib/rdoc/generator/template/darkfish/rdoc.css +696 -0
  51. data/rdoc/lib/rdoc/generator.rb +8 -0
  52. data/rdoc/lib/rdoc/ghost_method.rb +8 -0
  53. data/rdoc/lib/rdoc/include.rb +39 -0
  54. data/rdoc/lib/rdoc/known_classes.rb +68 -0
  55. data/rdoc/lib/rdoc/markup/attribute_manager.rb +311 -0
  56. data/rdoc/lib/rdoc/markup/formatter.rb +25 -0
  57. data/rdoc/lib/rdoc/markup/fragments.rb +377 -0
  58. data/rdoc/lib/rdoc/markup/inline.rb +126 -0
  59. data/rdoc/lib/rdoc/markup/lines.rb +156 -0
  60. data/rdoc/lib/rdoc/markup/preprocess.rb +80 -0
  61. data/rdoc/lib/rdoc/markup/to_flow.rb +211 -0
  62. data/rdoc/lib/rdoc/markup/to_html.rb +406 -0
  63. data/rdoc/lib/rdoc/markup/to_html_crossref.rb +140 -0
  64. data/rdoc/lib/rdoc/markup/to_latex.rb +328 -0
  65. data/rdoc/lib/rdoc/markup/to_test.rb +53 -0
  66. data/rdoc/lib/rdoc/markup/to_texinfo.rb +73 -0
  67. data/rdoc/lib/rdoc/markup.rb +378 -0
  68. data/rdoc/lib/rdoc/meta_method.rb +8 -0
  69. data/rdoc/lib/rdoc/normal_class.rb +18 -0
  70. data/rdoc/lib/rdoc/normal_module.rb +34 -0
  71. data/rdoc/lib/rdoc/options.rb +542 -0
  72. data/rdoc/lib/rdoc/parser/c.rb +678 -0
  73. data/rdoc/lib/rdoc/parser/perl.rb +165 -0
  74. data/rdoc/lib/rdoc/parser/ruby.rb +2904 -0
  75. data/rdoc/lib/rdoc/parser/simple.rb +39 -0
  76. data/rdoc/lib/rdoc/parser.rb +138 -0
  77. data/rdoc/lib/rdoc/rdoc.rb +375 -0
  78. data/rdoc/lib/rdoc/require.rb +32 -0
  79. data/rdoc/lib/rdoc/ri/cache.rb +187 -0
  80. data/rdoc/lib/rdoc/ri/descriptions.rb +156 -0
  81. data/rdoc/lib/rdoc/ri/display.rb +340 -0
  82. data/rdoc/lib/rdoc/ri/driver.rb +828 -0
  83. data/rdoc/lib/rdoc/ri/formatter.rb +654 -0
  84. data/rdoc/lib/rdoc/ri/paths.rb +93 -0
  85. data/rdoc/lib/rdoc/ri/reader.rb +106 -0
  86. data/rdoc/lib/rdoc/ri/util.rb +79 -0
  87. data/rdoc/lib/rdoc/ri/writer.rb +68 -0
  88. data/rdoc/lib/rdoc/ri.rb +8 -0
  89. data/rdoc/lib/rdoc/single_class.rb +8 -0
  90. data/rdoc/lib/rdoc/stats.rb +178 -0
  91. data/rdoc/lib/rdoc/task.rb +276 -0
  92. data/rdoc/lib/rdoc/tokenstream.rb +33 -0
  93. data/rdoc/lib/rdoc/top_level.rb +242 -0
  94. data/rdoc/lib/rdoc.rb +398 -0
  95. metadata +1 -1
@@ -0,0 +1,828 @@
1
+ require 'abbrev'
2
+ require 'optparse'
3
+ require 'yaml'
4
+
5
+ begin
6
+ require 'readline'
7
+ rescue LoadError
8
+ end
9
+
10
+ require 'rdoc/ri'
11
+ require 'rdoc/ri/paths'
12
+ require 'rdoc/ri/formatter'
13
+ require 'rdoc/ri/display'
14
+ require 'fileutils'
15
+ require 'rdoc/markup'
16
+ require 'rdoc/markup/to_flow'
17
+
18
+ class RDoc::RI::Driver
19
+
20
+ ##
21
+ # This class offers both Hash and OpenStruct functionality. We convert from
22
+ # the Core Hash to this before calling any of the display methods, in order
23
+ # to give the display methods a cleaner API for accessing the data.
24
+
25
+ class OpenStructHash < Hash
26
+
27
+ ##
28
+ # This method converts from a Hash to an OpenStructHash.
29
+
30
+ def self.convert(object)
31
+ case object
32
+ when Hash then
33
+ new_hash = new # Convert Hash -> OpenStructHash
34
+
35
+ object.each do |key, value|
36
+ new_hash[key] = convert value
37
+ end
38
+
39
+ new_hash
40
+ when Array then
41
+ object.map do |element|
42
+ convert element
43
+ end
44
+ else
45
+ object
46
+ end
47
+ end
48
+
49
+ def merge_enums(other)
50
+ other.each do |k, v|
51
+ if self[k] then
52
+ case v
53
+ when Array then
54
+ # HACK dunno
55
+ if String === self[k] and self[k].empty? then
56
+ self[k] = v
57
+ else
58
+ self[k] += v
59
+ end
60
+ when Hash then
61
+ self[k].update v
62
+ else
63
+ # do nothing
64
+ end
65
+ else
66
+ self[k] = v
67
+ end
68
+ end
69
+ end
70
+
71
+ def method_missing method, *args
72
+ self[method.to_s]
73
+ end
74
+ end
75
+
76
+ class Error < RDoc::RI::Error; end
77
+
78
+ class NotFoundError < Error
79
+ def message
80
+ "Nothing known about #{super}"
81
+ end
82
+ end
83
+
84
+ attr_accessor :homepath # :nodoc:
85
+
86
+ ##
87
+ # Default options for ri
88
+
89
+ def self.default_options
90
+ options = {}
91
+ options[:use_stdout] = !$stdout.tty?
92
+ options[:width] = 72
93
+ options[:formatter] = RDoc::RI::Formatter.for 'plain'
94
+ options[:interactive] = false
95
+ options[:use_cache] = true
96
+
97
+ # By default all standard paths are used.
98
+ options[:use_system] = true
99
+ options[:use_site] = true
100
+ options[:use_home] = true
101
+ options[:use_gems] = true
102
+ options[:extra_doc_dirs] = []
103
+
104
+ return options
105
+ end
106
+
107
+ ##
108
+ # Parses +argv+ and returns a Hash of options
109
+
110
+ def self.process_args(argv)
111
+ options = default_options
112
+
113
+ opts = OptionParser.new do |opt|
114
+ opt.program_name = File.basename $0
115
+ opt.version = RDoc::VERSION
116
+ opt.release = nil
117
+ opt.summary_indent = ' ' * 4
118
+
119
+ directories = [
120
+ RDoc::RI::Paths::SYSDIR,
121
+ RDoc::RI::Paths::SITEDIR,
122
+ RDoc::RI::Paths::HOMEDIR
123
+ ]
124
+
125
+ if RDoc::RI::Paths::GEMDIRS then
126
+ Gem.path.each do |dir|
127
+ directories << "#{dir}/doc/*/ri"
128
+ end
129
+ end
130
+
131
+ opt.banner = <<-EOT
132
+ Usage: #{opt.program_name} [options] [names...]
133
+
134
+ Where name can be:
135
+
136
+ Class | Class::method | Class#method | Class.method | method
137
+
138
+ All class names may be abbreviated to their minimum unambiguous form. If a name
139
+ is ambiguous, all valid options will be listed.
140
+
141
+ The form '.' method matches either class or instance methods, while #method
142
+ matches only instance and ::method matches only class methods.
143
+
144
+ For example:
145
+
146
+ #{opt.program_name} Fil
147
+ #{opt.program_name} File
148
+ #{opt.program_name} File.new
149
+ #{opt.program_name} zip
150
+
151
+ Note that shell quoting may be required for method names containing
152
+ punctuation:
153
+
154
+ #{opt.program_name} 'Array.[]'
155
+ #{opt.program_name} compact\\!
156
+
157
+ By default ri searches for documentation in the following directories:
158
+
159
+ #{directories.join "\n "}
160
+
161
+ Specifying the --system, --site, --home, --gems or --doc-dir options will
162
+ limit ri to searching only the specified directories.
163
+
164
+ Options may also be set in the 'RI' environment variable.
165
+ EOT
166
+
167
+ opt.separator nil
168
+ opt.separator "Options:"
169
+ opt.separator nil
170
+
171
+ opt.on("--fmt=FORMAT", "--format=FORMAT", "-f",
172
+ RDoc::RI::Formatter::FORMATTERS.keys,
173
+ "Format to use when displaying output:",
174
+ " #{RDoc::RI::Formatter.list}",
175
+ "Use 'bs' (backspace) with most pager",
176
+ "programs. To use ANSI, either disable the",
177
+ "pager or tell the pager to allow control",
178
+ "characters.") do |value|
179
+ options[:formatter] = RDoc::RI::Formatter.for value
180
+ end
181
+
182
+ opt.separator nil
183
+
184
+ opt.on("--doc-dir=DIRNAME", "-d", Array,
185
+ "List of directories from which to source",
186
+ "documentation in addition to the standard",
187
+ "directories. May be repeated.") do |value|
188
+ value.each do |dir|
189
+ unless File.directory? dir then
190
+ raise OptionParser::InvalidArgument, "#{dir} is not a directory"
191
+ end
192
+
193
+ options[:extra_doc_dirs] << File.expand_path(dir)
194
+ end
195
+ end
196
+
197
+ opt.separator nil
198
+
199
+ opt.on("--[no-]use-cache",
200
+ "Whether or not to use ri's cache.",
201
+ "True by default.") do |value|
202
+ options[:use_cache] = value
203
+ end
204
+
205
+ opt.separator nil
206
+
207
+ opt.on("--no-standard-docs",
208
+ "Do not include documentation from",
209
+ "the Ruby standard library, site_lib,",
210
+ "installed gems, or ~/.rdoc.",
211
+ "Equivalent to specifying",
212
+ "the options --no-system, --no-site, --no-gems,",
213
+ "and --no-home") do
214
+ options[:use_system] = false
215
+ options[:use_site] = false
216
+ options[:use_gems] = false
217
+ options[:use_home] = false
218
+ end
219
+
220
+ opt.separator nil
221
+
222
+ opt.on("--[no-]system",
223
+ "Include documentation from Ruby's standard",
224
+ "library. Defaults to true.") do |value|
225
+ options[:use_system] = value
226
+ end
227
+
228
+ opt.separator nil
229
+
230
+ opt.on("--[no-]site",
231
+ "Include documentation from libraries",
232
+ "installed in site_lib.",
233
+ "Defaults to true.") do |value|
234
+ options[:use_site] = value
235
+ end
236
+
237
+ opt.separator nil
238
+
239
+ opt.on("--[no-]gems",
240
+ "Include documentation from RubyGems.",
241
+ "Defaults to true.") do |value|
242
+ options[:use_gems] = value
243
+ end
244
+
245
+ opt.separator nil
246
+
247
+ opt.on("--[no-]home",
248
+ "Include documentation stored in ~/.rdoc.",
249
+ "Defaults to true.") do |value|
250
+ options[:use_home] = value
251
+ end
252
+
253
+ opt.separator nil
254
+
255
+ opt.on("--list-doc-dirs",
256
+ "List the directories from which ri will",
257
+ "source documentation on stdout and exit.") do
258
+ options[:list_doc_dirs] = true
259
+ end
260
+
261
+ opt.separator nil
262
+
263
+ opt.on("--no-pager", "-T",
264
+ "Send output directly to stdout,",
265
+ "rather than to a pager.") do
266
+ options[:use_stdout] = true
267
+ end
268
+
269
+ opt.on("--interactive", "-i",
270
+ "This makes ri go into interactive mode.",
271
+ "When ri is in interactive mode it will",
272
+ "allow the user to disambiguate lists of",
273
+ "methods in case multiple methods match",
274
+ "against a method search string. It also",
275
+ "will allow the user to enter in a method",
276
+ "name (with auto-completion, if readline",
277
+ "is supported) when viewing a class.") do
278
+ options[:interactive] = true
279
+ end
280
+
281
+ opt.separator nil
282
+
283
+ opt.on("--width=WIDTH", "-w", OptionParser::DecimalInteger,
284
+ "Set the width of the output.") do |value|
285
+ options[:width] = value
286
+ end
287
+ end
288
+
289
+ argv = ENV['RI'].to_s.split.concat argv
290
+
291
+ opts.parse! argv
292
+
293
+ options[:names] = argv
294
+
295
+ options[:formatter] ||= RDoc::RI::Formatter.for('plain')
296
+ options[:use_stdout] ||= !$stdout.tty?
297
+ options[:use_stdout] ||= options[:interactive]
298
+ options[:width] ||= 72
299
+
300
+ options
301
+
302
+ rescue OptionParser::InvalidArgument, OptionParser::InvalidOption => e
303
+ puts opts
304
+ puts
305
+ puts e
306
+ exit 1
307
+ end
308
+
309
+ ##
310
+ # Runs the ri command line executable using +argv+
311
+
312
+ def self.run(argv = ARGV)
313
+ options = process_args argv
314
+ ri = new options
315
+ ri.run
316
+ end
317
+
318
+ def initialize(initial_options={})
319
+ options = self.class.default_options.update(initial_options)
320
+
321
+ @names = options[:names]
322
+ @class_cache_name = 'classes'
323
+
324
+ @doc_dirs = RDoc::RI::Paths.path(options[:use_system],
325
+ options[:use_site],
326
+ options[:use_home],
327
+ options[:use_gems],
328
+ options[:extra_doc_dirs])
329
+
330
+ @homepath = RDoc::RI::Paths.raw_path(false, false, true, false).first
331
+ @homepath = @homepath.sub(/\.rdoc/, '.ri')
332
+ @sys_dir = RDoc::RI::Paths.raw_path(true, false, false, false).first
333
+ @list_doc_dirs = options[:list_doc_dirs]
334
+
335
+ FileUtils.mkdir_p cache_file_path unless File.directory? cache_file_path
336
+ @cache_doc_dirs_path = File.join cache_file_path, ".doc_dirs"
337
+
338
+ @use_cache = options[:use_cache]
339
+ @class_cache = nil
340
+
341
+ @interactive = options[:interactive]
342
+ @display = RDoc::RI::DefaultDisplay.new(options[:formatter],
343
+ options[:width],
344
+ options[:use_stdout])
345
+ end
346
+
347
+ ##
348
+ # Cache of classes ri knows about
349
+
350
+ def class_cache
351
+ return @class_cache if @class_cache
352
+
353
+ # Get the documentation directories used to make the cache in order to see
354
+ # whether the cache is valid for the current ri instantiation.
355
+ if(File.readable?(@cache_doc_dirs_path))
356
+ cache_doc_dirs = IO.read(@cache_doc_dirs_path).split("\n")
357
+ else
358
+ cache_doc_dirs = []
359
+ end
360
+
361
+ newest = map_dirs('created.rid') do |f|
362
+ File.mtime f if test ?f, f
363
+ end.max
364
+
365
+ # An up to date cache file must have been created more recently than
366
+ # the last modification of any of the documentation directories. It also
367
+ # must have been created with the same documentation directories
368
+ # as those from which ri currently is sourcing documentation.
369
+ up_to_date = (File.exist?(class_cache_file_path) and
370
+ newest and newest < File.mtime(class_cache_file_path) and
371
+ (cache_doc_dirs == @doc_dirs))
372
+
373
+ if up_to_date and @use_cache then
374
+ open class_cache_file_path, 'rb' do |fp|
375
+ begin
376
+ @class_cache = Marshal.load fp.read
377
+ rescue
378
+ #
379
+ # This shouldn't be necessary, since the up_to_date logic above
380
+ # should force the cache to be recreated when a new version of
381
+ # rdoc is installed. This seems like a worthwhile enhancement
382
+ # to ri's robustness, however.
383
+ #
384
+ $stderr.puts "Error reading the class cache; recreating the class cache!"
385
+ @class_cache = create_class_cache
386
+ end
387
+ end
388
+ else
389
+ @class_cache = create_class_cache
390
+ end
391
+
392
+ @class_cache
393
+ end
394
+
395
+ ##
396
+ # Creates the class cache if it is empty
397
+
398
+ def create_class_cache
399
+ class_cache = OpenStructHash.new
400
+
401
+ if(@use_cache)
402
+ # Dump the documentation directories to a file in the cache, so that
403
+ # we only will use the cache for future instantiations with identical
404
+ # documentation directories.
405
+ File.open @cache_doc_dirs_path, "wb" do |fp|
406
+ fp << @doc_dirs.join("\n")
407
+ end
408
+ end
409
+
410
+ classes = map_dirs('**/cdesc*.yaml') { |f| Dir[f] }
411
+ warn "Updating ri class cache with #{classes.size} classes..."
412
+ populate_class_cache class_cache, classes
413
+
414
+ write_cache class_cache, class_cache_file_path
415
+
416
+ class_cache
417
+ end
418
+
419
+ ##
420
+ # Populates +class_cache+ with +classes+, adding +extension+ data for found
421
+ # methods when asked
422
+
423
+ def populate_class_cache(class_cache, classes, extension = false)
424
+ classes.each do |cdesc|
425
+ desc = read_yaml cdesc
426
+ klassname = desc["full_name"]
427
+
428
+ unless class_cache.has_key? klassname then
429
+ desc["display_name"] = "Class"
430
+ desc["sources"] = [cdesc]
431
+ desc["instance_method_extensions"] = []
432
+ desc["class_method_extensions"] = []
433
+ class_cache[klassname] = desc
434
+ else
435
+ klass = class_cache[klassname]
436
+
437
+ if extension then
438
+ desc["instance_method_extensions"] = desc.delete "instance_methods"
439
+ desc["class_method_extensions"] = desc.delete "class_methods"
440
+ end
441
+
442
+ klass.merge_enums desc
443
+ klass["sources"] << cdesc
444
+ end
445
+ end
446
+ end
447
+
448
+ ##
449
+ # Path to the class_cache
450
+
451
+ def class_cache_file_path
452
+ File.join cache_file_path, @class_cache_name
453
+ end
454
+
455
+ ##
456
+ # Path to the cache file for +klassname+
457
+
458
+ def cache_file_for(klassname)
459
+ File.join cache_file_path, klassname.gsub(/:+/, "-")
460
+ end
461
+
462
+ ##
463
+ # Directory where cache files live
464
+
465
+ def cache_file_path
466
+ File.join @homepath, 'cache'
467
+ end
468
+
469
+ ##
470
+ # Displays the module, class or method +name+. For methods, locates the
471
+ # method in the ancestors list if it isn't in the named module.
472
+
473
+ def display_name(name)
474
+ if class_cache.key? name then
475
+ method_map = display_class name
476
+ elsif name =~ /::|\#|\./ then
477
+ method = nil
478
+ klass, = parse_name name
479
+
480
+ klass = expand_klass klass unless class_cache.key? klass
481
+
482
+ orig_klass = klass
483
+ orig_name = name
484
+
485
+ loop do
486
+ method = lookup_method name, klass
487
+
488
+ break if method
489
+
490
+ ancestor = lookup_ancestor klass, orig_klass
491
+
492
+ break unless ancestor
493
+
494
+ name = name.sub klass, ancestor
495
+ klass = ancestor
496
+ end
497
+
498
+ raise NotFoundError, orig_name unless method
499
+
500
+ display_method method
501
+ else
502
+ methods = select_methods(/#{name}/)
503
+
504
+ if methods.size == 0
505
+ raise NotFoundError, name
506
+ elsif methods.size == 1
507
+ display_method methods[0]
508
+ else
509
+ @display.display_method_list methods
510
+ end
511
+ end
512
+ end
513
+
514
+ ##
515
+ # Displays info for class or module +name+
516
+
517
+ def display_class(name)
518
+ klass = class_cache[name]
519
+ @display.display_class_info klass
520
+ end
521
+
522
+ ##
523
+ # Displays info for method +method+
524
+
525
+ def display_method(method)
526
+ @display.display_method_info method
527
+ end
528
+
529
+ ##
530
+ # Runs ri interactively using Readline if it is available.
531
+
532
+ def interactive
533
+ formatter = @display.formatter
534
+
535
+ if defined? Readline then
536
+ # prepare abbreviations for tab completion
537
+ klasses = class_cache.keys
538
+
539
+ Readline.completion_proc = proc do |name|
540
+ case name
541
+ when /(#|\.|::)([^A-Z]|$)/ then
542
+ methods = []
543
+ method_type = $1 == '.' ? '#|::' : $1
544
+
545
+ klass, method = if $2.empty? then
546
+ [$`, '']
547
+ else
548
+ parse_name name
549
+ end
550
+
551
+ cache = load_cache_for klass
552
+
553
+ methods += cache.keys.select do |method_name|
554
+ method_name =~ /^#{klass}#{method_type}#{method}/
555
+ end
556
+
557
+ # TODO ancestor lookup
558
+
559
+ if method_type == '::' and method.empty? then
560
+ methods += klasses.grep(/^#{klass}::/)
561
+ end
562
+
563
+ methods
564
+ when /^[A-Z]\w*/ then
565
+ klasses.grep(/^#{name}/)
566
+ else
567
+ []
568
+ end
569
+ end
570
+ end
571
+
572
+ formatter.raw_print_line "\nEnter the method name you want to look up.\n"
573
+
574
+ if defined? Readline then
575
+ formatter.raw_print_line "You can use tab to autocomplete.\n"
576
+ end
577
+
578
+ formatter.raw_print_line "Enter a blank line to exit.\n\n"
579
+
580
+ loop do
581
+ name = if defined? Readline then
582
+ Readline.readline ">> "
583
+ else
584
+ formatter.raw_print_line ">> "
585
+ $stdin.gets
586
+ end
587
+
588
+ return if name.nil? or name.empty?
589
+
590
+ name = name.strip
591
+
592
+ begin
593
+ display_name name
594
+ rescue NotFoundError => e
595
+ formatter.raw_print_line "#{e.message}\n"
596
+ end
597
+ end
598
+
599
+ rescue Interrupt
600
+ exit
601
+ end
602
+
603
+ ##
604
+ # Expands abbreviated klass +klass+ into a fully-qualified klass. "Zl::Da"
605
+ # will be expanded to Zlib::DataError.
606
+
607
+ def expand_klass(klass)
608
+ klass.split('::').inject '' do |expanded, klass_part|
609
+ expanded << '::' unless expanded.empty?
610
+ short = expanded << klass_part
611
+
612
+ subset = class_cache.keys.select do |klass_name|
613
+ klass =~ /^#{expanded}[^:]*$/
614
+ end
615
+
616
+ abbrevs = Abbrev.abbrev subset
617
+
618
+ expanded = abbrevs[short]
619
+
620
+ raise NotFoundError, short unless expanded
621
+
622
+ expanded.dup
623
+ end
624
+ end
625
+
626
+ ##
627
+ # Loads the cache for +klassname+
628
+
629
+ def load_cache_for(klassname)
630
+ path = cache_file_for klassname
631
+
632
+ cache = nil
633
+
634
+ if File.exist? path and
635
+ File.mtime(path) >= File.mtime(class_cache_file_path) and
636
+ @use_cache then
637
+ open path, 'rb' do |fp|
638
+ begin
639
+ cache = Marshal.load fp.read
640
+ rescue
641
+ #
642
+ # The cache somehow is bad. Recreate the cache.
643
+ #
644
+ $stderr.puts "Error reading the cache for #{klassname}; recreating the cache!"
645
+ cache = create_cache_for klassname, path
646
+ end
647
+ end
648
+ else
649
+ cache = create_cache_for klassname, path
650
+ end
651
+
652
+ cache
653
+ end
654
+
655
+ ##
656
+ # Writes a cache file for +klassname+ to +path+
657
+
658
+ def create_cache_for(klassname, path)
659
+ klass = class_cache[klassname]
660
+ return nil unless klass
661
+
662
+ method_files = klass["sources"]
663
+ cache = OpenStructHash.new
664
+
665
+ method_files.each do |f|
666
+ system_file = f.index(@sys_dir) == 0
667
+ Dir[File.join(File.dirname(f), "*")].each do |yaml|
668
+ next unless yaml =~ /yaml$/
669
+ next if yaml =~ /cdesc-[^\/]+yaml$/
670
+
671
+ method = read_yaml yaml
672
+
673
+ if system_file then
674
+ method["source_path"] = "Ruby #{RDoc::RI::Paths::VERSION}"
675
+ else
676
+ gem = Gem.path.any? do |gem_path|
677
+ pattern = File.join Regexp.escape(gem_path), 'doc', '(.*?)', ''
678
+
679
+ f =~ /^#{pattern}/
680
+ end
681
+
682
+ method["source_path"] = if gem then
683
+ "gem #{$1}"
684
+ else
685
+ f
686
+ end
687
+ end
688
+
689
+ name = method["full_name"]
690
+ cache[name] = method
691
+ end
692
+ end
693
+
694
+ write_cache cache, path
695
+ end
696
+
697
+ ##
698
+ # Finds the next ancestor of +orig_klass+ after +klass+.
699
+
700
+ def lookup_ancestor(klass, orig_klass)
701
+ # This is a bit hacky, but ri will go into an infinite loop otherwise,
702
+ # since Object has an Object ancestor for some reason. Depending on the
703
+ # documentation state, I've seen Kernel as an ancestor of Object and not
704
+ # as an ancestor of Object.
705
+ if orig_klass == "Object" && (klass == "Kernel" || klass == "Object") then
706
+ return nil
707
+ end
708
+
709
+ cache = class_cache[orig_klass]
710
+
711
+ return nil unless cache
712
+
713
+ ancestors = [orig_klass]
714
+ ancestors.push(*cache.includes.map { |inc| inc['name'] })
715
+ ancestors << cache.superclass
716
+
717
+ ancestor_index = ancestors.index klass
718
+
719
+ if ancestor_index
720
+ ancestor = ancestors[ancestors.index(klass) + 1]
721
+ return ancestor if ancestor
722
+ end
723
+
724
+ lookup_ancestor klass, cache.superclass
725
+ end
726
+
727
+ ##
728
+ # Finds the method
729
+
730
+ def lookup_method(name, klass)
731
+ cache = load_cache_for klass
732
+ return nil unless cache
733
+
734
+ method = cache[name.gsub('.', '#')]
735
+ method = cache[name.gsub('.', '::')] unless method
736
+ method
737
+ end
738
+
739
+ def map_dirs(file_name)
740
+ @doc_dirs.map { |dir| yield File.join(dir, file_name) }.flatten.compact
741
+ end
742
+
743
+ ##
744
+ # Extract the class and method name parts from +name+ like Foo::Bar#baz
745
+
746
+ def parse_name(name)
747
+ parts = name.split(/(::|\#|\.)/)
748
+
749
+ if parts[-2] != '::' or parts.last !~ /^[A-Z]/ then
750
+ meth = parts.pop
751
+ parts.pop
752
+ end
753
+
754
+ klass = parts.join
755
+
756
+ [klass, meth]
757
+ end
758
+
759
+ ##
760
+ # Reads ri YAML data from +path+, converting RDoc 1.x classes to RDoc 2.x
761
+ # classes
762
+
763
+ def read_yaml(path)
764
+ data = File.read path
765
+
766
+ # Necessary to be backward-compatible with documentation generated
767
+ # by earliar RDoc versions.
768
+ data = data.gsub(/ \!ruby\/(object|struct):(RDoc::RI|RI).*/, '')
769
+ data = data.gsub(/ \!ruby\/(object|struct):SM::(\S+)/,
770
+ ' !ruby/\1:RDoc::Markup::\2')
771
+
772
+ OpenStructHash.convert YAML.load(data)
773
+ end
774
+
775
+ ##
776
+ # Looks up and displays ri data according to the options given.
777
+
778
+ def run
779
+ if @list_doc_dirs then
780
+ puts @doc_dirs
781
+ elsif @interactive then
782
+ interactive
783
+ elsif @names.empty? then
784
+ @display.list_known_classes class_cache.keys.sort
785
+ else
786
+ @names.each do |name|
787
+ display_name name
788
+ end
789
+ end
790
+ rescue NotFoundError => e
791
+ abort e.message
792
+ end
793
+
794
+ ##
795
+ # Selects methods matching +pattern+ from all modules
796
+
797
+ def select_methods(pattern)
798
+ methods = []
799
+ class_cache.keys.sort.each do |klass|
800
+ class_cache[klass]["instance_methods"].map{|h|h["name"]}.grep(pattern) do |name|
801
+ method = load_cache_for(klass)[klass+'#'+name]
802
+ methods << method if method
803
+ end
804
+ class_cache[klass]["class_methods"].map{|h|h["name"]}.grep(pattern) do |name|
805
+ method = load_cache_for(klass)[klass+'::'+name]
806
+ methods << method if method
807
+ end
808
+ end
809
+ methods
810
+ end
811
+
812
+ ##
813
+ # Writes +cache+ to +path+
814
+
815
+ def write_cache(cache, path)
816
+ if @use_cache then
817
+ File.open path, "wb" do |cache_file|
818
+ Marshal.dump cache, cache_file
819
+ end
820
+ end
821
+
822
+ cache
823
+ rescue Errno::EISDIR # HACK toplevel, replace with main
824
+ cache
825
+ end
826
+
827
+ end
828
+