rdoc 2.4.3 → 2.5

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.

Files changed (139) hide show
  1. data.tar.gz.sig +0 -0
  2. data/.autotest +3 -1
  3. data/History.txt +68 -0
  4. data/LICENSE.txt +57 -0
  5. data/Manifest.txt +37 -19
  6. data/README.txt +2 -12
  7. data/Rakefile +12 -12
  8. data/bin/rdoc +4 -4
  9. data/lib/rdoc.rb +32 -9
  10. data/lib/rdoc/alias.rb +2 -2
  11. data/lib/rdoc/any_method.rb +108 -16
  12. data/lib/rdoc/attr.rb +87 -1
  13. data/lib/rdoc/class_module.rb +131 -5
  14. data/lib/rdoc/code_object.rb +28 -5
  15. data/lib/rdoc/constant.rb +22 -0
  16. data/lib/rdoc/context.rb +80 -37
  17. data/lib/rdoc/gauntlet.rb +48 -0
  18. data/lib/rdoc/generator/darkfish.rb +25 -23
  19. data/lib/rdoc/generator/markup.rb +6 -29
  20. data/lib/rdoc/generator/ri.rb +39 -189
  21. data/lib/rdoc/generator/template/darkfish/classpage.rhtml +17 -1
  22. data/lib/rdoc/generator/template/darkfish/filepage.rhtml +10 -0
  23. data/lib/rdoc/generator/template/darkfish/images/brick.png +0 -0
  24. data/lib/rdoc/generator/template/darkfish/images/brick_link.png +0 -0
  25. data/lib/rdoc/generator/template/darkfish/images/bullet_black.png +0 -0
  26. data/lib/rdoc/generator/template/darkfish/images/bullet_toggle_minus.png +0 -0
  27. data/lib/rdoc/generator/template/darkfish/images/bullet_toggle_plus.png +0 -0
  28. data/lib/rdoc/generator/template/darkfish/images/date.png +0 -0
  29. data/lib/rdoc/generator/template/darkfish/images/find.png +0 -0
  30. data/lib/rdoc/generator/template/darkfish/images/package.png +0 -0
  31. data/lib/rdoc/generator/template/darkfish/images/page_green.png +0 -0
  32. data/lib/rdoc/generator/template/darkfish/images/page_white_text.png +0 -0
  33. data/lib/rdoc/generator/template/darkfish/images/page_white_width.png +0 -0
  34. data/lib/rdoc/generator/template/darkfish/images/plugin.png +0 -0
  35. data/lib/rdoc/generator/template/darkfish/images/ruby.png +0 -0
  36. data/lib/rdoc/generator/template/darkfish/images/tag_green.png +0 -0
  37. data/lib/rdoc/generator/template/darkfish/images/wrench.png +0 -0
  38. data/lib/rdoc/generator/template/darkfish/images/wrench_orange.png +0 -0
  39. data/lib/rdoc/generator/template/darkfish/images/zoom.png +0 -0
  40. data/lib/rdoc/generator/template/darkfish/index.rhtml +2 -2
  41. data/lib/rdoc/generator/template/darkfish/rdoc.css +38 -33
  42. data/lib/rdoc/include.rb +22 -0
  43. data/lib/rdoc/markup.rb +10 -262
  44. data/lib/rdoc/markup/attribute_manager.rb +57 -50
  45. data/lib/rdoc/markup/blank_line.rb +19 -0
  46. data/lib/rdoc/markup/document.rb +72 -0
  47. data/lib/rdoc/markup/formatter.rb +118 -0
  48. data/lib/rdoc/markup/formatter_test_case.rb +341 -0
  49. data/lib/rdoc/markup/heading.rb +17 -0
  50. data/lib/rdoc/markup/inline.rb +6 -5
  51. data/lib/rdoc/markup/list.rb +78 -0
  52. data/lib/rdoc/markup/list_item.rb +83 -0
  53. data/lib/rdoc/markup/paragraph.rb +66 -0
  54. data/lib/rdoc/markup/parser.rb +528 -0
  55. data/lib/rdoc/markup/rule.rb +17 -0
  56. data/lib/rdoc/markup/to_ansi.rb +72 -0
  57. data/lib/rdoc/markup/to_bs.rb +74 -0
  58. data/lib/rdoc/markup/to_html.rb +106 -172
  59. data/lib/rdoc/markup/to_html_crossref.rb +10 -4
  60. data/lib/rdoc/markup/to_rdoc.rb +243 -0
  61. data/lib/rdoc/markup/to_test.rb +27 -16
  62. data/lib/rdoc/markup/verbatim.rb +42 -0
  63. data/lib/rdoc/normal_class.rb +38 -1
  64. data/lib/rdoc/normal_module.rb +38 -8
  65. data/lib/rdoc/options.rb +39 -151
  66. data/lib/rdoc/parser.rb +36 -18
  67. data/lib/rdoc/parser/c.rb +102 -109
  68. data/lib/rdoc/parser/ruby.rb +359 -1662
  69. data/lib/rdoc/parser/ruby_tools.rb +157 -0
  70. data/lib/rdoc/parser/simple.rb +0 -2
  71. data/lib/rdoc/rdoc.rb +142 -82
  72. data/lib/rdoc/ri.rb +10 -0
  73. data/lib/rdoc/ri/driver.rb +674 -444
  74. data/lib/rdoc/ri/formatter.rb +2 -651
  75. data/lib/rdoc/ri/paths.rb +70 -45
  76. data/lib/rdoc/ri/store.rb +248 -0
  77. data/lib/rdoc/ruby_lex.rb +1284 -0
  78. data/lib/rdoc/ruby_token.rb +416 -0
  79. data/lib/rdoc/single_class.rb +5 -0
  80. data/lib/rdoc/stats.rb +152 -83
  81. data/lib/rdoc/task.rb +27 -49
  82. data/lib/rdoc/text.rb +130 -0
  83. data/lib/rdoc/tokenstream.rb +28 -9
  84. data/lib/rdoc/top_level.rb +49 -43
  85. data/test/hidden.zip.txt +1 -0
  86. data/test/test_attribute_manager.rb +9 -16
  87. data/test/test_rdoc_any_method.rb +23 -0
  88. data/test/test_rdoc_attr.rb +40 -0
  89. data/test/test_rdoc_class_module.rb +100 -0
  90. data/test/test_rdoc_code_object.rb +18 -2
  91. data/test/test_rdoc_context.rb +41 -0
  92. data/test/test_rdoc_generator_ri.rb +56 -0
  93. data/test/test_rdoc_markup.rb +21 -610
  94. data/test/test_rdoc_markup_attribute_manager.rb +14 -17
  95. data/test/test_rdoc_markup_document.rb +51 -0
  96. data/test/test_rdoc_markup_paragraph.rb +27 -0
  97. data/test/test_rdoc_markup_parser.rb +1327 -0
  98. data/test/test_rdoc_markup_to_ansi.rb +426 -0
  99. data/test/test_rdoc_markup_to_bs.rb +443 -0
  100. data/test/test_rdoc_markup_to_html.rb +183 -18
  101. data/test/test_rdoc_markup_to_html_crossref.rb +1 -3
  102. data/test/test_rdoc_markup_to_rdoc.rb +426 -0
  103. data/test/test_rdoc_normal_class.rb +17 -0
  104. data/test/test_rdoc_normal_module.rb +6 -6
  105. data/test/test_rdoc_options.rb +41 -0
  106. data/test/test_rdoc_parser.rb +66 -13
  107. data/test/test_rdoc_parser_c.rb +93 -38
  108. data/test/test_rdoc_parser_perl.rb +2 -3
  109. data/test/test_rdoc_parser_ruby.rb +291 -28
  110. data/test/test_rdoc_parser_simple.rb +48 -0
  111. data/test/test_rdoc_rdoc.rb +66 -0
  112. data/test/test_rdoc_ri_driver.rb +752 -38
  113. data/test/test_rdoc_ri_paths.rb +39 -0
  114. data/test/test_rdoc_ri_store.rb +309 -0
  115. data/test/test_rdoc_text.rb +157 -0
  116. data/test/test_rdoc_top_level.rb +35 -9
  117. data/test/xref_data.rb +9 -1
  118. data/test/xref_test_case.rb +8 -3
  119. metadata +110 -38
  120. metadata.gz.sig +0 -0
  121. data/lib/rdoc/cache.rb +0 -41
  122. data/lib/rdoc/diagram.rb +0 -340
  123. data/lib/rdoc/dot.rb +0 -249
  124. data/lib/rdoc/markup/fragments.rb +0 -377
  125. data/lib/rdoc/markup/lines.rb +0 -156
  126. data/lib/rdoc/markup/to_flow.rb +0 -211
  127. data/lib/rdoc/markup/to_latex.rb +0 -328
  128. data/lib/rdoc/markup/to_texinfo.rb +0 -73
  129. data/lib/rdoc/ri/cache.rb +0 -187
  130. data/lib/rdoc/ri/descriptions.rb +0 -156
  131. data/lib/rdoc/ri/display.rb +0 -340
  132. data/lib/rdoc/ri/reader.rb +0 -106
  133. data/lib/rdoc/ri/util.rb +0 -79
  134. data/lib/rdoc/ri/writer.rb +0 -68
  135. data/test/test_rdoc_ri_attribute_formatter.rb +0 -44
  136. data/test/test_rdoc_ri_default_display.rb +0 -302
  137. data/test/test_rdoc_ri_formatter.rb +0 -320
  138. data/test/test_rdoc_ri_html_formatter.rb +0 -141
  139. data/test/test_rdoc_ri_overstrike_formatter.rb +0 -71
@@ -1,8 +1,18 @@
1
1
  require 'rdoc'
2
2
 
3
+ ##
4
+ # Namespace for the ri command line tool's implementation.
5
+ #
6
+ # See <tt>ri --help</tt> for details.
7
+
3
8
  module RDoc::RI
4
9
 
10
+ ##
11
+ # Base RI error class
12
+
5
13
  class Error < RDoc::Error; end
6
14
 
7
15
  end
8
16
 
17
+ require 'rdoc/ri/store'
18
+
@@ -1,6 +1,5 @@
1
1
  require 'abbrev'
2
2
  require 'optparse'
3
- require 'yaml'
4
3
 
5
4
  begin
6
5
  require 'readline'
@@ -9,79 +8,59 @@ end
9
8
 
10
9
  require 'rdoc/ri'
11
10
  require 'rdoc/ri/paths'
12
- require 'rdoc/ri/formatter'
13
- require 'rdoc/ri/display'
14
- require 'fileutils'
15
11
  require 'rdoc/markup'
16
- require 'rdoc/markup/to_flow'
12
+ require 'rdoc/markup/formatter'
13
+ require 'rdoc/text'
14
+
15
+ ##
16
+ # For RubyGems backwards compatibility
17
+
18
+ require 'rdoc/ri/formatter'
19
+
20
+ ##
21
+ # The RI driver implements the command-line ri tool.
22
+ #
23
+ # The driver supports:
24
+ # * loading RI data from:
25
+ # * Ruby's standard library
26
+ # * RubyGems
27
+ # * ~/.rdoc
28
+ # * A user-supplied directory
29
+ # * Paging output (uses RI_PAGER environment variable, PAGER environment
30
+ # variable or the less, more and pager programs)
31
+ # * Interactive mode with tab-completion
32
+ # * Abbreviated names (ri Zl shows Zlib documentation)
33
+ # * Colorized output
34
+ # * Merging output from multiple RI data sources
17
35
 
18
36
  class RDoc::RI::Driver
19
37
 
20
38
  ##
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.
39
+ # Base Driver error class
24
40
 
25
- class OpenStructHash < Hash
41
+ class Error < RDoc::RI::Error; end
26
42
 
27
- ##
28
- # This method converts from a Hash to an OpenStructHash.
43
+ ##
44
+ # Raised when a name isn't found in the ri data stores
29
45
 
30
- def self.convert(object)
31
- case object
32
- when Hash then
33
- new_hash = new # Convert Hash -> OpenStructHash
46
+ class NotFoundError < Error
34
47
 
35
- object.each do |key, value|
36
- new_hash[key] = convert value
37
- end
48
+ ##
49
+ # Name that wasn't found
38
50
 
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
51
+ alias name message
70
52
 
71
- def method_missing method, *args
72
- self[method.to_s]
53
+ def message # :nodoc:
54
+ "Nothing known about #{super}"
73
55
  end
74
56
  end
75
57
 
76
- class Error < RDoc::RI::Error; end
58
+ attr_accessor :stores
77
59
 
78
- class NotFoundError < Error
79
- def message
80
- "Nothing known about #{super}"
81
- end
82
- end
60
+ ##
61
+ # Controls the user of the pager vs $stdout
83
62
 
84
- attr_accessor :homepath # :nodoc:
63
+ attr_accessor :use_stdout
85
64
 
86
65
  ##
87
66
  # Default options for ri
@@ -90,9 +69,9 @@ class RDoc::RI::Driver
90
69
  options = {}
91
70
  options[:use_stdout] = !$stdout.tty?
92
71
  options[:width] = 72
93
- options[:formatter] = RDoc::RI::Formatter.for 'plain'
94
72
  options[:interactive] = false
95
73
  options[:use_cache] = true
74
+ options[:profile] = false
96
75
 
97
76
  # By default all standard paths are used.
98
77
  options[:use_system] = true
@@ -104,30 +83,33 @@ class RDoc::RI::Driver
104
83
  return options
105
84
  end
106
85
 
86
+ ##
87
+ # Dump +data_path+ using pp
88
+
89
+ def self.dump data_path
90
+ require 'pp'
91
+
92
+ open data_path, 'rb' do |io|
93
+ pp Marshal.load(io.read)
94
+ end
95
+ end
96
+
107
97
  ##
108
98
  # Parses +argv+ and returns a Hash of options
109
99
 
110
- def self.process_args(argv)
100
+ def self.process_args argv
111
101
  options = default_options
112
102
 
113
103
  opts = OptionParser.new do |opt|
104
+ opt.accept File do |file,|
105
+ File.readable?(file) and not File.directory?(file) and file
106
+ end
107
+
114
108
  opt.program_name = File.basename $0
115
109
  opt.version = RDoc::VERSION
116
110
  opt.release = nil
117
111
  opt.summary_indent = ' ' * 4
118
112
 
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
113
  opt.banner = <<-EOT
132
114
  Usage: #{opt.program_name} [options] [names...]
133
115
 
@@ -154,9 +136,9 @@ punctuation:
154
136
  #{opt.program_name} 'Array.[]'
155
137
  #{opt.program_name} compact\\!
156
138
 
157
- By default ri searches for documentation in the following directories:
139
+ To see the default directories ri will search, run:
158
140
 
159
- #{directories.join "\n "}
141
+ #{opt.program_name} --list-doc-dirs
160
142
 
161
143
  Specifying the --system, --site, --home, --gems or --doc-dir options will
162
144
  limit ri to searching only the specified directories.
@@ -166,17 +148,60 @@ Options may also be set in the 'RI' environment variable.
166
148
 
167
149
  opt.separator nil
168
150
  opt.separator "Options:"
151
+
152
+ opt.separator nil
153
+
154
+ formatters = RDoc::Markup.constants.grep(/^To[A-Z][a-z]+$/).sort
155
+ formatters = formatters.sort.map do |formatter|
156
+ formatter.to_s.sub('To', '').downcase
157
+ end
158
+
159
+ opt.on("--format=NAME", "-f",
160
+ "Uses the selected formatter. The default",
161
+ "formatter is bs for paged output and ansi",
162
+ "otherwise. Valid formatters are:",
163
+ formatters.join(' '), formatters) do |value|
164
+ options[:formatter] = RDoc::Markup.const_get "To#{value.capitalize}"
165
+ end
166
+
167
+ opt.separator nil
168
+
169
+ opt.on("--no-pager", "-T",
170
+ "Send output directly to stdout,",
171
+ "rather than to a pager.") do
172
+ options[:use_stdout] = true
173
+ end
174
+
175
+ opt.separator nil
176
+
177
+ opt.on("--width=WIDTH", "-w", OptionParser::DecimalInteger,
178
+ "Set the width of the output.") do |value|
179
+ options[:width] = value
180
+ end
181
+
182
+ opt.separator nil
183
+
184
+ opt.on("--interactive", "-i",
185
+ "In interactive mode you can repeatedly",
186
+ "look up methods with autocomplete.") do
187
+ options[:interactive] = true
188
+ end
189
+
190
+ opt.separator nil
191
+
192
+ opt.on("--[no-]profile",
193
+ "Run with the ruby profiler") do |value|
194
+ options[:profile] = value
195
+ end
196
+
197
+ opt.separator nil
198
+ opt.separator "Data source options:"
169
199
  opt.separator nil
170
200
 
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
201
+ opt.on("--list-doc-dirs",
202
+ "List the directories from which ri will",
203
+ "source documentation on stdout and exit.") do
204
+ options[:list_doc_dirs] = true
180
205
  end
181
206
 
182
207
  opt.separator nil
@@ -196,21 +221,11 @@ Options may also be set in the 'RI' environment variable.
196
221
 
197
222
  opt.separator nil
198
223
 
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
224
  opt.on("--no-standard-docs",
208
225
  "Do not include documentation from",
209
226
  "the Ruby standard library, site_lib,",
210
227
  "installed gems, or ~/.rdoc.",
211
- "Equivalent to specifying",
212
- "the options --no-system, --no-site, --no-gems,",
213
- "and --no-home") do
228
+ "Use with --doc-dir") do
214
229
  options[:use_system] = false
215
230
  options[:use_site] = false
216
231
  options[:use_gems] = false
@@ -251,38 +266,12 @@ Options may also be set in the 'RI' environment variable.
251
266
  end
252
267
 
253
268
  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
-
269
+ opt.separator "Debug options:"
281
270
  opt.separator nil
282
271
 
283
- opt.on("--width=WIDTH", "-w", OptionParser::DecimalInteger,
284
- "Set the width of the output.") do |value|
285
- options[:width] = value
272
+ opt.on("--dump=CACHE", File,
273
+ "Dumps data from an ri cache or data file") do |value|
274
+ options[:dump_path] = value
286
275
  end
287
276
  end
288
277
 
@@ -292,7 +281,6 @@ Options may also be set in the 'RI' environment variable.
292
281
 
293
282
  options[:names] = argv
294
283
 
295
- options[:formatter] ||= RDoc::RI::Formatter.for('plain')
296
284
  options[:use_stdout] ||= !$stdout.tty?
297
285
  options[:use_stdout] ||= options[:interactive]
298
286
  options[:width] ||= 72
@@ -309,308 +297,438 @@ Options may also be set in the 'RI' environment variable.
309
297
  ##
310
298
  # Runs the ri command line executable using +argv+
311
299
 
312
- def self.run(argv = ARGV)
300
+ def self.run argv = ARGV
313
301
  options = process_args argv
302
+
303
+ if options[:dump_path] then
304
+ dump options[:dump_path]
305
+ return
306
+ end
307
+
314
308
  ri = new options
315
309
  ri.run
316
310
  end
317
311
 
318
- def initialize(initial_options={})
312
+ ##
313
+ # Creates a new driver using +initial_options+ from ::process_args
314
+
315
+ def initialize initial_options = {}
316
+ @paging = false
317
+ @classes = nil
318
+
319
319
  options = self.class.default_options.update(initial_options)
320
320
 
321
+ @formatter_klass = options[:formatter]
322
+
323
+ require 'profile' if options[:profile]
324
+
321
325
  @names = options[:names]
322
- @class_cache_name = 'classes'
323
326
 
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])
327
+ @doc_dirs = []
328
+ @stores = []
329
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]
330
+ RDoc::RI::Paths.each(options[:use_system], options[:use_site],
331
+ options[:use_home], options[:use_gems],
332
+ *options[:extra_doc_dirs]) do |path, type|
333
+ @doc_dirs << path
334
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"
335
+ store = RDoc::RI::Store.new path, type
336
+ store.load_cache
337
+ @stores << store
338
+ end
337
339
 
338
- @use_cache = options[:use_cache]
339
- @class_cache = nil
340
+ @list_doc_dirs = options[:list_doc_dirs]
340
341
 
341
342
  @interactive = options[:interactive]
342
- @display = RDoc::RI::DefaultDisplay.new(options[:formatter],
343
- options[:width],
344
- options[:use_stdout])
343
+ @use_stdout = options[:use_stdout]
345
344
  end
346
345
 
347
346
  ##
348
- # Cache of classes ri knows about
347
+ # Adds paths for undocumented classes +also_in+ to +out+
349
348
 
350
- def class_cache
351
- return @class_cache if @class_cache
349
+ def add_also_in out, also_in
350
+ return if also_in.empty?
352
351
 
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
352
+ out << RDoc::Markup::Rule.new(1)
353
+ out << RDoc::Markup::Paragraph.new("Also found in:")
354
+
355
+ paths = RDoc::Markup::Verbatim.new
356
+ also_in.each do |store|
357
+ paths.parts.push ' ', store.friendly_path, "\n"
390
358
  end
359
+ out << paths
360
+ end
391
361
 
392
- @class_cache
362
+ ##
363
+ # Adds a class header to +out+ for class +name+ which is described in
364
+ # +classes+.
365
+
366
+ def add_class out, name, classes
367
+ heading = if classes.all? { |klass| klass.module? } then
368
+ name
369
+ else
370
+ superclass = classes.map do |klass|
371
+ klass.superclass unless klass.module?
372
+ end.compact.shift || 'Object'
373
+
374
+ "#{name} < #{superclass}"
375
+ end
376
+
377
+ out << RDoc::Markup::Heading.new(1, heading)
378
+ out << RDoc::Markup::BlankLine.new
393
379
  end
394
380
 
395
381
  ##
396
- # Creates the class cache if it is empty
382
+ # Adds "(from ...)" to +out+ for +store+
397
383
 
398
- def create_class_cache
399
- class_cache = OpenStructHash.new
384
+ def add_from out, store
385
+ out << RDoc::Markup::Paragraph.new("(from #{store.friendly_path})")
386
+ end
400
387
 
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
388
+ ##
389
+ # Adds +includes+ to +out+
409
390
 
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
391
+ def add_includes out, includes
392
+ return if includes.empty?
413
393
 
414
- write_cache class_cache, class_cache_file_path
394
+ out << RDoc::Markup::Rule.new(1)
395
+ out << RDoc::Markup::Heading.new(1, "Includes:")
415
396
 
416
- class_cache
417
- end
397
+ includes.each do |modules, store|
398
+ if modules.length == 1 then
399
+ include = modules.first
400
+ name = include.name
401
+ path = store.friendly_path
402
+ out << RDoc::Markup::Paragraph.new("#{name} (from #{path})")
418
403
 
419
- ##
420
- # Populates +class_cache+ with +classes+, adding +extension+ data for found
421
- # methods when asked
404
+ if include.comment then
405
+ out << RDoc::Markup::BlankLine.new
406
+ out << include.comment
407
+ end
408
+ else
409
+ out << RDoc::Markup::Paragraph.new("(from #{store.friendly_path})")
422
410
 
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"]
411
+ wout, with = modules.partition { |incl| incl.comment.empty? }
427
412
 
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]
413
+ out << RDoc::Markup::BlankLine.new unless with.empty?
436
414
 
437
- if extension then
438
- desc["instance_method_extensions"] = desc.delete "instance_methods"
439
- desc["class_method_extensions"] = desc.delete "class_methods"
415
+ with.each do |incl|
416
+ out << RDoc::Markup::Paragraph.new(incl.name)
417
+ out << RDoc::Markup::BlankLine.new
418
+ out << incl.comment
440
419
  end
441
420
 
442
- klass.merge_enums desc
443
- klass["sources"] << cdesc
421
+ unless wout.empty? then
422
+ verb = RDoc::Markup::Verbatim.new
423
+
424
+ wout.each do |incl|
425
+ verb.push ' ', incl.name, "\n"
426
+ end
427
+
428
+ out << verb
429
+ end
444
430
  end
445
431
  end
446
432
  end
447
433
 
448
434
  ##
449
- # Path to the class_cache
435
+ # Adds a list of +methods+ to +out+ with a heading of +name+
450
436
 
451
- def class_cache_file_path
452
- File.join cache_file_path, @class_cache_name
437
+ def add_method_list out, methods, name
438
+ return unless methods
439
+
440
+ out << RDoc::Markup::Heading.new(1, "#{name}:")
441
+ out << RDoc::Markup::BlankLine.new
442
+
443
+ out.push(*methods.map do |method|
444
+ RDoc::Markup::Verbatim.new ' ', method
445
+ end)
446
+
447
+ out << RDoc::Markup::BlankLine.new
453
448
  end
454
449
 
455
450
  ##
456
- # Path to the cache file for +klassname+
451
+ # Returns ancestor classes of +klass+
452
+
453
+ def ancestors_of klass
454
+ ancestors = []
455
+
456
+ unexamined = [klass]
457
+ seen = []
458
+
459
+ loop do
460
+ break if unexamined.empty?
461
+ current = unexamined.shift
462
+ seen << current
463
+
464
+ stores = classes[current]
465
+
466
+ break unless stores and not stores.empty?
467
+
468
+ klasses = stores.map do |store|
469
+ store.ancestors[current]
470
+ end.flatten.uniq
457
471
 
458
- def cache_file_for(klassname)
459
- File.join cache_file_path, klassname.gsub(/:+/, "-")
472
+ klasses = klasses - seen
473
+
474
+ ancestors.push(*klasses)
475
+ unexamined.push(*klasses)
476
+ end
477
+
478
+ ancestors.reverse
460
479
  end
461
480
 
462
481
  ##
463
- # Directory where cache files live
482
+ # For RubyGems backwards compatibility
464
483
 
465
- def cache_file_path
466
- File.join @homepath, 'cache'
484
+ def class_cache # :nodoc:
467
485
  end
468
486
 
469
487
  ##
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.
488
+ # Hash mapping a known class or module to the stores it can be loaded from
472
489
 
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
490
+ def classes
491
+ return @classes if @classes
479
492
 
480
- klass = expand_klass klass unless class_cache.key? klass
493
+ @classes = {}
481
494
 
482
- orig_klass = klass
483
- orig_name = name
495
+ @stores.each do |store|
496
+ store.cache[:modules].each do |mod|
497
+ # using default block causes searched-for modules to be added
498
+ @classes[mod] ||= []
499
+ @classes[mod] << store
500
+ end
501
+ end
484
502
 
485
- loop do
486
- method = lookup_method name, klass
503
+ @classes
504
+ end
487
505
 
488
- break if method
506
+ ##
507
+ # Completes +name+ based on the caches. For Readline
489
508
 
490
- ancestor = lookup_ancestor klass, orig_klass
509
+ def complete name
510
+ klasses = classes.keys
511
+ completions = []
491
512
 
492
- break unless ancestor
513
+ klass, selector, method = parse_name name
493
514
 
494
- name = name.sub klass, ancestor
495
- klass = ancestor
496
- end
515
+ # may need to include Foo when given Foo::
516
+ klass_name = method ? name : klass
497
517
 
498
- raise NotFoundError, orig_name unless method
518
+ if name !~ /#|\./ then
519
+ completions.push(*klasses.grep(/^#{klass_name}/))
520
+ elsif selector then
521
+ completions << klass if classes.key? klass
522
+ elsif classes.key? klass_name then
523
+ completions << klass_name
524
+ end
499
525
 
500
- display_method method
501
- else
502
- methods = select_methods(/#{name}/)
526
+ if completions.include? klass and name =~ /#|\.|::/ then
527
+ methods = list_methods_matching name
503
528
 
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
529
+ if not methods.empty? then
530
+ # remove Foo if given Foo:: and a method was found
531
+ completions.delete klass
532
+ elsif selector then
533
+ # replace Foo with Foo:: as given
534
+ completions.delete klass
535
+ completions << "#{klass}#{selector}"
510
536
  end
537
+
538
+ completions.push(*methods)
511
539
  end
540
+
541
+ completions.sort
512
542
  end
513
543
 
514
544
  ##
515
- # Displays info for class or module +name+
545
+ # Converts +document+ to text and writes it to the pager
516
546
 
517
- def display_class(name)
518
- klass = class_cache[name]
519
- @display.display_class_info klass
547
+ def display document
548
+ page do |io|
549
+ text = document.accept formatter
550
+
551
+ io.write text
552
+ end
520
553
  end
521
554
 
522
555
  ##
523
- # Displays info for method +method+
556
+ # Outputs formatted RI data for class +name+. Groups undocumented classes
557
+
558
+ def display_class name
559
+ return if name =~ /#|\./
560
+
561
+ klasses = []
562
+ includes = []
563
+
564
+ found = @stores.map do |store|
565
+ begin
566
+ klass = store.load_class name
567
+ klasses << klass
568
+ includes << [klass.includes, store] if klass.includes
569
+ [store, klass]
570
+ rescue Errno::ENOENT
571
+ end
572
+ end.compact
524
573
 
525
- def display_method(method)
526
- @display.display_method_info method
574
+ return if found.empty?
575
+
576
+ also_in = []
577
+
578
+ includes.reject! do |modules,| modules.empty? end
579
+
580
+ out = RDoc::Markup::Document.new
581
+
582
+ add_class out, name, klasses
583
+
584
+ add_includes out, includes
585
+
586
+ found.each do |store, klass|
587
+ comment = klass.comment
588
+ class_methods = store.class_methods[klass.full_name]
589
+ instance_methods = store.instance_methods[klass.full_name]
590
+ attributes = store.attributes[klass.full_name]
591
+
592
+ if comment.empty? and !(instance_methods or class_methods) then
593
+ also_in << store
594
+ next
595
+ end
596
+
597
+ add_from out, store
598
+
599
+ unless comment.empty? then
600
+ out << RDoc::Markup::Rule.new(1)
601
+ out << comment
602
+ end
603
+
604
+ if class_methods or instance_methods or not klass.constants.empty? then
605
+ out << RDoc::Markup::Rule.new
606
+ end
607
+
608
+ unless klass.constants.empty? then
609
+ out << RDoc::Markup::Heading.new(1, "Constants:")
610
+ out << RDoc::Markup::BlankLine.new
611
+ list = RDoc::Markup::List.new :NOTE
612
+
613
+ constants = klass.constants.sort_by { |constant| constant.name }
614
+
615
+ list.push(*constants.map do |constant|
616
+ parts = constant.comment.parts if constant.comment
617
+ parts << RDoc::Markup::Paragraph.new('[not documented]') if
618
+ parts.empty?
619
+
620
+ RDoc::Markup::ListItem.new(constant.name, *parts)
621
+ end)
622
+
623
+ out << list
624
+ end
625
+
626
+ add_method_list out, class_methods, 'Class methods'
627
+ add_method_list out, instance_methods, 'Instance methods'
628
+ add_method_list out, attributes, 'Attributes'
629
+
630
+ out << RDoc::Markup::BlankLine.new
631
+ end
632
+
633
+ add_also_in out, also_in
634
+
635
+ display out
527
636
  end
528
637
 
529
638
  ##
530
- # Runs ri interactively using Readline if it is available.
639
+ # Outputs formatted RI data for method +name+
531
640
 
532
- def interactive
533
- formatter = @display.formatter
641
+ def display_method name
642
+ found = load_methods_matching name
534
643
 
535
- if defined? Readline then
536
- # prepare abbreviations for tab completion
537
- klasses = class_cache.keys
644
+ raise NotFoundError, name if found.empty?
538
645
 
539
- Readline.completion_proc = proc do |name|
540
- case name
541
- when /(#|\.|::)([^A-Z]|$)/ then
542
- methods = []
543
- method_type = $1 == '.' ? '#|::' : $1
646
+ out = RDoc::Markup::Document.new
544
647
 
545
- klass, method = if $2.empty? then
546
- [$`, '']
547
- else
548
- parse_name name
549
- end
648
+ out << RDoc::Markup::Heading.new(1, name)
649
+ out << RDoc::Markup::BlankLine.new
550
650
 
551
- cache = load_cache_for klass
651
+ found.each do |store, methods|
652
+ methods.each do |method|
653
+ out << RDoc::Markup::Paragraph.new("(from #{store.friendly_path})")
552
654
 
553
- methods += cache.keys.select do |method_name|
554
- method_name =~ /^#{klass}#{method_type}#{method}/
555
- end
556
-
557
- # TODO ancestor lookup
655
+ unless name =~ /^#{Regexp.escape method.parent_name}/ then
656
+ out << RDoc::Markup::Heading.new(3, "Implementation from #{method.parent_name}")
657
+ end
658
+ out << RDoc::Markup::Rule.new(1)
558
659
 
559
- if method_type == '::' and method.empty? then
560
- methods += klasses.grep(/^#{klass}::/)
561
- end
660
+ if method.call_seq then
661
+ call_seq = method.call_seq.chomp.split "\n"
662
+ call_seq = call_seq.map { |line| [' ', line, "\n"] }
663
+ out << RDoc::Markup::Verbatim.new(*call_seq.flatten)
664
+ end
562
665
 
563
- methods
564
- when /^[A-Z]\w*/ then
565
- klasses.grep(/^#{name}/)
566
- else
567
- []
666
+ if method.block_params then
667
+ out << RDoc::Markup::BlankLine.new if method.call_seq
668
+ params = "yields: #{method.block_params}"
669
+ out << RDoc::Markup::Verbatim.new(' ', params, "\n")
568
670
  end
671
+
672
+ out << RDoc::Markup::Rule.new(1) if
673
+ method.call_seq or method.block_params
674
+
675
+ out << RDoc::Markup::BlankLine.new
676
+ out << method.comment
677
+ out << RDoc::Markup::BlankLine.new
569
678
  end
570
679
  end
571
680
 
572
- formatter.raw_print_line "\nEnter the method name you want to look up.\n"
681
+ display out
682
+ end
573
683
 
574
- if defined? Readline then
575
- formatter.raw_print_line "You can use tab to autocomplete.\n"
576
- end
684
+ ##
685
+ # Outputs formatted RI data for the class or method +name+.
686
+ #
687
+ # Returns true if +name+ was found, false if it was not an alternative could
688
+ # be guessed, raises an error if +name+ couldn't be guessed.
577
689
 
578
- formatter.raw_print_line "Enter a blank line to exit.\n\n"
690
+ def display_name name
691
+ return true if display_class name
579
692
 
580
- loop do
581
- name = if defined? Readline then
582
- Readline.readline ">> "
583
- else
584
- formatter.raw_print_line ">> "
585
- $stdin.gets
586
- end
693
+ display_method name if name =~ /::|#|\./
587
694
 
588
- return if name.nil? or name.empty?
695
+ true
696
+ rescue NotFoundError
697
+ matches = list_methods_matching name if name =~ /::|#|\./
698
+ matches = classes.keys.grep(/^#{name}/) if matches.empty?
589
699
 
590
- name = name.strip
700
+ raise if matches.empty?
591
701
 
592
- begin
593
- display_name name
594
- rescue NotFoundError => e
595
- formatter.raw_print_line "#{e.message}\n"
596
- end
702
+ page do |io|
703
+ io.puts "#{name} not found, maybe you meant:"
704
+ io.puts
705
+ io.puts matches.join("\n")
597
706
  end
598
707
 
599
- rescue Interrupt
600
- exit
708
+ false
601
709
  end
602
710
 
603
711
  ##
604
- # Expands abbreviated klass +klass+ into a fully-qualified klass. "Zl::Da"
712
+ # Displays each name in +name+
713
+
714
+ def display_names names
715
+ names.each do |name|
716
+ name = expand_name name
717
+
718
+ display_name name
719
+ end
720
+ end
721
+ ##
722
+ # Expands abbreviated klass +klass+ into a fully-qualified class. "Zl::Da"
605
723
  # will be expanded to Zlib::DataError.
606
724
 
607
- def expand_klass(klass)
725
+ def expand_class klass
608
726
  klass.split('::').inject '' do |expanded, klass_part|
609
727
  expanded << '::' unless expanded.empty?
610
728
  short = expanded << klass_part
611
729
 
612
- subset = class_cache.keys.select do |klass_name|
613
- klass =~ /^#{expanded}[^:]*$/
730
+ subset = classes.keys.select do |klass_name|
731
+ klass_name =~ /^#{expanded}[^:]*$/
614
732
  end
615
733
 
616
734
  abbrevs = Abbrev.abbrev subset
@@ -624,152 +742,277 @@ Options may also be set in the 'RI' environment variable.
624
742
  end
625
743
 
626
744
  ##
627
- # Loads the cache for +klassname+
745
+ # Expands the class portion of +name+ into a fully-qualified class. See
746
+ # #expand_class.
628
747
 
629
- def load_cache_for(klassname)
630
- path = cache_file_for klassname
748
+ def expand_name name
749
+ klass, selector, method = parse_name name
631
750
 
632
- cache = nil
751
+ return [selector, method].join if klass.empty?
633
752
 
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
753
+ "#{expand_class klass}#{selector}#{method}"
754
+ end
755
+
756
+ ##
757
+ # Yields items matching +name+ including the store they were found in, the
758
+ # class being searched for, the class they were found in (an ancestor) the
759
+ # types of methods to look up (from #method_type), and the method name being
760
+ # searched for
761
+
762
+ def find_methods name
763
+ klass, selector, method = parse_name name
764
+
765
+ types = method_type selector
766
+
767
+ klasses = nil
768
+ ambiguous = klass.empty?
769
+
770
+ if ambiguous then
771
+ klasses = classes.keys
648
772
  else
649
- cache = create_cache_for klassname, path
773
+ klasses = ancestors_of klass
774
+ klasses.unshift klass
775
+ end
776
+
777
+ methods = []
778
+
779
+ klasses.each do |ancestor|
780
+ ancestors = classes[ancestor]
781
+
782
+ next unless ancestors
783
+
784
+ klass = ancestor if ambiguous
785
+
786
+ ancestors.each do |store|
787
+ methods << [store, klass, ancestor, types, method]
788
+ end
789
+ end
790
+
791
+ methods = methods.sort_by do |_, k, a, _, m|
792
+ [k, a, m].compact
650
793
  end
651
794
 
652
- cache
795
+ methods.each do |item|
796
+ yield(*item)
797
+ end
798
+
799
+ self
653
800
  end
654
801
 
655
802
  ##
656
- # Writes a cache file for +klassname+ to +path+
803
+ # Creates a new RDoc::Markup::Formatter. If a formatter is given with -f,
804
+ # use it. If we're outputting to a pager, use bs, otherwise ansi.
805
+
806
+ def formatter
807
+ if @formatter_klass then
808
+ @formatter_klass.new
809
+ elsif paging? then
810
+ RDoc::Markup::ToBs.new
811
+ else
812
+ RDoc::Markup::ToAnsi.new
813
+ end
814
+ end
657
815
 
658
- def create_cache_for(klassname, path)
659
- klass = class_cache[klassname]
660
- return nil unless klass
816
+ ##
817
+ # Runs ri interactively using Readline if it is available.
661
818
 
662
- method_files = klass["sources"]
663
- cache = OpenStructHash.new
819
+ def interactive
820
+ puts "\nEnter the method name you want to look up."
664
821
 
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$/
822
+ if defined? Readline then
823
+ Readline.completion_proc = method :complete
824
+ puts "You can use tab to autocomplete."
825
+ end
670
826
 
671
- method = read_yaml yaml
827
+ puts "Enter a blank line to exit.\n\n"
672
828
 
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', '(.*?)', ''
829
+ loop do
830
+ name = if defined? Readline then
831
+ Readline.readline ">> "
832
+ else
833
+ print ">> "
834
+ $stdin.gets
835
+ end
678
836
 
679
- f =~ /^#{pattern}/
680
- end
837
+ return if name.nil? or name.empty?
681
838
 
682
- method["source_path"] = if gem then
683
- "gem #{$1}"
684
- else
685
- f
686
- end
687
- end
839
+ name = expand_name name.strip
688
840
 
689
- name = method["full_name"]
690
- cache[name] = method
841
+ begin
842
+ display_name name
843
+ rescue NotFoundError => e
844
+ puts e.message
691
845
  end
692
846
  end
693
847
 
694
- write_cache cache, path
848
+ rescue Interrupt
849
+ exit
695
850
  end
696
851
 
697
852
  ##
698
- # Finds the next ancestor of +orig_klass+ after +klass+.
853
+ # Lists classes known to ri
854
+
855
+ def list_known_classes
856
+ classes = []
699
857
 
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
858
+ stores.each do |store|
859
+ classes << store.modules
707
860
  end
708
861
 
709
- cache = class_cache[orig_klass]
862
+ classes = classes.flatten.uniq.sort
710
863
 
711
- return nil unless cache
864
+ page do |io|
865
+ if paging? or io.tty? then
866
+ io.puts "Classes and Modules known to ri:"
867
+ io.puts
868
+ end
869
+
870
+ io.puts classes.join("\n")
871
+ end
872
+ end
873
+
874
+ ##
875
+ # Returns an Array of methods matching +name+
876
+
877
+ def list_methods_matching name
878
+ found = []
879
+
880
+ find_methods name do |store, klass, ancestor, types, method|
881
+ if types == :instance or types == :both then
882
+ methods = store.instance_methods[ancestor]
883
+
884
+ if methods then
885
+ matches = methods.grep(/^#{method}/)
886
+
887
+ matches = matches.map do |match|
888
+ "#{klass}##{match}"
889
+ end
890
+
891
+ found.push(*matches)
892
+ end
893
+ end
894
+
895
+ if types == :class or types == :both then
896
+ methods = store.class_methods[ancestor]
712
897
 
713
- ancestors = [orig_klass]
714
- ancestors.push(*cache.includes.map { |inc| inc['name'] })
715
- ancestors << cache.superclass
898
+ next unless methods
899
+ matches = methods.grep(/^#{method}/)
716
900
 
717
- ancestor_index = ancestors.index klass
901
+ matches = matches.map do |match|
902
+ "#{klass}::#{match}"
903
+ end
718
904
 
719
- if ancestor_index
720
- ancestor = ancestors[ancestors.index(klass) + 1]
721
- return ancestor if ancestor
905
+ found.push(*matches)
906
+ end
722
907
  end
723
908
 
724
- lookup_ancestor klass, cache.superclass
909
+ found.uniq
725
910
  end
726
911
 
727
912
  ##
728
- # Finds the method
913
+ # Loads RI data for method +name+ on +klass+ from +store+. +type+ and
914
+ # +cache+ indicate if it is a class or instance method.
915
+
916
+ def load_method store, cache, klass, type, name
917
+ methods = store.send(cache)[klass]
918
+
919
+ return unless methods
920
+
921
+ method = methods.find do |method_name|
922
+ method_name == name
923
+ end
729
924
 
730
- def lookup_method(name, klass)
731
- cache = load_cache_for klass
732
- return nil unless cache
925
+ return unless method
733
926
 
734
- method = cache[name.gsub('.', '#')]
735
- method = cache[name.gsub('.', '::')] unless method
736
- method
927
+ store.load_method klass, "#{type}#{method}"
737
928
  end
738
929
 
739
- def map_dirs(file_name)
740
- @doc_dirs.map { |dir| yield File.join(dir, file_name) }.flatten.compact
930
+ ##
931
+ # Returns an Array of RI data for methods matching +name+
932
+
933
+ def load_methods_matching name
934
+ found = []
935
+
936
+ find_methods name do |store, klass, ancestor, types, method|
937
+ methods = []
938
+
939
+ methods << load_method(store, :class_methods, ancestor, '::', method) if
940
+ types == :class or types == :both
941
+
942
+ methods << load_method(store, :instance_methods, ancestor, '#', method) if
943
+ types == :instance or types == :both
944
+
945
+ found << [store, methods.compact]
946
+ end
947
+
948
+ found.reject do |path, methods| methods.empty? end
741
949
  end
742
950
 
743
951
  ##
744
- # Extract the class and method name parts from +name+ like Foo::Bar#baz
952
+ # Returns the type of method (:both, :instance, :class) for +selector+
745
953
 
746
- def parse_name(name)
747
- parts = name.split(/(::|\#|\.)/)
954
+ def method_type selector
955
+ case selector
956
+ when '.', nil then :both
957
+ when '#' then :instance
958
+ else :class
959
+ end
960
+ end
748
961
 
749
- if parts[-2] != '::' or parts.last !~ /^[A-Z]/ then
750
- meth = parts.pop
751
- parts.pop
962
+ ##
963
+ # Paginates output through a pager program.
964
+
965
+ def page
966
+ if pager = setup_pager then
967
+ begin
968
+ yield pager
969
+ ensure
970
+ pager.close
971
+ end
972
+ else
973
+ yield $stdout
752
974
  end
975
+ rescue Errno::EPIPE
976
+ ensure
977
+ @paging = false
978
+ end
753
979
 
754
- klass = parts.join
980
+ ##
981
+ # Are we using a pager?
755
982
 
756
- [klass, meth]
983
+ def paging?
984
+ @paging
757
985
  end
758
986
 
759
987
  ##
760
- # Reads ri YAML data from +path+, converting RDoc 1.x classes to RDoc 2.x
761
- # classes
988
+ # Extract the class, selector and method name parts from +name+ like
989
+ # Foo::Bar#baz.
990
+ #
991
+ # NOTE: Given Foo::Bar, Bar is considered a class even though it may be a
992
+ # method
993
+
994
+ def parse_name(name)
995
+ parts = name.split(/(::|#|\.)/)
762
996
 
763
- def read_yaml(path)
764
- data = File.read path
997
+ if parts.length == 1 then
998
+ if parts.first =~ /^[a-z]/ then
999
+ type = '.'
1000
+ meth = parts.pop
1001
+ else
1002
+ type = nil
1003
+ meth = nil
1004
+ end
1005
+ elsif parts.length == 2 or parts.last =~ /::|#|\./ then
1006
+ type = parts.pop
1007
+ meth = nil
1008
+ elsif parts[-2] != '::' or parts.last !~ /^[A-Z]/ then
1009
+ meth = parts.pop
1010
+ type = parts.pop
1011
+ end
765
1012
 
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')
1013
+ klass = parts.join
771
1014
 
772
- OpenStructHash.convert YAML.load(data)
1015
+ [klass, type, meth]
773
1016
  end
774
1017
 
775
1018
  ##
@@ -781,47 +1024,34 @@ Options may also be set in the 'RI' environment variable.
781
1024
  elsif @interactive then
782
1025
  interactive
783
1026
  elsif @names.empty? then
784
- @display.list_known_classes class_cache.keys.sort
1027
+ list_known_classes
785
1028
  else
786
- @names.each do |name|
787
- display_name name
788
- end
1029
+ display_names @names
789
1030
  end
790
1031
  rescue NotFoundError => e
791
1032
  abort e.message
792
1033
  end
793
1034
 
794
1035
  ##
795
- # Selects methods matching +pattern+ from all modules
1036
+ # Sets up a pager program to pass output through. Tries the RI_PAGER and
1037
+ # PAGER environment variables followed by pager, less then more.
796
1038
 
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
1039
+ def setup_pager
1040
+ return if @use_stdout
811
1041
 
812
- ##
813
- # Writes +cache+ to +path+
1042
+ pagers = [ENV['RI_PAGER'], ENV['PAGER'], 'pager', 'less', 'more']
814
1043
 
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
1044
+ pagers.compact.uniq.each do |pager|
1045
+ io = IO.popen(pager, "w") rescue next
1046
+
1047
+ @paging = true
1048
+
1049
+ return io
820
1050
  end
821
1051
 
822
- cache
823
- rescue Errno::EISDIR # HACK toplevel, replace with main
824
- cache
1052
+ @use_stdout = true
1053
+
1054
+ nil
825
1055
  end
826
1056
 
827
1057
  end