gitlab-rdoc 6.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (196) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTING.rdoc +220 -0
  3. data/CVE-2013-0256.rdoc +49 -0
  4. data/ExampleMarkdown.md +37 -0
  5. data/ExampleRDoc.rdoc +208 -0
  6. data/Gemfile +12 -0
  7. data/History.rdoc +1666 -0
  8. data/LEGAL.rdoc +50 -0
  9. data/LICENSE.rdoc +57 -0
  10. data/README.rdoc +133 -0
  11. data/RI.rdoc +57 -0
  12. data/Rakefile +101 -0
  13. data/TODO.rdoc +59 -0
  14. data/bin/console +7 -0
  15. data/bin/setup +6 -0
  16. data/exe/rdoc +44 -0
  17. data/exe/ri +12 -0
  18. data/lib/rdoc/alias.rb +112 -0
  19. data/lib/rdoc/anon_class.rb +11 -0
  20. data/lib/rdoc/any_method.rb +361 -0
  21. data/lib/rdoc/attr.rb +176 -0
  22. data/lib/rdoc/class_module.rb +802 -0
  23. data/lib/rdoc/code_object.rb +421 -0
  24. data/lib/rdoc/code_objects.rb +6 -0
  25. data/lib/rdoc/comment.rb +250 -0
  26. data/lib/rdoc/constant.rb +187 -0
  27. data/lib/rdoc/context/section.rb +232 -0
  28. data/lib/rdoc/context.rb +1266 -0
  29. data/lib/rdoc/cross_reference.rb +202 -0
  30. data/lib/rdoc/encoding.rb +136 -0
  31. data/lib/rdoc/erb_partial.rb +19 -0
  32. data/lib/rdoc/erbio.rb +42 -0
  33. data/lib/rdoc/extend.rb +10 -0
  34. data/lib/rdoc/generator/darkfish.rb +790 -0
  35. data/lib/rdoc/generator/json_index.rb +300 -0
  36. data/lib/rdoc/generator/markup.rb +160 -0
  37. data/lib/rdoc/generator/pot/message_extractor.rb +68 -0
  38. data/lib/rdoc/generator/pot/po.rb +84 -0
  39. data/lib/rdoc/generator/pot/po_entry.rb +141 -0
  40. data/lib/rdoc/generator/pot.rb +98 -0
  41. data/lib/rdoc/generator/ri.rb +31 -0
  42. data/lib/rdoc/generator/template/darkfish/.document +0 -0
  43. data/lib/rdoc/generator/template/darkfish/_footer.rhtml +5 -0
  44. data/lib/rdoc/generator/template/darkfish/_head.rhtml +22 -0
  45. data/lib/rdoc/generator/template/darkfish/_sidebar_VCS_info.rhtml +19 -0
  46. data/lib/rdoc/generator/template/darkfish/_sidebar_classes.rhtml +9 -0
  47. data/lib/rdoc/generator/template/darkfish/_sidebar_extends.rhtml +15 -0
  48. data/lib/rdoc/generator/template/darkfish/_sidebar_in_files.rhtml +9 -0
  49. data/lib/rdoc/generator/template/darkfish/_sidebar_includes.rhtml +15 -0
  50. data/lib/rdoc/generator/template/darkfish/_sidebar_installed.rhtml +15 -0
  51. data/lib/rdoc/generator/template/darkfish/_sidebar_methods.rhtml +12 -0
  52. data/lib/rdoc/generator/template/darkfish/_sidebar_navigation.rhtml +11 -0
  53. data/lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml +12 -0
  54. data/lib/rdoc/generator/template/darkfish/_sidebar_parent.rhtml +11 -0
  55. data/lib/rdoc/generator/template/darkfish/_sidebar_search.rhtml +14 -0
  56. data/lib/rdoc/generator/template/darkfish/_sidebar_sections.rhtml +11 -0
  57. data/lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml +18 -0
  58. data/lib/rdoc/generator/template/darkfish/class.rhtml +172 -0
  59. data/lib/rdoc/generator/template/darkfish/css/fonts.css +167 -0
  60. data/lib/rdoc/generator/template/darkfish/css/rdoc.css +639 -0
  61. data/lib/rdoc/generator/template/darkfish/fonts/Lato-Light.ttf +0 -0
  62. data/lib/rdoc/generator/template/darkfish/fonts/Lato-LightItalic.ttf +0 -0
  63. data/lib/rdoc/generator/template/darkfish/fonts/Lato-Regular.ttf +0 -0
  64. data/lib/rdoc/generator/template/darkfish/fonts/Lato-RegularItalic.ttf +0 -0
  65. data/lib/rdoc/generator/template/darkfish/fonts/SourceCodePro-Bold.ttf +0 -0
  66. data/lib/rdoc/generator/template/darkfish/fonts/SourceCodePro-Regular.ttf +0 -0
  67. data/lib/rdoc/generator/template/darkfish/images/add.png +0 -0
  68. data/lib/rdoc/generator/template/darkfish/images/arrow_up.png +0 -0
  69. data/lib/rdoc/generator/template/darkfish/images/brick.png +0 -0
  70. data/lib/rdoc/generator/template/darkfish/images/brick_link.png +0 -0
  71. data/lib/rdoc/generator/template/darkfish/images/bug.png +0 -0
  72. data/lib/rdoc/generator/template/darkfish/images/bullet_black.png +0 -0
  73. data/lib/rdoc/generator/template/darkfish/images/bullet_toggle_minus.png +0 -0
  74. data/lib/rdoc/generator/template/darkfish/images/bullet_toggle_plus.png +0 -0
  75. data/lib/rdoc/generator/template/darkfish/images/date.png +0 -0
  76. data/lib/rdoc/generator/template/darkfish/images/delete.png +0 -0
  77. data/lib/rdoc/generator/template/darkfish/images/find.png +0 -0
  78. data/lib/rdoc/generator/template/darkfish/images/loadingAnimation.gif +0 -0
  79. data/lib/rdoc/generator/template/darkfish/images/macFFBgHack.png +0 -0
  80. data/lib/rdoc/generator/template/darkfish/images/package.png +0 -0
  81. data/lib/rdoc/generator/template/darkfish/images/page_green.png +0 -0
  82. data/lib/rdoc/generator/template/darkfish/images/page_white_text.png +0 -0
  83. data/lib/rdoc/generator/template/darkfish/images/page_white_width.png +0 -0
  84. data/lib/rdoc/generator/template/darkfish/images/plugin.png +0 -0
  85. data/lib/rdoc/generator/template/darkfish/images/ruby.png +0 -0
  86. data/lib/rdoc/generator/template/darkfish/images/tag_blue.png +0 -0
  87. data/lib/rdoc/generator/template/darkfish/images/tag_green.png +0 -0
  88. data/lib/rdoc/generator/template/darkfish/images/transparent.png +0 -0
  89. data/lib/rdoc/generator/template/darkfish/images/wrench.png +0 -0
  90. data/lib/rdoc/generator/template/darkfish/images/wrench_orange.png +0 -0
  91. data/lib/rdoc/generator/template/darkfish/images/zoom.png +0 -0
  92. data/lib/rdoc/generator/template/darkfish/index.rhtml +22 -0
  93. data/lib/rdoc/generator/template/darkfish/js/darkfish.js +84 -0
  94. data/lib/rdoc/generator/template/darkfish/js/search.js +110 -0
  95. data/lib/rdoc/generator/template/darkfish/page.rhtml +18 -0
  96. data/lib/rdoc/generator/template/darkfish/servlet_not_found.rhtml +18 -0
  97. data/lib/rdoc/generator/template/darkfish/servlet_root.rhtml +62 -0
  98. data/lib/rdoc/generator/template/darkfish/table_of_contents.rhtml +58 -0
  99. data/lib/rdoc/generator/template/json_index/.document +1 -0
  100. data/lib/rdoc/generator/template/json_index/js/navigation.js +105 -0
  101. data/lib/rdoc/generator/template/json_index/js/searcher.js +229 -0
  102. data/lib/rdoc/generator.rb +51 -0
  103. data/lib/rdoc/ghost_method.rb +7 -0
  104. data/lib/rdoc/i18n/locale.rb +102 -0
  105. data/lib/rdoc/i18n/text.rb +126 -0
  106. data/lib/rdoc/i18n.rb +10 -0
  107. data/lib/rdoc/include.rb +10 -0
  108. data/lib/rdoc/known_classes.rb +73 -0
  109. data/lib/rdoc/markdown/entities.rb +2132 -0
  110. data/lib/rdoc/markdown/literals.kpeg +23 -0
  111. data/lib/rdoc/markdown/literals.rb +417 -0
  112. data/lib/rdoc/markdown.kpeg +1237 -0
  113. data/lib/rdoc/markdown.rb +16685 -0
  114. data/lib/rdoc/markup/attr_changer.rb +23 -0
  115. data/lib/rdoc/markup/attr_span.rb +36 -0
  116. data/lib/rdoc/markup/attribute_manager.rb +409 -0
  117. data/lib/rdoc/markup/attributes.rb +71 -0
  118. data/lib/rdoc/markup/blank_line.rb +28 -0
  119. data/lib/rdoc/markup/block_quote.rb +15 -0
  120. data/lib/rdoc/markup/document.rb +165 -0
  121. data/lib/rdoc/markup/formatter.rb +266 -0
  122. data/lib/rdoc/markup/hard_break.rb +32 -0
  123. data/lib/rdoc/markup/heading.rb +79 -0
  124. data/lib/rdoc/markup/include.rb +43 -0
  125. data/lib/rdoc/markup/indented_paragraph.rb +48 -0
  126. data/lib/rdoc/markup/list.rb +102 -0
  127. data/lib/rdoc/markup/list_item.rb +100 -0
  128. data/lib/rdoc/markup/paragraph.rb +29 -0
  129. data/lib/rdoc/markup/parser.rb +575 -0
  130. data/lib/rdoc/markup/pre_process.rb +296 -0
  131. data/lib/rdoc/markup/raw.rb +70 -0
  132. data/lib/rdoc/markup/regexp_handling.rb +41 -0
  133. data/lib/rdoc/markup/rule.rb +21 -0
  134. data/lib/rdoc/markup/table.rb +47 -0
  135. data/lib/rdoc/markup/to_ansi.rb +94 -0
  136. data/lib/rdoc/markup/to_bs.rb +77 -0
  137. data/lib/rdoc/markup/to_html.rb +444 -0
  138. data/lib/rdoc/markup/to_html_crossref.rb +176 -0
  139. data/lib/rdoc/markup/to_html_snippet.rb +285 -0
  140. data/lib/rdoc/markup/to_joined_paragraph.rb +47 -0
  141. data/lib/rdoc/markup/to_label.rb +75 -0
  142. data/lib/rdoc/markup/to_markdown.rb +192 -0
  143. data/lib/rdoc/markup/to_rdoc.rb +362 -0
  144. data/lib/rdoc/markup/to_table_of_contents.rb +89 -0
  145. data/lib/rdoc/markup/to_test.rb +70 -0
  146. data/lib/rdoc/markup/to_tt_only.rb +121 -0
  147. data/lib/rdoc/markup/verbatim.rb +84 -0
  148. data/lib/rdoc/markup.rb +867 -0
  149. data/lib/rdoc/meta_method.rb +7 -0
  150. data/lib/rdoc/method_attr.rb +419 -0
  151. data/lib/rdoc/mixin.rb +121 -0
  152. data/lib/rdoc/normal_class.rb +93 -0
  153. data/lib/rdoc/normal_module.rb +74 -0
  154. data/lib/rdoc/options.rb +1285 -0
  155. data/lib/rdoc/parser/c.rb +1225 -0
  156. data/lib/rdoc/parser/changelog.rb +335 -0
  157. data/lib/rdoc/parser/markdown.rb +24 -0
  158. data/lib/rdoc/parser/rd.rb +23 -0
  159. data/lib/rdoc/parser/ripper_state_lex.rb +590 -0
  160. data/lib/rdoc/parser/ruby.rb +2327 -0
  161. data/lib/rdoc/parser/ruby_tools.rb +167 -0
  162. data/lib/rdoc/parser/simple.rb +61 -0
  163. data/lib/rdoc/parser/text.rb +12 -0
  164. data/lib/rdoc/parser.rb +277 -0
  165. data/lib/rdoc/rd/block_parser.rb +1056 -0
  166. data/lib/rdoc/rd/block_parser.ry +639 -0
  167. data/lib/rdoc/rd/inline.rb +72 -0
  168. data/lib/rdoc/rd/inline_parser.rb +1208 -0
  169. data/lib/rdoc/rd/inline_parser.ry +593 -0
  170. data/lib/rdoc/rd.rb +100 -0
  171. data/lib/rdoc/rdoc.rb +579 -0
  172. data/lib/rdoc/require.rb +52 -0
  173. data/lib/rdoc/ri/driver.rb +1572 -0
  174. data/lib/rdoc/ri/formatter.rb +6 -0
  175. data/lib/rdoc/ri/paths.rb +171 -0
  176. data/lib/rdoc/ri/store.rb +7 -0
  177. data/lib/rdoc/ri/task.rb +71 -0
  178. data/lib/rdoc/ri.rb +21 -0
  179. data/lib/rdoc/rubygems_hook.rb +246 -0
  180. data/lib/rdoc/servlet.rb +451 -0
  181. data/lib/rdoc/single_class.rb +26 -0
  182. data/lib/rdoc/stats/normal.rb +58 -0
  183. data/lib/rdoc/stats/quiet.rb +60 -0
  184. data/lib/rdoc/stats/verbose.rb +46 -0
  185. data/lib/rdoc/stats.rb +462 -0
  186. data/lib/rdoc/store.rb +979 -0
  187. data/lib/rdoc/task.rb +329 -0
  188. data/lib/rdoc/text.rb +304 -0
  189. data/lib/rdoc/token_stream.rb +119 -0
  190. data/lib/rdoc/tom_doc.rb +263 -0
  191. data/lib/rdoc/top_level.rb +289 -0
  192. data/lib/rdoc/version.rb +8 -0
  193. data/lib/rdoc.rb +201 -0
  194. data/man/ri.1 +247 -0
  195. data/rdoc.gemspec +249 -0
  196. metadata +279 -0
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+ ##
3
+ # A file loaded by \#require
4
+
5
+ class RDoc::Require < RDoc::CodeObject
6
+
7
+ ##
8
+ # Name of the required file
9
+
10
+ attr_accessor :name
11
+
12
+ ##
13
+ # Creates a new Require that loads +name+ with +comment+
14
+
15
+ def initialize(name, comment)
16
+ super()
17
+ @name = name.gsub(/'|"/, "") #'
18
+ @top_level = nil
19
+ self.comment = comment
20
+ end
21
+
22
+ def inspect # :nodoc:
23
+ "#<%s:0x%x require '%s' in %s>" % [
24
+ self.class,
25
+ object_id,
26
+ @name,
27
+ parent_file_name,
28
+ ]
29
+ end
30
+
31
+ def to_s # :nodoc:
32
+ "require #{name} in: #{parent}"
33
+ end
34
+
35
+ ##
36
+ # The RDoc::TopLevel corresponding to this require, or +nil+ if not found.
37
+
38
+ def top_level
39
+ @top_level ||= begin
40
+ tl = RDoc::TopLevel.all_files_hash[name + '.rb']
41
+
42
+ if tl.nil? and RDoc::TopLevel.all_files.first.full_name =~ %r(^lib/) then
43
+ # second chance
44
+ tl = RDoc::TopLevel.all_files_hash['lib/' + name + '.rb']
45
+ end
46
+
47
+ tl
48
+ end
49
+ end
50
+
51
+ end
52
+
@@ -0,0 +1,1572 @@
1
+ # frozen_string_literal: true
2
+ require 'abbrev'
3
+ require 'optparse'
4
+
5
+ begin
6
+ require 'readline'
7
+ rescue LoadError
8
+ end
9
+
10
+ begin
11
+ require 'win32console'
12
+ rescue LoadError
13
+ end
14
+
15
+ require 'rdoc'
16
+
17
+ ##
18
+ # For RubyGems backwards compatibility
19
+
20
+ require_relative 'formatter'
21
+
22
+ ##
23
+ # The RI driver implements the command-line ri tool.
24
+ #
25
+ # The driver supports:
26
+ # * loading RI data from:
27
+ # * Ruby's standard library
28
+ # * RubyGems
29
+ # * ~/.rdoc
30
+ # * A user-supplied directory
31
+ # * Paging output (uses RI_PAGER environment variable, PAGER environment
32
+ # variable or the less, more and pager programs)
33
+ # * Interactive mode with tab-completion
34
+ # * Abbreviated names (ri Zl shows Zlib documentation)
35
+ # * Colorized output
36
+ # * Merging output from multiple RI data sources
37
+
38
+ class RDoc::RI::Driver
39
+
40
+ ##
41
+ # Base Driver error class
42
+
43
+ class Error < RDoc::RI::Error; end
44
+
45
+ ##
46
+ # Raised when a name isn't found in the ri data stores
47
+
48
+ class NotFoundError < Error
49
+
50
+ def initialize(klass, suggestions = nil) # :nodoc:
51
+ @klass = klass
52
+ @suggestions = suggestions
53
+ end
54
+
55
+ ##
56
+ # Name that wasn't found
57
+
58
+ def name
59
+ @klass
60
+ end
61
+
62
+ def message # :nodoc:
63
+ str = "Nothing known about #{@klass}"
64
+ if @suggestions and !@suggestions.empty?
65
+ str += "\nDid you mean? #{@suggestions.join("\n ")}"
66
+ end
67
+ str
68
+ end
69
+ end
70
+
71
+ ##
72
+ # Show all method documentation following a class or module
73
+
74
+ attr_accessor :show_all
75
+
76
+ ##
77
+ # An RDoc::RI::Store for each entry in the RI path
78
+
79
+ attr_accessor :stores
80
+
81
+ ##
82
+ # Controls the user of the pager vs $stdout
83
+
84
+ attr_accessor :use_stdout
85
+
86
+ ##
87
+ # Default options for ri
88
+
89
+ def self.default_options
90
+ options = {}
91
+ options[:interactive] = false
92
+ options[:profile] = false
93
+ options[:show_all] = false
94
+ options[:use_stdout] = !$stdout.tty?
95
+ options[:width] = 72
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
+ # Dump +data_path+ using pp
109
+
110
+ def self.dump data_path
111
+ require 'pp'
112
+
113
+ File.open data_path, 'rb' do |io|
114
+ pp Marshal.load(io.read)
115
+ end
116
+ end
117
+
118
+ ##
119
+ # Parses +argv+ and returns a Hash of options
120
+
121
+ def self.process_args argv
122
+ options = default_options
123
+
124
+ opts = OptionParser.new do |opt|
125
+ opt.accept File do |file,|
126
+ File.readable?(file) and not File.directory?(file) and file
127
+ end
128
+
129
+ opt.program_name = File.basename $0
130
+ opt.version = RDoc::VERSION
131
+ opt.release = nil
132
+ opt.summary_indent = ' ' * 4
133
+
134
+ opt.banner = <<-EOT
135
+ Usage: #{opt.program_name} [options] [name ...]
136
+
137
+ Where name can be:
138
+
139
+ Class | Module | Module::Class
140
+
141
+ Class::method | Class#method | Class.method | method
142
+
143
+ gem_name: | gem_name:README | gem_name:History
144
+
145
+ All class names may be abbreviated to their minimum unambiguous form.
146
+ If a name is ambiguous, all valid options will be listed.
147
+
148
+ A '.' matches either class or instance methods, while #method
149
+ matches only instance and ::method matches only class methods.
150
+
151
+ README and other files may be displayed by prefixing them with the gem name
152
+ they're contained in. If the gem name is followed by a ':' all files in the
153
+ gem will be shown. The file name extension may be omitted where it is
154
+ unambiguous.
155
+
156
+ For example:
157
+
158
+ #{opt.program_name} Fil
159
+ #{opt.program_name} File
160
+ #{opt.program_name} File.new
161
+ #{opt.program_name} zip
162
+ #{opt.program_name} rdoc:README
163
+
164
+ Note that shell quoting or escaping may be required for method names
165
+ containing punctuation:
166
+
167
+ #{opt.program_name} 'Array.[]'
168
+ #{opt.program_name} compact\\!
169
+
170
+ To see the default directories #{opt.program_name} will search, run:
171
+
172
+ #{opt.program_name} --list-doc-dirs
173
+
174
+ Specifying the --system, --site, --home, --gems, or --doc-dir options
175
+ will limit ri to searching only the specified directories.
176
+
177
+ ri options may be set in the RI environment variable.
178
+
179
+ The ri pager can be set with the RI_PAGER environment variable
180
+ or the PAGER environment variable.
181
+ EOT
182
+
183
+ opt.separator nil
184
+ opt.separator "Options:"
185
+
186
+ opt.separator nil
187
+
188
+ opt.on("--[no-]interactive", "-i",
189
+ "In interactive mode you can repeatedly",
190
+ "look up methods with autocomplete.") do |interactive|
191
+ options[:interactive] = interactive
192
+ end
193
+
194
+ opt.separator nil
195
+
196
+ opt.on("--[no-]all", "-a",
197
+ "Show all documentation for a class or",
198
+ "module.") do |show_all|
199
+ options[:show_all] = show_all
200
+ end
201
+
202
+ opt.separator nil
203
+
204
+ opt.on("--[no-]list", "-l",
205
+ "List classes ri knows about.") do |list|
206
+ options[:list] = list
207
+ end
208
+
209
+ opt.separator nil
210
+
211
+ opt.on("--[no-]pager",
212
+ "Send output to a pager,",
213
+ "rather than directly to stdout.") do |use_pager|
214
+ options[:use_stdout] = !use_pager
215
+ end
216
+
217
+ opt.separator nil
218
+
219
+ opt.on("-T",
220
+ "Synonym for --no-pager.") do
221
+ options[:use_stdout] = true
222
+ end
223
+
224
+ opt.separator nil
225
+
226
+ opt.on("--width=WIDTH", "-w", OptionParser::DecimalInteger,
227
+ "Set the width of the output.") do |width|
228
+ options[:width] = width
229
+ end
230
+
231
+ opt.separator nil
232
+
233
+ opt.on("--server[=PORT]", Integer,
234
+ "Run RDoc server on the given port.",
235
+ "The default port is 8214.") do |port|
236
+ options[:server] = port || 8214
237
+ end
238
+
239
+ opt.separator nil
240
+
241
+ formatters = RDoc::Markup.constants.grep(/^To[A-Z][a-z]+$/).sort
242
+ formatters = formatters.sort.map do |formatter|
243
+ formatter.to_s.sub('To', '').downcase
244
+ end
245
+ formatters -= %w[html label test] # remove useless output formats
246
+
247
+ opt.on("--format=NAME", "-f",
248
+ "Use the selected formatter. The default",
249
+ "formatter is bs for paged output and ansi",
250
+ "otherwise. Valid formatters are:",
251
+ "#{formatters.join(', ')}.", formatters) do |value|
252
+ options[:formatter] = RDoc::Markup.const_get "To#{value.capitalize}"
253
+ end
254
+
255
+ opt.separator nil
256
+
257
+ opt.on("--help", "-h",
258
+ "Show help and exit.") do
259
+ puts opts
260
+ exit
261
+ end
262
+
263
+ opt.separator nil
264
+
265
+ opt.on("--version", "-v",
266
+ "Output version information and exit.") do
267
+ puts "#{opts.program_name} #{opts.version}"
268
+ exit
269
+ end
270
+
271
+ opt.separator nil
272
+ opt.separator "Data source options:"
273
+ opt.separator nil
274
+
275
+ opt.on("--[no-]list-doc-dirs",
276
+ "List the directories from which ri will",
277
+ "source documentation on stdout and exit.") do |list_doc_dirs|
278
+ options[:list_doc_dirs] = list_doc_dirs
279
+ end
280
+
281
+ opt.separator nil
282
+
283
+ opt.on("--doc-dir=DIRNAME", "-d", Array,
284
+ "List of directories from which to source",
285
+ "documentation in addition to the standard",
286
+ "directories. May be repeated.") do |value|
287
+ value.each do |dir|
288
+ unless File.directory? dir then
289
+ raise OptionParser::InvalidArgument, "#{dir} is not a directory"
290
+ end
291
+
292
+ options[:extra_doc_dirs] << File.expand_path(dir)
293
+ end
294
+ end
295
+
296
+ opt.separator nil
297
+
298
+ opt.on("--no-standard-docs",
299
+ "Do not include documentation from",
300
+ "the Ruby standard library, site_lib,",
301
+ "installed gems, or ~/.rdoc.",
302
+ "Use with --doc-dir.") do
303
+ options[:use_system] = false
304
+ options[:use_site] = false
305
+ options[:use_gems] = false
306
+ options[:use_home] = false
307
+ end
308
+
309
+ opt.separator nil
310
+
311
+ opt.on("--[no-]system",
312
+ "Include documentation from Ruby's",
313
+ "standard library. Defaults to true.") do |value|
314
+ options[:use_system] = value
315
+ end
316
+
317
+ opt.separator nil
318
+
319
+ opt.on("--[no-]site",
320
+ "Include documentation from libraries",
321
+ "installed in site_lib.",
322
+ "Defaults to true.") do |value|
323
+ options[:use_site] = value
324
+ end
325
+
326
+ opt.separator nil
327
+
328
+ opt.on("--[no-]gems",
329
+ "Include documentation from RubyGems.",
330
+ "Defaults to true.") do |value|
331
+ options[:use_gems] = value
332
+ end
333
+
334
+ opt.separator nil
335
+
336
+ opt.on("--[no-]home",
337
+ "Include documentation stored in ~/.rdoc.",
338
+ "Defaults to true.") do |value|
339
+ options[:use_home] = value
340
+ end
341
+
342
+ opt.separator nil
343
+ opt.separator "Debug options:"
344
+ opt.separator nil
345
+
346
+ opt.on("--[no-]profile",
347
+ "Run with the ruby profiler.") do |value|
348
+ options[:profile] = value
349
+ end
350
+
351
+ opt.separator nil
352
+
353
+ opt.on("--dump=CACHE", File,
354
+ "Dump data from an ri cache or data file.") do |value|
355
+ options[:dump_path] = value
356
+ end
357
+ end
358
+
359
+ argv = ENV['RI'].to_s.split(' ').concat argv
360
+
361
+ opts.parse! argv
362
+
363
+ options[:names] = argv
364
+
365
+ options[:use_stdout] ||= !$stdout.tty?
366
+ options[:use_stdout] ||= options[:interactive]
367
+ options[:width] ||= 72
368
+
369
+ options
370
+
371
+ rescue OptionParser::InvalidArgument, OptionParser::InvalidOption => e
372
+ puts opts
373
+ puts
374
+ puts e
375
+ exit 1
376
+ end
377
+
378
+ ##
379
+ # Runs the ri command line executable using +argv+
380
+
381
+ def self.run argv = ARGV
382
+ options = process_args argv
383
+
384
+ if options[:dump_path] then
385
+ dump options[:dump_path]
386
+ return
387
+ end
388
+
389
+ ri = new options
390
+ ri.run
391
+ end
392
+
393
+ ##
394
+ # Creates a new driver using +initial_options+ from ::process_args
395
+
396
+ def initialize initial_options = {}
397
+ @paging = false
398
+ @classes = nil
399
+
400
+ options = self.class.default_options.update(initial_options)
401
+
402
+ @formatter_klass = options[:formatter]
403
+
404
+ require 'profile' if options[:profile]
405
+
406
+ @names = options[:names]
407
+ @list = options[:list]
408
+
409
+ @doc_dirs = []
410
+ @stores = []
411
+
412
+ RDoc::RI::Paths.each(options[:use_system], options[:use_site],
413
+ options[:use_home], options[:use_gems],
414
+ *options[:extra_doc_dirs]) do |path, type|
415
+ @doc_dirs << path
416
+
417
+ store = RDoc::RI::Store.new path, type
418
+ store.load_cache
419
+ @stores << store
420
+ end
421
+
422
+ @list_doc_dirs = options[:list_doc_dirs]
423
+
424
+ @interactive = options[:interactive]
425
+ @server = options[:server]
426
+ @use_stdout = options[:use_stdout]
427
+ @show_all = options[:show_all]
428
+ @width = options[:width]
429
+
430
+ # pager process for jruby
431
+ @jruby_pager_process = nil
432
+ end
433
+
434
+ ##
435
+ # Adds paths for undocumented classes +also_in+ to +out+
436
+
437
+ def add_also_in out, also_in
438
+ return if also_in.empty?
439
+
440
+ out << RDoc::Markup::Rule.new(1)
441
+ out << RDoc::Markup::Paragraph.new("Also found in:")
442
+
443
+ paths = RDoc::Markup::Verbatim.new
444
+ also_in.each do |store|
445
+ paths.parts.push store.friendly_path, "\n"
446
+ end
447
+ out << paths
448
+ end
449
+
450
+ ##
451
+ # Adds a class header to +out+ for class +name+ which is described in
452
+ # +classes+.
453
+
454
+ def add_class out, name, classes
455
+ heading = if classes.all? { |klass| klass.module? } then
456
+ name
457
+ else
458
+ superclass = classes.map do |klass|
459
+ klass.superclass unless klass.module?
460
+ end.compact.shift || 'Object'
461
+
462
+ superclass = superclass.full_name unless String === superclass
463
+
464
+ "#{name} < #{superclass}"
465
+ end
466
+
467
+ out << RDoc::Markup::Heading.new(1, heading)
468
+ out << RDoc::Markup::BlankLine.new
469
+ end
470
+
471
+ ##
472
+ # Adds "(from ...)" to +out+ for +store+
473
+
474
+ def add_from out, store
475
+ out << RDoc::Markup::Paragraph.new("(from #{store.friendly_path})")
476
+ end
477
+
478
+ ##
479
+ # Adds +extends+ to +out+
480
+
481
+ def add_extends out, extends
482
+ add_extension_modules out, 'Extended by', extends
483
+ end
484
+
485
+ ##
486
+ # Adds a list of +extensions+ to this module of the given +type+ to +out+.
487
+ # add_includes and add_extends call this, so you should use those directly.
488
+
489
+ def add_extension_modules out, type, extensions
490
+ return if extensions.empty?
491
+
492
+ out << RDoc::Markup::Rule.new(1)
493
+ out << RDoc::Markup::Heading.new(1, "#{type}:")
494
+
495
+ extensions.each do |modules, store|
496
+ if modules.length == 1 then
497
+ add_extension_modules_single out, store, modules.first
498
+ else
499
+ add_extension_modules_multiple out, store, modules
500
+ end
501
+ end
502
+ end
503
+
504
+ ##
505
+ # Renders multiple included +modules+ from +store+ to +out+.
506
+
507
+ def add_extension_modules_multiple out, store, modules # :nodoc:
508
+ out << RDoc::Markup::Paragraph.new("(from #{store.friendly_path})")
509
+
510
+ wout, with = modules.partition { |incl| incl.comment.empty? }
511
+
512
+ out << RDoc::Markup::BlankLine.new unless with.empty?
513
+
514
+ with.each do |incl|
515
+ out << RDoc::Markup::Paragraph.new(incl.name)
516
+ out << RDoc::Markup::BlankLine.new
517
+ out << incl.comment
518
+ end
519
+
520
+ unless wout.empty? then
521
+ verb = RDoc::Markup::Verbatim.new
522
+
523
+ wout.each do |incl|
524
+ verb.push incl.name, "\n"
525
+ end
526
+
527
+ out << verb
528
+ end
529
+ end
530
+
531
+ ##
532
+ # Adds a single extension module +include+ from +store+ to +out+
533
+
534
+ def add_extension_modules_single out, store, include # :nodoc:
535
+ name = include.name
536
+ path = store.friendly_path
537
+ out << RDoc::Markup::Paragraph.new("#{name} (from #{path})")
538
+
539
+ if include.comment then
540
+ out << RDoc::Markup::BlankLine.new
541
+ out << include.comment
542
+ end
543
+ end
544
+
545
+ ##
546
+ # Adds +includes+ to +out+
547
+
548
+ def add_includes out, includes
549
+ add_extension_modules out, 'Includes', includes
550
+ end
551
+
552
+ ##
553
+ # Looks up the method +name+ and adds it to +out+
554
+
555
+ def add_method out, name
556
+ filtered = lookup_method name
557
+
558
+ method_out = method_document name, filtered
559
+
560
+ out.concat method_out.parts
561
+ end
562
+
563
+ ##
564
+ # Adds documentation for all methods in +klass+ to +out+
565
+
566
+ def add_method_documentation out, klass
567
+ klass.method_list.each do |method|
568
+ begin
569
+ add_method out, method.full_name
570
+ rescue NotFoundError
571
+ next
572
+ end
573
+ end
574
+ end
575
+
576
+ ##
577
+ # Adds a list of +methods+ to +out+ with a heading of +name+
578
+
579
+ def add_method_list out, methods, name
580
+ return if methods.empty?
581
+
582
+ out << RDoc::Markup::Heading.new(1, "#{name}:")
583
+ out << RDoc::Markup::BlankLine.new
584
+
585
+ if @use_stdout and !@interactive then
586
+ out.concat methods.map { |method|
587
+ RDoc::Markup::Verbatim.new method
588
+ }
589
+ else
590
+ out << RDoc::Markup::IndentedParagraph.new(2, methods.join(', '))
591
+ end
592
+
593
+ out << RDoc::Markup::BlankLine.new
594
+ end
595
+
596
+ ##
597
+ # Returns ancestor classes of +klass+
598
+
599
+ def ancestors_of klass
600
+ ancestors = []
601
+
602
+ unexamined = [klass]
603
+ seen = []
604
+
605
+ loop do
606
+ break if unexamined.empty?
607
+ current = unexamined.shift
608
+ seen << current
609
+
610
+ stores = classes[current]
611
+
612
+ break unless stores and not stores.empty?
613
+
614
+ klasses = stores.map do |store|
615
+ store.ancestors[current]
616
+ end.flatten.uniq
617
+
618
+ klasses = klasses - seen
619
+
620
+ ancestors.concat klasses
621
+ unexamined.concat klasses
622
+ end
623
+
624
+ ancestors.reverse
625
+ end
626
+
627
+ ##
628
+ # For RubyGems backwards compatibility
629
+
630
+ def class_cache # :nodoc:
631
+ end
632
+
633
+ ##
634
+ # Builds a RDoc::Markup::Document from +found+, +klasess+ and +includes+
635
+
636
+ def class_document name, found, klasses, includes, extends
637
+ also_in = []
638
+
639
+ out = RDoc::Markup::Document.new
640
+
641
+ add_class out, name, klasses
642
+
643
+ add_includes out, includes
644
+ add_extends out, extends
645
+
646
+ found.each do |store, klass|
647
+ render_class out, store, klass, also_in
648
+ end
649
+
650
+ add_also_in out, also_in
651
+
652
+ out
653
+ end
654
+
655
+ ##
656
+ # Adds the class +comment+ to +out+.
657
+
658
+ def class_document_comment out, comment # :nodoc:
659
+ unless comment.empty? then
660
+ out << RDoc::Markup::Rule.new(1)
661
+
662
+ if comment.merged? then
663
+ parts = comment.parts
664
+ parts = parts.zip [RDoc::Markup::BlankLine.new] * parts.length
665
+ parts.flatten!
666
+ parts.pop
667
+
668
+ out.concat parts
669
+ else
670
+ out << comment
671
+ end
672
+ end
673
+ end
674
+
675
+ ##
676
+ # Adds the constants from +klass+ to the Document +out+.
677
+
678
+ def class_document_constants out, klass # :nodoc:
679
+ return if klass.constants.empty?
680
+
681
+ out << RDoc::Markup::Heading.new(1, "Constants:")
682
+ out << RDoc::Markup::BlankLine.new
683
+ list = RDoc::Markup::List.new :NOTE
684
+
685
+ constants = klass.constants.sort_by { |constant| constant.name }
686
+
687
+ list.items.concat constants.map { |constant|
688
+ parts = constant.comment.parts if constant.comment
689
+ parts << RDoc::Markup::Paragraph.new('[not documented]') if
690
+ parts.empty?
691
+
692
+ RDoc::Markup::ListItem.new(constant.name, *parts)
693
+ }
694
+
695
+ out << list
696
+ out << RDoc::Markup::BlankLine.new
697
+ end
698
+
699
+ ##
700
+ # Hash mapping a known class or module to the stores it can be loaded from
701
+
702
+ def classes
703
+ return @classes if @classes
704
+
705
+ @classes = {}
706
+
707
+ @stores.each do |store|
708
+ store.cache[:modules].each do |mod|
709
+ # using default block causes searched-for modules to be added
710
+ @classes[mod] ||= []
711
+ @classes[mod] << store
712
+ end
713
+ end
714
+
715
+ @classes
716
+ end
717
+
718
+ ##
719
+ # Returns the stores wherein +name+ is found along with the classes,
720
+ # extends and includes that match it
721
+
722
+ def classes_and_includes_and_extends_for name
723
+ klasses = []
724
+ extends = []
725
+ includes = []
726
+
727
+ found = @stores.map do |store|
728
+ begin
729
+ klass = store.load_class name
730
+ klasses << klass
731
+ extends << [klass.extends, store] if klass.extends
732
+ includes << [klass.includes, store] if klass.includes
733
+ [store, klass]
734
+ rescue RDoc::Store::MissingFileError
735
+ end
736
+ end.compact
737
+
738
+ extends.reject! do |modules,| modules.empty? end
739
+ includes.reject! do |modules,| modules.empty? end
740
+
741
+ [found, klasses, includes, extends]
742
+ end
743
+
744
+ ##
745
+ # Completes +name+ based on the caches. For Readline
746
+
747
+ def complete name
748
+ completions = []
749
+
750
+ klass, selector, method = parse_name name
751
+
752
+ complete_klass name, klass, selector, method, completions
753
+ complete_method name, klass, selector, completions
754
+
755
+ completions.sort.uniq
756
+ end
757
+
758
+ def complete_klass name, klass, selector, method, completions # :nodoc:
759
+ klasses = classes.keys
760
+
761
+ # may need to include Foo when given Foo::
762
+ klass_name = method ? name : klass
763
+
764
+ if name !~ /#|\./ then
765
+ completions.replace klasses.grep(/^#{Regexp.escape klass_name}[^:]*$/)
766
+ completions.concat klasses.grep(/^#{Regexp.escape name}[^:]*$/) if
767
+ name =~ /::$/
768
+
769
+ completions << klass if classes.key? klass # to complete a method name
770
+ elsif selector then
771
+ completions << klass if classes.key? klass
772
+ elsif classes.key? klass_name then
773
+ completions << klass_name
774
+ end
775
+ end
776
+
777
+ def complete_method name, klass, selector, completions # :nodoc:
778
+ if completions.include? klass and name =~ /#|\.|::/ then
779
+ methods = list_methods_matching name
780
+
781
+ if not methods.empty? then
782
+ # remove Foo if given Foo:: and a method was found
783
+ completions.delete klass
784
+ elsif selector then
785
+ # replace Foo with Foo:: as given
786
+ completions.delete klass
787
+ completions << "#{klass}#{selector}"
788
+ end
789
+
790
+ completions.concat methods
791
+ end
792
+ end
793
+
794
+ ##
795
+ # Converts +document+ to text and writes it to the pager
796
+
797
+ def display document
798
+ page do |io|
799
+ f = formatter(io)
800
+ f.width = @width if @width and f.respond_to?(:width)
801
+ text = document.accept f
802
+
803
+ io.write text
804
+ end
805
+ end
806
+
807
+ ##
808
+ # Outputs formatted RI data for class +name+. Groups undocumented classes
809
+
810
+ def display_class name
811
+ return if name =~ /#|\./
812
+
813
+ found, klasses, includes, extends =
814
+ classes_and_includes_and_extends_for name
815
+
816
+ return if found.empty?
817
+
818
+ out = class_document name, found, klasses, includes, extends
819
+
820
+ display out
821
+ end
822
+
823
+ ##
824
+ # Outputs formatted RI data for method +name+
825
+
826
+ def display_method name
827
+ out = RDoc::Markup::Document.new
828
+
829
+ add_method out, name
830
+
831
+ display out
832
+ end
833
+
834
+ ##
835
+ # Outputs formatted RI data for the class or method +name+.
836
+ #
837
+ # Returns true if +name+ was found, false if it was not an alternative could
838
+ # be guessed, raises an error if +name+ couldn't be guessed.
839
+
840
+ def display_name name
841
+ if name =~ /\w:(\w|$)/ then
842
+ display_page name
843
+ return true
844
+ end
845
+
846
+ return true if display_class name
847
+
848
+ display_method name if name =~ /::|#|\./
849
+
850
+ true
851
+ rescue NotFoundError
852
+ matches = list_methods_matching name if name =~ /::|#|\./
853
+ matches = classes.keys.grep(/^#{Regexp.escape name}/) if matches.empty?
854
+
855
+ raise if matches.empty?
856
+
857
+ page do |io|
858
+ io.puts "#{name} not found, maybe you meant:"
859
+ io.puts
860
+ io.puts matches.sort.join("\n")
861
+ end
862
+
863
+ false
864
+ end
865
+
866
+ ##
867
+ # Displays each name in +name+
868
+
869
+ def display_names names
870
+ names.each do |name|
871
+ name = expand_name name
872
+
873
+ display_name name
874
+ end
875
+ end
876
+
877
+ ##
878
+ # Outputs formatted RI data for page +name+.
879
+
880
+ def display_page name
881
+ store_name, page_name = name.split ':', 2
882
+
883
+ store = @stores.find { |s| s.source == store_name }
884
+
885
+ return display_page_list store if page_name.empty?
886
+
887
+ pages = store.cache[:pages]
888
+
889
+ unless pages.include? page_name then
890
+ found_names = pages.select do |n|
891
+ n =~ /#{Regexp.escape page_name}\.[^.]+$/
892
+ end
893
+
894
+ if found_names.length.zero? then
895
+ return display_page_list store, pages
896
+ elsif found_names.length > 1 then
897
+ return display_page_list store, found_names, page_name
898
+ end
899
+
900
+ page_name = found_names.first
901
+ end
902
+
903
+ page = store.load_page page_name
904
+
905
+ display page.comment
906
+ end
907
+
908
+ ##
909
+ # Outputs a formatted RI page list for the pages in +store+.
910
+
911
+ def display_page_list store, pages = store.cache[:pages], search = nil
912
+ out = RDoc::Markup::Document.new
913
+
914
+ title = if search then
915
+ "#{search} pages"
916
+ else
917
+ 'Pages'
918
+ end
919
+
920
+ out << RDoc::Markup::Heading.new(1, "#{title} in #{store.friendly_path}")
921
+ out << RDoc::Markup::BlankLine.new
922
+
923
+ list = RDoc::Markup::List.new(:BULLET)
924
+
925
+ pages.each do |page|
926
+ list << RDoc::Markup::Paragraph.new(page)
927
+ end
928
+
929
+ out << list
930
+
931
+ display out
932
+ end
933
+
934
+ def check_did_you_mean # :nodoc:
935
+ if defined? DidYouMean::SpellChecker
936
+ true
937
+ else
938
+ begin
939
+ require 'did_you_mean'
940
+ if defined? DidYouMean::SpellChecker
941
+ true
942
+ else
943
+ false
944
+ end
945
+ rescue LoadError
946
+ false
947
+ end
948
+ end
949
+ end
950
+
951
+ ##
952
+ # Expands abbreviated klass +klass+ into a fully-qualified class. "Zl::Da"
953
+ # will be expanded to Zlib::DataError.
954
+
955
+ def expand_class klass
956
+ class_names = classes.keys
957
+ ary = class_names.grep(Regexp.new("\\A#{klass.gsub(/(?=::|\z)/, '[^:]*')}\\z"))
958
+ if ary.length != 1 && ary.first != klass
959
+ if check_did_you_mean
960
+ suggestions = DidYouMean::SpellChecker.new(dictionary: class_names).correct(klass)
961
+ raise NotFoundError.new(klass, suggestions)
962
+ else
963
+ raise NotFoundError, klass
964
+ end
965
+ end
966
+ ary.first
967
+ end
968
+
969
+ ##
970
+ # Expands the class portion of +name+ into a fully-qualified class. See
971
+ # #expand_class.
972
+
973
+ def expand_name name
974
+ klass, selector, method = parse_name name
975
+
976
+ return [selector, method].join if klass.empty?
977
+
978
+ case selector
979
+ when ':' then
980
+ [find_store(klass), selector, method]
981
+ else
982
+ [expand_class(klass), selector, method]
983
+ end.join
984
+ end
985
+
986
+ ##
987
+ # Filters the methods in +found+ trying to find a match for +name+.
988
+
989
+ def filter_methods found, name
990
+ regexp = name_regexp name
991
+
992
+ filtered = found.find_all do |store, methods|
993
+ methods.any? { |method| method.full_name =~ regexp }
994
+ end
995
+
996
+ return filtered unless filtered.empty?
997
+
998
+ found
999
+ end
1000
+
1001
+ ##
1002
+ # Yields items matching +name+ including the store they were found in, the
1003
+ # class being searched for, the class they were found in (an ancestor) the
1004
+ # types of methods to look up (from #method_type), and the method name being
1005
+ # searched for
1006
+
1007
+ def find_methods name
1008
+ klass, selector, method = parse_name name
1009
+
1010
+ types = method_type selector
1011
+
1012
+ klasses = nil
1013
+ ambiguous = klass.empty?
1014
+
1015
+ if ambiguous then
1016
+ klasses = classes.keys
1017
+ else
1018
+ klasses = ancestors_of klass
1019
+ klasses.unshift klass
1020
+ end
1021
+
1022
+ methods = []
1023
+
1024
+ klasses.each do |ancestor|
1025
+ ancestors = classes[ancestor]
1026
+
1027
+ next unless ancestors
1028
+
1029
+ klass = ancestor if ambiguous
1030
+
1031
+ ancestors.each do |store|
1032
+ methods << [store, klass, ancestor, types, method]
1033
+ end
1034
+ end
1035
+
1036
+ methods = methods.sort_by do |_, k, a, _, m|
1037
+ [k, a, m].compact
1038
+ end
1039
+
1040
+ methods.each do |item|
1041
+ yield(*item) # :yields: store, klass, ancestor, types, method
1042
+ end
1043
+
1044
+ self
1045
+ end
1046
+
1047
+ ##
1048
+ # Finds the given +pager+ for jruby. Returns an IO if +pager+ was found.
1049
+ #
1050
+ # Returns false if +pager+ does not exist.
1051
+ #
1052
+ # Returns nil if the jruby JVM doesn't support ProcessBuilder redirection
1053
+ # (1.6 and older).
1054
+
1055
+ def find_pager_jruby pager
1056
+ require 'java'
1057
+ require 'shellwords'
1058
+
1059
+ return nil unless java.lang.ProcessBuilder.constants.include? :Redirect
1060
+
1061
+ pager = Shellwords.split pager
1062
+
1063
+ pb = java.lang.ProcessBuilder.new(*pager)
1064
+ pb = pb.redirect_output java.lang.ProcessBuilder::Redirect::INHERIT
1065
+
1066
+ @jruby_pager_process = pb.start
1067
+
1068
+ input = @jruby_pager_process.output_stream
1069
+
1070
+ io = input.to_io
1071
+ io.sync = true
1072
+ io
1073
+ rescue java.io.IOException
1074
+ false
1075
+ end
1076
+
1077
+ ##
1078
+ # Finds a store that matches +name+ which can be the name of a gem, "ruby",
1079
+ # "home" or "site".
1080
+ #
1081
+ # See also RDoc::Store#source
1082
+
1083
+ def find_store name
1084
+ @stores.each do |store|
1085
+ source = store.source
1086
+
1087
+ return source if source == name
1088
+
1089
+ return source if
1090
+ store.type == :gem and source =~ /^#{Regexp.escape name}-\d/
1091
+ end
1092
+
1093
+ raise RDoc::RI::Driver::NotFoundError, name
1094
+ end
1095
+
1096
+ ##
1097
+ # Creates a new RDoc::Markup::Formatter. If a formatter is given with -f,
1098
+ # use it. If we're outputting to a pager, use bs, otherwise ansi.
1099
+
1100
+ def formatter(io)
1101
+ if @formatter_klass then
1102
+ @formatter_klass.new
1103
+ elsif paging? or !io.tty? then
1104
+ RDoc::Markup::ToBs.new
1105
+ else
1106
+ RDoc::Markup::ToAnsi.new
1107
+ end
1108
+ end
1109
+
1110
+ ##
1111
+ # Runs ri interactively using Readline if it is available.
1112
+
1113
+ def interactive
1114
+ puts "\nEnter the method name you want to look up."
1115
+
1116
+ if defined? Readline then
1117
+ Readline.completion_proc = method :complete
1118
+ puts "You can use tab to autocomplete."
1119
+ end
1120
+
1121
+ puts "Enter a blank line to exit.\n\n"
1122
+
1123
+ loop do
1124
+ name = if defined? Readline then
1125
+ Readline.readline ">> "
1126
+ else
1127
+ print ">> "
1128
+ $stdin.gets
1129
+ end
1130
+
1131
+ return if name.nil? or name.empty?
1132
+
1133
+ begin
1134
+ display_name expand_name(name.strip)
1135
+ rescue NotFoundError => e
1136
+ puts e.message
1137
+ end
1138
+ end
1139
+
1140
+ rescue Interrupt
1141
+ exit
1142
+ end
1143
+
1144
+ ##
1145
+ # Is +file+ in ENV['PATH']?
1146
+
1147
+ def in_path? file
1148
+ return true if file =~ %r%\A/% and File.exist? file
1149
+
1150
+ ENV['PATH'].split(File::PATH_SEPARATOR).any? do |path|
1151
+ File.exist? File.join(path, file)
1152
+ end
1153
+ end
1154
+
1155
+ ##
1156
+ # Lists classes known to ri starting with +names+. If +names+ is empty all
1157
+ # known classes are shown.
1158
+
1159
+ def list_known_classes names = []
1160
+ classes = []
1161
+
1162
+ stores.each do |store|
1163
+ classes << store.module_names
1164
+ end
1165
+
1166
+ classes = classes.flatten.uniq.sort
1167
+
1168
+ unless names.empty? then
1169
+ filter = Regexp.union names.map { |name| /^#{name}/ }
1170
+
1171
+ classes = classes.grep filter
1172
+ end
1173
+
1174
+ page do |io|
1175
+ if paging? or io.tty? then
1176
+ if names.empty? then
1177
+ io.puts "Classes and Modules known to ri:"
1178
+ else
1179
+ io.puts "Classes and Modules starting with #{names.join ', '}:"
1180
+ end
1181
+ io.puts
1182
+ end
1183
+
1184
+ io.puts classes.join("\n")
1185
+ end
1186
+ end
1187
+
1188
+ ##
1189
+ # Returns an Array of methods matching +name+
1190
+
1191
+ def list_methods_matching name
1192
+ found = []
1193
+
1194
+ find_methods name do |store, klass, ancestor, types, method|
1195
+ if types == :instance or types == :both then
1196
+ methods = store.instance_methods[ancestor]
1197
+
1198
+ if methods then
1199
+ matches = methods.grep(/^#{Regexp.escape method.to_s}/)
1200
+
1201
+ matches = matches.map do |match|
1202
+ "#{klass}##{match}"
1203
+ end
1204
+
1205
+ found.concat matches
1206
+ end
1207
+ end
1208
+
1209
+ if types == :class or types == :both then
1210
+ methods = store.class_methods[ancestor]
1211
+
1212
+ next unless methods
1213
+ matches = methods.grep(/^#{Regexp.escape method.to_s}/)
1214
+
1215
+ matches = matches.map do |match|
1216
+ "#{klass}::#{match}"
1217
+ end
1218
+
1219
+ found.concat matches
1220
+ end
1221
+ end
1222
+
1223
+ found.uniq
1224
+ end
1225
+
1226
+ ##
1227
+ # Loads RI data for method +name+ on +klass+ from +store+. +type+ and
1228
+ # +cache+ indicate if it is a class or instance method.
1229
+
1230
+ def load_method store, cache, klass, type, name
1231
+ methods = store.public_send(cache)[klass]
1232
+
1233
+ return unless methods
1234
+
1235
+ method = methods.find do |method_name|
1236
+ method_name == name
1237
+ end
1238
+
1239
+ return unless method
1240
+
1241
+ store.load_method klass, "#{type}#{method}"
1242
+ rescue RDoc::Store::MissingFileError => e
1243
+ comment = RDoc::Comment.new("missing documentation at #{e.file}").parse
1244
+
1245
+ method = RDoc::AnyMethod.new nil, name
1246
+ method.comment = comment
1247
+ method
1248
+ end
1249
+
1250
+ ##
1251
+ # Returns an Array of RI data for methods matching +name+
1252
+
1253
+ def load_methods_matching name
1254
+ found = []
1255
+
1256
+ find_methods name do |store, klass, ancestor, types, method|
1257
+ methods = []
1258
+
1259
+ methods << load_method(store, :class_methods, ancestor, '::', method) if
1260
+ [:class, :both].include? types
1261
+
1262
+ methods << load_method(store, :instance_methods, ancestor, '#', method) if
1263
+ [:instance, :both].include? types
1264
+
1265
+ found << [store, methods.compact]
1266
+ end
1267
+
1268
+ found.reject do |path, methods| methods.empty? end
1269
+ end
1270
+
1271
+ ##
1272
+ # Returns a filtered list of methods matching +name+
1273
+
1274
+ def lookup_method name
1275
+ found = load_methods_matching name
1276
+
1277
+ if found.empty?
1278
+ if check_did_you_mean
1279
+ methods = []
1280
+ _, _, method_name = parse_name name
1281
+ find_methods name do |store, klass, ancestor, types, method|
1282
+ methods.push(*store.class_methods[klass]) if [:class, :both].include? types
1283
+ methods.push(*store.instance_methods[klass]) if [:instance, :both].include? types
1284
+ end
1285
+ methods = methods.uniq
1286
+ suggestions = DidYouMean::SpellChecker.new(dictionary: methods).correct(method_name)
1287
+ raise NotFoundError.new(name, suggestions)
1288
+ else
1289
+ raise NotFoundError, name
1290
+ end
1291
+ end
1292
+
1293
+ filter_methods found, name
1294
+ end
1295
+
1296
+ ##
1297
+ # Builds a RDoc::Markup::Document from +found+, +klasses+ and +includes+
1298
+
1299
+ def method_document name, filtered
1300
+ out = RDoc::Markup::Document.new
1301
+
1302
+ out << RDoc::Markup::Heading.new(1, name)
1303
+ out << RDoc::Markup::BlankLine.new
1304
+
1305
+ filtered.each do |store, methods|
1306
+ methods.each do |method|
1307
+ render_method out, store, method, name
1308
+ end
1309
+ end
1310
+
1311
+ out
1312
+ end
1313
+
1314
+ ##
1315
+ # Returns the type of method (:both, :instance, :class) for +selector+
1316
+
1317
+ def method_type selector
1318
+ case selector
1319
+ when '.', nil then :both
1320
+ when '#' then :instance
1321
+ else :class
1322
+ end
1323
+ end
1324
+
1325
+ ##
1326
+ # Returns a regular expression for +name+ that will match an
1327
+ # RDoc::AnyMethod's name.
1328
+
1329
+ def name_regexp name
1330
+ klass, type, name = parse_name name
1331
+
1332
+ case type
1333
+ when '#', '::' then
1334
+ /^#{klass}#{type}#{Regexp.escape name}$/
1335
+ else
1336
+ /^#{klass}(#|::)#{Regexp.escape name}$/
1337
+ end
1338
+ end
1339
+
1340
+ ##
1341
+ # Paginates output through a pager program.
1342
+
1343
+ def page
1344
+ if pager = setup_pager then
1345
+ begin
1346
+ yield pager
1347
+ ensure
1348
+ pager.close
1349
+ @jruby_pager_process.wait_for if @jruby_pager_process
1350
+ end
1351
+ else
1352
+ yield $stdout
1353
+ end
1354
+ rescue Errno::EPIPE
1355
+ ensure
1356
+ @paging = false
1357
+ end
1358
+
1359
+ ##
1360
+ # Are we using a pager?
1361
+
1362
+ def paging?
1363
+ @paging
1364
+ end
1365
+
1366
+ ##
1367
+ # Extracts the class, selector and method name parts from +name+ like
1368
+ # Foo::Bar#baz.
1369
+ #
1370
+ # NOTE: Given Foo::Bar, Bar is considered a class even though it may be a
1371
+ # method
1372
+
1373
+ def parse_name name
1374
+ parts = name.split(/(::?|#|\.)/)
1375
+
1376
+ if parts.length == 1 then
1377
+ if parts.first =~ /^[a-z]|^([%&*+\/<>^`|~-]|\+@|-@|<<|<=>?|===?|=>|=~|>>|\[\]=?|~@)$/ then
1378
+ type = '.'
1379
+ meth = parts.pop
1380
+ else
1381
+ type = nil
1382
+ meth = nil
1383
+ end
1384
+ elsif parts.length == 2 or parts.last =~ /::|#|\./ then
1385
+ type = parts.pop
1386
+ meth = nil
1387
+ elsif parts[1] == ':' then
1388
+ klass = parts.shift
1389
+ type = parts.shift
1390
+ meth = parts.join
1391
+ elsif parts[-2] != '::' or parts.last !~ /^[A-Z]/ then
1392
+ meth = parts.pop
1393
+ type = parts.pop
1394
+ end
1395
+
1396
+ klass ||= parts.join
1397
+
1398
+ [klass, type, meth]
1399
+ end
1400
+
1401
+ ##
1402
+ # Renders the +klass+ from +store+ to +out+. If the klass has no
1403
+ # documentable items the class is added to +also_in+ instead.
1404
+
1405
+ def render_class out, store, klass, also_in # :nodoc:
1406
+ comment = klass.comment
1407
+ # TODO the store's cache should always return an empty Array
1408
+ class_methods = store.class_methods[klass.full_name] || []
1409
+ instance_methods = store.instance_methods[klass.full_name] || []
1410
+ attributes = store.attributes[klass.full_name] || []
1411
+
1412
+ if comment.empty? and
1413
+ instance_methods.empty? and class_methods.empty? then
1414
+ also_in << store
1415
+ return
1416
+ end
1417
+
1418
+ add_from out, store
1419
+
1420
+ class_document_comment out, comment
1421
+
1422
+ if class_methods or instance_methods or not klass.constants.empty? then
1423
+ out << RDoc::Markup::Rule.new(1)
1424
+ end
1425
+
1426
+ class_document_constants out, klass
1427
+
1428
+ add_method_list out, class_methods, 'Class methods'
1429
+ add_method_list out, instance_methods, 'Instance methods'
1430
+ add_method_list out, attributes, 'Attributes'
1431
+
1432
+ add_method_documentation out, klass if @show_all
1433
+ end
1434
+
1435
+ def render_method out, store, method, name # :nodoc:
1436
+ out << RDoc::Markup::Paragraph.new("(from #{store.friendly_path})")
1437
+
1438
+ unless name =~ /^#{Regexp.escape method.parent_name}/ then
1439
+ out << RDoc::Markup::Heading.new(3, "Implementation from #{method.parent_name}")
1440
+ end
1441
+
1442
+ out << RDoc::Markup::Rule.new(1)
1443
+
1444
+ render_method_arguments out, method.arglists
1445
+ render_method_superclass out, method
1446
+ if method.is_alias_for
1447
+ al = method.is_alias_for
1448
+ alias_for = store.load_method al.parent_name, "#{al.name_prefix}#{al.name}"
1449
+ render_method_comment out, method, alias_for
1450
+ else
1451
+ render_method_comment out, method
1452
+ end
1453
+ end
1454
+
1455
+ def render_method_arguments out, arglists # :nodoc:
1456
+ return unless arglists
1457
+
1458
+ arglists = arglists.chomp.split "\n"
1459
+ arglists = arglists.map { |line| line + "\n" }
1460
+ out << RDoc::Markup::Verbatim.new(*arglists)
1461
+ out << RDoc::Markup::Rule.new(1)
1462
+ end
1463
+
1464
+ def render_method_comment out, method, alias_for = nil# :nodoc:
1465
+ if alias_for
1466
+ unless method.comment.nil? or method.comment.empty?
1467
+ out << RDoc::Markup::BlankLine.new
1468
+ out << method.comment
1469
+ end
1470
+ out << RDoc::Markup::BlankLine.new
1471
+ out << RDoc::Markup::Paragraph.new("(This method is an alias for #{alias_for.full_name}.)")
1472
+ out << RDoc::Markup::BlankLine.new
1473
+ out << alias_for.comment
1474
+ out << RDoc::Markup::BlankLine.new
1475
+ else
1476
+ out << RDoc::Markup::BlankLine.new
1477
+ out << method.comment
1478
+ out << RDoc::Markup::BlankLine.new
1479
+ end
1480
+ end
1481
+
1482
+ def render_method_superclass out, method # :nodoc:
1483
+ return unless
1484
+ method.respond_to?(:superclass_method) and method.superclass_method
1485
+
1486
+ out << RDoc::Markup::BlankLine.new
1487
+ out << RDoc::Markup::Heading.new(4, "(Uses superclass method #{method.superclass_method})")
1488
+ out << RDoc::Markup::Rule.new(1)
1489
+ end
1490
+
1491
+ ##
1492
+ # Looks up and displays ri data according to the options given.
1493
+
1494
+ def run
1495
+ if @list_doc_dirs then
1496
+ puts @doc_dirs
1497
+ elsif @list then
1498
+ list_known_classes @names
1499
+ elsif @server then
1500
+ start_server
1501
+ elsif @interactive or @names.empty? then
1502
+ interactive
1503
+ else
1504
+ display_names @names
1505
+ end
1506
+ rescue NotFoundError => e
1507
+ abort e.message
1508
+ end
1509
+
1510
+ ##
1511
+ # Sets up a pager program to pass output through. Tries the RI_PAGER and
1512
+ # PAGER environment variables followed by pager, less then more.
1513
+
1514
+ def setup_pager
1515
+ return if @use_stdout
1516
+
1517
+ jruby = RUBY_ENGINE == 'jruby'
1518
+
1519
+ pagers = [ENV['RI_PAGER'], ENV['PAGER'], 'pager', 'less', 'more']
1520
+
1521
+ pagers.compact.uniq.each do |pager|
1522
+ next unless pager
1523
+
1524
+ pager_cmd = pager.split(' ').first
1525
+
1526
+ next unless in_path? pager_cmd
1527
+
1528
+ if jruby then
1529
+ case io = find_pager_jruby(pager)
1530
+ when nil then break
1531
+ when false then next
1532
+ else io
1533
+ end
1534
+ else
1535
+ io = IO.popen(pager, 'w') rescue next
1536
+ end
1537
+
1538
+ next if $? and $?.pid == io.pid and $?.exited? # pager didn't work
1539
+
1540
+ @paging = true
1541
+
1542
+ return io
1543
+ end
1544
+
1545
+ @use_stdout = true
1546
+
1547
+ nil
1548
+ end
1549
+
1550
+ ##
1551
+ # Starts a WEBrick server for ri.
1552
+
1553
+ def start_server
1554
+ begin
1555
+ require 'webrick'
1556
+ rescue LoadError
1557
+ abort "webrick is not found. You may need to `gem install webrick` to install webrick."
1558
+ end
1559
+
1560
+ server = WEBrick::HTTPServer.new :Port => @server
1561
+
1562
+ extra_doc_dirs = @stores.map {|s| s.type == :extra ? s.path : nil}.compact
1563
+
1564
+ server.mount '/', RDoc::Servlet, nil, extra_doc_dirs
1565
+
1566
+ trap 'INT' do server.shutdown end
1567
+ trap 'TERM' do server.shutdown end
1568
+
1569
+ server.start
1570
+ end
1571
+
1572
+ end