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

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

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

Powered by Google App Engine
This is Rietveld 408576698