Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(722)

Side by Side Diff: tools/gn/command_format.cc

Issue 748863003: gn format: penalty-based scheme for line breaking (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@gn-more-disabled
Patch Set: x64 Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | tools/gn/command_format_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <sstream> 5 #include <sstream>
6 6
7 #include "base/command_line.h" 7 #include "base/command_line.h"
8 #include "base/files/file_util.h" 8 #include "base/files/file_util.h"
9 #include "base/strings/string_split.h" 9 #include "base/strings/string_split.h"
10 #include "tools/gn/commands.h" 10 #include "tools/gn/commands.h"
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
49 " gn format //some/BUILD.gn\n" 49 " gn format //some/BUILD.gn\n"
50 " gn format some\\BUILD.gn\n" 50 " gn format some\\BUILD.gn\n"
51 " gn format /abspath/some/BUILD.gn\n" 51 " gn format /abspath/some/BUILD.gn\n"
52 " gn format --stdin\n"; 52 " gn format --stdin\n";
53 53
54 namespace { 54 namespace {
55 55
56 const int kIndentSize = 2; 56 const int kIndentSize = 2;
57 const int kMaximumWidth = 80; 57 const int kMaximumWidth = 80;
58 58
59 const int kPenaltyLineBreak = 500;
60 const int kPenaltyHorizontalSeparation = 100;
61 const int kPenaltyExcess = 10000;
62 const int kPenaltyBrokenLineOnOneLiner = 1000;
63
59 enum Precedence { 64 enum Precedence {
60 kPrecedenceLowest, 65 kPrecedenceLowest,
61 kPrecedenceAssign, 66 kPrecedenceAssign,
62 kPrecedenceOr, 67 kPrecedenceOr,
63 kPrecedenceAnd, 68 kPrecedenceAnd,
64 kPrecedenceCompare, 69 kPrecedenceCompare,
65 kPrecedenceAdd, 70 kPrecedenceAdd,
66 kPrecedenceSuffix, 71 kPrecedenceSuffix,
67 kPrecedenceUnary, 72 kPrecedenceUnary,
68 }; 73 };
69 74
70 class Printer { 75 class Printer {
71 public: 76 public:
72 Printer(); 77 Printer();
73 ~Printer(); 78 ~Printer();
74 79
75 void Block(const ParseNode* file); 80 void Block(const ParseNode* file);
76 81
77 std::string String() const { return output_; } 82 std::string String() const { return output_; }
78 83
79 private: 84 private:
80 // Format a list of values using the given style. 85 // Format a list of values using the given style.
81 enum SequenceStyle { 86 enum SequenceStyle {
82 kSequenceStyleList, 87 kSequenceStyleList,
83 kSequenceStyleBlock, 88 kSequenceStyleBlock,
84 kSequenceStyleBracedBlock, 89 kSequenceStyleBracedBlock,
85 }; 90 };
86 91
87 enum ExprStyle {
88 kExprStyleRegular,
89 kExprStyleComment,
90 };
91
92 struct Metrics { 92 struct Metrics {
93 Metrics() : first_length(-1), longest_length(-1), multiline(false) {} 93 Metrics() : first_length(-1), longest_length(-1), multiline(false) {}
94 int first_length; 94 int first_length;
95 int longest_length; 95 int longest_length;
96 bool multiline; 96 bool multiline;
97 }; 97 };
98 98
99 // Add to output. 99 // Add to output.
100 void Print(base::StringPiece str); 100 void Print(base::StringPiece str);
101 101
102 // Add the current margin (as spaces) to the output. 102 // Add the current margin (as spaces) to the output.
103 void PrintMargin(); 103 void PrintMargin();
104 104
105 void TrimAndPrintToken(const Token& token); 105 void TrimAndPrintToken(const Token& token);
106 106
107 // End the current line, flushing end of line comments. 107 // End the current line, flushing end of line comments.
108 void Newline(); 108 void Newline();
109 109
110 // Remove trailing spaces from the current line. 110 // Remove trailing spaces from the current line.
111 void Trim(); 111 void Trim();
112 112
113 // Whether there's a blank separator line at the current position. 113 // Whether there's a blank separator line at the current position.
114 bool HaveBlankLine(); 114 bool HaveBlankLine();
115 115
116 bool IsAssignment(const ParseNode* node); 116 // Flag assignments to sources, deps, etc. to make their RHSs multiline.
117 void AnnotatePreferedMultilineAssignment(const BinaryOpNode* binop);
117 118
118 // Heuristics to decide if there should be a blank line added between two 119 // Heuristics to decide if there should be a blank line added between two
119 // items. For various "small" items, it doesn't look nice if there's too much 120 // items. For various "small" items, it doesn't look nice if there's too much
120 // vertical whitespace added. 121 // vertical whitespace added.
121 bool ShouldAddBlankLineInBetween(const ParseNode* a, const ParseNode* b); 122 bool ShouldAddBlankLineInBetween(const ParseNode* a, const ParseNode* b);
122 123
123 // Get the 0-based x position on the current line. 124 // Get the 0-based x position on the current line.
124 int CurrentColumn(); 125 int CurrentColumn() const;
126
127 // Get the current line in the output;
128 int CurrentLine() const;
125 129
126 // Adds an opening ( if prec is less than the outers (to maintain evalution 130 // Adds an opening ( if prec is less than the outers (to maintain evalution
127 // order for a subexpression). If an opening paren is emitted, *parenthesized 131 // order for a subexpression). If an opening paren is emitted, *parenthesized
128 // will be set so it can be closed at the end of the expression. 132 // will be set so it can be closed at the end of the expression.
129 void AddParen(int prec, int outer_prec, bool* parenthesized); 133 void AddParen(int prec, int outer_prec, bool* parenthesized);
130 134
131 // Print the expression to the output buffer. Returns the type of element 135 // Print the expression to the output buffer. Returns the type of element
132 // added to the output. The value of outer_prec gives the precedence of the 136 // added to the output. The value of outer_prec gives the precedence of the
133 // operator outside this Expr. If that operator binds tighter than root's, 137 // operator outside this Expr. If that operator binds tighter than root's,
134 // Expr must introduce parentheses. 138 // Expr must introduce parentheses.
135 ExprStyle Expr(const ParseNode* root, int outer_prec); 139 int Expr(const ParseNode* root, int outer_prec, const std::string& suffix);
136 140
137 // Use a sub-Printer recursively to figure out the size that an expression 141 // Generic penalties for exceeding maximum width, adding more lines, etc.
138 // would be before actually adding it to the output. 142 int AssessPenalty(const std::string& output);
139 Metrics GetLengthOfExpr(const ParseNode* expr, int outer_prec);
140 143
141 // Format a list of values using the given style. 144 // Format a list of values using the given style.
142 // |end| holds any trailing comments to be printed just before the closing 145 // |end| holds any trailing comments to be printed just before the closing
143 // bracket. 146 // bracket.
144 template <class PARSENODE> // Just for const covariance. 147 template <class PARSENODE> // Just for const covariance.
145 void Sequence(SequenceStyle style, 148 void Sequence(SequenceStyle style,
146 const std::vector<PARSENODE*>& list, 149 const std::vector<PARSENODE*>& list,
147 const ParseNode* end); 150 const ParseNode* end,
151 bool force_multiline);
148 152
149 void FunctionCall(const FunctionCallNode* func_call); 153 // Returns the penalty.
154 int FunctionCall(const FunctionCallNode* func_call);
155
156 // Create a clone of this Printer in a similar state (other than the output,
157 // but including margins, etc.) to be used for dry run measurements.
158 void InitializeSub(Printer* sub);
150 159
151 std::string output_; // Output buffer. 160 std::string output_; // Output buffer.
152 std::vector<Token> comments_; // Pending end-of-line comments. 161 std::vector<Token> comments_; // Pending end-of-line comments.
153 int margin_; // Left margin (number of spaces). 162 int margin() const { return stack_.back().margin; }
163
164 int penalty_depth_;
165 int GetPenaltyForLineBreak() const {
166 return penalty_depth_ * kPenaltyLineBreak;
167 }
168
169 struct IndentState {
170 IndentState() : margin(0), continuation_requires_indent(false) {}
171 IndentState(int margin, bool continuation_requires_indent)
172 : margin(margin),
173 continuation_requires_indent(continuation_requires_indent) {}
174
175 // The left margin (number of spaces).
176 int margin;
177
178 bool continuation_requires_indent;
179 };
180 // Stack used to track
181 std::vector<IndentState> stack_;
154 182
155 // Gives the precedence for operators in a BinaryOpNode. 183 // Gives the precedence for operators in a BinaryOpNode.
156 std::map<base::StringPiece, Precedence> precedence_; 184 std::map<base::StringPiece, Precedence> precedence_;
157 185
158 DISALLOW_COPY_AND_ASSIGN(Printer); 186 DISALLOW_COPY_AND_ASSIGN(Printer);
159 }; 187 };
160 188
161 Printer::Printer() : margin_(0) { 189 Printer::Printer() : penalty_depth_(0) {
162 output_.reserve(100 << 10); 190 output_.reserve(100 << 10);
163 precedence_["="] = kPrecedenceAssign; 191 precedence_["="] = kPrecedenceAssign;
164 precedence_["+="] = kPrecedenceAssign; 192 precedence_["+="] = kPrecedenceAssign;
165 precedence_["-="] = kPrecedenceAssign; 193 precedence_["-="] = kPrecedenceAssign;
166 precedence_["||"] = kPrecedenceOr; 194 precedence_["||"] = kPrecedenceOr;
167 precedence_["&&"] = kPrecedenceAnd; 195 precedence_["&&"] = kPrecedenceAnd;
168 precedence_["<"] = kPrecedenceCompare; 196 precedence_["<"] = kPrecedenceCompare;
169 precedence_[">"] = kPrecedenceCompare; 197 precedence_[">"] = kPrecedenceCompare;
170 precedence_["=="] = kPrecedenceCompare; 198 precedence_["=="] = kPrecedenceCompare;
171 precedence_["!="] = kPrecedenceCompare; 199 precedence_["!="] = kPrecedenceCompare;
172 precedence_["<="] = kPrecedenceCompare; 200 precedence_["<="] = kPrecedenceCompare;
173 precedence_[">="] = kPrecedenceCompare; 201 precedence_[">="] = kPrecedenceCompare;
174 precedence_["+"] = kPrecedenceAdd; 202 precedence_["+"] = kPrecedenceAdd;
175 precedence_["-"] = kPrecedenceAdd; 203 precedence_["-"] = kPrecedenceAdd;
176 precedence_["!"] = kPrecedenceUnary; 204 precedence_["!"] = kPrecedenceUnary;
205 stack_.push_back(IndentState());
177 } 206 }
178 207
179 Printer::~Printer() { 208 Printer::~Printer() {
180 } 209 }
181 210
182 void Printer::Print(base::StringPiece str) { 211 void Printer::Print(base::StringPiece str) {
183 str.AppendToString(&output_); 212 str.AppendToString(&output_);
184 } 213 }
185 214
186 void Printer::PrintMargin() { 215 void Printer::PrintMargin() {
187 output_ += std::string(margin_, ' '); 216 output_ += std::string(margin(), ' ');
188 } 217 }
189 218
190 void Printer::TrimAndPrintToken(const Token& token) { 219 void Printer::TrimAndPrintToken(const Token& token) {
191 std::string trimmed; 220 std::string trimmed;
192 TrimWhitespaceASCII(token.value().as_string(), base::TRIM_ALL, &trimmed); 221 TrimWhitespaceASCII(token.value().as_string(), base::TRIM_ALL, &trimmed);
193 Print(trimmed); 222 Print(trimmed);
194 } 223 }
195 224
196 void Printer::Newline() { 225 void Printer::Newline() {
197 if (!comments_.empty()) { 226 if (!comments_.empty()) {
198 Print(" "); 227 Print(" ");
199 int i = 0;
200 // Save the margin, and temporarily set it to where the first comment 228 // Save the margin, and temporarily set it to where the first comment
201 // starts so that multiple suffix comments are vertically aligned. This 229 // starts so that multiple suffix comments are vertically aligned. This
202 // will need to be fancier once we enforce 80 col. 230 // will need to be fancier once we enforce 80 col.
203 int old_margin = margin_; 231 stack_.push_back(IndentState(CurrentColumn(), false));
232 int i = 0;
204 for (const auto& c : comments_) { 233 for (const auto& c : comments_) {
205 if (i == 0) 234 if (i > 0) {
206 margin_ = CurrentColumn();
207 else {
208 Trim(); 235 Trim();
209 Print("\n"); 236 Print("\n");
210 PrintMargin(); 237 PrintMargin();
211 } 238 }
212 TrimAndPrintToken(c); 239 TrimAndPrintToken(c);
213 ++i; 240 ++i;
214 } 241 }
215 margin_ = old_margin; 242 stack_.pop_back();
216 comments_.clear(); 243 comments_.clear();
217 } 244 }
218 Trim(); 245 Trim();
219 Print("\n"); 246 Print("\n");
220 PrintMargin(); 247 PrintMargin();
221 } 248 }
222 249
223 void Printer::Trim() { 250 void Printer::Trim() {
224 size_t n = output_.size(); 251 size_t n = output_.size();
225 while (n > 0 && output_[n - 1] == ' ') 252 while (n > 0 && output_[n - 1] == ' ')
226 --n; 253 --n;
227 output_.resize(n); 254 output_.resize(n);
228 } 255 }
229 256
230 bool Printer::HaveBlankLine() { 257 bool Printer::HaveBlankLine() {
231 size_t n = output_.size(); 258 size_t n = output_.size();
232 while (n > 0 && output_[n - 1] == ' ') 259 while (n > 0 && output_[n - 1] == ' ')
233 --n; 260 --n;
234 return n > 2 && output_[n - 1] == '\n' && output_[n - 2] == '\n'; 261 return n > 2 && output_[n - 1] == '\n' && output_[n - 2] == '\n';
235 } 262 }
236 263
237 bool Printer::IsAssignment(const ParseNode* node) { 264 void Printer::AnnotatePreferedMultilineAssignment(const BinaryOpNode* binop) {
238 return node->AsBinaryOp() && (node->AsBinaryOp()->op().value() == "=" || 265 const IdentifierNode* ident = binop->left()->AsIdentifier();
239 node->AsBinaryOp()->op().value() == "+=" || 266 const ListNode* list = binop->right()->AsList();
240 node->AsBinaryOp()->op().value() == "-="); 267 // This is somewhat arbitrary, but we include the 'deps'- and 'sources'-like
268 // things, but not flags things.
269 if (binop->op().value() == "=" && ident && list &&
270 (ident->value().value() == "data" ||
271 ident->value().value() == "datadeps" ||
272 ident->value().value() == "deps" || ident->value().value() == "inputs" ||
273 ident->value().value() == "public" ||
274 ident->value().value() == "public_deps" ||
275 ident->value().value() == "sources")) {
276 const_cast<ListNode*>(list)->set_prefer_multiline(true);
277 }
241 } 278 }
242 279
243 bool Printer::ShouldAddBlankLineInBetween(const ParseNode* a, 280 bool Printer::ShouldAddBlankLineInBetween(const ParseNode* a,
244 const ParseNode* b) { 281 const ParseNode* b) {
245 LocationRange a_range = a->GetRange(); 282 LocationRange a_range = a->GetRange();
246 LocationRange b_range = b->GetRange(); 283 LocationRange b_range = b->GetRange();
247 // If they're already separated by 1 or more lines, then we want to keep a 284 // If they're already separated by 1 or more lines, then we want to keep a
248 // blank line. 285 // blank line.
249 return b_range.begin().line_number() > a_range.end().line_number() + 1; 286 return b_range.begin().line_number() > a_range.end().line_number() + 1;
250 } 287 }
251 288
252 int Printer::CurrentColumn() { 289 int Printer::CurrentColumn() const {
253 int n = 0; 290 int n = 0;
254 while (n < static_cast<int>(output_.size()) && 291 while (n < static_cast<int>(output_.size()) &&
255 output_[output_.size() - 1 - n] != '\n') { 292 output_[output_.size() - 1 - n] != '\n') {
256 ++n; 293 ++n;
257 } 294 }
258 return n; 295 return n;
259 } 296 }
260 297
298 int Printer::CurrentLine() const {
299 int count = 1;
300 for (const char* p = output_.c_str(); (p = strchr(p, '\n')) != NULL;) {
301 ++count;
302 ++p;
303 }
304 return count;
305 }
306
261 void Printer::Block(const ParseNode* root) { 307 void Printer::Block(const ParseNode* root) {
262 const BlockNode* block = root->AsBlock(); 308 const BlockNode* block = root->AsBlock();
263 309
264 if (block->comments()) { 310 if (block->comments()) {
265 for (const auto& c : block->comments()->before()) { 311 for (const auto& c : block->comments()->before()) {
266 TrimAndPrintToken(c); 312 TrimAndPrintToken(c);
267 Newline(); 313 Newline();
268 } 314 }
269 } 315 }
270 316
271 size_t i = 0; 317 size_t i = 0;
272 for (const auto& stmt : block->statements()) { 318 for (const auto& stmt : block->statements()) {
273 Expr(stmt, kPrecedenceLowest); 319 Expr(stmt, kPrecedenceLowest, std::string());
274 Newline(); 320 Newline();
275 if (stmt->comments()) { 321 if (stmt->comments()) {
276 // Why are before() not printed here too? before() are handled inside 322 // Why are before() not printed here too? before() are handled inside
277 // Expr(), as are suffix() which are queued to the next Newline(). 323 // Expr(), as are suffix() which are queued to the next Newline().
278 // However, because it's a general expression handler, it doesn't insert 324 // However, because it's a general expression handler, it doesn't insert
279 // the newline itself, which only happens between block statements. So, 325 // the newline itself, which only happens between block statements. So,
280 // the after are handled explicitly here. 326 // the after are handled explicitly here.
281 for (const auto& c : stmt->comments()->after()) { 327 for (const auto& c : stmt->comments()->after()) {
282 TrimAndPrintToken(c); 328 TrimAndPrintToken(c);
283 Newline(); 329 Newline();
284 } 330 }
285 } 331 }
286 if (i < block->statements().size() - 1 && 332 if (i < block->statements().size() - 1 &&
287 (ShouldAddBlankLineInBetween(block->statements()[i], 333 (ShouldAddBlankLineInBetween(block->statements()[i],
288 block->statements()[i + 1]))) { 334 block->statements()[i + 1]))) {
289 Newline(); 335 Newline();
290 } 336 }
291 ++i; 337 ++i;
292 } 338 }
293 339
294 if (block->comments()) { 340 if (block->comments()) {
295 for (const auto& c : block->comments()->after()) { 341 for (const auto& c : block->comments()->after()) {
296 TrimAndPrintToken(c); 342 TrimAndPrintToken(c);
297 Newline(); 343 Newline();
298 } 344 }
299 } 345 }
300 } 346 }
301 347
302 Printer::Metrics Printer::GetLengthOfExpr(const ParseNode* expr, 348 int Printer::AssessPenalty(const std::string& output) {
303 int outer_prec) { 349 int penalty = 0;
304 Metrics result;
305 Printer sub;
306 sub.Expr(expr, outer_prec);
307 std::vector<std::string> lines; 350 std::vector<std::string> lines;
308 base::SplitStringDontTrim(sub.String(), '\n', &lines); 351 base::SplitStringDontTrim(output, '\n', &lines);
309 result.multiline = lines.size() > 1; 352 penalty += static_cast<int>(lines.size() - 1) * GetPenaltyForLineBreak();
310 result.first_length = static_cast<int>(lines[0].size());
311 for (const auto& line : lines) { 353 for (const auto& line : lines) {
312 result.longest_length = 354 if (line.size() > kMaximumWidth)
313 std::max(result.longest_length, static_cast<int>(line.size())); 355 penalty += static_cast<int>(line.size() - kMaximumWidth) * kPenaltyExcess;
314 } 356 }
315 return result; 357 return penalty;
316 } 358 }
317 359
318 void Printer::AddParen(int prec, int outer_prec, bool* parenthesized) { 360 void Printer::AddParen(int prec, int outer_prec, bool* parenthesized) {
319 if (prec < outer_prec) { 361 if (prec < outer_prec) {
320 Print("("); 362 Print("(");
321 *parenthesized = true; 363 *parenthesized = true;
322 } 364 }
323 } 365 }
324 366
325 Printer::ExprStyle Printer::Expr(const ParseNode* root, int outer_prec) { 367 int Printer::Expr(const ParseNode* root,
326 ExprStyle result = kExprStyleRegular; 368 int outer_prec,
369 const std::string& suffix) {
370 int penalty = 0;
371 penalty_depth_++;
372
327 if (root->comments()) { 373 if (root->comments()) {
328 if (!root->comments()->before().empty()) { 374 if (!root->comments()->before().empty()) {
329 Trim(); 375 Trim();
330 // If there's already other text on the line, start a new line. 376 // If there's already other text on the line, start a new line.
331 if (CurrentColumn() > 0) 377 if (CurrentColumn() > 0)
332 Print("\n"); 378 Print("\n");
333 // We're printing a line comment, so we need to be at the current margin. 379 // We're printing a line comment, so we need to be at the current margin.
334 PrintMargin(); 380 PrintMargin();
335 for (const auto& c : root->comments()->before()) { 381 for (const auto& c : root->comments()->before()) {
336 TrimAndPrintToken(c); 382 TrimAndPrintToken(c);
337 Newline(); 383 Newline();
338 } 384 }
339 } 385 }
340 } 386 }
341 387
342 bool parenthesized = false; 388 bool parenthesized = false;
343 389
344 if (const AccessorNode* accessor = root->AsAccessor()) { 390 if (const AccessorNode* accessor = root->AsAccessor()) {
345 AddParen(kPrecedenceSuffix, outer_prec, &parenthesized); 391 AddParen(kPrecedenceSuffix, outer_prec, &parenthesized);
346 Print(accessor->base().value()); 392 Print(accessor->base().value());
347 if (accessor->member()) { 393 if (accessor->member()) {
348 Print("."); 394 Print(".");
349 Expr(accessor->member(), kPrecedenceLowest); 395 Expr(accessor->member(), kPrecedenceLowest, std::string());
350 } else { 396 } else {
351 CHECK(accessor->index()); 397 CHECK(accessor->index());
352 Print("["); 398 Print("[");
353 Expr(accessor->index(), kPrecedenceLowest); 399 Expr(accessor->index(), kPrecedenceLowest, "]");
354 Print("]");
355 } 400 }
356 } else if (const BinaryOpNode* binop = root->AsBinaryOp()) { 401 } else if (const BinaryOpNode* binop = root->AsBinaryOp()) {
357 CHECK(precedence_.find(binop->op().value()) != precedence_.end()); 402 CHECK(precedence_.find(binop->op().value()) != precedence_.end());
403 AnnotatePreferedMultilineAssignment(binop);
358 Precedence prec = precedence_[binop->op().value()]; 404 Precedence prec = precedence_[binop->op().value()];
359 AddParen(prec, outer_prec, &parenthesized); 405 AddParen(prec, outer_prec, &parenthesized);
360 Metrics right = GetLengthOfExpr(binop->right(), prec + 1); 406 int start_line = CurrentLine();
361 int op_length = static_cast<int>(binop->op().value().size()) + 2; 407 int start_column = CurrentColumn();
362 Expr(binop->left(), prec); 408 int indent_column =
363 if (CurrentColumn() + op_length + right.first_length <= kMaximumWidth) { 409 (binop->op().value() == "=" || binop->op().value() == "+=" ||
364 // If it just fits normally, put it here. 410 binop->op().value() == "-=")
411 ? margin() + kIndentSize * 2
412 : start_column;
413 if (stack_.back().continuation_requires_indent)
414 indent_column += kIndentSize * 2;
415
416 Expr(binop->left(),
417 prec,
418 std::string(" ") + binop->op().value().as_string());
419
420 // Single line.
421 Printer sub1;
422 InitializeSub(&sub1);
423 sub1.stack_.push_back(IndentState(indent_column, false));
424 sub1.Print(" ");
425 int penalty_current_line =
426 sub1.Expr(binop->right(), prec + 1, std::string());
427 sub1.Print(suffix);
428 penalty_current_line += AssessPenalty(sub1.String());
429
430 // Break after operator.
431 Printer sub2;
432 InitializeSub(&sub2);
433 sub2.stack_.push_back(IndentState(indent_column, false));
434 sub2.Newline();
435 int penalty_next_line = sub2.Expr(binop->right(), prec + 1, std::string());
436 sub2.Print(suffix);
437 penalty_next_line += AssessPenalty(sub2.String());
438
439 if (penalty_current_line < penalty_next_line) {
365 Print(" "); 440 Print(" ");
366 Print(binop->op().value()); 441 Expr(binop->right(), prec + 1, std::string());
367 Print(" ");
368 Expr(binop->right(), prec + 1);
369 } else { 442 } else {
370 // Otherwise, put first argument and op, and indent next. 443 // Otherwise, put first argument and op, and indent next.
371 Print(" "); 444 stack_.push_back(IndentState(indent_column, false));
372 Print(binop->op().value());
373 int old_margin = margin_;
374 margin_ += kIndentSize * 2;
375 Newline(); 445 Newline();
376 Expr(binop->right(), prec + 1); 446 penalty += std::abs(CurrentColumn() - start_column) *
377 margin_ = old_margin; 447 kPenaltyHorizontalSeparation;
448 Expr(binop->right(), prec + 1, std::string());
449 stack_.pop_back();
378 } 450 }
451 penalty += (CurrentLine() - start_line) * GetPenaltyForLineBreak();
379 } else if (const BlockNode* block = root->AsBlock()) { 452 } else if (const BlockNode* block = root->AsBlock()) {
380 Sequence(kSequenceStyleBracedBlock, block->statements(), block->End()); 453 Sequence(
454 kSequenceStyleBracedBlock, block->statements(), block->End(), false);
381 } else if (const ConditionNode* condition = root->AsConditionNode()) { 455 } else if (const ConditionNode* condition = root->AsConditionNode()) {
382 Print("if ("); 456 Print("if (");
383 Expr(condition->condition(), kPrecedenceLowest); 457 // TODO(scottmg): The { needs to be included in the suffix here.
384 Print(") "); 458 Expr(condition->condition(), kPrecedenceLowest, ") ");
385 Sequence(kSequenceStyleBracedBlock, 459 Sequence(kSequenceStyleBracedBlock,
386 condition->if_true()->statements(), 460 condition->if_true()->statements(),
387 condition->if_true()->End()); 461 condition->if_true()->End(),
462 false);
388 if (condition->if_false()) { 463 if (condition->if_false()) {
389 Print(" else "); 464 Print(" else ");
390 // If it's a block it's a bare 'else', otherwise it's an 'else if'. See 465 // If it's a block it's a bare 'else', otherwise it's an 'else if'. See
391 // ConditionNode::Execute. 466 // ConditionNode::Execute.
392 bool is_else_if = condition->if_false()->AsBlock() == NULL; 467 bool is_else_if = condition->if_false()->AsBlock() == NULL;
393 if (is_else_if) { 468 if (is_else_if) {
394 Expr(condition->if_false(), kPrecedenceLowest); 469 Expr(condition->if_false(), kPrecedenceLowest, std::string());
395 } else { 470 } else {
396 Sequence(kSequenceStyleBracedBlock, 471 Sequence(kSequenceStyleBracedBlock,
397 condition->if_false()->AsBlock()->statements(), 472 condition->if_false()->AsBlock()->statements(),
398 condition->if_false()->AsBlock()->End()); 473 condition->if_false()->AsBlock()->End(), false);
399 } 474 }
400 } 475 }
401 } else if (const FunctionCallNode* func_call = root->AsFunctionCall()) { 476 } else if (const FunctionCallNode* func_call = root->AsFunctionCall()) {
402 FunctionCall(func_call); 477 penalty += FunctionCall(func_call);
403 } else if (const IdentifierNode* identifier = root->AsIdentifier()) { 478 } else if (const IdentifierNode* identifier = root->AsIdentifier()) {
404 Print(identifier->value().value()); 479 Print(identifier->value().value());
405 } else if (const ListNode* list = root->AsList()) { 480 } else if (const ListNode* list = root->AsList()) {
406 Sequence(kSequenceStyleList, list->contents(), list->End()); 481 bool force_multiline =
482 list->prefer_multiline() && !list->contents().empty();
483 Sequence(
484 kSequenceStyleList, list->contents(), list->End(), force_multiline);
407 } else if (const LiteralNode* literal = root->AsLiteral()) { 485 } else if (const LiteralNode* literal = root->AsLiteral()) {
408 // TODO(scottmg): Quoting?
409 Print(literal->value().value()); 486 Print(literal->value().value());
410 } else if (const UnaryOpNode* unaryop = root->AsUnaryOp()) { 487 } else if (const UnaryOpNode* unaryop = root->AsUnaryOp()) {
411 Print(unaryop->op().value()); 488 Print(unaryop->op().value());
412 Expr(unaryop->operand(), kPrecedenceUnary); 489 Expr(unaryop->operand(), kPrecedenceUnary, std::string());
413 } else if (const BlockCommentNode* block_comment = root->AsBlockComment()) { 490 } else if (const BlockCommentNode* block_comment = root->AsBlockComment()) {
414 Print(block_comment->comment().value()); 491 Print(block_comment->comment().value());
415 result = kExprStyleComment;
416 } else if (const EndNode* end = root->AsEnd()) { 492 } else if (const EndNode* end = root->AsEnd()) {
417 Print(end->value().value()); 493 Print(end->value().value());
418 } else { 494 } else {
419 CHECK(false) << "Unhandled case in Expr."; 495 CHECK(false) << "Unhandled case in Expr.";
420 } 496 }
421 497
422 if (parenthesized) 498 if (parenthesized)
423 Print(")"); 499 Print(")");
424 500
425 // Defer any end of line comment until we reach the newline. 501 // Defer any end of line comment until we reach the newline.
426 if (root->comments() && !root->comments()->suffix().empty()) { 502 if (root->comments() && !root->comments()->suffix().empty()) {
427 std::copy(root->comments()->suffix().begin(), 503 std::copy(root->comments()->suffix().begin(),
428 root->comments()->suffix().end(), 504 root->comments()->suffix().end(),
429 std::back_inserter(comments_)); 505 std::back_inserter(comments_));
430 } 506 }
431 507
432 return result; 508 Print(suffix);
509
510 penalty_depth_--;
511 return penalty;
433 } 512 }
434 513
435 template <class PARSENODE> 514 template <class PARSENODE>
436 void Printer::Sequence(SequenceStyle style, 515 void Printer::Sequence(SequenceStyle style,
437 const std::vector<PARSENODE*>& list, 516 const std::vector<PARSENODE*>& list,
438 const ParseNode* end) { 517 const ParseNode* end,
439 bool force_multiline = false; 518 bool force_multiline) {
440 if (style == kSequenceStyleList) 519 if (style == kSequenceStyleList)
441 Print("["); 520 Print("[");
442 else if (style == kSequenceStyleBracedBlock) 521 else if (style == kSequenceStyleBracedBlock)
443 Print("{"); 522 Print("{");
444 523
445 if (style == kSequenceStyleBlock || style == kSequenceStyleBracedBlock) 524 if (style == kSequenceStyleBlock || style == kSequenceStyleBracedBlock)
446 force_multiline = true; 525 force_multiline = true;
447 526
448 if (end && end->comments() && !end->comments()->before().empty()) 527 if (end && end->comments() && !end->comments()->before().empty())
449 force_multiline = true; 528 force_multiline = true;
450 529
451 // If there's before line comments, make sure we have a place to put them. 530 // If there's before line comments, make sure we have a place to put them.
452 for (const auto& i : list) { 531 for (const auto& i : list) {
453 if (i->comments() && !i->comments()->before().empty()) 532 if (i->comments() && !i->comments()->before().empty())
454 force_multiline = true; 533 force_multiline = true;
455 } 534 }
456 535
457 if (list.size() == 0 && !force_multiline) { 536 if (list.size() == 0 && !force_multiline) {
458 // No elements, and not forcing newlines, print nothing. 537 // No elements, and not forcing newlines, print nothing.
459 } else if (list.size() == 1 && !force_multiline) { 538 } else if (list.size() == 1 && !force_multiline) {
460 Print(" "); 539 Print(" ");
461 Expr(list[0], kPrecedenceLowest); 540 Expr(list[0], kPrecedenceLowest, std::string());
462 CHECK(!list[0]->comments() || list[0]->comments()->after().empty()); 541 CHECK(!list[0]->comments() || list[0]->comments()->after().empty());
463 Print(" "); 542 Print(" ");
464 } else { 543 } else {
465 margin_ += kIndentSize; 544 stack_.push_back(
545 IndentState(margin() + kIndentSize, style == kSequenceStyleList));
466 size_t i = 0; 546 size_t i = 0;
467 for (const auto& x : list) { 547 for (const auto& x : list) {
468 Newline(); 548 Newline();
469 // If: 549 // If:
470 // - we're going to output some comments, and; 550 // - we're going to output some comments, and;
471 // - we haven't just started this multiline list, and; 551 // - we haven't just started this multiline list, and;
472 // - there isn't already a blank line here; 552 // - there isn't already a blank line here;
473 // Then: insert one. 553 // Then: insert one.
474 if (i != 0 && x->comments() && !x->comments()->before().empty() && 554 if (i != 0 && x->comments() && !x->comments()->before().empty() &&
475 !HaveBlankLine()) { 555 !HaveBlankLine()) {
476 Newline(); 556 Newline();
477 } 557 }
478 ExprStyle expr_style = Expr(x, kPrecedenceLowest); 558 bool body_of_list = i < list.size() - 1 || style == kSequenceStyleList;
559 bool want_comma =
560 body_of_list && (style == kSequenceStyleList && !x->AsBlockComment());
561 Expr(x, kPrecedenceLowest, want_comma ? "," : std::string());
479 CHECK(!x->comments() || x->comments()->after().empty()); 562 CHECK(!x->comments() || x->comments()->after().empty());
480 if (i < list.size() - 1 || style == kSequenceStyleList) { 563 if (body_of_list) {
481 if (style == kSequenceStyleList && expr_style == kExprStyleRegular) { 564 if (!want_comma) {
482 Print(",");
483 } else {
484 if (i < list.size() - 1 && 565 if (i < list.size() - 1 &&
485 ShouldAddBlankLineInBetween(list[i], list[i + 1])) 566 ShouldAddBlankLineInBetween(list[i], list[i + 1]))
486 Newline(); 567 Newline();
487 } 568 }
488 } 569 }
489 ++i; 570 ++i;
490 } 571 }
491 572
492 // Trailing comments. 573 // Trailing comments.
493 if (end->comments()) { 574 if (end->comments()) {
494 if (list.size() >= 2) 575 if (list.size() >= 2)
495 Newline(); 576 Newline();
496 for (const auto& c : end->comments()->before()) { 577 for (const auto& c : end->comments()->before()) {
497 Newline(); 578 Newline();
498 TrimAndPrintToken(c); 579 TrimAndPrintToken(c);
499 } 580 }
500 } 581 }
501 582
502 margin_ -= kIndentSize; 583 stack_.pop_back();
503 Newline(); 584 Newline();
504 585
505 // Defer any end of line comment until we reach the newline. 586 // Defer any end of line comment until we reach the newline.
506 if (end->comments() && !end->comments()->suffix().empty()) { 587 if (end->comments() && !end->comments()->suffix().empty()) {
507 std::copy(end->comments()->suffix().begin(), 588 std::copy(end->comments()->suffix().begin(),
508 end->comments()->suffix().end(), 589 end->comments()->suffix().end(),
509 std::back_inserter(comments_)); 590 std::back_inserter(comments_));
510 } 591 }
511 } 592 }
512 593
513 if (style == kSequenceStyleList) 594 if (style == kSequenceStyleList)
514 Print("]"); 595 Print("]");
515 else if (style == kSequenceStyleBracedBlock) 596 else if (style == kSequenceStyleBracedBlock)
516 Print("}"); 597 Print("}");
517 } 598 }
518 599
519 void Printer::FunctionCall(const FunctionCallNode* func_call) { 600 int Printer::FunctionCall(const FunctionCallNode* func_call) {
601 int start_line = CurrentLine();
602 int start_column = CurrentColumn();
520 Print(func_call->function().value()); 603 Print(func_call->function().value());
521 Print("("); 604 Print("(");
522 605
523 int old_margin = margin_;
524 bool have_block = func_call->block() != nullptr; 606 bool have_block = func_call->block() != nullptr;
525 bool force_multiline = false; 607 bool force_multiline = false;
526 608
527 const std::vector<const ParseNode*>& list = func_call->args()->contents(); 609 const std::vector<const ParseNode*>& list = func_call->args()->contents();
528 const ParseNode* end = func_call->args()->End(); 610 const ParseNode* end = func_call->args()->End();
529 611
530 if (end && end->comments() && !end->comments()->before().empty()) 612 if (end && end->comments() && !end->comments()->before().empty())
531 force_multiline = true; 613 force_multiline = true;
532 614
533 // If there's before line comments, make sure we have a place to put them. 615 // If there's before line comments, make sure we have a place to put them.
534 for (const auto& i : list) { 616 for (const auto& i : list) {
535 if (i->comments() && !i->comments()->before().empty()) 617 if (i->comments() && !i->comments()->before().empty())
536 force_multiline = true; 618 force_multiline = true;
537 } 619 }
538 620
539 // Calculate the length of the items for function calls so we can decide to 621 // Calculate the penalties for 3 possible layouts:
540 // compress them in various nicer ways. 622 // 1. all on same line;
541 std::vector<int> natural_lengths; 623 // 2. starting on same line, broken at each comma but paren aligned;
542 bool fits_on_current_line = true; 624 // 3. broken to next line + 4, broken at each comma.
543 int max_item_width = 0;
544 int total_length = 0;
545 natural_lengths.reserve(list.size());
546 std::string terminator = ")"; 625 std::string terminator = ")";
547 if (have_block) 626 if (have_block)
548 terminator += " {"; 627 terminator += " {";
628
629 // 1: Same line.
630 Printer sub1;
631 InitializeSub(&sub1);
632 sub1.stack_.push_back(IndentState(CurrentColumn(), true));
633 int penalty_one_line = 0;
549 for (size_t i = 0; i < list.size(); ++i) { 634 for (size_t i = 0; i < list.size(); ++i) {
550 Metrics sub = GetLengthOfExpr(list[i], kPrecedenceLowest); 635 penalty_one_line += sub1.Expr(list[i], kPrecedenceLowest,
551 if (sub.multiline) 636 i < list.size() - 1 ? ", " : std::string());
552 fits_on_current_line = false; 637 }
553 natural_lengths.push_back(sub.longest_length); 638 sub1.Print(terminator);
554 total_length += sub.longest_length; 639 penalty_one_line += AssessPenalty(sub1.String());
640 std::vector<std::string> lines;
641 base::SplitStringDontTrim(sub1.String(), '\n', &lines);
642 // This extra penalty prevents a short second argument from being squeezed in
643 // after a first argument that went multiline (and instead preferring a
644 // variant below).
645 if (lines.size() > 1)
646 penalty_one_line += kPenaltyBrokenLineOnOneLiner;
647
648 // 2: Starting on same line, broken at commas.
649 Printer sub2;
650 InitializeSub(&sub2);
651 sub2.stack_.push_back(IndentState(CurrentColumn(), true));
652 int penalty_multiline_start_same_line = 0;
653 for (size_t i = 0; i < list.size(); ++i) {
654 penalty_multiline_start_same_line += sub2.Expr(
655 list[i], kPrecedenceLowest, i < list.size() - 1 ? "," : std::string());
555 if (i < list.size() - 1) { 656 if (i < list.size() - 1) {
556 total_length += static_cast<int>(strlen(", ")); 657 sub2.Newline();
557 } 658 }
558 } 659 }
559 fits_on_current_line = 660 sub2.Print(terminator);
560 fits_on_current_line && 661 penalty_multiline_start_same_line += AssessPenalty(sub2.String());
561 CurrentColumn() + total_length + terminator.size() <= kMaximumWidth; 662
562 if (natural_lengths.size() > 0) { 663 // 3: Starting on next line, broken at commas.
563 max_item_width = 664 Printer sub3;
564 *std::max_element(natural_lengths.begin(), natural_lengths.end()); 665 InitializeSub(&sub3);
666 sub3.stack_.push_back(IndentState(margin() + kIndentSize * 2, true));
667 sub3.Newline();
668 int penalty_multiline_start_next_line = 0;
669 for (size_t i = 0; i < list.size(); ++i) {
670 if (i == 0) {
671 penalty_multiline_start_next_line +=
672 std::abs(sub3.CurrentColumn() - start_column) *
673 kPenaltyHorizontalSeparation;
674 }
675 penalty_multiline_start_next_line += sub3.Expr(
676 list[i], kPrecedenceLowest, i < list.size() - 1 ? "," : std::string());
677 if (i < list.size() - 1) {
678 sub3.Newline();
679 }
680 }
681 sub3.Print(terminator);
682 penalty_multiline_start_next_line += AssessPenalty(sub3.String());
683
684 int penalty = penalty_multiline_start_next_line;
685 bool fits_on_current_line = false;
686 if (penalty_one_line < penalty_multiline_start_next_line ||
687 penalty_multiline_start_same_line < penalty_multiline_start_next_line) {
688 fits_on_current_line = true;
689 penalty = penalty_one_line;
690 if (penalty_multiline_start_same_line < penalty_one_line) {
691 penalty = penalty_multiline_start_same_line;
692 force_multiline = true;
693 }
694 } else {
695 force_multiline = true;
565 } 696 }
566 697
567 if (list.size() == 0 && !force_multiline) { 698 if (list.size() == 0 && !force_multiline) {
568 // No elements, and not forcing newlines, print nothing. 699 // No elements, and not forcing newlines, print nothing.
569 } else if (list.size() == 1 && !force_multiline && fits_on_current_line) {
570 Expr(list[0], kPrecedenceLowest);
571 CHECK(!list[0]->comments() || list[0]->comments()->after().empty());
572 } else { 700 } else {
573 // Function calls get to be single line even with multiple arguments, if 701 if (penalty_multiline_start_next_line < penalty_multiline_start_same_line) {
574 // they fit inside the maximum width. 702 stack_.push_back(IndentState(margin() + kIndentSize * 2, true));
575 if (!force_multiline && fits_on_current_line) { 703 Newline();
576 for (size_t i = 0; i < list.size(); ++i) { 704 } else {
577 Expr(list[i], kPrecedenceLowest); 705 stack_.push_back(IndentState(CurrentColumn(), true));
578 if (i < list.size() - 1) 706 }
579 Print(", "); 707
708 for (size_t i = 0; i < list.size(); ++i) {
709 const auto& x = list[i];
710 if (i > 0) {
711 if (fits_on_current_line && !force_multiline)
712 Print(" ");
713 else
714 Newline();
580 } 715 }
581 } else { 716 bool want_comma = i < list.size() - 1 && !x->AsBlockComment();
582 bool should_break_to_next_line = true; 717 Expr(x, kPrecedenceLowest, want_comma ? "," : std::string());
583 int indent = kIndentSize * 2; 718 CHECK(!x->comments() || x->comments()->after().empty());
584 if (CurrentColumn() + max_item_width + terminator.size() <= 719 if (i < list.size() - 1) {
585 kMaximumWidth || 720 if (!want_comma)
586 CurrentColumn() < margin_ + indent) {
587 should_break_to_next_line = false;
588 margin_ = CurrentColumn();
589 } else {
590 margin_ += indent;
591 }
592 size_t i = 0;
593 for (const auto& x : list) {
594 // Function calls where all the arguments would fit at the current
595 // position should do that instead of going back to margin+4.
596 if (i > 0 || should_break_to_next_line)
597 Newline();
598 ExprStyle expr_style = Expr(x, kPrecedenceLowest);
599 CHECK(!x->comments() || x->comments()->after().empty());
600 if (i < list.size() - 1) {
601 if (expr_style == kExprStyleRegular) {
602 Print(",");
603 } else {
604 Newline();
605 }
606 }
607 ++i;
608 }
609
610 // Trailing comments.
611 if (end->comments()) {
612 if (!list.empty())
613 Newline();
614 for (const auto& c : end->comments()->before()) {
615 Newline();
616 TrimAndPrintToken(c);
617 }
618 if (!end->comments()->before().empty())
619 Newline(); 721 Newline();
620 } 722 }
621 } 723 }
724
725 // Trailing comments.
726 if (end->comments() && !end->comments()->before().empty()) {
727 if (!list.empty())
728 Newline();
729 for (const auto& c : end->comments()->before()) {
730 Newline();
731 TrimAndPrintToken(c);
732 }
733 Newline();
734 }
735 stack_.pop_back();
622 } 736 }
623 737
624 // Defer any end of line comment until we reach the newline. 738 // Defer any end of line comment until we reach the newline.
625 if (end->comments() && !end->comments()->suffix().empty()) { 739 if (end->comments() && !end->comments()->suffix().empty()) {
626 std::copy(end->comments()->suffix().begin(), 740 std::copy(end->comments()->suffix().begin(),
627 end->comments()->suffix().end(), 741 end->comments()->suffix().end(), std::back_inserter(comments_));
628 std::back_inserter(comments_));
629 } 742 }
630 743
631 Print(")"); 744 Print(")");
632 margin_ = old_margin;
633 745
634 if (have_block) { 746 if (have_block) {
635 Print(" "); 747 Print(" ");
636 Sequence(kSequenceStyleBracedBlock, 748 Sequence(kSequenceStyleBracedBlock,
637 func_call->block()->statements(), 749 func_call->block()->statements(),
638 func_call->block()->End()); 750 func_call->block()->End(),
751 false);
639 } 752 }
753 return penalty + (CurrentLine() - start_line) * GetPenaltyForLineBreak();
754 }
755
756 void Printer::InitializeSub(Printer* sub) {
757 sub->stack_ = stack_;
758 sub->comments_ = comments_;
759 sub->penalty_depth_ = penalty_depth_;
760 sub->Print(std::string(CurrentColumn(), 'x'));
640 } 761 }
641 762
642 void DoFormat(const ParseNode* root, bool dump_tree, std::string* output) { 763 void DoFormat(const ParseNode* root, bool dump_tree, std::string* output) {
643 if (dump_tree) { 764 if (dump_tree) {
644 std::ostringstream os; 765 std::ostringstream os;
645 root->Print(os, 0); 766 root->Print(os, 0);
646 printf("----------------------\n"); 767 printf("----------------------\n");
647 printf("-- PARSE TREE --------\n"); 768 printf("-- PARSE TREE --------\n");
648 printf("----------------------\n"); 769 printf("----------------------\n");
649 printf("%s", os.str().c_str()); 770 printf("%s", os.str().c_str());
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after
766 printf("Wrote formatted to '%s'.\n", to_write.AsUTF8Unsafe().c_str()); 887 printf("Wrote formatted to '%s'.\n", to_write.AsUTF8Unsafe().c_str());
767 } else { 888 } else {
768 printf("%s", output_string.c_str()); 889 printf("%s", output_string.c_str());
769 } 890 }
770 } 891 }
771 892
772 return 0; 893 return 0;
773 } 894 }
774 895
775 } // namespace commands 896 } // namespace commands
OLDNEW
« no previous file with comments | « no previous file | tools/gn/command_format_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698