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

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

Issue 607243002: gn format: add call wrapping (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@gn-suffix-missing-newline
Patch Set: make expression wrapping work a bit, test for accessors 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
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <sstream> 5 #include <sstream>
6 6
7 #include "base/command_line.h" 7 #include "base/command_line.h"
8 #include "base/strings/string_split.h"
8 #include "tools/gn/commands.h" 9 #include "tools/gn/commands.h"
9 #include "tools/gn/input_file.h" 10 #include "tools/gn/input_file.h"
10 #include "tools/gn/parser.h" 11 #include "tools/gn/parser.h"
11 #include "tools/gn/scheduler.h" 12 #include "tools/gn/scheduler.h"
12 #include "tools/gn/setup.h" 13 #include "tools/gn/setup.h"
13 #include "tools/gn/source_file.h" 14 #include "tools/gn/source_file.h"
14 #include "tools/gn/tokenizer.h" 15 #include "tools/gn/tokenizer.h"
15 16
16 namespace commands { 17 namespace commands {
17 18
18 const char kSwitchDumpTree[] = "dump-tree"; 19 const char kSwitchDumpTree[] = "dump-tree";
19 20
20 const char kFormat[] = "format"; 21 const char kFormat[] = "format";
21 const char kFormat_HelpShort[] = 22 const char kFormat_HelpShort[] =
22 "format: Format .gn file."; 23 "format: Format .gn file.";
23 const char kFormat_Help[] = 24 const char kFormat_Help[] =
24 "gn format: Format .gn file. (ALPHA, WILL CURRENTLY DESTROY DATA!)\n" 25 "gn format: Format .gn file. (ALPHA, WILL CURRENTLY DESTROY DATA!)\n"
25 "\n" 26 "\n"
26 " gn format //some/BUILD.gn\n" 27 " gn format //some/BUILD.gn\n"
27 " gn format some\\BUILD.gn\n" 28 " gn format some\\BUILD.gn\n"
28 "\n" 29 "\n"
29 " Formats .gn file to a standard format. THIS IS NOT FULLY IMPLEMENTED\n" 30 " 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 " 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 " At a minimum, make sure everything is `git commit`d so you can\n"
32 " `git checkout -f` to recover.\n"; 33 " `git checkout -f` to recover.\n";
33 34
34 namespace { 35 namespace {
35 36
36 const int kIndentSize = 2; 37 const int kIndentSize = 2;
38 const int kMaximumWidth = 80;
37 39
38 class Printer { 40 class Printer {
39 public: 41 public:
40 Printer(); 42 Printer();
41 ~Printer(); 43 ~Printer();
42 44
43 void Block(const ParseNode* file); 45 void Block(const ParseNode* file);
44 46
45 std::string String() const { return output_; } 47 std::string String() const { return output_; }
46 48
47 private: 49 private:
48 // Format a list of values using the given style. 50 // Format a list of values using the given style.
49 enum SequenceStyle { 51 enum SequenceStyle {
50 kSequenceStyleFunctionCall, 52 kSequenceStyleFunctionCall,
51 kSequenceStyleList, 53 kSequenceStyleList,
52 kSequenceStyleBlock, 54 kSequenceStyleBlock,
53 kSequenceStyleBracedBlock, 55 kSequenceStyleBracedBlock,
54 }; 56 };
55 57
56 enum ExprStyle { 58 enum ExprStyle {
57 kExprStyleRegular, 59 kExprStyleRegular,
58 kExprStyleComment, 60 kExprStyleComment,
59 }; 61 };
60 62
63 struct Metrics {
64 Metrics() : length(-1), multiline(false) {}
65 int length;
66 bool multiline;
67 };
68
61 // Add to output. 69 // Add to output.
62 void Print(base::StringPiece str); 70 void Print(base::StringPiece str);
63 71
64 // Add the current margin (as spaces) to the output. 72 // Add the current margin (as spaces) to the output.
65 void PrintMargin(); 73 void PrintMargin();
66 74
67 void TrimAndPrintToken(const Token& token); 75 void TrimAndPrintToken(const Token& token);
68 76
69 // End the current line, flushing end of line comments. 77 // End the current line, flushing end of line comments.
70 void Newline(); 78 void Newline();
71 79
72 // Remove trailing spaces from the current line. 80 // Remove trailing spaces from the current line.
73 void Trim(); 81 void Trim();
74 82
75 // Whether there's a blank separator line at the current position. 83 // Whether there's a blank separator line at the current position.
76 bool HaveBlankLine(); 84 bool HaveBlankLine();
77 85
78 // Get the 0-based x position on the current line. 86 // Get the 0-based x position on the current line.
79 int CurrentColumn(); 87 int CurrentColumn();
80 88
81 // Print the expression to the output buffer. Returns the type of element 89 // Print the expression to the output buffer. Returns the type of element
82 // added to the output. 90 // added to the output.
83 ExprStyle Expr(const ParseNode* root); 91 ExprStyle Expr(const ParseNode* root);
84 92
93 // Use a sub-Printer recursively to figure out the size that an expression
94 // would be before actually adding it to the output.
95 Metrics GetLengthOfExpr(const ParseNode* expr);
96
85 // Format a list of values using the given style. 97 // Format a list of values using the given style.
86 // |end| holds any trailing comments to be printed just before the closing 98 // |end| holds any trailing comments to be printed just before the closing
87 // bracket. 99 // bracket.
88 template <class PARSENODE> // Just for const covariance. 100 template <class PARSENODE> // Just for const covariance.
89 void Sequence(SequenceStyle style, 101 void Sequence(SequenceStyle style,
90 const std::vector<PARSENODE*>& list, 102 const std::vector<PARSENODE*>& list,
91 const ParseNode* end); 103 const ParseNode* end);
92 104
93 std::string output_; // Output buffer. 105 std::string output_; // Output buffer.
94 std::vector<Token> comments_; // Pending end-of-line comments. 106 std::vector<Token> comments_; // Pending end-of-line comments.
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after
199 } 211 }
200 212
201 if (block->comments()) { 213 if (block->comments()) {
202 for (const auto& c : block->comments()->after()) { 214 for (const auto& c : block->comments()->after()) {
203 TrimAndPrintToken(c); 215 TrimAndPrintToken(c);
204 Newline(); 216 Newline();
205 } 217 }
206 } 218 }
207 } 219 }
208 220
221 Printer::Metrics Printer::GetLengthOfExpr(const ParseNode* expr) {
222 Metrics result;
223 Printer sub;
224 sub.Expr(expr);
225 std::vector<std::string> lines;
226 base::SplitStringDontTrim(sub.String(), '\n', &lines);
227 result.multiline = lines.size() > 1;
228 for (const auto& line : lines)
229 result.length = std::max(result.length, static_cast<int>(line.size()));
230 return result;
231 }
232
209 Printer::ExprStyle Printer::Expr(const ParseNode* root) { 233 Printer::ExprStyle Printer::Expr(const ParseNode* root) {
210 ExprStyle result = kExprStyleRegular; 234 ExprStyle result = kExprStyleRegular;
211 if (root->comments()) { 235 if (root->comments()) {
212 if (!root->comments()->before().empty()) { 236 if (!root->comments()->before().empty()) {
213 Trim(); 237 Trim();
214 // If there's already other text on the line, start a new line. 238 // If there's already other text on the line, start a new line.
215 if (CurrentColumn() > 0) 239 if (CurrentColumn() > 0)
216 Print("\n"); 240 Print("\n");
217 // We're printing a line comment, so we need to be at the current margin. 241 // We're printing a line comment, so we need to be at the current margin.
218 PrintMargin(); 242 PrintMargin();
219 for (const auto& c : root->comments()->before()) { 243 for (const auto& c : root->comments()->before()) {
220 TrimAndPrintToken(c); 244 TrimAndPrintToken(c);
221 Newline(); 245 Newline();
222 } 246 }
223 } 247 }
224 } 248 }
225 249
226 if (root->AsAccessor()) { 250 if (const AccessorNode* accessor = root->AsAccessor()) {
227 Print("TODO(scottmg): AccessorNode"); 251 Print(accessor->base().value());
252 if (accessor->member()) {
253 Print(".");
254 Expr(accessor->member());
255 } else {
256 CHECK(accessor->index());
257 Print("[");
258 Expr(accessor->index());
259 Print("]");
260 }
228 } else if (const BinaryOpNode* binop = root->AsBinaryOp()) { 261 } else if (const BinaryOpNode* binop = root->AsBinaryOp()) {
229 // TODO(scottmg): Lots to do here for complex if expressions: reflowing, 262 // TODO(scottmg): Lots to do here for complex if expressions: reflowing,
230 // parenthesizing, etc. 263 // parenthesizing, etc.
231 Expr(binop->left()); 264 Metrics left = GetLengthOfExpr(binop->left());
232 Print(" "); 265 Metrics right = GetLengthOfExpr(binop->right());
233 Print(binop->op().value()); 266 int total_width =
234 Print(" "); 267 left.length + binop->op().value().size() + 2 + right.length;
235 Expr(binop->right()); 268 if (CurrentColumn() + total_width < kMaximumWidth ||
269 binop->right()->AsList()) {
270 // If it just fits normally, put it here.
271 Expr(binop->left());
272 Print(" ");
273 Print(binop->op().value());
274 Print(" ");
275 Expr(binop->right());
276 } else {
277 // Otherwise, put first argument and op, and indent next.
278 Expr(binop->left());
279 Print(" ");
280 Print(binop->op().value());
281 int old_margin = margin_;
282 margin_ += kIndentSize * 2;
283 Newline();
284 Expr(binop->right());
285 margin_ = old_margin;
286 }
236 } else if (const BlockNode* block = root->AsBlock()) { 287 } else if (const BlockNode* block = root->AsBlock()) {
237 Sequence(kSequenceStyleBracedBlock, block->statements(), block->End()); 288 Sequence(kSequenceStyleBracedBlock, block->statements(), block->End());
238 } else if (const ConditionNode* condition = root->AsConditionNode()) { 289 } else if (const ConditionNode* condition = root->AsConditionNode()) {
239 Print("if ("); 290 Print("if (");
240 Expr(condition->condition()); 291 Expr(condition->condition());
241 Print(") "); 292 Print(") ");
242 Sequence(kSequenceStyleBracedBlock, 293 Sequence(kSequenceStyleBracedBlock,
243 condition->if_true()->statements(), 294 condition->if_true()->statements(),
244 condition->if_true()->End()); 295 condition->if_true()->End());
245 if (condition->if_false()) { 296 if (condition->if_false()) {
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
292 std::back_inserter(comments_)); 343 std::back_inserter(comments_));
293 } 344 }
294 345
295 return result; 346 return result;
296 } 347 }
297 348
298 template <class PARSENODE> 349 template <class PARSENODE>
299 void Printer::Sequence(SequenceStyle style, 350 void Printer::Sequence(SequenceStyle style,
300 const std::vector<PARSENODE*>& list, 351 const std::vector<PARSENODE*>& list,
301 const ParseNode* end) { 352 const ParseNode* end) {
353 int old_margin = margin_;
354 int indent =
355 style == kSequenceStyleFunctionCall ? kIndentSize * 2 : kIndentSize;
302 bool force_multiline = false; 356 bool force_multiline = false;
303 if (style == kSequenceStyleFunctionCall) 357 if (style == kSequenceStyleFunctionCall)
304 Print("("); 358 Print("(");
305 else if (style == kSequenceStyleList) 359 else if (style == kSequenceStyleList)
306 Print("["); 360 Print("[");
307 else if (style == kSequenceStyleBracedBlock) 361 else if (style == kSequenceStyleBracedBlock)
308 Print("{"); 362 Print("{");
309 363
310 if (style == kSequenceStyleBlock || style == kSequenceStyleBracedBlock) 364 if (style == kSequenceStyleBlock || style == kSequenceStyleBracedBlock)
311 force_multiline = true; 365 force_multiline = true;
312 366
313 if (end && end->comments() && !end->comments()->before().empty()) 367 if (end && end->comments() && !end->comments()->before().empty())
314 force_multiline = true; 368 force_multiline = true;
315 369
316 // If there's before line comments, make sure we have a place to put them. 370 // If there's before line comments, make sure we have a place to put them.
317 for (const auto& i : list) { 371 for (const auto& i : list) {
318 if (i->comments() && !i->comments()->before().empty()) 372 if (i->comments() && !i->comments()->before().empty())
319 force_multiline = true; 373 force_multiline = true;
320 } 374 }
321 375
376 // Calculate the length of the items for function calls so we can decide to
377 // compress them in various nicer ways.
378 std::vector<int> natural_lengths;
379 bool fits_on_current_line = true;
380 int max_item_width = 0;
381 if (style == kSequenceStyleFunctionCall) {
382 int total_length = 0;
383 natural_lengths.reserve(list.size());
384 for (size_t i = 0; i < list.size(); ++i) {
385 Metrics sub = GetLengthOfExpr(list[i]);
386 if (sub.multiline)
387 fits_on_current_line = false;
388 natural_lengths.push_back(sub.length);
389 total_length += sub.length;
390 if (i < list.size() - 1)
391 total_length += 2; // ", "
392 }
393 // Strictly less than kMaximumWidth so there's room for closing ).
394 // TODO(scottmg): Need to know if there's an attached block for " {".
395 fits_on_current_line =
396 fits_on_current_line && CurrentColumn() + total_length < kMaximumWidth;
397 max_item_width =
398 *std::max_element(natural_lengths.begin(), natural_lengths.end());
399 }
400
322 if (list.size() == 0 && !force_multiline) { 401 if (list.size() == 0 && !force_multiline) {
323 // No elements, and not forcing newlines, print nothing. 402 // No elements, and not forcing newlines, print nothing.
324 } else if (list.size() == 1 && !force_multiline) { 403 } else if (list.size() == 1 && !force_multiline && fits_on_current_line) {
325 if (style != kSequenceStyleFunctionCall) 404 if (style != kSequenceStyleFunctionCall)
326 Print(" "); 405 Print(" ");
327 Expr(list[0]); 406 Expr(list[0]);
328 CHECK(!list[0]->comments() || list[0]->comments()->after().empty()); 407 CHECK(!list[0]->comments() || list[0]->comments()->after().empty());
329 if (style != kSequenceStyleFunctionCall) 408 if (style != kSequenceStyleFunctionCall)
330 Print(" "); 409 Print(" ");
331 } else { 410 } else {
332 margin_ += kIndentSize; 411 // Function calls get to be single line even with multiple arguments, if
333 size_t i = 0; 412 // they fit inside the maximum width.
334 for (const auto& x : list) { 413 if (style == kSequenceStyleFunctionCall && !force_multiline &&
335 Newline(); 414 fits_on_current_line) {
336 // If: 415 for (size_t i = 0; i < list.size(); ++i) {
337 // - we're going to output some comments, and; 416 Expr(list[i]);
338 // - we haven't just started this multiline list, and; 417 if (i < list.size() - 1)
339 // - there isn't already a blank line here; 418 Print(", ");
340 // Then: insert one. 419 }
341 if (i != 0 && x->comments() && !x->comments()->before().empty() && 420 } else {
342 !HaveBlankLine()) { 421 bool should_break_to_next_line = true;
422 if (style == kSequenceStyleFunctionCall &&
423 (CurrentColumn() + max_item_width < kMaximumWidth ||
424 CurrentColumn() < margin_ + indent)) {
425 should_break_to_next_line = false;
426 margin_ = CurrentColumn();
427 } else {
428 margin_ += indent;
429 }
430 size_t i = 0;
431 for (const auto& x : list) {
432 // Function calls where all the arguments would fit at the current
433 // position should do that instead of going back to margin+4.
434 if (i > 0 || should_break_to_next_line)
435 Newline();
436 // If:
437 // - we're going to output some comments, and;
438 // - we haven't just started this multiline list, and;
439 // - there isn't already a blank line here;
440 // Then: insert one.
441 if (i != 0 && x->comments() && !x->comments()->before().empty() &&
442 !HaveBlankLine()) {
443 Newline();
444 }
445 ExprStyle expr_style = Expr(x);
446 CHECK(!x->comments() || x->comments()->after().empty());
447 if (i < list.size() - 1 || style == kSequenceStyleList) {
448 if ((style == kSequenceStyleList ||
449 style == kSequenceStyleFunctionCall) &&
450 expr_style == kExprStyleRegular) {
451 Print(",");
452 } else {
453 Newline();
454 }
455 }
456 ++i;
457 }
458
459 // Trailing comments.
460 if (end->comments()) {
461 if (!list.empty())
462 Newline();
463 for (const auto& c : end->comments()->before()) {
464 Newline();
465 TrimAndPrintToken(c);
466 }
467 }
468
469 if (style == kSequenceStyleFunctionCall) {
470 if (end->comments() && !end->comments()->before().empty()) {
471 Newline();
472 }
473 } else {
474 margin_ = old_margin;
343 Newline(); 475 Newline();
344 } 476 }
345 ExprStyle expr_style = Expr(x);
346 CHECK(!x->comments() || x->comments()->after().empty());
347 if (i < list.size() - 1 || style == kSequenceStyleList) {
348 if ((style == kSequenceStyleList || kSequenceStyleFunctionCall) &&
349 expr_style == kExprStyleRegular) {
350 Print(",");
351 } else {
352 Newline();
353 }
354 }
355 ++i;
356 } 477 }
357
358 // Trailing comments.
359 if (end->comments()) {
360 if (!list.empty())
361 Newline();
362 for (const auto& c : end->comments()->before()) {
363 Newline();
364 TrimAndPrintToken(c);
365 }
366 }
367
368 margin_ -= kIndentSize;
369 Newline();
370 } 478 }
371 479
372 if (style == kSequenceStyleFunctionCall) 480 if (style == kSequenceStyleFunctionCall)
373 Print(")"); 481 Print(")");
374 else if (style == kSequenceStyleList) 482 else if (style == kSequenceStyleList)
375 Print("]"); 483 Print("]");
376 else if (style == kSequenceStyleBracedBlock) 484 else if (style == kSequenceStyleBracedBlock)
377 Print("}"); 485 Print("}");
486
487 margin_ = old_margin;
378 } 488 }
379 489
380 } // namespace 490 } // namespace
381 491
382 bool FormatFileToString(const std::string& input_filename, 492 bool FormatFileToString(const std::string& input_filename,
383 bool dump_tree, 493 bool dump_tree,
384 std::string* output) { 494 std::string* output) {
385 Setup setup; 495 Setup setup;
386 Err err; 496 Err err;
387 SourceFile input_file(input_filename); 497 SourceFile input_file(input_filename);
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
428 } 538 }
429 std::string output_string; 539 std::string output_string;
430 if (FormatFileToString(input_name, dump_tree, &output_string)) { 540 if (FormatFileToString(input_name, dump_tree, &output_string)) {
431 printf("%s", output_string.c_str()); 541 printf("%s", output_string.c_str());
432 } 542 }
433 543
434 return 0; 544 return 0;
435 } 545 }
436 546
437 } // namespace commands 547 } // namespace commands
OLDNEW
« no previous file with comments | « no previous file | tools/gn/command_format_unittest.cc » ('j') | tools/gn/format_test_data/022.golden » ('J')

Powered by Google App Engine
This is Rietveld 408576698