| Index: tools/gn/command_format.cc
|
| diff --git a/tools/gn/command_format.cc b/tools/gn/command_format.cc
|
| index 0a110f1b6af5a6f42a0b7a26dee51141104461c3..f1340bf0a0b04d2565318eecc12745acc75043d6 100644
|
| --- a/tools/gn/command_format.cc
|
| +++ b/tools/gn/command_format.cc
|
| @@ -5,6 +5,7 @@
|
| #include <sstream>
|
|
|
| #include "base/command_line.h"
|
| +#include "base/strings/string_split.h"
|
| #include "tools/gn/commands.h"
|
| #include "tools/gn/input_file.h"
|
| #include "tools/gn/parser.h"
|
| @@ -34,6 +35,7 @@ const char kFormat_Help[] =
|
| namespace {
|
|
|
| const int kIndentSize = 2;
|
| +const int kMaximumWidth = 80;
|
|
|
| class Printer {
|
| public:
|
| @@ -58,6 +60,12 @@ class Printer {
|
| kExprStyleComment,
|
| };
|
|
|
| + struct Metrics {
|
| + Metrics() : length(-1), multiline(false) {}
|
| + int length;
|
| + bool multiline;
|
| + };
|
| +
|
| // Add to output.
|
| void Print(base::StringPiece str);
|
|
|
| @@ -82,6 +90,10 @@ class Printer {
|
| // added to the output.
|
| ExprStyle Expr(const ParseNode* root);
|
|
|
| + // 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);
|
| +
|
| // Format a list of values using the given style.
|
| // |end| holds any trailing comments to be printed just before the closing
|
| // bracket.
|
| @@ -206,6 +218,18 @@ void Printer::Block(const ParseNode* root) {
|
| }
|
| }
|
|
|
| +Printer::Metrics Printer::GetLengthOfExpr(const ParseNode* expr) {
|
| + Metrics result;
|
| + Printer sub;
|
| + sub.Expr(expr);
|
| + std::vector<std::string> lines;
|
| + base::SplitStringDontTrim(sub.String(), '\n', &lines);
|
| + result.multiline = lines.size() > 1;
|
| + for (const auto& line : lines)
|
| + result.length = std::max(result.length, static_cast<int>(line.size()));
|
| + return result;
|
| +}
|
| +
|
| Printer::ExprStyle Printer::Expr(const ParseNode* root) {
|
| ExprStyle result = kExprStyleRegular;
|
| if (root->comments()) {
|
| @@ -223,16 +247,44 @@ Printer::ExprStyle Printer::Expr(const ParseNode* root) {
|
| }
|
| }
|
|
|
| - if (root->AsAccessor()) {
|
| - Print("TODO(scottmg): AccessorNode");
|
| + if (const AccessorNode* accessor = root->AsAccessor()) {
|
| + Print(accessor->base().value());
|
| + if (accessor->member()) {
|
| + Print(".");
|
| + Expr(accessor->member());
|
| + } else {
|
| + CHECK(accessor->index());
|
| + Print("[");
|
| + Expr(accessor->index());
|
| + Print("]");
|
| + }
|
| } else if (const BinaryOpNode* binop = root->AsBinaryOp()) {
|
| // TODO(scottmg): Lots to do here for complex if expressions: reflowing,
|
| // parenthesizing, etc.
|
| - Expr(binop->left());
|
| - Print(" ");
|
| - Print(binop->op().value());
|
| - Print(" ");
|
| - Expr(binop->right());
|
| + Metrics left = GetLengthOfExpr(binop->left());
|
| + Metrics right = GetLengthOfExpr(binop->right());
|
| + int total_width = left.length +
|
| + static_cast<int>(binop->op().value().size()) + 2 +
|
| + right.length;
|
| + if (CurrentColumn() + total_width < kMaximumWidth ||
|
| + binop->right()->AsList()) {
|
| + // If it just fits normally, put it here.
|
| + Expr(binop->left());
|
| + Print(" ");
|
| + Print(binop->op().value());
|
| + Print(" ");
|
| + Expr(binop->right());
|
| + } else {
|
| + // Otherwise, put first argument and op, and indent next.
|
| + Expr(binop->left());
|
| + Print(" ");
|
| + Print(binop->op().value());
|
| + int old_margin = margin_;
|
| + margin_ += kIndentSize * 2;
|
| + Newline();
|
| + Expr(binop->right());
|
| + margin_ = old_margin;
|
| + }
|
| } else if (const BlockNode* block = root->AsBlock()) {
|
| Sequence(kSequenceStyleBracedBlock, block->statements(), block->End());
|
| } else if (const ConditionNode* condition = root->AsConditionNode()) {
|
| @@ -299,6 +351,9 @@ template <class PARSENODE>
|
| void Printer::Sequence(SequenceStyle style,
|
| const std::vector<PARSENODE*>& list,
|
| const ParseNode* end) {
|
| + int old_margin = margin_;
|
| + int indent =
|
| + style == kSequenceStyleFunctionCall ? kIndentSize * 2 : kIndentSize;
|
| bool force_multiline = false;
|
| if (style == kSequenceStyleFunctionCall)
|
| Print("(");
|
| @@ -319,9 +374,34 @@ void Printer::Sequence(SequenceStyle style,
|
| 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;
|
| + if (style == kSequenceStyleFunctionCall) {
|
| + int total_length = 0;
|
| + natural_lengths.reserve(list.size());
|
| + for (size_t i = 0; i < list.size(); ++i) {
|
| + Metrics sub = GetLengthOfExpr(list[i]);
|
| + if (sub.multiline)
|
| + fits_on_current_line = false;
|
| + natural_lengths.push_back(sub.length);
|
| + total_length += sub.length;
|
| + if (i < list.size() - 1)
|
| + total_length += 2; // ", "
|
| + }
|
| + // Strictly less than kMaximumWidth so there's room for closing ).
|
| + // TODO(scottmg): Need to know if there's an attached block for " {".
|
| + fits_on_current_line =
|
| + fits_on_current_line && CurrentColumn() + total_length < kMaximumWidth;
|
| + max_item_width =
|
| + *std::max_element(natural_lengths.begin(), natural_lengths.end());
|
| + }
|
| +
|
| if (list.size() == 0 && !force_multiline) {
|
| // No elements, and not forcing newlines, print nothing.
|
| - } else if (list.size() == 1 && !force_multiline) {
|
| + } else if (list.size() == 1 && !force_multiline && fits_on_current_line) {
|
| if (style != kSequenceStyleFunctionCall)
|
| Print(" ");
|
| Expr(list[0]);
|
| @@ -329,44 +409,73 @@ void Printer::Sequence(SequenceStyle style,
|
| if (style != kSequenceStyleFunctionCall)
|
| Print(" ");
|
| } else {
|
| - margin_ += kIndentSize;
|
| - size_t i = 0;
|
| - for (const auto& x : list) {
|
| - Newline();
|
| - // If:
|
| - // - we're going to output some comments, and;
|
| - // - we haven't just started this multiline list, and;
|
| - // - there isn't already a blank line here;
|
| - // Then: insert one.
|
| - if (i != 0 && x->comments() && !x->comments()->before().empty() &&
|
| - !HaveBlankLine()) {
|
| - Newline();
|
| + // Function calls get to be single line even with multiple arguments, if
|
| + // they fit inside the maximum width.
|
| + if (style == kSequenceStyleFunctionCall && !force_multiline &&
|
| + fits_on_current_line) {
|
| + for (size_t i = 0; i < list.size(); ++i) {
|
| + Expr(list[i]);
|
| + if (i < list.size() - 1)
|
| + Print(", ");
|
| }
|
| - ExprStyle expr_style = Expr(x);
|
| - CHECK(!x->comments() || x->comments()->after().empty());
|
| - if (i < list.size() - 1 || style == kSequenceStyleList) {
|
| - if ((style == kSequenceStyleList || kSequenceStyleFunctionCall) &&
|
| - expr_style == kExprStyleRegular) {
|
| - Print(",");
|
| - } else {
|
| + } else {
|
| + bool should_break_to_next_line = true;
|
| + if (style == kSequenceStyleFunctionCall &&
|
| + (CurrentColumn() + max_item_width < kMaximumWidth ||
|
| + CurrentColumn() < margin_ + indent)) {
|
| + should_break_to_next_line = false;
|
| + margin_ = CurrentColumn();
|
| + } else {
|
| + margin_ += indent;
|
| + }
|
| + 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)
|
| + Newline();
|
| + // If:
|
| + // - we're going to output some comments, and;
|
| + // - we haven't just started this multiline list, and;
|
| + // - there isn't already a blank line here;
|
| + // Then: insert one.
|
| + if (i != 0 && x->comments() && !x->comments()->before().empty() &&
|
| + !HaveBlankLine()) {
|
| Newline();
|
| }
|
| + ExprStyle expr_style = Expr(x);
|
| + CHECK(!x->comments() || x->comments()->after().empty());
|
| + if (i < list.size() - 1 || style == kSequenceStyleList) {
|
| + if ((style == kSequenceStyleList ||
|
| + style == kSequenceStyleFunctionCall) &&
|
| + expr_style == kExprStyleRegular) {
|
| + Print(",");
|
| + } else {
|
| + Newline();
|
| + }
|
| + }
|
| + ++i;
|
| }
|
| - ++i;
|
| - }
|
|
|
| - // Trailing comments.
|
| - if (end->comments()) {
|
| - if (!list.empty())
|
| - Newline();
|
| - for (const auto& c : end->comments()->before()) {
|
| + // Trailing comments.
|
| + if (end->comments()) {
|
| + if (!list.empty())
|
| + Newline();
|
| + for (const auto& c : end->comments()->before()) {
|
| + Newline();
|
| + TrimAndPrintToken(c);
|
| + }
|
| + }
|
| +
|
| + if (style == kSequenceStyleFunctionCall) {
|
| + if (end->comments() && !end->comments()->before().empty()) {
|
| + Newline();
|
| + }
|
| + } else {
|
| + margin_ = old_margin;
|
| Newline();
|
| - TrimAndPrintToken(c);
|
| }
|
| }
|
| -
|
| - margin_ -= kIndentSize;
|
| - Newline();
|
| }
|
|
|
| if (style == kSequenceStyleFunctionCall)
|
| @@ -375,6 +484,8 @@ void Printer::Sequence(SequenceStyle style,
|
| Print("]");
|
| else if (style == kSequenceStyleBracedBlock)
|
| Print("}");
|
| +
|
| + margin_ = old_margin;
|
| }
|
|
|
| } // namespace
|
|
|