• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "gn/command_format.h"
6 
7 #include <stddef.h>
8 
9 #include <sstream>
10 
11 #include "base/command_line.h"
12 #include "base/files/file_util.h"
13 #include "base/json/json_writer.h"
14 #include "base/macros.h"
15 #include "base/strings/string_split.h"
16 #include "base/strings/string_util.h"
17 #include "gn/commands.h"
18 #include "gn/filesystem_utils.h"
19 #include "gn/input_file.h"
20 #include "gn/parser.h"
21 #include "gn/scheduler.h"
22 #include "gn/setup.h"
23 #include "gn/source_file.h"
24 #include "gn/string_utils.h"
25 #include "gn/switches.h"
26 #include "gn/tokenizer.h"
27 #include "util/build_config.h"
28 
29 #if defined(OS_WIN)
30 #include <fcntl.h>
31 #include <io.h>
32 #endif
33 
34 namespace commands {
35 
36 const char kSwitchDryRun[] = "dry-run";
37 const char kSwitchDumpTree[] = "dump-tree";
38 const char kSwitchDumpTreeText[] = "text";
39 const char kSwitchDumpTreeJSON[] = "json";
40 const char kSwitchStdin[] = "stdin";
41 
42 const char kFormat[] = "format";
43 const char kFormat_HelpShort[] = "format: Format .gn files.";
44 const char kFormat_Help[] =
45     R"(gn format [--dump-tree] (--stdin | <list of build_files...>)
46 
47   Formats .gn file to a standard format.
48 
49   The contents of some lists ('sources', 'deps', etc.) will be sorted to a
50   canonical order. To suppress this, you can add a comment of the form "#
51   NOSORT" immediately preceding the assignment. e.g.
52 
53   # NOSORT
54   sources = [
55     "z.cc",
56     "a.cc",
57   ]
58 
59 Arguments
60 
61   --dry-run
62       Does not change or output anything, but sets the process exit code based
63       on whether output would be different than what's on disk. This is useful
64       for presubmit/lint-type checks.
65       - Exit code 0: successful format, matches on disk.
66       - Exit code 1: general failure (parse error, etc.)
67       - Exit code 2: successful format, but differs from on disk.
68 
69   --dump-tree[=( text | json )]
70       Dumps the parse tree to stdout and does not update the file or print
71       formatted output. If no format is specified, text format will be used.
72 
73   --stdin
74       Read input from stdin and write to stdout rather than update a file
75       in-place.
76 
77 Examples
78   gn format //some/BUILD.gn //some/other/BUILD.gn //and/another/BUILD.gn
79   gn format some\\BUILD.gn
80   gn format /abspath/some/BUILD.gn
81   gn format --stdin
82 )";
83 
84 namespace {
85 
86 const int kIndentSize = 2;
87 const int kMaximumWidth = 80;
88 
89 const int kPenaltyLineBreak = 500;
90 const int kPenaltyHorizontalSeparation = 100;
91 const int kPenaltyExcess = 10000;
92 const int kPenaltyBrokenLineOnOneLiner = 5000;
93 
94 enum Precedence {
95   kPrecedenceLowest,
96   kPrecedenceAssign,
97   kPrecedenceOr,
98   kPrecedenceAnd,
99   kPrecedenceCompare,
100   kPrecedenceAdd,
101   kPrecedenceUnary,
102   kPrecedenceSuffix,
103 };
104 
CountLines(const std::string & str)105 int CountLines(const std::string& str) {
106   return static_cast<int>(base::SplitStringPiece(str, "\n",
107                                                  base::KEEP_WHITESPACE,
108                                                  base::SPLIT_WANT_ALL)
109                               .size());
110 }
111 
112 class Printer {
113  public:
114   Printer();
115   ~Printer();
116 
117   void Block(const ParseNode* file);
118 
String() const119   std::string String() const { return output_; }
120 
121  private:
122   // Format a list of values using the given style.
123   enum SequenceStyle {
124     kSequenceStyleList,
125     kSequenceStyleBracedBlock,
126     kSequenceStyleBracedBlockAlreadyOpen,
127   };
128 
129   struct Metrics {
Metricscommands::__anonfd1b22c10111::Printer::Metrics130     Metrics() : first_length(-1), longest_length(-1), multiline(false) {}
131     int first_length;
132     int longest_length;
133     bool multiline;
134   };
135 
136   // Add to output.
137   void Print(std::string_view str);
138 
139   // Add the current margin (as spaces) to the output.
140   void PrintMargin();
141 
142   void TrimAndPrintToken(const Token& token);
143 
144   void PrintTrailingCommentsWrapped(const std::vector<Token>& comments);
145 
146   void FlushComments();
147 
148   void PrintSuffixComments(const ParseNode* node);
149 
150   // End the current line, flushing end of line comments.
151   void Newline();
152 
153   // Remove trailing spaces from the current line.
154   void Trim();
155 
156   // Whether there's a blank separator line at the current position.
157   bool HaveBlankLine();
158 
159   // Sort a list on the RHS if the LHS is 'sources', 'deps' or 'public_deps'.
160   // The 'sources' are sorted alphabetically while the 'deps' and 'public_deps'
161   // are sorted putting first the relative targets and then the global ones
162   // (both sorted alphabetically).
163   void SortIfSourcesOrDeps(const BinaryOpNode* binop);
164 
165   // Sort contiguous import() function calls in the given ordered list of
166   // statements (the body of a block or scope).
167   template <class PARSENODE>
168   void SortImports(std::vector<std::unique_ptr<PARSENODE>>& statements);
169 
170   // Heuristics to decide if there should be a blank line added between two
171   // items. For various "small" items, it doesn't look nice if there's too much
172   // vertical whitespace added.
173   bool ShouldAddBlankLineInBetween(const ParseNode* a, const ParseNode* b);
174 
175   // Get the 0-based x position on the current line.
176   int CurrentColumn() const;
177 
178   // Get the current line in the output;
179   int CurrentLine() const;
180 
181   // Adds an opening ( if prec is less than the outers (to maintain evalution
182   // order for a subexpression). If an opening paren is emitted, *parenthesized
183   // will be set so it can be closed at the end of the expression.
184   void AddParen(int prec, int outer_prec, bool* parenthesized);
185 
186   // Print the expression to the output buffer. Returns the type of element
187   // added to the output. The value of outer_prec gives the precedence of the
188   // operator outside this Expr. If that operator binds tighter than root's,
189   // Expr must introduce parentheses.
190   int Expr(const ParseNode* root, int outer_prec, const std::string& suffix);
191 
192   // Generic penalties for exceeding maximum width, adding more lines, etc.
193   int AssessPenalty(const std::string& output);
194 
195   // Tests if any lines exceed the maximum width.
196   bool ExceedsMaximumWidth(const std::string& output);
197 
198   // Format a list of values using the given style.
199   // |end| holds any trailing comments to be printed just before the closing
200   // bracket.
201   template <class PARSENODE>  // Just for const covariance.
202   void Sequence(SequenceStyle style,
203                 const std::vector<std::unique_ptr<PARSENODE>>& list,
204                 const ParseNode* end,
205                 bool force_multiline);
206 
207   // Returns the penalty.
208   int FunctionCall(const FunctionCallNode* func_call,
209                    const std::string& suffix);
210 
211   // Create a clone of this Printer in a similar state (other than the output,
212   // but including margins, etc.) to be used for dry run measurements.
213   void InitializeSub(Printer* sub);
214 
215   template <class PARSENODE>
216   bool ListWillBeMultiline(const std::vector<std::unique_ptr<PARSENODE>>& list,
217                            const ParseNode* end);
218 
219   std::string output_;           // Output buffer.
220   std::vector<Token> comments_;  // Pending end-of-line comments.
margin() const221   int margin() const { return stack_.back().margin; }
222 
223   int penalty_depth_;
GetPenaltyForLineBreak() const224   int GetPenaltyForLineBreak() const {
225     return penalty_depth_ * kPenaltyLineBreak;
226   }
227 
228   struct IndentState {
IndentStatecommands::__anonfd1b22c10111::Printer::IndentState229     IndentState()
230         : margin(0),
231           continuation_requires_indent(false),
232           parent_is_boolean_or(false) {}
IndentStatecommands::__anonfd1b22c10111::Printer::IndentState233     IndentState(int margin,
234                 bool continuation_requires_indent,
235                 bool parent_is_boolean_or)
236         : margin(margin),
237           continuation_requires_indent(continuation_requires_indent),
238           parent_is_boolean_or(parent_is_boolean_or) {}
239 
240     // The left margin (number of spaces).
241     int margin;
242 
243     bool continuation_requires_indent;
244 
245     bool parent_is_boolean_or;
246   };
247   // Stack used to track
248   std::vector<IndentState> stack_;
249 
250   // Gives the precedence for operators in a BinaryOpNode.
251   std::map<std::string_view, Precedence> precedence_;
252 
253   DISALLOW_COPY_AND_ASSIGN(Printer);
254 };
255 
Printer()256 Printer::Printer() : penalty_depth_(0) {
257   output_.reserve(100 << 10);
258   precedence_["="] = kPrecedenceAssign;
259   precedence_["+="] = kPrecedenceAssign;
260   precedence_["-="] = kPrecedenceAssign;
261   precedence_["||"] = kPrecedenceOr;
262   precedence_["&&"] = kPrecedenceAnd;
263   precedence_["<"] = kPrecedenceCompare;
264   precedence_[">"] = kPrecedenceCompare;
265   precedence_["=="] = kPrecedenceCompare;
266   precedence_["!="] = kPrecedenceCompare;
267   precedence_["<="] = kPrecedenceCompare;
268   precedence_[">="] = kPrecedenceCompare;
269   precedence_["+"] = kPrecedenceAdd;
270   precedence_["-"] = kPrecedenceAdd;
271   precedence_["!"] = kPrecedenceUnary;
272   stack_.push_back(IndentState());
273 }
274 
275 Printer::~Printer() = default;
276 
Print(std::string_view str)277 void Printer::Print(std::string_view str) {
278   output_.append(str);
279 }
280 
PrintMargin()281 void Printer::PrintMargin() {
282   output_ += std::string(margin(), ' ');
283 }
284 
TrimAndPrintToken(const Token & token)285 void Printer::TrimAndPrintToken(const Token& token) {
286   std::string trimmed;
287   TrimWhitespaceASCII(std::string(token.value()), base::TRIM_ALL, &trimmed);
288   Print(trimmed);
289 }
290 
291 // Assumes that the margin is set to the indent level where the comments should
292 // be aligned. This doesn't de-wrap, it only wraps. So if a suffix comment
293 // causes the line to exceed 80 col it will be wrapped, but the subsequent line
294 // would fit on the then-broken line it will not be merged with it. This is
295 // partly because it's difficult to implement at this level, but also because
296 // it can break hand-authored line breaks where they're starting a new paragraph
297 // or statement.
PrintTrailingCommentsWrapped(const std::vector<Token> & comments)298 void Printer::PrintTrailingCommentsWrapped(const std::vector<Token>& comments) {
299   bool have_empty_line = true;
300   auto start_next_line = [this, &have_empty_line]() {
301     Trim();
302     Print("\n");
303     PrintMargin();
304     have_empty_line = true;
305   };
306   for (const auto& c : comments) {
307     if (!have_empty_line) {
308       start_next_line();
309     }
310 
311     std::string trimmed;
312     TrimWhitespaceASCII(std::string(c.value()), base::TRIM_ALL, &trimmed);
313 
314     if (margin() + trimmed.size() <= kMaximumWidth) {
315       Print(trimmed);
316       have_empty_line = false;
317     } else {
318       bool continuation = false;
319       std::vector<std::string> split_on_spaces = base::SplitString(
320           c.value(), " ", base::WhitespaceHandling::TRIM_WHITESPACE,
321           base::SplitResult::SPLIT_WANT_NONEMPTY);
322       for (size_t j = 0; j < split_on_spaces.size(); ++j) {
323         if (have_empty_line && continuation) {
324           Print("# ");
325         }
326         Print(split_on_spaces[j]) ;
327         Print(" ");
328         if (split_on_spaces[j] != "#") {
329           have_empty_line = false;
330         }
331         if (!have_empty_line &&
332             (j < split_on_spaces.size() - 1 &&
333              CurrentColumn() + split_on_spaces[j + 1].size() > kMaximumWidth)) {
334           start_next_line();
335           continuation = true;
336         }
337       }
338     }
339   }
340 }
341 
342 // Used during penalty evaluation, similar to Newline().
PrintSuffixComments(const ParseNode * node)343 void Printer::PrintSuffixComments(const ParseNode* node) {
344   if (node->comments() && !node->comments()->suffix().empty()) {
345     Print("  ");
346     stack_.push_back(IndentState(CurrentColumn(), false, false));
347     PrintTrailingCommentsWrapped(node->comments()->suffix());
348     stack_.pop_back();
349   }
350 }
351 
FlushComments()352 void Printer::FlushComments() {
353   if (!comments_.empty()) {
354     Print("  ");
355     // Save the margin, and temporarily set it to where the first comment
356     // starts so that multiple suffix comments are vertically aligned.
357     stack_.push_back(IndentState(CurrentColumn(), false, false));
358     PrintTrailingCommentsWrapped(comments_);
359     stack_.pop_back();
360     comments_.clear();
361   }
362 }
363 
Newline()364 void Printer::Newline() {
365   FlushComments();
366   Trim();
367   Print("\n");
368   PrintMargin();
369 }
370 
Trim()371 void Printer::Trim() {
372   size_t n = output_.size();
373   while (n > 0 && output_[n - 1] == ' ')
374     --n;
375   output_.resize(n);
376 }
377 
HaveBlankLine()378 bool Printer::HaveBlankLine() {
379   size_t n = output_.size();
380   while (n > 0 && output_[n - 1] == ' ')
381     --n;
382   return n > 2 && output_[n - 1] == '\n' && output_[n - 2] == '\n';
383 }
384 
SortIfSourcesOrDeps(const BinaryOpNode * binop)385 void Printer::SortIfSourcesOrDeps(const BinaryOpNode* binop) {
386   if (const Comments* comments = binop->comments()) {
387     const std::vector<Token>& before = comments->before();
388     if (!before.empty() && (before.front().value() == "# NOSORT" ||
389                             before.back().value() == "# NOSORT")) {
390       // Allow disabling of sort for specific actions that might be
391       // order-sensitive.
392       return;
393     }
394   }
395   const IdentifierNode* ident = binop->left()->AsIdentifier();
396   const ListNode* list = binop->right()->AsList();
397   if ((binop->op().value() == "=" || binop->op().value() == "+=" ||
398        binop->op().value() == "-=") &&
399       ident && list) {
400     const std::string_view lhs = ident->value().value();
401     if (base::EndsWith(lhs, "sources", base::CompareCase::SENSITIVE) ||
402         lhs == "public")
403       const_cast<ListNode*>(list)->SortAsStringsList();
404     else if (base::EndsWith(lhs, "deps", base::CompareCase::SENSITIVE))
405       const_cast<ListNode*>(list)->SortAsDepsList();
406   }
407 }
408 
409 template <class PARSENODE>
SortImports(std::vector<std::unique_ptr<PARSENODE>> & statements)410 void Printer::SortImports(std::vector<std::unique_ptr<PARSENODE>>& statements) {
411   // Build a set of ranges by indices of FunctionCallNode's that are imports.
412 
413   std::vector<std::vector<size_t>> import_statements;
414 
415   auto is_import = [](const PARSENODE* p) {
416     const FunctionCallNode* func_call = p->AsFunctionCall();
417     return func_call && func_call->function().value() == "import";
418   };
419 
420   std::vector<size_t> current_group;
421   for (size_t i = 0; i < statements.size(); ++i) {
422     if (is_import(statements[i].get())) {
423       if (i > 0 && (!is_import(statements[i - 1].get()) ||
424                     ShouldAddBlankLineInBetween(statements[i - 1].get(),
425                                                 statements[i].get()))) {
426         if (!current_group.empty()) {
427           import_statements.push_back(current_group);
428           current_group.clear();
429         }
430       }
431       current_group.push_back(i);
432     }
433   }
434 
435   if (!current_group.empty())
436     import_statements.push_back(current_group);
437 
438   struct CompareByImportFile {
439     bool operator()(const std::unique_ptr<PARSENODE>& a,
440                     const std::unique_ptr<PARSENODE>& b) const {
441       const auto& a_args = a->AsFunctionCall()->args()->contents();
442       const auto& b_args = b->AsFunctionCall()->args()->contents();
443       std::string_view a_name;
444       std::string_view b_name;
445       if (!a_args.empty())
446         a_name = a_args[0]->AsLiteral()->value().value();
447       if (!b_args.empty())
448         b_name = b_args[0]->AsLiteral()->value().value();
449 
450       auto is_absolute = [](std::string_view import) {
451         return import.size() >= 3 && import[0] == '"' && import[1] == '/' &&
452                import[2] == '/';
453       };
454       int a_is_rel = !is_absolute(a_name);
455       int b_is_rel = !is_absolute(b_name);
456 
457       return std::tie(a_is_rel, a_name) < std::tie(b_is_rel, b_name);
458     }
459   };
460 
461   int line_after_previous = -1;
462 
463   for (const auto& group : import_statements) {
464     size_t begin = group[0];
465     size_t end = group.back() + 1;
466 
467     // Save the original line number so that ranges can be re-assigned. They're
468     // contiguous because of the partitioning code above. Later formatting
469     // relies on correct line number to know whether to insert blank lines,
470     // which is why these need to be fixed up. Additionally, to handle multiple
471     // imports on one line, they're assigned sequential line numbers, and
472     // subsequent blocks will be gapped from them.
473     int start_line =
474         std::max(statements[begin]->GetRange().begin().line_number(),
475                  line_after_previous + 1);
476 
477     std::sort(statements.begin() + begin, statements.begin() + end,
478               CompareByImportFile());
479 
480     const PARSENODE* prev = nullptr;
481     for (size_t i = begin; i < end; ++i) {
482       const PARSENODE* node = statements[i].get();
483       int line_number =
484           prev ? prev->GetRange().end().line_number() + 1 : start_line;
485       if (node->comments() && !node->comments()->before().empty())
486         line_number++;
487       const_cast<FunctionCallNode*>(node->AsFunctionCall())
488           ->SetNewLocation(line_number);
489       prev = node;
490       line_after_previous = line_number + 1;
491     }
492   }
493 }
494 
495 namespace {
496 
SuffixCommentTreeWalk(const ParseNode * node)497 int SuffixCommentTreeWalk(const ParseNode* node) {
498   // Check all the children for suffix comments. This is conceptually simple,
499   // but ugly as there's not a generic parse tree walker. This walker goes
500   // lowest child first so that if it's valid that's returned.
501   if (!node)
502     return -1;
503 
504 #define RETURN_IF_SET(x)             \
505   if (int result = (x); result >= 0) \
506     return result;
507 
508   if (const AccessorNode* accessor = node->AsAccessor()) {
509     RETURN_IF_SET(SuffixCommentTreeWalk(accessor->subscript()));
510     RETURN_IF_SET(SuffixCommentTreeWalk(accessor->member()));
511   } else if (const BinaryOpNode* binop = node->AsBinaryOp()) {
512     RETURN_IF_SET(SuffixCommentTreeWalk(binop->right()));
513   } else if (const BlockNode* block = node->AsBlock()) {
514     RETURN_IF_SET(SuffixCommentTreeWalk(block->End()));
515   } else if (const ConditionNode* condition = node->AsConditionNode()) {
516     RETURN_IF_SET(SuffixCommentTreeWalk(condition->if_false()));
517     RETURN_IF_SET(SuffixCommentTreeWalk(condition->if_true()));
518     RETURN_IF_SET(SuffixCommentTreeWalk(condition->condition()));
519   } else if (const FunctionCallNode* func_call = node->AsFunctionCall()) {
520     RETURN_IF_SET(SuffixCommentTreeWalk(func_call->block()));
521     RETURN_IF_SET(SuffixCommentTreeWalk(func_call->args()));
522   } else if (node->AsIdentifier()) {
523     // Nothing.
524   } else if (const ListNode* list = node->AsList()) {
525     RETURN_IF_SET(SuffixCommentTreeWalk(list->End()));
526   } else if (node->AsLiteral()) {
527     // Nothing.
528   } else if (const UnaryOpNode* unaryop = node->AsUnaryOp()) {
529     RETURN_IF_SET(SuffixCommentTreeWalk(unaryop->operand()));
530   } else if (node->AsBlockComment()) {
531     // Nothing.
532   } else if (node->AsEnd()) {
533     // Nothing.
534   } else {
535     CHECK(false) << "Unhandled case in SuffixCommentTreeWalk.";
536   }
537 
538 #undef RETURN_IF_SET
539 
540   // Check this node if there are no child comments.
541   if (node->comments() && !node->comments()->suffix().empty()) {
542     return node->comments()->suffix().back().location().line_number();
543   }
544 
545   return -1;
546 };
547 
548 // If there are suffix comments on the first node or its children, they might
549 // carry down multiple lines. Otherwise, use the node's normal end range. This
550 // function is needed because the parse tree doesn't include comments in the
551 // location ranges, and it's not a straightforword change to add them. So this
552 // is effectively finding the "real" range for |root| including suffix comments.
553 // Note that it's not enough to simply look at |root|'s suffix comments because
554 // in the case of:
555 //
556 //   a =
557 //       b + c  # something
558 //              # or other
559 //   x = y
560 //
561 // the comments are attached to a BinOp+ which is a child of BinOp=, not
562 // directly to the BinOp= which will be what's being used to determine if there
563 // should be a blank line inserted before the |x| line.
FindLowestSuffixComment(const ParseNode * root)564 int FindLowestSuffixComment(const ParseNode* root) {
565   LocationRange range = root->GetRange();
566   int end = range.end().line_number();
567   int result = SuffixCommentTreeWalk(root);
568   return (result == -1 || result < end) ? end : result;
569 }
570 
571 }  // namespace
572 
ShouldAddBlankLineInBetween(const ParseNode * a,const ParseNode * b)573 bool Printer::ShouldAddBlankLineInBetween(const ParseNode* a,
574                                           const ParseNode* b) {
575   LocationRange b_range = b->GetRange();
576   int a_end = FindLowestSuffixComment(a);
577 
578   // If they're already separated by 1 or more lines, then we want to keep a
579   // blank line.
580   return (b_range.begin().line_number() > a_end + 1) ||
581          // Always put a blank line before a block comment.
582          b->AsBlockComment();
583 }
584 
CurrentColumn() const585 int Printer::CurrentColumn() const {
586   int n = 0;
587   while (n < static_cast<int>(output_.size()) &&
588          output_[output_.size() - 1 - n] != '\n') {
589     ++n;
590   }
591   return n;
592 }
593 
CurrentLine() const594 int Printer::CurrentLine() const {
595   int count = 1;
596   for (const char* p = output_.c_str(); (p = strchr(p, '\n')) != nullptr;) {
597     ++count;
598     ++p;
599   }
600   return count;
601 }
602 
Block(const ParseNode * root)603 void Printer::Block(const ParseNode* root) {
604   const BlockNode* block = root->AsBlock();
605 
606   if (block->comments()) {
607     for (const auto& c : block->comments()->before()) {
608       TrimAndPrintToken(c);
609       Newline();
610     }
611   }
612 
613   SortImports(const_cast<std::vector<std::unique_ptr<ParseNode>>&>(
614       block->statements()));
615 
616   size_t i = 0;
617   for (const auto& stmt : block->statements()) {
618     Expr(stmt.get(), kPrecedenceLowest, std::string());
619     Newline();
620     if (stmt->comments()) {
621       // Why are before() not printed here too? before() are handled inside
622       // Expr(), as are suffix() which are queued to the next Newline().
623       // However, because it's a general expression handler, it doesn't insert
624       // the newline itself, which only happens between block statements. So,
625       // the after are handled explicitly here.
626       for (const auto& c : stmt->comments()->after()) {
627         TrimAndPrintToken(c);
628         Newline();
629       }
630     }
631     if (i < block->statements().size() - 1 &&
632         (ShouldAddBlankLineInBetween(block->statements()[i].get(),
633                                      block->statements()[i + 1].get()))) {
634       Newline();
635     }
636     ++i;
637   }
638 
639   if (block->comments()) {
640     if (!block->statements().empty() &&
641         block->statements().back()->AsBlockComment()) {
642       // If the block ends in a comment, and there's a comment following it,
643       // then the two comments were originally separate, so keep them that way.
644       Newline();
645     }
646     for (const auto& c : block->comments()->after()) {
647       TrimAndPrintToken(c);
648       Newline();
649     }
650   }
651 }
652 
AssessPenalty(const std::string & output)653 int Printer::AssessPenalty(const std::string& output) {
654   int penalty = 0;
655   std::vector<std::string> lines = base::SplitString(
656       output, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
657   penalty += static_cast<int>(lines.size() - 1) * GetPenaltyForLineBreak();
658   for (const auto& line : lines) {
659     if (line.size() > kMaximumWidth)
660       penalty += static_cast<int>(line.size() - kMaximumWidth) * kPenaltyExcess;
661   }
662   return penalty;
663 }
664 
ExceedsMaximumWidth(const std::string & output)665 bool Printer::ExceedsMaximumWidth(const std::string& output) {
666   for (const auto& line : base::SplitString(output, "\n", base::KEEP_WHITESPACE,
667                                             base::SPLIT_WANT_ALL)) {
668     std::string_view trimmed =
669         TrimString(line, " ", base::TrimPositions::TRIM_TRAILING);
670     if (trimmed.size() > kMaximumWidth) {
671       return true;
672     }
673   }
674   return false;
675 }
676 
AddParen(int prec,int outer_prec,bool * parenthesized)677 void Printer::AddParen(int prec, int outer_prec, bool* parenthesized) {
678   if (prec < outer_prec) {
679     Print("(");
680     *parenthesized = true;
681   }
682 }
683 
Expr(const ParseNode * root,int outer_prec,const std::string & suffix)684 int Printer::Expr(const ParseNode* root,
685                   int outer_prec,
686                   const std::string& suffix) {
687   std::string at_end = suffix;
688   int penalty = 0;
689   penalty_depth_++;
690 
691   if (root->comments()) {
692     if (!root->comments()->before().empty()) {
693       Trim();
694       // If there's already other text on the line, start a new line.
695       if (CurrentColumn() > 0)
696         Print("\n");
697       // We're printing a line comment, so we need to be at the current margin.
698       PrintMargin();
699       for (const auto& c : root->comments()->before()) {
700         TrimAndPrintToken(c);
701         Newline();
702       }
703     }
704   }
705 
706   bool parenthesized = false;
707 
708   if (const AccessorNode* accessor = root->AsAccessor()) {
709     AddParen(kPrecedenceSuffix, outer_prec, &parenthesized);
710     Print(accessor->base().value());
711     if (accessor->member()) {
712       Print(".");
713       Expr(accessor->member(), kPrecedenceLowest, std::string());
714     } else {
715       CHECK(accessor->subscript());
716       Print("[");
717       Expr(accessor->subscript(), kPrecedenceLowest, "]");
718     }
719   } else if (const BinaryOpNode* binop = root->AsBinaryOp()) {
720     CHECK(precedence_.find(binop->op().value()) != precedence_.end());
721 
722     SortIfSourcesOrDeps(binop);
723 
724     Precedence prec = precedence_[binop->op().value()];
725 
726     // Since binary operators format left-to-right, it is ok for the left side
727     // use the same operator without parentheses, so the left uses prec. For the
728     // same reason, the right side cannot reuse the same operator, or else "x +
729     // (y + z)" would format as "x + y + z" which means "(x + y) + z". So, treat
730     // the right expression as appearing one precedence level higher.
731     // However, because the source parens are not in the parse tree, as a
732     // special case for && and || we insert strictly-redundant-but-helpful-for-
733     // human-readers parentheses.
734     int prec_left = prec;
735     int prec_right = prec + 1;
736     if (binop->op().value() == "&&" && stack_.back().parent_is_boolean_or) {
737       Print("(");
738       parenthesized = true;
739     } else {
740       AddParen(prec_left, outer_prec, &parenthesized);
741     }
742 
743     int start_line = CurrentLine();
744     int start_column = CurrentColumn();
745     bool is_assignment = binop->op().value() == "=" ||
746                          binop->op().value() == "+=" ||
747                          binop->op().value() == "-=";
748 
749     int indent_column = start_column;
750     if (is_assignment) {
751       // Default to a double-indent for wrapped assignments.
752       indent_column = margin() + kIndentSize * 2;
753 
754       // A special case for the long lists and scope assignments that are
755       // common in .gn files, don't indent them + 4, even though they're just
756       // continuations when they're simple lists like "x = [ a, b, c, ... ]" or
757       // scopes like "x = { a = 1 b = 2 }". Put back to "normal" indenting.
758       if (const ListNode* right_as_list = binop->right()->AsList()) {
759         if (ListWillBeMultiline(right_as_list->contents(),
760                                 right_as_list->End()))
761           indent_column = start_column;
762       } else {
763         if (binop->right()->AsBlock())
764           indent_column = start_column;
765       }
766     }
767     if (stack_.back().continuation_requires_indent)
768       indent_column += kIndentSize * 2;
769 
770     stack_.push_back(IndentState(indent_column,
771                                  stack_.back().continuation_requires_indent,
772                                  binop->op().value() == "||"));
773     Printer sub_left;
774     InitializeSub(&sub_left);
775     sub_left.Expr(binop->left(), prec_left,
776                   std::string(" ") + std::string(binop->op().value()));
777     bool left_is_multiline = CountLines(sub_left.String()) > 1;
778     // Avoid walking the whole left redundantly times (see timing of Format.046)
779     // so pull the output and comments from subprinter.
780     Print(sub_left.String().substr(start_column));
781     std::copy(sub_left.comments_.begin(), sub_left.comments_.end(),
782               std::back_inserter(comments_));
783 
784     // Single line.
785     Printer sub1;
786     InitializeSub(&sub1);
787     sub1.Print(" ");
788     int penalty_current_line =
789         sub1.Expr(binop->right(), prec_right, std::string());
790     sub1.Print(suffix);
791     sub1.PrintSuffixComments(root);
792     sub1.FlushComments();
793     penalty_current_line += AssessPenalty(sub1.String());
794     if (!is_assignment && left_is_multiline) {
795       // In e.g. xxx + yyy, if xxx is already multiline, then we want a penalty
796       // for trying to continue as if this were one line.
797       penalty_current_line +=
798           (CountLines(sub1.String()) - 1) * kPenaltyBrokenLineOnOneLiner;
799     }
800 
801     // Break after operator.
802     Printer sub2;
803     InitializeSub(&sub2);
804     sub2.Newline();
805     int penalty_next_line =
806         sub2.Expr(binop->right(), prec_right, std::string());
807     sub2.Print(suffix);
808     sub2.PrintSuffixComments(root);
809     sub2.FlushComments();
810     penalty_next_line += AssessPenalty(sub2.String());
811 
812     // Force a list on the RHS that would normally be a single line into
813     // multiline.
814     bool tried_rhs_multiline = false;
815     Printer sub3;
816     InitializeSub(&sub3);
817     int penalty_multiline_rhs_list = std::numeric_limits<int>::max();
818     const ListNode* rhs_list = binop->right()->AsList();
819     if (is_assignment && rhs_list &&
820         !ListWillBeMultiline(rhs_list->contents(), rhs_list->End())) {
821       sub3.Print(" ");
822       sub3.stack_.push_back(IndentState(start_column, false, false));
823       sub3.Sequence(kSequenceStyleList, rhs_list->contents(), rhs_list->End(),
824                     true);
825       sub3.PrintSuffixComments(root);
826       sub3.FlushComments();
827       sub3.stack_.pop_back();
828       penalty_multiline_rhs_list = AssessPenalty(sub3.String());
829       tried_rhs_multiline = true;
830     }
831 
832     // If in all cases it was forced past 80col, then we don't break to avoid
833     // breaking after '=' in the case of:
834     //   variable = "... very long string ..."
835     // as breaking and indenting doesn't make things much more readable, even
836     // though there's fewer characters past the maximum width.
837     bool exceeds_maximum_all_ways =
838         ExceedsMaximumWidth(sub1.String()) &&
839         ExceedsMaximumWidth(sub2.String()) &&
840         (!tried_rhs_multiline || ExceedsMaximumWidth(sub3.String()));
841 
842     if (penalty_current_line < penalty_next_line || exceeds_maximum_all_ways) {
843       Print(" ");
844       Expr(binop->right(), prec_right, std::string());
845     } else if (tried_rhs_multiline &&
846                penalty_multiline_rhs_list < penalty_next_line) {
847       // Force a multiline list on the right.
848       Print(" ");
849       stack_.push_back(IndentState(start_column, false, false));
850       Sequence(kSequenceStyleList, rhs_list->contents(), rhs_list->End(), true);
851       stack_.pop_back();
852     } else {
853       // Otherwise, put first argument and op, and indent next.
854       Newline();
855       penalty += std::abs(CurrentColumn() - start_column) *
856                  kPenaltyHorizontalSeparation;
857       Expr(binop->right(), prec_right, std::string());
858     }
859     stack_.pop_back();
860     penalty += (CurrentLine() - start_line) * GetPenaltyForLineBreak();
861   } else if (const BlockNode* block = root->AsBlock()) {
862     Sequence(kSequenceStyleBracedBlock, block->statements(), block->End(),
863              false);
864   } else if (const ConditionNode* condition = root->AsConditionNode()) {
865     Print("if (");
866     Expr(condition->condition(), kPrecedenceLowest, ") {");
867     Sequence(kSequenceStyleBracedBlockAlreadyOpen,
868              condition->if_true()->statements(), condition->if_true()->End(),
869              false);
870     if (condition->if_false()) {
871       Print(" else ");
872       // If it's a block it's a bare 'else', otherwise it's an 'else if'. See
873       // ConditionNode::Execute.
874       bool is_else_if = condition->if_false()->AsBlock() == nullptr;
875       if (is_else_if) {
876         Expr(condition->if_false(), kPrecedenceLowest, std::string());
877       } else {
878         Sequence(kSequenceStyleBracedBlock,
879                  condition->if_false()->AsBlock()->statements(),
880                  condition->if_false()->AsBlock()->End(), false);
881       }
882     }
883   } else if (const FunctionCallNode* func_call = root->AsFunctionCall()) {
884     penalty += FunctionCall(func_call, at_end);
885     at_end = "";
886   } else if (const IdentifierNode* identifier = root->AsIdentifier()) {
887     Print(identifier->value().value());
888   } else if (const ListNode* list = root->AsList()) {
889     Sequence(kSequenceStyleList, list->contents(), list->End(),
890              /*force_multiline=*/false);
891   } else if (const LiteralNode* literal = root->AsLiteral()) {
892     Print(literal->value().value());
893   } else if (const UnaryOpNode* unaryop = root->AsUnaryOp()) {
894     Print(unaryop->op().value());
895     Expr(unaryop->operand(), kPrecedenceUnary, std::string());
896   } else if (const BlockCommentNode* block_comment = root->AsBlockComment()) {
897     Print(block_comment->comment().value());
898   } else if (const EndNode* end = root->AsEnd()) {
899     Print(end->value().value());
900   } else {
901     CHECK(false) << "Unhandled case in Expr.";
902   }
903 
904   if (parenthesized)
905     Print(")");
906 
907   // Defer any end of line comment until we reach the newline.
908   if (root->comments() && !root->comments()->suffix().empty()) {
909     std::copy(root->comments()->suffix().begin(),
910               root->comments()->suffix().end(), std::back_inserter(comments_));
911   }
912 
913   Print(at_end);
914 
915   penalty_depth_--;
916   return penalty;
917 }
918 
919 template <class PARSENODE>
Sequence(SequenceStyle style,const std::vector<std::unique_ptr<PARSENODE>> & list,const ParseNode * end,bool force_multiline)920 void Printer::Sequence(SequenceStyle style,
921                        const std::vector<std::unique_ptr<PARSENODE>>& list,
922                        const ParseNode* end,
923                        bool force_multiline) {
924   if (style == kSequenceStyleList) {
925     Print("[");
926   } else if (style == kSequenceStyleBracedBlock) {
927     Print("{");
928   } else if (style == kSequenceStyleBracedBlockAlreadyOpen) {
929     style = kSequenceStyleBracedBlock;
930   }
931 
932   if (style == kSequenceStyleBracedBlock) {
933     force_multiline = true;
934     SortImports(const_cast<std::vector<std::unique_ptr<PARSENODE>>&>(list));
935   }
936 
937   force_multiline |= ListWillBeMultiline(list, end);
938 
939   if (list.size() == 0 && !force_multiline) {
940     // No elements, and not forcing newlines, print nothing.
941   } else if (list.size() == 1 && !force_multiline) {
942     Print(" ");
943     Expr(list[0].get(), kPrecedenceLowest, std::string());
944     CHECK(!list[0]->comments() || list[0]->comments()->after().empty());
945     Print(" ");
946   } else {
947     stack_.push_back(IndentState(margin() + kIndentSize,
948                                  style == kSequenceStyleList, false));
949     size_t i = 0;
950     for (const auto& x : list) {
951       Newline();
952       // If:
953       // - we're going to output some comments, and;
954       // - we haven't just started this multiline list, and;
955       // - there isn't already a blank line here;
956       // Then: insert one.
957       if (i != 0 && x->comments() && !x->comments()->before().empty() &&
958           !HaveBlankLine()) {
959         Newline();
960       }
961       bool body_of_list = i < list.size() - 1 || style == kSequenceStyleList;
962       bool want_comma =
963           body_of_list && (style == kSequenceStyleList && !x->AsBlockComment());
964       Expr(x.get(), kPrecedenceLowest, want_comma ? "," : std::string());
965       CHECK(!x->comments() || x->comments()->after().empty());
966       if (body_of_list) {
967         if (i < list.size() - 1 &&
968             ShouldAddBlankLineInBetween(list[i].get(), list[i + 1].get()))
969           Newline();
970       }
971       ++i;
972     }
973 
974     // Trailing comments.
975     if (end->comments() && !end->comments()->before().empty()) {
976       if (list.size() >= 2)
977         Newline();
978       for (const auto& c : end->comments()->before()) {
979         Newline();
980         TrimAndPrintToken(c);
981       }
982     }
983 
984     stack_.pop_back();
985     Newline();
986   }
987 
988   // Defer any end of line comment until we reach the newline.
989   if (end->comments() && !end->comments()->suffix().empty()) {
990     std::copy(end->comments()->suffix().begin(),
991               end->comments()->suffix().end(), std::back_inserter(comments_));
992   }
993 
994   if (style == kSequenceStyleList)
995     Print("]");
996   else if (style == kSequenceStyleBracedBlock)
997     Print("}");
998 }
999 
FunctionCall(const FunctionCallNode * func_call,const std::string & suffix)1000 int Printer::FunctionCall(const FunctionCallNode* func_call,
1001                           const std::string& suffix) {
1002   int start_line = CurrentLine();
1003   int start_column = CurrentColumn();
1004   Print(func_call->function().value());
1005   Print("(");
1006 
1007   bool have_block = func_call->block() != nullptr;
1008   bool force_multiline = false;
1009 
1010   const auto& list = func_call->args()->contents();
1011   const ParseNode* end = func_call->args()->End();
1012 
1013   if (end->comments() && !end->comments()->before().empty())
1014     force_multiline = true;
1015 
1016   // If there's before line comments, make sure we have a place to put them.
1017   for (const auto& i : list) {
1018     if (i->comments() && !i->comments()->before().empty())
1019       force_multiline = true;
1020   }
1021 
1022   // Calculate the penalties for 3 possible layouts:
1023   // 1. all on same line;
1024   // 2. starting on same line, broken at each comma but paren aligned;
1025   // 3. broken to next line + 4, broken at each comma.
1026   std::string terminator = ")";
1027   if (have_block)
1028     terminator += " {";
1029   terminator += suffix;
1030 
1031   // Special case to make function calls of one arg taking a long list of
1032   // boolean operators not indent.
1033   bool continuation_requires_indent =
1034       list.size() != 1 || !list[0]->AsBinaryOp();
1035 
1036   // 1: Same line.
1037   Printer sub1;
1038   InitializeSub(&sub1);
1039   sub1.stack_.push_back(
1040       IndentState(CurrentColumn(), continuation_requires_indent, false));
1041   int penalty_one_line = 0;
1042   for (size_t i = 0; i < list.size(); ++i) {
1043     penalty_one_line += sub1.Expr(list[i].get(), kPrecedenceLowest,
1044                                   i < list.size() - 1 ? ", " : std::string());
1045   }
1046   sub1.Print(terminator);
1047   penalty_one_line += AssessPenalty(sub1.String());
1048   // This extra penalty prevents a short second argument from being squeezed in
1049   // after a first argument that went multiline (and instead preferring a
1050   // variant below).
1051   penalty_one_line +=
1052       (CountLines(sub1.String()) - 1) * kPenaltyBrokenLineOnOneLiner;
1053 
1054   // 2: Starting on same line, broken at commas.
1055   Printer sub2;
1056   InitializeSub(&sub2);
1057   sub2.stack_.push_back(
1058       IndentState(CurrentColumn(), continuation_requires_indent, false));
1059   int penalty_multiline_start_same_line = 0;
1060   for (size_t i = 0; i < list.size(); ++i) {
1061     penalty_multiline_start_same_line +=
1062         sub2.Expr(list[i].get(), kPrecedenceLowest,
1063                   i < list.size() - 1 ? "," : std::string());
1064     if (i < list.size() - 1) {
1065       sub2.Newline();
1066     }
1067   }
1068   sub2.Print(terminator);
1069   penalty_multiline_start_same_line += AssessPenalty(sub2.String());
1070 
1071   // 3: Starting on next line, broken at commas.
1072   Printer sub3;
1073   InitializeSub(&sub3);
1074   sub3.stack_.push_back(IndentState(margin() + kIndentSize * 2,
1075                                     continuation_requires_indent, false));
1076   sub3.Newline();
1077   int penalty_multiline_start_next_line = 0;
1078   for (size_t i = 0; i < list.size(); ++i) {
1079     if (i == 0) {
1080       penalty_multiline_start_next_line +=
1081           std::abs(sub3.CurrentColumn() - start_column) *
1082           kPenaltyHorizontalSeparation;
1083     }
1084     penalty_multiline_start_next_line +=
1085         sub3.Expr(list[i].get(), kPrecedenceLowest,
1086                   i < list.size() - 1 ? "," : std::string());
1087     if (i < list.size() - 1) {
1088       sub3.Newline();
1089     }
1090   }
1091   sub3.Print(terminator);
1092   penalty_multiline_start_next_line += AssessPenalty(sub3.String());
1093 
1094   int penalty = penalty_multiline_start_next_line;
1095   bool fits_on_current_line = false;
1096   if (penalty_one_line < penalty_multiline_start_next_line ||
1097       penalty_multiline_start_same_line < penalty_multiline_start_next_line) {
1098     fits_on_current_line = true;
1099     penalty = penalty_one_line;
1100     if (penalty_multiline_start_same_line < penalty_one_line) {
1101       penalty = penalty_multiline_start_same_line;
1102       force_multiline = true;
1103     }
1104   } else {
1105     force_multiline = true;
1106   }
1107 
1108   if (list.size() == 0 && !force_multiline) {
1109     // No elements, and not forcing newlines, print nothing.
1110   } else {
1111     if (penalty_multiline_start_next_line < penalty_multiline_start_same_line) {
1112       stack_.push_back(IndentState(margin() + kIndentSize * 2,
1113                                    continuation_requires_indent, false));
1114       Newline();
1115     } else {
1116       stack_.push_back(
1117           IndentState(CurrentColumn(), continuation_requires_indent, false));
1118     }
1119 
1120     for (size_t i = 0; i < list.size(); ++i) {
1121       const auto& x = list[i];
1122       if (i > 0) {
1123         if (fits_on_current_line && !force_multiline)
1124           Print(" ");
1125         else
1126           Newline();
1127       }
1128       bool want_comma = i < list.size() - 1 && !x->AsBlockComment();
1129       Expr(x.get(), kPrecedenceLowest, want_comma ? "," : std::string());
1130       CHECK(!x->comments() || x->comments()->after().empty());
1131       if (i < list.size() - 1) {
1132         if (!want_comma)
1133           Newline();
1134       }
1135     }
1136 
1137     // Trailing comments.
1138     if (end->comments() && !end->comments()->before().empty()) {
1139       if (!list.empty())
1140         Newline();
1141       for (const auto& c : end->comments()->before()) {
1142         Newline();
1143         TrimAndPrintToken(c);
1144       }
1145       Newline();
1146     }
1147     stack_.pop_back();
1148   }
1149 
1150   // Defer any end of line comment until we reach the newline.
1151   if (end->comments() && !end->comments()->suffix().empty()) {
1152     std::copy(end->comments()->suffix().begin(),
1153               end->comments()->suffix().end(), std::back_inserter(comments_));
1154   }
1155 
1156   Print(")");
1157   Print(suffix);
1158 
1159   if (have_block) {
1160     Print(" ");
1161     Sequence(kSequenceStyleBracedBlock, func_call->block()->statements(),
1162              func_call->block()->End(), false);
1163   }
1164   return penalty + (CurrentLine() - start_line) * GetPenaltyForLineBreak();
1165 }
1166 
InitializeSub(Printer * sub)1167 void Printer::InitializeSub(Printer* sub) {
1168   sub->stack_ = stack_;
1169   sub->comments_ = comments_;
1170   sub->penalty_depth_ = penalty_depth_;
1171   sub->Print(std::string(CurrentColumn(), 'x'));
1172 }
1173 
1174 template <class PARSENODE>
ListWillBeMultiline(const std::vector<std::unique_ptr<PARSENODE>> & list,const ParseNode * end)1175 bool Printer::ListWillBeMultiline(
1176     const std::vector<std::unique_ptr<PARSENODE>>& list,
1177     const ParseNode* end) {
1178   if (list.size() > 1)
1179     return true;
1180 
1181   if (end && end->comments() && !end->comments()->before().empty())
1182     return true;
1183 
1184   // If there's before or suffix line comments, make sure we have a place to put
1185   // them.
1186   for (const auto& i : list) {
1187     if (i->comments() && (!i->comments()->before().empty() ||
1188                           !i->comments()->suffix().empty())) {
1189       return true;
1190     }
1191   }
1192 
1193   // When a scope is used as a list entry, it's too complicated to go one a
1194   // single line (the block will always be formatted multiline itself).
1195   if (list.size() >= 1 && list[0]->AsBlock())
1196     return true;
1197 
1198   return false;
1199 }
1200 
DoFormat(const ParseNode * root,TreeDumpMode dump_tree,std::string * output)1201 void DoFormat(const ParseNode* root,
1202               TreeDumpMode dump_tree,
1203               std::string* output) {
1204 #if defined(OS_WIN)
1205     // Set stderr to binary mode to prevent converting newlines to \r\n.
1206     _setmode(_fileno(stderr), _O_BINARY);
1207 #endif
1208 
1209   if (dump_tree == TreeDumpMode::kPlainText) {
1210     std::ostringstream os;
1211     RenderToText(root->GetJSONNode(), 0, os);
1212     fprintf(stderr, "%s", os.str().c_str());
1213   } else if (dump_tree == TreeDumpMode::kJSON) {
1214     std::string os;
1215     base::JSONWriter::WriteWithOptions(
1216         root->GetJSONNode(), base::JSONWriter::OPTIONS_PRETTY_PRINT, &os);
1217     fprintf(stderr, "%s", os.c_str());
1218   }
1219 
1220   Printer pr;
1221   pr.Block(root);
1222   *output = pr.String();
1223 }
1224 
1225 }  // namespace
1226 
FormatStringToString(const std::string & input,TreeDumpMode dump_tree,std::string * output)1227 bool FormatStringToString(const std::string& input,
1228                           TreeDumpMode dump_tree,
1229                           std::string* output) {
1230   SourceFile source_file;
1231   InputFile file(source_file);
1232   file.SetContents(input);
1233   Err err;
1234   // Tokenize.
1235   std::vector<Token> tokens =
1236       Tokenizer::Tokenize(&file, &err, WhitespaceTransform::kInvalidToSpace);
1237   if (err.has_error()) {
1238     err.PrintToStdout();
1239     return false;
1240   }
1241 
1242   // Parse.
1243   std::unique_ptr<ParseNode> parse_node = Parser::Parse(tokens, &err);
1244   if (err.has_error()) {
1245     err.PrintToStdout();
1246     return false;
1247   }
1248 
1249   DoFormat(parse_node.get(), dump_tree, output);
1250   return true;
1251 }
1252 
RunFormat(const std::vector<std::string> & args)1253 int RunFormat(const std::vector<std::string>& args) {
1254   bool dry_run =
1255       base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchDryRun);
1256   TreeDumpMode dump_tree = TreeDumpMode::kInactive;
1257   if (base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchDumpTree)) {
1258     std::string tree_type =
1259         base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
1260             kSwitchDumpTree);
1261     if (tree_type == kSwitchDumpTreeJSON) {
1262       dump_tree = TreeDumpMode::kJSON;
1263     } else if (tree_type.empty() || tree_type == kSwitchDumpTreeText) {
1264       dump_tree = TreeDumpMode::kPlainText;
1265     } else {
1266       Err(Location(), tree_type +
1267                           " is an invalid value for --dump-tree. Specify "
1268                           "\"" +
1269                           kSwitchDumpTreeText + "\" or \"" +
1270                           kSwitchDumpTreeJSON + "\".\n")
1271           .PrintToStdout();
1272       return 1;
1273     }
1274   }
1275   bool from_stdin =
1276       base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchStdin);
1277 
1278   if (dry_run) {
1279     // --dry-run only works with an actual file to compare to.
1280     from_stdin = false;
1281   }
1282 
1283   bool quiet =
1284       base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kQuiet);
1285 
1286   if (from_stdin) {
1287     if (args.size() != 0) {
1288       Err(Location(), "Expecting no arguments when reading from stdin.\n")
1289           .PrintToStdout();
1290       return 1;
1291     }
1292     std::string input = ReadStdin();
1293     std::string output;
1294     if (!FormatStringToString(input, dump_tree, &output))
1295       return 1;
1296 #if defined(OS_WIN)
1297     // Set stdout to binary mode to prevent converting newlines to \r\n.
1298     _setmode(_fileno(stdout), _O_BINARY);
1299 #endif
1300     printf("%s", output.c_str());
1301     return 0;
1302   }
1303 
1304   if (args.size() == 0) {
1305     Err(Location(), "Expecting one or more arguments, see `gn help format`.\n")
1306         .PrintToStdout();
1307     return 1;
1308   }
1309 
1310   Setup setup;
1311   SourceDir source_dir =
1312       SourceDirForCurrentDirectory(setup.build_settings().root_path());
1313 
1314   // TODO(scottmg): Eventually, this list of files should be processed in
1315   // parallel.
1316   for (const auto& arg : args) {
1317     Err err;
1318     SourceFile file = source_dir.ResolveRelativeFile(Value(nullptr, arg), &err);
1319     if (err.has_error()) {
1320       err.PrintToStdout();
1321       return 1;
1322     }
1323 
1324     base::FilePath to_format = setup.build_settings().GetFullPath(file);
1325     std::string original_contents;
1326     if (!base::ReadFileToString(to_format, &original_contents)) {
1327       Err(Location(),
1328           std::string("Couldn't read \"") + FilePathToUTF8(to_format))
1329           .PrintToStdout();
1330       return 1;
1331     }
1332 
1333     std::string output_string;
1334     if (!FormatStringToString(original_contents, dump_tree, &output_string)) {
1335       return 1;
1336     }
1337     if (dump_tree == TreeDumpMode::kInactive) {
1338       // Update the file in-place.
1339       if (dry_run)
1340         return original_contents == output_string ? 0 : 2;
1341       if (original_contents != output_string) {
1342         if (base::WriteFile(to_format, output_string.data(),
1343                             static_cast<int>(output_string.size())) == -1) {
1344           Err(Location(),
1345               std::string("Failed to write formatted output back to \"") +
1346                   FilePathToUTF8(to_format) + std::string("\"."))
1347               .PrintToStdout();
1348           return 1;
1349         }
1350         if (!quiet) {
1351           printf("Wrote formatted to '%s'.\n",
1352                  FilePathToUTF8(to_format).c_str());
1353         }
1354       }
1355     }
1356   }
1357 
1358   return 0;
1359 }
1360 
1361 }  // namespace commands
1362