iownbey-rdoc 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. data/History.txt +13 -0
  2. data/Manifest.txt +61 -0
  3. data/README.txt +34 -0
  4. data/bin/rdoc +22 -0
  5. data/bin/ri +6 -0
  6. data/lib/rdoc.rb +277 -0
  7. data/lib/rdoc/code_objects.rb +776 -0
  8. data/lib/rdoc/diagram.rb +338 -0
  9. data/lib/rdoc/dot.rb +249 -0
  10. data/lib/rdoc/generator.rb +1050 -0
  11. data/lib/rdoc/generator/chm.rb +113 -0
  12. data/lib/rdoc/generator/chm/chm.rb +98 -0
  13. data/lib/rdoc/generator/html.rb +370 -0
  14. data/lib/rdoc/generator/html/hefss.rb +414 -0
  15. data/lib/rdoc/generator/html/html.rb +704 -0
  16. data/lib/rdoc/generator/html/kilmer.rb +418 -0
  17. data/lib/rdoc/generator/html/one_page_html.rb +121 -0
  18. data/lib/rdoc/generator/ri.rb +229 -0
  19. data/lib/rdoc/generator/texinfo.rb +84 -0
  20. data/lib/rdoc/generator/texinfo/class.texinfo.erb +44 -0
  21. data/lib/rdoc/generator/texinfo/file.texinfo.erb +6 -0
  22. data/lib/rdoc/generator/texinfo/method.texinfo.erb +6 -0
  23. data/lib/rdoc/generator/texinfo/texinfo.erb +28 -0
  24. data/lib/rdoc/generator/xml.rb +120 -0
  25. data/lib/rdoc/generator/xml/rdf.rb +113 -0
  26. data/lib/rdoc/generator/xml/xml.rb +111 -0
  27. data/lib/rdoc/markup.rb +473 -0
  28. data/lib/rdoc/markup/attribute_manager.rb +274 -0
  29. data/lib/rdoc/markup/formatter.rb +14 -0
  30. data/lib/rdoc/markup/fragments.rb +337 -0
  31. data/lib/rdoc/markup/inline.rb +101 -0
  32. data/lib/rdoc/markup/lines.rb +152 -0
  33. data/lib/rdoc/markup/preprocess.rb +71 -0
  34. data/lib/rdoc/markup/to_flow.rb +185 -0
  35. data/lib/rdoc/markup/to_html.rb +354 -0
  36. data/lib/rdoc/markup/to_html_crossref.rb +86 -0
  37. data/lib/rdoc/markup/to_latex.rb +328 -0
  38. data/lib/rdoc/markup/to_test.rb +50 -0
  39. data/lib/rdoc/markup/to_texinfo.rb +69 -0
  40. data/lib/rdoc/options.rb +621 -0
  41. data/lib/rdoc/parsers/parse_c.rb +775 -0
  42. data/lib/rdoc/parsers/parse_f95.rb +1841 -0
  43. data/lib/rdoc/parsers/parse_rb.rb +2584 -0
  44. data/lib/rdoc/parsers/parse_simple.rb +40 -0
  45. data/lib/rdoc/parsers/parserfactory.rb +99 -0
  46. data/lib/rdoc/rdoc.rb +277 -0
  47. data/lib/rdoc/ri.rb +4 -0
  48. data/lib/rdoc/ri/cache.rb +188 -0
  49. data/lib/rdoc/ri/descriptions.rb +150 -0
  50. data/lib/rdoc/ri/display.rb +274 -0
  51. data/lib/rdoc/ri/driver.rb +452 -0
  52. data/lib/rdoc/ri/formatter.rb +616 -0
  53. data/lib/rdoc/ri/paths.rb +102 -0
  54. data/lib/rdoc/ri/reader.rb +106 -0
  55. data/lib/rdoc/ri/util.rb +81 -0
  56. data/lib/rdoc/ri/writer.rb +68 -0
  57. data/lib/rdoc/stats.rb +25 -0
  58. data/lib/rdoc/template.rb +64 -0
  59. data/lib/rdoc/tokenstream.rb +33 -0
  60. data/test/test_rdoc_c_parser.rb +261 -0
  61. data/test/test_rdoc_info_formatting.rb +179 -0
  62. data/test/test_rdoc_info_sections.rb +93 -0
  63. data/test/test_rdoc_markup.rb +613 -0
  64. data/test/test_rdoc_markup_attribute_manager.rb +224 -0
  65. data/test/test_rdoc_ri_attribute_formatter.rb +42 -0
  66. data/test/test_rdoc_ri_default_display.rb +295 -0
  67. data/test/test_rdoc_ri_formatter.rb +318 -0
  68. data/test/test_rdoc_ri_overstrike_formatter.rb +69 -0
  69. metadata +142 -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
+