mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
221 lines
5.4 KiB
Ruby
221 lines
5.4 KiB
Ruby
|
# = Synopsis
|
||
|
#
|
||
|
# This library allows command-line tools to encapsulate their usage
|
||
|
# as a comment at the top of the main file. Calling <tt>RDoc::show_usage</tt>
|
||
|
# then displays some or all of that comment, and optionally exits
|
||
|
# the program with an exit status. We always look for the comment
|
||
|
# in the main program file, so it is safe to call this method
|
||
|
# from anywhere in the executing program.
|
||
|
#
|
||
|
# = Usage
|
||
|
#
|
||
|
# RDoc::usage( [ exit_status ], [ section, ...], [options])
|
||
|
# RDoc::usage_no_exit( [ section, ...], [options])
|
||
|
#
|
||
|
# where:
|
||
|
#
|
||
|
# exit_status::
|
||
|
# the integer exit code (default zero). RDoc::usage will exit
|
||
|
# the calling program with this status.
|
||
|
#
|
||
|
# section::
|
||
|
# an optional list of section names. If specified, only the
|
||
|
# sections with the given names as headings will be output.
|
||
|
# For example, this section is named 'Usage', and the next
|
||
|
# section is named 'Examples'. The section names are case
|
||
|
# insensitive.
|
||
|
#
|
||
|
# options::
|
||
|
# a hash of options. Option keys and values must implement +to_str+.
|
||
|
# Supported options are:
|
||
|
#
|
||
|
# formatter::
|
||
|
# plain | html | bs | ansi
|
||
|
#
|
||
|
# Select the output format: plain text, html, text using
|
||
|
# backspace combinations to do underling and bold, or
|
||
|
# text using ANSI escape sequences to colorize bold, italic, etc
|
||
|
#
|
||
|
#
|
||
|
# = Examples
|
||
|
#
|
||
|
# # Comment block describing usage
|
||
|
# # with (optional) section headings
|
||
|
# # . . .
|
||
|
#
|
||
|
# require 'rdoc/usage'
|
||
|
#
|
||
|
# # Display all usage and exit with a status of 0
|
||
|
#
|
||
|
# RDoc::usage
|
||
|
#
|
||
|
# # Display all usage and exit with a status of 99
|
||
|
#
|
||
|
# RDoc::usage(99)
|
||
|
#
|
||
|
# # Display usage in the 'Summary' section only, then
|
||
|
# # exit with a status of 99
|
||
|
#
|
||
|
# RDoc::usage(99, 'Summary')
|
||
|
#
|
||
|
# # Display information in the Author and Copyright
|
||
|
# # sections, then exit 0. Use the ANSI formatter
|
||
|
# # (which colorized using ANSI escape sequences)
|
||
|
#
|
||
|
# RDoc::usage('Author', 'Copyright', :formatter => :ansi)
|
||
|
#
|
||
|
# # Display information in the Author and Copyright
|
||
|
# # sections, but don't exit
|
||
|
#
|
||
|
# RDoc::usage_no_exit('Author', 'Copyright')
|
||
|
#
|
||
|
# = Author
|
||
|
#
|
||
|
# Dave Thomas, The Pragmatic Programmers, LLC
|
||
|
#
|
||
|
# = Copyright
|
||
|
#
|
||
|
# Copyright (c) 2004 Dave Thomas.
|
||
|
# Licensed under the same terms as Ruby
|
||
|
#
|
||
|
|
||
|
require 'rdoc/markup/simple_markup'
|
||
|
require 'rdoc/markup/simple_markup/to_flow'
|
||
|
require 'rdoc/ri/ri_formatter'
|
||
|
require 'rdoc/ri/ri_options'
|
||
|
|
||
|
module RDoc
|
||
|
|
||
|
# Display usage information from the comment at the top of
|
||
|
# the file. String arguments identify specific sections of the
|
||
|
# comment to display. An optional integer first argument
|
||
|
# specifies the exit status (defaults to 0)
|
||
|
|
||
|
def RDoc.usage(*args)
|
||
|
exit_code = 0
|
||
|
|
||
|
if args.size > 0
|
||
|
status = args[0]
|
||
|
if status.respond_to?(:to_int)
|
||
|
exit_code = status.to_int
|
||
|
args.shift
|
||
|
end
|
||
|
end
|
||
|
|
||
|
# display the usage and exit with the given code
|
||
|
usage_no_exit(*args)
|
||
|
exit(exit_code)
|
||
|
end
|
||
|
|
||
|
# Display usage
|
||
|
def RDoc.usage_no_exit(*args)
|
||
|
main_program_file, = caller[-1].split(/:/, 2)
|
||
|
comment = File.open(main_program_file) do |file|
|
||
|
find_comment(file)
|
||
|
end
|
||
|
|
||
|
comment = comment.gsub(/^\s*#/, '')
|
||
|
|
||
|
markup = SM::SimpleMarkup.new
|
||
|
flow_convertor = SM::ToFlow.new
|
||
|
|
||
|
flow = markup.convert(comment, flow_convertor)
|
||
|
|
||
|
format = "plain"
|
||
|
|
||
|
unless args.empty?
|
||
|
if args[-1].class == Hash
|
||
|
options = args.pop
|
||
|
o_format = options[:formatter] || options['formatter']
|
||
|
format = o_format.to_s if o_format
|
||
|
end
|
||
|
flow = extract_sections(flow, args)
|
||
|
end
|
||
|
|
||
|
options = RI::Options.instance
|
||
|
formatter = RI::TextFormatter.for(format).new(options, " ")
|
||
|
formatter.display_flow(flow)
|
||
|
end
|
||
|
|
||
|
######################################################################
|
||
|
|
||
|
private
|
||
|
|
||
|
# Find the first comment in the file (that isn't a shebang line)
|
||
|
# If the file doesn't start with a comment, report the fact
|
||
|
# and return nil
|
||
|
|
||
|
def RDoc.find_comment(file)
|
||
|
# skip leading blank lines and shebangs
|
||
|
while line = file.gets
|
||
|
break unless line =~ /^(#!|\s*$)/
|
||
|
end
|
||
|
|
||
|
comment = []
|
||
|
|
||
|
while line && line =~ /^\s*#/
|
||
|
comment << line
|
||
|
line = file.gets
|
||
|
end
|
||
|
|
||
|
return no_comment if comment.empty?
|
||
|
|
||
|
comment.join
|
||
|
end
|
||
|
|
||
|
|
||
|
#####
|
||
|
# Given an array of flow items and an array of section names, extract those
|
||
|
# sections from the flow which have headings corresponding to
|
||
|
# a section name in the list. Return them in the order
|
||
|
# of names in the +sections+ array.
|
||
|
|
||
|
def RDoc.extract_sections(flow, sections)
|
||
|
result = []
|
||
|
sections.each do |name|
|
||
|
name = name.downcase
|
||
|
copy_upto_level = nil
|
||
|
|
||
|
flow.each do |item|
|
||
|
case item
|
||
|
when SM::Flow::H
|
||
|
if copy_upto_level && item.level >= copy_upto_level
|
||
|
copy_upto_level = nil
|
||
|
else
|
||
|
if item.text[0].downcase == name
|
||
|
result << item
|
||
|
copy_upto_level = item.level
|
||
|
end
|
||
|
end
|
||
|
else
|
||
|
if copy_upto_level
|
||
|
result << item
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
result
|
||
|
end
|
||
|
|
||
|
#####
|
||
|
# Report the fact that no doc comment count be found
|
||
|
def RDoc.no_comment
|
||
|
$stderr.puts "No usage information available for this program"
|
||
|
nil
|
||
|
end
|
||
|
end
|
||
|
|
||
|
|
||
|
if $0 == __FILE__
|
||
|
|
||
|
opts = {}
|
||
|
if ARGV[0] && ARGV[0] =~ /^(plain|ansi|html|bs)$/
|
||
|
opts[:formatter] = ARGV.shift
|
||
|
end
|
||
|
|
||
|
ARGV.push opts unless opts.empty?
|
||
|
|
||
|
RDoc::usage(*ARGV)
|
||
|
|
||
|
end
|