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

Side by Side Diff: pkg/analyzer_experimental/lib/src/services/formatter_impl.dart

Issue 26280005: Formatter code transform bit-flip support. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
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
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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698