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

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

Issue 23589005: Formatter comment-handling improvements. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 years, 3 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
« no previous file with comments | « no previous file | pkg/analyzer_experimental/test/services/formatter_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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';
(...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after
146 146
147 /// Cached line info for calculating blank lines. 147 /// Cached line info for calculating blank lines.
148 LineInfo lineInfo; 148 LineInfo lineInfo;
149 149
150 /// Cached previous token for calculating preceding whitespace. 150 /// Cached previous token for calculating preceding whitespace.
151 Token previousToken; 151 Token previousToken;
152 152
153 /// A flag to indicate that a newline should be emitted before the next token. 153 /// A flag to indicate that a newline should be emitted before the next token.
154 bool needsNewline = false; 154 bool needsNewline = false;
155 155
156 /// A flag to indicate that user introduced newlines should be emitted before 156 /// Used for matching EOL comments
157 /// the next token. 157 final twoSlashes = new RegExp(r'//[^/]');
158 bool preservePrecedingNewlines = false; 158
159
160 /// Initialize a newly created visitor to write source code representing 159 /// Initialize a newly created visitor to write source code representing
161 /// the visited nodes to the given [writer]. 160 /// the visited nodes to the given [writer].
162 SourceVisitor(FormatterOptions options, this.lineInfo) : 161 SourceVisitor(FormatterOptions options, this.lineInfo) :
163 writer = new SourceWriter(indentCount: options.initialIndentationLevel, 162 writer = new SourceWriter(indentCount: options.initialIndentationLevel,
164 lineSeparator: options.lineSeparator); 163 lineSeparator: options.lineSeparator);
165 164
166 visitAdjacentStrings(AdjacentStrings node) { 165 visitAdjacentStrings(AdjacentStrings node) {
167 visitNodes(node.strings, separatedBy: space); 166 visitNodes(node.strings, separatedBy: space);
168 } 167 }
169 168
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after
315 // Cache EOF for leading whitespace calculation 314 // Cache EOF for leading whitespace calculation
316 var start = node.beginToken.previous; 315 var start = node.beginToken.previous;
317 if (start != null && start.type is TokenType_EOF) { 316 if (start != null && start.type is TokenType_EOF) {
318 previousToken = start; 317 previousToken = start;
319 } 318 }
320 319
321 var scriptTag = node.scriptTag; 320 var scriptTag = node.scriptTag;
322 var directives = node.directives; 321 var directives = node.directives;
323 visit(scriptTag); 322 visit(scriptTag);
324 323
325 preservePrecedingNewlines = true;
326 visitNodes(directives, separatedBy: newlines, followedBy: newlines); 324 visitNodes(directives, separatedBy: newlines, followedBy: newlines);
327 325
328 preservePrecedingNewlines = true;
329 visitNodes(node.declarations, separatedBy: newlines); 326 visitNodes(node.declarations, separatedBy: newlines);
330 327
331 // Handle trailing whitespace 328 // Handle trailing whitespace
332 preservePrecedingNewlines = true;
333 token(node.endToken /* EOF */); 329 token(node.endToken /* EOF */);
334 } 330 }
335 331
336 visitConditionalExpression(ConditionalExpression node) { 332 visitConditionalExpression(ConditionalExpression node) {
337 visit(node.condition); 333 visit(node.condition);
338 space(); 334 space();
339 token(node.question); 335 token(node.question);
340 space(); 336 space();
341 visit(node.thenExpression); 337 visit(node.thenExpression);
342 space(); 338 space();
(...skipping 683 matching lines...) Expand 10 before | Expand all | Expand 10 after
1026 /// Emit the given [modifier] if it's non null, followed by non-breaking 1022 /// Emit the given [modifier] if it's non null, followed by non-breaking
1027 /// whitespace. 1023 /// whitespace.
1028 modifier(Token modifier) { 1024 modifier(Token modifier) {
1029 token(modifier, followedBy: space); 1025 token(modifier, followedBy: space);
1030 } 1026 }
1031 1027
1032 1028
1033 /// Indicate that at least one newline should be emitted and possibly more 1029 /// Indicate that at least one newline should be emitted and possibly more
1034 /// if the source has them. 1030 /// if the source has them.
1035 newlines() { 1031 newlines() {
1036 preservePrecedingNewlines = true;
1037 needsNewline = true; 1032 needsNewline = true;
1038 } 1033 }
1039 1034
1040 token(Token token, {precededBy(), followedBy(), int minNewlines: 0}) { 1035 token(Token token, {precededBy(), followedBy(), int minNewlines: 0}) {
1041 if (token != null) { 1036 if (token != null) {
1042 if (needsNewline) { 1037 if (needsNewline) {
1043 minNewlines = max(1, minNewlines); 1038 minNewlines = max(1, minNewlines);
1044 } 1039 }
1045 if (preservePrecedingNewlines || minNewlines > 0) { 1040 var emitted = emitPrecedingCommentsAndNewlines(token, min: minNewlines);
1046 var emitted = emitPrecedingNewlines(token, min: minNewlines); 1041 if (emitted > 0) {
1047 preservePrecedingNewlines = false; 1042 needsNewline = false;
1048 if (emitted > 0) {
1049 needsNewline = false;
1050 }
1051 } 1043 }
1052 if (precededBy !=null) { 1044 if (precededBy !=null) {
1053 precededBy(); 1045 precededBy();
1054 } 1046 }
1055 emitBlockComments(token);
1056 append(token.lexeme); 1047 append(token.lexeme);
1057 if (followedBy != null) { 1048 if (followedBy != null) {
1058 followedBy(); 1049 followedBy();
1059 } 1050 }
1060 previousToken = token; 1051 previousToken = token;
1061 } 1052 }
1062 } 1053 }
1063 1054
1064 commaSeperator() { 1055 commaSeperator() {
1065 comma(); 1056 comma();
(...skipping 25 matching lines...) Expand all
1091 /// Indent. 1082 /// Indent.
1092 indent() { 1083 indent() {
1093 writer.indent(); 1084 writer.indent();
1094 } 1085 }
1095 1086
1096 /// Unindent 1087 /// Unindent
1097 unindent() { 1088 unindent() {
1098 writer.unindent(); 1089 writer.unindent();
1099 } 1090 }
1100 1091
1101 emitBlockComments(Token token) { 1092
1102 var comment = token.precedingComments; 1093 /// Emit any detected comments and newlines or a minimum as specified
1103 while (comment != null) { 1094 /// by [min].
1104 if (isBlock(comment)) { 1095 int emitPrecedingCommentsAndNewlines(Token token, {min: 0}) {
1105 append(comment.toString().trim()); 1096
1106 if (linesBetween(comment.end, token.offset) >= 1) {
1107 writer.newline();
1108 } else {
1109 space();
1110 }
1111 }
1112 comment = comment.next;
1113 }
1114 }
1115
1116 /// Emit any detected newlines or a minimum as specified by [min].
1117 int emitPrecedingNewlines(Token token, {min: 0}) {
1118 var comment = token.precedingComments; 1097 var comment = token.precedingComments;
1119 var currentToken = comment != null ? comment : token; 1098 var currentToken = comment != null ? comment : token;
1099
1100 //Handle EOLs before newlines
1101 if (isAtEOL(comment)) {
1102 emitComment(comment, previousToken);
1103 comment = comment.next;
1104 currentToken = comment != null ? comment : token;
1105 }
1106
1120 var lines = max(min, countNewlinesBetween(previousToken, currentToken)); 1107 var lines = max(min, countNewlinesBetween(previousToken, currentToken));
1121 writer.newlines(lines); 1108 writer.newlines(lines);
1109
1110 var previousToken = currentToken.previous;
1111
1112 while (comment != null) {
1122 1113
1123 while (comment != null) { 1114 emitComment(comment, previousToken);
1124 if (!isBlock(comment)) { 1115
1125 append(comment.toString().trim()); 1116 var nextToken = comment.next != null ? comment.next : token;
1126 var nextToken = comment.next != null ? comment.next : token; 1117 var newlines = calculateNewlinesBetweenComments(comment, nextToken);
1127 var postCommentNewlines = 1118 if (newlines > 0) {
1128 max(1, countNewlinesBetween(comment, nextToken)); 1119 writer.newlines(newlines);
1129 writer.newlines(postCommentNewlines); 1120 lines += newlines;
1130 lines += postCommentNewlines; 1121 } else if (!isEOF(token)) {
1122 space();
1131 } 1123 }
1124
1125 previousToken = comment;
1132 comment = comment.next; 1126 comment = comment.next;
1133 } 1127 }
1134 1128
1135 previousToken = token; 1129 previousToken = token;
1136 return lines; 1130 return lines;
1137 } 1131 }
1138 1132
1133 /// Test if this [comment] is at the end of a line.
1134 bool isAtEOL(Token comment) =>
1135 comment != null && comment.toString().trim().startsWith(twoSlashes) &&
1136 sameLine(comment, previousToken);
1137
1138 /// Emit this [comment], inserting leading whitespace if appropriate.
1139 emitComment(Token comment, Token previousToken) {
1140 if (!writer.currentLine.isWhitespace() && !isBlock(comment)) {
1141 var ws = countSpacesBetween(previousToken, comment);
1142 // Preserve one space but no more
1143 if (ws > 0) {
1144 space();
1145 }
1146 }
1147
1148 append(comment.toString().trim());
1149 }
1150
1151 /// Test if this token is an EOF token.
1152 bool isEOF(Token token) => token.type == TokenType.EOF;
1153
1154 /// Count spaces between these tokens. Tokens on different lines return 0.
1155 int countSpacesBetween(Token last, Token current) => isEOF(last) ||
1156 countNewlinesBetween(last, current) > 0 ? 0 : current.offset - last.end;
1157
1139 /// Count the blanks between these two nodes. 1158 /// Count the blanks between these two nodes.
1140 int countBlankLinesBetween(ASTNode lastNode, ASTNode currentNode) => 1159 int countBlankLinesBetween(ASTNode lastNode, ASTNode currentNode) =>
1141 countNewlinesBetween(lastNode.endToken, currentNode.beginToken); 1160 countNewlinesBetween(lastNode.endToken, currentNode.beginToken);
1142 1161
1143 /// Count newlines preceeding this [node]. 1162 /// Count newlines preceeding this [node].
1144 int countPrecedingNewlines(ASTNode node) => 1163 int countPrecedingNewlines(ASTNode node) =>
1145 countNewlinesBetween(node.beginToken.previous, node.beginToken); 1164 countNewlinesBetween(node.beginToken.previous, node.beginToken);
1146 1165
1147 /// Count newlines succeeding this [node]. 1166 /// Count newlines succeeding this [node].
1148 int countSucceedingNewlines(ASTNode node) => node == null ? 0 : 1167 int countSucceedingNewlines(ASTNode node) => node == null ? 0 :
1149 countNewlinesBetween(node.endToken, node.endToken.next); 1168 countNewlinesBetween(node.endToken, node.endToken.next);
1150 1169
1151 /// Count the blanks between these two nodes. 1170 /// Count the blanks between these two tokens.
1152 int countNewlinesBetween(Token last, Token current) { 1171 int countNewlinesBetween(Token last, Token current) {
1153 if (last == null || current == null) { 1172 if (last == null || current == null) {
1154 return 0; 1173 return 0;
1155 } 1174 }
1156 1175
1157 return linesBetween(last.end - 1, current.offset); 1176 return linesBetween(last.end - 1, current.offset);
1158 } 1177 }
1178
1179 /// Calculate the newlines that should separate these comments.
1180 int calculateNewlinesBetweenComments(Token last, Token current) {
1181 // Insist on a newline after doc comments or single line comments
1182 // (NOTE that EOL comments have already been processed).
1183 if (isOldSingleLineDocComment(last) || isSingleLineComment(last)) {
1184 return max(1, countNewlinesBetween(last, current));
1185 } else {
1186 return countNewlinesBetween(last, current);
1187 }
1188 }
1189
1190 /// Single line multi-line comments (e.g., '/** like this */').
1191 bool isOldSingleLineDocComment(Token comment) =>
1192 comment.lexeme.startsWith(r'/**') && singleLine(comment);
1193
1194 /// Test if this [token] spans just one line.
1195 bool singleLine(Token token) => linesBetween(token.offset, token.end) < 1;
1159 1196
1160 /// Test if this [comment] is a block comment. 1197 /// Test if token [first] is on the same line as [second].
1198 bool sameLine(Token first, Token second) =>
1199 countNewlinesBetween(first, second) == 0;
1200
1201 /// Test if this is a multi-line [comment] (e.g., '/* ...' or '/** ...')
1202 bool isMultiLineComment(Token comment) =>
1203 comment.type == TokenType.MULTI_LINE_COMMENT;
1204
1205 /// Test if this is a single-line [comment] (e.g., '// ...')
1206 bool isSingleLineComment(Token comment) =>
1207 comment.type == TokenType.SINGLE_LINE_COMMENT;
1208
1209 /// Test if this [comment] is a block comment (e.g., '/* like this */')..
1161 bool isBlock(Token comment) => 1210 bool isBlock(Token comment) =>
1162 comment.type != TokenType.SINGLE_LINE_COMMENT && 1211 isMultiLineComment(comment) && singleLine(comment);
1163 linesBetween(comment.offset, comment.end) < 1;
1164 1212
1165 /// Count the lines between two offsets. 1213 /// Count the lines between two offsets.
1166 int linesBetween(int lastOffset, int currentOffset) { 1214 int linesBetween(int lastOffset, int currentOffset) {
1167 var lastLine = 1215 var lastLine =
1168 lineInfo.getLocation(lastOffset).lineNumber; 1216 lineInfo.getLocation(lastOffset).lineNumber;
1169 var currentLine = 1217 var currentLine =
1170 lineInfo.getLocation(currentOffset).lineNumber; 1218 lineInfo.getLocation(currentOffset).lineNumber;
1171 return currentLine - lastLine; 1219 return currentLine - lastLine;
1172 } 1220 }
1173 1221
1174 String toString() => writer.toString(); 1222 String toString() => writer.toString();
1175 1223
1176 } 1224 }
OLDNEW
« no previous file with comments | « no previous file | pkg/analyzer_experimental/test/services/formatter_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698