gitlab-rdoc 6.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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