| OLD | NEW |
| 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 Loading... |
| 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 = |
| 234 Print(" "); | 267 left.length + binop->op().value().size() + 2 + right.length; |
| 235 Expr(binop->right()); | 268 if (CurrentColumn() + total_width < kMaximumWidth || |
| 269 binop->right()->AsList()) { |
| 270 // If it just fits normally, put it here. |
| 271 Expr(binop->left()); |
| 272 Print(" "); |
| 273 Print(binop->op().value()); |
| 274 Print(" "); |
| 275 Expr(binop->right()); |
| 276 } else { |
| 277 // Otherwise, put first argument and op, and indent next. |
| 278 Expr(binop->left()); |
| 279 Print(" "); |
| 280 Print(binop->op().value()); |
| 281 int old_margin = margin_; |
| 282 margin_ += kIndentSize * 2; |
| 283 Newline(); |
| 284 Expr(binop->right()); |
| 285 margin_ = old_margin; |
| 286 } |
| 236 } else if (const BlockNode* block = root->AsBlock()) { | 287 } else if (const BlockNode* block = root->AsBlock()) { |
| 237 Sequence(kSequenceStyleBracedBlock, block->statements(), block->End()); | 288 Sequence(kSequenceStyleBracedBlock, block->statements(), block->End()); |
| 238 } else if (const ConditionNode* condition = root->AsConditionNode()) { | 289 } else if (const ConditionNode* condition = root->AsConditionNode()) { |
| 239 Print("if ("); | 290 Print("if ("); |
| 240 Expr(condition->condition()); | 291 Expr(condition->condition()); |
| 241 Print(") "); | 292 Print(") "); |
| 242 Sequence(kSequenceStyleBracedBlock, | 293 Sequence(kSequenceStyleBracedBlock, |
| 243 condition->if_true()->statements(), | 294 condition->if_true()->statements(), |
| 244 condition->if_true()->End()); | 295 condition->if_true()->End()); |
| 245 if (condition->if_false()) { | 296 if (condition->if_false()) { |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 292 std::back_inserter(comments_)); | 343 std::back_inserter(comments_)); |
| 293 } | 344 } |
| 294 | 345 |
| 295 return result; | 346 return result; |
| 296 } | 347 } |
| 297 | 348 |
| 298 template <class PARSENODE> | 349 template <class PARSENODE> |
| 299 void Printer::Sequence(SequenceStyle style, | 350 void Printer::Sequence(SequenceStyle style, |
| 300 const std::vector<PARSENODE*>& list, | 351 const std::vector<PARSENODE*>& list, |
| 301 const ParseNode* end) { | 352 const ParseNode* end) { |
| 353 int old_margin = margin_; |
| 354 int indent = |
| 355 style == kSequenceStyleFunctionCall ? kIndentSize * 2 : kIndentSize; |
| 302 bool force_multiline = false; | 356 bool force_multiline = false; |
| 303 if (style == kSequenceStyleFunctionCall) | 357 if (style == kSequenceStyleFunctionCall) |
| 304 Print("("); | 358 Print("("); |
| 305 else if (style == kSequenceStyleList) | 359 else if (style == kSequenceStyleList) |
| 306 Print("["); | 360 Print("["); |
| 307 else if (style == kSequenceStyleBracedBlock) | 361 else if (style == kSequenceStyleBracedBlock) |
| 308 Print("{"); | 362 Print("{"); |
| 309 | 363 |
| 310 if (style == kSequenceStyleBlock || style == kSequenceStyleBracedBlock) | 364 if (style == kSequenceStyleBlock || style == kSequenceStyleBracedBlock) |
| 311 force_multiline = true; | 365 force_multiline = true; |
| 312 | 366 |
| 313 if (end && end->comments() && !end->comments()->before().empty()) | 367 if (end && end->comments() && !end->comments()->before().empty()) |
| 314 force_multiline = true; | 368 force_multiline = true; |
| 315 | 369 |
| 316 // If there's before line comments, make sure we have a place to put them. | 370 // If there's before line comments, make sure we have a place to put them. |
| 317 for (const auto& i : list) { | 371 for (const auto& i : list) { |
| 318 if (i->comments() && !i->comments()->before().empty()) | 372 if (i->comments() && !i->comments()->before().empty()) |
| 319 force_multiline = true; | 373 force_multiline = true; |
| 320 } | 374 } |
| 321 | 375 |
| 376 // Calculate the length of the items for function calls so we can decide to |
| 377 // compress them in various nicer ways. |
| 378 std::vector<int> natural_lengths; |
| 379 bool fits_on_current_line = true; |
| 380 int max_item_width = 0; |
| 381 if (style == kSequenceStyleFunctionCall) { |
| 382 int total_length = 0; |
| 383 natural_lengths.reserve(list.size()); |
| 384 for (size_t i = 0; i < list.size(); ++i) { |
| 385 Metrics sub = GetLengthOfExpr(list[i]); |
| 386 if (sub.multiline) |
| 387 fits_on_current_line = false; |
| 388 natural_lengths.push_back(sub.length); |
| 389 total_length += sub.length; |
| 390 if (i < list.size() - 1) |
| 391 total_length += 2; // ", " |
| 392 } |
| 393 // Strictly less than kMaximumWidth so there's room for closing ). |
| 394 // TODO(scottmg): Need to know if there's an attached block for " {". |
| 395 fits_on_current_line = |
| 396 fits_on_current_line && CurrentColumn() + total_length < kMaximumWidth; |
| 397 max_item_width = |
| 398 *std::max_element(natural_lengths.begin(), natural_lengths.end()); |
| 399 } |
| 400 |
| 322 if (list.size() == 0 && !force_multiline) { | 401 if (list.size() == 0 && !force_multiline) { |
| 323 // No elements, and not forcing newlines, print nothing. | 402 // No elements, and not forcing newlines, print nothing. |
| 324 } else if (list.size() == 1 && !force_multiline) { | 403 } else if (list.size() == 1 && !force_multiline && fits_on_current_line) { |
| 325 if (style != kSequenceStyleFunctionCall) | 404 if (style != kSequenceStyleFunctionCall) |
| 326 Print(" "); | 405 Print(" "); |
| 327 Expr(list[0]); | 406 Expr(list[0]); |
| 328 CHECK(!list[0]->comments() || list[0]->comments()->after().empty()); | 407 CHECK(!list[0]->comments() || list[0]->comments()->after().empty()); |
| 329 if (style != kSequenceStyleFunctionCall) | 408 if (style != kSequenceStyleFunctionCall) |
| 330 Print(" "); | 409 Print(" "); |
| 331 } else { | 410 } else { |
| 332 margin_ += kIndentSize; | 411 // Function calls get to be single line even with multiple arguments, if |
| 333 size_t i = 0; | 412 // they fit inside the maximum width. |
| 334 for (const auto& x : list) { | 413 if (style == kSequenceStyleFunctionCall && !force_multiline && |
| 335 Newline(); | 414 fits_on_current_line) { |
| 336 // If: | 415 for (size_t i = 0; i < list.size(); ++i) { |
| 337 // - we're going to output some comments, and; | 416 Expr(list[i]); |
| 338 // - we haven't just started this multiline list, and; | 417 if (i < list.size() - 1) |
| 339 // - there isn't already a blank line here; | 418 Print(", "); |
| 340 // Then: insert one. | 419 } |
| 341 if (i != 0 && x->comments() && !x->comments()->before().empty() && | 420 } else { |
| 342 !HaveBlankLine()) { | 421 bool should_break_to_next_line = true; |
| 422 if (style == kSequenceStyleFunctionCall && |
| 423 (CurrentColumn() + max_item_width < kMaximumWidth || |
| 424 CurrentColumn() < margin_ + indent)) { |
| 425 should_break_to_next_line = false; |
| 426 margin_ = CurrentColumn(); |
| 427 } else { |
| 428 margin_ += indent; |
| 429 } |
| 430 size_t i = 0; |
| 431 for (const auto& x : list) { |
| 432 // Function calls where all the arguments would fit at the current |
| 433 // position should do that instead of going back to margin+4. |
| 434 if (i > 0 || should_break_to_next_line) |
| 435 Newline(); |
| 436 // If: |
| 437 // - we're going to output some comments, and; |
| 438 // - we haven't just started this multiline list, and; |
| 439 // - there isn't already a blank line here; |
| 440 // Then: insert one. |
| 441 if (i != 0 && x->comments() && !x->comments()->before().empty() && |
| 442 !HaveBlankLine()) { |
| 443 Newline(); |
| 444 } |
| 445 ExprStyle expr_style = Expr(x); |
| 446 CHECK(!x->comments() || x->comments()->after().empty()); |
| 447 if (i < list.size() - 1 || style == kSequenceStyleList) { |
| 448 if ((style == kSequenceStyleList || |
| 449 style == kSequenceStyleFunctionCall) && |
| 450 expr_style == kExprStyleRegular) { |
| 451 Print(","); |
| 452 } else { |
| 453 Newline(); |
| 454 } |
| 455 } |
| 456 ++i; |
| 457 } |
| 458 |
| 459 // Trailing comments. |
| 460 if (end->comments()) { |
| 461 if (!list.empty()) |
| 462 Newline(); |
| 463 for (const auto& c : end->comments()->before()) { |
| 464 Newline(); |
| 465 TrimAndPrintToken(c); |
| 466 } |
| 467 } |
| 468 |
| 469 if (style == kSequenceStyleFunctionCall) { |
| 470 if (end->comments() && !end->comments()->before().empty()) { |
| 471 Newline(); |
| 472 } |
| 473 } else { |
| 474 margin_ = old_margin; |
| 343 Newline(); | 475 Newline(); |
| 344 } | 476 } |
| 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 } | 477 } |
| 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 } | 478 } |
| 371 | 479 |
| 372 if (style == kSequenceStyleFunctionCall) | 480 if (style == kSequenceStyleFunctionCall) |
| 373 Print(")"); | 481 Print(")"); |
| 374 else if (style == kSequenceStyleList) | 482 else if (style == kSequenceStyleList) |
| 375 Print("]"); | 483 Print("]"); |
| 376 else if (style == kSequenceStyleBracedBlock) | 484 else if (style == kSequenceStyleBracedBlock) |
| 377 Print("}"); | 485 Print("}"); |
| 486 |
| 487 margin_ = old_margin; |
| 378 } | 488 } |
| 379 | 489 |
| 380 } // namespace | 490 } // namespace |
| 381 | 491 |
| 382 bool FormatFileToString(const std::string& input_filename, | 492 bool FormatFileToString(const std::string& input_filename, |
| 383 bool dump_tree, | 493 bool dump_tree, |
| 384 std::string* output) { | 494 std::string* output) { |
| 385 Setup setup; | 495 Setup setup; |
| 386 Err err; | 496 Err err; |
| 387 SourceFile input_file(input_filename); | 497 SourceFile input_file(input_filename); |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 428 } | 538 } |
| 429 std::string output_string; | 539 std::string output_string; |
| 430 if (FormatFileToString(input_name, dump_tree, &output_string)) { | 540 if (FormatFileToString(input_name, dump_tree, &output_string)) { |
| 431 printf("%s", output_string.c_str()); | 541 printf("%s", output_string.c_str()); |
| 432 } | 542 } |
| 433 | 543 |
| 434 return 0; | 544 return 0; |
| 435 } | 545 } |
| 436 | 546 |
| 437 } // namespace commands | 547 } // namespace commands |
| OLD | NEW |