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

Side by Side Diff: tools/gn/command_format.cc

Issue 591373002: gn: start of format command (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@gn-more-comment-stuff
Patch Set: ignore blockcomment in list eval Created 6 years, 2 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
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <sstream>
6
7 #include "base/command_line.h"
8 #include "tools/gn/commands.h"
9 #include "tools/gn/input_file.h"
10 #include "tools/gn/parser.h"
11 #include "tools/gn/scheduler.h"
12 #include "tools/gn/setup.h"
13 #include "tools/gn/source_file.h"
14 #include "tools/gn/tokenizer.h"
15
16 namespace commands {
17
18 const char kSwitchDumpTree[] = "dump-tree";
19
20 const char kFormat[] = "format";
21 const char kFormat_HelpShort[] =
22 "format: Format .gn file.";
23 const char kFormat_Help[] =
24 "gn format: Format .gn file.\n"
brettw 2014/09/25 22:11:32 Can you append here "(alpha, use at own risk)" or
scottmg 2014/09/25 23:03:11 Done.
25 "\n"
26 " gn format //some/BUILD.gn\n"
27 " gn format some\\BUILD.gn\n"
28 "\n"
29 " Formats .gn file to a standard format. THIS IS NOT FULLY IMPLEMENTED\n"
30 " YET! IT WILL EAT YOUR BEAUTIFUL .GN FILES. AND YOUR LAUNDRY.\n"
31 " At a minimum, make sure everything is `git commit`d so you can\n"
32 " `git checkout -f` to recover.\n";
33
34 namespace {
35
36 class Printer {
37 public:
38 Printer();
39 ~Printer();
40
41 void Block(const ParseNode* file);
42
43 std::string String() const { return output_; }
44
45 private:
46 // Add to output.
47 void Print(base::StringPiece str);
48
49 // Formatted output.
50 void Printf(const char* fmt, ...);
51
52 void TrimAndPrintToken(const Token& token);
53
54 // End the current line, flushing end of line comments.
55 void Newline();
56
57 // Remove trailing spaces from the current line.
58 void Trim();
59
60 // Get the 0-based x position on the current line.
61 int CurrentColumn();
62
63 enum ExprStyle {
64 kExprStyleRegular,
65 kExprStyleComment,
66 };
67 // Print the expression to the output buffer. Returns the type of element
68 // added to the output.
69 ExprStyle Expr(const ParseNode* root);
70
71 // Format a list of values using the given style.
brettw 2014/09/25 22:11:32 These enums should go at the top of the "private"
scottmg 2014/09/25 23:03:11 Done.
72 enum SequenceStyle {
73 kSequenceStyleFunctionCall,
74 kSequenceStyleList,
75 kSequenceStyleBlock,
76 };
77
78 template <class PARSENODE> // Just for const covariance.
79 void Sequence(SequenceStyle style, const std::vector<PARSENODE*>& list);
80
81 enum { kIndentSize = 2 };
brettw 2014/09/25 22:11:32 Can this be a static const int instead so it's typ
scottmg 2014/09/25 23:03:11 Done.
82
83 std::string output_; // Output buffer.
84 std::vector<Token> comments_; // Pending end-of-line comments.
85 int margin_; // Left margin (number of spaces).
86
87 DISALLOW_COPY_AND_ASSIGN(Printer);
88 };
89
90 Printer::Printer() : margin_(0) {
91 output_.reserve(100 << 10);
92 }
93
94 Printer::~Printer() {
95 }
96
97 void Printer::Print(base::StringPiece str) {
98 str.AppendToString(&output_);
99 }
100
101 void Printer::Printf(const char* fmt, ...) {
102 va_list ap;
103 va_start(ap, fmt);
104 char buf[256]; // Intended for single line output, so shouldn't be a factor.
105 vsnprintf(buf, sizeof(buf), fmt, ap);
106 va_end(ap);
107 output_ += buf;
brettw 2014/09/25 22:11:32 Can this all be replaced with: va_list ap; va_
scottmg 2014/09/25 23:03:11 Good point! I had other more complicated format st
108 }
109
110 void Printer::TrimAndPrintToken(const Token& token) {
111 std::string trimmed;
112 TrimWhitespaceASCII(token.value().as_string(), base::TRIM_ALL, &trimmed);
113 Print(trimmed);
114 }
115
116 void Printer::Newline() {
117 if (!comments_.empty()) {
118 Print(" ");
119 int i = 0;
120 for (const auto& c : comments_) {
121 if (i > 0) {
122 Trim();
123 Printf("\n%*s", margin_, "");
124 }
125 TrimAndPrintToken(c);
126 }
127 comments_.clear();
128 }
129 Trim();
130 Printf("\n%*s", margin_, "");
131 }
132
133 void Printer::Trim() {
134 size_t n = output_.size();
135 while (n > 0 && output_[n - 1] == ' ')
136 --n;
137 output_.resize(n);
138 }
139
140 int Printer::CurrentColumn() {
141 int n = 0;
142 while (n < static_cast<int>(output_.size()) &&
143 output_[output_.size() - 1 - n] != '\n') {
144 ++n;
145 }
146 return n;
147 }
148
149 void Printer::Block(const ParseNode* root) {
150 const BlockNode* block = root->AsBlock();
151
152 if (block->comments()) {
153 for (const auto& c : block->comments()->before()) {
154 TrimAndPrintToken(c);
155 Newline();
156 }
157 }
158
159 size_t i = 0;
160 for (const auto& stmt : block->statements()) {
161 Expr(stmt);
162 Newline();
163 if (stmt->comments()) {
brettw 2014/09/25 22:11:32 What about "before" statement comments?
scottmg 2014/09/25 23:03:11 Hmm, the asymmetry is kind of confusing. The befor
brettw 2014/09/25 23:06:58 Can you explain this in a comment?
scottmg 2014/09/25 23:13:12 Done.
164 for (const auto& c : stmt->comments()->after()) {
165 TrimAndPrintToken(c);
166 Newline();
167 }
168 }
169 if (i < block->statements().size() - 1)
170 Newline();
171 ++i;
172 }
173
174 if (block->comments()) {
175 for (const auto& c : block->comments()->after()) {
176 TrimAndPrintToken(c);
177 Newline();
178 }
179 }
180 }
181
182 Printer::ExprStyle Printer::Expr(const ParseNode* root) {
183 ExprStyle result = kExprStyleRegular;
184 if (root->comments()) {
185 if (!root->comments()->before().empty()) {
186 Trim();
187 // If there's already other text on the line, start a new line.
188 if (CurrentColumn() > 0)
189 Print("\n");
190 // We're printing a line comment, so we need to be at the current margin.
191 Printf("%*s", margin_, "");
192 for (const auto& c : root->comments()->before()) {
193 TrimAndPrintToken(c);
194 Newline();
195 }
196 }
197 }
198
199 if (const AccessorNode* accessor = root->AsAccessor()) {
200 Print("TODO(scottmg): AccessorNode");
201 } else if (const BinaryOpNode* binop = root->AsBinaryOp()) {
202 // TODO(scottmg): Lots to do here for complex if expressions: reflowing,
203 // parenthesizing, etc.
204 Expr(binop->left());
205 Print(" ");
206 Print(binop->op().value());
207 Print(" ");
208 Expr(binop->right());
209 } else if (const BlockNode* block = root->AsBlock()) {
210 Sequence(kSequenceStyleBlock, block->statements());
211 } else if (const ConditionNode* condition = root->AsConditionNode()) {
212 Print("if (");
213 Expr(condition->condition());
214 Print(") {");
215 margin_ += kIndentSize;
216 Newline();
217 Block(condition->if_true());
218 margin_ -= kIndentSize;
219 Trim();
220 Printf("%*s}", margin_, "");
221 if (condition->if_false()) {
222 Print(" else ");
223 // If it's a block it's a bare 'else', otherwise it's an 'else if'. See
224 // ConditionNode::Execute.
225 bool is_else_if = condition->if_false()->AsBlock() == NULL;
226 if (is_else_if) {
227 Expr(condition->if_false());
228 } else {
229 Print("{");
230 margin_ += kIndentSize;
231 Newline();
232 Block(condition->if_false());
233 margin_ -= kIndentSize;
234 Trim();
235 Printf("%*s}", margin_, "");
236 }
237 }
238 } else if (const FunctionCallNode* func_call = root->AsFunctionCall()) {
239 Print(func_call->function().value());
240 Sequence(kSequenceStyleFunctionCall, func_call->args()->contents());
241 Print(" {");
242 margin_ += kIndentSize;
243 Newline();
244 Block(func_call->block());
245 margin_ -= kIndentSize;
246 Trim();
247 Printf("%*s}", margin_, "");
248 } else if (const IdentifierNode* identifier = root->AsIdentifier()) {
249 Print(identifier->value().value());
250 } else if (const ListNode* list = root->AsList()) {
251 Sequence(kSequenceStyleList, list->contents());
252 } else if (const LiteralNode* literal = root->AsLiteral()) {
253 // TODO(scottmg): Quoting?
254 Print(literal->value().value());
255 } else if (const UnaryOpNode* unaryop = root->AsUnaryOp()) {
256 Print(unaryop->op().value());
257 Expr(unaryop->operand());
258 } else if (const BlockCommentNode* block_comment = root->AsBlockComment()) {
259 Print(block_comment->comment().value());
260 result = kExprStyleComment;
261 } else {
262 CHECK(false) << "Unhandled case in Expr.";
263 }
264
265 // Defer any end of line comment until we reach the newline.
266 if (root->comments() && !root->comments()->suffix().empty()) {
267 std::copy(root->comments()->suffix().begin(),
268 root->comments()->suffix().end(),
269 std::back_inserter(comments_));
270 }
271
272 return result;
273 }
274
275 template <class PARSENODE>
276 void Printer::Sequence(SequenceStyle style,
277 const std::vector<PARSENODE*>& list) {
278 bool force_multiline = false;
279 if (style == kSequenceStyleFunctionCall)
280 Print("(");
281 else if (style == kSequenceStyleList)
282 Print("[");
283
284 if (style == kSequenceStyleBlock)
285 force_multiline = true;
286
287 // If there's before line comments, make sure we have a place to put them.
288 for (const auto& i : list) {
289 if (i->comments() && !i->comments()->before().empty())
290 force_multiline = true;
291 }
292
293 if (list.size() == 0 && !force_multiline) {
294 // No elements, and not forcing newlines, print nothing.
295 } else if (list.size() == 1 && !force_multiline) {
296 if (style != kSequenceStyleFunctionCall)
297 Print(" ");
298 Expr(list[0]);
299 if (style != kSequenceStyleFunctionCall)
300 Print(" ");
301 } else {
302 margin_ += kIndentSize;
303 size_t i = 0;
304 for (const auto& x : list) {
305 Newline();
306 ExprStyle expr_style = Expr(x);
307 if (i < list.size() - 1 || style == kSequenceStyleList) {
308 if (expr_style == kExprStyleRegular)
309 Print(",");
310 else
311 Newline();
312 }
313 ++i;
314 }
315
316 margin_ -= kIndentSize;
317 Newline();
318 }
319
320 if (style == kSequenceStyleFunctionCall)
321 Print(")");
322 else if (style == kSequenceStyleList)
323 Print("]");
324 }
325
326 } // namespace
327
328 bool FormatFileToString(const std::string& input_filename,
329 std::string* output, bool dump_tree) {
brettw 2014/09/25 22:11:32 Out arg should be last.
scottmg 2014/09/25 23:03:11 Done.
330 Setup setup;
331 Err err;
332 SourceFile input_file(input_filename);
333 const ParseNode* parse_node =
334 setup.scheduler().input_file_manager()->SyncLoadFile(
335 LocationRange(), &setup.build_settings(), input_file, &err);
336 if (err.has_error()) {
337 err.PrintToStdout();
338 return false;
339 }
340 if (dump_tree) {
341 std::ostringstream os;
342 parse_node->Print(os, 0);
343 printf("----------------------\n");
344 printf("-- PARSE TREE --------\n");
345 printf("----------------------\n");
346 printf("%s", os.str().c_str());
347 printf("----------------------\n");
348 }
349 Printer pr;
350 pr.Block(parse_node);
351 *output = pr.String();
352 return true;
353 }
354
355 int RunFormat(const std::vector<std::string>& args) {
356 // TODO(scottmg): Eventually, this should be a list/spec of files, and they
357 // should all be done in parallel and in-place. For now, we don't want to
358 // overwrite good data with mistakenly reformatted stuff, so we just simply
359 // print the formatted output to stdout.
360 if (args.size() != 1) {
361 Err(Location(), "Expecting exactly one argument, see `gn help format`.\n")
362 .PrintToStdout();
363 return 1;
364 }
365
366 bool dump_tree =
367 base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchDumpTree);
368
369 std::string input_name = args[0];
370 if (input_name[0] != '/') {
371 std::replace(input_name.begin(), input_name.end(), '\\', '/');
372 input_name = "//" + input_name;
373 }
374 std::string output_string;
375 if (FormatFileToString(input_name, &output_string, dump_tree)) {
376 printf("%s", output_string.c_str());
377 }
378
379 return 0;
380 }
381
382 } // namespace commands
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698