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