When run out of budget during not-yet-imported, mark incomplete.

Change-Id: Ic03bf7f20301f6790c3041b51cd4ef01208d840f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/225860
Reviewed-by: Samuel Rawlins <srawlins@google.com>
diff --git a/pkg/analysis_server/lib/src/domain_completion.dart b/pkg/analysis_server/lib/src/domain_completion.dart
index 661049b..71fad87 100644
--- a/pkg/analysis_server/lib/src/domain_completion.dart
+++ b/pkg/analysis_server/lib/src/domain_completion.dart
@@ -3,7 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:async';
-import 'dart:collection';
 
 import 'package:analysis_server/protocol/protocol.dart';
 import 'package:analysis_server/protocol/protocol_constants.dart';
@@ -76,7 +75,7 @@
     Set<ElementKind>? includedElementKinds,
     Set<String>? includedElementNames,
     List<IncludedSuggestionRelevanceTag>? includedSuggestionRelevanceTags,
-    Map<CompletionSuggestion, Uri>? notImportedSuggestions,
+    NotImportedSuggestions? notImportedSuggestions,
   }) async {
     //
     // Allow plugins to start computing fixes.
@@ -358,8 +357,7 @@
         );
         setNewRequest(completionRequest);
 
-        var notImportedSuggestions =
-            HashMap<CompletionSuggestion, Uri>.identity();
+        var notImportedSuggestions = NotImportedSuggestions();
         var suggestions = <CompletionSuggestion>[];
         try {
           suggestions = await computeSuggestions(
@@ -390,7 +388,6 @@
         });
 
         var lengthRestricted = suggestions.take(params.maxResults).toList();
-        var isIncomplete = lengthRestricted.length < suggestions.length;
         completionPerformance.suggestionCount = lengthRestricted.length;
 
         // Update `libraryUriToImportIndex` for not yet imported.
@@ -398,7 +395,7 @@
         var librariesToImport = <Uri, int>{};
         for (var i = 0; i < lengthRestricted.length; i++) {
           var suggestion = lengthRestricted[i];
-          var libraryToImport = notImportedSuggestions[suggestion];
+          var libraryToImport = notImportedSuggestions.map[suggestion];
           if (libraryToImport != null) {
             var index = librariesToImport.putIfAbsent(
               libraryToImport,
@@ -410,6 +407,9 @@
           }
         }
 
+        var isIncomplete = notImportedSuggestions.isIncomplete ||
+            lengthRestricted.length < suggestions.length;
+
         performance.run('sendResponse', (_) {
           server.sendResponse(
             CompletionGetSuggestions2Result(
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart b/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
index 4193128..c3a351b 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/completion_manager.dart
@@ -2,6 +2,8 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+import 'dart:collection';
+
 import 'package:analysis_server/src/protocol_server.dart';
 import 'package:analysis_server/src/provisional/completion/completion_core.dart';
 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
@@ -96,7 +98,7 @@
   /// that are not yet imported, but could be imported into the requested
   /// target. It is up to the client to make copies of [CompletionSuggestion]s
   /// with the import index property updated.
-  final Map<protocol.CompletionSuggestion, Uri>? notImportedSuggestions;
+  final NotImportedSuggestions? notImportedSuggestions;
 
   /// Initialize a newly created completion manager. The parameters
   /// [includedElementKinds], [includedElementNames], and
@@ -498,3 +500,12 @@
     }
   }
 }
+
+/// Information provided by [NotImportedContributor] in addition to suggestions.
+class NotImportedSuggestions {
+  final Map<protocol.CompletionSuggestion, Uri> map = HashMap.identity();
+
+  /// This flag is set to `true` if the contributor decided to stop before it
+  /// processed all available libraries, e.g. we ran out of budget.
+  bool isIncomplete = false;
+}
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/not_imported_contributor.dart b/pkg/analysis_server/lib/src/services/completion/dart/not_imported_contributor.dart
index 070294f..4046262 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart/not_imported_contributor.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart/not_imported_contributor.dart
@@ -4,7 +4,6 @@
 
 import 'dart:async';
 
-import 'package:analysis_server/src/protocol_server.dart' as protocol;
 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
 import 'package:analysis_server/src/services/completion/dart/completion_manager.dart';
 import 'package:analysis_server/src/services/completion/dart/extension_member_contributor.dart';
@@ -16,13 +15,13 @@
 /// A contributor of suggestions from not yet imported libraries.
 class NotImportedContributor extends DartCompletionContributor {
   final CompletionBudget budget;
-  final Map<protocol.CompletionSuggestion, Uri> notImportedSuggestions;
+  final NotImportedSuggestions additionalData;
 
   NotImportedContributor(
     DartCompletionRequest request,
     SuggestionBuilder builder,
     this.budget,
-    this.notImportedSuggestions,
+    this.additionalData,
   ) : super(request, builder);
 
   @override
@@ -37,6 +36,7 @@
     try {
       await analysisDriver.discoverAvailableFiles().timeout(budget.left);
     } on TimeoutException {
+      additionalData.isIncomplete = true;
       return;
     }
 
@@ -46,6 +46,7 @@
     var knownFiles = fsState.knownFiles.toList();
     for (var file in knownFiles) {
       if (budget.isEmpty) {
+        additionalData.isIncomplete = true;
         return;
       }
 
@@ -63,7 +64,7 @@
 
       builder.laterReplacesEarlier = false;
       builder.suggestionAdded = (suggestion) {
-        notImportedSuggestions[suggestion] = file.uri;
+        additionalData.map[suggestion] = file.uri;
       };
 
       if (request.includeIdentifiers) {
diff --git a/pkg/analysis_server/test/domain_completion_test.dart b/pkg/analysis_server/test/domain_completion_test.dart
index e3a94a0..2a94276 100644
--- a/pkg/analysis_server/test/domain_completion_test.dart
+++ b/pkg/analysis_server/test/domain_completion_test.dart
@@ -335,7 +335,7 @@
 ''');
 
     responseValidator
-      ..assertComplete()
+      ..assertIncomplete()
       ..assertReplacementBack(4)
       ..assertLibrariesToImport(includes: [], excludes: [
         'dart:core',