Index: tools/gn/line_breaker.cc |
diff --git a/tools/gn/line_breaker.cc b/tools/gn/line_breaker.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a3015fe26ebed570257b971c159a78efab4d098a |
--- /dev/null |
+++ b/tools/gn/line_breaker.cc |
@@ -0,0 +1,204 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "tools/gn/line_breaker.h" |
+ |
+#include "tools/gn/parse_tree.h" |
+ |
+namespace commands { |
+ |
+const int kExcessCharacterPenalty = 1000000; |
+ |
+Precedence GetPrecedence(const base::StringPiece op) { |
+ if (op == "=" || op == "+=" || op == "-=") |
+ return kPrecedenceAssign; |
+ if (op == "||") |
+ return kPrecedenceOr; |
+ if (op == "&&") |
+ return kPrecedenceAnd; |
+ if (op == "<" || op == ">" || op == "==" || op == "!=" || op == "<=" || |
+ op == ">=") { |
+ return kPrecedenceCompare; |
+ } |
+ if (op == "+" || op == "-") |
+ return kPrecedenceAdd; |
+ if (op == "!") |
+ return kPrecedenceUnary; |
+ DCHECK(false); |
+ return kPrecedenceLowest; |
+} |
+ |
+AnnotatedToken::AnnotatedToken(const Token& token, Type type) |
+ : token_(token), type_(type) { |
+} |
+ |
+AnnotatedToken::~AnnotatedToken() { |
+} |
+ |
+void AddParen(int prec, |
+ int outer_prec, |
+ bool* parenthesized, |
+ std::vector<AnnotatedToken>* result) { |
+ if (prec < outer_prec) { |
+ result->push_back(AnnotatedToken(Token(Location(), Token::LEFT_PAREN, "("), |
+ AnnotatedToken::LEFT_PAREN_GROUPING)); |
+ *parenthesized = true; |
+ } |
+} |
+ |
+void FlattenUntilNextChunk(const ParseNode* root, |
+ int outer_prec, |
+ std::vector<AnnotatedToken>* result) { |
+ if (root) { |
+ bool parenthesized = false; |
+ if (const AccessorNode* accessor = root->AsAccessor()) { |
+ AddParen(kPrecedenceSuffix, outer_prec, &parenthesized, result); |
+ if (accessor->member()) { |
+ result->push_back(AnnotatedToken(Token(Location(), Token::DOT, "."), |
+ AnnotatedToken::UNDERLYING)); |
+ FlattenUntilNextChunk(accessor->member(), kPrecedenceLowest, result); |
+ } else { |
+ result->push_back( |
+ AnnotatedToken(Token(Location(), Token::LEFT_BRACKET, "["), |
+ AnnotatedToken::LEFT_BRACKET_ACCESSOR)); |
+ FlattenUntilNextChunk(accessor->index(), kPrecedenceLowest, result); |
+ result->push_back( |
+ AnnotatedToken(Token(Location(), Token::RIGHT_BRACKET, "]"), |
+ AnnotatedToken::UNDERLYING)); |
+ } |
+ } else if (const BinaryOpNode* binop = root->AsBinaryOp()) { |
+ Precedence prec = GetPrecedence(binop->op().value()); |
+ AddParen(prec, outer_prec, &parenthesized, result); |
+ FlattenUntilNextChunk(binop->left(), prec, result); |
+ result->push_back( |
+ AnnotatedToken(binop->op(), AnnotatedToken::UNDERLYING)); |
+ FlattenUntilNextChunk(binop->right(), prec + 1, result); |
+ } else if (const BlockNode* block = root->AsBlock()) { |
+ if (block->begin_token().type() != Token::INVALID) { |
+ result->push_back( |
+ AnnotatedToken(block->begin_token(), AnnotatedToken::UNDERLYING)); |
+ } |
+ // Don't walk rest after first. |
+ for (const auto& statement : block->statements()) { |
+ FlattenUntilNextChunk(statement, kPrecedenceLowest, result); |
+ break; |
+ } |
+ } else if (const ConditionNode* condition = root->AsConditionNode()) { |
+ FlattenUntilNextChunk(condition->condition(), kPrecedenceLowest, result); |
+ // Don't traverse into true/false as those count as breaks. |
+ } else if (const FunctionCallNode* func_call = root->AsFunctionCall()) { |
+ result->push_back( |
+ AnnotatedToken(func_call->function(), AnnotatedToken::UNDERLYING)); |
+ result->push_back( |
+ AnnotatedToken(func_call->args()->begin_token(), |
+ AnnotatedToken::LEFT_PAREN_FUNCTION_CALL)); |
+ size_t i = 0; |
+ for (const auto& node : func_call->args()->contents()) { |
+ FlattenUntilNextChunk(node, kPrecedenceLowest, result); |
+ if (i < func_call->args()->contents().size() - 1) { |
+ result->push_back(AnnotatedToken(Token(Location(), Token::COMMA, ","), |
+ AnnotatedToken::UNDERLYING)); |
+ } |
+ ++i; |
+ } |
+ FlattenUntilNextChunk(func_call->args()->End(), kPrecedenceLowest, |
+ result); |
+ } else if (const IdentifierNode* identifier = root->AsIdentifier()) { |
+ result->push_back( |
+ AnnotatedToken(identifier->value(), AnnotatedToken::UNDERLYING)); |
+ } else if (const ListNode* list = root->AsList()) { |
+ result->push_back(AnnotatedToken( |
+ list->begin_token(), AnnotatedToken::LEFT_BRACKET_LIST_LITERAL)); |
+ size_t i = 0; |
+ for (const auto& node : list->contents()) { |
+ FlattenUntilNextChunk(node, kPrecedenceLowest, result); |
+ if (i < list->contents().size() - 1) { |
+ result->push_back(AnnotatedToken(Token(Location(), Token::COMMA, ","), |
+ AnnotatedToken::UNDERLYING)); |
+ } |
+ ++i; |
+ } |
+ FlattenUntilNextChunk(list->End(), kPrecedenceLowest, result); |
+ } else if (const LiteralNode* literal = root->AsLiteral()) { |
+ result->push_back( |
+ AnnotatedToken(literal->value(), AnnotatedToken::UNDERLYING)); |
+ } else if (const UnaryOpNode* unaryop = root->AsUnaryOp()) { |
+ FlattenUntilNextChunk(unaryop->operand(), kPrecedenceUnary, result); |
+ } else if (const BlockCommentNode* comment = root->AsBlockComment()) { |
+ result->push_back( |
+ AnnotatedToken(comment->comment(), AnnotatedToken::UNDERLYING)); |
+ } else if (const EndNode* end = root->AsEnd()) { |
+ result->push_back( |
+ AnnotatedToken(end->value(), AnnotatedToken::UNDERLYING)); |
+ } else { |
+ CHECK(false) << "Unhandled case in FlattenUntilNextChunk."; |
+ } |
+ |
+ if (parenthesized) { |
+ result->push_back( |
+ AnnotatedToken(Token(Location(), Token::RIGHT_PAREN, ")"), |
+ AnnotatedToken::UNDERLYING)); |
+ } |
+ } |
+} |
+ |
+std::vector<AnnotatedToken> BuildAnnotatedTokenList( |
+ const ParseNode* statement) { |
+ std::vector<AnnotatedToken> result; |
+ FlattenUntilNextChunk(statement, kPrecedenceLowest, &result); |
+ return result; |
+} |
+ |
+LineBreaker::LineBreaker(const std::vector<AnnotatedToken>& annotated_tokens, |
+ int initial_column) |
+ : annotated_tokens_(annotated_tokens), |
+ initial_column_(initial_column), |
+ end_(static_cast<int>(annotated_tokens.size())) { |
+} |
+ |
+LineBreaker::~LineBreaker() { |
+} |
+ |
+std::string LineBreaker::GetBestLayout() { |
+ Format(0, initial_column_); |
+ return std::string(); |
+} |
+ |
+void LineBreaker::Format(int index, int start_column) { |
+ LineState state; |
+ state.column = start_column; |
+ state.next = index + 1; |
+ |
+ // todo move past first, it's already in place |
+ |
+ while (state.next < end_) { |
+ |
+ } |
+} |
+ |
+int LineBreaker::CalculatePenalty(LineState state, bool newline) { |
+ if (state.next == end_) |
+ return 0; |
+ |
+ int penalty = 0; |
+ |
+ AddTokenToState(newline, &state); |
+ |
+ // Assign penalty for being too long. |
+ if (state.column > kMaximumWidth) { |
+ int excess_characters = state.column - kMaximumWidth; |
+ penalty += excess_characters * kExcessCharacterPenalty; |
+ } |
+ |
+ int no_break = CalculatePenalty(state, false); |
+ int with_break = CalculatePenalty(state, true); |
+ int result = std::min(no_break, with_break); |
+ //penalty_cache_[state] = result; |
+ return result + penalty; |
+} |
+ |
+void LineBreaker::AddTokenToState(bool newline, LineState* state) { |
+} |
+ |
+} // namespace commands |