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