1 | # = Synopsis
|
---|
2 | #
|
---|
3 | # This library allows command-line tools to encapsulate their usage
|
---|
4 | # as a comment at the top of the main file. Calling <tt>RDoc::usage</tt>
|
---|
5 | # then displays some or all of that comment, and optionally exits
|
---|
6 | # the program with an exit status. We always look for the comment
|
---|
7 | # in the main program file, so it is safe to call this method
|
---|
8 | # from anywhere in the executing program.
|
---|
9 | #
|
---|
10 | # = Usage
|
---|
11 | #
|
---|
12 | # RDoc::usage( [ exit_status ], [ section, ...])
|
---|
13 | # RDoc::usage_no_exit( [ section, ...])
|
---|
14 | #
|
---|
15 | # where:
|
---|
16 | #
|
---|
17 | # exit_status::
|
---|
18 | # the integer exit code (default zero). RDoc::usage will exit
|
---|
19 | # the calling program with this status.
|
---|
20 | #
|
---|
21 | # section::
|
---|
22 | # an optional list of section names. If specified, only the
|
---|
23 | # sections with the given names as headings will be output.
|
---|
24 | # For example, this section is named 'Usage', and the next
|
---|
25 | # section is named 'Examples'. The section names are case
|
---|
26 | # insensitive.
|
---|
27 | #
|
---|
28 | # = Examples
|
---|
29 | #
|
---|
30 | # # Comment block describing usage
|
---|
31 | # # with (optional) section headings
|
---|
32 | # # . . .
|
---|
33 | #
|
---|
34 | # require 'rdoc/usage'
|
---|
35 | #
|
---|
36 | # # Display all usage and exit with a status of 0
|
---|
37 | #
|
---|
38 | # RDoc::usage
|
---|
39 | #
|
---|
40 | # # Display all usage and exit with a status of 99
|
---|
41 | #
|
---|
42 | # RDoc::usage(99)
|
---|
43 | #
|
---|
44 | # # Display usage in the 'Summary' section only, then
|
---|
45 | # # exit with a status of 99
|
---|
46 | #
|
---|
47 | # RDoc::usage(99, 'Summary')
|
---|
48 | #
|
---|
49 | # # Display information in the Author and Copyright
|
---|
50 | # # sections, then exit 0.
|
---|
51 | #
|
---|
52 | # RDoc::usage('Author', 'Copyright')
|
---|
53 | #
|
---|
54 | # # Display information in the Author and Copyright
|
---|
55 | # # sections, but don't exit
|
---|
56 | #
|
---|
57 | # RDoc::usage_no_exit('Author', 'Copyright')
|
---|
58 | #
|
---|
59 | # = Author
|
---|
60 | #
|
---|
61 | # Dave Thomas, The Pragmatic Programmers, LLC
|
---|
62 | #
|
---|
63 | # = Copyright
|
---|
64 | #
|
---|
65 | # Copyright (c) 2004 Dave Thomas.
|
---|
66 | # Licensed under the same terms as Ruby
|
---|
67 | #
|
---|
68 |
|
---|
69 | require 'rdoc/markup/simple_markup'
|
---|
70 | require 'rdoc/markup/simple_markup/to_flow'
|
---|
71 | require 'rdoc/ri/ri_formatter'
|
---|
72 | require 'rdoc/ri/ri_options'
|
---|
73 |
|
---|
74 | module RDoc
|
---|
75 |
|
---|
76 | # Display usage information from the comment at the top of
|
---|
77 | # the file. String arguments identify specific sections of the
|
---|
78 | # comment to display. An optional integer first argument
|
---|
79 | # specifies the exit status (defaults to 0)
|
---|
80 |
|
---|
81 | def RDoc.usage(*args)
|
---|
82 | exit_code = 0
|
---|
83 |
|
---|
84 | if args.size > 0
|
---|
85 | status = args[0]
|
---|
86 | if status.respond_to?(:to_int)
|
---|
87 | exit_code = status.to_int
|
---|
88 | args.shift
|
---|
89 | end
|
---|
90 | end
|
---|
91 |
|
---|
92 | # display the usage and exit with the given code
|
---|
93 | usage_no_exit(*args)
|
---|
94 | exit(exit_code)
|
---|
95 | end
|
---|
96 |
|
---|
97 | # Display usage
|
---|
98 | def RDoc.usage_no_exit(*args)
|
---|
99 | main_program_file = caller[-1].sub(/:\d+$/, '')
|
---|
100 | comment = File.open(main_program_file) do |file|
|
---|
101 | find_comment(file)
|
---|
102 | end
|
---|
103 |
|
---|
104 | comment = comment.gsub(/^\s*#/, '')
|
---|
105 |
|
---|
106 | markup = SM::SimpleMarkup.new
|
---|
107 | flow_convertor = SM::ToFlow.new
|
---|
108 |
|
---|
109 | flow = markup.convert(comment, flow_convertor)
|
---|
110 |
|
---|
111 | format = "plain"
|
---|
112 |
|
---|
113 | unless args.empty?
|
---|
114 | flow = extract_sections(flow, args)
|
---|
115 | end
|
---|
116 |
|
---|
117 | options = RI::Options.instance
|
---|
118 | if args = ENV["RI"]
|
---|
119 | options.parse(args.split)
|
---|
120 | end
|
---|
121 | formatter = options.formatter.new(options, "")
|
---|
122 | formatter.display_flow(flow)
|
---|
123 | end
|
---|
124 |
|
---|
125 | ######################################################################
|
---|
126 |
|
---|
127 | private
|
---|
128 |
|
---|
129 | # Find the first comment in the file (that isn't a shebang line)
|
---|
130 | # If the file doesn't start with a comment, report the fact
|
---|
131 | # and return empty string
|
---|
132 |
|
---|
133 | def RDoc.gets(file)
|
---|
134 | if (line = file.gets) && (line =~ /^#!/) # shebang
|
---|
135 | throw :exit, find_comment(file)
|
---|
136 | else
|
---|
137 | line
|
---|
138 | end
|
---|
139 | end
|
---|
140 |
|
---|
141 | def RDoc.find_comment(file)
|
---|
142 | catch(:exit) do
|
---|
143 | # skip leading blank lines
|
---|
144 | 0 while (line = gets(file)) && (line =~ /^\s*$/)
|
---|
145 |
|
---|
146 | comment = []
|
---|
147 | while line && line =~ /^\s*#/
|
---|
148 | comment << line
|
---|
149 | line = gets(file)
|
---|
150 | end
|
---|
151 |
|
---|
152 | 0 while line && (line = gets(file))
|
---|
153 | return no_comment if comment.empty?
|
---|
154 | return comment.join
|
---|
155 | end
|
---|
156 | end
|
---|
157 |
|
---|
158 |
|
---|
159 | #####
|
---|
160 | # Given an array of flow items and an array of section names, extract those
|
---|
161 | # sections from the flow which have headings corresponding to
|
---|
162 | # a section name in the list. Return them in the order
|
---|
163 | # of names in the +sections+ array.
|
---|
164 |
|
---|
165 | def RDoc.extract_sections(flow, sections)
|
---|
166 | result = []
|
---|
167 | sections.each do |name|
|
---|
168 | name = name.downcase
|
---|
169 | copy_upto_level = nil
|
---|
170 |
|
---|
171 | flow.each do |item|
|
---|
172 | case item
|
---|
173 | when SM::Flow::H
|
---|
174 | if copy_upto_level && item.level >= copy_upto_level
|
---|
175 | copy_upto_level = nil
|
---|
176 | else
|
---|
177 | if item.text.downcase == name
|
---|
178 | result << item
|
---|
179 | copy_upto_level = item.level
|
---|
180 | end
|
---|
181 | end
|
---|
182 | else
|
---|
183 | if copy_upto_level
|
---|
184 | result << item
|
---|
185 | end
|
---|
186 | end
|
---|
187 | end
|
---|
188 | end
|
---|
189 | if result.empty?
|
---|
190 | puts "Note to developer: requested section(s) [#{sections.join(', ')}] " +
|
---|
191 | "not found"
|
---|
192 | result = flow
|
---|
193 | end
|
---|
194 | result
|
---|
195 | end
|
---|
196 |
|
---|
197 | #####
|
---|
198 | # Report the fact that no doc comment count be found
|
---|
199 | def RDoc.no_comment
|
---|
200 | $stderr.puts "No usage information available for this program"
|
---|
201 | ""
|
---|
202 | end
|
---|
203 | end
|
---|
204 |
|
---|
205 |
|
---|
206 | if $0 == __FILE__
|
---|
207 |
|
---|
208 | RDoc::usage(*ARGV)
|
---|
209 |
|
---|
210 | end
|
---|