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