Skip to content

Commit 2e838b7

Browse files
author
Dart CI
committed
Version 2.10.0-107.0.dev
Merge commit 'd3b0979b04f594f93ad49990e51d06314c6ab816' into 'dev'
2 parents 67e447a + d3b0979 commit 2e838b7

File tree

8 files changed

+416
-278
lines changed

8 files changed

+416
-278
lines changed

pkg/analysis_server/lib/src/services/correction/organize_imports.dart

Lines changed: 139 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'package:analysis_server/src/utilities/strings.dart';
66
import 'package:analyzer/dart/ast/ast.dart';
77
import 'package:analyzer/dart/ast/token.dart';
88
import 'package:analyzer/error/error.dart';
9+
import 'package:analyzer/source/line_info.dart';
910
import 'package:analyzer/src/error/codes.dart';
1011
import 'package:analyzer_plugin/protocol/protocol_common.dart'
1112
hide AnalysisError, Element;
@@ -58,63 +59,59 @@ class ImportOrganizer {
5859
/// Organize all [Directive]s.
5960
void _organizeDirectives() {
6061
var lineInfo = unit.lineInfo;
62+
var hasLibraryDirective = false;
6163
var directives = <_DirectiveInfo>[];
6264
for (var directive in unit.directives) {
65+
if (directive is LibraryDirective) {
66+
hasLibraryDirective = true;
67+
}
6368
if (directive is UriBasedDirective) {
6469
var priority = getDirectivePriority(directive);
6570
if (priority != null) {
6671
var offset = directive.offset;
67-
if (directive.beginToken.precedingComments != null) {
68-
var firstComment = directive.beginToken.precedingComments;
69-
var comment = firstComment;
70-
// Don't connect comments that have a blank line between them
71-
while (comment.next != null) {
72-
var currentLine = lineInfo.getLocation(comment.offset).lineNumber;
73-
var nextLine =
74-
lineInfo.getLocation(comment.next.offset).lineNumber;
75-
if (nextLine - currentLine > 1) {
76-
firstComment = comment.next;
77-
}
72+
var end = directive.end;
7873

79-
comment = comment.next;
80-
}
81-
// Check if the comment is the first comment in the document
82-
if (firstComment != unit.beginToken.precedingComments) {
83-
var previousDirectiveLine = lineInfo
84-
.getLocation(directive.beginToken.previous.end)
85-
.lineNumber;
74+
final leadingComment = getLeadingComment(unit, directive, lineInfo);
75+
final trailingComment =
76+
getTrailingComment(unit, directive, lineInfo, end);
8677

87-
// Skip over any comments on the same line as the previous directive
88-
// as they will be attached to the end of it.
89-
var comment = firstComment;
90-
while (comment != null &&
91-
previousDirectiveLine ==
92-
lineInfo.getLocation(comment.offset).lineNumber) {
93-
comment = comment.next;
94-
}
95-
if (comment != null) {
96-
offset = comment.offset;
97-
}
98-
}
78+
String leadingCommentText;
79+
if (leadingComment != null) {
80+
leadingCommentText =
81+
code.substring(leadingComment.offset, directive.offset);
82+
offset = leadingComment.offset;
9983
}
100-
101-
var end = directive.end;
102-
var line = lineInfo.getLocation(end).lineNumber;
103-
Token comment = directive.endToken.next.precedingComments;
104-
while (comment != null) {
105-
if (lineInfo.getLocation(comment.offset).lineNumber == line) {
106-
end = comment.end;
107-
}
108-
comment = comment.next;
84+
String trailingCommentText;
85+
if (trailingComment != null) {
86+
trailingCommentText =
87+
code.substring(directive.end, trailingComment.end);
88+
end = trailingComment.end;
10989
}
110-
111-
var text = code.substring(offset, end);
90+
String documentationText;
91+
if (directive.documentationComment != null) {
92+
documentationText = code.substring(
93+
directive.documentationComment.offset,
94+
directive.documentationComment.end);
95+
}
96+
String annotationText;
97+
if (directive.metadata.beginToken != null) {
98+
annotationText = code.substring(
99+
directive.metadata.beginToken.offset,
100+
directive.metadata.endToken.end);
101+
}
102+
var text = code.substring(
103+
directive.firstTokenAfterCommentAndMetadata.offset,
104+
directive.end);
112105
var uriContent = directive.uri.stringValue;
113106
directives.add(
114107
_DirectiveInfo(
115108
directive,
116109
priority,
110+
leadingCommentText,
111+
documentationText,
112+
annotationText,
117113
uriContent,
114+
trailingCommentText,
118115
offset,
119116
end,
120117
text,
@@ -129,13 +126,25 @@ class ImportOrganizer {
129126
}
130127
var firstDirectiveOffset = directives.first.offset;
131128
var lastDirectiveEnd = directives.last.end;
129+
130+
// Without a library directive, the library comment is the comment of the
131+
// first directive.
132+
_DirectiveInfo libraryDocumentationDirective;
133+
if (!hasLibraryDirective && directives.isNotEmpty) {
134+
libraryDocumentationDirective = directives.first;
135+
}
136+
132137
// sort
133138
directives.sort();
134139
// append directives with grouping
135140
String directivesCode;
136141
{
137142
var sb = StringBuffer();
138-
_DirectivePriority currentPriority;
143+
if (libraryDocumentationDirective?.documentationText != null) {
144+
sb.write(libraryDocumentationDirective.documentationText);
145+
sb.write(endOfLine);
146+
}
147+
var currentPriority = directives.first.priority;
139148
for (var directiveInfo in directives) {
140149
if (!hasUnresolvedIdentifierError) {
141150
var directive = directiveInfo.directive;
@@ -144,12 +153,25 @@ class ImportOrganizer {
144153
}
145154
}
146155
if (currentPriority != directiveInfo.priority) {
147-
if (sb.length != 0) {
148-
sb.write(endOfLine);
149-
}
156+
sb.write(endOfLine);
150157
currentPriority = directiveInfo.priority;
151158
}
159+
if (directiveInfo.leadingCommentText != null) {
160+
sb.write(directiveInfo.leadingCommentText);
161+
}
162+
if (directiveInfo != libraryDocumentationDirective &&
163+
directiveInfo.documentationText != null) {
164+
sb.write(directiveInfo.documentationText);
165+
sb.write(endOfLine);
166+
}
167+
if (directiveInfo.annotationText != null) {
168+
sb.write(directiveInfo.annotationText);
169+
sb.write(endOfLine);
170+
}
152171
sb.write(directiveInfo.text);
172+
if (directiveInfo.trailingCommentText != null) {
173+
sb.write(directiveInfo.trailingCommentText);
174+
}
153175
sb.write(endOfLine);
154176
}
155177
directivesCode = sb.toString();
@@ -162,7 +184,7 @@ class ImportOrganizer {
162184
}
163185

164186
static _DirectivePriority getDirectivePriority(UriBasedDirective directive) {
165-
var uriContent = directive.uri.stringValue;
187+
var uriContent = directive.uri.stringValue ?? '';
166188
if (directive is ImportDirective) {
167189
if (uriContent.startsWith('dart:')) {
168190
return _DirectivePriority.IMPORT_SDK;
@@ -199,26 +221,94 @@ class ImportOrganizer {
199221
return '\n';
200222
}
201223
}
224+
225+
/// Gets the first comment token considered to be the leading comment for this
226+
/// directive.
227+
///
228+
/// Leading comments for the first directive in a file are considered library
229+
/// comments and not returned unless they contain blank lines, in which case
230+
/// only the last part of the comment will be returned.
231+
static Token getLeadingComment(
232+
CompilationUnit unit, UriBasedDirective directive, LineInfo lineInfo) {
233+
if (directive.beginToken.precedingComments == null) {
234+
return null;
235+
}
236+
237+
var firstComment = directive.beginToken.precedingComments;
238+
var comment = firstComment;
239+
// Don't connect comments that have a blank line between them
240+
while (comment.next != null) {
241+
var currentLine = lineInfo.getLocation(comment.offset).lineNumber;
242+
var nextLine = lineInfo.getLocation(comment.next.offset).lineNumber;
243+
if (nextLine - currentLine > 1) {
244+
firstComment = comment.next;
245+
}
246+
comment = comment.next;
247+
}
248+
249+
// Check if the comment is the first comment in the document
250+
if (firstComment != unit.beginToken.precedingComments) {
251+
var previousDirectiveLine =
252+
lineInfo.getLocation(directive.beginToken.previous.end).lineNumber;
253+
254+
// Skip over any comments on the same line as the previous directive
255+
// as they will be attached to the end of it.
256+
var comment = firstComment;
257+
while (comment != null &&
258+
previousDirectiveLine ==
259+
lineInfo.getLocation(comment.offset).lineNumber) {
260+
comment = comment.next;
261+
}
262+
return comment;
263+
}
264+
return null;
265+
}
266+
267+
/// Gets the last comment token considered to be the trailing comment for this
268+
/// directive.
269+
///
270+
/// To be considered a trailing comment, the comment must be on the same line
271+
/// as the directive.
272+
static Token getTrailingComment(CompilationUnit unit,
273+
UriBasedDirective directive, LineInfo lineInfo, int end) {
274+
var line = lineInfo.getLocation(end).lineNumber;
275+
Token comment = directive.endToken.next.precedingComments;
276+
while (comment != null) {
277+
if (lineInfo.getLocation(comment.offset).lineNumber == line) {
278+
return comment;
279+
}
280+
comment = comment.next;
281+
}
282+
return null;
283+
}
202284
}
203285

204286
class _DirectiveInfo implements Comparable<_DirectiveInfo> {
205287
final UriBasedDirective directive;
206288
final _DirectivePriority priority;
289+
final String leadingCommentText;
290+
final String documentationText;
291+
final String annotationText;
207292
final String uri;
293+
final String trailingCommentText;
208294

209-
/// The offset of the first token, usually the keyword.
295+
/// The offset of the first token, usually the keyword but may include leading comments.
210296
final int offset;
211297

212-
/// The offset after the least token, including the end-of-line comment.
298+
/// The offset after the last token, including the end-of-line comment.
213299
final int end;
214300

215-
/// The text between [offset] and [end].
301+
/// The text excluding comments, documentation and annotations.
216302
final String text;
217303

218304
_DirectiveInfo(
219305
this.directive,
220306
this.priority,
307+
this.leadingCommentText,
308+
this.documentationText,
309+
this.annotationText,
221310
this.uri,
311+
this.trailingCommentText,
222312
this.offset,
223313
this.end,
224314
this.text,

0 commit comments

Comments
 (0)