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