[analysis_server] Simplify ImportOrganizer to use a single range for each directive

This should be a no-op and existing tests pass. This is a step towards extracting code that computes a range for removing/sorting directives that could be used elsewhere such as the RemoveUnusedImport() fix.

Change-Id: Ib43b57bb1e34b817cb3771b60c65ad9ba2fe2919
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/252142
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/lib/src/services/correction/organize_imports.dart b/pkg/analysis_server/lib/src/services/correction/organize_imports.dart
index 30ace8f..c0bcd6e 100644
--- a/pkg/analysis_server/lib/src/services/correction/organize_imports.dart
+++ b/pkg/analysis_server/lib/src/services/correction/organize_imports.dart
@@ -72,24 +72,25 @@
     var lineInfo = unit.lineInfo;
     var hasLibraryDirective = false;
     var directives = <_DirectiveInfo>[];
-    // Track the end offset of any library-level comment/annotations that should
-    // remain at the top of the file regardless of whether it was attached to a
-    // directive that's moved/removed.
-    // Code up to this offset will be excluded from the comment/docs/annotation
-    // text for the computed DirectiveInfo and also its range for replacement
-    // in the document.
-    int? libraryDocsAndAnnotationsEndOffset;
     for (var directive in unit.directives) {
       if (directive is LibraryDirective) {
         hasLibraryDirective = true;
       }
       if (directive is UriBasedDirective) {
+        // Track the end offset of any library-level comment/annotations that should
+        // remain at the top of the file regardless of whether it was attached to a
+        // directive that's moved/removed.
+        // Code up to this offset will be excluded from the comment/docs/annotation
+        // text for the computed DirectiveInfo and also its range for replacement
+        // in the document.
+        int? libraryDocsAndAnnotationsEndOffset;
         var priority = _getDirectivePriority(directive);
         if (priority != null) {
           var offset = directive.offset;
           var end = directive.end;
 
-          final isPseudoLibraryDirective = directive == unit.directives.first;
+          final isPseudoLibraryDirective =
+              !hasLibraryDirective && directive == unit.directives.first;
           Annotation? lastLibraryAnnotation;
           if (isPseudoLibraryDirective) {
             // Find the last library-level annotation that does not come
@@ -99,10 +100,17 @@
             lastLibraryAnnotation = directive.metadata
                 .takeWhile(_isLibraryTargetAnnotation)
                 .lastOrNull;
-            if (lastLibraryAnnotation != null) {
-              libraryDocsAndAnnotationsEndOffset =
-                  lineInfo.getOffsetOfLineAfter(lastLibraryAnnotation.end);
-              // In the case of a blank line after the last library annotation
+
+            // If there is no annotation, use the end of the doc text (since the
+            // doc text is considered library-level here).
+            libraryDocsAndAnnotationsEndOffset = lastLibraryAnnotation?.end ??
+                directive.documentationComment?.end;
+
+            // Fix up the offset to be after the line end.
+            if (libraryDocsAndAnnotationsEndOffset != null) {
+              libraryDocsAndAnnotationsEndOffset = lineInfo
+                  .getOffsetOfLineAfter(libraryDocsAndAnnotationsEndOffset);
+              // In the case of a blank line after the annotation/doc text
               // we should include that in the library part. Otherwise it will
               // be included in the top of the following directive and may
               // result in an extra blank line in the annotation block if it
@@ -128,80 +136,20 @@
               : null;
           final trailingComment = getTrailingComment(unit, directive, lineInfo);
 
-          /// Computes the offset to use for the start of directive-specific
-          /// code below taking into account code already included by
-          /// [libraryDocsAndAnnotationsEndOffset].
-          final clampedOffset = libraryDocsAndAnnotationsEndOffset == null
-              ? (int offset) => offset
-              : (int offset) =>
-                  math.max(libraryDocsAndAnnotationsEndOffset!, offset);
-
-          String? leadingCommentText;
           if (leadingComment != null && leadingToken != null) {
-            offset = clampedOffset(leadingComment.offset);
-            leadingCommentText = code.substring(
-              offset,
-              clampedOffset(leadingToken.offset),
-            );
+            offset = libraryDocsAndAnnotationsEndOffset != null
+                ? math.max(
+                    libraryDocsAndAnnotationsEndOffset, leadingComment.offset)
+                : leadingComment.offset;
           }
-          String? trailingCommentText;
           if (trailingComment != null) {
-            trailingCommentText =
-                code.substring(directive.end, trailingComment.end);
             end = trailingComment.end;
           }
-          String? documentationText;
-          var documentationComment = directive.documentationComment;
-          if (documentationComment != null) {
-            documentationText = code.substring(
-              clampedOffset(documentationComment.offset),
-              clampedOffset(documentationComment.end),
-            );
-          }
-          String? annotationText;
-          String? postAnnotationCommentText;
-          var beginToken = directive.metadata.beginToken;
-          var endToken = directive.metadata.endToken;
-          if (beginToken != null && endToken != null) {
-            var annotationOffset = clampedOffset(beginToken.offset);
-            var annotationEnd = clampedOffset(endToken.end);
-            if (annotationOffset != annotationEnd) {
-              annotationText = code.substring(
-                annotationOffset,
-                annotationEnd,
-              );
-            }
-            // Capture text between the end of the annotation and the directive
-            // text as there may be end-of line or line comments between.
-            // If not, this will capture the newline between the two, as it
-            // cannot be assumed there is a newline after annotationText because
-            // of the possibility of comments.
-            if (annotationEnd <
-                directive.firstTokenAfterCommentAndMetadata.offset) {
-              postAnnotationCommentText = code.substring(annotationEnd,
-                  directive.firstTokenAfterCommentAndMetadata.offset);
-            }
-          }
-          var text = code.substring(
-              directive.firstTokenAfterCommentAndMetadata.offset,
-              directive.end);
-          var uriContent = directive.uri.stringValue ?? '';
+          offset = libraryDocsAndAnnotationsEndOffset ?? offset;
+          final text = code.substring(offset, end);
+          final uriContent = directive.uri.stringValue ?? '';
           directives.add(
-            _DirectiveInfo(
-              directive,
-              priority,
-              leadingCommentText,
-              documentationText,
-              annotationText,
-              postAnnotationCommentText,
-              uriContent,
-              trailingCommentText,
-              isPseudoLibraryDirective
-                  ? (libraryDocsAndAnnotationsEndOffset ?? offset)
-                  : offset,
-              end,
-              text,
-            ),
+            _DirectiveInfo(directive, priority, uriContent, offset, end, text),
           );
         }
       }
@@ -213,24 +161,12 @@
     var firstDirectiveOffset = directives.first.offset;
     var lastDirectiveEnd = directives.last.end;
 
-    // Without a library directive, the library comment is the comment of the
-    // first directive.
-    _DirectiveInfo? libraryDocumentationDirective;
-    if (!hasLibraryDirective && directives.isNotEmpty) {
-      libraryDocumentationDirective = directives.first;
-    }
-
     // sort
     directives.sort();
     // append directives with grouping
     String directivesCode;
     {
       var sb = StringBuffer();
-      if (libraryDocumentationDirective != null &&
-          libraryDocumentationDirective.documentationText != null) {
-        sb.write(libraryDocumentationDirective.documentationText);
-        sb.write(endOfLine);
-      }
       _DirectivePriority? currentPriority;
       for (var directiveInfo in directives) {
         if (!hasUnresolvedIdentifierError) {
@@ -245,24 +181,7 @@
           }
           currentPriority = directiveInfo.priority;
         }
-        if (directiveInfo.leadingCommentText != null) {
-          sb.write(directiveInfo.leadingCommentText);
-        }
-        if (directiveInfo != libraryDocumentationDirective &&
-            directiveInfo.documentationText != null) {
-          sb.write(directiveInfo.documentationText);
-          sb.write(endOfLine);
-        }
-        if (directiveInfo.annotationText != null) {
-          sb.write(directiveInfo.annotationText);
-        }
-        if (directiveInfo.postAnnotationCommentText != null) {
-          sb.write(directiveInfo.postAnnotationCommentText);
-        }
         sb.write(directiveInfo.text);
-        if (directiveInfo.trailingCommentText != null) {
-          sb.write(directiveInfo.trailingCommentText);
-        }
         sb.write(endOfLine);
       }
       directivesCode = sb.toString();
@@ -400,12 +319,7 @@
 class _DirectiveInfo implements Comparable<_DirectiveInfo> {
   final UriBasedDirective directive;
   final _DirectivePriority priority;
-  final String? leadingCommentText;
-  final String? documentationText;
-  final String? annotationText;
-  final String? postAnnotationCommentText;
   final String uri;
-  final String? trailingCommentText;
 
   /// The offset of the first token, usually the keyword but may include leading comments.
   final int offset;
@@ -419,12 +333,7 @@
   _DirectiveInfo(
     this.directive,
     this.priority,
-    this.leadingCommentText,
-    this.documentationText,
-    this.annotationText,
-    this.postAnnotationCommentText,
     this.uri,
-    this.trailingCommentText,
     this.offset,
     this.end,
     this.text,