| Index: tools/gn/command_format.cc
|
| diff --git a/tools/gn/command_format.cc b/tools/gn/command_format.cc
|
| index 593c150139016949dbe4722d17d7ebde6fb92985..f30984a14503cfef553c57623afa2f339d1879e2 100644
|
| --- a/tools/gn/command_format.cc
|
| +++ b/tools/gn/command_format.cc
|
| @@ -59,7 +59,7 @@ const int kMaximumWidth = 80;
|
| const int kPenaltyLineBreak = 500;
|
| const int kPenaltyHorizontalSeparation = 100;
|
| const int kPenaltyExcess = 10000;
|
| -const int kPenaltyBrokenLineOnOneLiner = 1000;
|
| +const int kPenaltyBrokenLineOnOneLiner = 5000;
|
|
|
| enum Precedence {
|
| kPrecedenceLowest,
|
| @@ -72,6 +72,12 @@ enum Precedence {
|
| kPrecedenceUnary,
|
| };
|
|
|
| +int CountLines(const std::string& str) {
|
| + std::vector<std::string> lines;
|
| + base::SplitStringDontTrim(str, '\n', &lines);
|
| + return static_cast<int>(lines.size());
|
| +}
|
| +
|
| class Printer {
|
| public:
|
| Printer();
|
| @@ -151,12 +157,17 @@ class Printer {
|
| bool force_multiline);
|
|
|
| // Returns the penalty.
|
| - int FunctionCall(const FunctionCallNode* func_call);
|
| + int FunctionCall(const FunctionCallNode* func_call,
|
| + const std::string& suffix);
|
|
|
| // Create a clone of this Printer in a similar state (other than the output,
|
| // but including margins, etc.) to be used for dry run measurements.
|
| void InitializeSub(Printer* sub);
|
|
|
| + template <class PARSENODE>
|
| + bool ListWillBeMultiline(const std::vector<PARSENODE*>& list,
|
| + const ParseNode* end);
|
| +
|
| std::string output_; // Output buffer.
|
| std::vector<Token> comments_; // Pending end-of-line comments.
|
| int margin() const { return stack_.back().margin; }
|
| @@ -367,6 +378,7 @@ void Printer::AddParen(int prec, int outer_prec, bool* parenthesized) {
|
| int Printer::Expr(const ParseNode* root,
|
| int outer_prec,
|
| const std::string& suffix) {
|
| + std::string at_end = suffix;
|
| int penalty = 0;
|
| penalty_depth_++;
|
|
|
| @@ -405,32 +417,55 @@ int Printer::Expr(const ParseNode* root,
|
| AddParen(prec, outer_prec, &parenthesized);
|
| int start_line = CurrentLine();
|
| int start_column = CurrentColumn();
|
| + bool is_assignment = binop->op().value() == "=" ||
|
| + binop->op().value() == "+=" ||
|
| + binop->op().value() == "-=";
|
| + // A sort of funny special case for the long lists that are common in .gn
|
| + // files, don't indent them + 4, even though they're just continuations when
|
| + // they're simple lists like "x = [ a, b, c, ... ]"
|
| + const ListNode* right_as_list = binop->right()->AsList();
|
| int indent_column =
|
| - (binop->op().value() == "=" || binop->op().value() == "+=" ||
|
| - binop->op().value() == "-=")
|
| + (is_assignment &&
|
| + (!right_as_list || (!right_as_list->prefer_multiline() &&
|
| + !ListWillBeMultiline(right_as_list->contents(),
|
| + right_as_list->End()))))
|
| ? margin() + kIndentSize * 2
|
| : start_column;
|
| if (stack_.back().continuation_requires_indent)
|
| indent_column += kIndentSize * 2;
|
|
|
| - Expr(binop->left(),
|
| - prec,
|
| - std::string(" ") + binop->op().value().as_string());
|
| + stack_.push_back(IndentState(indent_column, false));
|
| + Printer sub_left;
|
| + InitializeSub(&sub_left);
|
| + sub_left.Expr(binop->left(),
|
| + prec,
|
| + std::string(" ") + binop->op().value().as_string());
|
| + bool left_is_multiline = CountLines(sub_left.String()) > 1;
|
| + // Avoid walking the whole left redundantly times (see timing of Format.046)
|
| + // so pull the output and comments from subprinter.
|
| + Print(sub_left.String().substr(start_column));
|
| + std::copy(sub_left.comments_.begin(),
|
| + sub_left.comments_.end(),
|
| + std::back_inserter(comments_));
|
|
|
| // Single line.
|
| Printer sub1;
|
| InitializeSub(&sub1);
|
| - sub1.stack_.push_back(IndentState(indent_column, false));
|
| sub1.Print(" ");
|
| int penalty_current_line =
|
| sub1.Expr(binop->right(), prec + 1, std::string());
|
| sub1.Print(suffix);
|
| penalty_current_line += AssessPenalty(sub1.String());
|
| + if (!is_assignment && left_is_multiline) {
|
| + // In e.g. xxx + yyy, if xxx is already multiline, then we want a penalty
|
| + // for trying to continue as if this were one line.
|
| + penalty_current_line +=
|
| + (CountLines(sub1.String()) - 1) * kPenaltyBrokenLineOnOneLiner;
|
| + }
|
|
|
| // Break after operator.
|
| Printer sub2;
|
| InitializeSub(&sub2);
|
| - sub2.stack_.push_back(IndentState(indent_column, false));
|
| sub2.Newline();
|
| int penalty_next_line = sub2.Expr(binop->right(), prec + 1, std::string());
|
| sub2.Print(suffix);
|
| @@ -441,13 +476,12 @@ int Printer::Expr(const ParseNode* root,
|
| Expr(binop->right(), prec + 1, std::string());
|
| } else {
|
| // Otherwise, put first argument and op, and indent next.
|
| - stack_.push_back(IndentState(indent_column, false));
|
| Newline();
|
| penalty += std::abs(CurrentColumn() - start_column) *
|
| kPenaltyHorizontalSeparation;
|
| Expr(binop->right(), prec + 1, std::string());
|
| - stack_.pop_back();
|
| }
|
| + stack_.pop_back();
|
| penalty += (CurrentLine() - start_line) * GetPenaltyForLineBreak();
|
| } else if (const BlockNode* block = root->AsBlock()) {
|
| Sequence(
|
| @@ -470,11 +504,13 @@ int Printer::Expr(const ParseNode* root,
|
| } else {
|
| Sequence(kSequenceStyleBracedBlock,
|
| condition->if_false()->AsBlock()->statements(),
|
| - condition->if_false()->AsBlock()->End(), false);
|
| + condition->if_false()->AsBlock()->End(),
|
| + false);
|
| }
|
| }
|
| } else if (const FunctionCallNode* func_call = root->AsFunctionCall()) {
|
| - penalty += FunctionCall(func_call);
|
| + penalty += FunctionCall(func_call, at_end);
|
| + at_end = "";
|
| } else if (const IdentifierNode* identifier = root->AsIdentifier()) {
|
| Print(identifier->value().value());
|
| } else if (const ListNode* list = root->AsList()) {
|
| @@ -505,7 +541,7 @@ int Printer::Expr(const ParseNode* root,
|
| std::back_inserter(comments_));
|
| }
|
|
|
| - Print(suffix);
|
| + Print(at_end);
|
|
|
| penalty_depth_--;
|
| return penalty;
|
| @@ -524,14 +560,7 @@ void Printer::Sequence(SequenceStyle style,
|
| if (style == kSequenceStyleBlock || style == kSequenceStyleBracedBlock)
|
| force_multiline = true;
|
|
|
| - if (end && end->comments() && !end->comments()->before().empty())
|
| - force_multiline = true;
|
| -
|
| - // If there's before line comments, make sure we have a place to put them.
|
| - for (const auto& i : list) {
|
| - if (i->comments() && !i->comments()->before().empty())
|
| - force_multiline = true;
|
| - }
|
| + force_multiline |= ListWillBeMultiline(list, end);
|
|
|
| if (list.size() == 0 && !force_multiline) {
|
| // No elements, and not forcing newlines, print nothing.
|
| @@ -571,7 +600,7 @@ void Printer::Sequence(SequenceStyle style,
|
| }
|
|
|
| // Trailing comments.
|
| - if (end->comments()) {
|
| + if (end->comments() && !end->comments()->before().empty()) {
|
| if (list.size() >= 2)
|
| Newline();
|
| for (const auto& c : end->comments()->before()) {
|
| @@ -597,7 +626,8 @@ void Printer::Sequence(SequenceStyle style,
|
| Print("}");
|
| }
|
|
|
| -int Printer::FunctionCall(const FunctionCallNode* func_call) {
|
| +int Printer::FunctionCall(const FunctionCallNode* func_call,
|
| + const std::string& suffix) {
|
| int start_line = CurrentLine();
|
| int start_column = CurrentColumn();
|
| Print(func_call->function().value());
|
| @@ -625,6 +655,7 @@ int Printer::FunctionCall(const FunctionCallNode* func_call) {
|
| std::string terminator = ")";
|
| if (have_block)
|
| terminator += " {";
|
| + terminator += suffix;
|
|
|
| // 1: Same line.
|
| Printer sub1;
|
| @@ -637,13 +668,11 @@ int Printer::FunctionCall(const FunctionCallNode* func_call) {
|
| }
|
| sub1.Print(terminator);
|
| penalty_one_line += AssessPenalty(sub1.String());
|
| - std::vector<std::string> lines;
|
| - base::SplitStringDontTrim(sub1.String(), '\n', &lines);
|
| // This extra penalty prevents a short second argument from being squeezed in
|
| // after a first argument that went multiline (and instead preferring a
|
| // variant below).
|
| - if (lines.size() > 1)
|
| - penalty_one_line += kPenaltyBrokenLineOnOneLiner;
|
| + penalty_one_line +=
|
| + (CountLines(sub1.String()) - 1) * kPenaltyBrokenLineOnOneLiner;
|
|
|
| // 2: Starting on same line, broken at commas.
|
| Printer sub2;
|
| @@ -742,6 +771,7 @@ int Printer::FunctionCall(const FunctionCallNode* func_call) {
|
| }
|
|
|
| Print(")");
|
| + Print(suffix);
|
|
|
| if (have_block) {
|
| Print(" ");
|
| @@ -760,6 +790,24 @@ void Printer::InitializeSub(Printer* sub) {
|
| sub->Print(std::string(CurrentColumn(), 'x'));
|
| }
|
|
|
| +template <class PARSENODE>
|
| +bool Printer::ListWillBeMultiline(const std::vector<PARSENODE*>& list,
|
| + const ParseNode* end) {
|
| + if (list.size() > 1)
|
| + return true;
|
| +
|
| + if (end && end->comments() && !end->comments()->before().empty())
|
| + return true;
|
| +
|
| + // If there's before line comments, make sure we have a place to put them.
|
| + for (const auto& i : list) {
|
| + if (i->comments() && !i->comments()->before().empty())
|
| + return true;
|
| + }
|
| +
|
| + return false;
|
| +}
|
| +
|
| void DoFormat(const ParseNode* root, bool dump_tree, std::string* output) {
|
| if (dump_tree) {
|
| std::ostringstream os;
|
|
|