| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library dart_style.src.source_visitor; | 5 library dart_style.src.source_visitor; |
| 6 | 6 |
| 7 import 'package:analyzer/analyzer.dart'; | 7 import 'package:analyzer/analyzer.dart'; |
| 8 import 'package:analyzer/src/generated/scanner.dart'; | 8 import 'package:analyzer/dart/ast/token.dart'; |
| 9 import 'package:analyzer/src/generated/source.dart'; | 9 import 'package:analyzer/src/generated/source.dart'; |
| 10 | 10 |
| 11 import 'argument_list_visitor.dart'; | 11 import 'argument_list_visitor.dart'; |
| 12 import 'call_chain_visitor.dart'; | 12 import 'call_chain_visitor.dart'; |
| 13 import 'chunk.dart'; | 13 import 'chunk.dart'; |
| 14 import 'chunk_builder.dart'; | 14 import 'chunk_builder.dart'; |
| 15 import 'dart_formatter.dart'; | 15 import 'dart_formatter.dart'; |
| 16 import 'rule/argument.dart'; | 16 import 'rule/argument.dart'; |
| 17 import 'rule/combinator.dart'; | 17 import 'rule/combinator.dart'; |
| 18 import 'rule/metadata.dart'; | 18 import 'rule/metadata.dart'; |
| 19 import 'rule/rule.dart'; | 19 import 'rule/rule.dart'; |
| 20 import 'rule/type_argument.dart'; | 20 import 'rule/type_argument.dart'; |
| 21 import 'source_code.dart'; | 21 import 'source_code.dart'; |
| 22 import 'whitespace.dart'; | 22 import 'whitespace.dart'; |
| 23 | 23 |
| 24 /// Visits every token of the AST and passes all of the relevant bits to a | 24 /// Visits every token of the AST and passes all of the relevant bits to a |
| 25 /// [ChunkBuilder]. | 25 /// [ChunkBuilder]. |
| 26 class SourceVisitor implements AstVisitor { | 26 class SourceVisitor extends ThrowingAstVisitor { |
| 27 /// The builder for the block that is currently being visited. | 27 /// The builder for the block that is currently being visited. |
| 28 ChunkBuilder builder; | 28 ChunkBuilder builder; |
| 29 | 29 |
| 30 final DartFormatter _formatter; | 30 final DartFormatter _formatter; |
| 31 | 31 |
| 32 /// Cached line info for calculating blank lines. | 32 /// Cached line info for calculating blank lines. |
| 33 LineInfo _lineInfo; | 33 LineInfo _lineInfo; |
| 34 | 34 |
| 35 /// The source being formatted. | 35 /// The source being formatted. |
| 36 final SourceCode _source; | 36 final SourceCode _source; |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 126 /// | 126 /// |
| 127 /// This is a bit complex to handle the rules for formatting positional and | 127 /// This is a bit complex to handle the rules for formatting positional and |
| 128 /// named arguments. The goals, in rough order of descending priority are: | 128 /// named arguments. The goals, in rough order of descending priority are: |
| 129 /// | 129 /// |
| 130 /// 1. Keep everything on the first line. | 130 /// 1. Keep everything on the first line. |
| 131 /// 2. Keep the named arguments together on the next line. | 131 /// 2. Keep the named arguments together on the next line. |
| 132 /// 3. Keep everything together on the second line. | 132 /// 3. Keep everything together on the second line. |
| 133 /// 4. Split between one or more positional arguments, trying to keep as many | 133 /// 4. Split between one or more positional arguments, trying to keep as many |
| 134 /// on earlier lines as possible. | 134 /// on earlier lines as possible. |
| 135 /// 5. Split the named arguments each onto their own line. | 135 /// 5. Split the named arguments each onto their own line. |
| 136 visitArgumentList(ArgumentList node) { | 136 visitArgumentList(ArgumentList node, {bool nestExpression: true}) { |
| 137 // Corner case: handle empty argument lists. | 137 // Corner case: handle empty argument lists. |
| 138 if (node.arguments.isEmpty) { | 138 if (node.arguments.isEmpty) { |
| 139 token(node.leftParenthesis); | 139 token(node.leftParenthesis); |
| 140 | 140 |
| 141 // If there is a comment inside the parens, do allow splitting before it. | 141 // If there is a comment inside the parens, do allow splitting before it. |
| 142 if (node.rightParenthesis.precedingComments != null) soloZeroSplit(); | 142 if (node.rightParenthesis.precedingComments != null) soloZeroSplit(); |
| 143 | 143 |
| 144 token(node.rightParenthesis); | 144 token(node.rightParenthesis); |
| 145 return; | 145 return; |
| 146 } | 146 } |
| 147 | 147 |
| 148 // If the argument list has a trailing comma, format it like a collection |
| 149 // literal where each argument goes on its own line, they are indented +2, |
| 150 // and the ")" ends up on its own line. |
| 151 if (node.arguments.last.endToken.next.type == TokenType.COMMA) { |
| 152 _visitCollectionLiteral( |
| 153 null, node.leftParenthesis, node.arguments, node.rightParenthesis); |
| 154 return; |
| 155 } |
| 156 |
| 157 if (nestExpression) builder.nestExpression(); |
| 148 new ArgumentListVisitor(this, node).visit(); | 158 new ArgumentListVisitor(this, node).visit(); |
| 159 if (nestExpression) builder.unnest(); |
| 149 } | 160 } |
| 150 | 161 |
| 151 visitAsExpression(AsExpression node) { | 162 visitAsExpression(AsExpression node) { |
| 152 builder.startSpan(); | 163 builder.startSpan(); |
| 164 builder.nestExpression(); |
| 153 visit(node.expression); | 165 visit(node.expression); |
| 154 soloSplit(); | 166 soloSplit(); |
| 155 token(node.asOperator); | 167 token(node.asOperator); |
| 156 space(); | 168 space(); |
| 157 visit(node.type); | 169 visit(node.type); |
| 170 builder.unnest(); |
| 158 builder.endSpan(); | 171 builder.endSpan(); |
| 159 } | 172 } |
| 160 | 173 |
| 174 // TODO(rnystrom): Type annotate once analyzer publishes a version with the |
| 175 // new AST type. |
| 176 // TODO(rnystrom): Test. |
| 177 visitAssertInitializer(node) { |
| 178 _simpleStatement(node, () { |
| 179 token(node.assertKeyword); |
| 180 |
| 181 var arguments = <Expression>[node.condition]; |
| 182 if (node.message != null) arguments.add(node.message); |
| 183 |
| 184 builder.nestExpression(); |
| 185 var visitor = new ArgumentListVisitor.forArguments( |
| 186 this, node.leftParenthesis, node.rightParenthesis, arguments); |
| 187 visitor.visit(); |
| 188 builder.unnest(); |
| 189 }); |
| 190 } |
| 191 |
| 161 visitAssertStatement(AssertStatement node) { | 192 visitAssertStatement(AssertStatement node) { |
| 162 _simpleStatement(node, () { | 193 _simpleStatement(node, () { |
| 163 token(node.assertKeyword); | 194 token(node.assertKeyword); |
| 164 token(node.leftParenthesis); | 195 |
| 165 soloZeroSplit(); | 196 var arguments = [node.condition]; |
| 166 visit(node.condition); | 197 if (node.message != null) arguments.add(node.message); |
| 167 token(node.rightParenthesis); | 198 |
| 199 var visitor = new ArgumentListVisitor.forArguments( |
| 200 this, node.leftParenthesis, node.rightParenthesis, arguments); |
| 201 visitor.visit(); |
| 168 }); | 202 }); |
| 169 } | 203 } |
| 170 | 204 |
| 171 visitAssignmentExpression(AssignmentExpression node) { | 205 visitAssignmentExpression(AssignmentExpression node) { |
| 172 builder.nestExpression(); | 206 builder.nestExpression(); |
| 173 | 207 |
| 174 visit(node.leftHandSide); | 208 visit(node.leftHandSide); |
| 175 _visitAssignment(node.operator, node.rightHandSide); | 209 _visitAssignment(node.operator, node.rightHandSide); |
| 176 | 210 |
| 177 builder.unnest(); | 211 builder.unnest(); |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 234 | 268 |
| 235 visitBlock(Block node) { | 269 visitBlock(Block node) { |
| 236 // Don't allow splitting in an empty block. | 270 // Don't allow splitting in an empty block. |
| 237 if (node.statements.isEmpty && | 271 if (node.statements.isEmpty && |
| 238 node.rightBracket.precedingComments == null) { | 272 node.rightBracket.precedingComments == null) { |
| 239 token(node.leftBracket); | 273 token(node.leftBracket); |
| 240 token(node.rightBracket); | 274 token(node.rightBracket); |
| 241 return; | 275 return; |
| 242 } | 276 } |
| 243 | 277 |
| 244 // For a block that is not a function body, just bump the indentation and | 278 // If the block is a function body, it may get expression-level indentation, |
| 245 // keep it in the current block. | 279 // so handle it specially. Otherwise, just bump the indentation and keep it |
| 246 if (node.parent is! BlockFunctionBody) { | 280 // in the current block. |
| 247 _writeBody(node.leftBracket, node.rightBracket, body: () { | 281 if (node.parent is BlockFunctionBody) { |
| 248 visitNodes(node.statements, between: oneOrTwoNewlines, after: newline); | 282 _startLiteralBody(node.leftBracket); |
| 249 }); | 283 } else { |
| 250 return; | 284 _beginBody(node.leftBracket); |
| 251 } | 285 } |
| 252 | 286 |
| 253 _startLiteralBody(node.leftBracket); | 287 var needsDouble = true; |
| 254 visitNodes(node.statements, between: oneOrTwoNewlines, after: newline); | 288 for (var statement in node.statements) { |
| 255 _endLiteralBody(node.rightBracket, forceSplit: node.statements.isNotEmpty); | 289 if (needsDouble) { |
| 290 twoNewlines(); |
| 291 } else { |
| 292 oneOrTwoNewlines(); |
| 293 } |
| 294 |
| 295 visit(statement); |
| 296 |
| 297 needsDouble = false; |
| 298 if (statement is FunctionDeclarationStatement) { |
| 299 // Add a blank line after non-empty block functions. |
| 300 var body = statement.functionDeclaration.functionExpression.body; |
| 301 if (body is BlockFunctionBody) { |
| 302 needsDouble = body.block.statements.isNotEmpty; |
| 303 } |
| 304 } |
| 305 } |
| 306 |
| 307 if (node.statements.isNotEmpty) newline(); |
| 308 |
| 309 if (node.parent is BlockFunctionBody) { |
| 310 _endLiteralBody(node.rightBracket, |
| 311 forceSplit: node.statements.isNotEmpty); |
| 312 } else { |
| 313 _endBody(node.rightBracket); |
| 314 } |
| 256 } | 315 } |
| 257 | 316 |
| 258 visitBlockFunctionBody(BlockFunctionBody node) { | 317 visitBlockFunctionBody(BlockFunctionBody node) { |
| 259 // Space after the parameter list. | 318 // Space after the parameter list. |
| 260 space(); | 319 space(); |
| 261 | 320 |
| 262 // The "async" or "sync" keyword. | 321 // The "async" or "sync" keyword. |
| 263 token(node.keyword); | 322 token(node.keyword); |
| 264 | 323 |
| 265 // The "*" in "async*" or "sync*". | 324 // The "*" in "async*" or "sync*". |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 332 /// Whether a cascade should be allowed to be inline as opposed to one | 391 /// Whether a cascade should be allowed to be inline as opposed to one |
| 333 /// expression per line. | 392 /// expression per line. |
| 334 bool _allowInlineCascade(List<Expression> sections) { | 393 bool _allowInlineCascade(List<Expression> sections) { |
| 335 if (sections.length < 2) return true; | 394 if (sections.length < 2) return true; |
| 336 | 395 |
| 337 var name; | 396 var name; |
| 338 // We could be more forgiving about what constitutes sections with | 397 // We could be more forgiving about what constitutes sections with |
| 339 // consistent names but for now we require all sections to have the same | 398 // consistent names but for now we require all sections to have the same |
| 340 // method name. | 399 // method name. |
| 341 for (var expression in sections) { | 400 for (var expression in sections) { |
| 342 if (expression is! MethodInvocation) return false; | 401 if (expression is MethodInvocation) { |
| 343 if (name == null) { | 402 if (name == null) { |
| 344 name = expression.methodName.name; | 403 name = expression.methodName.name; |
| 345 } else if (name != expression.methodName.name) { | 404 } else if (name != expression.methodName.name) { |
| 405 return false; |
| 406 } |
| 407 } else { |
| 346 return false; | 408 return false; |
| 347 } | 409 } |
| 348 } | 410 } |
| 349 return true; | 411 return true; |
| 350 } | 412 } |
| 351 | 413 |
| 352 visitCatchClause(CatchClause node) { | 414 visitCatchClause(CatchClause node) { |
| 353 token(node.onKeyword, after: space); | 415 token(node.onKeyword, after: space); |
| 354 visit(node.exceptionType); | 416 visit(node.exceptionType); |
| 355 | 417 |
| 356 if (node.catchKeyword != null) { | 418 if (node.catchKeyword != null) { |
| 357 if (node.exceptionType != null) { | 419 if (node.exceptionType != null) { |
| 358 space(); | 420 space(); |
| 359 } | 421 } |
| 360 token(node.catchKeyword); | 422 token(node.catchKeyword); |
| 361 space(); | 423 space(); |
| 362 token(node.leftParenthesis); | 424 token(node.leftParenthesis); |
| 363 visit(node.exceptionParameter); | 425 visit(node.exceptionParameter); |
| 364 token(node.comma, after: space); | 426 token(node.comma, after: space); |
| 365 visit(node.stackTraceParameter); | 427 visit(node.stackTraceParameter); |
| 366 token(node.rightParenthesis); | 428 token(node.rightParenthesis); |
| 367 space(); | 429 space(); |
| 368 } else { | 430 } else { |
| 369 space(); | 431 space(); |
| 370 } | 432 } |
| 371 visit(node.body); | 433 visit(node.body); |
| 372 } | 434 } |
| 373 | 435 |
| 374 visitClassDeclaration(ClassDeclaration node) { | 436 visitClassDeclaration(ClassDeclaration node) { |
| 375 visitDeclarationMetadata(node.metadata); | 437 visitMetadata(node.metadata); |
| 376 | 438 |
| 377 builder.nestExpression(); | 439 builder.nestExpression(); |
| 378 modifier(node.abstractKeyword); | 440 modifier(node.abstractKeyword); |
| 379 token(node.classKeyword); | 441 token(node.classKeyword); |
| 380 space(); | 442 space(); |
| 381 visit(node.name); | 443 visit(node.name); |
| 382 visit(node.typeParameters); | 444 visit(node.typeParameters); |
| 383 visit(node.extendsClause); | 445 visit(node.extendsClause); |
| 384 | 446 |
| 385 builder.startRule(new CombinatorRule()); | 447 builder.startRule(new CombinatorRule()); |
| 386 visit(node.withClause); | 448 visit(node.withClause); |
| 387 visit(node.implementsClause); | 449 visit(node.implementsClause); |
| 388 builder.endRule(); | 450 builder.endRule(); |
| 389 | 451 |
| 390 visit(node.nativeClause, before: space); | 452 visit(node.nativeClause, before: space); |
| 391 space(); | 453 space(); |
| 392 | 454 |
| 393 builder.unnest(); | 455 builder.unnest(); |
| 394 _writeBody(node.leftBracket, node.rightBracket, body: () { | 456 _beginBody(node.leftBracket); |
| 395 if (node.members.isNotEmpty) { | |
| 396 for (var member in node.members) { | |
| 397 visit(member); | |
| 398 | 457 |
| 399 if (member == node.members.last) { | 458 if (node.members.isNotEmpty) { |
| 400 newline(); | 459 for (var member in node.members) { |
| 401 break; | 460 visit(member); |
| 402 } | |
| 403 | 461 |
| 404 var needsDouble = false; | 462 if (member == node.members.last) { |
| 405 if (member is ClassDeclaration) { | 463 newline(); |
| 406 // Add a blank line after classes. | 464 break; |
| 407 twoNewlines(); | 465 } |
| 408 } else if (member is MethodDeclaration) { | |
| 409 // Add a blank line after non-empty block methods. | |
| 410 var method = member as MethodDeclaration; | |
| 411 if (method.body is BlockFunctionBody) { | |
| 412 var body = method.body as BlockFunctionBody; | |
| 413 needsDouble = body.block.statements.isNotEmpty; | |
| 414 } | |
| 415 } | |
| 416 | 466 |
| 417 if (needsDouble) { | 467 var needsDouble = false; |
| 418 twoNewlines(); | 468 if (member is ClassDeclaration) { |
| 419 } else { | 469 // Add a blank line after classes. |
| 420 // Variables and arrow-bodied members can be more tightly packed if | 470 twoNewlines(); |
| 421 // the user wants to group things together. | 471 } else if (member is MethodDeclaration) { |
| 422 oneOrTwoNewlines(); | 472 // Add a blank line after non-empty block methods. |
| 473 if (member.body is BlockFunctionBody) { |
| 474 var body = member.body as BlockFunctionBody; |
| 475 needsDouble = body.block.statements.isNotEmpty; |
| 423 } | 476 } |
| 424 } | 477 } |
| 478 |
| 479 if (needsDouble) { |
| 480 twoNewlines(); |
| 481 } else { |
| 482 // Variables and arrow-bodied members can be more tightly packed if |
| 483 // the user wants to group things together. |
| 484 oneOrTwoNewlines(); |
| 485 } |
| 425 } | 486 } |
| 426 }); | 487 } |
| 488 |
| 489 _endBody(node.rightBracket); |
| 427 } | 490 } |
| 428 | 491 |
| 429 visitClassTypeAlias(ClassTypeAlias node) { | 492 visitClassTypeAlias(ClassTypeAlias node) { |
| 430 visitDeclarationMetadata(node.metadata); | 493 visitMetadata(node.metadata); |
| 431 | 494 |
| 432 _simpleStatement(node, () { | 495 _simpleStatement(node, () { |
| 433 modifier(node.abstractKeyword); | 496 modifier(node.abstractKeyword); |
| 434 token(node.typedefKeyword); | 497 token(node.typedefKeyword); |
| 435 space(); | 498 space(); |
| 436 visit(node.name); | 499 visit(node.name); |
| 437 visit(node.typeParameters); | 500 visit(node.typeParameters); |
| 438 space(); | 501 space(); |
| 439 token(node.equals); | 502 token(node.equals); |
| 440 space(); | 503 space(); |
| (...skipping 18 matching lines...) Expand all Loading... |
| 459 var directives = node.directives; | 522 var directives = node.directives; |
| 460 if (directives.isNotEmpty && directives.first is LibraryDirective) { | 523 if (directives.isNotEmpty && directives.first is LibraryDirective) { |
| 461 visit(directives.first); | 524 visit(directives.first); |
| 462 twoNewlines(); | 525 twoNewlines(); |
| 463 | 526 |
| 464 directives = directives.skip(1); | 527 directives = directives.skip(1); |
| 465 } | 528 } |
| 466 | 529 |
| 467 visitNodes(directives, between: oneOrTwoNewlines); | 530 visitNodes(directives, between: oneOrTwoNewlines); |
| 468 | 531 |
| 469 if (node.declarations.isNotEmpty) { | 532 var needsDouble = true; |
| 470 var needsDouble = true; | 533 for (var declaration in node.declarations) { |
| 534 // Add a blank line before classes. |
| 535 if (declaration is ClassDeclaration) needsDouble = true; |
| 471 | 536 |
| 472 for (var declaration in node.declarations) { | 537 if (needsDouble) { |
| 473 // Add a blank line before classes. | 538 twoNewlines(); |
| 474 if (declaration is ClassDeclaration) needsDouble = true; | 539 } else { |
| 540 // Variables and arrow-bodied members can be more tightly packed if |
| 541 // the user wants to group things together. |
| 542 oneOrTwoNewlines(); |
| 543 } |
| 475 | 544 |
| 476 if (needsDouble) { | 545 visit(declaration); |
| 477 twoNewlines(); | |
| 478 } else { | |
| 479 // Variables and arrow-bodied members can be more tightly packed if | |
| 480 // the user wants to group things together. | |
| 481 oneOrTwoNewlines(); | |
| 482 } | |
| 483 | 546 |
| 484 visit(declaration); | 547 needsDouble = false; |
| 485 | 548 if (declaration is ClassDeclaration) { |
| 486 needsDouble = false; | 549 // Add a blank line after classes. |
| 487 if (declaration is ClassDeclaration) { | 550 needsDouble = true; |
| 488 // Add a blank line after classes. | 551 } else if (declaration is FunctionDeclaration) { |
| 489 needsDouble = true; | 552 // Add a blank line after non-empty block functions. |
| 490 } else if (declaration is FunctionDeclaration) { | 553 var body = declaration.functionExpression.body; |
| 491 // Add a blank line after non-empty block functions. | 554 if (body is BlockFunctionBody) { |
| 492 var function = declaration as FunctionDeclaration; | 555 needsDouble = body.block.statements.isNotEmpty; |
| 493 if (function.functionExpression.body is BlockFunctionBody) { | |
| 494 var body = function.functionExpression.body as BlockFunctionBody; | |
| 495 needsDouble = body.block.statements.isNotEmpty; | |
| 496 } | |
| 497 } | 556 } |
| 498 } | 557 } |
| 499 } | 558 } |
| 500 } | 559 } |
| 501 | 560 |
| 502 visitConditionalExpression(ConditionalExpression node) { | 561 visitConditionalExpression(ConditionalExpression node) { |
| 503 builder.nestExpression(); | 562 builder.nestExpression(); |
| 504 | 563 |
| 564 // Start lazily so we don't force the operator to split if a line comment |
| 565 // appears before the first operand. If we split after one clause in a |
| 566 // conditional, always split after both. |
| 567 builder.startLazyRule(); |
| 568 visit(node.condition); |
| 569 |
| 505 // Push any block arguments all the way past the leading "?" and ":". | 570 // Push any block arguments all the way past the leading "?" and ":". |
| 506 builder.nestExpression(indent: Indent.block, now: true); | 571 builder.nestExpression(indent: Indent.block, now: true); |
| 507 builder.startBlockArgumentNesting(); | 572 builder.startBlockArgumentNesting(); |
| 508 builder.unnest(); | 573 builder.unnest(); |
| 509 | 574 |
| 510 visit(node.condition); | |
| 511 | |
| 512 builder.startSpan(); | 575 builder.startSpan(); |
| 513 | 576 |
| 514 // If we split after one clause in a conditional, always split after both. | |
| 515 builder.startRule(); | |
| 516 split(); | 577 split(); |
| 517 token(node.question); | 578 token(node.question); |
| 518 space(); | 579 space(); |
| 519 | |
| 520 builder.nestExpression(); | 580 builder.nestExpression(); |
| 521 visit(node.thenExpression); | 581 visit(node.thenExpression); |
| 522 builder.unnest(); | 582 builder.unnest(); |
| 523 | 583 |
| 524 split(); | 584 split(); |
| 525 token(node.colon); | 585 token(node.colon); |
| 526 space(); | 586 space(); |
| 527 | |
| 528 visit(node.elseExpression); | 587 visit(node.elseExpression); |
| 529 | 588 |
| 530 builder.endRule(); | 589 builder.endRule(); |
| 531 builder.endSpan(); | 590 builder.endSpan(); |
| 532 builder.endBlockArgumentNesting(); | 591 builder.endBlockArgumentNesting(); |
| 533 builder.unnest(); | 592 builder.unnest(); |
| 534 } | 593 } |
| 535 | 594 |
| 595 visitConfiguration(Configuration node) { |
| 596 token(node.ifKeyword); |
| 597 space(); |
| 598 token(node.leftParenthesis); |
| 599 visit(node.name); |
| 600 |
| 601 if (node.equalToken != null) { |
| 602 builder.nestExpression(); |
| 603 space(); |
| 604 token(node.equalToken); |
| 605 soloSplit(); |
| 606 visit(node.value); |
| 607 builder.unnest(); |
| 608 } |
| 609 |
| 610 token(node.rightParenthesis); |
| 611 space(); |
| 612 visit(node.uri); |
| 613 } |
| 614 |
| 536 visitConstructorDeclaration(ConstructorDeclaration node) { | 615 visitConstructorDeclaration(ConstructorDeclaration node) { |
| 537 visitMemberMetadata(node.metadata); | 616 visitMetadata(node.metadata); |
| 538 | 617 |
| 539 modifier(node.externalKeyword); | 618 modifier(node.externalKeyword); |
| 540 modifier(node.constKeyword); | 619 modifier(node.constKeyword); |
| 541 modifier(node.factoryKeyword); | 620 modifier(node.factoryKeyword); |
| 542 visit(node.returnType); | 621 visit(node.returnType); |
| 543 token(node.period); | 622 token(node.period); |
| 544 visit(node.name); | 623 visit(node.name); |
| 545 | 624 |
| 546 // Make the rule for the ":" span both the preceding parameter list and | 625 // Make the rule for the ":" span both the preceding parameter list and |
| 547 // the entire initialization list. This ensures that we split before the | 626 // the entire initialization list. This ensures that we split before the |
| 548 // ":" if the parameters and initialization list don't all fit on one line. | 627 // ":" if the parameters and initialization list don't all fit on one line. |
| 549 builder.startRule(); | 628 builder.startRule(); |
| 550 | 629 |
| 551 // If the redirecting constructor happens to wrap, we want to make sure | 630 // If the redirecting constructor happens to wrap, we want to make sure |
| 552 // the parameter list gets more deeply indented. | 631 // the parameter list gets more deeply indented. |
| 553 if (node.redirectedConstructor != null) builder.nestExpression(); | 632 if (node.redirectedConstructor != null) builder.nestExpression(); |
| 554 | 633 |
| 555 _visitBody(node.parameters, node.body, () { | 634 _visitBody(null, node.parameters, node.body, () { |
| 556 // Check for redirects or initializer lists. | 635 // Check for redirects or initializer lists. |
| 557 if (node.redirectedConstructor != null) { | 636 if (node.redirectedConstructor != null) { |
| 558 _visitConstructorRedirects(node); | 637 _visitConstructorRedirects(node); |
| 559 builder.unnest(); | 638 builder.unnest(); |
| 560 } else if (node.initializers.isNotEmpty) { | 639 } else if (node.initializers.isNotEmpty) { |
| 561 _visitConstructorInitializers(node); | 640 _visitConstructorInitializers(node); |
| 562 } | 641 } |
| 563 }); | 642 }); |
| 564 } | 643 } |
| 565 | 644 |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 659 token(node.whileKeyword); | 738 token(node.whileKeyword); |
| 660 space(); | 739 space(); |
| 661 token(node.leftParenthesis); | 740 token(node.leftParenthesis); |
| 662 soloZeroSplit(); | 741 soloZeroSplit(); |
| 663 visit(node.condition); | 742 visit(node.condition); |
| 664 token(node.rightParenthesis); | 743 token(node.rightParenthesis); |
| 665 token(node.semicolon); | 744 token(node.semicolon); |
| 666 builder.unnest(); | 745 builder.unnest(); |
| 667 } | 746 } |
| 668 | 747 |
| 748 visitDottedName(DottedName node) { |
| 749 for (var component in node.components) { |
| 750 // Write the preceding ".". |
| 751 if (component != node.components.first) { |
| 752 token(component.beginToken.previous); |
| 753 } |
| 754 |
| 755 visit(component); |
| 756 } |
| 757 } |
| 758 |
| 669 visitDoubleLiteral(DoubleLiteral node) { | 759 visitDoubleLiteral(DoubleLiteral node) { |
| 670 token(node.literal); | 760 token(node.literal); |
| 671 } | 761 } |
| 672 | 762 |
| 673 visitEmptyFunctionBody(EmptyFunctionBody node) { | 763 visitEmptyFunctionBody(EmptyFunctionBody node) { |
| 674 token(node.semicolon); | 764 token(node.semicolon); |
| 675 } | 765 } |
| 676 | 766 |
| 677 visitEmptyStatement(EmptyStatement node) { | 767 visitEmptyStatement(EmptyStatement node) { |
| 678 token(node.semicolon); | 768 token(node.semicolon); |
| 679 } | 769 } |
| 680 | 770 |
| 681 visitEnumConstantDeclaration(EnumConstantDeclaration node) { | 771 visitEnumConstantDeclaration(EnumConstantDeclaration node) { |
| 682 visit(node.name); | 772 visit(node.name); |
| 683 } | 773 } |
| 684 | 774 |
| 685 visitEnumDeclaration(EnumDeclaration node) { | 775 visitEnumDeclaration(EnumDeclaration node) { |
| 686 visitDeclarationMetadata(node.metadata); | 776 visitMetadata(node.metadata); |
| 687 | 777 |
| 688 token(node.enumKeyword); | 778 token(node.enumKeyword); |
| 689 space(); | 779 space(); |
| 690 visit(node.name); | 780 visit(node.name); |
| 691 space(); | 781 space(); |
| 692 | 782 |
| 693 _writeBody(node.leftBracket, node.rightBracket, space: true, body: () { | 783 _beginBody(node.leftBracket, space: true); |
| 694 visitCommaSeparatedNodes(node.constants, between: split); | 784 visitCommaSeparatedNodes(node.constants, between: split); |
| 695 }); | 785 |
| 786 // If there is a trailing comma, always force the constants to split. |
| 787 if (node.constants.last.endToken.next.type == TokenType.COMMA) { |
| 788 builder.forceRules(); |
| 789 } |
| 790 |
| 791 _endBody(node.rightBracket, space: true); |
| 696 } | 792 } |
| 697 | 793 |
| 698 visitExportDirective(ExportDirective node) { | 794 visitExportDirective(ExportDirective node) { |
| 699 visitDeclarationMetadata(node.metadata); | 795 visitMetadata(node.metadata); |
| 700 | 796 |
| 701 _simpleStatement(node, () { | 797 _simpleStatement(node, () { |
| 702 token(node.keyword); | 798 token(node.keyword); |
| 703 space(); | 799 space(); |
| 704 visit(node.uri); | 800 visit(node.uri); |
| 705 | 801 |
| 802 _visitConfigurations(node.configurations); |
| 803 |
| 706 builder.startRule(new CombinatorRule()); | 804 builder.startRule(new CombinatorRule()); |
| 707 visitNodes(node.combinators); | 805 visitNodes(node.combinators); |
| 708 builder.endRule(); | 806 builder.endRule(); |
| 709 }); | 807 }); |
| 710 } | 808 } |
| 711 | 809 |
| 712 visitExpressionFunctionBody(ExpressionFunctionBody node) { | 810 visitExpressionFunctionBody(ExpressionFunctionBody node) { |
| 713 // Space after the parameter list. | 811 // Space after the parameter list. |
| 714 space(); | 812 space(); |
| 715 | 813 |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 750 } | 848 } |
| 751 | 849 |
| 752 visitExtendsClause(ExtendsClause node) { | 850 visitExtendsClause(ExtendsClause node) { |
| 753 soloSplit(); | 851 soloSplit(); |
| 754 token(node.extendsKeyword); | 852 token(node.extendsKeyword); |
| 755 space(); | 853 space(); |
| 756 visit(node.superclass); | 854 visit(node.superclass); |
| 757 } | 855 } |
| 758 | 856 |
| 759 visitFieldDeclaration(FieldDeclaration node) { | 857 visitFieldDeclaration(FieldDeclaration node) { |
| 760 visitMemberMetadata(node.metadata); | 858 visitMetadata(node.metadata); |
| 761 | 859 |
| 762 _simpleStatement(node, () { | 860 _simpleStatement(node, () { |
| 763 modifier(node.staticKeyword); | 861 modifier(node.staticKeyword); |
| 862 modifier(node.covariantKeyword); |
| 764 visit(node.fields); | 863 visit(node.fields); |
| 765 }); | 864 }); |
| 766 } | 865 } |
| 767 | 866 |
| 768 visitFieldFormalParameter(FieldFormalParameter node) { | 867 visitFieldFormalParameter(FieldFormalParameter node) { |
| 769 visitParameterMetadata(node.metadata, () { | 868 visitParameterMetadata(node.metadata, () { |
| 869 modifier(node.covariantKeyword); |
| 770 token(node.keyword, after: space); | 870 token(node.keyword, after: space); |
| 771 visit(node.type, after: space); | 871 visit(node.type, after: space); |
| 772 token(node.thisKeyword); | 872 token(node.thisKeyword); |
| 773 token(node.period); | 873 token(node.period); |
| 774 visit(node.identifier); | 874 visit(node.identifier); |
| 775 visit(node.parameters); | 875 visit(node.parameters); |
| 776 }); | 876 }); |
| 777 } | 877 } |
| 778 | 878 |
| 779 visitForEachStatement(ForEachStatement node) { | 879 visitForEachStatement(ForEachStatement node) { |
| 780 builder.nestExpression(); | 880 builder.nestExpression(); |
| 781 token(node.awaitKeyword, after: space); | 881 token(node.awaitKeyword, after: space); |
| 782 token(node.forKeyword); | 882 token(node.forKeyword); |
| 783 space(); | 883 space(); |
| 784 token(node.leftParenthesis); | 884 token(node.leftParenthesis); |
| 785 if (node.loopVariable != null) { | 885 if (node.loopVariable != null) { |
| 786 visit(node.loopVariable); | 886 visit(node.loopVariable); |
| 787 } else { | 887 } else { |
| 788 visit(node.identifier); | 888 visit(node.identifier); |
| 789 } | 889 } |
| 790 soloSplit(); | 890 soloSplit(); |
| 791 token(node.inKeyword); | 891 token(node.inKeyword); |
| 792 space(); | 892 space(); |
| 793 visit(node.iterable); | 893 visit(node.iterable); |
| 794 token(node.rightParenthesis); | 894 token(node.rightParenthesis); |
| 795 builder.unnest(now: false); | 895 builder.unnest(); |
| 796 | 896 |
| 797 _visitLoopBody(node.body); | 897 _visitLoopBody(node.body); |
| 798 } | 898 } |
| 799 | 899 |
| 800 visitFormalParameterList(FormalParameterList node) { | 900 visitFormalParameterList(FormalParameterList node, |
| 901 {bool nestExpression: true}) { |
| 801 // Corner case: empty parameter lists. | 902 // Corner case: empty parameter lists. |
| 802 if (node.parameters.isEmpty) { | 903 if (node.parameters.isEmpty) { |
| 803 token(node.leftParenthesis); | 904 token(node.leftParenthesis); |
| 804 | 905 |
| 805 // If there is a comment, do allow splitting before it. | 906 // If there is a comment, do allow splitting before it. |
| 806 if (node.rightParenthesis.precedingComments != null) soloZeroSplit(); | 907 if (node.rightParenthesis.precedingComments != null) soloZeroSplit(); |
| 807 | 908 |
| 808 token(node.rightParenthesis); | 909 token(node.rightParenthesis); |
| 809 return; | 910 return; |
| 810 } | 911 } |
| 811 | 912 |
| 913 // If the parameter list has a trailing comma, format it like a collection |
| 914 // literal where each parameter goes on its own line, they are indented +2, |
| 915 // and the ")" ends up on its own line. |
| 916 if (node.parameters.last.endToken.next.type == TokenType.COMMA) { |
| 917 _visitTrailingCommaParameterList(node); |
| 918 return; |
| 919 } |
| 920 |
| 812 var requiredParams = node.parameters | 921 var requiredParams = node.parameters |
| 813 .where((param) => param is! DefaultFormalParameter) | 922 .where((param) => param is! DefaultFormalParameter) |
| 814 .toList(); | 923 .toList(); |
| 815 var optionalParams = node.parameters | 924 var optionalParams = node.parameters |
| 816 .where((param) => param is DefaultFormalParameter) | 925 .where((param) => param is DefaultFormalParameter) |
| 817 .toList(); | 926 .toList(); |
| 818 | 927 |
| 819 builder.nestExpression(); | 928 if (nestExpression) builder.nestExpression(); |
| 820 token(node.leftParenthesis); | 929 token(node.leftParenthesis); |
| 821 | 930 |
| 822 _metadataRules.add(new MetadataRule()); | 931 _metadataRules.add(new MetadataRule()); |
| 823 | 932 |
| 824 var rule; | 933 var rule; |
| 825 if (requiredParams.isNotEmpty) { | 934 if (requiredParams.isNotEmpty) { |
| 826 if (requiredParams.length > 1) { | 935 rule = new PositionalRule(null, 0, 0); |
| 827 rule = new MultiplePositionalRule(null, 0, 0); | |
| 828 } else { | |
| 829 rule = new SinglePositionalRule(null); | |
| 830 } | |
| 831 | |
| 832 _metadataRules.last.bindPositionalRule(rule); | 936 _metadataRules.last.bindPositionalRule(rule); |
| 833 | 937 |
| 834 builder.startRule(rule); | 938 builder.startRule(rule); |
| 835 if (_isInLambda(node)) { | 939 if (_isInLambda(node)) { |
| 836 // Don't allow splitting before the first argument (i.e. right after | 940 // Don't allow splitting before the first argument (i.e. right after |
| 837 // the bare "(" in a lambda. Instead, just stuff a null chunk in there | 941 // the bare "(" in a lambda. Instead, just stuff a null chunk in there |
| 838 // to avoid confusing the arg rule. | 942 // to avoid confusing the arg rule. |
| 839 rule.beforeArgument(null); | 943 rule.beforeArgument(null); |
| 840 } else { | 944 } else { |
| 841 // Split before the first argument. | 945 // Split before the first argument. |
| 842 rule.beforeArgument(zeroSplit()); | 946 rule.beforeArgument(zeroSplit()); |
| 843 } | 947 } |
| 844 | 948 |
| 845 builder.startSpan(); | 949 builder.startSpan(); |
| 846 | 950 |
| 847 for (var param in requiredParams) { | 951 for (var param in requiredParams) { |
| 848 visit(param); | 952 visit(param); |
| 849 | 953 |
| 850 // Write the trailing comma. | 954 // Write the following comma. |
| 851 if (param != node.parameters.last) token(param.endToken.next); | 955 if (param.endToken.next.type == TokenType.COMMA) { |
| 956 token(param.endToken.next); |
| 957 } |
| 852 | 958 |
| 853 if (param != requiredParams.last) rule.beforeArgument(split()); | 959 if (param != requiredParams.last) rule.beforeArgument(split()); |
| 854 } | 960 } |
| 855 | 961 |
| 856 builder.endSpan(); | 962 builder.endSpan(); |
| 857 builder.endRule(); | 963 builder.endRule(); |
| 858 } | 964 } |
| 859 | 965 |
| 860 if (optionalParams.isNotEmpty) { | 966 if (optionalParams.isNotEmpty) { |
| 861 var namedRule = new NamedRule(null, 0, 0); | 967 var namedRule = new NamedRule(null, 0, 0); |
| 862 if (rule != null) rule.setNamedArgsRule(namedRule); | 968 if (rule != null) rule.setNamedArgsRule(namedRule); |
| 863 | 969 |
| 864 _metadataRules.last.bindNamedRule(namedRule); | 970 _metadataRules.last.bindNamedRule(namedRule); |
| 865 | 971 |
| 866 builder.startRule(namedRule); | 972 builder.startRule(namedRule); |
| 867 | 973 |
| 868 // Make sure multi-line default values are indented. | 974 // Make sure multi-line default values are indented. |
| 869 builder.startBlockArgumentNesting(); | 975 builder.startBlockArgumentNesting(); |
| 870 | 976 |
| 871 namedRule.beforeArgument(builder.split(space: requiredParams.isNotEmpty)); | 977 namedRule.beforeArgument(builder.split(space: requiredParams.isNotEmpty)); |
| 872 | 978 |
| 873 // "[" or "{" for optional parameters. | 979 // "[" or "{" for optional parameters. |
| 874 token(node.leftDelimiter); | 980 token(node.leftDelimiter); |
| 875 | 981 |
| 876 for (var param in optionalParams) { | 982 for (var param in optionalParams) { |
| 877 visit(param); | 983 visit(param); |
| 878 | 984 |
| 879 // Write the trailing comma. | 985 // Write the following comma. |
| 880 if (param != node.parameters.last) token(param.endToken.next); | 986 if (param.endToken.next.type == TokenType.COMMA) { |
| 987 token(param.endToken.next); |
| 988 } |
| 989 |
| 881 if (param != optionalParams.last) namedRule.beforeArgument(split()); | 990 if (param != optionalParams.last) namedRule.beforeArgument(split()); |
| 882 } | 991 } |
| 883 | 992 |
| 884 builder.endBlockArgumentNesting(); | 993 builder.endBlockArgumentNesting(); |
| 885 builder.endRule(); | 994 builder.endRule(); |
| 886 | 995 |
| 887 // "]" or "}" for optional parameters. | 996 // "]" or "}" for optional parameters. |
| 888 token(node.rightDelimiter); | 997 token(node.rightDelimiter); |
| 889 } | 998 } |
| 890 | 999 |
| 891 _metadataRules.removeLast(); | 1000 _metadataRules.removeLast(); |
| 892 | 1001 |
| 893 token(node.rightParenthesis); | 1002 token(node.rightParenthesis); |
| 894 builder.unnest(); | 1003 if (nestExpression) builder.unnest(); |
| 895 } | 1004 } |
| 896 | 1005 |
| 897 visitForStatement(ForStatement node) { | 1006 visitForStatement(ForStatement node) { |
| 898 builder.nestExpression(); | 1007 builder.nestExpression(); |
| 899 token(node.forKeyword); | 1008 token(node.forKeyword); |
| 900 space(); | 1009 space(); |
| 901 token(node.leftParenthesis); | 1010 token(node.leftParenthesis); |
| 902 | 1011 |
| 903 builder.startRule(); | 1012 builder.startRule(); |
| 904 | 1013 |
| 905 // The initialization clause. | 1014 // The initialization clause. |
| 906 if (node.initialization != null) { | 1015 if (node.initialization != null) { |
| 907 visit(node.initialization); | 1016 visit(node.initialization); |
| 908 } else if (node.variables != null) { | 1017 } else if (node.variables != null) { |
| 909 // Nest split variables more so they aren't at the same level | 1018 // Nest split variables more so they aren't at the same level |
| 910 // as the rest of the loop clauses. | 1019 // as the rest of the loop clauses. |
| 911 builder.nestExpression(); | 1020 builder.nestExpression(); |
| 912 | 1021 |
| 913 // Allow the variables to stay unsplit even if the clauses split. | 1022 // Allow the variables to stay unsplit even if the clauses split. |
| 914 builder.startRule(); | 1023 builder.startRule(); |
| 915 | 1024 |
| 916 var declaration = node.variables; | 1025 var declaration = node.variables; |
| 917 visitDeclarationMetadata(declaration.metadata); | 1026 visitMetadata(declaration.metadata); |
| 918 modifier(declaration.keyword); | 1027 modifier(declaration.keyword); |
| 919 visit(declaration.type, after: space); | 1028 visit(declaration.type, after: space); |
| 920 | 1029 |
| 921 visitCommaSeparatedNodes(declaration.variables, between: () { | 1030 visitCommaSeparatedNodes(declaration.variables, between: () { |
| 922 split(); | 1031 split(); |
| 923 }); | 1032 }); |
| 924 | 1033 |
| 925 builder.endRule(); | 1034 builder.endRule(); |
| 926 builder.unnest(); | 1035 builder.unnest(); |
| 927 } | 1036 } |
| (...skipping 26 matching lines...) Expand all Loading... |
| 954 | 1063 |
| 955 visitFunctionDeclaration(FunctionDeclaration node) { | 1064 visitFunctionDeclaration(FunctionDeclaration node) { |
| 956 _visitMemberDeclaration(node, node.functionExpression); | 1065 _visitMemberDeclaration(node, node.functionExpression); |
| 957 } | 1066 } |
| 958 | 1067 |
| 959 visitFunctionDeclarationStatement(FunctionDeclarationStatement node) { | 1068 visitFunctionDeclarationStatement(FunctionDeclarationStatement node) { |
| 960 visit(node.functionDeclaration); | 1069 visit(node.functionDeclaration); |
| 961 } | 1070 } |
| 962 | 1071 |
| 963 visitFunctionExpression(FunctionExpression node) { | 1072 visitFunctionExpression(FunctionExpression node) { |
| 964 _visitBody(node.parameters, node.body); | 1073 // TODO(rnystrom): This is working but not tested. As of 2016/11/29, the |
| 1074 // latest version of analyzer on pub does not parse generic lambdas. When |
| 1075 // a version of it that does is published, upgrade dart_style to use it and |
| 1076 // then test it: |
| 1077 // |
| 1078 // >>> generic function expression |
| 1079 // main() { |
| 1080 // var generic = < T,S >(){}; |
| 1081 // } |
| 1082 // <<< |
| 1083 // main() { |
| 1084 // var generic = <T, S>() {}; |
| 1085 // } |
| 1086 _visitBody(node.typeParameters, node.parameters, node.body); |
| 965 } | 1087 } |
| 966 | 1088 |
| 967 visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { | 1089 visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { |
| 968 visit(node.function); | 1090 visit(node.function); |
| 969 visit(node.argumentList); | 1091 visit(node.argumentList); |
| 970 } | 1092 } |
| 971 | 1093 |
| 972 visitFunctionTypeAlias(FunctionTypeAlias node) { | 1094 visitFunctionTypeAlias(FunctionTypeAlias node) { |
| 973 visitDeclarationMetadata(node.metadata); | 1095 visitMetadata(node.metadata); |
| 974 | 1096 |
| 975 _simpleStatement(node, () { | 1097 _simpleStatement(node, () { |
| 976 token(node.typedefKeyword); | 1098 token(node.typedefKeyword); |
| 977 space(); | 1099 space(); |
| 978 visit(node.returnType, after: space); | 1100 visit(node.returnType, after: space); |
| 979 visit(node.name); | 1101 visit(node.name); |
| 980 visit(node.typeParameters); | 1102 visit(node.typeParameters); |
| 981 visit(node.parameters); | 1103 visit(node.parameters); |
| 982 }); | 1104 }); |
| 983 } | 1105 } |
| 984 | 1106 |
| 985 visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { | 1107 visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) { |
| 986 visitParameterMetadata(node.metadata, () { | 1108 visitParameterMetadata(node.metadata, () { |
| 1109 modifier(node.covariantKeyword); |
| 987 visit(node.returnType, after: space); | 1110 visit(node.returnType, after: space); |
| 988 | 1111 |
| 989 // Try to keep the function's parameters with its name. | 1112 // Try to keep the function's parameters with its name. |
| 990 builder.startSpan(); | 1113 builder.startSpan(); |
| 991 visit(node.identifier); | 1114 visit(node.identifier); |
| 992 visit(node.parameters); | 1115 visit(node.parameters); |
| 993 builder.endSpan(); | 1116 builder.endSpan(); |
| 994 }); | 1117 }); |
| 995 } | 1118 } |
| 996 | 1119 |
| 997 visitHideCombinator(HideCombinator node) { | 1120 visitHideCombinator(HideCombinator node) { |
| 998 _visitCombinator(node.keyword, node.hiddenNames); | 1121 _visitCombinator(node.keyword, node.hiddenNames); |
| 999 } | 1122 } |
| 1000 | 1123 |
| 1001 visitIfStatement(IfStatement node) { | 1124 visitIfStatement(IfStatement node) { |
| 1002 builder.nestExpression(); | 1125 builder.nestExpression(); |
| 1003 token(node.ifKeyword); | 1126 token(node.ifKeyword); |
| 1004 space(); | 1127 space(); |
| 1005 token(node.leftParenthesis); | 1128 token(node.leftParenthesis); |
| 1006 visit(node.condition); | 1129 visit(node.condition); |
| 1007 token(node.rightParenthesis); | 1130 token(node.rightParenthesis); |
| 1008 builder.unnest(now: false); | 1131 builder.unnest(); |
| 1009 | 1132 |
| 1010 visitClause(Statement clause) { | 1133 visitClause(Statement clause) { |
| 1011 if (clause is Block || clause is IfStatement) { | 1134 if (clause is Block || clause is IfStatement) { |
| 1012 space(); | 1135 space(); |
| 1013 visit(clause); | 1136 visit(clause); |
| 1014 } else { | 1137 } else { |
| 1015 // Allow splitting in an expression-bodied if even though it's against | 1138 // Allow splitting in a statement-bodied if even though it's against |
| 1016 // the style guide. Since we can't fix the code itself to follow the | 1139 // the style guide. Since we can't fix the code itself to follow the |
| 1017 // style guide, we should at least format it as well as we can. | 1140 // style guide, we should at least format it as well as we can. |
| 1018 builder.nestExpression(indent: 2, now: true); | 1141 builder.indent(); |
| 1019 builder.startRule(); | 1142 builder.startRule(); |
| 1020 | 1143 |
| 1021 // If there is an else clause, always split before both the then and | 1144 // If there is an else clause, always split before both the then and |
| 1022 // else statements. | 1145 // else statements. |
| 1023 if (node.elseStatement != null) { | 1146 if (node.elseStatement != null) { |
| 1024 builder.writeWhitespace(Whitespace.nestedNewline); | 1147 builder.writeWhitespace(Whitespace.newline); |
| 1025 } else { | 1148 } else { |
| 1026 split(); | 1149 builder.split(nest: false, space: true); |
| 1027 } | 1150 } |
| 1028 | 1151 |
| 1029 visit(clause); | 1152 visit(clause); |
| 1030 | 1153 |
| 1031 builder.endRule(); | 1154 builder.endRule(); |
| 1032 builder.unnest(); | 1155 builder.unindent(); |
| 1033 } | 1156 } |
| 1034 } | 1157 } |
| 1035 | 1158 |
| 1036 visitClause(node.thenStatement); | 1159 visitClause(node.thenStatement); |
| 1037 | 1160 |
| 1038 if (node.elseStatement != null) { | 1161 if (node.elseStatement != null) { |
| 1039 if (node.thenStatement is Block) { | 1162 if (node.thenStatement is Block) { |
| 1040 space(); | 1163 space(); |
| 1041 } else { | 1164 } else { |
| 1042 // Corner case where an else follows a single-statement then clause. | 1165 // Corner case where an else follows a single-statement then clause. |
| 1043 // This is against the style guide, but we still need to handle it. If | 1166 // This is against the style guide, but we still need to handle it. If |
| 1044 // it happens, put the else on the next line. | 1167 // it happens, put the else on the next line. |
| 1045 newline(); | 1168 newline(); |
| 1046 } | 1169 } |
| 1047 | 1170 |
| 1048 token(node.elseKeyword); | 1171 token(node.elseKeyword); |
| 1049 visitClause(node.elseStatement); | 1172 visitClause(node.elseStatement); |
| 1050 } | 1173 } |
| 1051 } | 1174 } |
| 1052 | 1175 |
| 1053 visitImplementsClause(ImplementsClause node) { | 1176 visitImplementsClause(ImplementsClause node) { |
| 1054 _visitCombinator(node.implementsKeyword, node.interfaces); | 1177 _visitCombinator(node.implementsKeyword, node.interfaces); |
| 1055 } | 1178 } |
| 1056 | 1179 |
| 1057 visitImportDirective(ImportDirective node) { | 1180 visitImportDirective(ImportDirective node) { |
| 1058 visitDeclarationMetadata(node.metadata); | 1181 visitMetadata(node.metadata); |
| 1059 | 1182 |
| 1060 _simpleStatement(node, () { | 1183 _simpleStatement(node, () { |
| 1061 token(node.keyword); | 1184 token(node.keyword); |
| 1062 space(); | 1185 space(); |
| 1063 visit(node.uri); | 1186 visit(node.uri); |
| 1064 | 1187 |
| 1188 _visitConfigurations(node.configurations); |
| 1189 |
| 1065 if (node.asKeyword != null) { | 1190 if (node.asKeyword != null) { |
| 1066 soloSplit(); | 1191 soloSplit(); |
| 1067 token(node.deferredKeyword, after: space); | 1192 token(node.deferredKeyword, after: space); |
| 1068 token(node.asKeyword); | 1193 token(node.asKeyword); |
| 1069 space(); | 1194 space(); |
| 1070 visit(node.prefix); | 1195 visit(node.prefix); |
| 1071 } | 1196 } |
| 1072 | 1197 |
| 1073 builder.startRule(new CombinatorRule()); | 1198 builder.startRule(new CombinatorRule()); |
| 1074 visitNodes(node.combinators); | 1199 visitNodes(node.combinators); |
| (...skipping 21 matching lines...) Expand all Loading... |
| 1096 /// call chains. | 1221 /// call chains. |
| 1097 void finishIndexExpression(IndexExpression node) { | 1222 void finishIndexExpression(IndexExpression node) { |
| 1098 if (node.target is IndexExpression) { | 1223 if (node.target is IndexExpression) { |
| 1099 // Edge case: On a chain of [] accesses, allow splitting between them. | 1224 // Edge case: On a chain of [] accesses, allow splitting between them. |
| 1100 // Produces nicer output in cases like: | 1225 // Produces nicer output in cases like: |
| 1101 // | 1226 // |
| 1102 // someJson['property']['property']['property']['property']... | 1227 // someJson['property']['property']['property']['property']... |
| 1103 soloZeroSplit(); | 1228 soloZeroSplit(); |
| 1104 } | 1229 } |
| 1105 | 1230 |
| 1106 builder.startSpan(); | 1231 builder.startSpan(Cost.index); |
| 1107 token(node.leftBracket); | 1232 token(node.leftBracket); |
| 1108 soloZeroSplit(); | 1233 soloZeroSplit(); |
| 1109 visit(node.index); | 1234 visit(node.index); |
| 1110 token(node.rightBracket); | 1235 token(node.rightBracket); |
| 1111 builder.endSpan(); | 1236 builder.endSpan(); |
| 1112 } | 1237 } |
| 1113 | 1238 |
| 1114 visitInstanceCreationExpression(InstanceCreationExpression node) { | 1239 visitInstanceCreationExpression(InstanceCreationExpression node) { |
| 1115 builder.startSpan(); | 1240 builder.startSpan(); |
| 1116 token(node.keyword); | 1241 token(node.keyword); |
| 1117 space(); | 1242 space(); |
| 1118 builder.startSpan(Cost.constructorName); | 1243 builder.startSpan(Cost.constructorName); |
| 1244 |
| 1245 // Start the expression nesting for the argument list here, in case this |
| 1246 // is a generic constructor with type arguments. If it is, we need the type |
| 1247 // arguments to be nested too so they get indented past the arguments. |
| 1248 builder.nestExpression(); |
| 1119 visit(node.constructorName); | 1249 visit(node.constructorName); |
| 1250 |
| 1120 builder.endSpan(); | 1251 builder.endSpan(); |
| 1121 visit(node.argumentList); | 1252 visitArgumentList(node.argumentList, nestExpression: false); |
| 1122 builder.endSpan(); | 1253 builder.endSpan(); |
| 1254 |
| 1255 builder.unnest(); |
| 1123 } | 1256 } |
| 1124 | 1257 |
| 1125 visitIntegerLiteral(IntegerLiteral node) { | 1258 visitIntegerLiteral(IntegerLiteral node) { |
| 1126 token(node.literal); | 1259 token(node.literal); |
| 1127 } | 1260 } |
| 1128 | 1261 |
| 1129 visitInterpolationExpression(InterpolationExpression node) { | 1262 visitInterpolationExpression(InterpolationExpression node) { |
| 1130 token(node.leftBracket); | 1263 token(node.leftBracket); |
| 1131 visit(node.expression); | 1264 visit(node.expression); |
| 1132 token(node.rightBracket); | 1265 token(node.rightBracket); |
| 1133 } | 1266 } |
| 1134 | 1267 |
| 1135 visitInterpolationString(InterpolationString node) { | 1268 visitInterpolationString(InterpolationString node) { |
| 1136 token(node.contents); | 1269 token(node.contents); |
| 1137 } | 1270 } |
| 1138 | 1271 |
| 1139 visitIsExpression(IsExpression node) { | 1272 visitIsExpression(IsExpression node) { |
| 1140 builder.startSpan(); | 1273 builder.startSpan(); |
| 1274 builder.nestExpression(); |
| 1141 visit(node.expression); | 1275 visit(node.expression); |
| 1142 soloSplit(); | 1276 soloSplit(); |
| 1143 token(node.isOperator); | 1277 token(node.isOperator); |
| 1144 token(node.notOperator); | 1278 token(node.notOperator); |
| 1145 space(); | 1279 space(); |
| 1146 visit(node.type); | 1280 visit(node.type); |
| 1281 builder.unnest(); |
| 1147 builder.endSpan(); | 1282 builder.endSpan(); |
| 1148 } | 1283 } |
| 1149 | 1284 |
| 1150 visitLabel(Label node) { | 1285 visitLabel(Label node) { |
| 1151 visit(node.label); | 1286 visit(node.label); |
| 1152 token(node.colon); | 1287 token(node.colon); |
| 1153 } | 1288 } |
| 1154 | 1289 |
| 1155 visitLabeledStatement(LabeledStatement node) { | 1290 visitLabeledStatement(LabeledStatement node) { |
| 1156 visitNodes(node.labels, between: space, after: space); | 1291 _visitLabels(node.labels); |
| 1157 visit(node.statement); | 1292 visit(node.statement); |
| 1158 } | 1293 } |
| 1159 | 1294 |
| 1160 visitLibraryDirective(LibraryDirective node) { | 1295 visitLibraryDirective(LibraryDirective node) { |
| 1161 visitDeclarationMetadata(node.metadata); | 1296 visitMetadata(node.metadata); |
| 1162 | 1297 |
| 1163 _simpleStatement(node, () { | 1298 _simpleStatement(node, () { |
| 1164 token(node.keyword); | 1299 token(node.keyword); |
| 1165 space(); | 1300 space(); |
| 1166 visit(node.name); | 1301 visit(node.name); |
| 1167 }); | 1302 }); |
| 1168 } | 1303 } |
| 1169 | 1304 |
| 1170 visitLibraryIdentifier(LibraryIdentifier node) { | 1305 visitLibraryIdentifier(LibraryIdentifier node) { |
| 1171 visit(node.components.first); | 1306 visit(node.components.first); |
| (...skipping 30 matching lines...) Expand all Loading... |
| 1202 visitMethodInvocation(MethodInvocation node) { | 1337 visitMethodInvocation(MethodInvocation node) { |
| 1203 // If there's no target, this is a "bare" function call like "foo(1, 2)", | 1338 // If there's no target, this is a "bare" function call like "foo(1, 2)", |
| 1204 // or a section in a cascade. Handle this case specially. | 1339 // or a section in a cascade. Handle this case specially. |
| 1205 if (node.target == null) { | 1340 if (node.target == null) { |
| 1206 // Try to keep the entire method invocation one line. | 1341 // Try to keep the entire method invocation one line. |
| 1207 builder.startSpan(); | 1342 builder.startSpan(); |
| 1208 builder.nestExpression(); | 1343 builder.nestExpression(); |
| 1209 | 1344 |
| 1210 // This will be non-null for cascade sections. | 1345 // This will be non-null for cascade sections. |
| 1211 token(node.operator); | 1346 token(node.operator); |
| 1212 token(node.methodName.token); | 1347 visit(node.methodName); |
| 1213 visit(node.argumentList); | 1348 |
| 1349 // TODO(rnystrom): Currently, there are no constraints between a generic |
| 1350 // method's type arguments and arguments. That can lead to some funny |
| 1351 // splitting like: |
| 1352 // |
| 1353 // method<VeryLongType, |
| 1354 // AnotherTypeArgument>(argument, |
| 1355 // argument, argument, argument); |
| 1356 // |
| 1357 // The indentation is fine, but splitting in the middle of each argument |
| 1358 // list looks kind of strange. If this ends up happening in real world |
| 1359 // code, consider putting a constraint between them. |
| 1360 visit(node.typeArguments); |
| 1361 visitArgumentList(node.argumentList, nestExpression: false); |
| 1214 | 1362 |
| 1215 builder.unnest(); | 1363 builder.unnest(); |
| 1216 builder.endSpan(); | 1364 builder.endSpan(); |
| 1217 return; | 1365 return; |
| 1218 } | 1366 } |
| 1219 | 1367 |
| 1220 new CallChainVisitor(this, node).visit(); | 1368 new CallChainVisitor(this, node).visit(); |
| 1221 } | 1369 } |
| 1222 | 1370 |
| 1223 visitNamedExpression(NamedExpression node) { | 1371 visitNamedExpression(NamedExpression node) { |
| 1224 builder.nestExpression(); | 1372 visitNamedArgument(node); |
| 1225 builder.startSpan(); | |
| 1226 visit(node.name); | |
| 1227 | |
| 1228 // Don't allow a split between a name and a collection. Instead, we want | |
| 1229 // the collection itself to split, or to split before the argument. | |
| 1230 if (node.expression is ListLiteral || node.expression is MapLiteral) { | |
| 1231 space(); | |
| 1232 } else { | |
| 1233 soloSplit(); | |
| 1234 } | |
| 1235 | |
| 1236 visit(node.expression); | |
| 1237 builder.endSpan(); | |
| 1238 builder.unnest(); | |
| 1239 } | 1373 } |
| 1240 | 1374 |
| 1241 visitNativeClause(NativeClause node) { | 1375 visitNativeClause(NativeClause node) { |
| 1242 token(node.nativeKeyword); | 1376 token(node.nativeKeyword); |
| 1243 space(); | 1377 space(); |
| 1244 visit(node.name); | 1378 visit(node.name); |
| 1245 } | 1379 } |
| 1246 | 1380 |
| 1247 visitNativeFunctionBody(NativeFunctionBody node) { | 1381 visitNativeFunctionBody(NativeFunctionBody node) { |
| 1248 _simpleStatement(node, () { | 1382 _simpleStatement(node, () { |
| (...skipping 12 matching lines...) Expand all Loading... |
| 1261 | 1395 |
| 1262 visitParenthesizedExpression(ParenthesizedExpression node) { | 1396 visitParenthesizedExpression(ParenthesizedExpression node) { |
| 1263 builder.nestExpression(); | 1397 builder.nestExpression(); |
| 1264 token(node.leftParenthesis); | 1398 token(node.leftParenthesis); |
| 1265 visit(node.expression); | 1399 visit(node.expression); |
| 1266 builder.unnest(); | 1400 builder.unnest(); |
| 1267 token(node.rightParenthesis); | 1401 token(node.rightParenthesis); |
| 1268 } | 1402 } |
| 1269 | 1403 |
| 1270 visitPartDirective(PartDirective node) { | 1404 visitPartDirective(PartDirective node) { |
| 1271 visitDeclarationMetadata(node.metadata); | 1405 visitMetadata(node.metadata); |
| 1272 | 1406 |
| 1273 _simpleStatement(node, () { | 1407 _simpleStatement(node, () { |
| 1274 token(node.keyword); | 1408 token(node.keyword); |
| 1275 space(); | 1409 space(); |
| 1276 visit(node.uri); | 1410 visit(node.uri); |
| 1277 }); | 1411 }); |
| 1278 } | 1412 } |
| 1279 | 1413 |
| 1280 visitPartOfDirective(PartOfDirective node) { | 1414 visitPartOfDirective(PartOfDirective node) { |
| 1281 visitDeclarationMetadata(node.metadata); | 1415 visitMetadata(node.metadata); |
| 1282 | 1416 |
| 1283 _simpleStatement(node, () { | 1417 _simpleStatement(node, () { |
| 1284 token(node.keyword); | 1418 token(node.keyword); |
| 1285 space(); | 1419 space(); |
| 1286 token(node.ofKeyword); | 1420 token(node.ofKeyword); |
| 1287 space(); | 1421 space(); |
| 1288 visit(node.libraryName); | 1422 visit(node.libraryName); |
| 1289 }); | 1423 }); |
| 1290 } | 1424 } |
| 1291 | 1425 |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1342 visit(node.expression, before: space); | 1476 visit(node.expression, before: space); |
| 1343 }); | 1477 }); |
| 1344 } | 1478 } |
| 1345 | 1479 |
| 1346 visitScriptTag(ScriptTag node) { | 1480 visitScriptTag(ScriptTag node) { |
| 1347 // The lexeme includes the trailing newline. Strip it off since the | 1481 // The lexeme includes the trailing newline. Strip it off since the |
| 1348 // formatter ensures it gets a newline after it. Since the script tag must | 1482 // formatter ensures it gets a newline after it. Since the script tag must |
| 1349 // come at the top of the file, we don't have to worry about preceding | 1483 // come at the top of the file, we don't have to worry about preceding |
| 1350 // comments or whitespace. | 1484 // comments or whitespace. |
| 1351 _writeText(node.scriptTag.lexeme.trim(), node.offset); | 1485 _writeText(node.scriptTag.lexeme.trim(), node.offset); |
| 1352 | 1486 newline(); |
| 1353 oneOrTwoNewlines(); | |
| 1354 } | 1487 } |
| 1355 | 1488 |
| 1356 visitShowCombinator(ShowCombinator node) { | 1489 visitShowCombinator(ShowCombinator node) { |
| 1357 _visitCombinator(node.keyword, node.shownNames); | 1490 _visitCombinator(node.keyword, node.shownNames); |
| 1358 } | 1491 } |
| 1359 | 1492 |
| 1360 visitSimpleFormalParameter(SimpleFormalParameter node) { | 1493 visitSimpleFormalParameter(SimpleFormalParameter node) { |
| 1361 visitParameterMetadata(node.metadata, () { | 1494 visitParameterMetadata(node.metadata, () { |
| 1495 modifier(node.covariantKeyword); |
| 1362 modifier(node.keyword); | 1496 modifier(node.keyword); |
| 1363 visit(node.type, after: space); | 1497 visit(node.type, after: space); |
| 1364 visit(node.identifier); | 1498 visit(node.identifier); |
| 1365 }); | 1499 }); |
| 1366 } | 1500 } |
| 1367 | 1501 |
| 1368 visitSimpleIdentifier(SimpleIdentifier node) { | 1502 visitSimpleIdentifier(SimpleIdentifier node) { |
| 1369 token(node.token); | 1503 token(node.token); |
| 1370 } | 1504 } |
| 1371 | 1505 |
| (...skipping 27 matching lines...) Expand all Loading... |
| 1399 visit(node.argumentList); | 1533 visit(node.argumentList); |
| 1400 | 1534 |
| 1401 builder.endSpan(); | 1535 builder.endSpan(); |
| 1402 } | 1536 } |
| 1403 | 1537 |
| 1404 visitSuperExpression(SuperExpression node) { | 1538 visitSuperExpression(SuperExpression node) { |
| 1405 token(node.superKeyword); | 1539 token(node.superKeyword); |
| 1406 } | 1540 } |
| 1407 | 1541 |
| 1408 visitSwitchCase(SwitchCase node) { | 1542 visitSwitchCase(SwitchCase node) { |
| 1409 visitNodes(node.labels, between: space, after: space); | 1543 _visitLabels(node.labels); |
| 1410 token(node.keyword); | 1544 token(node.keyword); |
| 1411 space(); | 1545 space(); |
| 1412 visit(node.expression); | 1546 visit(node.expression); |
| 1413 token(node.colon); | 1547 token(node.colon); |
| 1414 | 1548 |
| 1415 builder.indent(); | 1549 builder.indent(); |
| 1416 // TODO(rnystrom): Allow inline cases? | 1550 // TODO(rnystrom): Allow inline cases? |
| 1417 newline(); | 1551 newline(); |
| 1418 | 1552 |
| 1419 visitNodes(node.statements, between: oneOrTwoNewlines); | 1553 visitNodes(node.statements, between: oneOrTwoNewlines); |
| 1420 builder.unindent(); | 1554 builder.unindent(); |
| 1421 } | 1555 } |
| 1422 | 1556 |
| 1423 visitSwitchDefault(SwitchDefault node) { | 1557 visitSwitchDefault(SwitchDefault node) { |
| 1424 visitNodes(node.labels, between: space, after: space); | 1558 _visitLabels(node.labels); |
| 1425 token(node.keyword); | 1559 token(node.keyword); |
| 1426 token(node.colon); | 1560 token(node.colon); |
| 1427 | 1561 |
| 1428 builder.indent(); | 1562 builder.indent(); |
| 1429 // TODO(rnystrom): Allow inline cases? | 1563 // TODO(rnystrom): Allow inline cases? |
| 1430 newline(); | 1564 newline(); |
| 1431 | 1565 |
| 1432 visitNodes(node.statements, between: oneOrTwoNewlines); | 1566 visitNodes(node.statements, between: oneOrTwoNewlines); |
| 1433 builder.unindent(); | 1567 builder.unindent(); |
| 1434 } | 1568 } |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1470 token(node.thisKeyword); | 1604 token(node.thisKeyword); |
| 1471 } | 1605 } |
| 1472 | 1606 |
| 1473 visitThrowExpression(ThrowExpression node) { | 1607 visitThrowExpression(ThrowExpression node) { |
| 1474 token(node.throwKeyword); | 1608 token(node.throwKeyword); |
| 1475 space(); | 1609 space(); |
| 1476 visit(node.expression); | 1610 visit(node.expression); |
| 1477 } | 1611 } |
| 1478 | 1612 |
| 1479 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { | 1613 visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { |
| 1480 visitDeclarationMetadata(node.metadata); | 1614 visitMetadata(node.metadata); |
| 1481 | 1615 |
| 1482 _simpleStatement(node, () { | 1616 _simpleStatement(node, () { |
| 1483 visit(node.variables); | 1617 visit(node.variables); |
| 1484 }); | 1618 }); |
| 1485 } | 1619 } |
| 1486 | 1620 |
| 1487 visitTryStatement(TryStatement node) { | 1621 visitTryStatement(TryStatement node) { |
| 1488 token(node.tryKeyword); | 1622 token(node.tryKeyword); |
| 1489 space(); | 1623 space(); |
| 1490 visit(node.body); | 1624 visit(node.body); |
| (...skipping 24 matching lines...) Expand all Loading... |
| 1515 | 1649 |
| 1516 _visitGenericList(node.leftBracket, node.rightBracket, node.typeParameters); | 1650 _visitGenericList(node.leftBracket, node.rightBracket, node.typeParameters); |
| 1517 | 1651 |
| 1518 _metadataRules.removeLast(); | 1652 _metadataRules.removeLast(); |
| 1519 } | 1653 } |
| 1520 | 1654 |
| 1521 visitVariableDeclaration(VariableDeclaration node) { | 1655 visitVariableDeclaration(VariableDeclaration node) { |
| 1522 visit(node.name); | 1656 visit(node.name); |
| 1523 if (node.initializer == null) return; | 1657 if (node.initializer == null) return; |
| 1524 | 1658 |
| 1525 _visitAssignment(node.equals, node.initializer); | 1659 // If there are multiple variables being declared, we want to nest the |
| 1660 // initializers farther so they don't line up with the variables. Bad: |
| 1661 // |
| 1662 // var a = |
| 1663 // aValue, |
| 1664 // b = |
| 1665 // bValue; |
| 1666 // |
| 1667 // Good: |
| 1668 // |
| 1669 // var a = |
| 1670 // aValue, |
| 1671 // b = |
| 1672 // bValue; |
| 1673 var hasMultipleVariables = |
| 1674 (node.parent as VariableDeclarationList).variables.length > 1; |
| 1675 |
| 1676 _visitAssignment(node.equals, node.initializer, nest: hasMultipleVariables); |
| 1526 } | 1677 } |
| 1527 | 1678 |
| 1528 visitVariableDeclarationList(VariableDeclarationList node) { | 1679 visitVariableDeclarationList(VariableDeclarationList node) { |
| 1529 visitDeclarationMetadata(node.metadata); | 1680 visitMetadata(node.metadata); |
| 1530 | 1681 |
| 1531 // Allow but try to avoid splitting between the type and name. | 1682 // Allow but try to avoid splitting between the type and name. |
| 1532 builder.startSpan(); | 1683 builder.startSpan(); |
| 1533 | 1684 |
| 1534 modifier(node.keyword); | 1685 modifier(node.keyword); |
| 1535 visit(node.type, after: soloSplit); | 1686 visit(node.type, after: soloSplit); |
| 1536 | 1687 |
| 1537 builder.endSpan(); | 1688 builder.endSpan(); |
| 1538 | 1689 |
| 1539 // Use a single rule for all of the variables. If there are multiple | 1690 // Use a single rule for all of the variables. If there are multiple |
| (...skipping 12 matching lines...) Expand all Loading... |
| 1552 } | 1703 } |
| 1553 | 1704 |
| 1554 visitWhileStatement(WhileStatement node) { | 1705 visitWhileStatement(WhileStatement node) { |
| 1555 builder.nestExpression(); | 1706 builder.nestExpression(); |
| 1556 token(node.whileKeyword); | 1707 token(node.whileKeyword); |
| 1557 space(); | 1708 space(); |
| 1558 token(node.leftParenthesis); | 1709 token(node.leftParenthesis); |
| 1559 soloZeroSplit(); | 1710 soloZeroSplit(); |
| 1560 visit(node.condition); | 1711 visit(node.condition); |
| 1561 token(node.rightParenthesis); | 1712 token(node.rightParenthesis); |
| 1562 builder.unnest(now: false); | 1713 builder.unnest(); |
| 1563 | 1714 |
| 1564 _visitLoopBody(node.body); | 1715 _visitLoopBody(node.body); |
| 1565 } | 1716 } |
| 1566 | 1717 |
| 1567 visitWithClause(WithClause node) { | 1718 visitWithClause(WithClause node) { |
| 1568 _visitCombinator(node.withKeyword, node.mixinTypes); | 1719 _visitCombinator(node.withKeyword, node.mixinTypes); |
| 1569 } | 1720 } |
| 1570 | 1721 |
| 1571 visitYieldStatement(YieldStatement node) { | 1722 visitYieldStatement(YieldStatement node) { |
| 1572 _simpleStatement(node, () { | 1723 _simpleStatement(node, () { |
| 1573 token(node.yieldKeyword); | 1724 token(node.yieldKeyword); |
| 1574 token(node.star); | 1725 token(node.star); |
| 1575 space(); | 1726 space(); |
| 1576 visit(node.expression); | 1727 visit(node.expression); |
| 1577 }); | 1728 }); |
| 1578 } | 1729 } |
| 1579 | 1730 |
| 1580 /// Visit a [node], and if not null, optionally preceded or followed by the | 1731 /// Visit a [node], and if not null, optionally preceded or followed by the |
| 1581 /// specified functions. | 1732 /// specified functions. |
| 1582 void visit(AstNode node, {void before(), void after()}) { | 1733 void visit(AstNode node, {void before(), void after()}) { |
| 1583 if (node == null) return; | 1734 if (node == null) return; |
| 1584 | 1735 |
| 1585 if (before != null) before(); | 1736 if (before != null) before(); |
| 1586 | 1737 |
| 1587 node.accept(this); | 1738 node.accept(this); |
| 1588 | 1739 |
| 1589 if (after != null) after(); | 1740 if (after != null) after(); |
| 1590 } | 1741 } |
| 1591 | 1742 |
| 1592 /// Visit metadata annotations on directives and declarations. | 1743 /// Visit metadata annotations on directives, declarations, and members. |
| 1593 /// | 1744 /// |
| 1594 /// These always force the annotations to be on the previous line. | 1745 /// These always force the annotations to be on the previous line. |
| 1595 void visitDeclarationMetadata(NodeList<Annotation> metadata) { | 1746 void visitMetadata(NodeList<Annotation> metadata) { |
| 1596 // If there are multiple annotations, they are always on their own lines, | 1747 visitNodes(metadata, between: newline, after: newline); |
| 1597 // even the last. | |
| 1598 if (metadata.length > 1) { | |
| 1599 visitNodes(metadata, between: newline, after: newline); | |
| 1600 } else { | |
| 1601 visitNodes(metadata, between: space, after: newline); | |
| 1602 } | |
| 1603 } | |
| 1604 | |
| 1605 /// Visit metadata annotations on members. | |
| 1606 /// | |
| 1607 /// These may be on the same line as the member, or on the previous. | |
| 1608 void visitMemberMetadata(NodeList<Annotation> metadata) { | |
| 1609 // If there are multiple annotations, they are always on their own lines, | |
| 1610 // even the last. | |
| 1611 if (metadata.length > 1) { | |
| 1612 visitNodes(metadata, between: newline, after: newline); | |
| 1613 } else { | |
| 1614 visitNodes(metadata, between: space, after: spaceOrNewline); | |
| 1615 } | |
| 1616 } | 1748 } |
| 1617 | 1749 |
| 1618 /// Visits metadata annotations on parameters and type parameters. | 1750 /// Visits metadata annotations on parameters and type parameters. |
| 1619 /// | 1751 /// |
| 1620 /// These are always on the same line as the parameter. | 1752 /// Unlike other annotations, these are allowed to stay on the same line as |
| 1753 /// the parameter. |
| 1621 void visitParameterMetadata( | 1754 void visitParameterMetadata( |
| 1622 NodeList<Annotation> metadata, void visitParameter()) { | 1755 NodeList<Annotation> metadata, void visitParameter()) { |
| 1623 if (metadata == null || metadata.isEmpty) { | 1756 if (metadata == null || metadata.isEmpty) { |
| 1624 visitParameter(); | 1757 visitParameter(); |
| 1625 return; | 1758 return; |
| 1626 } | 1759 } |
| 1627 | 1760 |
| 1628 // Split before all of the annotations or none. | 1761 // Split before all of the annotations or none. |
| 1629 builder.startLazyRule(_metadataRules.last); | 1762 builder.startLazyRule(_metadataRules.last); |
| 1630 | 1763 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 1641 }); | 1774 }); |
| 1642 visitParameter(); | 1775 visitParameter(); |
| 1643 | 1776 |
| 1644 builder.unnest(); | 1777 builder.unnest(); |
| 1645 | 1778 |
| 1646 // Wrap the rule around the parameter too. If it splits, we want to force | 1779 // Wrap the rule around the parameter too. If it splits, we want to force |
| 1647 // the annotations to split as well. | 1780 // the annotations to split as well. |
| 1648 builder.endRule(); | 1781 builder.endRule(); |
| 1649 } | 1782 } |
| 1650 | 1783 |
| 1784 /// Visits [node], which may be in an argument list controlled by [rule]. |
| 1785 /// |
| 1786 /// This is called directly by [ArgumentListVisitor] so that it can pass in |
| 1787 /// the surrounding named argument rule. That way, this can ensure that a |
| 1788 /// split between the name and argument forces the argument list to split |
| 1789 /// too. |
| 1790 void visitNamedArgument(NamedExpression node, [NamedRule rule]) { |
| 1791 builder.nestExpression(); |
| 1792 builder.startSpan(); |
| 1793 visit(node.name); |
| 1794 |
| 1795 // Don't allow a split between a name and a collection. Instead, we want |
| 1796 // the collection itself to split, or to split before the argument. |
| 1797 if (node.expression is ListLiteral || node.expression is MapLiteral) { |
| 1798 space(); |
| 1799 } else { |
| 1800 var split = soloSplit(); |
| 1801 if (rule != null) split.imply(rule); |
| 1802 } |
| 1803 |
| 1804 visit(node.expression); |
| 1805 builder.endSpan(); |
| 1806 builder.unnest(); |
| 1807 } |
| 1808 |
| 1651 /// Visits the `=` and the following expression in any place where an `=` | 1809 /// Visits the `=` and the following expression in any place where an `=` |
| 1652 /// appears: | 1810 /// appears: |
| 1653 /// | 1811 /// |
| 1654 /// * Assignment | 1812 /// * Assignment |
| 1655 /// * Variable declaration | 1813 /// * Variable declaration |
| 1656 /// * Constructor initialization | 1814 /// * Constructor initialization |
| 1657 void _visitAssignment(Token equalsOperator, Expression rightHandSide) { | 1815 /// |
| 1816 /// If [nest] is true, an extra level of expression nesting is added after |
| 1817 /// the "=". |
| 1818 void _visitAssignment(Token equalsOperator, Expression rightHandSide, |
| 1819 {bool nest: false}) { |
| 1658 space(); | 1820 space(); |
| 1659 token(equalsOperator); | 1821 token(equalsOperator); |
| 1822 |
| 1823 if (nest) builder.nestExpression(now: true); |
| 1824 |
| 1660 soloSplit(_assignmentCost(rightHandSide)); | 1825 soloSplit(_assignmentCost(rightHandSide)); |
| 1661 builder.startSpan(); | 1826 builder.startSpan(); |
| 1662 visit(rightHandSide); | 1827 visit(rightHandSide); |
| 1663 builder.endSpan(); | 1828 builder.endSpan(); |
| 1829 |
| 1830 if (nest) builder.unnest(); |
| 1664 } | 1831 } |
| 1665 | 1832 |
| 1666 /// Visits a type parameter or type argument list. | 1833 /// Visits a type parameter or type argument list. |
| 1667 void _visitGenericList( | 1834 void _visitGenericList( |
| 1668 Token leftBracket, Token rightBracket, List<AstNode> nodes) { | 1835 Token leftBracket, Token rightBracket, List<AstNode> nodes) { |
| 1669 var rule = new TypeArgumentRule(); | 1836 var rule = new TypeArgumentRule(); |
| 1670 builder.startRule(rule); | 1837 builder.startRule(rule); |
| 1671 builder.startSpan(); | 1838 builder.startSpan(); |
| 1672 builder.nestExpression(); | 1839 builder.nestExpression(); |
| 1673 | 1840 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 1684 } | 1851 } |
| 1685 } | 1852 } |
| 1686 | 1853 |
| 1687 token(rightBracket); | 1854 token(rightBracket); |
| 1688 | 1855 |
| 1689 builder.unnest(); | 1856 builder.unnest(); |
| 1690 builder.endSpan(); | 1857 builder.endSpan(); |
| 1691 builder.endRule(); | 1858 builder.endRule(); |
| 1692 } | 1859 } |
| 1693 | 1860 |
| 1861 /// Visits a sequence of labels before a statement or switch case. |
| 1862 void _visitLabels(NodeList<Label> labels) { |
| 1863 visitNodes(labels, between: newline, after: newline); |
| 1864 } |
| 1865 |
| 1694 /// Visits a top-level function or method declaration. | 1866 /// Visits a top-level function or method declaration. |
| 1695 /// | 1867 /// |
| 1696 /// The two AST node types are very similar but, alas, share no common | 1868 /// The two AST node types are very similar but, alas, share no common |
| 1697 /// interface type in analyzer, hence the dynamic typing. | 1869 /// interface type in analyzer, hence the dynamic typing. |
| 1698 void _visitMemberDeclaration( | 1870 void _visitMemberDeclaration( |
| 1699 /* FunctionDeclaration|MethodDeclaration */ node, | 1871 /* FunctionDeclaration|MethodDeclaration */ node, |
| 1700 /* FunctionExpression|MethodDeclaration */ function) { | 1872 /* FunctionExpression|MethodDeclaration */ function) { |
| 1701 visitMemberMetadata(node.metadata); | 1873 visitMetadata(node.metadata as NodeList<Annotation>); |
| 1702 | 1874 |
| 1703 // Nest the signature in case we have to split between the return type and | 1875 // Nest the signature in case we have to split between the return type and |
| 1704 // name. | 1876 // name. |
| 1705 builder.nestExpression(); | 1877 builder.nestExpression(); |
| 1706 builder.startSpan(); | 1878 builder.startSpan(); |
| 1707 modifier(node.externalKeyword); | 1879 modifier(node.externalKeyword); |
| 1708 if (node is MethodDeclaration) modifier(node.modifierKeyword); | 1880 if (node is MethodDeclaration) modifier(node.modifierKeyword); |
| 1709 visit(node.returnType, after: soloSplit); | 1881 visit(node.returnType, after: soloSplit); |
| 1710 modifier(node.propertyKeyword); | 1882 modifier(node.propertyKeyword); |
| 1711 if (node is MethodDeclaration) modifier(node.operatorKeyword); | 1883 if (node is MethodDeclaration) modifier(node.operatorKeyword); |
| 1712 visit(node.name); | 1884 visit(node.name); |
| 1713 builder.endSpan(); | 1885 builder.endSpan(); |
| 1714 | 1886 |
| 1715 // If the body is a block, we need to exit any nesting first. If it's an | 1887 TypeParameterList typeParameters; |
| 1716 // expression, we want to wrap the nesting around that so that the body | 1888 if (node is FunctionDeclaration) { |
| 1717 // gets nested farther. | 1889 typeParameters = node.functionExpression.typeParameters; |
| 1718 if (function.body is! ExpressionFunctionBody) builder.unnest(); | 1890 } else { |
| 1891 typeParameters = (node as MethodDeclaration).typeParameters; |
| 1892 } |
| 1719 | 1893 |
| 1720 _visitBody(function.parameters, function.body); | 1894 _visitBody(typeParameters, function.parameters, function.body, () { |
| 1895 // If the body is a block, we need to exit nesting before we hit the body |
| 1896 // indentation, but we do want to wrap it around the parameters. |
| 1897 if (function.body is! ExpressionFunctionBody) builder.unnest(); |
| 1898 }); |
| 1721 | 1899 |
| 1900 // If it's an expression, we want to wrap the nesting around that so that |
| 1901 // the body gets nested farther. |
| 1722 if (function.body is ExpressionFunctionBody) builder.unnest(); | 1902 if (function.body is ExpressionFunctionBody) builder.unnest(); |
| 1723 } | 1903 } |
| 1724 | 1904 |
| 1725 /// Visit the given function [parameters] followed by its [body], printing a | 1905 /// Visit the given function [parameters] followed by its [body], printing a |
| 1726 /// space before it if it's not empty. | 1906 /// space before it if it's not empty. |
| 1727 /// | 1907 /// |
| 1728 /// If [afterParameters] is provided, it is invoked between the parameters | 1908 /// If [beforeBody] is provided, it is invoked before the body is visited. |
| 1729 /// and body. (It's used for constructor initialization lists.) | 1909 void _visitBody(TypeParameterList typeParameters, |
| 1730 void _visitBody(FormalParameterList parameters, FunctionBody body, | 1910 FormalParameterList parameters, FunctionBody body, |
| 1731 [afterParameters()]) { | 1911 [beforeBody()]) { |
| 1732 // If the body is "=>", add an extra level of indentation around the | 1912 // If the body is "=>", add an extra level of indentation around the |
| 1733 // parameters and a rule that spans the parameters and the "=>". This | 1913 // parameters and a rule that spans the parameters and the "=>". This |
| 1734 // ensures that if the parameters wrap, they wrap more deeply than the "=>" | 1914 // ensures that if the parameters wrap, they wrap more deeply than the "=>" |
| 1735 // does, as in: | 1915 // does, as in: |
| 1736 // | 1916 // |
| 1737 // someFunction(parameter, | 1917 // someFunction(parameter, |
| 1738 // parameter, parameter) => | 1918 // parameter, parameter) => |
| 1739 // "the body"; | 1919 // "the body"; |
| 1740 // | 1920 // |
| 1741 // Also, it ensures that if the parameters wrap, we split at the "=>" too | 1921 // Also, it ensures that if the parameters wrap, we split at the "=>" too |
| (...skipping 10 matching lines...) Expand all Loading... |
| 1752 // parameter) => | 1932 // parameter) => |
| 1753 // function( | 1933 // function( |
| 1754 // argument); | 1934 // argument); |
| 1755 if (body is ExpressionFunctionBody) { | 1935 if (body is ExpressionFunctionBody) { |
| 1756 builder.nestExpression(); | 1936 builder.nestExpression(); |
| 1757 | 1937 |
| 1758 // This rule is ended by visitExpressionFunctionBody(). | 1938 // This rule is ended by visitExpressionFunctionBody(). |
| 1759 builder.startLazyRule(new Rule(Cost.arrow)); | 1939 builder.startLazyRule(new Rule(Cost.arrow)); |
| 1760 } | 1940 } |
| 1761 | 1941 |
| 1942 // Start the nesting for the parameters here, so they wrap around the |
| 1943 // type parameters too, if any. |
| 1944 builder.nestExpression(); |
| 1945 |
| 1946 visit(typeParameters); |
| 1762 if (parameters != null) { | 1947 if (parameters != null) { |
| 1763 builder.nestExpression(); | 1948 visitFormalParameterList(parameters, nestExpression: false); |
| 1764 visit(parameters); | |
| 1765 builder.unnest(); | |
| 1766 | |
| 1767 if (afterParameters != null) afterParameters(); | |
| 1768 } | 1949 } |
| 1769 | 1950 |
| 1951 builder.unnest(); |
| 1952 |
| 1953 if (beforeBody != null) beforeBody(); |
| 1770 visit(body); | 1954 visit(body); |
| 1771 | 1955 |
| 1772 if (body is ExpressionFunctionBody) builder.unnest(); | 1956 if (body is ExpressionFunctionBody) builder.unnest(); |
| 1773 } | 1957 } |
| 1774 | 1958 |
| 1775 /// Visits the body statement of a `for` or `for in` loop. | 1959 /// Visits the body statement of a `for`, `for in`, or `while` loop. |
| 1776 void _visitLoopBody(Statement body) { | 1960 void _visitLoopBody(Statement body) { |
| 1777 if (body is EmptyStatement) { | 1961 if (body is EmptyStatement) { |
| 1778 // No space before the ";". | 1962 // No space before the ";". |
| 1779 visit(body); | 1963 visit(body); |
| 1780 } else if (body is Block) { | 1964 } else if (body is Block) { |
| 1781 space(); | 1965 space(); |
| 1782 visit(body); | 1966 visit(body); |
| 1783 } else { | 1967 } else { |
| 1784 // Allow splitting in an expression-bodied for even though it's against | 1968 // Allow splitting in a statement-bodied loop even though it's against |
| 1785 // the style guide. Since we can't fix the code itself to follow the | 1969 // the style guide. Since we can't fix the code itself to follow the |
| 1786 // style guide, we should at least format it as well as we can. | 1970 // style guide, we should at least format it as well as we can. |
| 1787 builder.nestExpression(indent: 2, now: true); | 1971 builder.indent(); |
| 1788 builder.startRule(); | 1972 builder.startRule(); |
| 1789 | 1973 |
| 1790 split(); | 1974 builder.split(nest: false, space: true); |
| 1791 visit(body); | 1975 visit(body); |
| 1792 | 1976 |
| 1793 builder.endRule(); | 1977 builder.endRule(); |
| 1794 builder.unnest(); | 1978 builder.unindent(); |
| 1795 } | 1979 } |
| 1796 } | 1980 } |
| 1797 | 1981 |
| 1798 /// Visit a list of [nodes] if not null, optionally separated and/or preceded | 1982 /// Visit a list of [nodes] if not null, optionally separated and/or preceded |
| 1799 /// and followed by the given functions. | 1983 /// and followed by the given functions. |
| 1800 void visitNodes(Iterable<AstNode> nodes, {before(), between(), after()}) { | 1984 void visitNodes(Iterable<AstNode> nodes, {before(), between(), after()}) { |
| 1801 if (nodes == null || nodes.isEmpty) return; | 1985 if (nodes == null || nodes.isEmpty) return; |
| 1802 | 1986 |
| 1803 if (before != null) before(); | 1987 if (before != null) before(); |
| 1804 | 1988 |
| (...skipping 19 matching lines...) Expand all Loading... |
| 1824 | 2008 |
| 1825 visit(node); | 2009 visit(node); |
| 1826 | 2010 |
| 1827 // The comma after the node. | 2011 // The comma after the node. |
| 1828 if (node.endToken.next.lexeme == ",") token(node.endToken.next); | 2012 if (node.endToken.next.lexeme == ",") token(node.endToken.next); |
| 1829 } | 2013 } |
| 1830 } | 2014 } |
| 1831 | 2015 |
| 1832 /// Visits the collection literal [node] whose body starts with [leftBracket], | 2016 /// Visits the collection literal [node] whose body starts with [leftBracket], |
| 1833 /// ends with [rightBracket] and contains [elements]. | 2017 /// ends with [rightBracket] and contains [elements]. |
| 2018 /// |
| 2019 /// This is also used for argument lists with a trailing comma which are |
| 2020 /// considered "collection-like". In that case, [node] is `null`. |
| 1834 void _visitCollectionLiteral(TypedLiteral node, Token leftBracket, | 2021 void _visitCollectionLiteral(TypedLiteral node, Token leftBracket, |
| 1835 Iterable<AstNode> elements, Token rightBracket, | 2022 Iterable<AstNode> elements, Token rightBracket, |
| 1836 [int cost]) { | 2023 [int cost]) { |
| 1837 modifier(node.constKeyword); | 2024 if (node != null) { |
| 1838 visit(node.typeArguments); | 2025 modifier(node.constKeyword); |
| 2026 visit(node.typeArguments); |
| 2027 } |
| 1839 | 2028 |
| 1840 // Don't allow splitting in an empty collection. | 2029 // Don't allow splitting in an empty collection. |
| 1841 if (elements.isEmpty && rightBracket.precedingComments == null) { | 2030 if (elements.isEmpty && rightBracket.precedingComments == null) { |
| 1842 token(leftBracket); | 2031 token(leftBracket); |
| 1843 token(rightBracket); | 2032 token(rightBracket); |
| 1844 return; | 2033 return; |
| 1845 } | 2034 } |
| 1846 | 2035 |
| 1847 // Force all of the surrounding collections to split. | 2036 // Force all of the surrounding collections to split. |
| 1848 for (var i = 0; i < _collectionSplits.length; i++) { | 2037 for (var i = 0; i < _collectionSplits.length; i++) { |
| 1849 _collectionSplits[i] = true; | 2038 _collectionSplits[i] = true; |
| 1850 } | 2039 } |
| 1851 | 2040 |
| 1852 // Add this collection to the stack. | 2041 // Add this collection to the stack. |
| 1853 _collectionSplits.add(false); | 2042 _collectionSplits.add(false); |
| 1854 | 2043 |
| 1855 _startLiteralBody(leftBracket); | 2044 _startLiteralBody(leftBracket); |
| 1856 | 2045 |
| 1857 // Always use a hard rule to split the elements. The parent chunk of | |
| 1858 // the collection will handle the unsplit case, so this only comes | |
| 1859 // into play when the collection is split. | |
| 1860 var rule = new Rule.hard(); | |
| 1861 builder.startRule(rule); | |
| 1862 | |
| 1863 // If a collection contains a line comment, we assume it's a big complex | 2046 // If a collection contains a line comment, we assume it's a big complex |
| 1864 // blob of data with some documented structure. In that case, the user | 2047 // blob of data with some documented structure. In that case, the user |
| 1865 // probably broke the elements into lines deliberately, so preserve those. | 2048 // probably broke the elements into lines deliberately, so preserve those. |
| 1866 var preserveNewlines = _containsLineComments(elements, rightBracket); | 2049 var preserveNewlines = _containsLineComments(elements, rightBracket); |
| 1867 | 2050 |
| 2051 var rule; |
| 2052 var lineRule; |
| 2053 if (preserveNewlines) { |
| 2054 // Newlines are significant, so we'll explicitly write those. Elements |
| 2055 // on the same line all share an argument-list-like rule that allows |
| 2056 // splitting between zero, one, or all of them. This is faster in long |
| 2057 // lists than using individual splits after each element. |
| 2058 lineRule = new TypeArgumentRule(); |
| 2059 builder.startLazyRule(lineRule); |
| 2060 } else { |
| 2061 // Newlines aren't significant, so use a hard rule to split the elements. |
| 2062 // The parent chunk of the collection will handle the unsplit case, so |
| 2063 // this only comes into play when the collection is split. |
| 2064 rule = new Rule.hard(); |
| 2065 builder.startRule(rule); |
| 2066 } |
| 2067 |
| 1868 for (var element in elements) { | 2068 for (var element in elements) { |
| 1869 if (element != elements.first) { | 2069 if (element != elements.first) { |
| 1870 if (preserveNewlines) { | 2070 if (preserveNewlines) { |
| 2071 // See if the next element is on the next line. |
| 1871 if (_endLine(element.beginToken.previous) != | 2072 if (_endLine(element.beginToken.previous) != |
| 1872 _startLine(element.beginToken)) { | 2073 _startLine(element.beginToken)) { |
| 1873 oneOrTwoNewlines(); | 2074 oneOrTwoNewlines(); |
| 2075 |
| 2076 // Start a new rule for the new line. |
| 2077 builder.endRule(); |
| 2078 lineRule = new TypeArgumentRule(); |
| 2079 builder.startLazyRule(lineRule); |
| 1874 } else { | 2080 } else { |
| 1875 soloSplit(); | 2081 lineRule.beforeArgument(split()); |
| 1876 } | 2082 } |
| 1877 } else { | 2083 } else { |
| 1878 builder.split(nest: false, space: true); | 2084 builder.split(nest: false, space: true); |
| 1879 } | 2085 } |
| 1880 } | 2086 } |
| 1881 | 2087 |
| 1882 builder.nestExpression(); | 2088 builder.nestExpression(); |
| 1883 visit(element); | 2089 visit(element); |
| 1884 | 2090 |
| 1885 // The comma after the element. | 2091 // The comma after the element. |
| 1886 if (element.endToken.next.lexeme == ",") token(element.endToken.next); | 2092 if (element.endToken.next.type == TokenType.COMMA) { |
| 2093 token(element.endToken.next); |
| 2094 } |
| 1887 | 2095 |
| 1888 builder.unnest(); | 2096 builder.unnest(); |
| 1889 } | 2097 } |
| 1890 | 2098 |
| 1891 builder.endRule(); | 2099 builder.endRule(); |
| 1892 | 2100 |
| 1893 // If there is a collection inside this one, it forces this one to split. | 2101 // If there is a collection inside this one, it forces this one to split. |
| 1894 var force = _collectionSplits.removeLast(); | 2102 var force = _collectionSplits.removeLast(); |
| 1895 | 2103 |
| 2104 // If the collection has a trailing comma, the user must want it to split. |
| 2105 if (elements.isNotEmpty && |
| 2106 elements.last.endToken.next.type == TokenType.COMMA) { |
| 2107 force = true; |
| 2108 } |
| 2109 |
| 1896 _endLiteralBody(rightBracket, ignoredRule: rule, forceSplit: force); | 2110 _endLiteralBody(rightBracket, ignoredRule: rule, forceSplit: force); |
| 1897 } | 2111 } |
| 1898 | 2112 |
| 2113 /// Writes [parameters], which is assumed to have a trailing comma after the |
| 2114 /// last parameter. |
| 2115 /// |
| 2116 /// Parameter lists with trailing commas are formatted differently from |
| 2117 /// regular parameter lists. They are treated more like collection literals. |
| 2118 /// |
| 2119 /// We don't reuse [_visitCollectionLiteral] here because there are enough |
| 2120 /// weird differences around optional parameters that it's easiest just to |
| 2121 /// give them their own method. |
| 2122 void _visitTrailingCommaParameterList(FormalParameterList parameters) { |
| 2123 // Can't have a trailing comma if there are no parameters. |
| 2124 assert(parameters.parameters.isNotEmpty); |
| 2125 |
| 2126 _metadataRules.add(new MetadataRule()); |
| 2127 |
| 2128 // Always split the parameters. |
| 2129 builder.startRule(new Rule.hard()); |
| 2130 |
| 2131 token(parameters.leftParenthesis); |
| 2132 |
| 2133 // Find the parameter immediately preceding the optional parameters (if |
| 2134 // there are any). |
| 2135 FormalParameter lastRequired; |
| 2136 for (var i = 0; i < parameters.parameters.length; i++) { |
| 2137 if (parameters.parameters[i] is DefaultFormalParameter) { |
| 2138 if (i > 0) lastRequired = parameters.parameters[i - 1]; |
| 2139 break; |
| 2140 } |
| 2141 } |
| 2142 |
| 2143 // If all parameters are optional, put the "[" or "{" right after "(". |
| 2144 if (parameters.parameters.first is DefaultFormalParameter) { |
| 2145 token(parameters.leftDelimiter); |
| 2146 } |
| 2147 |
| 2148 // Process the parameters as a separate set of chunks. |
| 2149 builder = builder.startBlock(null); |
| 2150 |
| 2151 for (var parameter in parameters.parameters) { |
| 2152 visit(parameter); |
| 2153 |
| 2154 // The comma after the parameter. |
| 2155 if (parameter.endToken.next.type == TokenType.COMMA) { |
| 2156 token(parameter.endToken.next); |
| 2157 } |
| 2158 |
| 2159 // If the optional parameters start after this one, put the delimiter |
| 2160 // at the end of its line. |
| 2161 if (parameter == lastRequired) { |
| 2162 space(); |
| 2163 token(parameters.leftDelimiter); |
| 2164 lastRequired = null; |
| 2165 } |
| 2166 |
| 2167 newline(); |
| 2168 } |
| 2169 |
| 2170 // Put comments before the closing ")", "]", or "}" inside the block. |
| 2171 var firstDelimiter = |
| 2172 parameters.rightDelimiter ?? parameters.rightParenthesis; |
| 2173 writePrecedingCommentsAndNewlines(firstDelimiter); |
| 2174 builder = builder.endBlock(null, forceSplit: true); |
| 2175 builder.endRule(); |
| 2176 |
| 2177 _metadataRules.removeLast(); |
| 2178 |
| 2179 // Now write the delimiter itself. |
| 2180 _writeText(firstDelimiter.lexeme, firstDelimiter.offset); |
| 2181 if (firstDelimiter != parameters.rightParenthesis) { |
| 2182 token(parameters.rightParenthesis); |
| 2183 } |
| 2184 } |
| 2185 |
| 1899 /// Gets the cost to split at an assignment (or `:` in the case of a named | 2186 /// Gets the cost to split at an assignment (or `:` in the case of a named |
| 1900 /// default value) with the given [rightHandSide]. | 2187 /// default value) with the given [rightHandSide]. |
| 1901 /// | 2188 /// |
| 1902 /// "Block-like" expressions (collections and cascades) bind a bit tighter | 2189 /// "Block-like" expressions (collections and cascades) bind a bit tighter |
| 1903 /// because it looks better to have code like: | 2190 /// because it looks better to have code like: |
| 1904 /// | 2191 /// |
| 1905 /// var list = [ | 2192 /// var list = [ |
| 1906 /// element, | 2193 /// element, |
| 1907 /// element, | 2194 /// element, |
| 1908 /// element | 2195 /// element |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1990 | 2277 |
| 1991 builder = builder.endBlock(ignoredRule, | 2278 builder = builder.endBlock(ignoredRule, |
| 1992 forceSplit: hasLeadingNewline || forceSplit); | 2279 forceSplit: hasLeadingNewline || forceSplit); |
| 1993 | 2280 |
| 1994 builder.endRule(); | 2281 builder.endRule(); |
| 1995 | 2282 |
| 1996 // Now write the delimiter itself. | 2283 // Now write the delimiter itself. |
| 1997 _writeText(rightBracket.lexeme, rightBracket.offset); | 2284 _writeText(rightBracket.lexeme, rightBracket.offset); |
| 1998 } | 2285 } |
| 1999 | 2286 |
| 2287 /// Visits a list of configurations in an import or export directive. |
| 2288 void _visitConfigurations(NodeList<Configuration> configurations) { |
| 2289 if (configurations.isEmpty) return; |
| 2290 |
| 2291 builder.startRule(); |
| 2292 |
| 2293 for (var configuration in configurations) { |
| 2294 split(); |
| 2295 visit(configuration); |
| 2296 } |
| 2297 |
| 2298 builder.endRule(); |
| 2299 } |
| 2300 |
| 2000 /// Visits a "combinator". | 2301 /// Visits a "combinator". |
| 2001 /// | 2302 /// |
| 2002 /// This is a [keyword] followed by a list of [nodes], with specific line | 2303 /// This is a [keyword] followed by a list of [nodes], with specific line |
| 2003 /// splitting rules. As the name implies, this is used for [HideCombinator] | 2304 /// splitting rules. As the name implies, this is used for [HideCombinator] |
| 2004 /// and [ShowCombinator], but it also used for "with" and "implements" | 2305 /// and [ShowCombinator], but it also used for "with" and "implements" |
| 2005 /// clauses in class declarations, which are formatted the same way. | 2306 /// clauses in class declarations, which are formatted the same way. |
| 2006 /// | 2307 /// |
| 2007 /// This assumes the current rule is a [CombinatorRule]. | 2308 /// This assumes the current rule is a [CombinatorRule]. |
| 2008 void _visitCombinator(Token keyword, Iterable<AstNode> nodes) { | 2309 void _visitCombinator(Token keyword, Iterable<AstNode> nodes) { |
| 2009 // Allow splitting before the keyword. | 2310 // Allow splitting before the keyword. |
| (...skipping 25 matching lines...) Expand all Loading... |
| 2035 | 2336 |
| 2036 /// Marks the collection literal that starts with [leftBracket] as being | 2337 /// Marks the collection literal that starts with [leftBracket] as being |
| 2037 /// controlled by [argumentList]. | 2338 /// controlled by [argumentList]. |
| 2038 /// | 2339 /// |
| 2039 /// When the collection is visited, [argumentList] will determine the | 2340 /// When the collection is visited, [argumentList] will determine the |
| 2040 /// indentation and splitting rule for the collection. | 2341 /// indentation and splitting rule for the collection. |
| 2041 void beforeCollection(Token leftBracket, ArgumentSublist argumentList) { | 2342 void beforeCollection(Token leftBracket, ArgumentSublist argumentList) { |
| 2042 _collectionArgumentLists[leftBracket] = argumentList; | 2343 _collectionArgumentLists[leftBracket] = argumentList; |
| 2043 } | 2344 } |
| 2044 | 2345 |
| 2045 /// Writes an bracket-delimited body and handles indenting and starting the | 2346 /// Writes the beginning of a brace-delimited body and handles indenting and |
| 2046 /// rule used to split the contents. | 2347 /// starting the rule used to split the contents. |
| 2047 /// | 2348 void _beginBody(Token leftBracket, {bool space: false}) { |
| 2048 /// If [space] is `true`, then the contents and delimiters will have a space | |
| 2049 /// between then when unsplit. | |
| 2050 void _writeBody(Token leftBracket, Token rightBracket, | |
| 2051 {bool space: false, body()}) { | |
| 2052 token(leftBracket); | 2349 token(leftBracket); |
| 2053 | 2350 |
| 2054 // Indent the body. | 2351 // Indent the body. |
| 2055 builder.indent(); | 2352 builder.indent(); |
| 2056 | 2353 |
| 2057 // Split after the bracket. | 2354 // Split after the bracket. |
| 2058 builder.startRule(); | 2355 builder.startRule(); |
| 2059 builder.split(isDouble: false, nest: false, space: space); | 2356 builder.split(isDouble: false, nest: false, space: space); |
| 2357 } |
| 2060 | 2358 |
| 2061 body(); | 2359 /// Finishes the body started by a call to [_beginBody]. |
| 2062 | 2360 void _endBody(Token rightBracket, {bool space: false}) { |
| 2063 token(rightBracket, before: () { | 2361 token(rightBracket, before: () { |
| 2064 // Split before the closing bracket character. | 2362 // Split before the closing bracket character. |
| 2065 builder.unindent(); | 2363 builder.unindent(); |
| 2066 builder.split(nest: false, space: space); | 2364 builder.split(nest: false, space: space); |
| 2067 }); | 2365 }); |
| 2068 | 2366 |
| 2069 builder.endRule(); | 2367 builder.endRule(); |
| 2070 } | 2368 } |
| 2071 | 2369 |
| 2072 /// Returns `true` if [node] is immediately contained within an anonymous | 2370 /// Returns `true` if [node] is immediately contained within an anonymous |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2108 /// Emit a single mandatory newline. | 2406 /// Emit a single mandatory newline. |
| 2109 void newline() { | 2407 void newline() { |
| 2110 builder.writeWhitespace(Whitespace.newline); | 2408 builder.writeWhitespace(Whitespace.newline); |
| 2111 } | 2409 } |
| 2112 | 2410 |
| 2113 /// Emit a two mandatory newlines. | 2411 /// Emit a two mandatory newlines. |
| 2114 void twoNewlines() { | 2412 void twoNewlines() { |
| 2115 builder.writeWhitespace(Whitespace.twoNewlines); | 2413 builder.writeWhitespace(Whitespace.twoNewlines); |
| 2116 } | 2414 } |
| 2117 | 2415 |
| 2118 /// Allow either a single space or newline to be emitted before the next | |
| 2119 /// non-whitespace token based on whether a newline exists in the source | |
| 2120 /// between the last token and the next one. | |
| 2121 void spaceOrNewline() { | |
| 2122 builder.writeWhitespace(Whitespace.spaceOrNewline); | |
| 2123 } | |
| 2124 | |
| 2125 /// Allow either a single split or newline to be emitted before the next | 2416 /// Allow either a single split or newline to be emitted before the next |
| 2126 /// non-whitespace token based on whether a newline exists in the source | 2417 /// non-whitespace token based on whether a newline exists in the source |
| 2127 /// between the last token and the next one. | 2418 /// between the last token and the next one. |
| 2128 void splitOrNewline() { | 2419 void splitOrNewline() { |
| 2129 builder.writeWhitespace(Whitespace.splitOrNewline); | 2420 builder.writeWhitespace(Whitespace.splitOrNewline); |
| 2130 } | 2421 } |
| 2131 | 2422 |
| 2132 /// Allow either one or two newlines to be emitted before the next | 2423 /// Allow either one or two newlines to be emitted before the next |
| 2133 /// non-whitespace token based on whether more than one newline exists in the | 2424 /// non-whitespace token based on whether more than one newline exists in the |
| 2134 /// source between the last token and the next one. | 2425 /// source between the last token and the next one. |
| 2135 void oneOrTwoNewlines() { | 2426 void oneOrTwoNewlines() { |
| 2136 builder.writeWhitespace(Whitespace.oneOrTwoNewlines); | 2427 builder.writeWhitespace(Whitespace.oneOrTwoNewlines); |
| 2137 } | 2428 } |
| 2138 | 2429 |
| 2139 /// Writes a single space split owned by the current rule. | 2430 /// Writes a single space split owned by the current rule. |
| 2140 /// | 2431 /// |
| 2141 /// Returns the chunk the split was applied to. | 2432 /// Returns the chunk the split was applied to. |
| 2142 Chunk split() => builder.split(space: true); | 2433 Chunk split() => builder.split(space: true); |
| 2143 | 2434 |
| 2144 /// Writes a zero-space split owned by the current rule. | 2435 /// Writes a zero-space split owned by the current rule. |
| 2145 /// | 2436 /// |
| 2146 /// Returns the chunk the split was applied to. | 2437 /// Returns the chunk the split was applied to. |
| 2147 Chunk zeroSplit() => builder.split(); | 2438 Chunk zeroSplit() => builder.split(); |
| 2148 | 2439 |
| 2149 /// Writes a single space split with its own rule. | 2440 /// Writes a single space split with its own rule. |
| 2150 void soloSplit([int cost]) { | 2441 Rule soloSplit([int cost]) { |
| 2151 builder.startRule(new Rule(cost)); | 2442 var rule = new Rule(cost); |
| 2443 builder.startRule(rule); |
| 2152 split(); | 2444 split(); |
| 2153 builder.endRule(); | 2445 builder.endRule(); |
| 2446 return rule; |
| 2154 } | 2447 } |
| 2155 | 2448 |
| 2156 /// Writes a zero-space split with its own rule. | 2449 /// Writes a zero-space split with its own rule. |
| 2157 void soloZeroSplit() { | 2450 void soloZeroSplit() { |
| 2158 builder.startRule(); | 2451 builder.startRule(); |
| 2159 builder.split(); | 2452 builder.split(); |
| 2160 builder.endRule(); | 2453 builder.endRule(); |
| 2161 } | 2454 } |
| 2162 | 2455 |
| 2163 /// Emit [token], along with any comments and formatted whitespace that comes | 2456 /// Emit [token], along with any comments and formatted whitespace that comes |
| (...skipping 22 matching lines...) Expand all Loading... |
| 2186 // actually needed. | 2479 // actually needed. |
| 2187 if (comment == null) { | 2480 if (comment == null) { |
| 2188 if (builder.needsToPreserveNewlines) { | 2481 if (builder.needsToPreserveNewlines) { |
| 2189 builder.preserveNewlines(_startLine(token) - _endLine(token.previous)); | 2482 builder.preserveNewlines(_startLine(token) - _endLine(token.previous)); |
| 2190 } | 2483 } |
| 2191 | 2484 |
| 2192 return false; | 2485 return false; |
| 2193 } | 2486 } |
| 2194 | 2487 |
| 2195 var previousLine = _endLine(token.previous); | 2488 var previousLine = _endLine(token.previous); |
| 2196 | |
| 2197 // Corner case! The analyzer includes the "\n" in the script tag's lexeme, | |
| 2198 // which means it appears to be one line later than it is. That causes a | |
| 2199 // comment following it to appear to be on the same line. Fix that here by | |
| 2200 // correcting the script tag's line. | |
| 2201 if (token.previous.type == TokenType.SCRIPT_TAG) previousLine--; | |
| 2202 | |
| 2203 var tokenLine = _startLine(token); | 2489 var tokenLine = _startLine(token); |
| 2204 | 2490 |
| 2205 var comments = []; | 2491 // Edge case: The analyzer includes the "\n" in the script tag's lexeme, |
| 2492 // which confuses some of these calculations. We don't want to allow a |
| 2493 // blank line between the script tag and a following comment anyway, so |
| 2494 // just override the script tag's line. |
| 2495 if (token.previous.type == TokenType.SCRIPT_TAG) previousLine = tokenLine; |
| 2496 |
| 2497 var comments = <SourceComment>[]; |
| 2206 while (comment != null) { | 2498 while (comment != null) { |
| 2207 var commentLine = _startLine(comment); | 2499 var commentLine = _startLine(comment); |
| 2208 | 2500 |
| 2209 // Don't preserve newlines at the top of the file. | 2501 // Don't preserve newlines at the top of the file. |
| 2210 if (comment == token.precedingComments && | 2502 if (comment == token.precedingComments && |
| 2211 token.previous.type == TokenType.EOF) { | 2503 token.previous.type == TokenType.EOF) { |
| 2212 previousLine = commentLine; | 2504 previousLine = commentLine; |
| 2213 } | 2505 } |
| 2214 | 2506 |
| 2215 var text = comment.toString().trim(); | 2507 var text = comment.toString().trim(); |
| (...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2362 /// Gets the 1-based line number that the beginning of [token] lies on. | 2654 /// Gets the 1-based line number that the beginning of [token] lies on. |
| 2363 int _startLine(Token token) => _lineInfo.getLocation(token.offset).lineNumber; | 2655 int _startLine(Token token) => _lineInfo.getLocation(token.offset).lineNumber; |
| 2364 | 2656 |
| 2365 /// Gets the 1-based line number that the end of [token] lies on. | 2657 /// Gets the 1-based line number that the end of [token] lies on. |
| 2366 int _endLine(Token token) => _lineInfo.getLocation(token.end).lineNumber; | 2658 int _endLine(Token token) => _lineInfo.getLocation(token.end).lineNumber; |
| 2367 | 2659 |
| 2368 /// Gets the 1-based column number that the beginning of [token] lies on. | 2660 /// Gets the 1-based column number that the beginning of [token] lies on. |
| 2369 int _startColumn(Token token) => | 2661 int _startColumn(Token token) => |
| 2370 _lineInfo.getLocation(token.offset).columnNumber; | 2662 _lineInfo.getLocation(token.offset).columnNumber; |
| 2371 } | 2663 } |
| OLD | NEW |