gn: SourceDirForCurrentDirectory returns absolute on empty source_root
[chromium-blink-merge.git] / tools / gn / command_format.cc
blob75fdfb6a2271f48cccab79551d7d9107f7658a07
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.
5 #include <sstream>
7 #include "base/command_line.h"
8 #include "base/files/file_util.h"
9 #include "base/strings/string_split.h"
10 #include "tools/gn/commands.h"
11 #include "tools/gn/filesystem_utils.h"
12 #include "tools/gn/input_file.h"
13 #include "tools/gn/parser.h"
14 #include "tools/gn/scheduler.h"
15 #include "tools/gn/setup.h"
16 #include "tools/gn/source_file.h"
17 #include "tools/gn/tokenizer.h"
19 namespace commands {
21 const char kSwitchDumpTree[] = "dump-tree";
22 const char kSwitchInPlace[] = "in-place";
23 const char kSwitchStdin[] = "stdin";
25 const char kFormat[] = "format";
26 const char kFormat_HelpShort[] =
27 "format: Format .gn file.";
28 const char kFormat_Help[] =
29 "gn format [--dump-tree] [--in-place] [--stdin] BUILD.gn\n"
30 "\n"
31 " Formats .gn file to a standard format.\n"
32 "\n"
33 "Arguments\n"
34 " --dump-tree\n"
35 " For debugging only, dumps the parse tree.\n"
36 "\n"
37 " --in-place\n"
38 " Instead of writing the formatted file to stdout, replace the input\n"
39 " file with the formatted output. If no reformatting is required,\n"
40 " the input file will not be touched, and nothing printed.\n"
41 "\n"
42 " --stdin\n"
43 " Read input from stdin (and write to stdout). Not compatible with\n"
44 " --in-place of course.\n"
45 "\n"
46 "Examples\n"
47 " gn format //some/BUILD.gn\n"
48 " gn format some\\BUILD.gn\n"
49 " gn format /abspath/some/BUILD.gn\n"
50 " gn format --stdin\n";
52 namespace {
54 const int kIndentSize = 2;
55 const int kMaximumWidth = 80;
57 const int kPenaltyLineBreak = 500;
58 const int kPenaltyHorizontalSeparation = 100;
59 const int kPenaltyExcess = 10000;
60 const int kPenaltyBrokenLineOnOneLiner = 5000;
62 enum Precedence {
63 kPrecedenceLowest,
64 kPrecedenceAssign,
65 kPrecedenceOr,
66 kPrecedenceAnd,
67 kPrecedenceCompare,
68 kPrecedenceAdd,
69 kPrecedenceUnary,
70 kPrecedenceSuffix,
73 int CountLines(const std::string& str) {
74 std::vector<std::string> lines;
75 base::SplitStringDontTrim(str, '\n', &lines);
76 return static_cast<int>(lines.size());
79 class Printer {
80 public:
81 Printer();
82 ~Printer();
84 void Block(const ParseNode* file);
86 std::string String() const { return output_; }
88 private:
89 // Format a list of values using the given style.
90 enum SequenceStyle {
91 kSequenceStyleList,
92 kSequenceStyleBlock,
93 kSequenceStyleBracedBlock,
96 struct Metrics {
97 Metrics() : first_length(-1), longest_length(-1), multiline(false) {}
98 int first_length;
99 int longest_length;
100 bool multiline;
103 // Add to output.
104 void Print(base::StringPiece str);
106 // Add the current margin (as spaces) to the output.
107 void PrintMargin();
109 void TrimAndPrintToken(const Token& token);
111 // End the current line, flushing end of line comments.
112 void Newline();
114 // Remove trailing spaces from the current line.
115 void Trim();
117 // Whether there's a blank separator line at the current position.
118 bool HaveBlankLine();
120 // Flag assignments to sources, deps, etc. to make their RHSs multiline.
121 void AnnotatePreferedMultilineAssignment(const BinaryOpNode* binop);
123 // Heuristics to decide if there should be a blank line added between two
124 // items. For various "small" items, it doesn't look nice if there's too much
125 // vertical whitespace added.
126 bool ShouldAddBlankLineInBetween(const ParseNode* a, const ParseNode* b);
128 // Get the 0-based x position on the current line.
129 int CurrentColumn() const;
131 // Get the current line in the output;
132 int CurrentLine() const;
134 // Adds an opening ( if prec is less than the outers (to maintain evalution
135 // order for a subexpression). If an opening paren is emitted, *parenthesized
136 // will be set so it can be closed at the end of the expression.
137 void AddParen(int prec, int outer_prec, bool* parenthesized);
139 // Print the expression to the output buffer. Returns the type of element
140 // added to the output. The value of outer_prec gives the precedence of the
141 // operator outside this Expr. If that operator binds tighter than root's,
142 // Expr must introduce parentheses.
143 int Expr(const ParseNode* root, int outer_prec, const std::string& suffix);
145 // Generic penalties for exceeding maximum width, adding more lines, etc.
146 int AssessPenalty(const std::string& output);
148 // Format a list of values using the given style.
149 // |end| holds any trailing comments to be printed just before the closing
150 // bracket.
151 template <class PARSENODE> // Just for const covariance.
152 void Sequence(SequenceStyle style,
153 const std::vector<PARSENODE*>& list,
154 const ParseNode* end,
155 bool force_multiline);
157 // Returns the penalty.
158 int FunctionCall(const FunctionCallNode* func_call,
159 const std::string& suffix);
161 // Create a clone of this Printer in a similar state (other than the output,
162 // but including margins, etc.) to be used for dry run measurements.
163 void InitializeSub(Printer* sub);
165 template <class PARSENODE>
166 bool ListWillBeMultiline(const std::vector<PARSENODE*>& list,
167 const ParseNode* end);
169 std::string output_; // Output buffer.
170 std::vector<Token> comments_; // Pending end-of-line comments.
171 int margin() const { return stack_.back().margin; }
173 int penalty_depth_;
174 int GetPenaltyForLineBreak() const {
175 return penalty_depth_ * kPenaltyLineBreak;
178 struct IndentState {
179 IndentState()
180 : margin(0),
181 continuation_requires_indent(false),
182 parent_is_boolean_or(false) {}
183 IndentState(int margin,
184 bool continuation_requires_indent,
185 bool parent_is_boolean_or)
186 : margin(margin),
187 continuation_requires_indent(continuation_requires_indent),
188 parent_is_boolean_or(parent_is_boolean_or) {}
190 // The left margin (number of spaces).
191 int margin;
193 bool continuation_requires_indent;
195 bool parent_is_boolean_or;
197 // Stack used to track
198 std::vector<IndentState> stack_;
200 // Gives the precedence for operators in a BinaryOpNode.
201 std::map<base::StringPiece, Precedence> precedence_;
203 DISALLOW_COPY_AND_ASSIGN(Printer);
206 Printer::Printer() : penalty_depth_(0) {
207 output_.reserve(100 << 10);
208 precedence_["="] = kPrecedenceAssign;
209 precedence_["+="] = kPrecedenceAssign;
210 precedence_["-="] = kPrecedenceAssign;
211 precedence_["||"] = kPrecedenceOr;
212 precedence_["&&"] = kPrecedenceAnd;
213 precedence_["<"] = kPrecedenceCompare;
214 precedence_[">"] = kPrecedenceCompare;
215 precedence_["=="] = kPrecedenceCompare;
216 precedence_["!="] = kPrecedenceCompare;
217 precedence_["<="] = kPrecedenceCompare;
218 precedence_[">="] = kPrecedenceCompare;
219 precedence_["+"] = kPrecedenceAdd;
220 precedence_["-"] = kPrecedenceAdd;
221 precedence_["!"] = kPrecedenceUnary;
222 stack_.push_back(IndentState());
225 Printer::~Printer() {
228 void Printer::Print(base::StringPiece str) {
229 str.AppendToString(&output_);
232 void Printer::PrintMargin() {
233 output_ += std::string(margin(), ' ');
236 void Printer::TrimAndPrintToken(const Token& token) {
237 std::string trimmed;
238 TrimWhitespaceASCII(token.value().as_string(), base::TRIM_ALL, &trimmed);
239 Print(trimmed);
242 void Printer::Newline() {
243 if (!comments_.empty()) {
244 Print(" ");
245 // Save the margin, and temporarily set it to where the first comment
246 // starts so that multiple suffix comments are vertically aligned. This
247 // will need to be fancier once we enforce 80 col.
248 stack_.push_back(IndentState(CurrentColumn(), false, false));
249 int i = 0;
250 for (const auto& c : comments_) {
251 if (i > 0) {
252 Trim();
253 Print("\n");
254 PrintMargin();
256 TrimAndPrintToken(c);
257 ++i;
259 stack_.pop_back();
260 comments_.clear();
262 Trim();
263 Print("\n");
264 PrintMargin();
267 void Printer::Trim() {
268 size_t n = output_.size();
269 while (n > 0 && output_[n - 1] == ' ')
270 --n;
271 output_.resize(n);
274 bool Printer::HaveBlankLine() {
275 size_t n = output_.size();
276 while (n > 0 && output_[n - 1] == ' ')
277 --n;
278 return n > 2 && output_[n - 1] == '\n' && output_[n - 2] == '\n';
281 void Printer::AnnotatePreferedMultilineAssignment(const BinaryOpNode* binop) {
282 const IdentifierNode* ident = binop->left()->AsIdentifier();
283 const ListNode* list = binop->right()->AsList();
284 // This is somewhat arbitrary, but we include the 'deps'- and 'sources'-like
285 // things, but not flags things.
286 if (binop->op().value() == "=" && ident && list &&
287 (ident->value().value() == "data" ||
288 ident->value().value() == "datadeps" ||
289 ident->value().value() == "deps" || ident->value().value() == "inputs" ||
290 ident->value().value() == "public" ||
291 ident->value().value() == "public_deps" ||
292 ident->value().value() == "sources")) {
293 const_cast<ListNode*>(list)->set_prefer_multiline(true);
297 bool Printer::ShouldAddBlankLineInBetween(const ParseNode* a,
298 const ParseNode* b) {
299 LocationRange a_range = a->GetRange();
300 LocationRange b_range = b->GetRange();
301 // If they're already separated by 1 or more lines, then we want to keep a
302 // blank line.
303 return b_range.begin().line_number() > a_range.end().line_number() + 1;
306 int Printer::CurrentColumn() const {
307 int n = 0;
308 while (n < static_cast<int>(output_.size()) &&
309 output_[output_.size() - 1 - n] != '\n') {
310 ++n;
312 return n;
315 int Printer::CurrentLine() const {
316 int count = 1;
317 for (const char* p = output_.c_str(); (p = strchr(p, '\n')) != NULL;) {
318 ++count;
319 ++p;
321 return count;
324 void Printer::Block(const ParseNode* root) {
325 const BlockNode* block = root->AsBlock();
327 if (block->comments()) {
328 for (const auto& c : block->comments()->before()) {
329 TrimAndPrintToken(c);
330 Newline();
334 size_t i = 0;
335 for (const auto& stmt : block->statements()) {
336 Expr(stmt, kPrecedenceLowest, std::string());
337 Newline();
338 if (stmt->comments()) {
339 // Why are before() not printed here too? before() are handled inside
340 // Expr(), as are suffix() which are queued to the next Newline().
341 // However, because it's a general expression handler, it doesn't insert
342 // the newline itself, which only happens between block statements. So,
343 // the after are handled explicitly here.
344 for (const auto& c : stmt->comments()->after()) {
345 TrimAndPrintToken(c);
346 Newline();
349 if (i < block->statements().size() - 1 &&
350 (ShouldAddBlankLineInBetween(block->statements()[i],
351 block->statements()[i + 1]))) {
352 Newline();
354 ++i;
357 if (block->comments()) {
358 for (const auto& c : block->comments()->after()) {
359 TrimAndPrintToken(c);
360 Newline();
365 int Printer::AssessPenalty(const std::string& output) {
366 int penalty = 0;
367 std::vector<std::string> lines;
368 base::SplitStringDontTrim(output, '\n', &lines);
369 penalty += static_cast<int>(lines.size() - 1) * GetPenaltyForLineBreak();
370 for (const auto& line : lines) {
371 if (line.size() > kMaximumWidth)
372 penalty += static_cast<int>(line.size() - kMaximumWidth) * kPenaltyExcess;
374 return penalty;
377 void Printer::AddParen(int prec, int outer_prec, bool* parenthesized) {
378 if (prec < outer_prec) {
379 Print("(");
380 *parenthesized = true;
384 int Printer::Expr(const ParseNode* root,
385 int outer_prec,
386 const std::string& suffix) {
387 std::string at_end = suffix;
388 int penalty = 0;
389 penalty_depth_++;
391 if (root->comments()) {
392 if (!root->comments()->before().empty()) {
393 Trim();
394 // If there's already other text on the line, start a new line.
395 if (CurrentColumn() > 0)
396 Print("\n");
397 // We're printing a line comment, so we need to be at the current margin.
398 PrintMargin();
399 for (const auto& c : root->comments()->before()) {
400 TrimAndPrintToken(c);
401 Newline();
406 bool parenthesized = false;
408 if (const AccessorNode* accessor = root->AsAccessor()) {
409 AddParen(kPrecedenceSuffix, outer_prec, &parenthesized);
410 Print(accessor->base().value());
411 if (accessor->member()) {
412 Print(".");
413 Expr(accessor->member(), kPrecedenceLowest, std::string());
414 } else {
415 CHECK(accessor->index());
416 Print("[");
417 Expr(accessor->index(), kPrecedenceLowest, "]");
419 } else if (const BinaryOpNode* binop = root->AsBinaryOp()) {
420 CHECK(precedence_.find(binop->op().value()) != precedence_.end());
421 AnnotatePreferedMultilineAssignment(binop);
423 Precedence prec = precedence_[binop->op().value()];
425 // Since binary operators format left-to-right, it is ok for the left side
426 // use the same operator without parentheses, so the left uses prec. For the
427 // same reason, the right side cannot reuse the same operator, or else "x +
428 // (y + z)" would format as "x + y + z" which means "(x + y) + z". So, treat
429 // the right expression as appearing one precedence level higher.
430 // However, because the source parens are not in the parse tree, as a
431 // special case for && and || we insert strictly-redundant-but-helpful-for-
432 // human-readers parentheses.
433 int prec_left = prec;
434 int prec_right = prec + 1;
435 if (binop->op().value() == "&&" && stack_.back().parent_is_boolean_or) {
436 Print("(");
437 parenthesized = true;
438 } else {
439 AddParen(prec_left, outer_prec, &parenthesized);
442 int start_line = CurrentLine();
443 int start_column = CurrentColumn();
444 bool is_assignment = binop->op().value() == "=" ||
445 binop->op().value() == "+=" ||
446 binop->op().value() == "-=";
447 // A sort of funny special case for the long lists that are common in .gn
448 // files, don't indent them + 4, even though they're just continuations when
449 // they're simple lists like "x = [ a, b, c, ... ]"
450 const ListNode* right_as_list = binop->right()->AsList();
451 int indent_column =
452 (is_assignment &&
453 (!right_as_list || (!right_as_list->prefer_multiline() &&
454 !ListWillBeMultiline(right_as_list->contents(),
455 right_as_list->End()))))
456 ? margin() + kIndentSize * 2
457 : start_column;
458 if (stack_.back().continuation_requires_indent)
459 indent_column += kIndentSize * 2;
461 stack_.push_back(
462 IndentState(indent_column, false, binop->op().value() == "||"));
463 Printer sub_left;
464 InitializeSub(&sub_left);
465 sub_left.Expr(binop->left(),
466 prec_left,
467 std::string(" ") + binop->op().value().as_string());
468 bool left_is_multiline = CountLines(sub_left.String()) > 1;
469 // Avoid walking the whole left redundantly times (see timing of Format.046)
470 // so pull the output and comments from subprinter.
471 Print(sub_left.String().substr(start_column));
472 std::copy(sub_left.comments_.begin(),
473 sub_left.comments_.end(),
474 std::back_inserter(comments_));
476 // Single line.
477 Printer sub1;
478 InitializeSub(&sub1);
479 sub1.Print(" ");
480 int penalty_current_line =
481 sub1.Expr(binop->right(), prec_right, std::string());
482 sub1.Print(suffix);
483 penalty_current_line += AssessPenalty(sub1.String());
484 if (!is_assignment && left_is_multiline) {
485 // In e.g. xxx + yyy, if xxx is already multiline, then we want a penalty
486 // for trying to continue as if this were one line.
487 penalty_current_line +=
488 (CountLines(sub1.String()) - 1) * kPenaltyBrokenLineOnOneLiner;
491 // Break after operator.
492 Printer sub2;
493 InitializeSub(&sub2);
494 sub2.Newline();
495 int penalty_next_line =
496 sub2.Expr(binop->right(), prec_right, std::string());
497 sub2.Print(suffix);
498 penalty_next_line += AssessPenalty(sub2.String());
500 if (penalty_current_line < penalty_next_line) {
501 Print(" ");
502 Expr(binop->right(), prec_right, std::string());
503 } else {
504 // Otherwise, put first argument and op, and indent next.
505 Newline();
506 penalty += std::abs(CurrentColumn() - start_column) *
507 kPenaltyHorizontalSeparation;
508 Expr(binop->right(), prec_right, std::string());
510 stack_.pop_back();
511 penalty += (CurrentLine() - start_line) * GetPenaltyForLineBreak();
512 } else if (const BlockNode* block = root->AsBlock()) {
513 Sequence(
514 kSequenceStyleBracedBlock, block->statements(), block->End(), false);
515 } else if (const ConditionNode* condition = root->AsConditionNode()) {
516 Print("if (");
517 // TODO(scottmg): The { needs to be included in the suffix here.
518 Expr(condition->condition(), kPrecedenceLowest, ") ");
519 Sequence(kSequenceStyleBracedBlock,
520 condition->if_true()->statements(),
521 condition->if_true()->End(),
522 false);
523 if (condition->if_false()) {
524 Print(" else ");
525 // If it's a block it's a bare 'else', otherwise it's an 'else if'. See
526 // ConditionNode::Execute.
527 bool is_else_if = condition->if_false()->AsBlock() == NULL;
528 if (is_else_if) {
529 Expr(condition->if_false(), kPrecedenceLowest, std::string());
530 } else {
531 Sequence(kSequenceStyleBracedBlock,
532 condition->if_false()->AsBlock()->statements(),
533 condition->if_false()->AsBlock()->End(),
534 false);
537 } else if (const FunctionCallNode* func_call = root->AsFunctionCall()) {
538 penalty += FunctionCall(func_call, at_end);
539 at_end = "";
540 } else if (const IdentifierNode* identifier = root->AsIdentifier()) {
541 Print(identifier->value().value());
542 } else if (const ListNode* list = root->AsList()) {
543 bool force_multiline =
544 list->prefer_multiline() && !list->contents().empty();
545 Sequence(
546 kSequenceStyleList, list->contents(), list->End(), force_multiline);
547 } else if (const LiteralNode* literal = root->AsLiteral()) {
548 Print(literal->value().value());
549 } else if (const UnaryOpNode* unaryop = root->AsUnaryOp()) {
550 Print(unaryop->op().value());
551 Expr(unaryop->operand(), kPrecedenceUnary, std::string());
552 } else if (const BlockCommentNode* block_comment = root->AsBlockComment()) {
553 Print(block_comment->comment().value());
554 } else if (const EndNode* end = root->AsEnd()) {
555 Print(end->value().value());
556 } else {
557 CHECK(false) << "Unhandled case in Expr.";
560 if (parenthesized)
561 Print(")");
563 // Defer any end of line comment until we reach the newline.
564 if (root->comments() && !root->comments()->suffix().empty()) {
565 std::copy(root->comments()->suffix().begin(),
566 root->comments()->suffix().end(),
567 std::back_inserter(comments_));
570 Print(at_end);
572 penalty_depth_--;
573 return penalty;
576 template <class PARSENODE>
577 void Printer::Sequence(SequenceStyle style,
578 const std::vector<PARSENODE*>& list,
579 const ParseNode* end,
580 bool force_multiline) {
581 if (style == kSequenceStyleList)
582 Print("[");
583 else if (style == kSequenceStyleBracedBlock)
584 Print("{");
586 if (style == kSequenceStyleBlock || style == kSequenceStyleBracedBlock)
587 force_multiline = true;
589 force_multiline |= ListWillBeMultiline(list, end);
591 if (list.size() == 0 && !force_multiline) {
592 // No elements, and not forcing newlines, print nothing.
593 } else if (list.size() == 1 && !force_multiline) {
594 Print(" ");
595 Expr(list[0], kPrecedenceLowest, std::string());
596 CHECK(!list[0]->comments() || list[0]->comments()->after().empty());
597 Print(" ");
598 } else {
599 stack_.push_back(IndentState(margin() + kIndentSize,
600 style == kSequenceStyleList,
601 false));
602 size_t i = 0;
603 for (const auto& x : list) {
604 Newline();
605 // If:
606 // - we're going to output some comments, and;
607 // - we haven't just started this multiline list, and;
608 // - there isn't already a blank line here;
609 // Then: insert one.
610 if (i != 0 && x->comments() && !x->comments()->before().empty() &&
611 !HaveBlankLine()) {
612 Newline();
614 bool body_of_list = i < list.size() - 1 || style == kSequenceStyleList;
615 bool want_comma =
616 body_of_list && (style == kSequenceStyleList && !x->AsBlockComment());
617 Expr(x, kPrecedenceLowest, want_comma ? "," : std::string());
618 CHECK(!x->comments() || x->comments()->after().empty());
619 if (body_of_list) {
620 if (!want_comma) {
621 if (i < list.size() - 1 &&
622 ShouldAddBlankLineInBetween(list[i], list[i + 1]))
623 Newline();
626 ++i;
629 // Trailing comments.
630 if (end->comments() && !end->comments()->before().empty()) {
631 if (list.size() >= 2)
632 Newline();
633 for (const auto& c : end->comments()->before()) {
634 Newline();
635 TrimAndPrintToken(c);
639 stack_.pop_back();
640 Newline();
642 // Defer any end of line comment until we reach the newline.
643 if (end->comments() && !end->comments()->suffix().empty()) {
644 std::copy(end->comments()->suffix().begin(),
645 end->comments()->suffix().end(),
646 std::back_inserter(comments_));
650 if (style == kSequenceStyleList)
651 Print("]");
652 else if (style == kSequenceStyleBracedBlock)
653 Print("}");
656 int Printer::FunctionCall(const FunctionCallNode* func_call,
657 const std::string& suffix) {
658 int start_line = CurrentLine();
659 int start_column = CurrentColumn();
660 Print(func_call->function().value());
661 Print("(");
663 bool have_block = func_call->block() != nullptr;
664 bool force_multiline = false;
666 const std::vector<const ParseNode*>& list = func_call->args()->contents();
667 const ParseNode* end = func_call->args()->End();
669 if (end && end->comments() && !end->comments()->before().empty())
670 force_multiline = true;
672 // If there's before line comments, make sure we have a place to put them.
673 for (const auto& i : list) {
674 if (i->comments() && !i->comments()->before().empty())
675 force_multiline = true;
678 // Calculate the penalties for 3 possible layouts:
679 // 1. all on same line;
680 // 2. starting on same line, broken at each comma but paren aligned;
681 // 3. broken to next line + 4, broken at each comma.
682 std::string terminator = ")";
683 if (have_block)
684 terminator += " {";
685 terminator += suffix;
687 // Special case to make function calls of one arg taking a long list of
688 // boolean operators not indent.
689 bool continuation_requires_indent =
690 list.size() != 1 || !list[0]->AsBinaryOp() ||
691 (list[0]->AsBinaryOp()->op().value() != "||" &&
692 list[0]->AsBinaryOp()->op().value() != "&&");
694 // 1: Same line.
695 Printer sub1;
696 InitializeSub(&sub1);
697 sub1.stack_.push_back(
698 IndentState(CurrentColumn(), continuation_requires_indent, false));
699 int penalty_one_line = 0;
700 for (size_t i = 0; i < list.size(); ++i) {
701 penalty_one_line += sub1.Expr(list[i], kPrecedenceLowest,
702 i < list.size() - 1 ? ", " : std::string());
704 sub1.Print(terminator);
705 penalty_one_line += AssessPenalty(sub1.String());
706 // This extra penalty prevents a short second argument from being squeezed in
707 // after a first argument that went multiline (and instead preferring a
708 // variant below).
709 penalty_one_line +=
710 (CountLines(sub1.String()) - 1) * kPenaltyBrokenLineOnOneLiner;
712 // 2: Starting on same line, broken at commas.
713 Printer sub2;
714 InitializeSub(&sub2);
715 sub2.stack_.push_back(
716 IndentState(CurrentColumn(), continuation_requires_indent, false));
717 int penalty_multiline_start_same_line = 0;
718 for (size_t i = 0; i < list.size(); ++i) {
719 penalty_multiline_start_same_line += sub2.Expr(
720 list[i], kPrecedenceLowest, i < list.size() - 1 ? "," : std::string());
721 if (i < list.size() - 1) {
722 sub2.Newline();
725 sub2.Print(terminator);
726 penalty_multiline_start_same_line += AssessPenalty(sub2.String());
728 // 3: Starting on next line, broken at commas.
729 Printer sub3;
730 InitializeSub(&sub3);
731 sub3.stack_.push_back(IndentState(margin() + kIndentSize * 2,
732 continuation_requires_indent, false));
733 sub3.Newline();
734 int penalty_multiline_start_next_line = 0;
735 for (size_t i = 0; i < list.size(); ++i) {
736 if (i == 0) {
737 penalty_multiline_start_next_line +=
738 std::abs(sub3.CurrentColumn() - start_column) *
739 kPenaltyHorizontalSeparation;
741 penalty_multiline_start_next_line += sub3.Expr(
742 list[i], kPrecedenceLowest, i < list.size() - 1 ? "," : std::string());
743 if (i < list.size() - 1) {
744 sub3.Newline();
747 sub3.Print(terminator);
748 penalty_multiline_start_next_line += AssessPenalty(sub3.String());
750 int penalty = penalty_multiline_start_next_line;
751 bool fits_on_current_line = false;
752 if (penalty_one_line < penalty_multiline_start_next_line ||
753 penalty_multiline_start_same_line < penalty_multiline_start_next_line) {
754 fits_on_current_line = true;
755 penalty = penalty_one_line;
756 if (penalty_multiline_start_same_line < penalty_one_line) {
757 penalty = penalty_multiline_start_same_line;
758 force_multiline = true;
760 } else {
761 force_multiline = true;
764 if (list.size() == 0 && !force_multiline) {
765 // No elements, and not forcing newlines, print nothing.
766 } else {
767 if (penalty_multiline_start_next_line < penalty_multiline_start_same_line) {
768 stack_.push_back(IndentState(margin() + kIndentSize * 2,
769 continuation_requires_indent,
770 false));
771 Newline();
772 } else {
773 stack_.push_back(
774 IndentState(CurrentColumn(), continuation_requires_indent, false));
777 for (size_t i = 0; i < list.size(); ++i) {
778 const auto& x = list[i];
779 if (i > 0) {
780 if (fits_on_current_line && !force_multiline)
781 Print(" ");
782 else
783 Newline();
785 bool want_comma = i < list.size() - 1 && !x->AsBlockComment();
786 Expr(x, kPrecedenceLowest, want_comma ? "," : std::string());
787 CHECK(!x->comments() || x->comments()->after().empty());
788 if (i < list.size() - 1) {
789 if (!want_comma)
790 Newline();
794 // Trailing comments.
795 if (end->comments() && !end->comments()->before().empty()) {
796 if (!list.empty())
797 Newline();
798 for (const auto& c : end->comments()->before()) {
799 Newline();
800 TrimAndPrintToken(c);
802 Newline();
804 stack_.pop_back();
807 // Defer any end of line comment until we reach the newline.
808 if (end->comments() && !end->comments()->suffix().empty()) {
809 std::copy(end->comments()->suffix().begin(),
810 end->comments()->suffix().end(), std::back_inserter(comments_));
813 Print(")");
814 Print(suffix);
816 if (have_block) {
817 Print(" ");
818 Sequence(kSequenceStyleBracedBlock,
819 func_call->block()->statements(),
820 func_call->block()->End(),
821 false);
823 return penalty + (CurrentLine() - start_line) * GetPenaltyForLineBreak();
826 void Printer::InitializeSub(Printer* sub) {
827 sub->stack_ = stack_;
828 sub->comments_ = comments_;
829 sub->penalty_depth_ = penalty_depth_;
830 sub->Print(std::string(CurrentColumn(), 'x'));
833 template <class PARSENODE>
834 bool Printer::ListWillBeMultiline(const std::vector<PARSENODE*>& list,
835 const ParseNode* end) {
836 if (list.size() > 1)
837 return true;
839 if (end && end->comments() && !end->comments()->before().empty())
840 return true;
842 // If there's before line comments, make sure we have a place to put them.
843 for (const auto& i : list) {
844 if (i->comments() && !i->comments()->before().empty())
845 return true;
848 return false;
851 void DoFormat(const ParseNode* root, bool dump_tree, std::string* output) {
852 if (dump_tree) {
853 std::ostringstream os;
854 root->Print(os, 0);
855 printf("----------------------\n");
856 printf("-- PARSE TREE --------\n");
857 printf("----------------------\n");
858 printf("%s", os.str().c_str());
859 printf("----------------------\n");
861 Printer pr;
862 pr.Block(root);
863 *output = pr.String();
866 std::string ReadStdin() {
867 static const int kBufferSize = 256;
868 char buffer[kBufferSize];
869 std::string result;
870 while (true) {
871 char* input = NULL;
872 input = fgets(buffer, kBufferSize, stdin);
873 if (input == NULL && feof(stdin))
874 return result;
875 int length = static_cast<int>(strlen(buffer));
876 if (length == 0)
877 return result;
878 else
879 result += std::string(buffer, length);
883 } // namespace
885 bool FormatFileToString(Setup* setup,
886 const SourceFile& file,
887 bool dump_tree,
888 std::string* output) {
889 Err err;
890 const ParseNode* parse_node =
891 setup->scheduler().input_file_manager()->SyncLoadFile(
892 LocationRange(), &setup->build_settings(), file, &err);
893 if (err.has_error()) {
894 err.PrintToStdout();
895 return false;
897 DoFormat(parse_node, dump_tree, output);
898 return true;
901 bool FormatStringToString(const std::string& input,
902 bool dump_tree,
903 std::string* output) {
904 SourceFile source_file;
905 InputFile file(source_file);
906 file.SetContents(input);
907 Err err;
908 // Tokenize.
909 std::vector<Token> tokens = Tokenizer::Tokenize(&file, &err);
910 if (err.has_error()) {
911 err.PrintToStdout();
912 return false;
915 // Parse.
916 scoped_ptr<ParseNode> parse_node = Parser::Parse(tokens, &err);
917 if (err.has_error()) {
918 err.PrintToStdout();
919 return false;
922 DoFormat(parse_node.get(), dump_tree, output);
923 return true;
926 int RunFormat(const std::vector<std::string>& args) {
927 bool dump_tree =
928 base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchDumpTree);
930 bool from_stdin =
931 base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchStdin);
933 if (from_stdin) {
934 if (args.size() != 0) {
935 Err(Location(), "Expecting no arguments when reading from stdin.\n")
936 .PrintToStdout();
937 return 1;
939 std::string input = ReadStdin();
940 std::string output;
941 if (!FormatStringToString(input, dump_tree, &output))
942 return 1;
943 printf("%s", output.c_str());
944 return 0;
947 // TODO(scottmg): Eventually, this should be a list/spec of files, and they
948 // should all be done in parallel.
949 if (args.size() != 1) {
950 Err(Location(), "Expecting exactly one argument, see `gn help format`.\n")
951 .PrintToStdout();
952 return 1;
955 Setup setup;
956 SourceDir source_dir =
957 SourceDirForCurrentDirectory(setup.build_settings().root_path());
958 SourceFile file = source_dir.ResolveRelativeFile(args[0]);
960 std::string output_string;
961 if (FormatFileToString(&setup, file, dump_tree, &output_string)) {
962 bool in_place =
963 base::CommandLine::ForCurrentProcess()->HasSwitch(kSwitchInPlace);
964 if (in_place) {
965 base::FilePath to_write = setup.build_settings().GetFullPath(file);
966 std::string original_contents;
967 if (!base::ReadFileToString(to_write, &original_contents)) {
968 Err(Location(), std::string("Couldn't read \"") +
969 to_write.AsUTF8Unsafe() +
970 std::string("\" for comparison.")).PrintToStdout();
971 return 1;
973 if (original_contents != output_string) {
974 if (base::WriteFile(to_write,
975 output_string.data(),
976 static_cast<int>(output_string.size())) == -1) {
977 Err(Location(),
978 std::string("Failed to write formatted output back to \"") +
979 to_write.AsUTF8Unsafe() + std::string("\".")).PrintToStdout();
980 return 1;
982 printf("Wrote formatted to '%s'.\n", to_write.AsUTF8Unsafe().c_str());
984 } else {
985 printf("%s", output_string.c_str());
989 return 0;
992 } // namespace commands