Add ICU message format support
[chromium-blink-merge.git] / tools / gn / command_format.cc
bloba8143ffb8eb24585c35a8481638bb1ae39dfe109
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.
5 #include <sstream>
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"
19 namespace commands {
21 const char kSwitchDryRun[] = "dry-run";
22 const char kSwitchDumpTree[] = "dump-tree";
23 const char kSwitchInPlace[] = "in-place";
24 const char kSwitchStdin[] = "stdin";
26 const char kFormat[] = "format";
27 const char kFormat_HelpShort[] =
28 "format: Format .gn file.";
29 const char kFormat_Help[] =
30 "gn format [--dump-tree] [--in-place] [--stdin] BUILD.gn\n"
31 "\n"
32 " Formats .gn file to a standard format.\n"
33 "\n"
34 "Arguments\n"
35 " --dry-run\n"
36 " Does not change or output anything, but sets the process exit code\n"
37 " based on whether output would be different than what's on disk.\n"
38 " This is useful for presubmit/lint-type checks.\n"
39 " - Exit code 0: successful format, matches on disk.\n"
40 " - Exit code 1: general failure (parse error, etc.)\n"
41 " - Exit code 2: successful format, but differs from on disk.\n"
42 "\n"
43 " --dump-tree\n"
44 " For debugging only, dumps the parse tree.\n"
45 "\n"
46 " --in-place\n"
47 " Instead of writing the formatted file to stdout, replace the input\n"
48 " file with the formatted output. If no reformatting is required,\n"
49 " the input file will not be touched, and nothing printed.\n"
50 "\n"
51 " --stdin\n"
52 " Read input from stdin (and write to stdout). Not compatible with\n"
53 " --in-place of course.\n"
54 "\n"
55 "Examples\n"
56 " gn format //some/BUILD.gn\n"
57 " gn format some\\BUILD.gn\n"
58 " gn format /abspath/some/BUILD.gn\n"
59 " gn format --stdin\n";
61 namespace {
63 const int kIndentSize = 2;
64 const int kMaximumWidth = 80;
66 const int kPenaltyLineBreak = 500;
67 const int kPenaltyHorizontalSeparation = 100;
68 const int kPenaltyExcess = 10000;
69 const int kPenaltyBrokenLineOnOneLiner = 5000;
71 enum Precedence {
72 kPrecedenceLowest,
73 kPrecedenceAssign,
74 kPrecedenceOr,
75 kPrecedenceAnd,
76 kPrecedenceCompare,
77 kPrecedenceAdd,
78 kPrecedenceUnary,
79 kPrecedenceSuffix,
82 int CountLines(const std::string& str) {
83 std::vector<std::string> lines;
84 base::SplitStringDontTrim(str, '\n', &lines);
85 return static_cast<int>(lines.size());
88 class Printer {
89 public:
90 Printer();
91 ~Printer();
93 void Block(const ParseNode* file);
95 std::string String() const { return output_; }
97 private:
98 // Format a list of values using the given style.
99 enum SequenceStyle {
100 kSequenceStyleList,
101 kSequenceStyleBlock,
102 kSequenceStyleBracedBlock,
105 struct Metrics {
106 Metrics() : first_length(-1), longest_length(-1), multiline(false) {}
107 int first_length;
108 int longest_length;
109 bool multiline;
112 // Add to output.
113 void Print(base::StringPiece str);
115 // Add the current margin (as spaces) to the output.
116 void PrintMargin();
118 void TrimAndPrintToken(const Token& token);
120 // End the current line, flushing end of line comments.
121 void Newline();
123 // Remove trailing spaces from the current line.
124 void Trim();
126 // Whether there's a blank separator line at the current position.
127 bool HaveBlankLine();
129 // Flag assignments to sources, deps, etc. to make their RHSs multiline.
130 void AnnotatePreferredMultilineAssignment(const BinaryOpNode* binop);
132 // Alphabetically a list on the RHS if the LHS is 'sources'.
133 void SortIfSources(const BinaryOpNode* binop);
135 // Heuristics to decide if there should be a blank line added between two
136 // items. For various "small" items, it doesn't look nice if there's too much
137 // vertical whitespace added.
138 bool ShouldAddBlankLineInBetween(const ParseNode* a, const ParseNode* b);
140 // Get the 0-based x position on the current line.
141 int CurrentColumn() const;
143 // Get the current line in the output;
144 int CurrentLine() const;
146 // Adds an opening ( if prec is less than the outers (to maintain evalution
147 // order for a subexpression). If an opening paren is emitted, *parenthesized
148 // will be set so it can be closed at the end of the expression.
149 void AddParen(int prec, int outer_prec, bool* parenthesized);
151 // Print the expression to the output buffer. Returns the type of element
152 // added to the output. The value of outer_prec gives the precedence of the
153 // operator outside this Expr. If that operator binds tighter than root's,
154 // Expr must introduce parentheses.
155 int Expr(const ParseNode* root, int outer_prec, const std::string& suffix);
157 // Generic penalties for exceeding maximum width, adding more lines, etc.
158 int AssessPenalty(const std::string& output);
160 // Tests if any lines exceed the maximum width.
161 bool ExceedsMaximumWidth(const std::string& output);
163 // Format a list of values using the given style.
164 // |end| holds any trailing comments to be printed just before the closing
165 // bracket.
166 template <class PARSENODE> // Just for const covariance.
167 void Sequence(SequenceStyle style,
168 const std::vector<PARSENODE*>& list,
169 const ParseNode* end,
170 bool force_multiline);
172 // Returns the penalty.
173 int FunctionCall(const FunctionCallNode* func_call,
174 const std::string& suffix);
176 // Create a clone of this Printer in a similar state (other than the output,
177 // but including margins, etc.) to be used for dry run measurements.
178 void InitializeSub(Printer* sub);
180 template <class PARSENODE>
181 bool ListWillBeMultiline(const std::vector<PARSENODE*>& list,
182 const ParseNode* end);
184 std::string output_; // Output buffer.
185 std::vector<Token> comments_; // Pending end-of-line comments.
186 int margin() const { return stack_.back().margin; }
188 int penalty_depth_;
189 int GetPenaltyForLineBreak() const {
190 return penalty_depth_ * kPenaltyLineBreak;
193 struct IndentState {
194 IndentState()
195 : margin(0),
196 continuation_requires_indent(false),
197 parent_is_boolean_or(false) {}
198 IndentState(int margin,
199 bool continuation_requires_indent,
200 bool parent_is_boolean_or)
201 : margin(margin),
202 continuation_requires_indent(continuation_requires_indent),
203 parent_is_boolean_or(parent_is_boolean_or) {}
205 // The left margin (number of spaces).
206 int margin;
208 bool continuation_requires_indent;
210 bool parent_is_boolean_or;
212 // Stack used to track
213 std::vector<IndentState> stack_;
215 // Gives the precedence for operators in a BinaryOpNode.
216 std::map<base::StringPiece, Precedence> precedence_;
218 DISALLOW_COPY_AND_ASSIGN(Printer);
221 Printer::Printer() : penalty_depth_(0) {
222 output_.reserve(100 << 10);
223 precedence_["="] = kPrecedenceAssign;
224 precedence_["+="] = kPrecedenceAssign;
225 precedence_["-="] = kPrecedenceAssign;
226 precedence_["||"] = kPrecedenceOr;
227 precedence_["&&"] = kPrecedenceAnd;
228 precedence_["<"] = kPrecedenceCompare;
229 precedence_[">"] = kPrecedenceCompare;
230 precedence_["=="] = kPrecedenceCompare;
231 precedence_["!="] = kPrecedenceCompare;
232 precedence_["<="] = kPrecedenceCompare;
233 precedence_[">="] = kPrecedenceCompare;
234 precedence_["+"] = kPrecedenceAdd;
235 precedence_["-"] = kPrecedenceAdd;
236 precedence_["!"] = kPrecedenceUnary;
237 stack_.push_back(IndentState());
240 Printer::~Printer() {
243 void Printer::Print(base::StringPiece str) {
244 str.AppendToString(&output_);
247 void Printer::PrintMargin() {
248 output_ += std::string(margin(), ' ');
251 void Printer::TrimAndPrintToken(const Token& token) {
252 std::string trimmed;
253 TrimWhitespaceASCII(token.value().as_string(), base::TRIM_ALL, &trimmed);
254 Print(trimmed);
257 void Printer::Newline() {
258 if (!comments_.empty()) {
259 Print(" ");
260 // Save the margin, and temporarily set it to where the first comment
261 // starts so that multiple suffix comments are vertically aligned. This
262 // will need to be fancier once we enforce 80 col.
263 stack_.push_back(IndentState(CurrentColumn(), false, false));
264 int i = 0;
265 for (const auto& c : comments_) {
266 if (i > 0) {
267 Trim();
268 Print("\n");
269 PrintMargin();
271 TrimAndPrintToken(c);
272 ++i;
274 stack_.pop_back();
275 comments_.clear();
277 Trim();
278 Print("\n");
279 PrintMargin();
282 void Printer::Trim() {
283 size_t n = output_.size();
284 while (n > 0 && output_[n - 1] == ' ')
285 --n;
286 output_.resize(n);
289 bool Printer::HaveBlankLine() {
290 size_t n = output_.size();
291 while (n > 0 && output_[n - 1] == ' ')
292 --n;
293 return n > 2 && output_[n - 1] == '\n' && output_[n - 2] == '\n';
296 void Printer::AnnotatePreferredMultilineAssignment(const BinaryOpNode* binop) {
297 const IdentifierNode* ident = binop->left()->AsIdentifier();
298 const ListNode* list = binop->right()->AsList();
299 // This is somewhat arbitrary, but we include the 'deps'- and 'sources'-like
300 // things, but not flags things.
301 if (binop->op().value() == "=" && ident && list) {
302 const base::StringPiece lhs = ident->value().value();
303 if (lhs == "data" || lhs == "datadeps" || lhs == "deps" ||
304 lhs == "inputs" || lhs == "outputs" || lhs == "public" ||
305 lhs == "public_deps" || lhs == "sources") {
306 const_cast<ListNode*>(list)->set_prefer_multiline(true);
311 void Printer::SortIfSources(const BinaryOpNode* binop) {
312 const IdentifierNode* ident = binop->left()->AsIdentifier();
313 const ListNode* list = binop->right()->AsList();
314 // TODO(scottmg): Sort more than 'sources'?
315 if ((binop->op().value() == "=" || binop->op().value() == "+=" ||
316 binop->op().value() == "-=") &&
317 ident && list) {
318 const base::StringPiece lhs = ident->value().value();
319 if (lhs == "sources")
320 const_cast<ListNode*>(list)->SortAsStringsList();
325 bool Printer::ShouldAddBlankLineInBetween(const ParseNode* a,
326 const ParseNode* b) {
327 LocationRange a_range = a->GetRange();
328 LocationRange b_range = b->GetRange();
329 // If they're already separated by 1 or more lines, then we want to keep a
330 // blank line.
331 return (b_range.begin().line_number() > a_range.end().line_number() + 1) ||
332 // Always put a blank line before a block comment.
333 b->AsBlockComment();
336 int Printer::CurrentColumn() const {
337 int n = 0;
338 while (n < static_cast<int>(output_.size()) &&
339 output_[output_.size() - 1 - n] != '\n') {
340 ++n;
342 return n;
345 int Printer::CurrentLine() const {
346 int count = 1;
347 for (const char* p = output_.c_str(); (p = strchr(p, '\n')) != nullptr;) {
348 ++count;
349 ++p;
351 return count;
354 void Printer::Block(const ParseNode* root) {
355 const BlockNode* block = root->AsBlock();
357 if (block->comments()) {
358 for (const auto& c : block->comments()->before()) {
359 TrimAndPrintToken(c);
360 Newline();
364 size_t i = 0;
365 for (const auto& stmt : block->statements()) {
366 Expr(stmt, kPrecedenceLowest, std::string());
367 Newline();
368 if (stmt->comments()) {
369 // Why are before() not printed here too? before() are handled inside
370 // Expr(), as are suffix() which are queued to the next Newline().
371 // However, because it's a general expression handler, it doesn't insert
372 // the newline itself, which only happens between block statements. So,
373 // the after are handled explicitly here.
374 for (const auto& c : stmt->comments()->after()) {
375 TrimAndPrintToken(c);
376 Newline();
379 if (i < block->statements().size() - 1 &&
380 (ShouldAddBlankLineInBetween(block->statements()[i],
381 block->statements()[i + 1]))) {
382 Newline();
384 ++i;
387 if (block->comments()) {
388 for (const auto& c : block->comments()->after()) {
389 TrimAndPrintToken(c);
390 Newline();
395 int Printer::AssessPenalty(const std::string& output) {
396 int penalty = 0;
397 std::vector<std::string> lines;
398 base::SplitStringDontTrim(output, '\n', &lines);
399 penalty += static_cast<int>(lines.size() - 1) * GetPenaltyForLineBreak();
400 for (const auto& line : lines) {
401 if (line.size() > kMaximumWidth)
402 penalty += static_cast<int>(line.size() - kMaximumWidth) * kPenaltyExcess;
404 return penalty;
407 bool Printer::ExceedsMaximumWidth(const std::string& output) {
408 std::vector<std::string> lines;
409 base::SplitStringDontTrim(output, '\n', &lines);
410 for (const auto& line : lines) {
411 if (line.size() > kMaximumWidth)
412 return true;
414 return false;
417 void Printer::AddParen(int prec, int outer_prec, bool* parenthesized) {
418 if (prec < outer_prec) {
419 Print("(");
420 *parenthesized = true;
424 int Printer::Expr(const ParseNode* root,
425 int outer_prec,
426 const std::string& suffix) {
427 std::string at_end = suffix;
428 int penalty = 0;
429 penalty_depth_++;
431 if (root->comments()) {
432 if (!root->comments()->before().empty()) {
433 Trim();
434 // If there's already other text on the line, start a new line.
435 if (CurrentColumn() > 0)
436 Print("\n");
437 // We're printing a line comment, so we need to be at the current margin.
438 PrintMargin();
439 for (const auto& c : root->comments()->before()) {
440 TrimAndPrintToken(c);
441 Newline();
446 bool parenthesized = false;
448 if (const AccessorNode* accessor = root->AsAccessor()) {
449 AddParen(kPrecedenceSuffix, outer_prec, &parenthesized);
450 Print(accessor->base().value());
451 if (accessor->member()) {
452 Print(".");
453 Expr(accessor->member(), kPrecedenceLowest, std::string());
454 } else {
455 CHECK(accessor->index());
456 Print("[");
457 Expr(accessor->index(), kPrecedenceLowest, "]");
459 } else if (const BinaryOpNode* binop = root->AsBinaryOp()) {
460 CHECK(precedence_.find(binop->op().value()) != precedence_.end());
461 AnnotatePreferredMultilineAssignment(binop);
463 SortIfSources(binop);
465 Precedence prec = precedence_[binop->op().value()];
467 // Since binary operators format left-to-right, it is ok for the left side
468 // use the same operator without parentheses, so the left uses prec. For the
469 // same reason, the right side cannot reuse the same operator, or else "x +
470 // (y + z)" would format as "x + y + z" which means "(x + y) + z". So, treat
471 // the right expression as appearing one precedence level higher.
472 // However, because the source parens are not in the parse tree, as a
473 // special case for && and || we insert strictly-redundant-but-helpful-for-
474 // human-readers parentheses.
475 int prec_left = prec;
476 int prec_right = prec + 1;
477 if (binop->op().value() == "&&" && stack_.back().parent_is_boolean_or) {
478 Print("(");
479 parenthesized = true;
480 } else {
481 AddParen(prec_left, outer_prec, &parenthesized);
484 int start_line = CurrentLine();
485 int start_column = CurrentColumn();
486 bool is_assignment = binop->op().value() == "=" ||
487 binop->op().value() == "+=" ||
488 binop->op().value() == "-=";
489 // A sort of funny special case for the long lists that are common in .gn
490 // files, don't indent them + 4, even though they're just continuations when
491 // they're simple lists like "x = [ a, b, c, ... ]"
492 const ListNode* right_as_list = binop->right()->AsList();
493 int indent_column =
494 (is_assignment &&
495 (!right_as_list || (!right_as_list->prefer_multiline() &&
496 !ListWillBeMultiline(right_as_list->contents(),
497 right_as_list->End()))))
498 ? margin() + kIndentSize * 2
499 : start_column;
500 if (stack_.back().continuation_requires_indent)
501 indent_column += kIndentSize * 2;
503 stack_.push_back(IndentState(indent_column,
504 stack_.back().continuation_requires_indent,
505 binop->op().value() == "||"));
506 Printer sub_left;
507 InitializeSub(&sub_left);
508 sub_left.Expr(binop->left(),
509 prec_left,
510 std::string(" ") + binop->op().value().as_string());
511 bool left_is_multiline = CountLines(sub_left.String()) > 1;
512 // Avoid walking the whole left redundantly times (see timing of Format.046)
513 // so pull the output and comments from subprinter.
514 Print(sub_left.String().substr(start_column));
515 std::copy(sub_left.comments_.begin(),
516 sub_left.comments_.end(),
517 std::back_inserter(comments_));
519 // Single line.
520 Printer sub1;
521 InitializeSub(&sub1);
522 sub1.Print(" ");
523 int penalty_current_line =
524 sub1.Expr(binop->right(), prec_right, std::string());
525 sub1.Print(suffix);
526 penalty_current_line += AssessPenalty(sub1.String());
527 if (!is_assignment && left_is_multiline) {
528 // In e.g. xxx + yyy, if xxx is already multiline, then we want a penalty
529 // for trying to continue as if this were one line.
530 penalty_current_line +=
531 (CountLines(sub1.String()) - 1) * kPenaltyBrokenLineOnOneLiner;
534 // Break after operator.
535 Printer sub2;
536 InitializeSub(&sub2);
537 sub2.Newline();
538 int penalty_next_line =
539 sub2.Expr(binop->right(), prec_right, std::string());
540 sub2.Print(suffix);
541 penalty_next_line += AssessPenalty(sub2.String());
543 // If in both cases it was forced past 80col, then we don't break to avoid
544 // breaking after '=' in the case of:
545 // variable = "... very long string ..."
546 // as breaking and indenting doesn't make things much more readable, even
547 // though there's less characters past the maximum width.
548 bool exceeds_maximum_either_way = ExceedsMaximumWidth(sub1.String()) &&
549 ExceedsMaximumWidth(sub2.String());
551 if (penalty_current_line < penalty_next_line ||
552 exceeds_maximum_either_way) {
553 Print(" ");
554 Expr(binop->right(), prec_right, std::string());
555 } else {
556 // Otherwise, put first argument and op, and indent next.
557 Newline();
558 penalty += std::abs(CurrentColumn() - start_column) *
559 kPenaltyHorizontalSeparation;
560 Expr(binop->right(), prec_right, std::string());
562 stack_.pop_back();
563 penalty += (CurrentLine() - start_line) * GetPenaltyForLineBreak();
564 } else if (const BlockNode* block = root->AsBlock()) {
565 Sequence(
566 kSequenceStyleBracedBlock, block->statements(), block->End(), false);
567 } else if (const ConditionNode* condition = root->AsConditionNode()) {
568 Print("if (");
569 // TODO(scottmg): The { needs to be included in the suffix here.
570 Expr(condition->condition(), kPrecedenceLowest, ") ");
571 Sequence(kSequenceStyleBracedBlock,
572 condition->if_true()->statements(),
573 condition->if_true()->End(),
574 false);
575 if (condition->if_false()) {
576 Print(" else ");
577 // If it's a block it's a bare 'else', otherwise it's an 'else if'. See
578 // ConditionNode::Execute.
579 bool is_else_if = condition->if_false()->AsBlock() == nullptr;
580 if (is_else_if) {
581 Expr(condition->if_false(), kPrecedenceLowest, std::string());
582 } else {
583 Sequence(kSequenceStyleBracedBlock,
584 condition->if_false()->AsBlock()->statements(),
585 condition->if_false()->AsBlock()->End(),
586 false);
589 } else if (const FunctionCallNode* func_call = root->AsFunctionCall()) {
590 penalty += FunctionCall(func_call, at_end);
591 at_end = "";
592 } else if (const IdentifierNode* identifier = root->AsIdentifier()) {
593 Print(identifier->value().value());
594 } else if (const ListNode* list = root->AsList()) {
595 bool force_multiline =
596 list->prefer_multiline() && !list->contents().empty();
597 Sequence(
598 kSequenceStyleList, list->contents(), list->End(), force_multiline);
599 } else if (const LiteralNode* literal = root->AsLiteral()) {
600 Print(literal->value().value());
601 } else if (const UnaryOpNode* unaryop = root->AsUnaryOp()) {
602 Print(unaryop->op().value());
603 Expr(unaryop->operand(), kPrecedenceUnary, std::string());
604 } else if (const BlockCommentNode* block_comment = root->AsBlockComment()) {
605 Print(block_comment->comment().value());
606 } else if (const EndNode* end = root->AsEnd()) {
607 Print(end->value().value());
608 } else {
609 CHECK(false) << "Unhandled case in Expr.";
612 if (parenthesized)
613 Print(")");
615 // Defer any end of line comment until we reach the newline.
616 if (root->comments() && !root->comments()->suffix().empty()) {
617 std::copy(root->comments()->suffix().begin(),
618 root->comments()->suffix().end(),
619 std::back_inserter(comments_));
622 Print(at_end);
624 penalty_depth_--;
625 return penalty;
628 template <class PARSENODE>
629 void Printer::Sequence(SequenceStyle style,
630 const std::vector<PARSENODE*>& list,
631 const ParseNode* end,
632 bool force_multiline) {
633 if (style == kSequenceStyleList)
634 Print("[");
635 else if (style == kSequenceStyleBracedBlock)
636 Print("{");
638 if (style == kSequenceStyleBlock || style == kSequenceStyleBracedBlock)
639 force_multiline = true;
641 force_multiline |= ListWillBeMultiline(list, end);
643 if (list.size() == 0 && !force_multiline) {
644 // No elements, and not forcing newlines, print nothing.
645 } else if (list.size() == 1 && !force_multiline) {
646 Print(" ");
647 Expr(list[0], kPrecedenceLowest, std::string());
648 CHECK(!list[0]->comments() || list[0]->comments()->after().empty());
649 Print(" ");
650 } else {
651 stack_.push_back(IndentState(margin() + kIndentSize,
652 style == kSequenceStyleList,
653 false));
654 size_t i = 0;
655 for (const auto& x : list) {
656 Newline();
657 // If:
658 // - we're going to output some comments, and;
659 // - we haven't just started this multiline list, and;
660 // - there isn't already a blank line here;
661 // Then: insert one.
662 if (i != 0 && x->comments() && !x->comments()->before().empty() &&
663 !HaveBlankLine()) {
664 Newline();
666 bool body_of_list = i < list.size() - 1 || style == kSequenceStyleList;
667 bool want_comma =
668 body_of_list && (style == kSequenceStyleList && !x->AsBlockComment());
669 Expr(x, kPrecedenceLowest, want_comma ? "," : std::string());
670 CHECK(!x->comments() || x->comments()->after().empty());
671 if (body_of_list) {
672 if (i < list.size() - 1 &&
673 ShouldAddBlankLineInBetween(list[i], list[i + 1]))
674 Newline();
676 ++i;
679 // Trailing comments.
680 if (end->comments() && !end->comments()->before().empty()) {
681 if (list.size() >= 2)
682 Newline();
683 for (const auto& c : end->comments()->before()) {
684 Newline();
685 TrimAndPrintToken(c);
689 stack_.pop_back();
690 Newline();
692 // Defer any end of line comment until we reach the newline.
693 if (end->comments() && !end->comments()->suffix().empty()) {
694 std::copy(end->comments()->suffix().begin(),
695 end->comments()->suffix().end(),
696 std::back_inserter(comments_));
700 if (style == kSequenceStyleList)
701 Print("]");
702 else if (style == kSequenceStyleBracedBlock)
703 Print("}");
706 int Printer::FunctionCall(const FunctionCallNode* func_call,
707 const std::string& suffix) {
708 int start_line = CurrentLine();
709 int start_column = CurrentColumn();
710 Print(func_call->function().value());
711 Print("(");
713 bool have_block = func_call->block() != nullptr;
714 bool force_multiline = false;
716 const std::vector<const ParseNode*>& list = func_call->args()->contents();
717 const ParseNode* end = func_call->args()->End();
719 if (end && end->comments() && !end->comments()->before().empty())
720 force_multiline = true;
722 // If there's before line comments, make sure we have a place to put them.
723 for (const auto& i : list) {
724 if (i->comments() && !i->comments()->before().empty())
725 force_multiline = true;
728 // Calculate the penalties for 3 possible layouts:
729 // 1. all on same line;
730 // 2. starting on same line, broken at each comma but paren aligned;
731 // 3. broken to next line + 4, broken at each comma.
732 std::string terminator = ")";
733 if (have_block)
734 terminator += " {";
735 terminator += suffix;
737 // Special case to make function calls of one arg taking a long list of
738 // boolean operators not indent.
739 bool continuation_requires_indent =
740 list.size() != 1 || !list[0]->AsBinaryOp();
742 // 1: Same line.
743 Printer sub1;
744 InitializeSub(&sub1);
745 sub1.stack_.push_back(
746 IndentState(CurrentColumn(), continuation_requires_indent, false));
747 int penalty_one_line = 0;
748 for (size_t i = 0; i < list.size(); ++i) {
749 penalty_one_line += sub1.Expr(list[i], kPrecedenceLowest,
750 i < list.size() - 1 ? ", " : std::string());
752 sub1.Print(terminator);
753 penalty_one_line += AssessPenalty(sub1.String());
754 // This extra penalty prevents a short second argument from being squeezed in
755 // after a first argument that went multiline (and instead preferring a
756 // variant below).
757 penalty_one_line +=
758 (CountLines(sub1.String()) - 1) * kPenaltyBrokenLineOnOneLiner;
760 // 2: Starting on same line, broken at commas.
761 Printer sub2;
762 InitializeSub(&sub2);
763 sub2.stack_.push_back(
764 IndentState(CurrentColumn(), continuation_requires_indent, false));
765 int penalty_multiline_start_same_line = 0;
766 for (size_t i = 0; i < list.size(); ++i) {
767 penalty_multiline_start_same_line += sub2.Expr(
768 list[i], kPrecedenceLowest, i < list.size() - 1 ? "," : std::string());
769 if (i < list.size() - 1) {
770 sub2.Newline();
773 sub2.Print(terminator);
774 penalty_multiline_start_same_line += AssessPenalty(sub2.String());
776 // 3: Starting on next line, broken at commas.
777 Printer sub3;
778 InitializeSub(&sub3);
779 sub3.stack_.push_back(IndentState(margin() + kIndentSize * 2,
780 continuation_requires_indent, false));
781 sub3.Newline();
782 int penalty_multiline_start_next_line = 0;
783 for (size_t i = 0; i < list.size(); ++i) {
784 if (i == 0) {
785 penalty_multiline_start_next_line +=
786 std::abs(sub3.CurrentColumn() - start_column) *
787 kPenaltyHorizontalSeparation;
789 penalty_multiline_start_next_line += sub3.Expr(
790 list[i], kPrecedenceLowest, i < list.size() - 1 ? "," : std::string());
791 if (i < list.size() - 1) {
792 sub3.Newline();
795 sub3.Print(terminator);
796 penalty_multiline_start_next_line += AssessPenalty(sub3.String());
798 int penalty = penalty_multiline_start_next_line;
799 bool fits_on_current_line = false;
800 if (penalty_one_line < penalty_multiline_start_next_line ||
801 penalty_multiline_start_same_line < penalty_multiline_start_next_line) {
802 fits_on_current_line = true;
803 penalty = penalty_one_line;
804 if (penalty_multiline_start_same_line < penalty_one_line) {
805 penalty = penalty_multiline_start_same_line;
806 force_multiline = true;
808 } else {
809 force_multiline = true;
812 if (list.size() == 0 && !force_multiline) {
813 // No elements, and not forcing newlines, print nothing.
814 } else {
815 if (penalty_multiline_start_next_line < penalty_multiline_start_same_line) {
816 stack_.push_back(IndentState(margin() + kIndentSize * 2,
817 continuation_requires_indent,
818 false));
819 Newline();
820 } else {
821 stack_.push_back(
822 IndentState(CurrentColumn(), continuation_requires_indent, false));
825 for (size_t i = 0; i < list.size(); ++i) {
826 const auto& x = list[i];
827 if (i > 0) {
828 if (fits_on_current_line && !force_multiline)
829 Print(" ");
830 else
831 Newline();
833 bool want_comma = i < list.size() - 1 && !x->AsBlockComment();
834 Expr(x, kPrecedenceLowest, want_comma ? "," : std::string());
835 CHECK(!x->comments() || x->comments()->after().empty());
836 if (i < list.size() - 1) {
837 if (!want_comma)
838 Newline();
842 // Trailing comments.
843 if (end->comments() && !end->comments()->before().empty()) {
844 if (!list.empty())
845 Newline();
846 for (const auto& c : end->comments()->before()) {
847 Newline();
848 TrimAndPrintToken(c);
850 Newline();
852 stack_.pop_back();
855 // Defer any end of line comment until we reach the newline.
856 if (end->comments() && !end->comments()->suffix().empty()) {
857 std::copy(end->comments()->suffix().begin(),
858 end->comments()->suffix().end(), std::back_inserter(comments_));
861 Print(")");
862 Print(suffix);
864 if (have_block) {
865 Print(" ");
866 Sequence(kSequenceStyleBracedBlock,
867 func_call->block()->statements(),
868 func_call->block()->End(),
869 false);
871 return penalty + (CurrentLine() - start_line) * GetPenaltyForLineBreak();
874 void Printer::InitializeSub(Printer* sub) {
875 sub->stack_ = stack_;
876 sub->comments_ = comments_;
877 sub->penalty_depth_ = penalty_depth_;
878 sub->Print(std::string(CurrentColumn(), 'x'));
881 template <class PARSENODE>
882 bool Printer::ListWillBeMultiline(const std::vector<PARSENODE*>& list,
883 const ParseNode* end) {
884 if (list.size() > 1)
885 return true;
887 if (end && end->comments() && !end->comments()->before().empty())
888 return true;
890 // If there's before line comments, make sure we have a place to put them.
891 for (const auto& i : list) {
892 if (i->comments() && !i->comments()->before().empty())
893 return true;
896 return false;
899 void DoFormat(const ParseNode* root, bool dump_tree, std::string* output) {
900 if (dump_tree) {
901 std::ostringstream os;
902 root->Print(os, 0);
903 printf("----------------------\n");
904 printf("-- PARSE TREE --------\n");
905 printf("----------------------\n");
906 printf("%s", os.str().c_str());
907 printf("----------------------\n");
909 Printer pr;
910 pr.Block(root);
911 *output = pr.String();
914 std::string ReadStdin() {
915 static const int kBufferSize = 256;
916 char buffer[kBufferSize];
917 std::string result;
918 while (true) {
919 char* input = nullptr;
920 input = fgets(buffer, kBufferSize, stdin);
921 if (input == nullptr && feof(stdin))
922 return result;
923 int length = static_cast<int>(strlen(buffer));
924 if (length == 0)
925 return result;
926 else
927 result += std::string(buffer, length);
931 } // namespace
933 bool FormatFileToString(Setup* setup,
934 const SourceFile& file,
935 bool dump_tree,
936 std::string* output) {
937 Err err;
938 const ParseNode* parse_node =
939 setup->scheduler().input_file_manager()->SyncLoadFile(
940 LocationRange(), &setup->build_settings(), file, &err);
941 if (err.has_error()) {
942 err.PrintToStdout();
943 return false;
945 DoFormat(parse_node, dump_tree, output);
946 return true;
949 bool FormatStringToString(const std::string& input,
950 bool dump_tree,
951 std::string* output) {
952 SourceFile source_file;
953 InputFile file(source_file);
954 file.SetContents(input);
955 Err err;
956 // Tokenize.
957 std::vector<Token> tokens = Tokenizer::Tokenize(&file, &err);
958 if (err.has_error()) {
959 err.PrintToStdout();
960 return false;
963 // Parse.
964 scoped_ptr<ParseNode> parse_node = Parser::Parse(tokens, &err);
965 if (err.has_error()) {
966 err.PrintToStdout();
967 return false;
970 DoFormat(parse_node.get(), dump_tree, output);
971 return true;
974 int RunFormat(const std::vector<std::string>& args) {
975 bool dry_run =
976 base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchDryRun);
977 bool dump_tree =
978 base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchDumpTree);
979 bool from_stdin =
980 base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchStdin);
981 bool in_place =
982 base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchInPlace);
984 if (dry_run) {
985 // --dry-run only works with an actual file to compare to.
986 from_stdin = false;
987 in_place = true;
990 if (from_stdin) {
991 if (args.size() != 0) {
992 Err(Location(), "Expecting no arguments when reading from stdin.\n")
993 .PrintToStdout();
994 return 1;
996 std::string input = ReadStdin();
997 std::string output;
998 if (!FormatStringToString(input, dump_tree, &output))
999 return 1;
1000 printf("%s", output.c_str());
1001 return 0;
1004 // TODO(scottmg): Eventually, this should be a list/spec of files, and they
1005 // should all be done in parallel.
1006 if (args.size() != 1) {
1007 Err(Location(), "Expecting exactly one argument, see `gn help format`.\n")
1008 .PrintToStdout();
1009 return 1;
1012 Setup setup;
1013 SourceDir source_dir =
1014 SourceDirForCurrentDirectory(setup.build_settings().root_path());
1016 Err err;
1017 SourceFile file = source_dir.ResolveRelativeFile(Value(nullptr, args[0]),
1018 &err);
1019 if (err.has_error()) {
1020 err.PrintToStdout();
1021 return 1;
1024 std::string output_string;
1025 if (FormatFileToString(&setup, file, dump_tree, &output_string)) {
1026 if (in_place) {
1027 base::FilePath to_write = setup.build_settings().GetFullPath(file);
1028 std::string original_contents;
1029 if (!base::ReadFileToString(to_write, &original_contents)) {
1030 Err(Location(), std::string("Couldn't read \"") +
1031 to_write.AsUTF8Unsafe() +
1032 std::string("\" for comparison.")).PrintToStdout();
1033 return 1;
1035 if (dry_run)
1036 return original_contents == output_string ? 0 : 2;
1037 if (original_contents != output_string) {
1038 if (base::WriteFile(to_write,
1039 output_string.data(),
1040 static_cast<int>(output_string.size())) == -1) {
1041 Err(Location(),
1042 std::string("Failed to write formatted output back to \"") +
1043 to_write.AsUTF8Unsafe() + std::string("\".")).PrintToStdout();
1044 return 1;
1046 printf("Wrote formatted to '%s'.\n", to_write.AsUTF8Unsafe().c_str());
1048 } else {
1049 printf("%s", output_string.c_str());
1053 return 0;
1056 } // namespace commands