@@ -6,6 +6,7 @@ import 'package:analysis_server/src/utilities/strings.dart';
6
6
import 'package:analyzer/dart/ast/ast.dart' ;
7
7
import 'package:analyzer/dart/ast/token.dart' ;
8
8
import 'package:analyzer/error/error.dart' ;
9
+ import 'package:analyzer/source/line_info.dart' ;
9
10
import 'package:analyzer/src/error/codes.dart' ;
10
11
import 'package:analyzer_plugin/protocol/protocol_common.dart'
11
12
hide AnalysisError, Element;
@@ -58,63 +59,59 @@ class ImportOrganizer {
58
59
/// Organize all [Directive] s.
59
60
void _organizeDirectives () {
60
61
var lineInfo = unit.lineInfo;
62
+ var hasLibraryDirective = false ;
61
63
var directives = < _DirectiveInfo > [];
62
64
for (var directive in unit.directives) {
65
+ if (directive is LibraryDirective ) {
66
+ hasLibraryDirective = true ;
67
+ }
63
68
if (directive is UriBasedDirective ) {
64
69
var priority = getDirectivePriority (directive);
65
70
if (priority != null ) {
66
71
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;
78
73
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);
86
77
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;
99
83
}
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;
109
89
}
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);
112
105
var uriContent = directive.uri.stringValue;
113
106
directives.add (
114
107
_DirectiveInfo (
115
108
directive,
116
109
priority,
110
+ leadingCommentText,
111
+ documentationText,
112
+ annotationText,
117
113
uriContent,
114
+ trailingCommentText,
118
115
offset,
119
116
end,
120
117
text,
@@ -129,13 +126,25 @@ class ImportOrganizer {
129
126
}
130
127
var firstDirectiveOffset = directives.first.offset;
131
128
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
+
132
137
// sort
133
138
directives.sort ();
134
139
// append directives with grouping
135
140
String directivesCode;
136
141
{
137
142
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;
139
148
for (var directiveInfo in directives) {
140
149
if (! hasUnresolvedIdentifierError) {
141
150
var directive = directiveInfo.directive;
@@ -144,12 +153,25 @@ class ImportOrganizer {
144
153
}
145
154
}
146
155
if (currentPriority != directiveInfo.priority) {
147
- if (sb.length != 0 ) {
148
- sb.write (endOfLine);
149
- }
156
+ sb.write (endOfLine);
150
157
currentPriority = directiveInfo.priority;
151
158
}
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
+ }
152
171
sb.write (directiveInfo.text);
172
+ if (directiveInfo.trailingCommentText != null ) {
173
+ sb.write (directiveInfo.trailingCommentText);
174
+ }
153
175
sb.write (endOfLine);
154
176
}
155
177
directivesCode = sb.toString ();
@@ -162,7 +184,7 @@ class ImportOrganizer {
162
184
}
163
185
164
186
static _DirectivePriority getDirectivePriority (UriBasedDirective directive) {
165
- var uriContent = directive.uri.stringValue;
187
+ var uriContent = directive.uri.stringValue ?? '' ;
166
188
if (directive is ImportDirective ) {
167
189
if (uriContent.startsWith ('dart:' )) {
168
190
return _DirectivePriority .IMPORT_SDK ;
@@ -199,26 +221,94 @@ class ImportOrganizer {
199
221
return '\n ' ;
200
222
}
201
223
}
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
+ }
202
284
}
203
285
204
286
class _DirectiveInfo implements Comparable <_DirectiveInfo > {
205
287
final UriBasedDirective directive;
206
288
final _DirectivePriority priority;
289
+ final String leadingCommentText;
290
+ final String documentationText;
291
+ final String annotationText;
207
292
final String uri;
293
+ final String trailingCommentText;
208
294
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 .
210
296
final int offset;
211
297
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.
213
299
final int end;
214
300
215
- /// The text between [offset] and [end] .
301
+ /// The text excluding comments, documentation and annotations .
216
302
final String text;
217
303
218
304
_DirectiveInfo (
219
305
this .directive,
220
306
this .priority,
307
+ this .leadingCommentText,
308
+ this .documentationText,
309
+ this .annotationText,
221
310
this .uri,
311
+ this .trailingCommentText,
222
312
this .offset,
223
313
this .end,
224
314
this .text,
0 commit comments