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

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

Issue 607243002: gn format: add call wrapping (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@gn-suffix-missing-newline
Patch Set: 64 Created 6 years, 2 months 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/strings/string_split.h"
8 #include "tools/gn/commands.h" 9 #include "tools/gn/commands.h"
9 #include "tools/gn/input_file.h" 10 #include "tools/gn/input_file.h"
10 #include "tools/gn/parser.h" 11 #include "tools/gn/parser.h"
11 #include "tools/gn/scheduler.h" 12 #include "tools/gn/scheduler.h"
12 #include "tools/gn/setup.h" 13 #include "tools/gn/setup.h"
13 #include "tools/gn/source_file.h" 14 #include "tools/gn/source_file.h"
14 #include "tools/gn/tokenizer.h" 15 #include "tools/gn/tokenizer.h"
15 16
16 namespace commands { 17 namespace commands {
17 18
18 const char kSwitchDumpTree[] = "dump-tree"; 19 const char kSwitchDumpTree[] = "dump-tree";
19 20
20 const char kFormat[] = "format"; 21 const char kFormat[] = "format";
21 const char kFormat_HelpShort[] = 22 const char kFormat_HelpShort[] =
22 "format: Format .gn file."; 23 "format: Format .gn file.";
23 const char kFormat_Help[] = 24 const char kFormat_Help[] =
24 "gn format: Format .gn file. (ALPHA, WILL CURRENTLY DESTROY DATA!)\n" 25 "gn format: Format .gn file. (ALPHA, WILL CURRENTLY DESTROY DATA!)\n"
25 "\n" 26 "\n"
26 " gn format //some/BUILD.gn\n" 27 " gn format //some/BUILD.gn\n"
27 " gn format some\\BUILD.gn\n" 28 " gn format some\\BUILD.gn\n"
28 "\n" 29 "\n"
29 " Formats .gn file to a standard format. THIS IS NOT FULLY IMPLEMENTED\n" 30 " Formats .gn file to a standard format. THIS IS NOT FULLY IMPLEMENTED\n"
30 " YET! IT WILL EAT YOUR BEAUTIFUL .GN FILES. AND YOUR LAUNDRY.\n" 31 " YET! IT WILL EAT YOUR BEAUTIFUL .GN FILES. AND YOUR LAUNDRY.\n"
31 " At a minimum, make sure everything is `git commit`d so you can\n" 32 " At a minimum, make sure everything is `git commit`d so you can\n"
32 " `git checkout -f` to recover.\n"; 33 " `git checkout -f` to recover.\n";
33 34
34 namespace { 35 namespace {
35 36
36 const int kIndentSize = 2; 37 const int kIndentSize = 2;
38 const int kMaximumWidth = 80;
37 39
38 class Printer { 40 class Printer {
39 public: 41 public:
40 Printer(); 42 Printer();
41 ~Printer(); 43 ~Printer();
42 44
43 void Block(const ParseNode* file); 45 void Block(const ParseNode* file);
44 46
45 std::string String() const { return output_; } 47 std::string String() const { return output_; }
46 48
47 private: 49 private:
48 // Format a list of values using the given style. 50 // Format a list of values using the given style.
49 enum SequenceStyle { 51 enum SequenceStyle {
50 kSequenceStyleFunctionCall, 52 kSequenceStyleFunctionCall,
51 kSequenceStyleList, 53 kSequenceStyleList,
52 kSequenceStyleBlock, 54 kSequenceStyleBlock,
53 kSequenceStyleBracedBlock, 55 kSequenceStyleBracedBlock,
54 }; 56 };
55 57
56 enum ExprStyle { 58 enum ExprStyle {
57 kExprStyleRegular, 59 kExprStyleRegular,
58 kExprStyleComment, 60 kExprStyleComment,
59 }; 61 };
60 62
63 struct Metrics {
64 Metrics() : length(-1), multiline(false) {}
65 int length;
66 bool multiline;
67 };
68
61 // Add to output. 69 // Add to output.
62 void Print(base::StringPiece str); 70 void Print(base::StringPiece str);
63 71
64 // Add the current margin (as spaces) to the output. 72 // Add the current margin (as spaces) to the output.
65 void PrintMargin(); 73 void PrintMargin();
66 74
67 void TrimAndPrintToken(const Token& token); 75 void TrimAndPrintToken(const Token& token);
68 76
69 // End the current line, flushing end of line comments. 77 // End the current line, flushing end of line comments.
70 void Newline(); 78 void Newline();
71 79
72 // Remove trailing spaces from the current line. 80 // Remove trailing spaces from the current line.
73 void Trim(); 81 void Trim();
74 82
75 // Whether there's a blank separator line at the current position. 83 // Whether there's a blank separator line at the current position.
76 bool HaveBlankLine(); 84 bool HaveBlankLine();
77 85
78 // Get the 0-based x position on the current line. 86 // Get the 0-based x position on the current line.
79 int CurrentColumn(); 87 int CurrentColumn();
80 88
81 // Print the expression to the output buffer. Returns the type of element 89 // Print the expression to the output buffer. Returns the type of element
82 // added to the output. 90 // added to the output.
83 ExprStyle Expr(const ParseNode* root); 91 ExprStyle Expr(const ParseNode* root);
84 92
93 // Use a sub-Printer recursively to figure out the size that an expression
94 // would be before actually adding it to the output.
95 Metrics GetLengthOfExpr(const ParseNode* expr);
96
85 // Format a list of values using the given style. 97 // Format a list of values using the given style.
86 // |end| holds any trailing comments to be printed just before the closing 98 // |end| holds any trailing comments to be printed just before the closing
87 // bracket. 99 // bracket.
88 template <class PARSENODE> // Just for const covariance. 100 template <class PARSENODE> // Just for const covariance.
89 void Sequence(SequenceStyle style, 101 void Sequence(SequenceStyle style,
90 const std::vector<PARSENODE*>& list, 102 const std::vector<PARSENODE*>& list,
91 const ParseNode* end); 103 const ParseNode* end);
92 104
93 std::string output_; // Output buffer. 105 std::string output_; // Output buffer.
94 std::vector<Token> comments_; // Pending end-of-line comments. 106 std::vector<Token> comments_; // Pending end-of-line comments.
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after
199 } 211 }
200 212
201 if (block->comments()) { 213 if (block->comments()) {
202 for (const auto& c : block->comments()->after()) { 214 for (const auto& c : block->comments()->after()) {
203 TrimAndPrintToken(c); 215 TrimAndPrintToken(c);
204 Newline(); 216 Newline();
205 } 217 }
206 } 218 }
207 } 219 }
208 220
221 Printer::Metrics Printer::GetLengthOfExpr(const ParseNode* expr) {
222 Metrics result;
223 Printer sub;
224 sub.Expr(expr);
225 std::vector<std::string> lines;
226 base::SplitStringDontTrim(sub.String(), '\n', &lines);
227 result.multiline = lines.size() > 1;
228 for (const auto& line : lines)
229 result.length = std::max(result.length, static_cast<int>(line.size()));
230 return result;
231 }
232
209 Printer::ExprStyle Printer::Expr(const ParseNode* root) { 233 Printer::ExprStyle Printer::Expr(const ParseNode* root) {
210 ExprStyle result = kExprStyleRegular; 234 ExprStyle result = kExprStyleRegular;
211 if (root->comments()) { 235 if (root->comments()) {
212 if (!root->comments()->before().empty()) { 236 if (!root->comments()->before().empty()) {
213 Trim(); 237 Trim();
214 // If there's already other text on the line, start a new line. 238 // If there's already other text on the line, start a new line.
215 if (CurrentColumn() > 0) 239 if (CurrentColumn() > 0)
216 Print("\n"); 240 Print("\n");
217 // We're printing a line comment, so we need to be at the current margin. 241 // We're printing a line comment, so we need to be at the current margin.
218 PrintMargin(); 242 PrintMargin();
219 for (const auto& c : root->comments()->before()) { 243 for (const auto& c : root->comments()->before()) {
220 TrimAndPrintToken(c); 244 TrimAndPrintToken(c);
221 Newline(); 245 Newline();
222 } 246 }
223 } 247 }
224 } 248 }
225 249
226 if (root->AsAccessor()) { 250 if (const AccessorNode* accessor = root->AsAccessor()) {
227 Print("TODO(scottmg): AccessorNode"); 251 Print(accessor->base().value());
252 if (accessor->member()) {
253 Print(".");
254 Expr(accessor->member());
255 } else {
256 CHECK(accessor->index());
257 Print("[");
258 Expr(accessor->index());
259 Print("]");
260 }
228 } else if (const BinaryOpNode* binop = root->AsBinaryOp()) { 261 } else if (const BinaryOpNode* binop = root->AsBinaryOp()) {
229 // TODO(scottmg): Lots to do here for complex if expressions: reflowing, 262 // TODO(scottmg): Lots to do here for complex if expressions: reflowing,
230 // parenthesizing, etc. 263 // parenthesizing, etc.
231 Expr(binop->left()); 264 Metrics left = GetLengthOfExpr(binop->left());
232 Print(" "); 265 Metrics right = GetLengthOfExpr(binop->right());
233 Print(binop->op().value()); 266 int total_width = left.length +
234 Print(" "); 267 static_cast<int>(binop->op().value().size()) + 2 +
235 Expr(binop->right()); 268 right.length;
269 if (CurrentColumn() + total_width < kMaximumWidth ||
270 binop->right()->AsList()) {
271 // If it just fits normally, put it here.
272 Expr(binop->left());
273 Print(" ");
274 Print(binop->op().value());
275 Print(" ");
276 Expr(binop->right());
277 } else {
278 // Otherwise, put first argument and op, and indent next.
279 Expr(binop->left());
280 Print(" ");
281 Print(binop->op().value());
282 int old_margin = margin_;
283 margin_ += kIndentSize * 2;
284 Newline();
285 Expr(binop->right());
286 margin_ = old_margin;
287 }
236 } else if (const BlockNode* block = root->AsBlock()) { 288 } else if (const BlockNode* block = root->AsBlock()) {
237 Sequence(kSequenceStyleBracedBlock, block->statements(), block->End()); 289 Sequence(kSequenceStyleBracedBlock, block->statements(), block->End());
238 } else if (const ConditionNode* condition = root->AsConditionNode()) { 290 } else if (const ConditionNode* condition = root->AsConditionNode()) {
239 Print("if ("); 291 Print("if (");
240 Expr(condition->condition()); 292 Expr(condition->condition());
241 Print(") "); 293 Print(") ");
242 Sequence(kSequenceStyleBracedBlock, 294 Sequence(kSequenceStyleBracedBlock,
243 condition->if_true()->statements(), 295 condition->if_true()->statements(),
244 condition->if_true()->End()); 296 condition->if_true()->End());
245 if (condition->if_false()) { 297 if (condition->if_false()) {
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
292 std::back_inserter(comments_)); 344 std::back_inserter(comments_));
293 } 345 }
294 346
295 return result; 347 return result;
296 } 348 }
297 349
298 template <class PARSENODE> 350 template <class PARSENODE>
299 void Printer::Sequence(SequenceStyle style, 351 void Printer::Sequence(SequenceStyle style,
300 const std::vector<PARSENODE*>& list, 352 const std::vector<PARSENODE*>& list,
301 const ParseNode* end) { 353 const ParseNode* end) {
354 int old_margin = margin_;
355 int indent =
356 style == kSequenceStyleFunctionCall ? kIndentSize * 2 : kIndentSize;
302 bool force_multiline = false; 357 bool force_multiline = false;
303 if (style == kSequenceStyleFunctionCall) 358 if (style == kSequenceStyleFunctionCall)
304 Print("("); 359 Print("(");
305 else if (style == kSequenceStyleList) 360 else if (style == kSequenceStyleList)
306 Print("["); 361 Print("[");
307 else if (style == kSequenceStyleBracedBlock) 362 else if (style == kSequenceStyleBracedBlock)
308 Print("{"); 363 Print("{");
309 364
310 if (style == kSequenceStyleBlock || style == kSequenceStyleBracedBlock) 365 if (style == kSequenceStyleBlock || style == kSequenceStyleBracedBlock)
311 force_multiline = true; 366 force_multiline = true;
312 367
313 if (end && end->comments() && !end->comments()->before().empty()) 368 if (end && end->comments() && !end->comments()->before().empty())
314 force_multiline = true; 369 force_multiline = true;
315 370
316 // If there's before line comments, make sure we have a place to put them. 371 // If there's before line comments, make sure we have a place to put them.
317 for (const auto& i : list) { 372 for (const auto& i : list) {
318 if (i->comments() && !i->comments()->before().empty()) 373 if (i->comments() && !i->comments()->before().empty())
319 force_multiline = true; 374 force_multiline = true;
320 } 375 }
321 376
377 // Calculate the length of the items for function calls so we can decide to
378 // compress them in various nicer ways.
379 std::vector<int> natural_lengths;
380 bool fits_on_current_line = true;
381 int max_item_width = 0;
382 if (style == kSequenceStyleFunctionCall) {
383 int total_length = 0;
384 natural_lengths.reserve(list.size());
385 for (size_t i = 0; i < list.size(); ++i) {
386 Metrics sub = GetLengthOfExpr(list[i]);
387 if (sub.multiline)
388 fits_on_current_line = false;
389 natural_lengths.push_back(sub.length);
390 total_length += sub.length;
391 if (i < list.size() - 1)
392 total_length += 2; // ", "
393 }
394 // Strictly less than kMaximumWidth so there's room for closing ).
395 // TODO(scottmg): Need to know if there's an attached block for " {".
396 fits_on_current_line =
397 fits_on_current_line && CurrentColumn() + total_length < kMaximumWidth;
398 max_item_width =
399 *std::max_element(natural_lengths.begin(), natural_lengths.end());
400 }
401
322 if (list.size() == 0 && !force_multiline) { 402 if (list.size() == 0 && !force_multiline) {
323 // No elements, and not forcing newlines, print nothing. 403 // No elements, and not forcing newlines, print nothing.
324 } else if (list.size() == 1 && !force_multiline) { 404 } else if (list.size() == 1 && !force_multiline && fits_on_current_line) {
325 if (style != kSequenceStyleFunctionCall) 405 if (style != kSequenceStyleFunctionCall)
326 Print(" "); 406 Print(" ");
327 Expr(list[0]); 407 Expr(list[0]);
328 CHECK(!list[0]->comments() || list[0]->comments()->after().empty()); 408 CHECK(!list[0]->comments() || list[0]->comments()->after().empty());
329 if (style != kSequenceStyleFunctionCall) 409 if (style != kSequenceStyleFunctionCall)
330 Print(" "); 410 Print(" ");
331 } else { 411 } else {
332 margin_ += kIndentSize; 412 // Function calls get to be single line even with multiple arguments, if
333 size_t i = 0; 413 // they fit inside the maximum width.
334 for (const auto& x : list) { 414 if (style == kSequenceStyleFunctionCall && !force_multiline &&
335 Newline(); 415 fits_on_current_line) {
336 // If: 416 for (size_t i = 0; i < list.size(); ++i) {
337 // - we're going to output some comments, and; 417 Expr(list[i]);
338 // - we haven't just started this multiline list, and; 418 if (i < list.size() - 1)
339 // - there isn't already a blank line here; 419 Print(", ");
340 // Then: insert one. 420 }
341 if (i != 0 && x->comments() && !x->comments()->before().empty() && 421 } else {
342 !HaveBlankLine()) { 422 bool should_break_to_next_line = true;
423 if (style == kSequenceStyleFunctionCall &&
424 (CurrentColumn() + max_item_width < kMaximumWidth ||
425 CurrentColumn() < margin_ + indent)) {
426 should_break_to_next_line = false;
427 margin_ = CurrentColumn();
428 } else {
429 margin_ += indent;
430 }
431 size_t i = 0;
432 for (const auto& x : list) {
433 // Function calls where all the arguments would fit at the current
434 // position should do that instead of going back to margin+4.
435 if (i > 0 || should_break_to_next_line)
436 Newline();
437 // If:
438 // - we're going to output some comments, and;
439 // - we haven't just started this multiline list, and;
440 // - there isn't already a blank line here;
441 // Then: insert one.
442 if (i != 0 && x->comments() && !x->comments()->before().empty() &&
443 !HaveBlankLine()) {
444 Newline();
445 }
446 ExprStyle expr_style = Expr(x);
447 CHECK(!x->comments() || x->comments()->after().empty());
448 if (i < list.size() - 1 || style == kSequenceStyleList) {
449 if ((style == kSequenceStyleList ||
450 style == kSequenceStyleFunctionCall) &&
451 expr_style == kExprStyleRegular) {
452 Print(",");
453 } else {
454 Newline();
455 }
456 }
457 ++i;
458 }
459
460 // Trailing comments.
461 if (end->comments()) {
462 if (!list.empty())
463 Newline();
464 for (const auto& c : end->comments()->before()) {
465 Newline();
466 TrimAndPrintToken(c);
467 }
468 }
469
470 if (style == kSequenceStyleFunctionCall) {
471 if (end->comments() && !end->comments()->before().empty()) {
472 Newline();
473 }
474 } else {
475 margin_ = old_margin;
343 Newline(); 476 Newline();
344 } 477 }
345 ExprStyle expr_style = Expr(x);
346 CHECK(!x->comments() || x->comments()->after().empty());
347 if (i < list.size() - 1 || style == kSequenceStyleList) {
348 if ((style == kSequenceStyleList || kSequenceStyleFunctionCall) &&
349 expr_style == kExprStyleRegular) {
350 Print(",");
351 } else {
352 Newline();
353 }
354 }
355 ++i;
356 } 478 }
357
358 // Trailing comments.
359 if (end->comments()) {
360 if (!list.empty())
361 Newline();
362 for (const auto& c : end->comments()->before()) {
363 Newline();
364 TrimAndPrintToken(c);
365 }
366 }
367
368 margin_ -= kIndentSize;
369 Newline();
370 } 479 }
371 480
372 if (style == kSequenceStyleFunctionCall) 481 if (style == kSequenceStyleFunctionCall)
373 Print(")"); 482 Print(")");
374 else if (style == kSequenceStyleList) 483 else if (style == kSequenceStyleList)
375 Print("]"); 484 Print("]");
376 else if (style == kSequenceStyleBracedBlock) 485 else if (style == kSequenceStyleBracedBlock)
377 Print("}"); 486 Print("}");
487
488 margin_ = old_margin;
378 } 489 }
379 490
380 } // namespace 491 } // namespace
381 492
382 bool FormatFileToString(const std::string& input_filename, 493 bool FormatFileToString(const std::string& input_filename,
383 bool dump_tree, 494 bool dump_tree,
384 std::string* output) { 495 std::string* output) {
385 Setup setup; 496 Setup setup;
386 Err err; 497 Err err;
387 SourceFile input_file(input_filename); 498 SourceFile input_file(input_filename);
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
428 } 539 }
429 std::string output_string; 540 std::string output_string;
430 if (FormatFileToString(input_name, dump_tree, &output_string)) { 541 if (FormatFileToString(input_name, dump_tree, &output_string)) {
431 printf("%s", output_string.c_str()); 542 printf("%s", output_string.c_str());
432 } 543 }
433 544
434 return 0; 545 return 0;
435 } 546 }
436 547
437 } // namespace commands 548 } // 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