| Index: tools/gn/command_format.cc
|
| diff --git a/tools/gn/command_format.cc b/tools/gn/command_format.cc
|
| index 8162698861d8d522fba405f8ada0dbe54e339f56..593c150139016949dbe4722d17d7ebde6fb92985 100644
|
| --- a/tools/gn/command_format.cc
|
| +++ b/tools/gn/command_format.cc
|
| @@ -56,6 +56,11 @@ namespace {
|
| const int kIndentSize = 2;
|
| const int kMaximumWidth = 80;
|
|
|
| +const int kPenaltyLineBreak = 500;
|
| +const int kPenaltyHorizontalSeparation = 100;
|
| +const int kPenaltyExcess = 10000;
|
| +const int kPenaltyBrokenLineOnOneLiner = 1000;
|
| +
|
| enum Precedence {
|
| kPrecedenceLowest,
|
| kPrecedenceAssign,
|
| @@ -84,11 +89,6 @@ class Printer {
|
| kSequenceStyleBracedBlock,
|
| };
|
|
|
| - enum ExprStyle {
|
| - kExprStyleRegular,
|
| - kExprStyleComment,
|
| - };
|
| -
|
| struct Metrics {
|
| Metrics() : first_length(-1), longest_length(-1), multiline(false) {}
|
| int first_length;
|
| @@ -113,7 +113,8 @@ class Printer {
|
| // Whether there's a blank separator line at the current position.
|
| bool HaveBlankLine();
|
|
|
| - bool IsAssignment(const ParseNode* node);
|
| + // Flag assignments to sources, deps, etc. to make their RHSs multiline.
|
| + void AnnotatePreferedMultilineAssignment(const BinaryOpNode* binop);
|
|
|
| // Heuristics to decide if there should be a blank line added between two
|
| // items. For various "small" items, it doesn't look nice if there's too much
|
| @@ -121,7 +122,10 @@ class Printer {
|
| bool ShouldAddBlankLineInBetween(const ParseNode* a, const ParseNode* b);
|
|
|
| // Get the 0-based x position on the current line.
|
| - int CurrentColumn();
|
| + int CurrentColumn() const;
|
| +
|
| + // Get the current line in the output;
|
| + int CurrentLine() const;
|
|
|
| // Adds an opening ( if prec is less than the outers (to maintain evalution
|
| // order for a subexpression). If an opening paren is emitted, *parenthesized
|
| @@ -132,11 +136,10 @@ class Printer {
|
| // added to the output. The value of outer_prec gives the precedence of the
|
| // operator outside this Expr. If that operator binds tighter than root's,
|
| // Expr must introduce parentheses.
|
| - ExprStyle Expr(const ParseNode* root, int outer_prec);
|
| + int Expr(const ParseNode* root, int outer_prec, const std::string& suffix);
|
|
|
| - // Use a sub-Printer recursively to figure out the size that an expression
|
| - // would be before actually adding it to the output.
|
| - Metrics GetLengthOfExpr(const ParseNode* expr, int outer_prec);
|
| + // Generic penalties for exceeding maximum width, adding more lines, etc.
|
| + int AssessPenalty(const std::string& output);
|
|
|
| // Format a list of values using the given style.
|
| // |end| holds any trailing comments to be printed just before the closing
|
| @@ -144,13 +147,38 @@ class Printer {
|
| template <class PARSENODE> // Just for const covariance.
|
| void Sequence(SequenceStyle style,
|
| const std::vector<PARSENODE*>& list,
|
| - const ParseNode* end);
|
| + const ParseNode* end,
|
| + bool force_multiline);
|
|
|
| - void FunctionCall(const FunctionCallNode* func_call);
|
| + // Returns the penalty.
|
| + int FunctionCall(const FunctionCallNode* func_call);
|
| +
|
| + // 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);
|
|
|
| std::string output_; // Output buffer.
|
| std::vector<Token> comments_; // Pending end-of-line comments.
|
| - int margin_; // Left margin (number of spaces).
|
| + int margin() const { return stack_.back().margin; }
|
| +
|
| + int penalty_depth_;
|
| + int GetPenaltyForLineBreak() const {
|
| + return penalty_depth_ * kPenaltyLineBreak;
|
| + }
|
| +
|
| + struct IndentState {
|
| + IndentState() : margin(0), continuation_requires_indent(false) {}
|
| + IndentState(int margin, bool continuation_requires_indent)
|
| + : margin(margin),
|
| + continuation_requires_indent(continuation_requires_indent) {}
|
| +
|
| + // The left margin (number of spaces).
|
| + int margin;
|
| +
|
| + bool continuation_requires_indent;
|
| + };
|
| + // Stack used to track
|
| + std::vector<IndentState> stack_;
|
|
|
| // Gives the precedence for operators in a BinaryOpNode.
|
| std::map<base::StringPiece, Precedence> precedence_;
|
| @@ -158,7 +186,7 @@ class Printer {
|
| DISALLOW_COPY_AND_ASSIGN(Printer);
|
| };
|
|
|
| -Printer::Printer() : margin_(0) {
|
| +Printer::Printer() : penalty_depth_(0) {
|
| output_.reserve(100 << 10);
|
| precedence_["="] = kPrecedenceAssign;
|
| precedence_["+="] = kPrecedenceAssign;
|
| @@ -174,6 +202,7 @@ Printer::Printer() : margin_(0) {
|
| precedence_["+"] = kPrecedenceAdd;
|
| precedence_["-"] = kPrecedenceAdd;
|
| precedence_["!"] = kPrecedenceUnary;
|
| + stack_.push_back(IndentState());
|
| }
|
|
|
| Printer::~Printer() {
|
| @@ -184,7 +213,7 @@ void Printer::Print(base::StringPiece str) {
|
| }
|
|
|
| void Printer::PrintMargin() {
|
| - output_ += std::string(margin_, ' ');
|
| + output_ += std::string(margin(), ' ');
|
| }
|
|
|
| void Printer::TrimAndPrintToken(const Token& token) {
|
| @@ -196,15 +225,13 @@ void Printer::TrimAndPrintToken(const Token& token) {
|
| void Printer::Newline() {
|
| if (!comments_.empty()) {
|
| Print(" ");
|
| - int i = 0;
|
| // Save the margin, and temporarily set it to where the first comment
|
| // starts so that multiple suffix comments are vertically aligned. This
|
| // will need to be fancier once we enforce 80 col.
|
| - int old_margin = margin_;
|
| + stack_.push_back(IndentState(CurrentColumn(), false));
|
| + int i = 0;
|
| for (const auto& c : comments_) {
|
| - if (i == 0)
|
| - margin_ = CurrentColumn();
|
| - else {
|
| + if (i > 0) {
|
| Trim();
|
| Print("\n");
|
| PrintMargin();
|
| @@ -212,7 +239,7 @@ void Printer::Newline() {
|
| TrimAndPrintToken(c);
|
| ++i;
|
| }
|
| - margin_ = old_margin;
|
| + stack_.pop_back();
|
| comments_.clear();
|
| }
|
| Trim();
|
| @@ -234,10 +261,20 @@ bool Printer::HaveBlankLine() {
|
| return n > 2 && output_[n - 1] == '\n' && output_[n - 2] == '\n';
|
| }
|
|
|
| -bool Printer::IsAssignment(const ParseNode* node) {
|
| - return node->AsBinaryOp() && (node->AsBinaryOp()->op().value() == "=" ||
|
| - node->AsBinaryOp()->op().value() == "+=" ||
|
| - node->AsBinaryOp()->op().value() == "-=");
|
| +void Printer::AnnotatePreferedMultilineAssignment(const BinaryOpNode* binop) {
|
| + const IdentifierNode* ident = binop->left()->AsIdentifier();
|
| + const ListNode* list = binop->right()->AsList();
|
| + // This is somewhat arbitrary, but we include the 'deps'- and 'sources'-like
|
| + // things, but not flags things.
|
| + if (binop->op().value() == "=" && ident && list &&
|
| + (ident->value().value() == "data" ||
|
| + ident->value().value() == "datadeps" ||
|
| + ident->value().value() == "deps" || ident->value().value() == "inputs" ||
|
| + ident->value().value() == "public" ||
|
| + ident->value().value() == "public_deps" ||
|
| + ident->value().value() == "sources")) {
|
| + const_cast<ListNode*>(list)->set_prefer_multiline(true);
|
| + }
|
| }
|
|
|
| bool Printer::ShouldAddBlankLineInBetween(const ParseNode* a,
|
| @@ -249,7 +286,7 @@ bool Printer::ShouldAddBlankLineInBetween(const ParseNode* a,
|
| return b_range.begin().line_number() > a_range.end().line_number() + 1;
|
| }
|
|
|
| -int Printer::CurrentColumn() {
|
| +int Printer::CurrentColumn() const {
|
| int n = 0;
|
| while (n < static_cast<int>(output_.size()) &&
|
| output_[output_.size() - 1 - n] != '\n') {
|
| @@ -258,6 +295,15 @@ int Printer::CurrentColumn() {
|
| return n;
|
| }
|
|
|
| +int Printer::CurrentLine() const {
|
| + int count = 1;
|
| + for (const char* p = output_.c_str(); (p = strchr(p, '\n')) != NULL;) {
|
| + ++count;
|
| + ++p;
|
| + }
|
| + return count;
|
| +}
|
| +
|
| void Printer::Block(const ParseNode* root) {
|
| const BlockNode* block = root->AsBlock();
|
|
|
| @@ -270,7 +316,7 @@ void Printer::Block(const ParseNode* root) {
|
|
|
| size_t i = 0;
|
| for (const auto& stmt : block->statements()) {
|
| - Expr(stmt, kPrecedenceLowest);
|
| + Expr(stmt, kPrecedenceLowest, std::string());
|
| Newline();
|
| if (stmt->comments()) {
|
| // Why are before() not printed here too? before() are handled inside
|
| @@ -299,20 +345,16 @@ void Printer::Block(const ParseNode* root) {
|
| }
|
| }
|
|
|
| -Printer::Metrics Printer::GetLengthOfExpr(const ParseNode* expr,
|
| - int outer_prec) {
|
| - Metrics result;
|
| - Printer sub;
|
| - sub.Expr(expr, outer_prec);
|
| +int Printer::AssessPenalty(const std::string& output) {
|
| + int penalty = 0;
|
| std::vector<std::string> lines;
|
| - base::SplitStringDontTrim(sub.String(), '\n', &lines);
|
| - result.multiline = lines.size() > 1;
|
| - result.first_length = static_cast<int>(lines[0].size());
|
| + base::SplitStringDontTrim(output, '\n', &lines);
|
| + penalty += static_cast<int>(lines.size() - 1) * GetPenaltyForLineBreak();
|
| for (const auto& line : lines) {
|
| - result.longest_length =
|
| - std::max(result.longest_length, static_cast<int>(line.size()));
|
| + if (line.size() > kMaximumWidth)
|
| + penalty += static_cast<int>(line.size() - kMaximumWidth) * kPenaltyExcess;
|
| }
|
| - return result;
|
| + return penalty;
|
| }
|
|
|
| void Printer::AddParen(int prec, int outer_prec, bool* parenthesized) {
|
| @@ -322,8 +364,12 @@ void Printer::AddParen(int prec, int outer_prec, bool* parenthesized) {
|
| }
|
| }
|
|
|
| -Printer::ExprStyle Printer::Expr(const ParseNode* root, int outer_prec) {
|
| - ExprStyle result = kExprStyleRegular;
|
| +int Printer::Expr(const ParseNode* root,
|
| + int outer_prec,
|
| + const std::string& suffix) {
|
| + int penalty = 0;
|
| + penalty_depth_++;
|
| +
|
| if (root->comments()) {
|
| if (!root->comments()->before().empty()) {
|
| Trim();
|
| @@ -346,73 +392,103 @@ Printer::ExprStyle Printer::Expr(const ParseNode* root, int outer_prec) {
|
| Print(accessor->base().value());
|
| if (accessor->member()) {
|
| Print(".");
|
| - Expr(accessor->member(), kPrecedenceLowest);
|
| + Expr(accessor->member(), kPrecedenceLowest, std::string());
|
| } else {
|
| CHECK(accessor->index());
|
| Print("[");
|
| - Expr(accessor->index(), kPrecedenceLowest);
|
| - Print("]");
|
| + Expr(accessor->index(), kPrecedenceLowest, "]");
|
| }
|
| } else if (const BinaryOpNode* binop = root->AsBinaryOp()) {
|
| CHECK(precedence_.find(binop->op().value()) != precedence_.end());
|
| + AnnotatePreferedMultilineAssignment(binop);
|
| Precedence prec = precedence_[binop->op().value()];
|
| AddParen(prec, outer_prec, &parenthesized);
|
| - Metrics right = GetLengthOfExpr(binop->right(), prec + 1);
|
| - int op_length = static_cast<int>(binop->op().value().size()) + 2;
|
| - Expr(binop->left(), prec);
|
| - if (CurrentColumn() + op_length + right.first_length <= kMaximumWidth) {
|
| - // If it just fits normally, put it here.
|
| - Print(" ");
|
| - Print(binop->op().value());
|
| + int start_line = CurrentLine();
|
| + int start_column = CurrentColumn();
|
| + int indent_column =
|
| + (binop->op().value() == "=" || binop->op().value() == "+=" ||
|
| + binop->op().value() == "-=")
|
| + ? 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());
|
| +
|
| + // 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());
|
| +
|
| + // 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);
|
| + penalty_next_line += AssessPenalty(sub2.String());
|
| +
|
| + if (penalty_current_line < penalty_next_line) {
|
| Print(" ");
|
| - Expr(binop->right(), prec + 1);
|
| + Expr(binop->right(), prec + 1, std::string());
|
| } else {
|
| // Otherwise, put first argument and op, and indent next.
|
| - Print(" ");
|
| - Print(binop->op().value());
|
| - int old_margin = margin_;
|
| - margin_ += kIndentSize * 2;
|
| + stack_.push_back(IndentState(indent_column, false));
|
| Newline();
|
| - Expr(binop->right(), prec + 1);
|
| - margin_ = old_margin;
|
| + penalty += std::abs(CurrentColumn() - start_column) *
|
| + kPenaltyHorizontalSeparation;
|
| + Expr(binop->right(), prec + 1, std::string());
|
| + stack_.pop_back();
|
| }
|
| + penalty += (CurrentLine() - start_line) * GetPenaltyForLineBreak();
|
| } else if (const BlockNode* block = root->AsBlock()) {
|
| - Sequence(kSequenceStyleBracedBlock, block->statements(), block->End());
|
| + Sequence(
|
| + kSequenceStyleBracedBlock, block->statements(), block->End(), false);
|
| } else if (const ConditionNode* condition = root->AsConditionNode()) {
|
| Print("if (");
|
| - Expr(condition->condition(), kPrecedenceLowest);
|
| - Print(") ");
|
| + // TODO(scottmg): The { needs to be included in the suffix here.
|
| + Expr(condition->condition(), kPrecedenceLowest, ") ");
|
| Sequence(kSequenceStyleBracedBlock,
|
| condition->if_true()->statements(),
|
| - condition->if_true()->End());
|
| + condition->if_true()->End(),
|
| + false);
|
| if (condition->if_false()) {
|
| Print(" else ");
|
| // If it's a block it's a bare 'else', otherwise it's an 'else if'. See
|
| // ConditionNode::Execute.
|
| bool is_else_if = condition->if_false()->AsBlock() == NULL;
|
| if (is_else_if) {
|
| - Expr(condition->if_false(), kPrecedenceLowest);
|
| + Expr(condition->if_false(), kPrecedenceLowest, std::string());
|
| } else {
|
| Sequence(kSequenceStyleBracedBlock,
|
| condition->if_false()->AsBlock()->statements(),
|
| - condition->if_false()->AsBlock()->End());
|
| + condition->if_false()->AsBlock()->End(), false);
|
| }
|
| }
|
| } else if (const FunctionCallNode* func_call = root->AsFunctionCall()) {
|
| - FunctionCall(func_call);
|
| + penalty += FunctionCall(func_call);
|
| } else if (const IdentifierNode* identifier = root->AsIdentifier()) {
|
| Print(identifier->value().value());
|
| } else if (const ListNode* list = root->AsList()) {
|
| - Sequence(kSequenceStyleList, list->contents(), list->End());
|
| + bool force_multiline =
|
| + list->prefer_multiline() && !list->contents().empty();
|
| + Sequence(
|
| + kSequenceStyleList, list->contents(), list->End(), force_multiline);
|
| } else if (const LiteralNode* literal = root->AsLiteral()) {
|
| - // TODO(scottmg): Quoting?
|
| Print(literal->value().value());
|
| } else if (const UnaryOpNode* unaryop = root->AsUnaryOp()) {
|
| Print(unaryop->op().value());
|
| - Expr(unaryop->operand(), kPrecedenceUnary);
|
| + Expr(unaryop->operand(), kPrecedenceUnary, std::string());
|
| } else if (const BlockCommentNode* block_comment = root->AsBlockComment()) {
|
| Print(block_comment->comment().value());
|
| - result = kExprStyleComment;
|
| } else if (const EndNode* end = root->AsEnd()) {
|
| Print(end->value().value());
|
| } else {
|
| @@ -429,14 +505,17 @@ Printer::ExprStyle Printer::Expr(const ParseNode* root, int outer_prec) {
|
| std::back_inserter(comments_));
|
| }
|
|
|
| - return result;
|
| + Print(suffix);
|
| +
|
| + penalty_depth_--;
|
| + return penalty;
|
| }
|
|
|
| template <class PARSENODE>
|
| void Printer::Sequence(SequenceStyle style,
|
| const std::vector<PARSENODE*>& list,
|
| - const ParseNode* end) {
|
| - bool force_multiline = false;
|
| + const ParseNode* end,
|
| + bool force_multiline) {
|
| if (style == kSequenceStyleList)
|
| Print("[");
|
| else if (style == kSequenceStyleBracedBlock)
|
| @@ -458,11 +537,12 @@ void Printer::Sequence(SequenceStyle style,
|
| // No elements, and not forcing newlines, print nothing.
|
| } else if (list.size() == 1 && !force_multiline) {
|
| Print(" ");
|
| - Expr(list[0], kPrecedenceLowest);
|
| + Expr(list[0], kPrecedenceLowest, std::string());
|
| CHECK(!list[0]->comments() || list[0]->comments()->after().empty());
|
| Print(" ");
|
| } else {
|
| - margin_ += kIndentSize;
|
| + stack_.push_back(
|
| + IndentState(margin() + kIndentSize, style == kSequenceStyleList));
|
| size_t i = 0;
|
| for (const auto& x : list) {
|
| Newline();
|
| @@ -475,12 +555,13 @@ void Printer::Sequence(SequenceStyle style,
|
| !HaveBlankLine()) {
|
| Newline();
|
| }
|
| - ExprStyle expr_style = Expr(x, kPrecedenceLowest);
|
| + bool body_of_list = i < list.size() - 1 || style == kSequenceStyleList;
|
| + bool want_comma =
|
| + body_of_list && (style == kSequenceStyleList && !x->AsBlockComment());
|
| + Expr(x, kPrecedenceLowest, want_comma ? "," : std::string());
|
| CHECK(!x->comments() || x->comments()->after().empty());
|
| - if (i < list.size() - 1 || style == kSequenceStyleList) {
|
| - if (style == kSequenceStyleList && expr_style == kExprStyleRegular) {
|
| - Print(",");
|
| - } else {
|
| + if (body_of_list) {
|
| + if (!want_comma) {
|
| if (i < list.size() - 1 &&
|
| ShouldAddBlankLineInBetween(list[i], list[i + 1]))
|
| Newline();
|
| @@ -499,7 +580,7 @@ void Printer::Sequence(SequenceStyle style,
|
| }
|
| }
|
|
|
| - margin_ -= kIndentSize;
|
| + stack_.pop_back();
|
| Newline();
|
|
|
| // Defer any end of line comment until we reach the newline.
|
| @@ -516,11 +597,12 @@ void Printer::Sequence(SequenceStyle style,
|
| Print("}");
|
| }
|
|
|
| -void Printer::FunctionCall(const FunctionCallNode* func_call) {
|
| +int Printer::FunctionCall(const FunctionCallNode* func_call) {
|
| + int start_line = CurrentLine();
|
| + int start_column = CurrentColumn();
|
| Print(func_call->function().value());
|
| Print("(");
|
|
|
| - int old_margin = margin_;
|
| bool have_block = func_call->block() != nullptr;
|
| bool force_multiline = false;
|
|
|
| @@ -536,107 +618,146 @@ void Printer::FunctionCall(const FunctionCallNode* func_call) {
|
| force_multiline = true;
|
| }
|
|
|
| - // Calculate the length of the items for function calls so we can decide to
|
| - // compress them in various nicer ways.
|
| - std::vector<int> natural_lengths;
|
| - bool fits_on_current_line = true;
|
| - int max_item_width = 0;
|
| - int total_length = 0;
|
| - natural_lengths.reserve(list.size());
|
| + // Calculate the penalties for 3 possible layouts:
|
| + // 1. all on same line;
|
| + // 2. starting on same line, broken at each comma but paren aligned;
|
| + // 3. broken to next line + 4, broken at each comma.
|
| std::string terminator = ")";
|
| if (have_block)
|
| terminator += " {";
|
| +
|
| + // 1: Same line.
|
| + Printer sub1;
|
| + InitializeSub(&sub1);
|
| + sub1.stack_.push_back(IndentState(CurrentColumn(), true));
|
| + int penalty_one_line = 0;
|
| for (size_t i = 0; i < list.size(); ++i) {
|
| - Metrics sub = GetLengthOfExpr(list[i], kPrecedenceLowest);
|
| - if (sub.multiline)
|
| - fits_on_current_line = false;
|
| - natural_lengths.push_back(sub.longest_length);
|
| - total_length += sub.longest_length;
|
| + penalty_one_line += sub1.Expr(list[i], kPrecedenceLowest,
|
| + i < list.size() - 1 ? ", " : std::string());
|
| + }
|
| + 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;
|
| +
|
| + // 2: Starting on same line, broken at commas.
|
| + Printer sub2;
|
| + InitializeSub(&sub2);
|
| + sub2.stack_.push_back(IndentState(CurrentColumn(), true));
|
| + int penalty_multiline_start_same_line = 0;
|
| + for (size_t i = 0; i < list.size(); ++i) {
|
| + penalty_multiline_start_same_line += sub2.Expr(
|
| + list[i], kPrecedenceLowest, i < list.size() - 1 ? "," : std::string());
|
| if (i < list.size() - 1) {
|
| - total_length += static_cast<int>(strlen(", "));
|
| + sub2.Newline();
|
| }
|
| }
|
| - fits_on_current_line =
|
| - fits_on_current_line &&
|
| - CurrentColumn() + total_length + terminator.size() <= kMaximumWidth;
|
| - if (natural_lengths.size() > 0) {
|
| - max_item_width =
|
| - *std::max_element(natural_lengths.begin(), natural_lengths.end());
|
| + sub2.Print(terminator);
|
| + penalty_multiline_start_same_line += AssessPenalty(sub2.String());
|
| +
|
| + // 3: Starting on next line, broken at commas.
|
| + Printer sub3;
|
| + InitializeSub(&sub3);
|
| + sub3.stack_.push_back(IndentState(margin() + kIndentSize * 2, true));
|
| + sub3.Newline();
|
| + int penalty_multiline_start_next_line = 0;
|
| + for (size_t i = 0; i < list.size(); ++i) {
|
| + if (i == 0) {
|
| + penalty_multiline_start_next_line +=
|
| + std::abs(sub3.CurrentColumn() - start_column) *
|
| + kPenaltyHorizontalSeparation;
|
| + }
|
| + penalty_multiline_start_next_line += sub3.Expr(
|
| + list[i], kPrecedenceLowest, i < list.size() - 1 ? "," : std::string());
|
| + if (i < list.size() - 1) {
|
| + sub3.Newline();
|
| + }
|
| + }
|
| + sub3.Print(terminator);
|
| + penalty_multiline_start_next_line += AssessPenalty(sub3.String());
|
| +
|
| + int penalty = penalty_multiline_start_next_line;
|
| + bool fits_on_current_line = false;
|
| + if (penalty_one_line < penalty_multiline_start_next_line ||
|
| + penalty_multiline_start_same_line < penalty_multiline_start_next_line) {
|
| + fits_on_current_line = true;
|
| + penalty = penalty_one_line;
|
| + if (penalty_multiline_start_same_line < penalty_one_line) {
|
| + penalty = penalty_multiline_start_same_line;
|
| + force_multiline = true;
|
| + }
|
| + } else {
|
| + force_multiline = true;
|
| }
|
|
|
| if (list.size() == 0 && !force_multiline) {
|
| // No elements, and not forcing newlines, print nothing.
|
| - } else if (list.size() == 1 && !force_multiline && fits_on_current_line) {
|
| - Expr(list[0], kPrecedenceLowest);
|
| - CHECK(!list[0]->comments() || list[0]->comments()->after().empty());
|
| } else {
|
| - // Function calls get to be single line even with multiple arguments, if
|
| - // they fit inside the maximum width.
|
| - if (!force_multiline && fits_on_current_line) {
|
| - for (size_t i = 0; i < list.size(); ++i) {
|
| - Expr(list[i], kPrecedenceLowest);
|
| - if (i < list.size() - 1)
|
| - Print(", ");
|
| - }
|
| + if (penalty_multiline_start_next_line < penalty_multiline_start_same_line) {
|
| + stack_.push_back(IndentState(margin() + kIndentSize * 2, true));
|
| + Newline();
|
| } else {
|
| - bool should_break_to_next_line = true;
|
| - int indent = kIndentSize * 2;
|
| - if (CurrentColumn() + max_item_width + terminator.size() <=
|
| - kMaximumWidth ||
|
| - CurrentColumn() < margin_ + indent) {
|
| - should_break_to_next_line = false;
|
| - margin_ = CurrentColumn();
|
| - } else {
|
| - margin_ += indent;
|
| + stack_.push_back(IndentState(CurrentColumn(), true));
|
| + }
|
| +
|
| + for (size_t i = 0; i < list.size(); ++i) {
|
| + const auto& x = list[i];
|
| + if (i > 0) {
|
| + if (fits_on_current_line && !force_multiline)
|
| + Print(" ");
|
| + else
|
| + Newline();
|
| }
|
| - size_t i = 0;
|
| - for (const auto& x : list) {
|
| - // Function calls where all the arguments would fit at the current
|
| - // position should do that instead of going back to margin+4.
|
| - if (i > 0 || should_break_to_next_line)
|
| + bool want_comma = i < list.size() - 1 && !x->AsBlockComment();
|
| + Expr(x, kPrecedenceLowest, want_comma ? "," : std::string());
|
| + CHECK(!x->comments() || x->comments()->after().empty());
|
| + if (i < list.size() - 1) {
|
| + if (!want_comma)
|
| Newline();
|
| - ExprStyle expr_style = Expr(x, kPrecedenceLowest);
|
| - CHECK(!x->comments() || x->comments()->after().empty());
|
| - if (i < list.size() - 1) {
|
| - if (expr_style == kExprStyleRegular) {
|
| - Print(",");
|
| - } else {
|
| - Newline();
|
| - }
|
| - }
|
| - ++i;
|
| }
|
| + }
|
|
|
| - // Trailing comments.
|
| - if (end->comments()) {
|
| - if (!list.empty())
|
| - Newline();
|
| - for (const auto& c : end->comments()->before()) {
|
| - Newline();
|
| - TrimAndPrintToken(c);
|
| - }
|
| - if (!end->comments()->before().empty())
|
| - Newline();
|
| + // Trailing comments.
|
| + if (end->comments() && !end->comments()->before().empty()) {
|
| + if (!list.empty())
|
| + Newline();
|
| + for (const auto& c : end->comments()->before()) {
|
| + Newline();
|
| + TrimAndPrintToken(c);
|
| }
|
| + Newline();
|
| }
|
| + stack_.pop_back();
|
| }
|
|
|
| // Defer any end of line comment until we reach the newline.
|
| if (end->comments() && !end->comments()->suffix().empty()) {
|
| std::copy(end->comments()->suffix().begin(),
|
| - end->comments()->suffix().end(),
|
| - std::back_inserter(comments_));
|
| + end->comments()->suffix().end(), std::back_inserter(comments_));
|
| }
|
|
|
| Print(")");
|
| - margin_ = old_margin;
|
|
|
| if (have_block) {
|
| Print(" ");
|
| Sequence(kSequenceStyleBracedBlock,
|
| func_call->block()->statements(),
|
| - func_call->block()->End());
|
| + func_call->block()->End(),
|
| + false);
|
| }
|
| + return penalty + (CurrentLine() - start_line) * GetPenaltyForLineBreak();
|
| +}
|
| +
|
| +void Printer::InitializeSub(Printer* sub) {
|
| + sub->stack_ = stack_;
|
| + sub->comments_ = comments_;
|
| + sub->penalty_depth_ = penalty_depth_;
|
| + sub->Print(std::string(CurrentColumn(), 'x'));
|
| }
|
|
|
| void DoFormat(const ParseNode* root, bool dump_tree, std::string* output) {
|
|
|