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