| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 formatter_impl; | 5 library formatter_impl; |
| 6 | 6 |
| 7 import 'dart:math'; | 7 import 'dart:math'; |
| 8 | 8 |
| 9 import 'package:analyzer_experimental/analyzer.dart'; | 9 import 'package:analyzer_experimental/analyzer.dart'; |
| 10 import 'package:analyzer_experimental/src/generated/parser.dart'; | 10 import 'package:analyzer_experimental/src/generated/parser.dart'; |
| 11 import 'package:analyzer_experimental/src/generated/scanner.dart'; | 11 import 'package:analyzer_experimental/src/generated/scanner.dart'; |
| 12 import 'package:analyzer_experimental/src/generated/source.dart'; | 12 import 'package:analyzer_experimental/src/generated/source.dart'; |
| 13 import 'package:analyzer_experimental/src/services/writer.dart'; | 13 import 'package:analyzer_experimental/src/services/writer.dart'; |
| 14 | 14 |
| 15 /// Formatter options. | 15 /// Formatter options. |
| 16 class FormatterOptions { | 16 class FormatterOptions { |
| 17 | 17 |
| 18 /// Create formatter options with defaults derived (where defined) from | 18 /// Create formatter options with defaults derived (where defined) from |
| 19 /// the style guide: <http://www.dartlang.org/articles/style-guide/>. | 19 /// the style guide: <http://www.dartlang.org/articles/style-guide/>. |
| 20 const FormatterOptions({this.initialIndentationLevel: 0, | 20 const FormatterOptions({this.initialIndentationLevel: 0, |
| 21 this.spacesPerIndent: 2, | 21 this.spacesPerIndent: 2, |
| 22 this.lineSeparator: NEW_LINE, | 22 this.lineSeparator: NEW_LINE, |
| 23 this.pageWidth: 80, | 23 this.pageWidth: 80, |
| 24 this.tabsForIndent: false, | 24 this.tabsForIndent: false, |
| 25 this.tabSize: 2}); | 25 this.tabSize: 2, |
| 26 this.codeTransforms: false}); |
| 26 | 27 |
| 27 final String lineSeparator; | 28 final String lineSeparator; |
| 28 final int initialIndentationLevel; | 29 final int initialIndentationLevel; |
| 29 final int spacesPerIndent; | 30 final int spacesPerIndent; |
| 30 final int tabSize; | 31 final int tabSize; |
| 31 final bool tabsForIndent; | 32 final bool tabsForIndent; |
| 32 final int pageWidth; | 33 final int pageWidth; |
| 34 final bool codeTransforms; |
| 33 } | 35 } |
| 34 | 36 |
| 35 | 37 |
| 36 /// Thrown when an error occurs in formatting. | 38 /// Thrown when an error occurs in formatting. |
| 37 class FormatterException implements Exception { | 39 class FormatterException implements Exception { |
| 38 | 40 |
| 39 /// A message describing the error. | 41 /// A message describing the error. |
| 40 final String message; | 42 final String message; |
| 41 | 43 |
| 42 /// Creates a new FormatterException with an optional error [message]. | 44 /// Creates a new FormatterException with an optional error [message]. |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 78 | 80 |
| 79 /// Format the specified portion (from [offset] with [length]) of the given | 81 /// Format the specified portion (from [offset] with [length]) of the given |
| 80 /// [source] string, optionally providing an [indentationLevel]. | 82 /// [source] string, optionally providing an [indentationLevel]. |
| 81 FormattedSource format(CodeKind kind, String source, {int offset, int end, | 83 FormattedSource format(CodeKind kind, String source, {int offset, int end, |
| 82 int indentationLevel: 0, Selection selection: null}); | 84 int indentationLevel: 0, Selection selection: null}); |
| 83 | 85 |
| 84 } | 86 } |
| 85 | 87 |
| 86 /// Source selection state information. | 88 /// Source selection state information. |
| 87 class Selection { | 89 class Selection { |
| 88 | 90 |
| 89 /// The offset of the source selection. | 91 /// The offset of the source selection. |
| 90 final int offset; | 92 final int offset; |
| 91 | 93 |
| 92 /// The length of the selection. | 94 /// The length of the selection. |
| 93 final int length; | 95 final int length; |
| 94 | 96 |
| 95 Selection(this.offset, this.length); | 97 Selection(this.offset, this.length); |
| 96 | 98 |
| 97 String toString() => 'Selection (offset: $offset, length: $length)'; | 99 String toString() => 'Selection (offset: $offset, length: $length)'; |
| 98 } | 100 } |
| 99 | 101 |
| 100 /// Formatted source. | 102 /// Formatted source. |
| 101 class FormattedSource { | 103 class FormattedSource { |
| 102 | 104 |
| 103 /// Selection state or null if unspecified. | 105 /// Selection state or null if unspecified. |
| 104 final Selection selection; | 106 Selection selection; |
| 105 | 107 |
| 106 /// Formatted source string. | 108 /// Formatted source string. |
| 107 final String source; | 109 final String source; |
| 108 | 110 |
| 109 /// Create a formatted [source] result, with optional [selection] information. | 111 /// Create a formatted [source] result, with optional [selection] information. |
| 110 FormattedSource(this.source, [this.selection = null]); | 112 FormattedSource(this.source, [this.selection = null]); |
| 111 } | 113 } |
| 112 | 114 |
| 113 | 115 |
| 114 class CodeFormatterImpl implements CodeFormatter, AnalysisErrorListener { | 116 class CodeFormatterImpl implements CodeFormatter, AnalysisErrorListener { |
| 115 | 117 |
| 116 final FormatterOptions options; | 118 final FormatterOptions options; |
| 117 final errors = <AnalysisError>[]; | 119 final errors = <AnalysisError>[]; |
| 118 final whitespace = new RegExp(r'[\s]+'); | 120 final whitespace = new RegExp(r'[\s]+'); |
| 119 | 121 |
| 120 LineInfo lineInfo; | 122 LineInfo lineInfo; |
| 121 | 123 |
| 122 CodeFormatterImpl(this.options); | 124 CodeFormatterImpl(this.options); |
| 123 | 125 |
| 124 FormattedSource format(CodeKind kind, String source, {int offset, int end, | 126 FormattedSource format(CodeKind kind, String source, {int offset, int end, |
| 125 int indentationLevel: 0, Selection selection: null}) { | 127 int indentationLevel: 0, Selection selection: null}) { |
| 126 | 128 |
| 127 var startToken = tokenize(source); | 129 var startToken = tokenize(source); |
| 128 checkForErrors(); | 130 checkForErrors(); |
| 129 | 131 |
| 130 var node = parse(kind, startToken); | 132 var node = parse(kind, startToken); |
| 131 checkForErrors(); | 133 checkForErrors(); |
| 132 | 134 |
| 133 var formatter = new SourceVisitor(options, lineInfo, selection); | 135 var formatter = new SourceVisitor(options, lineInfo, selection); |
| 134 node.accept(formatter); | 136 node.accept(formatter); |
| 135 | 137 |
| 136 var formattedSource = formatter.writer.toString(); | 138 var formattedSource = formatter.writer.toString(); |
| 137 | 139 |
| 138 checkTokenStreams(startToken, tokenize(formattedSource)); | 140 checkTokenStreams(startToken, tokenize(formattedSource)); |
| 139 | 141 |
| 140 return new FormattedSource(formattedSource, formatter.selection); | 142 return new FormattedSource(formattedSource, formatter.selection); |
| 141 } | 143 } |
| 142 | 144 |
| 143 checkTokenStreams(Token t1, Token t2) => | 145 checkTokenStreams(Token t1, Token t2) => |
| 144 new TokenStreamComparator(lineInfo, t1, t2).verifyEquals(); | 146 new TokenStreamComparator(lineInfo, t1, t2).verifyEquals(); |
| 145 | 147 |
| 146 ASTNode parse(CodeKind kind, Token start) { | 148 ASTNode parse(CodeKind kind, Token start) { |
| 147 | 149 |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 189 verifyEquals() { | 191 verifyEquals() { |
| 190 while (!isEOF(token1)) { | 192 while (!isEOF(token1)) { |
| 191 checkPrecedingComments(); | 193 checkPrecedingComments(); |
| 192 if (!checkTokens()) { | 194 if (!checkTokens()) { |
| 193 throwNotEqualException(token1, token2); | 195 throwNotEqualException(token1, token2); |
| 194 } | 196 } |
| 195 advance(); | 197 advance(); |
| 196 | 198 |
| 197 } | 199 } |
| 198 // TODO(pquitslund): consider a better way to notice trailing synthetics | 200 // TODO(pquitslund): consider a better way to notice trailing synthetics |
| 199 if (!isEOF(token2) && | 201 if (!isEOF(token2) && |
| 200 !(isCLOSE_CURLY_BRACKET(token2) && isEOF(token2.next))) { | 202 !(isCLOSE_CURLY_BRACKET(token2) && isEOF(token2.next))) { |
| 201 throw new FormatterException( | 203 throw new FormatterException( |
| 202 'Expected "EOF" but got "${token2}".'); | 204 'Expected "EOF" but got "${token2}".'); |
| 203 } | 205 } |
| 204 } | 206 } |
| 205 | 207 |
| 206 checkPrecedingComments() { | 208 checkPrecedingComments() { |
| 207 var comment1 = token1.precedingComments; | 209 var comment1 = token1.precedingComments; |
| 208 var comment2 = token2.precedingComments; | 210 var comment2 = token2.precedingComments; |
| 209 while (comment1 != null) { | 211 while (comment1 != null) { |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 274 | 276 |
| 275 } | 277 } |
| 276 | 278 |
| 277 // Cached parser for testing token types. | 279 // Cached parser for testing token types. |
| 278 final tokenTester = new Parser(null,null); | 280 final tokenTester = new Parser(null,null); |
| 279 | 281 |
| 280 /// Test if this token is an EOF token. | 282 /// Test if this token is an EOF token. |
| 281 bool isEOF(Token token) => tokenIs(token, TokenType.EOF); | 283 bool isEOF(Token token) => tokenIs(token, TokenType.EOF); |
| 282 | 284 |
| 283 /// Test for token type. | 285 /// Test for token type. |
| 284 bool tokenIs(Token token, TokenType type) => | 286 bool tokenIs(Token token, TokenType type) => |
| 285 token != null && tokenTester.matches4(token, type); | 287 token != null && tokenTester.matches4(token, type); |
| 286 | 288 |
| 287 /// Test if this token is a GT token. | 289 /// Test if this token is a GT token. |
| 288 bool isGT(Token token) => tokenIs(token, TokenType.GT); | 290 bool isGT(Token token) => tokenIs(token, TokenType.GT); |
| 289 | 291 |
| 290 /// Test if this token is a GT_GT token. | 292 /// Test if this token is a GT_GT token. |
| 291 bool isGT_GT(Token token) => tokenIs(token, TokenType.GT_GT); | 293 bool isGT_GT(Token token) => tokenIs(token, TokenType.GT_GT); |
| 292 | 294 |
| 293 /// Test if this token is an INDEX token. | 295 /// Test if this token is an INDEX token. |
| 294 bool isINDEX(Token token) => tokenIs(token, TokenType.INDEX); | 296 bool isINDEX(Token token) => tokenIs(token, TokenType.INDEX); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 305 bool isOPEN_SQ_BRACKET(Token token) => | 307 bool isOPEN_SQ_BRACKET(Token token) => |
| 306 tokenIs(token, TokenType.OPEN_SQUARE_BRACKET); | 308 tokenIs(token, TokenType.OPEN_SQUARE_BRACKET); |
| 307 | 309 |
| 308 /// Test if this token is a CLOSE_SQUARE_BRACKET token. | 310 /// Test if this token is a CLOSE_SQUARE_BRACKET token. |
| 309 bool isCLOSE_SQUARE_BRACKET(Token token) => | 311 bool isCLOSE_SQUARE_BRACKET(Token token) => |
| 310 tokenIs(token, TokenType.CLOSE_SQUARE_BRACKET); | 312 tokenIs(token, TokenType.CLOSE_SQUARE_BRACKET); |
| 311 | 313 |
| 312 | 314 |
| 313 /// An AST visitor that drives formatting heuristics. | 315 /// An AST visitor that drives formatting heuristics. |
| 314 class SourceVisitor implements ASTVisitor { | 316 class SourceVisitor implements ASTVisitor { |
| 315 | 317 |
| 316 static final OPEN_CURLY = syntheticToken(TokenType.OPEN_CURLY_BRACKET, '{'); | 318 static final OPEN_CURLY = syntheticToken(TokenType.OPEN_CURLY_BRACKET, '{'); |
| 317 static final CLOSE_CURLY = syntheticToken(TokenType.CLOSE_CURLY_BRACKET, '}'); | 319 static final CLOSE_CURLY = syntheticToken(TokenType.CLOSE_CURLY_BRACKET, '}'); |
| 318 | 320 |
| 319 static const SYNTH_OFFSET = -13; | 321 static const SYNTH_OFFSET = -13; |
| 320 | 322 |
| 321 static StringToken syntheticToken(TokenType type, String value) => | 323 static StringToken syntheticToken(TokenType type, String value) => |
| 322 new StringToken(type, value, SYNTH_OFFSET); | 324 new StringToken(type, value, SYNTH_OFFSET); |
| 323 | 325 |
| 324 static bool isSynthetic(Token token) => token.offset == SYNTH_OFFSET; | 326 static bool isSynthetic(Token token) => token.offset == SYNTH_OFFSET; |
| 325 | 327 |
| 326 /// The writer to which the source is to be written. | 328 /// The writer to which the source is to be written. |
| 327 final SourceWriter writer; | 329 final SourceWriter writer; |
| 328 | 330 |
| 329 /// Cached line info for calculating blank lines. | 331 /// Cached line info for calculating blank lines. |
| 330 LineInfo lineInfo; | 332 LineInfo lineInfo; |
| 331 | 333 |
| 332 /// Cached previous token for calculating preceding whitespace. | 334 /// Cached previous token for calculating preceding whitespace. |
| 333 Token previousToken; | 335 Token previousToken; |
| 334 | 336 |
| 335 /// A flag to indicate that a newline should be emitted before the next token. | 337 /// A flag to indicate that a newline should be emitted before the next token. |
| 336 bool needsNewline = false; | 338 bool needsNewline = false; |
| 337 | 339 |
| 338 /// A counter for spaces that should be emitted preceding the next token. | 340 /// A counter for spaces that should be emitted preceding the next token. |
| 339 int leadingSpaces = 0; | 341 int leadingSpaces = 0; |
| 340 | 342 |
| 341 /// Used for matching EOL comments | 343 /// Used for matching EOL comments |
| 342 final twoSlashes = new RegExp(r'//[^/]'); | 344 final twoSlashes = new RegExp(r'//[^/]'); |
| 343 | 345 |
| 344 /// Original pre-format selection information (may be null). | 346 /// Original pre-format selection information (may be null). |
| 345 final Selection preSelection; | 347 final Selection preSelection; |
| 346 | 348 |
| 349 final bool codeTransforms; |
| 350 |
| 347 /// Post format selection information. | 351 /// Post format selection information. |
| 348 Selection selection; | 352 Selection selection; |
| 349 | 353 |
| 354 |
| 350 /// Initialize a newly created visitor to write source code representing | 355 /// Initialize a newly created visitor to write source code representing |
| 351 /// the visited nodes to the given [writer]. | 356 /// the visited nodes to the given [writer]. |
| 352 SourceVisitor(FormatterOptions options, this.lineInfo, this.preSelection) : | 357 SourceVisitor(FormatterOptions options, this.lineInfo, this.preSelection): |
| 353 writer = new SourceWriter(indentCount: options.initialIndentationLevel, | 358 writer = new SourceWriter(indentCount: options.initialIndentationLevel, |
| 354 lineSeparator: options.lineSeparator); | 359 lineSeparator: options.lineSeparator), |
| 360 codeTransforms = options.codeTransforms; |
| 355 | 361 |
| 356 visitAdjacentStrings(AdjacentStrings node) { | 362 visitAdjacentStrings(AdjacentStrings node) { |
| 357 visitNodes(node.strings, separatedBy: space); | 363 visitNodes(node.strings, separatedBy: space); |
| 358 } | 364 } |
| 359 | 365 |
| 360 visitAnnotation(Annotation node) { | 366 visitAnnotation(Annotation node) { |
| 361 token(node.atSign); | 367 token(node.atSign); |
| 362 visit(node.name); | 368 visit(node.name); |
| 363 token(node.period); | 369 token(node.period); |
| 364 visit(node.constructorName); | 370 visit(node.constructorName); |
| (...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 511 var scriptTag = node.scriptTag; | 517 var scriptTag = node.scriptTag; |
| 512 var directives = node.directives; | 518 var directives = node.directives; |
| 513 visit(scriptTag); | 519 visit(scriptTag); |
| 514 | 520 |
| 515 visitNodes(directives, separatedBy: newlines, followedBy: newlines); | 521 visitNodes(directives, separatedBy: newlines, followedBy: newlines); |
| 516 | 522 |
| 517 visitNodes(node.declarations, separatedBy: newlines); | 523 visitNodes(node.declarations, separatedBy: newlines); |
| 518 | 524 |
| 519 // Handle trailing whitespace | 525 // Handle trailing whitespace |
| 520 token(node.endToken /* EOF */); | 526 token(node.endToken /* EOF */); |
| 521 | 527 |
| 522 // Be a good citizen, end with a NL | 528 // Be a good citizen, end with a NL |
| 523 ensureTrailingNewline(); | 529 ensureTrailingNewline(); |
| 524 } | 530 } |
| 525 | 531 |
| 526 visitConditionalExpression(ConditionalExpression node) { | 532 visitConditionalExpression(ConditionalExpression node) { |
| 527 visit(node.condition); | 533 visit(node.condition); |
| 528 space(); | 534 space(); |
| 529 token(node.question); | 535 token(node.question); |
| 530 space(); | 536 space(); |
| 531 visit(node.thenExpression); | 537 visit(node.thenExpression); |
| 532 space(); | 538 space(); |
| 533 token(node.colon); | 539 token(node.colon); |
| 534 space(); | 540 space(); |
| 535 visit(node.elseExpression); | 541 visit(node.elseExpression); |
| 536 } | 542 } |
| 537 | 543 |
| 538 visitConstructorDeclaration(ConstructorDeclaration node) { | 544 visitConstructorDeclaration(ConstructorDeclaration node) { |
| 539 modifier(node.externalKeyword); | 545 modifier(node.externalKeyword); |
| 540 modifier(node.constKeyword); | 546 modifier(node.constKeyword); |
| 541 modifier(node.factoryKeyword); | 547 modifier(node.factoryKeyword); |
| 542 visit(node.returnType); | 548 visit(node.returnType); |
| 543 token(node.period); | 549 token(node.period); |
| 544 visit(node.name); | 550 visit(node.name); |
| 545 visit(node.parameters); | 551 visit(node.parameters); |
| 546 | 552 |
| 547 // Check for redirects or initializer lists | 553 // Check for redirects or initializer lists |
| 548 if (node.separator != null) { | 554 if (node.separator != null) { |
| 549 if (node.redirectedConstructor != null) { | 555 if (node.redirectedConstructor != null) { |
| 550 visitConstructorRedirects(node); | 556 visitConstructorRedirects(node); |
| 551 } else { | 557 } else { |
| 552 visitConstructorInitializers(node); | 558 visitConstructorInitializers(node); |
| 553 } | 559 } |
| 554 } | 560 } |
| 555 | 561 |
| 556 visitPrefixedBody(space, node.body); | 562 visitPrefixedBody(space, node.body); |
| 557 } | 563 } |
| 558 | 564 |
| 559 visitConstructorInitializers(ConstructorDeclaration node) { | 565 visitConstructorInitializers(ConstructorDeclaration node) { |
| 560 newlines(); | 566 newlines(); |
| 561 indent(2); | 567 indent(2); |
| 562 token(node.separator /* : */); | 568 token(node.separator /* : */); |
| 563 space(); | 569 space(); |
| 564 for (var i = 0; i < node.initializers.length; i++) { | 570 for (var i = 0; i < node.initializers.length; i++) { |
| 565 if (i > 0) { | 571 if (i > 0) { |
| 566 comma(); | 572 comma(); |
| 567 newlines(); | 573 newlines(); |
| 568 space(2); | 574 space(2); |
| 569 } | 575 } |
| 570 node.initializers[i].accept(this); | 576 node.initializers[i].accept(this); |
| 571 } | 577 } |
| 572 unindent(2); | 578 unindent(2); |
| 573 } | 579 } |
| 574 | 580 |
| 575 visitConstructorRedirects(ConstructorDeclaration node) { | 581 visitConstructorRedirects(ConstructorDeclaration node) { |
| 576 token(node.separator /* = */, precededBy: space, followedBy: space); | 582 token(node.separator /* = */, precededBy: space, followedBy: space); |
| 577 visitNodes(node.initializers, separatedBy: commaSeperator); | 583 visitNodes(node.initializers, separatedBy: commaSeperator); |
| 578 visit(node.redirectedConstructor); | 584 visit(node.redirectedConstructor); |
| 579 } | 585 } |
| 580 | 586 |
| 581 visitConstructorFieldInitializer(ConstructorFieldInitializer node) { | 587 visitConstructorFieldInitializer(ConstructorFieldInitializer node) { |
| 582 token(node.keyword); | 588 token(node.keyword); |
| 583 token(node.period); | 589 token(node.period); |
| 584 visit(node.fieldName); | 590 visit(node.fieldName); |
| 585 space(); | 591 space(); |
| 586 token(node.equals); | 592 token(node.equals); |
| 587 space(); | 593 space(); |
| 588 visit(node.expression); | 594 visit(node.expression); |
| 589 } | 595 } |
| 590 | 596 |
| (...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 780 visitNode(node.returnType, followedBy: space); | 786 visitNode(node.returnType, followedBy: space); |
| 781 visit(node.identifier); | 787 visit(node.identifier); |
| 782 visit(node.parameters); | 788 visit(node.parameters); |
| 783 } | 789 } |
| 784 | 790 |
| 785 visitHideCombinator(HideCombinator node) { | 791 visitHideCombinator(HideCombinator node) { |
| 786 token(node.keyword); | 792 token(node.keyword); |
| 787 space(); | 793 space(); |
| 788 visitNodes(node.hiddenNames, separatedBy: commaSeperator); | 794 visitNodes(node.hiddenNames, separatedBy: commaSeperator); |
| 789 } | 795 } |
| 790 | 796 |
| 791 visitIfStatement(IfStatement node) { | 797 visitIfStatement(IfStatement node) { |
| 792 var hasElse = node.elseStatement != null; | 798 var hasElse = node.elseStatement != null; |
| 793 token(node.ifKeyword); | 799 token(node.ifKeyword); |
| 794 space(); | 800 space(); |
| 795 token(node.leftParenthesis); | 801 token(node.leftParenthesis); |
| 796 visit(node.condition); | 802 visit(node.condition); |
| 797 token(node.rightParenthesis); | 803 token(node.rightParenthesis); |
| 798 space(); | 804 space(); |
| 799 if (hasElse) { | 805 if (hasElse) { |
| 800 printAsBlock(node.thenStatement); | 806 printAsBlock(node.thenStatement); |
| 801 space(); | 807 space(); |
| 802 token(node.elseKeyword); | 808 token(node.elseKeyword); |
| 803 space(); | 809 space(); |
| 804 printAsBlock(node.elseStatement); | 810 printAsBlock(node.elseStatement); |
| 805 } else { | 811 } else { |
| 806 visit(node.thenStatement); | 812 visit(node.thenStatement); |
| 807 } | 813 } |
| 808 } | 814 } |
| 809 | 815 |
| 810 visitImplementsClause(ImplementsClause node) { | 816 visitImplementsClause(ImplementsClause node) { |
| 811 token(node.keyword); | 817 token(node.keyword); |
| 812 space(); | 818 space(); |
| 813 visitNodes(node.interfaces, separatedBy: commaSeperator); | 819 visitNodes(node.interfaces, separatedBy: commaSeperator); |
| 814 } | 820 } |
| 815 | 821 |
| 816 visitImportDirective(ImportDirective node) { | 822 visitImportDirective(ImportDirective node) { |
| 817 token(node.keyword); | 823 token(node.keyword); |
| 818 space(); | 824 space(); |
| 819 visit(node.uri); | 825 visit(node.uri); |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 900 } | 906 } |
| 901 | 907 |
| 902 visitMapLiteral(MapLiteral node) { | 908 visitMapLiteral(MapLiteral node) { |
| 903 modifier(node.constKeyword); | 909 modifier(node.constKeyword); |
| 904 visitNode(node.typeArguments, followedBy: space); | 910 visitNode(node.typeArguments, followedBy: space); |
| 905 token(node.leftBracket); | 911 token(node.leftBracket); |
| 906 visitNodes(node.entries, separatedBy: commaSeperator); | 912 visitNodes(node.entries, separatedBy: commaSeperator); |
| 907 optionalTrailingComma(node.rightBracket); | 913 optionalTrailingComma(node.rightBracket); |
| 908 token(node.rightBracket); | 914 token(node.rightBracket); |
| 909 } | 915 } |
| 910 | 916 |
| 911 visitMapLiteralEntry(MapLiteralEntry node) { | 917 visitMapLiteralEntry(MapLiteralEntry node) { |
| 912 visit(node.key); | 918 visit(node.key); |
| 913 token(node.separator); | 919 token(node.separator); |
| 914 space(); | 920 space(); |
| 915 visit(node.value); | 921 visit(node.value); |
| 916 } | 922 } |
| 917 | 923 |
| 918 visitMethodDeclaration(MethodDeclaration node) { | 924 visitMethodDeclaration(MethodDeclaration node) { |
| 919 modifier(node.externalKeyword); | 925 modifier(node.externalKeyword); |
| 920 modifier(node.modifierKeyword); | 926 modifier(node.modifierKeyword); |
| (...skipping 335 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1256 newlines() { | 1262 newlines() { |
| 1257 needsNewline = true; | 1263 needsNewline = true; |
| 1258 } | 1264 } |
| 1259 | 1265 |
| 1260 /// Optionally emit a trailing comma. | 1266 /// Optionally emit a trailing comma. |
| 1261 optionalTrailingComma(Token rightBracket) { | 1267 optionalTrailingComma(Token rightBracket) { |
| 1262 if (rightBracket.previous.lexeme == ',') { | 1268 if (rightBracket.previous.lexeme == ',') { |
| 1263 comma(); | 1269 comma(); |
| 1264 } | 1270 } |
| 1265 } | 1271 } |
| 1266 | 1272 |
| 1267 token(Token token, {precededBy(), followedBy(), int minNewlines: 0}) { | 1273 token(Token token, {precededBy(), followedBy(), int minNewlines: 0}) { |
| 1268 if (token != null) { | 1274 if (token != null) { |
| 1269 if (needsNewline) { | 1275 if (needsNewline) { |
| 1270 minNewlines = max(1, minNewlines); | 1276 minNewlines = max(1, minNewlines); |
| 1271 } | 1277 } |
| 1272 var emitted = emitPrecedingCommentsAndNewlines(token, min: minNewlines); | 1278 var emitted = emitPrecedingCommentsAndNewlines(token, min: minNewlines); |
| 1273 if (emitted > 0) { | 1279 if (emitted > 0) { |
| 1274 needsNewline = false; | 1280 needsNewline = false; |
| 1275 } | 1281 } |
| 1276 if (precededBy != null) { | 1282 if (precededBy != null) { |
| 1277 precededBy(); | 1283 precededBy(); |
| 1278 } | 1284 } |
| 1279 checkForSelectionUpdate(token); | 1285 checkForSelectionUpdate(token); |
| 1280 append(token.lexeme); | 1286 append(token.lexeme); |
| 1281 if (followedBy != null) { | 1287 if (followedBy != null) { |
| 1282 followedBy(); | 1288 followedBy(); |
| 1283 } | 1289 } |
| 1284 previousToken = token; | 1290 previousToken = token; |
| 1285 } | 1291 } |
| 1286 } | 1292 } |
| 1287 | 1293 |
| 1288 emitSpaces() { | 1294 emitSpaces() { |
| 1289 while (leadingSpaces > 0) { | 1295 while (leadingSpaces > 0) { |
| 1290 writer.print(' '); | 1296 writer.print(' '); |
| 1291 leadingSpaces--; | 1297 leadingSpaces--; |
| 1292 } | 1298 } |
| 1293 } | 1299 } |
| 1294 | 1300 |
| 1295 checkForSelectionUpdate(Token token) { | 1301 checkForSelectionUpdate(Token token) { |
| 1296 // Cache the first token on or AFTER the selection offset | 1302 // Cache the first token on or AFTER the selection offset |
| 1297 if (preSelection != null && selection == null) { | 1303 if (preSelection != null && selection == null) { |
| 1298 // Check for overshots | 1304 // Check for overshots |
| 1299 var overshot = token.offset - preSelection.offset; | 1305 var overshot = token.offset - preSelection.offset; |
| 1300 if (overshot >= 0) { | 1306 if (overshot >= 0) { |
| 1301 //TODO(pquitslund): update length (may need truncating) | 1307 //TODO(pquitslund): update length (may need truncating) |
| 1302 selection = new Selection( | 1308 selection = new Selection( |
| 1303 writer.toString().length + leadingSpaces - overshot, | 1309 writer.toString().length + leadingSpaces - overshot, |
| 1304 preSelection.length); | 1310 preSelection.length); |
| 1305 } | 1311 } |
| 1306 } | 1312 } |
| 1307 } | 1313 } |
| 1308 | 1314 |
| 1309 commaSeperator() { | 1315 commaSeperator() { |
| 1310 comma(); | 1316 comma(); |
| 1311 space(); | 1317 space(); |
| 1312 } | 1318 } |
| 1313 | 1319 |
| 1314 comma() { | 1320 comma() { |
| 1315 writer.print(','); | 1321 writer.print(','); |
| 1316 } | 1322 } |
| 1317 | 1323 |
| 1318 | 1324 |
| 1319 /// Emit a non-breakable space. | 1325 /// Emit a non-breakable space. |
| 1320 space([n = 1]) { | 1326 space([n = 1]) { |
| 1321 //TODO(pquitslund): replace with a proper space token | 1327 //TODO(pquitslund): replace with a proper space token |
| 1322 leadingSpaces+=n; | 1328 leadingSpaces+=n; |
| 1323 } | 1329 } |
| 1324 | 1330 |
| 1325 /// Emit a breakable space | 1331 /// Emit a breakable space |
| 1326 breakableSpace() { | 1332 breakableSpace() { |
| 1327 //Implement | 1333 //Implement |
| 1328 } | 1334 } |
| 1329 | 1335 |
| 1330 /// Append the given [string] to the source writer if it's non-null. | 1336 /// Append the given [string] to the source writer if it's non-null. |
| 1331 append(String string) { | 1337 append(String string) { |
| 1332 if (string != null && !string.isEmpty) { | 1338 if (string != null && !string.isEmpty) { |
| 1333 emitSpaces(); | 1339 emitSpaces(); |
| 1334 writer.print(string); | 1340 writer.print(string); |
| 1335 } | 1341 } |
| 1336 } | 1342 } |
| 1337 | 1343 |
| 1338 /// Indent. | 1344 /// Indent. |
| 1339 indent([n = 1]) { | 1345 indent([n = 1]) { |
| 1340 while (n-- > 0) { | 1346 while (n-- > 0) { |
| 1341 writer.indent(); | 1347 writer.indent(); |
| 1342 } | 1348 } |
| 1343 } | 1349 } |
| 1344 | 1350 |
| 1345 /// Unindent | 1351 /// Unindent |
| 1346 unindent([n = 1]) { | 1352 unindent([n = 1]) { |
| 1347 while (n-- > 0) { | 1353 while (n-- > 0) { |
| 1348 writer.unindent(); | 1354 writer.unindent(); |
| 1349 } | 1355 } |
| 1350 } | 1356 } |
| 1351 | 1357 |
| 1352 /// Print this statement as if it were a block (e.g., surrounded by braces). | 1358 /// Print this statement as if it were a block (e.g., surrounded by braces). |
| 1353 printAsBlock(Statement statement) { | 1359 printAsBlock(Statement statement) { |
| 1354 if (statement is! Block) { | 1360 if (codeTransforms && statement is! Block) { |
| 1355 token(OPEN_CURLY); | 1361 token(OPEN_CURLY); |
| 1356 indent(); | 1362 indent(); |
| 1357 newlines(); | 1363 newlines(); |
| 1358 visit(statement); | 1364 visit(statement); |
| 1359 newlines(); | 1365 newlines(); |
| 1360 unindent(); | 1366 unindent(); |
| 1361 token(CLOSE_CURLY); | 1367 token(CLOSE_CURLY); |
| 1362 } else { | 1368 } else { |
| 1363 visit(statement); | 1369 visit(statement); |
| 1364 } | 1370 } |
| 1365 } | 1371 } |
| 1366 | 1372 |
| 1367 /// Emit any detected comments and newlines or a minimum as specified | 1373 /// Emit any detected comments and newlines or a minimum as specified |
| 1368 /// by [min]. | 1374 /// by [min]. |
| 1369 int emitPrecedingCommentsAndNewlines(Token token, {min: 0}) { | 1375 int emitPrecedingCommentsAndNewlines(Token token, {min: 0}) { |
| 1370 | 1376 |
| 1371 var comment = token.precedingComments; | 1377 var comment = token.precedingComments; |
| 1372 var currentToken = comment != null ? comment : token; | 1378 var currentToken = comment != null ? comment : token; |
| 1373 | 1379 |
| 1374 //Handle EOLs before newlines | 1380 //Handle EOLs before newlines |
| 1375 if (isAtEOL(comment)) { | 1381 if (isAtEOL(comment)) { |
| 1376 emitComment(comment, previousToken); | 1382 emitComment(comment, previousToken); |
| (...skipping 26 matching lines...) Expand all Loading... |
| 1403 previousToken = token; | 1409 previousToken = token; |
| 1404 return lines; | 1410 return lines; |
| 1405 } | 1411 } |
| 1406 | 1412 |
| 1407 | 1413 |
| 1408 ensureTrailingNewline() { | 1414 ensureTrailingNewline() { |
| 1409 if (writer.lastToken is! NewlineToken) { | 1415 if (writer.lastToken is! NewlineToken) { |
| 1410 writer.newline(); | 1416 writer.newline(); |
| 1411 } | 1417 } |
| 1412 } | 1418 } |
| 1413 | 1419 |
| 1414 | 1420 |
| 1415 /// Test if this [comment] is at the end of a line. | 1421 /// Test if this [comment] is at the end of a line. |
| 1416 bool isAtEOL(Token comment) => | 1422 bool isAtEOL(Token comment) => |
| 1417 comment != null && comment.toString().trim().startsWith(twoSlashes) && | 1423 comment != null && comment.toString().trim().startsWith(twoSlashes) && |
| 1418 sameLine(comment, previousToken); | 1424 sameLine(comment, previousToken); |
| 1419 | 1425 |
| 1420 /// Emit this [comment], inserting leading whitespace if appropriate. | 1426 /// Emit this [comment], inserting leading whitespace if appropriate. |
| 1421 emitComment(Token comment, Token previousToken) { | 1427 emitComment(Token comment, Token previousToken) { |
| 1422 if (!writer.currentLine.isWhitespace() && !isBlock(comment)) { | 1428 if (!writer.currentLine.isWhitespace() && !isBlock(comment)) { |
| 1423 var ws = countSpacesBetween(previousToken, comment); | 1429 var ws = countSpacesBetween(previousToken, comment); |
| 1424 // Preserve one space but no more | 1430 // Preserve one space but no more |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1494 var lastLine = | 1500 var lastLine = |
| 1495 lineInfo.getLocation(lastOffset).lineNumber; | 1501 lineInfo.getLocation(lastOffset).lineNumber; |
| 1496 var currentLine = | 1502 var currentLine = |
| 1497 lineInfo.getLocation(currentOffset).lineNumber; | 1503 lineInfo.getLocation(currentOffset).lineNumber; |
| 1498 return currentLine - lastLine; | 1504 return currentLine - lastLine; |
| 1499 } | 1505 } |
| 1500 | 1506 |
| 1501 String toString() => writer.toString(); | 1507 String toString() => writer.toString(); |
| 1502 | 1508 |
| 1503 } | 1509 } |
| OLD | NEW |