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