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.
7 #include "base/command_line.h"
8 #include "base/files/file_util.h"
9 #include "base/strings/string_split.h"
10 #include "tools/gn/commands.h"
11 #include "tools/gn/filesystem_utils.h"
12 #include "tools/gn/input_file.h"
13 #include "tools/gn/parser.h"
14 #include "tools/gn/scheduler.h"
15 #include "tools/gn/setup.h"
16 #include "tools/gn/source_file.h"
17 #include "tools/gn/tokenizer.h"
21 const char kSwitchDumpTree
[] = "dump-tree";
22 const char kSwitchInPlace
[] = "in-place";
23 const char kSwitchStdin
[] = "stdin";
25 const char kFormat
[] = "format";
26 const char kFormat_HelpShort
[] =
27 "format: Format .gn file. (ALPHA, WILL DESTROY DATA!)";
28 const char kFormat_Help
[] =
29 "gn format [--dump-tree] [--in-place] [--stdin] BUILD.gn\n"
31 " Formats .gn file to a standard format. THIS IS NOT FULLY IMPLEMENTED\n"
32 " YET! IT WILL EAT YOUR BEAUTIFUL .GN FILES. AND YOUR LAUNDRY.\n"
33 " At a minimum, make sure everything is `git commit`d so you can\n"
34 " `git checkout -f` to recover.\n"
38 " For debugging only, dumps the parse tree.\n"
41 " Instead writing the formatted file to stdout, replace the input\n"
42 " with the formatted output.\n"
45 " Read input from stdin (and write to stdout). Not compatible with\n"
46 " --in-place of course.\n"
49 " gn format //some/BUILD.gn\n"
50 " gn format some\\BUILD.gn\n"
51 " gn format /abspath/some/BUILD.gn\n"
52 " gn format --stdin\n";
56 const int kIndentSize
= 2;
57 const int kMaximumWidth
= 80;
75 void Block(const ParseNode
* file
);
77 std::string
String() const { return output_
; }
80 // Format a list of values using the given style.
84 kSequenceStyleBracedBlock
,
93 Metrics() : first_length(-1), longest_length(-1), multiline(false) {}
100 void Print(base::StringPiece str
);
102 // Add the current margin (as spaces) to the output.
105 void TrimAndPrintToken(const Token
& token
);
107 // End the current line, flushing end of line comments.
110 // Remove trailing spaces from the current line.
113 // Whether there's a blank separator line at the current position.
114 bool HaveBlankLine();
116 bool IsAssignment(const ParseNode
* node
);
118 // Heuristics to decide if there should be a blank line added between two
119 // items. For various "small" items, it doesn't look nice if there's too much
120 // vertical whitespace added.
121 bool ShouldAddBlankLineInBetween(const ParseNode
* a
, const ParseNode
* b
);
123 // Get the 0-based x position on the current line.
126 // Adds an opening ( if prec is less than the outers (to maintain evalution
127 // order for a subexpression). If an opening paren is emitted, *parenthesized
128 // will be set so it can be closed at the end of the expression.
129 void AddParen(int prec
, int outer_prec
, bool* parenthesized
);
131 // Print the expression to the output buffer. Returns the type of element
132 // added to the output. The value of outer_prec gives the precedence of the
133 // operator outside this Expr. If that operator binds tighter than root's,
134 // Expr must introduce parentheses.
135 ExprStyle
Expr(const ParseNode
* root
, int outer_prec
);
137 // Use a sub-Printer recursively to figure out the size that an expression
138 // would be before actually adding it to the output.
139 Metrics
GetLengthOfExpr(const ParseNode
* expr
, int outer_prec
);
141 // Format a list of values using the given style.
142 // |end| holds any trailing comments to be printed just before the closing
144 template <class PARSENODE
> // Just for const covariance.
145 void Sequence(SequenceStyle style
,
146 const std::vector
<PARSENODE
*>& list
,
147 const ParseNode
* end
);
149 void FunctionCall(const FunctionCallNode
* func_call
);
151 std::string output_
; // Output buffer.
152 std::vector
<Token
> comments_
; // Pending end-of-line comments.
153 int margin_
; // Left margin (number of spaces).
155 // Gives the precedence for operators in a BinaryOpNode.
156 std::map
<base::StringPiece
, Precedence
> precedence_
;
158 DISALLOW_COPY_AND_ASSIGN(Printer
);
161 Printer::Printer() : margin_(0) {
162 output_
.reserve(100 << 10);
163 precedence_
["="] = kPrecedenceAssign
;
164 precedence_
["+="] = kPrecedenceAssign
;
165 precedence_
["-="] = kPrecedenceAssign
;
166 precedence_
["||"] = kPrecedenceOr
;
167 precedence_
["&&"] = kPrecedenceAnd
;
168 precedence_
["<"] = kPrecedenceCompare
;
169 precedence_
[">"] = kPrecedenceCompare
;
170 precedence_
["=="] = kPrecedenceCompare
;
171 precedence_
["!="] = kPrecedenceCompare
;
172 precedence_
["<="] = kPrecedenceCompare
;
173 precedence_
[">="] = kPrecedenceCompare
;
174 precedence_
["+"] = kPrecedenceAdd
;
175 precedence_
["-"] = kPrecedenceAdd
;
176 precedence_
["!"] = kPrecedenceUnary
;
179 Printer::~Printer() {
182 void Printer::Print(base::StringPiece str
) {
183 str
.AppendToString(&output_
);
186 void Printer::PrintMargin() {
187 output_
+= std::string(margin_
, ' ');
190 void Printer::TrimAndPrintToken(const Token
& token
) {
192 TrimWhitespaceASCII(token
.value().as_string(), base::TRIM_ALL
, &trimmed
);
196 void Printer::Newline() {
197 if (!comments_
.empty()) {
200 // Save the margin, and temporarily set it to where the first comment
201 // starts so that multiple suffix comments are vertically aligned. This
202 // will need to be fancier once we enforce 80 col.
203 int old_margin
= margin_
;
204 for (const auto& c
: comments_
) {
206 margin_
= CurrentColumn();
212 TrimAndPrintToken(c
);
215 margin_
= old_margin
;
223 void Printer::Trim() {
224 size_t n
= output_
.size();
225 while (n
> 0 && output_
[n
- 1] == ' ')
230 bool Printer::HaveBlankLine() {
231 size_t n
= output_
.size();
232 while (n
> 0 && output_
[n
- 1] == ' ')
234 return n
> 2 && output_
[n
- 1] == '\n' && output_
[n
- 2] == '\n';
237 bool Printer::IsAssignment(const ParseNode
* node
) {
238 return node
->AsBinaryOp() && (node
->AsBinaryOp()->op().value() == "=" ||
239 node
->AsBinaryOp()->op().value() == "+=" ||
240 node
->AsBinaryOp()->op().value() == "-=");
243 bool Printer::ShouldAddBlankLineInBetween(const ParseNode
* a
,
244 const ParseNode
* b
) {
245 LocationRange a_range
= a
->GetRange();
246 LocationRange b_range
= b
->GetRange();
247 // If they're already separated by 1 or more lines, then we want to keep a
249 return b_range
.begin().line_number() > a_range
.end().line_number() + 1;
252 int Printer::CurrentColumn() {
254 while (n
< static_cast<int>(output_
.size()) &&
255 output_
[output_
.size() - 1 - n
] != '\n') {
261 void Printer::Block(const ParseNode
* root
) {
262 const BlockNode
* block
= root
->AsBlock();
264 if (block
->comments()) {
265 for (const auto& c
: block
->comments()->before()) {
266 TrimAndPrintToken(c
);
272 for (const auto& stmt
: block
->statements()) {
273 Expr(stmt
, kPrecedenceLowest
);
275 if (stmt
->comments()) {
276 // Why are before() not printed here too? before() are handled inside
277 // Expr(), as are suffix() which are queued to the next Newline().
278 // However, because it's a general expression handler, it doesn't insert
279 // the newline itself, which only happens between block statements. So,
280 // the after are handled explicitly here.
281 for (const auto& c
: stmt
->comments()->after()) {
282 TrimAndPrintToken(c
);
286 if (i
< block
->statements().size() - 1 &&
287 (ShouldAddBlankLineInBetween(block
->statements()[i
],
288 block
->statements()[i
+ 1]))) {
294 if (block
->comments()) {
295 for (const auto& c
: block
->comments()->after()) {
296 TrimAndPrintToken(c
);
302 Printer::Metrics
Printer::GetLengthOfExpr(const ParseNode
* expr
,
306 sub
.Expr(expr
, outer_prec
);
307 std::vector
<std::string
> lines
;
308 base::SplitStringDontTrim(sub
.String(), '\n', &lines
);
309 result
.multiline
= lines
.size() > 1;
310 result
.first_length
= static_cast<int>(lines
[0].size());
311 for (const auto& line
: lines
) {
312 result
.longest_length
=
313 std::max(result
.longest_length
, static_cast<int>(line
.size()));
318 void Printer::AddParen(int prec
, int outer_prec
, bool* parenthesized
) {
319 if (prec
< outer_prec
) {
321 *parenthesized
= true;
325 Printer::ExprStyle
Printer::Expr(const ParseNode
* root
, int outer_prec
) {
326 ExprStyle result
= kExprStyleRegular
;
327 if (root
->comments()) {
328 if (!root
->comments()->before().empty()) {
330 // If there's already other text on the line, start a new line.
331 if (CurrentColumn() > 0)
333 // We're printing a line comment, so we need to be at the current margin.
335 for (const auto& c
: root
->comments()->before()) {
336 TrimAndPrintToken(c
);
342 bool parenthesized
= false;
344 if (const AccessorNode
* accessor
= root
->AsAccessor()) {
345 AddParen(kPrecedenceSuffix
, outer_prec
, &parenthesized
);
346 Print(accessor
->base().value());
347 if (accessor
->member()) {
349 Expr(accessor
->member(), kPrecedenceLowest
);
351 CHECK(accessor
->index());
353 Expr(accessor
->index(), kPrecedenceLowest
);
356 } else if (const BinaryOpNode
* binop
= root
->AsBinaryOp()) {
357 CHECK(precedence_
.find(binop
->op().value()) != precedence_
.end());
358 Precedence prec
= precedence_
[binop
->op().value()];
359 AddParen(prec
, outer_prec
, &parenthesized
);
360 Metrics right
= GetLengthOfExpr(binop
->right(), prec
+ 1);
361 int op_length
= static_cast<int>(binop
->op().value().size()) + 2;
362 Expr(binop
->left(), prec
);
363 if (CurrentColumn() + op_length
+ right
.first_length
<= kMaximumWidth
) {
364 // If it just fits normally, put it here.
366 Print(binop
->op().value());
368 Expr(binop
->right(), prec
+ 1);
370 // Otherwise, put first argument and op, and indent next.
372 Print(binop
->op().value());
373 int old_margin
= margin_
;
374 margin_
+= kIndentSize
* 2;
376 Expr(binop
->right(), prec
+ 1);
377 margin_
= old_margin
;
379 } else if (const BlockNode
* block
= root
->AsBlock()) {
380 Sequence(kSequenceStyleBracedBlock
, block
->statements(), block
->End());
381 } else if (const ConditionNode
* condition
= root
->AsConditionNode()) {
383 Expr(condition
->condition(), kPrecedenceLowest
);
385 Sequence(kSequenceStyleBracedBlock
,
386 condition
->if_true()->statements(),
387 condition
->if_true()->End());
388 if (condition
->if_false()) {
390 // If it's a block it's a bare 'else', otherwise it's an 'else if'. See
391 // ConditionNode::Execute.
392 bool is_else_if
= condition
->if_false()->AsBlock() == NULL
;
394 Expr(condition
->if_false(), kPrecedenceLowest
);
396 Sequence(kSequenceStyleBracedBlock
,
397 condition
->if_false()->AsBlock()->statements(),
398 condition
->if_false()->AsBlock()->End());
401 } else if (const FunctionCallNode
* func_call
= root
->AsFunctionCall()) {
402 FunctionCall(func_call
);
403 } else if (const IdentifierNode
* identifier
= root
->AsIdentifier()) {
404 Print(identifier
->value().value());
405 } else if (const ListNode
* list
= root
->AsList()) {
406 Sequence(kSequenceStyleList
, list
->contents(), list
->End());
407 } else if (const LiteralNode
* literal
= root
->AsLiteral()) {
408 // TODO(scottmg): Quoting?
409 Print(literal
->value().value());
410 } else if (const UnaryOpNode
* unaryop
= root
->AsUnaryOp()) {
411 Print(unaryop
->op().value());
412 Expr(unaryop
->operand(), kPrecedenceUnary
);
413 } else if (const BlockCommentNode
* block_comment
= root
->AsBlockComment()) {
414 Print(block_comment
->comment().value());
415 result
= kExprStyleComment
;
416 } else if (const EndNode
* end
= root
->AsEnd()) {
417 Print(end
->value().value());
419 CHECK(false) << "Unhandled case in Expr.";
425 // Defer any end of line comment until we reach the newline.
426 if (root
->comments() && !root
->comments()->suffix().empty()) {
427 std::copy(root
->comments()->suffix().begin(),
428 root
->comments()->suffix().end(),
429 std::back_inserter(comments_
));
435 template <class PARSENODE
>
436 void Printer::Sequence(SequenceStyle style
,
437 const std::vector
<PARSENODE
*>& list
,
438 const ParseNode
* end
) {
439 bool force_multiline
= false;
440 if (style
== kSequenceStyleList
)
442 else if (style
== kSequenceStyleBracedBlock
)
445 if (style
== kSequenceStyleBlock
|| style
== kSequenceStyleBracedBlock
)
446 force_multiline
= true;
448 if (end
&& end
->comments() && !end
->comments()->before().empty())
449 force_multiline
= true;
451 // If there's before line comments, make sure we have a place to put them.
452 for (const auto& i
: list
) {
453 if (i
->comments() && !i
->comments()->before().empty())
454 force_multiline
= true;
457 if (list
.size() == 0 && !force_multiline
) {
458 // No elements, and not forcing newlines, print nothing.
459 } else if (list
.size() == 1 && !force_multiline
) {
461 Expr(list
[0], kPrecedenceLowest
);
462 CHECK(!list
[0]->comments() || list
[0]->comments()->after().empty());
465 margin_
+= kIndentSize
;
467 for (const auto& x
: list
) {
470 // - we're going to output some comments, and;
471 // - we haven't just started this multiline list, and;
472 // - there isn't already a blank line here;
474 if (i
!= 0 && x
->comments() && !x
->comments()->before().empty() &&
478 ExprStyle expr_style
= Expr(x
, kPrecedenceLowest
);
479 CHECK(!x
->comments() || x
->comments()->after().empty());
480 if (i
< list
.size() - 1 || style
== kSequenceStyleList
) {
481 if (style
== kSequenceStyleList
&& expr_style
== kExprStyleRegular
) {
484 if (i
< list
.size() - 1 &&
485 ShouldAddBlankLineInBetween(list
[i
], list
[i
+ 1]))
492 // Trailing comments.
493 if (end
->comments()) {
494 if (list
.size() >= 2)
496 for (const auto& c
: end
->comments()->before()) {
498 TrimAndPrintToken(c
);
502 margin_
-= kIndentSize
;
505 // Defer any end of line comment until we reach the newline.
506 if (end
->comments() && !end
->comments()->suffix().empty()) {
507 std::copy(end
->comments()->suffix().begin(),
508 end
->comments()->suffix().end(),
509 std::back_inserter(comments_
));
513 if (style
== kSequenceStyleList
)
515 else if (style
== kSequenceStyleBracedBlock
)
519 void Printer::FunctionCall(const FunctionCallNode
* func_call
) {
520 Print(func_call
->function().value());
523 int old_margin
= margin_
;
524 bool have_block
= func_call
->block() != nullptr;
525 bool force_multiline
= false;
527 const std::vector
<const ParseNode
*>& list
= func_call
->args()->contents();
528 const ParseNode
* end
= func_call
->args()->End();
530 if (end
&& end
->comments() && !end
->comments()->before().empty())
531 force_multiline
= true;
533 // If there's before line comments, make sure we have a place to put them.
534 for (const auto& i
: list
) {
535 if (i
->comments() && !i
->comments()->before().empty())
536 force_multiline
= true;
539 // Calculate the length of the items for function calls so we can decide to
540 // compress them in various nicer ways.
541 std::vector
<int> natural_lengths
;
542 bool fits_on_current_line
= true;
543 int max_item_width
= 0;
544 int total_length
= 0;
545 natural_lengths
.reserve(list
.size());
546 std::string terminator
= ")";
549 for (size_t i
= 0; i
< list
.size(); ++i
) {
550 Metrics sub
= GetLengthOfExpr(list
[i
], kPrecedenceLowest
);
552 fits_on_current_line
= false;
553 natural_lengths
.push_back(sub
.longest_length
);
554 total_length
+= sub
.longest_length
;
555 if (i
< list
.size() - 1) {
556 total_length
+= static_cast<int>(strlen(", "));
559 fits_on_current_line
=
560 fits_on_current_line
&&
561 CurrentColumn() + total_length
+ terminator
.size() <= kMaximumWidth
;
562 if (natural_lengths
.size() > 0) {
564 *std::max_element(natural_lengths
.begin(), natural_lengths
.end());
567 if (list
.size() == 0 && !force_multiline
) {
568 // No elements, and not forcing newlines, print nothing.
569 } else if (list
.size() == 1 && !force_multiline
&& fits_on_current_line
) {
570 Expr(list
[0], kPrecedenceLowest
);
571 CHECK(!list
[0]->comments() || list
[0]->comments()->after().empty());
573 // Function calls get to be single line even with multiple arguments, if
574 // they fit inside the maximum width.
575 if (!force_multiline
&& fits_on_current_line
) {
576 for (size_t i
= 0; i
< list
.size(); ++i
) {
577 Expr(list
[i
], kPrecedenceLowest
);
578 if (i
< list
.size() - 1)
582 bool should_break_to_next_line
= true;
583 int indent
= kIndentSize
* 2;
584 if (CurrentColumn() + max_item_width
+ terminator
.size() <=
586 CurrentColumn() < margin_
+ indent
) {
587 should_break_to_next_line
= false;
588 margin_
= CurrentColumn();
593 for (const auto& x
: list
) {
594 // Function calls where all the arguments would fit at the current
595 // position should do that instead of going back to margin+4.
596 if (i
> 0 || should_break_to_next_line
)
598 ExprStyle expr_style
= Expr(x
, kPrecedenceLowest
);
599 CHECK(!x
->comments() || x
->comments()->after().empty());
600 if (i
< list
.size() - 1) {
601 if (expr_style
== kExprStyleRegular
) {
610 // Trailing comments.
611 if (end
->comments()) {
614 for (const auto& c
: end
->comments()->before()) {
616 TrimAndPrintToken(c
);
618 if (!end
->comments()->before().empty())
624 // Defer any end of line comment until we reach the newline.
625 if (end
->comments() && !end
->comments()->suffix().empty()) {
626 std::copy(end
->comments()->suffix().begin(),
627 end
->comments()->suffix().end(),
628 std::back_inserter(comments_
));
632 margin_
= old_margin
;
636 Sequence(kSequenceStyleBracedBlock
,
637 func_call
->block()->statements(),
638 func_call
->block()->End());
642 void DoFormat(const ParseNode
* root
, bool dump_tree
, std::string
* output
) {
644 std::ostringstream os
;
646 printf("----------------------\n");
647 printf("-- PARSE TREE --------\n");
648 printf("----------------------\n");
649 printf("%s", os
.str().c_str());
650 printf("----------------------\n");
654 *output
= pr
.String();
657 std::string
ReadStdin() {
658 static const int kBufferSize
= 256;
659 char buffer
[kBufferSize
];
663 input
= fgets(buffer
, kBufferSize
, stdin
);
664 if (input
== NULL
&& feof(stdin
))
666 int length
= static_cast<int>(strlen(buffer
));
670 result
+= std::string(buffer
, length
);
676 bool FormatFileToString(Setup
* setup
,
677 const SourceFile
& file
,
679 std::string
* output
) {
681 const ParseNode
* parse_node
=
682 setup
->scheduler().input_file_manager()->SyncLoadFile(
683 LocationRange(), &setup
->build_settings(), file
, &err
);
684 if (err
.has_error()) {
688 DoFormat(parse_node
, dump_tree
, output
);
692 bool FormatStringToString(const std::string
& input
,
694 std::string
* output
) {
695 SourceFile source_file
;
696 InputFile
file(source_file
);
697 file
.SetContents(input
);
700 std::vector
<Token
> tokens
= Tokenizer::Tokenize(&file
, &err
);
701 if (err
.has_error()) {
707 scoped_ptr
<ParseNode
> parse_node
= Parser::Parse(tokens
, &err
);
708 if (err
.has_error()) {
713 DoFormat(parse_node
.get(), dump_tree
, output
);
717 int RunFormat(const std::vector
<std::string
>& args
) {
719 base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchDumpTree
);
722 base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchStdin
);
725 if (args
.size() != 0) {
726 Err(Location(), "Expecting no arguments when reading from stdin.\n")
730 std::string input
= ReadStdin();
732 if (!FormatStringToString(input
, dump_tree
, &output
))
734 printf("%s", output
.c_str());
738 // TODO(scottmg): Eventually, this should be a list/spec of files, and they
739 // should all be done in parallel.
740 if (args
.size() != 1) {
741 Err(Location(), "Expecting exactly one argument, see `gn help format`.\n")
747 SourceDir source_dir
=
748 SourceDirForCurrentDirectory(setup
.build_settings().root_path());
749 SourceFile file
= source_dir
.ResolveRelativeFile(args
[0],
750 setup
.build_settings().root_path_utf8());
752 std::string output_string
;
753 if (FormatFileToString(&setup
, file
, dump_tree
, &output_string
)) {
755 base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchInPlace
);
757 base::FilePath to_write
= setup
.build_settings().GetFullPath(file
);
758 if (base::WriteFile(to_write
,
759 output_string
.data(),
760 static_cast<int>(output_string
.size())) == -1) {
762 std::string("Failed to write formatted output back to \"") +
763 to_write
.AsUTF8Unsafe() + std::string("\".")).PrintToStdout();
766 printf("Wrote formatted to '%s'.\n", to_write
.AsUTF8Unsafe().c_str());
768 printf("%s", output_string
.c_str());
775 } // namespace commands