Imported File#ftype spec from rubyspecs.
[rbx.git] / lib / rdoc / options.rb
blob8dc55a3f182e1ac1bec84987d85ed778e19400f7
1 # We handle the parsing of options, and subsequently as a singleton
2 # object to be queried for option values
4 require "rdoc/ri/paths"
5 require 'optparse'
7 class RDoc::Options
9   ##
10   # Should the output be placed into a single file
12   attr_reader :all_one_file
14   ##
15   # Character-set
17   attr_reader :charset
19   ##
20   # URL of stylesheet
22   attr_reader :css
24   ##
25   # Should diagrams be drawn
27   attr_reader :diagram
29   ##
30   # Files matching this pattern will be excluded
32   attr_accessor :exclude
34   ##
35   # Additional attr_... style method flags
37   attr_reader :extra_accessor_flags
39   ##
40   # Pattern for additional attr_... style methods
42   attr_reader :extra_accessors
44   ##
45   # Should we draw fileboxes in diagrams
47   attr_reader :fileboxes
49   ##
50   # The list of files to be processed
52   attr_accessor :files
54   ##
55   # Scan newer sources than the flag file if true.
57   attr_reader :force_update
59   ##
60   # Description of the output generator (set with the <tt>-fmt</tt> option)
62   attr_accessor :generator
64   ##
65   # image format for diagrams
67   attr_reader :image_format
69   ##
70   # Include line numbers in the source listings
72   attr_reader :include_line_numbers
74   ##
75   # Should source code be included inline, or displayed in a popup
77   attr_accessor :inline_source
79   ##
80   # Name of the file, class or module to display in the initial index page (if
81   # not specified the first file we encounter is used)
83   attr_accessor :main_page
85   ##
86   # Merge into classes of the same name when generating ri
88   attr_reader :merge
90   ##
91   # The name of the output directory
93   attr_accessor :op_dir
95   ##
96   # The name to use for the output
98   attr_reader :op_name
100   ##
101   # Are we promiscuous about showing module contents across multiple files
103   attr_reader :promiscuous
105   ##
106   # Don't display progress as we process the files
108   attr_reader :quiet
110   ##
111   # Array of directories to search for files to satisfy an :include:
113   attr_reader :rdoc_include
115   ##
116   # Include private and protected methods in the output
118   attr_accessor :show_all
120   ##
121   # Include the '#' at the front of hyperlinked instance method names
123   attr_reader :show_hash
125   ##
126   # The number of columns in a tab
128   attr_reader :tab_width
130   ##
131   # template to be used when generating output
133   attr_reader :template
135   ##
136   # Template class for file generation
137   #--
138   # HACK around dependencies in lib/rdoc/generator/html.rb
140   attr_accessor :template_class # :nodoc:
142   ##
143   # Documentation title
145   attr_reader :title
147   ##
148   # URL of web cvs frontend
150   attr_reader :webcvs
152   def initialize(generators) # :nodoc:
153     @op_dir = "doc"
154     @op_name = nil
155     @show_all = false
156     @main_page = nil
157     @merge = false
158     @exclude = []
159     @quiet = false
160     @generators = generators
161     @generator_name = 'html'
162     @generator = @generators[@generator_name]
163     @rdoc_include = []
164     @title = nil
165     @template = nil
166     @template_class = nil
167     @diagram = false
168     @fileboxes = false
169     @show_hash = false
170     @image_format = 'png'
171     @inline_source = false
172     @all_one_file = false
173     @tab_width = 8
174     @include_line_numbers = false
175     @extra_accessor_flags = {}
176     @promiscuous = false
177     @force_update = false
178     @title = "RDoc Documentation"
180     @css = nil
181     @webcvs = nil
183     @charset = 'iso-8859-1'
184   end
186   ##
187   # Parse command line options.
189   def parse(argv)
190     accessors = []
192     opts = OptionParser.new do |opt|
193       opt.program_name = File.basename $0
194       opt.version = RDoc::VERSION
195       opt.summary_indent = ' ' * 4
196       opt.banner = <<-EOF
197 Usage: #{opt.program_name} [options] [names...]
199   Files are parsed, and the information they contain collected, before any
200   output is produced. This allows cross references between all files to be
201   resolved. If a name is a directory, it is traversed. If no names are
202   specified, all Ruby files in the current directory (and subdirectories) are
203   processed.
205   How RDoc generates output depends on the output formatter being used, and on
206   the options you give.
208   - HTML output is normally produced into a number of separate files
209     (one per class, module, and file, along with various indices).
210     These files will appear in the directory given by the --op
211     option (doc/ by default).
213   - XML output by default is written to standard output. If a
214     --opname option is given, the output will instead be written
215     to a file with that name in the output directory.
217   - .chm files (Windows help files) are written in the --op directory.
218     If an --opname parameter is present, that name is used, otherwise
219     the file will be called rdoc.chm.
220       EOF
222       opt.separator nil
223       opt.separator "Options:"
224       opt.separator nil
226       opt.on("--accessor=ACCESSORS", "-A", Array,
227              "A comma separated list of additional class",
228              "methods that should be treated like",
229              "'attr_reader' and friends.",
230              " ",
231              "Option may be repeated.",
232              " ",
233              "Each accessorname may have '=text'",
234              "appended, in which case that text appears",
235              "where the r/w/rw appears for normal.",
236              "accessors") do |value|
237         value.each do |accessor|
238           if accessor =~ /^(\w+)(=(.*))?$/
239             accessors << $1
240             @extra_accessor_flags[$1] = $3
241           end
242         end
243       end
245       opt.separator nil
247       opt.on("--all", "-a",
248              "Include all methods (not just public) in",
249              "the output.") do |value|
250         @show_all = value
251       end
253       opt.separator nil
255       opt.on("--charset=CHARSET", "-c",
256              "Specifies the HTML character-set.") do |value|
257         @charset = value
258       end
260       opt.separator nil
262       opt.on("--debug", "-D",
263              "Displays lots on internal stuff.") do |value|
264         $DEBUG_RDOC = value
265       end
267       opt.separator nil
269       opt.on("--diagram", "-d",
270              "Generate diagrams showing modules and",
271              "classes. You need dot V1.8.6 or later to",
272              "use the --diagram option correctly. Dot is",
273              "available from http://graphviz.org") do |value|
274         check_diagram
275         @diagram = true
276       end
278       opt.separator nil
280       opt.on("--exclude=PATTERN", "-x", Regexp,
281              "Do not process files or directories",
282              "matching PATTERN. Files given explicitly",
283              "on the command line will never be",
284              "excluded.") do |value|
285         @exclude << value
286       end
288       opt.separator nil
290       opt.on("--extension=NEW=OLD", "-E",
291              "Treat files ending with .new as if they",
292              "ended with .old. Using '-E cgi=rb' will",
293              "cause xxx.cgi to be parsed as a Ruby file.") do |value|
294         new, old = value.split(/=/, 2)
296         unless new and old then
297           raise OptionParser::InvalidArgument, "Invalid parameter to '-E'"
298         end
300         unless RDoc::ParserFactory.alias_extension old, new then
301           raise OptionParser::InvalidArgument, "Unknown extension .#{old} to -E"
302         end
303       end
305       opt.separator nil
307       opt.on("--fileboxes", "-F",
308              "Classes are put in boxes which represents",
309              "files, where these classes reside. Classes",
310              "shared between more than one file are",
311              "shown with list of files that are sharing",
312              "them. Silently discarded if --diagram is",
313              "not given.") do |value|
314         @fileboxes = value
315       end
317       opt.separator nil
319       opt.on("--force-update", "-U",
320              "Forces rdoc to scan all sources even if",
321              "newer than the flag file.") do |value|
322         @force_update = value
323       end
325       opt.separator nil
327       opt.on("--fmt=FORMAT", "--format=FORMAT", "-f", @generators.keys,
328              "Set the output formatter.") do |value|
329         @generator_name = value.downcase
330         setup_generator
331       end
333       opt.separator nil
335       image_formats = %w[gif png jpg jpeg]
336       opt.on("--image-format=FORMAT", "-I", image_formats,
337              "Sets output image format for diagrams. Can",
338              "be #{image_formats.join ', '}. If this option",
339              "is omitted, png is used. Requires",
340              "diagrams.") do |value|
341         @image_format = value
342       end
344       opt.separator nil
346       opt.on("--include=DIRECTORIES", "-i", Array,
347              "set (or add to) the list of directories to",
348              "be searched when satisfying :include:",
349              "requests. Can be used more than once.") do |value|
350         @rdoc_include.concat value.map { |dir| dir.strip }
351       end
353       opt.separator nil
355       opt.on("--inline-source", "-S",
356              "Show method source code inline, rather than",
357              "via a popup link.") do |value|
358         @inline_source = value
359       end
361       opt.separator nil
363       opt.on("--line-numbers", "-N",
364              "Include line numbers in the source code.") do |value|
365         @include_line_numbers = value
366       end
368       opt.separator nil
370       opt.on("--main=NAME", "-m",
371              "NAME will be the initial page displayed.") do |value|
372         @main_page = value
373       end
375       opt.separator nil
377       opt.on("--merge", "-M",
378              "When creating ri output, merge previously",
379              "processed classes into previously",
380              "documented classes of the same name.") do |value|
381         @merge = value
382       end
384       opt.separator nil
386       opt.on("--one-file", "-1",
387              "Put all the output into a single file.") do |value|
388         @all_one_file = value
389         @inline_source = value if value
390         @template = 'one_page_html'
391       end
393       opt.separator nil
395       opt.on("--op=DIR", "-o",
396              "Set the output directory.") do |value|
397         @op_dir = value
398       end
400       opt.separator nil
402       opt.on("--opname=NAME", "-n",
403              "Set the NAME of the output. Has no effect",
404              "for HTML.") do |value|
405         @op_name = value
406       end
408       opt.separator nil
410       opt.on("--promiscuous", "-p",
411              "When documenting a file that contains a",
412              "module or class also defined in other",
413              "files, show all stuff for that module or",
414              "class in each files page. By default, only",
415              "show stuff defined in that particular file.") do |value|
416         @promiscuous = value
417       end
419       opt.separator nil
421       opt.on("--quiet", "-q",
422              "Don't show progress as we parse.") do |value|
423         @quiet = value
424       end
426       opt.separator nil
428       opt.on("--ri", "-r",
429              "Generate output for use by `ri`. The files",
430              "are stored in the '.rdoc' directory under",
431              "your home directory unless overridden by a",
432              "subsequent --op parameter, so no special",
433              "privileges are needed.") do |value|
434         @generator_name = "ri"
435         @op_dir = RDoc::RI::Paths::HOMEDIR
436         setup_generator
437       end
439       opt.separator nil
441       opt.on("--ri-site", "-R",
442              "Generate output for use by `ri`. The files",
443              "are stored in a site-wide directory,",
444              "making them accessible to others, so",
445              "special privileges are needed.") do |value|
446         @generator_name = "ri"
447         @op_dir = RDoc::RI::Paths::SITEDIR
448         setup_generator
449       end
451       opt.separator nil
453       opt.on("--ri-system", "-Y",
454              "Generate output for use by `ri`. The files",
455              "are stored in a site-wide directory,",
456              "making them accessible to others, so",
457              "special privileges are needed.  This",
458              "option is intended to be used during Ruby",
459              "installation.") do |value|
460         @generator_name = "ri"
461         @op_dir = RDoc::RI::Paths::SYSDIR
462         setup_generator
463       end
465       opt.separator nil
467       opt.on("--show-hash", "-H",
468              "A name of the form #name in a comment is a",
469              "possible hyperlink to an instance method",
470              "name. When displayed, the '#' is removed",
471              "unless this option is specified.") do |value|
472         @show_hash = value
473       end
475       opt.separator nil
477       opt.on("--style=URL", "-s",
478              "Specifies the URL of a separate stylesheet.") do |value|
479         @css = value
480       end
482       opt.separator nil
484       opt.on("--tab-width=WIDTH", "-w", OptionParser::DecimalInteger,
485              "Set the width of tab characters.") do |value|
486         @tab_width = value
487       end
489       opt.separator nil
491       opt.on("--template=NAME", "-T",
492              "Set the template used when generating",
493              "output.") do |value|
494         @template = value
495       end
497       opt.separator nil
499       opt.on("--title=TITLE", "-t",
500              "Set TITLE as the title for HTML output.") do |value|
501         @title = value
502       end
504       opt.separator nil
506       opt.on("--webcvs=URL", "-W",
507              "Specify a URL for linking to a web frontend",
508              "to CVS. If the URL contains a '\%s', the",
509              "name of the current file will be",
510              "substituted; if the URL doesn't contain a",
511              "'\%s', the filename will be appended to it.") do |value|
512         @webcvs = value
513       end
514     end
516     opts.parse! argv
518     @files = argv.dup
520     @rdoc_include << "." if @rdoc_include.empty?
522     if @exclude.empty? then
523       @exclude = nil
524     else
525       @exclude = Regexp.new(@exclude.join("|"))
526     end
528     check_files
530     # If no template was specified, use the default template for the output
531     # formatter
533     @template ||= @generator_name
535     # Generate a regexp from the accessors
536     unless accessors.empty? then
537       re = '^(' + accessors.map { |a| Regexp.quote a }.join('|') + ')$'
538       @extra_accessors = Regexp.new re
539     end
541   rescue OptionParser::InvalidArgument, OptionParser::InvalidOption => e
542     puts opts
543     puts
544     puts e
545     exit 1
546   end
548   ##
549   # Set the title, but only if not already set. This means that a title set
550   # from the command line trumps one set in a source file
552   def title=(string)
553     @title ||= string
554   end
556   private
558   ##
559   # Set up an output generator for the format in @generator_name
561   def setup_generator
562     @generator = @generators[@generator_name]
564     unless @generator then
565       raise OptionParser::InvalidArgument, "Invalid output formatter"
566     end
568     if @generator_name == "xml" then
569       @all_one_file = true
570       @inline_source = true
571     end
572   end
574   # Check that the right version of 'dot' is available.  Unfortuately this
575   # doesn't work correctly under Windows NT, so we'll bypass the test under
576   # Windows.
578   def check_diagram
579     return if RUBY_PLATFORM =~ /mswin|cygwin|mingw|bccwin/
581     ok = false
582     ver = nil
584     IO.popen "dot -V 2>&1" do |io|
585       ver = io.read
586       if ver =~ /dot.+version(?:\s+gviz)?\s+(\d+)\.(\d+)/ then
587         ok = ($1.to_i > 1) || ($1.to_i == 1 && $2.to_i >= 8)
588       end
589     end
591     unless ok then
592       if ver =~ /^dot.+version/ then
593         $stderr.puts "Warning: You may need dot V1.8.6 or later to use\n",
594           "the --diagram option correctly. You have:\n\n   ",
595           ver,
596           "\nDiagrams might have strange background colors.\n\n"
597       else
598         $stderr.puts "You need the 'dot' program to produce diagrams.",
599           "(see http://www.research.att.com/sw/tools/graphviz/)\n\n"
600         exit
601       end
602     end
603   end
605   ##
606   # Check that the files on the command line exist
608   def check_files
609     @files.each do |f|
610       stat = File.stat f rescue abort("File not found: #{f}")
611       abort("File '#{f}' not readable") unless stat.readable?
612     end
613   end