[refactor] More post-NSS WebCrypto cleanups (utility functions).
[chromium-blink-merge.git] / tools / gn / command_format.cc
blob5eff6017f3f9c2f4a4574e852e7e0ca0373d2d0c
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 return static_cast<int>(base::SplitStringPiece(
84 str, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL).size());
87 class Printer {
88 public:
89 Printer();
90 ~Printer();
92 void Block(const ParseNode* file);
94 std::string String() const { return output_; }
96 private:
97 // Format a list of values using the given style.
98 enum SequenceStyle {
99 kSequenceStyleList,
100 kSequenceStyleBlock,
101 kSequenceStyleBracedBlock,
104 struct Metrics {
105 Metrics() : first_length(-1), longest_length(-1), multiline(false) {}
106 int first_length;
107 int longest_length;
108 bool multiline;
111 // Add to output.
112 void Print(base::StringPiece str);
114 // Add the current margin (as spaces) to the output.
115 void PrintMargin();
117 void TrimAndPrintToken(const Token& token);
119 // End the current line, flushing end of line comments.
120 void Newline();
122 // Remove trailing spaces from the current line.
123 void Trim();
125 // Whether there's a blank separator line at the current position.
126 bool HaveBlankLine();
128 // Flag assignments to sources, deps, etc. to make their RHSs multiline.
129 void AnnotatePreferredMultilineAssignment(const BinaryOpNode* binop);
131 // Alphabetically a list on the RHS if the LHS is 'sources'.
132 void SortIfSources(const BinaryOpNode* binop);
134 // Heuristics to decide if there should be a blank line added between two
135 // items. For various "small" items, it doesn't look nice if there's too much
136 // vertical whitespace added.
137 bool ShouldAddBlankLineInBetween(const ParseNode* a, const ParseNode* b);
139 // Get the 0-based x position on the current line.
140 int CurrentColumn() const;
142 // Get the current line in the output;
143 int CurrentLine() const;
145 // Adds an opening ( if prec is less than the outers (to maintain evalution
146 // order for a subexpression). If an opening paren is emitted, *parenthesized
147 // will be set so it can be closed at the end of the expression.
148 void AddParen(int prec, int outer_prec, bool* parenthesized);
150 // Print the expression to the output buffer. Returns the type of element
151 // added to the output. The value of outer_prec gives the precedence of the
152 // operator outside this Expr. If that operator binds tighter than root's,
153 // Expr must introduce parentheses.
154 int Expr(const ParseNode* root, int outer_prec, const std::string& suffix);
156 // Generic penalties for exceeding maximum width, adding more lines, etc.
157 int AssessPenalty(const std::string& output);
159 // Tests if any lines exceed the maximum width.
160 bool ExceedsMaximumWidth(const std::string& output);
162 // Format a list of values using the given style.
163 // |end| holds any trailing comments to be printed just before the closing
164 // bracket.
165 template <class PARSENODE> // Just for const covariance.
166 void Sequence(SequenceStyle style,
167 const std::vector<PARSENODE*>& list,
168 const ParseNode* end,
169 bool force_multiline);
171 // Returns the penalty.
172 int FunctionCall(const FunctionCallNode* func_call,
173 const std::string& suffix);
175 // Create a clone of this Printer in a similar state (other than the output,
176 // but including margins, etc.) to be used for dry run measurements.
177 void InitializeSub(Printer* sub);
179 template <class PARSENODE>
180 bool ListWillBeMultiline(const std::vector<PARSENODE*>& list,
181 const ParseNode* end);
183 std::string output_; // Output buffer.
184 std::vector<Token> comments_; // Pending end-of-line comments.
185 int margin() const { return stack_.back().margin; }
187 int penalty_depth_;
188 int GetPenaltyForLineBreak() const {
189 return penalty_depth_ * kPenaltyLineBreak;
192 struct IndentState {
193 IndentState()
194 : margin(0),
195 continuation_requires_indent(false),
196 parent_is_boolean_or(false) {}
197 IndentState(int margin,
198 bool continuation_requires_indent,
199 bool parent_is_boolean_or)
200 : margin(margin),
201 continuation_requires_indent(continuation_requires_indent),
202 parent_is_boolean_or(parent_is_boolean_or) {}
204 // The left margin (number of spaces).
205 int margin;
207 bool continuation_requires_indent;
209 bool parent_is_boolean_or;
211 // Stack used to track
212 std::vector<IndentState> stack_;
214 // Gives the precedence for operators in a BinaryOpNode.
215 std::map<base::StringPiece, Precedence> precedence_;
217 DISALLOW_COPY_AND_ASSIGN(Printer);
220 Printer::Printer() : penalty_depth_(0) {
221 output_.reserve(100 << 10);
222 precedence_["="] = kPrecedenceAssign;
223 precedence_["+="] = kPrecedenceAssign;
224 precedence_["-="] = kPrecedenceAssign;
225 precedence_["||"] = kPrecedenceOr;
226 precedence_["&&"] = kPrecedenceAnd;
227 precedence_["<"] = kPrecedenceCompare;
228 precedence_[">"] = kPrecedenceCompare;
229 precedence_["=="] = kPrecedenceCompare;
230 precedence_["!="] = kPrecedenceCompare;
231 precedence_["<="] = kPrecedenceCompare;
232 precedence_[">="] = kPrecedenceCompare;
233 precedence_["+"] = kPrecedenceAdd;
234 precedence_["-"] = kPrecedenceAdd;
235 precedence_["!"] = kPrecedenceUnary;
236 stack_.push_back(IndentState());
239 Printer::~Printer() {
242 void Printer::Print(base::StringPiece str) {
243 str.AppendToString(&output_);
246 void Printer::PrintMargin() {
247 output_ += std::string(margin(), ' ');
250 void Printer::TrimAndPrintToken(const Token& token) {
251 std::string trimmed;
252 TrimWhitespaceASCII(token.value().as_string(), base::TRIM_ALL, &trimmed);
253 Print(trimmed);
256 void Printer::Newline() {
257 if (!comments_.empty()) {
258 Print(" ");
259 // Save the margin, and temporarily set it to where the first comment
260 // starts so that multiple suffix comments are vertically aligned. This
261 // will need to be fancier once we enforce 80 col.
262 stack_.push_back(IndentState(CurrentColumn(), false, false));
263 int i = 0;
264 for (const auto& c : comments_) {
265 if (i > 0) {
266 Trim();
267 Print("\n");
268 PrintMargin();
270 TrimAndPrintToken(c);
271 ++i;
273 stack_.pop_back();
274 comments_.clear();
276 Trim();
277 Print("\n");
278 PrintMargin();
281 void Printer::Trim() {
282 size_t n = output_.size();
283 while (n > 0 && output_[n - 1] == ' ')
284 --n;
285 output_.resize(n);
288 bool Printer::HaveBlankLine() {
289 size_t n = output_.size();
290 while (n > 0 && output_[n - 1] == ' ')
291 --n;
292 return n > 2 && output_[n - 1] == '\n' && output_[n - 2] == '\n';
295 void Printer::AnnotatePreferredMultilineAssignment(const BinaryOpNode* binop) {
296 const IdentifierNode* ident = binop->left()->AsIdentifier();
297 const ListNode* list = binop->right()->AsList();
298 // This is somewhat arbitrary, but we include the 'deps'- and 'sources'-like
299 // things, but not flags things.
300 if (binop->op().value() == "=" && ident && list) {
301 const base::StringPiece lhs = ident->value().value();
302 if (lhs == "data" || lhs == "datadeps" || lhs == "data_deps" ||
303 lhs == "deps" || lhs == "inputs" || lhs == "outputs" ||
304 lhs == "public" || lhs == "public_deps" || lhs == "sources") {
305 const_cast<ListNode*>(list)->set_prefer_multiline(true);
310 void Printer::SortIfSources(const BinaryOpNode* binop) {
311 const IdentifierNode* ident = binop->left()->AsIdentifier();
312 const ListNode* list = binop->right()->AsList();
313 // TODO(scottmg): Sort more than 'sources'?
314 if ((binop->op().value() == "=" || binop->op().value() == "+=" ||
315 binop->op().value() == "-=") &&
316 ident && list) {
317 const base::StringPiece lhs = ident->value().value();
318 if (lhs == "sources")
319 const_cast<ListNode*>(list)->SortAsStringsList();
324 bool Printer::ShouldAddBlankLineInBetween(const ParseNode* a,
325 const ParseNode* b) {
326 LocationRange a_range = a->GetRange();
327 LocationRange b_range = b->GetRange();
328 // If they're already separated by 1 or more lines, then we want to keep a
329 // blank line.
330 return (b_range.begin().line_number() > a_range.end().line_number() + 1) ||
331 // Always put a blank line before a block comment.
332 b->AsBlockComment();
335 int Printer::CurrentColumn() const {
336 int n = 0;
337 while (n < static_cast<int>(output_.size()) &&
338 output_[output_.size() - 1 - n] != '\n') {
339 ++n;
341 return n;
344 int Printer::CurrentLine() const {
345 int count = 1;
346 for (const char* p = output_.c_str(); (p = strchr(p, '\n')) != nullptr;) {
347 ++count;
348 ++p;
350 return count;
353 void Printer::Block(const ParseNode* root) {
354 const BlockNode* block = root->AsBlock();
356 if (block->comments()) {
357 for (const auto& c : block->comments()->before()) {
358 TrimAndPrintToken(c);
359 Newline();
363 size_t i = 0;
364 for (const auto& stmt : block->statements()) {
365 Expr(stmt, kPrecedenceLowest, std::string());
366 Newline();
367 if (stmt->comments()) {
368 // Why are before() not printed here too? before() are handled inside
369 // Expr(), as are suffix() which are queued to the next Newline().
370 // However, because it's a general expression handler, it doesn't insert
371 // the newline itself, which only happens between block statements. So,
372 // the after are handled explicitly here.
373 for (const auto& c : stmt->comments()->after()) {
374 TrimAndPrintToken(c);
375 Newline();
378 if (i < block->statements().size() - 1 &&
379 (ShouldAddBlankLineInBetween(block->statements()[i],
380 block->statements()[i + 1]))) {
381 Newline();
383 ++i;
386 if (block->comments()) {
387 for (const auto& c : block->comments()->after()) {
388 TrimAndPrintToken(c);
389 Newline();
394 int Printer::AssessPenalty(const std::string& output) {
395 int penalty = 0;
396 std::vector<std::string> lines = base::SplitString(
397 output, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
398 penalty += static_cast<int>(lines.size() - 1) * GetPenaltyForLineBreak();
399 for (const auto& line : lines) {
400 if (line.size() > kMaximumWidth)
401 penalty += static_cast<int>(line.size() - kMaximumWidth) * kPenaltyExcess;
403 return penalty;
406 bool Printer::ExceedsMaximumWidth(const std::string& output) {
407 for (const auto& line : base::SplitString(
408 output, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL)) {
409 if (line.size() > kMaximumWidth)
410 return true;
412 return false;
415 void Printer::AddParen(int prec, int outer_prec, bool* parenthesized) {
416 if (prec < outer_prec) {
417 Print("(");
418 *parenthesized = true;
422 int Printer::Expr(const ParseNode* root,
423 int outer_prec,
424 const std::string& suffix) {
425 std::string at_end = suffix;
426 int penalty = 0;
427 penalty_depth_++;
429 if (root->comments()) {
430 if (!root->comments()->before().empty()) {
431 Trim();
432 // If there's already other text on the line, start a new line.
433 if (CurrentColumn() > 0)
434 Print("\n");
435 // We're printing a line comment, so we need to be at the current margin.
436 PrintMargin();
437 for (const auto& c : root->comments()->before()) {
438 TrimAndPrintToken(c);
439 Newline();
444 bool parenthesized = false;
446 if (const AccessorNode* accessor = root->AsAccessor()) {
447 AddParen(kPrecedenceSuffix, outer_prec, &parenthesized);
448 Print(accessor->base().value());
449 if (accessor->member()) {
450 Print(".");
451 Expr(accessor->member(), kPrecedenceLowest, std::string());
452 } else {
453 CHECK(accessor->index());
454 Print("[");
455 Expr(accessor->index(), kPrecedenceLowest, "]");
457 } else if (const BinaryOpNode* binop = root->AsBinaryOp()) {
458 CHECK(precedence_.find(binop->op().value()) != precedence_.end());
459 AnnotatePreferredMultilineAssignment(binop);
461 SortIfSources(binop);
463 Precedence prec = precedence_[binop->op().value()];
465 // Since binary operators format left-to-right, it is ok for the left side
466 // use the same operator without parentheses, so the left uses prec. For the
467 // same reason, the right side cannot reuse the same operator, or else "x +
468 // (y + z)" would format as "x + y + z" which means "(x + y) + z". So, treat
469 // the right expression as appearing one precedence level higher.
470 // However, because the source parens are not in the parse tree, as a
471 // special case for && and || we insert strictly-redundant-but-helpful-for-
472 // human-readers parentheses.
473 int prec_left = prec;
474 int prec_right = prec + 1;
475 if (binop->op().value() == "&&" && stack_.back().parent_is_boolean_or) {
476 Print("(");
477 parenthesized = true;
478 } else {
479 AddParen(prec_left, outer_prec, &parenthesized);
482 int start_line = CurrentLine();
483 int start_column = CurrentColumn();
484 bool is_assignment = binop->op().value() == "=" ||
485 binop->op().value() == "+=" ||
486 binop->op().value() == "-=";
487 // A sort of funny special case for the long lists that are common in .gn
488 // files, don't indent them + 4, even though they're just continuations when
489 // they're simple lists like "x = [ a, b, c, ... ]"
490 const ListNode* right_as_list = binop->right()->AsList();
491 int indent_column =
492 (is_assignment &&
493 (!right_as_list || (!right_as_list->prefer_multiline() &&
494 !ListWillBeMultiline(right_as_list->contents(),
495 right_as_list->End()))))
496 ? margin() + kIndentSize * 2
497 : start_column;
498 if (stack_.back().continuation_requires_indent)
499 indent_column += kIndentSize * 2;
501 stack_.push_back(IndentState(indent_column,
502 stack_.back().continuation_requires_indent,
503 binop->op().value() == "||"));
504 Printer sub_left;
505 InitializeSub(&sub_left);
506 sub_left.Expr(binop->left(),
507 prec_left,
508 std::string(" ") + binop->op().value().as_string());
509 bool left_is_multiline = CountLines(sub_left.String()) > 1;
510 // Avoid walking the whole left redundantly times (see timing of Format.046)
511 // so pull the output and comments from subprinter.
512 Print(sub_left.String().substr(start_column));
513 std::copy(sub_left.comments_.begin(),
514 sub_left.comments_.end(),
515 std::back_inserter(comments_));
517 // Single line.
518 Printer sub1;
519 InitializeSub(&sub1);
520 sub1.Print(" ");
521 int penalty_current_line =
522 sub1.Expr(binop->right(), prec_right, std::string());
523 sub1.Print(suffix);
524 penalty_current_line += AssessPenalty(sub1.String());
525 if (!is_assignment && left_is_multiline) {
526 // In e.g. xxx + yyy, if xxx is already multiline, then we want a penalty
527 // for trying to continue as if this were one line.
528 penalty_current_line +=
529 (CountLines(sub1.String()) - 1) * kPenaltyBrokenLineOnOneLiner;
532 // Break after operator.
533 Printer sub2;
534 InitializeSub(&sub2);
535 sub2.Newline();
536 int penalty_next_line =
537 sub2.Expr(binop->right(), prec_right, std::string());
538 sub2.Print(suffix);
539 penalty_next_line += AssessPenalty(sub2.String());
541 // If in both cases it was forced past 80col, then we don't break to avoid
542 // breaking after '=' in the case of:
543 // variable = "... very long string ..."
544 // as breaking and indenting doesn't make things much more readable, even
545 // though there's less characters past the maximum width.
546 bool exceeds_maximum_either_way = ExceedsMaximumWidth(sub1.String()) &&
547 ExceedsMaximumWidth(sub2.String());
549 if (penalty_current_line < penalty_next_line ||
550 exceeds_maximum_either_way) {
551 Print(" ");
552 Expr(binop->right(), prec_right, std::string());
553 } else {
554 // Otherwise, put first argument and op, and indent next.
555 Newline();
556 penalty += std::abs(CurrentColumn() - start_column) *
557 kPenaltyHorizontalSeparation;
558 Expr(binop->right(), prec_right, std::string());
560 stack_.pop_back();
561 penalty += (CurrentLine() - start_line) * GetPenaltyForLineBreak();
562 } else if (const BlockNode* block = root->AsBlock()) {
563 Sequence(
564 kSequenceStyleBracedBlock, block->statements(), block->End(), false);
565 } else if (const ConditionNode* condition = root->AsConditionNode()) {
566 Print("if (");
567 // TODO(scottmg): The { needs to be included in the suffix here.
568 Expr(condition->condition(), kPrecedenceLowest, ") ");
569 Sequence(kSequenceStyleBracedBlock,
570 condition->if_true()->statements(),
571 condition->if_true()->End(),
572 false);
573 if (condition->if_false()) {
574 Print(" else ");
575 // If it's a block it's a bare 'else', otherwise it's an 'else if'. See
576 // ConditionNode::Execute.
577 bool is_else_if = condition->if_false()->AsBlock() == nullptr;
578 if (is_else_if) {
579 Expr(condition->if_false(), kPrecedenceLowest, std::string());
580 } else {
581 Sequence(kSequenceStyleBracedBlock,
582 condition->if_false()->AsBlock()->statements(),
583 condition->if_false()->AsBlock()->End(),
584 false);
587 } else if (const FunctionCallNode* func_call = root->AsFunctionCall()) {
588 penalty += FunctionCall(func_call, at_end);
589 at_end = "";
590 } else if (const IdentifierNode* identifier = root->AsIdentifier()) {
591 Print(identifier->value().value());
592 } else if (const ListNode* list = root->AsList()) {
593 bool force_multiline =
594 list->prefer_multiline() && !list->contents().empty();
595 Sequence(
596 kSequenceStyleList, list->contents(), list->End(), force_multiline);
597 } else if (const LiteralNode* literal = root->AsLiteral()) {
598 Print(literal->value().value());
599 } else if (const UnaryOpNode* unaryop = root->AsUnaryOp()) {
600 Print(unaryop->op().value());
601 Expr(unaryop->operand(), kPrecedenceUnary, std::string());
602 } else if (const BlockCommentNode* block_comment = root->AsBlockComment()) {
603 Print(block_comment->comment().value());
604 } else if (const EndNode* end = root->AsEnd()) {
605 Print(end->value().value());
606 } else {
607 CHECK(false) << "Unhandled case in Expr.";
610 if (parenthesized)
611 Print(")");
613 // Defer any end of line comment until we reach the newline.
614 if (root->comments() && !root->comments()->suffix().empty()) {
615 std::copy(root->comments()->suffix().begin(),
616 root->comments()->suffix().end(),
617 std::back_inserter(comments_));
620 Print(at_end);
622 penalty_depth_--;
623 return penalty;
626 template <class PARSENODE>
627 void Printer::Sequence(SequenceStyle style,
628 const std::vector<PARSENODE*>& list,
629 const ParseNode* end,
630 bool force_multiline) {
631 if (style == kSequenceStyleList)
632 Print("[");
633 else if (style == kSequenceStyleBracedBlock)
634 Print("{");
636 if (style == kSequenceStyleBlock || style == kSequenceStyleBracedBlock)
637 force_multiline = true;
639 force_multiline |= ListWillBeMultiline(list, end);
641 if (list.size() == 0 && !force_multiline) {
642 // No elements, and not forcing newlines, print nothing.
643 } else if (list.size() == 1 && !force_multiline) {
644 Print(" ");
645 Expr(list[0], kPrecedenceLowest, std::string());
646 CHECK(!list[0]->comments() || list[0]->comments()->after().empty());
647 Print(" ");
648 } else {
649 stack_.push_back(IndentState(margin() + kIndentSize,
650 style == kSequenceStyleList,
651 false));
652 size_t i = 0;
653 for (const auto& x : list) {
654 Newline();
655 // If:
656 // - we're going to output some comments, and;
657 // - we haven't just started this multiline list, and;
658 // - there isn't already a blank line here;
659 // Then: insert one.
660 if (i != 0 && x->comments() && !x->comments()->before().empty() &&
661 !HaveBlankLine()) {
662 Newline();
664 bool body_of_list = i < list.size() - 1 || style == kSequenceStyleList;
665 bool want_comma =
666 body_of_list && (style == kSequenceStyleList && !x->AsBlockComment());
667 Expr(x, kPrecedenceLowest, want_comma ? "," : std::string());
668 CHECK(!x->comments() || x->comments()->after().empty());
669 if (body_of_list) {
670 if (i < list.size() - 1 &&
671 ShouldAddBlankLineInBetween(list[i], list[i + 1]))
672 Newline();
674 ++i;
677 // Trailing comments.
678 if (end->comments() && !end->comments()->before().empty()) {
679 if (list.size() >= 2)
680 Newline();
681 for (const auto& c : end->comments()->before()) {
682 Newline();
683 TrimAndPrintToken(c);
687 stack_.pop_back();
688 Newline();
690 // Defer any end of line comment until we reach the newline.
691 if (end->comments() && !end->comments()->suffix().empty()) {
692 std::copy(end->comments()->suffix().begin(),
693 end->comments()->suffix().end(),
694 std::back_inserter(comments_));
698 if (style == kSequenceStyleList)
699 Print("]");
700 else if (style == kSequenceStyleBracedBlock)
701 Print("}");
704 int Printer::FunctionCall(const FunctionCallNode* func_call,
705 const std::string& suffix) {
706 int start_line = CurrentLine();
707 int start_column = CurrentColumn();
708 Print(func_call->function().value());
709 Print("(");
711 bool have_block = func_call->block() != nullptr;
712 bool force_multiline = false;
714 const std::vector<const ParseNode*>& list = func_call->args()->contents();
715 const ParseNode* end = func_call->args()->End();
717 if (end && end->comments() && !end->comments()->before().empty())
718 force_multiline = true;
720 // If there's before line comments, make sure we have a place to put them.
721 for (const auto& i : list) {
722 if (i->comments() && !i->comments()->before().empty())
723 force_multiline = true;
726 // Calculate the penalties for 3 possible layouts:
727 // 1. all on same line;
728 // 2. starting on same line, broken at each comma but paren aligned;
729 // 3. broken to next line + 4, broken at each comma.
730 std::string terminator = ")";
731 if (have_block)
732 terminator += " {";
733 terminator += suffix;
735 // Special case to make function calls of one arg taking a long list of
736 // boolean operators not indent.
737 bool continuation_requires_indent =
738 list.size() != 1 || !list[0]->AsBinaryOp();
740 // 1: Same line.
741 Printer sub1;
742 InitializeSub(&sub1);
743 sub1.stack_.push_back(
744 IndentState(CurrentColumn(), continuation_requires_indent, false));
745 int penalty_one_line = 0;
746 for (size_t i = 0; i < list.size(); ++i) {
747 penalty_one_line += sub1.Expr(list[i], kPrecedenceLowest,
748 i < list.size() - 1 ? ", " : std::string());
750 sub1.Print(terminator);
751 penalty_one_line += AssessPenalty(sub1.String());
752 // This extra penalty prevents a short second argument from being squeezed in
753 // after a first argument that went multiline (and instead preferring a
754 // variant below).
755 penalty_one_line +=
756 (CountLines(sub1.String()) - 1) * kPenaltyBrokenLineOnOneLiner;
758 // 2: Starting on same line, broken at commas.
759 Printer sub2;
760 InitializeSub(&sub2);
761 sub2.stack_.push_back(
762 IndentState(CurrentColumn(), continuation_requires_indent, false));
763 int penalty_multiline_start_same_line = 0;
764 for (size_t i = 0; i < list.size(); ++i) {
765 penalty_multiline_start_same_line += sub2.Expr(
766 list[i], kPrecedenceLowest, i < list.size() - 1 ? "," : std::string());
767 if (i < list.size() - 1) {
768 sub2.Newline();
771 sub2.Print(terminator);
772 penalty_multiline_start_same_line += AssessPenalty(sub2.String());
774 // 3: Starting on next line, broken at commas.
775 Printer sub3;
776 InitializeSub(&sub3);
777 sub3.stack_.push_back(IndentState(margin() + kIndentSize * 2,
778 continuation_requires_indent, false));
779 sub3.Newline();
780 int penalty_multiline_start_next_line = 0;
781 for (size_t i = 0; i < list.size(); ++i) {
782 if (i == 0) {
783 penalty_multiline_start_next_line +=
784 std::abs(sub3.CurrentColumn() - start_column) *
785 kPenaltyHorizontalSeparation;
787 penalty_multiline_start_next_line += sub3.Expr(
788 list[i], kPrecedenceLowest, i < list.size() - 1 ? "," : std::string());
789 if (i < list.size() - 1) {
790 sub3.Newline();
793 sub3.Print(terminator);
794 penalty_multiline_start_next_line += AssessPenalty(sub3.String());
796 int penalty = penalty_multiline_start_next_line;
797 bool fits_on_current_line = false;
798 if (penalty_one_line < penalty_multiline_start_next_line ||
799 penalty_multiline_start_same_line < penalty_multiline_start_next_line) {
800 fits_on_current_line = true;
801 penalty = penalty_one_line;
802 if (penalty_multiline_start_same_line < penalty_one_line) {
803 penalty = penalty_multiline_start_same_line;
804 force_multiline = true;
806 } else {
807 force_multiline = true;
810 if (list.size() == 0 && !force_multiline) {
811 // No elements, and not forcing newlines, print nothing.
812 } else {
813 if (penalty_multiline_start_next_line < penalty_multiline_start_same_line) {
814 stack_.push_back(IndentState(margin() + kIndentSize * 2,
815 continuation_requires_indent,
816 false));
817 Newline();
818 } else {
819 stack_.push_back(
820 IndentState(CurrentColumn(), continuation_requires_indent, false));
823 for (size_t i = 0; i < list.size(); ++i) {
824 const auto& x = list[i];
825 if (i > 0) {
826 if (fits_on_current_line && !force_multiline)
827 Print(" ");
828 else
829 Newline();
831 bool want_comma = i < list.size() - 1 && !x->AsBlockComment();
832 Expr(x, kPrecedenceLowest, want_comma ? "," : std::string());
833 CHECK(!x->comments() || x->comments()->after().empty());
834 if (i < list.size() - 1) {
835 if (!want_comma)
836 Newline();
840 // Trailing comments.
841 if (end->comments() && !end->comments()->before().empty()) {
842 if (!list.empty())
843 Newline();
844 for (const auto& c : end->comments()->before()) {
845 Newline();
846 TrimAndPrintToken(c);
848 Newline();
850 stack_.pop_back();
853 // Defer any end of line comment until we reach the newline.
854 if (end->comments() && !end->comments()->suffix().empty()) {
855 std::copy(end->comments()->suffix().begin(),
856 end->comments()->suffix().end(), std::back_inserter(comments_));
859 Print(")");
860 Print(suffix);
862 if (have_block) {
863 Print(" ");
864 Sequence(kSequenceStyleBracedBlock,
865 func_call->block()->statements(),
866 func_call->block()->End(),
867 false);
869 return penalty + (CurrentLine() - start_line) * GetPenaltyForLineBreak();
872 void Printer::InitializeSub(Printer* sub) {
873 sub->stack_ = stack_;
874 sub->comments_ = comments_;
875 sub->penalty_depth_ = penalty_depth_;
876 sub->Print(std::string(CurrentColumn(), 'x'));
879 template <class PARSENODE>
880 bool Printer::ListWillBeMultiline(const std::vector<PARSENODE*>& list,
881 const ParseNode* end) {
882 if (list.size() > 1)
883 return true;
885 if (end && end->comments() && !end->comments()->before().empty())
886 return true;
888 // If there's before line comments, make sure we have a place to put them.
889 for (const auto& i : list) {
890 if (i->comments() && !i->comments()->before().empty())
891 return true;
894 return false;
897 void DoFormat(const ParseNode* root, bool dump_tree, std::string* output) {
898 if (dump_tree) {
899 std::ostringstream os;
900 root->Print(os, 0);
901 printf("----------------------\n");
902 printf("-- PARSE TREE --------\n");
903 printf("----------------------\n");
904 printf("%s", os.str().c_str());
905 printf("----------------------\n");
907 Printer pr;
908 pr.Block(root);
909 *output = pr.String();
912 std::string ReadStdin() {
913 static const int kBufferSize = 256;
914 char buffer[kBufferSize];
915 std::string result;
916 while (true) {
917 char* input = nullptr;
918 input = fgets(buffer, kBufferSize, stdin);
919 if (input == nullptr && feof(stdin))
920 return result;
921 int length = static_cast<int>(strlen(buffer));
922 if (length == 0)
923 return result;
924 else
925 result += std::string(buffer, length);
929 } // namespace
931 bool FormatFileToString(Setup* setup,
932 const SourceFile& file,
933 bool dump_tree,
934 std::string* output) {
935 Err err;
936 const ParseNode* parse_node =
937 setup->scheduler().input_file_manager()->SyncLoadFile(
938 LocationRange(), &setup->build_settings(), file, &err);
939 if (err.has_error()) {
940 err.PrintToStdout();
941 return false;
943 DoFormat(parse_node, dump_tree, output);
944 return true;
947 bool FormatStringToString(const std::string& input,
948 bool dump_tree,
949 std::string* output) {
950 SourceFile source_file;
951 InputFile file(source_file);
952 file.SetContents(input);
953 Err err;
954 // Tokenize.
955 std::vector<Token> tokens = Tokenizer::Tokenize(&file, &err);
956 if (err.has_error()) {
957 err.PrintToStdout();
958 return false;
961 // Parse.
962 scoped_ptr<ParseNode> parse_node = Parser::Parse(tokens, &err);
963 if (err.has_error()) {
964 err.PrintToStdout();
965 return false;
968 DoFormat(parse_node.get(), dump_tree, output);
969 return true;
972 int RunFormat(const std::vector<std::string>& args) {
973 bool dry_run =
974 base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchDryRun);
975 bool dump_tree =
976 base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchDumpTree);
977 bool from_stdin =
978 base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchStdin);
979 bool in_place =
980 base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchInPlace);
982 if (dry_run) {
983 // --dry-run only works with an actual file to compare to.
984 from_stdin = false;
985 in_place = true;
988 if (from_stdin) {
989 if (args.size() != 0) {
990 Err(Location(), "Expecting no arguments when reading from stdin.\n")
991 .PrintToStdout();
992 return 1;
994 std::string input = ReadStdin();
995 std::string output;
996 if (!FormatStringToString(input, dump_tree, &output))
997 return 1;
998 printf("%s", output.c_str());
999 return 0;
1002 // TODO(scottmg): Eventually, this should be a list/spec of files, and they
1003 // should all be done in parallel.
1004 if (args.size() != 1) {
1005 Err(Location(), "Expecting exactly one argument, see `gn help format`.\n")
1006 .PrintToStdout();
1007 return 1;
1010 Setup setup;
1011 SourceDir source_dir =
1012 SourceDirForCurrentDirectory(setup.build_settings().root_path());
1014 Err err;
1015 SourceFile file = source_dir.ResolveRelativeFile(Value(nullptr, args[0]),
1016 &err);
1017 if (err.has_error()) {
1018 err.PrintToStdout();
1019 return 1;
1022 std::string output_string;
1023 if (FormatFileToString(&setup, file, dump_tree, &output_string)) {
1024 if (in_place) {
1025 base::FilePath to_write = setup.build_settings().GetFullPath(file);
1026 std::string original_contents;
1027 if (!base::ReadFileToString(to_write, &original_contents)) {
1028 Err(Location(), std::string("Couldn't read \"") +
1029 to_write.AsUTF8Unsafe() +
1030 std::string("\" for comparison.")).PrintToStdout();
1031 return 1;
1033 if (dry_run)
1034 return original_contents == output_string ? 0 : 2;
1035 if (original_contents != output_string) {
1036 if (base::WriteFile(to_write,
1037 output_string.data(),
1038 static_cast<int>(output_string.size())) == -1) {
1039 Err(Location(),
1040 std::string("Failed to write formatted output back to \"") +
1041 to_write.AsUTF8Unsafe() + std::string("\".")).PrintToStdout();
1042 return 1;
1044 printf("Wrote formatted to '%s'.\n", to_write.AsUTF8Unsafe().c_str());
1046 } else {
1047 printf("%s", output_string.c_str());
1051 return 0;
1054 } // namespace commands