| 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'; |
| (...skipping 177 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 188 /// Verify that these two token streams are equal. | 188 /// Verify that these two token streams are equal. |
| 189 verifyEquals() { | 189 verifyEquals() { |
| 190 while (!isEOF(token1)) { | 190 while (!isEOF(token1)) { |
| 191 checkPrecedingComments(); | 191 checkPrecedingComments(); |
| 192 if (!checkTokens()) { | 192 if (!checkTokens()) { |
| 193 throwNotEqualException(token1, token2); | 193 throwNotEqualException(token1, token2); |
| 194 } | 194 } |
| 195 advance(); | 195 advance(); |
| 196 | 196 |
| 197 } | 197 } |
| 198 if (!isEOF(token2)) { | 198 // TODO(pquitslund): consider a better way to notice trailing synthetics |
| 199 if (!isEOF(token2) && |
| 200 !(isCLOSE_CURLY_BRACKET(token2) && isEOF(token2.next))) { |
| 199 throw new FormatterException( | 201 throw new FormatterException( |
| 200 'Expected "EOF" but got "${token2}".'); | 202 'Expected "EOF" but got "${token2}".'); |
| 201 } | 203 } |
| 202 } | 204 } |
| 203 | 205 |
| 204 checkPrecedingComments() { | 206 checkPrecedingComments() { |
| 205 var comment1 = token1.precedingComments; | 207 var comment1 = token1.precedingComments; |
| 206 var comment2 = token2.precedingComments; | 208 var comment2 = token2.precedingComments; |
| 207 while (comment1 != null) { | 209 while (comment1 != null) { |
| 208 if (comment2 == null) { | 210 if (comment2 == null) { |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 254 return true; | 256 return true; |
| 255 } | 257 } |
| 256 } | 258 } |
| 257 // '>' '>' => '>>' | 259 // '>' '>' => '>>' |
| 258 if (isGT(token1) && isGT(token1.next)) { | 260 if (isGT(token1) && isGT(token1.next)) { |
| 259 if (isGT_GT(token2)) { | 261 if (isGT_GT(token2)) { |
| 260 token1 = token1.next; | 262 token1 = token1.next; |
| 261 return true; | 263 return true; |
| 262 } | 264 } |
| 263 } | 265 } |
| 266 // Advance past synthetic { } tokens |
| 267 if (isOPEN_CURLY_BRACKET(token2) || isCLOSE_CURLY_BRACKET(token2)) { |
| 268 token2 = token2.next; |
| 269 return checkTokens(); |
| 270 } |
| 264 | 271 |
| 265 return false; | 272 return false; |
| 266 } | 273 } |
| 267 | 274 |
| 268 } | 275 } |
| 269 | 276 |
| 270 // Cached parser for testing token types. | 277 // Cached parser for testing token types. |
| 271 final tokenTester = new Parser(null,null); | 278 final tokenTester = new Parser(null,null); |
| 272 | 279 |
| 273 /// Test if this token is an EOF token. | 280 /// Test if this token is an EOF token. |
| 274 bool isEOF(Token token) => tokenIs(token, TokenType.EOF); | 281 bool isEOF(Token token) => tokenIs(token, TokenType.EOF); |
| 275 | 282 |
| 276 /// Test for token type. | 283 /// Test for token type. |
| 277 bool tokenIs(Token token, TokenType type) => | 284 bool tokenIs(Token token, TokenType type) => |
| 278 token != null && tokenTester.matches4(token, type); | 285 token != null && tokenTester.matches4(token, type); |
| 279 | 286 |
| 280 /// Test if this token is a GT token. | 287 /// Test if this token is a GT token. |
| 281 bool isGT(Token token) => tokenIs(token, TokenType.GT); | 288 bool isGT(Token token) => tokenIs(token, TokenType.GT); |
| 282 | 289 |
| 283 /// Test if this token is a GT_GT token. | 290 /// Test if this token is a GT_GT token. |
| 284 bool isGT_GT(Token token) => tokenIs(token, TokenType.GT_GT); | 291 bool isGT_GT(Token token) => tokenIs(token, TokenType.GT_GT); |
| 285 | 292 |
| 286 /// Test if this token is an INDEX token. | 293 /// Test if this token is an INDEX token. |
| 287 bool isINDEX(Token token) => tokenIs(token, TokenType.INDEX); | 294 bool isINDEX(Token token) => tokenIs(token, TokenType.INDEX); |
| 288 | 295 |
| 296 /// Test if this token is a OPEN_CURLY_BRACKET token. |
| 297 bool isOPEN_CURLY_BRACKET(Token token) => |
| 298 tokenIs(token, TokenType.OPEN_CURLY_BRACKET); |
| 299 |
| 300 /// Test if this token is a CLOSE_CURLY_BRACKET token. |
| 301 bool isCLOSE_CURLY_BRACKET(Token token) => |
| 302 tokenIs(token, TokenType.CLOSE_CURLY_BRACKET); |
| 303 |
| 289 /// Test if this token is a OPEN_SQUARE_BRACKET token. | 304 /// Test if this token is a OPEN_SQUARE_BRACKET token. |
| 290 bool isOPEN_SQ_BRACKET(Token token) => | 305 bool isOPEN_SQ_BRACKET(Token token) => |
| 291 tokenIs(token, TokenType.OPEN_SQUARE_BRACKET); | 306 tokenIs(token, TokenType.OPEN_SQUARE_BRACKET); |
| 292 | 307 |
| 293 /// Test if this token is a CLOSE_SQUARE_BRACKET token. | 308 /// Test if this token is a CLOSE_SQUARE_BRACKET token. |
| 294 bool isCLOSE_SQUARE_BRACKET(Token token) => | 309 bool isCLOSE_SQUARE_BRACKET(Token token) => |
| 295 tokenIs(token, TokenType.CLOSE_SQUARE_BRACKET); | 310 tokenIs(token, TokenType.CLOSE_SQUARE_BRACKET); |
| 296 | 311 |
| 312 |
| 297 /// An AST visitor that drives formatting heuristics. | 313 /// An AST visitor that drives formatting heuristics. |
| 298 class SourceVisitor implements ASTVisitor { | 314 class SourceVisitor implements ASTVisitor { |
| 315 |
| 316 static final OPEN_CURLY = syntheticToken(TokenType.OPEN_CURLY_BRACKET, '{'); |
| 317 static final CLOSE_CURLY = syntheticToken(TokenType.CLOSE_CURLY_BRACKET, '}'); |
| 318 |
| 319 static const SYNTH_OFFSET = -13; |
| 320 |
| 321 static StringToken syntheticToken(TokenType type, String value) => |
| 322 new StringToken(type, value, SYNTH_OFFSET); |
| 323 |
| 324 static bool isSynthetic(Token token) => token.offset == SYNTH_OFFSET; |
| 299 | 325 |
| 300 /// The writer to which the source is to be written. | 326 /// The writer to which the source is to be written. |
| 301 final SourceWriter writer; | 327 final SourceWriter writer; |
| 302 | 328 |
| 303 /// Cached line info for calculating blank lines. | 329 /// Cached line info for calculating blank lines. |
| 304 LineInfo lineInfo; | 330 LineInfo lineInfo; |
| 305 | 331 |
| 306 /// Cached previous token for calculating preceding whitespace. | 332 /// Cached previous token for calculating preceding whitespace. |
| 307 Token previousToken; | 333 Token previousToken; |
| 308 | 334 |
| (...skipping 414 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 723 visitNode(node.returnType, followedBy: space); | 749 visitNode(node.returnType, followedBy: space); |
| 724 visit(node.identifier); | 750 visit(node.identifier); |
| 725 visit(node.parameters); | 751 visit(node.parameters); |
| 726 } | 752 } |
| 727 | 753 |
| 728 visitHideCombinator(HideCombinator node) { | 754 visitHideCombinator(HideCombinator node) { |
| 729 token(node.keyword); | 755 token(node.keyword); |
| 730 space(); | 756 space(); |
| 731 visitNodes(node.hiddenNames, separatedBy: commaSeperator); | 757 visitNodes(node.hiddenNames, separatedBy: commaSeperator); |
| 732 } | 758 } |
| 733 | 759 |
| 734 visitIfStatement(IfStatement node) { | 760 visitIfStatement(IfStatement node) { |
| 761 var hasElse = node.elseStatement != null; |
| 735 token(node.ifKeyword); | 762 token(node.ifKeyword); |
| 736 space(); | 763 space(); |
| 737 token(node.leftParenthesis); | 764 token(node.leftParenthesis); |
| 738 visit(node.condition); | 765 visit(node.condition); |
| 739 token(node.rightParenthesis); | 766 token(node.rightParenthesis); |
| 740 space(); | 767 space(); |
| 741 visit(node.thenStatement); | 768 if (hasElse) { |
| 742 //visitPrefixed(' else ', node.elseStatement); | 769 printAsBlock(node.thenStatement); |
| 743 if (node.elseStatement != null) { | |
| 744 space(); | 770 space(); |
| 745 token(node.elseKeyword); | 771 token(node.elseKeyword); |
| 746 space(); | 772 space(); |
| 747 visit(node.elseStatement); | 773 printAsBlock(node.elseStatement); |
| 774 } else { |
| 775 visit(node.thenStatement); |
| 748 } | 776 } |
| 749 } | 777 } |
| 750 | 778 |
| 751 visitImplementsClause(ImplementsClause node) { | 779 visitImplementsClause(ImplementsClause node) { |
| 752 token(node.keyword); | 780 token(node.keyword); |
| 753 space(); | 781 space(); |
| 754 visitNodes(node.interfaces, separatedBy: commaSeperator); | 782 visitNodes(node.interfaces, separatedBy: commaSeperator); |
| 755 } | 783 } |
| 756 | 784 |
| 757 visitImportDirective(ImportDirective node) { | 785 visitImportDirective(ImportDirective node) { |
| 758 token(node.keyword); | 786 token(node.keyword); |
| 759 space(); | 787 space(); |
| 760 visit(node.uri); | 788 visit(node.uri); |
| (...skipping 507 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1268 | 1296 |
| 1269 /// Indent. | 1297 /// Indent. |
| 1270 indent() { | 1298 indent() { |
| 1271 writer.indent(); | 1299 writer.indent(); |
| 1272 } | 1300 } |
| 1273 | 1301 |
| 1274 /// Unindent | 1302 /// Unindent |
| 1275 unindent() { | 1303 unindent() { |
| 1276 writer.unindent(); | 1304 writer.unindent(); |
| 1277 } | 1305 } |
| 1278 | 1306 |
| 1279 | 1307 /// Print this statement as if it were a block (e.g., surrounded by braces). |
| 1308 printAsBlock(Statement statement) { |
| 1309 if (statement is! Block) { |
| 1310 token(OPEN_CURLY); |
| 1311 indent(); |
| 1312 newlines(); |
| 1313 visit(statement); |
| 1314 newlines(); |
| 1315 unindent(); |
| 1316 token(CLOSE_CURLY); |
| 1317 } else { |
| 1318 visit(statement); |
| 1319 } |
| 1320 } |
| 1321 |
| 1280 /// Emit any detected comments and newlines or a minimum as specified | 1322 /// Emit any detected comments and newlines or a minimum as specified |
| 1281 /// by [min]. | 1323 /// by [min]. |
| 1282 int emitPrecedingCommentsAndNewlines(Token token, {min: 0}) { | 1324 int emitPrecedingCommentsAndNewlines(Token token, {min: 0}) { |
| 1283 | 1325 |
| 1284 var comment = token.precedingComments; | 1326 var comment = token.precedingComments; |
| 1285 var currentToken = comment != null ? comment : token; | 1327 var currentToken = comment != null ? comment : token; |
| 1286 | 1328 |
| 1287 //Handle EOLs before newlines | 1329 //Handle EOLs before newlines |
| 1288 if (isAtEOL(comment)) { | 1330 if (isAtEOL(comment)) { |
| 1289 emitComment(comment, previousToken); | 1331 emitComment(comment, previousToken); |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1354 /// Count newlines preceeding this [node]. | 1396 /// Count newlines preceeding this [node]. |
| 1355 int countPrecedingNewlines(ASTNode node) => | 1397 int countPrecedingNewlines(ASTNode node) => |
| 1356 countNewlinesBetween(node.beginToken.previous, node.beginToken); | 1398 countNewlinesBetween(node.beginToken.previous, node.beginToken); |
| 1357 | 1399 |
| 1358 /// Count newlines succeeding this [node]. | 1400 /// Count newlines succeeding this [node]. |
| 1359 int countSucceedingNewlines(ASTNode node) => node == null ? 0 : | 1401 int countSucceedingNewlines(ASTNode node) => node == null ? 0 : |
| 1360 countNewlinesBetween(node.endToken, node.endToken.next); | 1402 countNewlinesBetween(node.endToken, node.endToken.next); |
| 1361 | 1403 |
| 1362 /// Count the blanks between these two tokens. | 1404 /// Count the blanks between these two tokens. |
| 1363 int countNewlinesBetween(Token last, Token current) { | 1405 int countNewlinesBetween(Token last, Token current) { |
| 1364 if (last == null || current == null) { | 1406 if (last == null || current == null || isSynthetic(last)) { |
| 1365 return 0; | 1407 return 0; |
| 1366 } | 1408 } |
| 1367 | 1409 |
| 1368 return linesBetween(last.end - 1, current.offset); | 1410 return linesBetween(last.end - 1, current.offset); |
| 1369 } | 1411 } |
| 1370 | 1412 |
| 1371 /// Calculate the newlines that should separate these comments. | 1413 /// Calculate the newlines that should separate these comments. |
| 1372 int calculateNewlinesBetweenComments(Token last, Token current) { | 1414 int calculateNewlinesBetweenComments(Token last, Token current) { |
| 1373 // Insist on a newline after doc comments or single line comments | 1415 // Insist on a newline after doc comments or single line comments |
| 1374 // (NOTE that EOL comments have already been processed). | 1416 // (NOTE that EOL comments have already been processed). |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1407 var lastLine = | 1449 var lastLine = |
| 1408 lineInfo.getLocation(lastOffset).lineNumber; | 1450 lineInfo.getLocation(lastOffset).lineNumber; |
| 1409 var currentLine = | 1451 var currentLine = |
| 1410 lineInfo.getLocation(currentOffset).lineNumber; | 1452 lineInfo.getLocation(currentOffset).lineNumber; |
| 1411 return currentLine - lastLine; | 1453 return currentLine - lastLine; |
| 1412 } | 1454 } |
| 1413 | 1455 |
| 1414 String toString() => writer.toString(); | 1456 String toString() => writer.toString(); |
| 1415 | 1457 |
| 1416 } | 1458 } |
| OLD | NEW |