Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(401)

Side by Side Diff: pkg/args/lib/src/usage.dart

Issue 11819068: Add command support to args. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Fix a couple of type annotations. Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « pkg/args/lib/src/parser.dart ('k') | pkg/args/lib/src/utils.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 library args.src.usage;
6
7 import 'dart:math';
8
9 import '../args.dart';
10
11 /**
12 * Takes an [ArgParser] and generates a string of usage (i.e. help) text for its
13 * defined options. Internally, it works like a tabular printer. The output is
14 * divided into three horizontal columns, like so:
15 *
16 * -h, --help Prints the usage information
17 * | | | |
18 *
19 * It builds the usage text up one column at a time and handles padding with
20 * spaces and wrapping to the next line to keep the cells correctly lined up.
21 */
22 class Usage {
23 static const NUM_COLUMNS = 3; // Abbreviation, long name, help.
24
25 /** The parser this is generating usage for. */
26 final ArgParser args;
27
28 /** The working buffer for the generated usage text. */
29 StringBuffer buffer;
30
31 /**
32 * The column that the "cursor" is currently on. If the next call to
33 * [write()] is not for this column, it will correctly handle advancing to
34 * the next column (and possibly the next row).
35 */
36 int currentColumn = 0;
37
38 /** The width in characters of each column. */
39 List<int> columnWidths;
40
41 /**
42 * The number of sequential lines of text that have been written to the last
43 * column (which shows help info). We track this so that help text that spans
44 * multiple lines can be padded with a blank line after it for separation.
45 * Meanwhile, sequential options with single-line help will be compacted next
46 * to each other.
47 */
48 int numHelpLines = 0;
49
50 /**
51 * How many newlines need to be rendered before the next bit of text can be
52 * written. We do this lazily so that the last bit of usage doesn't have
53 * dangling newlines. We only write newlines right *before* we write some
54 * real content.
55 */
56 int newlinesNeeded = 0;
57
58 Usage(this.args);
59
60 /**
61 * Generates a string displaying usage information for the defined options.
62 * This is basically the help text shown on the command line.
63 */
64 String generate() {
65 buffer = new StringBuffer();
66
67 calculateColumnWidths();
68
69 args.options.forEach((name, option) {
70 write(0, getAbbreviation(option));
71 write(1, getLongOption(option));
72
73 if (option.help != null) write(2, option.help);
74
75 if (option.allowedHelp != null) {
76 var allowedNames = option.allowedHelp.keys.toList();
77 allowedNames.sort();
78 newline();
79 for (var name in allowedNames) {
80 write(1, getAllowedTitle(name));
81 write(2, option.allowedHelp[name]);
82 }
83 newline();
84 } else if (option.allowed != null) {
85 write(2, buildAllowedList(option));
86 } else if (option.defaultValue != null) {
87 if (option.isFlag && option.defaultValue == true) {
88 write(2, '(defaults to on)');
89 } else if (!option.isFlag) {
90 write(2, '(defaults to "${option.defaultValue}")');
91 }
92 }
93
94 // If any given option displays more than one line of text on the right
95 // column (i.e. help, default value, allowed options, etc.) then put a
96 // blank line after it. This gives space where it's useful while still
97 // keeping simple one-line options clumped together.
98 if (numHelpLines > 1) newline();
99 });
100
101 return buffer.toString();
102 }
103
104 String getAbbreviation(Option option) {
105 if (option.abbreviation != null) {
106 return '-${option.abbreviation}, ';
107 } else {
108 return '';
109 }
110 }
111
112 String getLongOption(Option option) {
113 if (option.negatable) {
114 return '--[no-]${option.name}';
115 } else {
116 return '--${option.name}';
117 }
118 }
119
120 String getAllowedTitle(String allowed) {
121 return ' [$allowed]';
122 }
123
124 void calculateColumnWidths() {
125 int abbr = 0;
126 int title = 0;
127 args.options.forEach((name, option) {
128 // Make room in the first column if there are abbreviations.
129 abbr = max(abbr, getAbbreviation(option).length);
130
131 // Make room for the option.
132 title = max(title, getLongOption(option).length);
133
134 // Make room for the allowed help.
135 if (option.allowedHelp != null) {
136 for (var allowed in option.allowedHelp.keys) {
137 title = max(title, getAllowedTitle(allowed).length);
138 }
139 }
140 });
141
142 // Leave a gutter between the columns.
143 title += 4;
144 columnWidths = [abbr, title];
145 }
146
147 newline() {
148 newlinesNeeded++;
149 currentColumn = 0;
150 numHelpLines = 0;
151 }
152
153 write(int column, String text) {
154 var lines = text.split('\n');
155
156 // Strip leading and trailing empty lines.
157 while (lines.length > 0 && lines[0].trim() == '') {
158 lines.removeRange(0, 1);
159 }
160
161 while (lines.length > 0 && lines[lines.length - 1].trim() == '') {
162 lines.removeLast();
163 }
164
165 for (var line in lines) {
166 writeLine(column, line);
167 }
168 }
169
170 writeLine(int column, String text) {
171 // Write any pending newlines.
172 while (newlinesNeeded > 0) {
173 buffer.add('\n');
174 newlinesNeeded--;
175 }
176
177 // Advance until we are at the right column (which may mean wrapping around
178 // to the next line.
179 while (currentColumn != column) {
180 if (currentColumn < NUM_COLUMNS - 1) {
181 buffer.add(padRight('', columnWidths[currentColumn]));
182 } else {
183 buffer.add('\n');
184 }
185 currentColumn = (currentColumn + 1) % NUM_COLUMNS;
186 }
187
188 if (column < columnWidths.length) {
189 // Fixed-size column, so pad it.
190 buffer.add(padRight(text, columnWidths[column]));
191 } else {
192 // The last column, so just write it.
193 buffer.add(text);
194 }
195
196 // Advance to the next column.
197 currentColumn = (currentColumn + 1) % NUM_COLUMNS;
198
199 // If we reached the last column, we need to wrap to the next line.
200 if (column == NUM_COLUMNS - 1) newlinesNeeded++;
201
202 // Keep track of how many consecutive lines we've written in the last
203 // column.
204 if (column == NUM_COLUMNS - 1) {
205 numHelpLines++;
206 } else {
207 numHelpLines = 0;
208 }
209 }
210
211 buildAllowedList(Option option) {
212 var allowedBuffer = new StringBuffer();
213 allowedBuffer.add('[');
214 bool first = true;
215 for (var allowed in option.allowed) {
216 if (!first) allowedBuffer.add(', ');
217 allowedBuffer.add(allowed);
218 if (allowed == option.defaultValue) {
219 allowedBuffer.add(' (default)');
220 }
221 first = false;
222 }
223 allowedBuffer.add(']');
224 return allowedBuffer.toString();
225 }
226 }
227
228 /** Pads [source] to [length] by adding spaces at the end. */
229 String padRight(String source, int length) {
230 final result = new StringBuffer();
231 result.add(source);
232
233 while (result.length < length) {
234 result.add(' ');
235 }
236
237 return result.toString();
238 }
OLDNEW
« no previous file with comments | « pkg/args/lib/src/parser.dart ('k') | pkg/args/lib/src/utils.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698