Version 2.15.0-256.0.dev

Merge commit '626c8c5f847c8cd88542b244489819be0d4e0020' into 'dev'
diff --git a/pkg/analysis_server/lib/src/domain_completion.dart b/pkg/analysis_server/lib/src/domain_completion.dart
index 9ae5561..e798ddb 100644
--- a/pkg/analysis_server/lib/src/domain_completion.dart
+++ b/pkg/analysis_server/lib/src/domain_completion.dart
@@ -64,51 +64,38 @@
   /// automatically called when a client listens to the stream returned by
   /// [results]. Subclasses should override this method, append at least one
   /// result to the [controller], and close the controller stream once complete.
-  Future<CompletionResult> computeSuggestions(
-    OperationPerformanceImpl perf,
+  Future<List<CompletionSuggestion>> computeSuggestions(
+    OperationPerformanceImpl performance,
     DartCompletionRequest request,
-    CompletionGetSuggestionsParams params,
     Set<ElementKind>? includedElementKinds,
     Set<String>? includedElementNames,
     List<IncludedSuggestionRelevanceTag>? includedSuggestionRelevanceTags,
   ) async {
-    var file = params.file;
-    var offset = params.offset;
     //
     // Allow plugins to start computing fixes.
     //
-    Map<PluginInfo, Future<plugin.Response>>? pluginFutures;
-    plugin.CompletionGetSuggestionsParams? requestParams;
-    var driver = server.getAnalysisDriver(file);
-    if (driver != null) {
-      requestParams = plugin.CompletionGetSuggestionsParams(file, offset);
-      pluginFutures = server.pluginManager.broadcastRequest(
-        requestParams,
-        contextRoot: driver.analysisContext!.contextRoot,
-      );
-    }
+    var requestToPlugins = performance.run('askPlugins', (_) {
+      return _sendRequestToPlugins(request);
+    });
+
     //
     // Compute completions generated by server.
     //
     var suggestions = <CompletionSuggestion>[];
-    const COMPUTE_SUGGESTIONS_TAG = 'computeSuggestions';
-    await perf.runAsync(COMPUTE_SUGGESTIONS_TAG, (perf) async {
+    await performance.runAsync('computeSuggestions', (performance) async {
       var manager = DartCompletionManager(
         includedElementKinds: includedElementKinds,
         includedElementNames: includedElementNames,
         includedSuggestionRelevanceTags: includedSuggestionRelevanceTags,
       );
 
-      var contributorTag = 'computeSuggestions - ${manager.runtimeType}';
-      await perf.runAsync(contributorTag, (performance) async {
-        try {
-          suggestions.addAll(
-            await manager.computeSuggestions(request, performance),
-          );
-        } on AbortCompletion {
-          suggestions.clear();
-        }
-      });
+      try {
+        suggestions.addAll(
+          await manager.computeSuggestions(request, performance),
+        );
+      } on AbortCompletion {
+        suggestions.clear();
+      }
     });
     // TODO (danrubel) if request is obsolete (processAnalysisRequest returns
     // false) then send empty results
@@ -116,29 +103,13 @@
     //
     // Add the completions produced by plugins to the server-generated list.
     //
-    if (pluginFutures != null) {
-      var responses = await waitForResponses(pluginFutures,
-          requestParameters: requestParams, timeout: 100);
-      for (var response in responses) {
-        var result =
-            plugin.CompletionGetSuggestionsResult.fromResponse(response);
-        if (result.results.isNotEmpty) {
-          if (request.replacementOffset != result.replacementOffset &&
-              request.replacementLength != result.replacementLength) {
-            server.instrumentationService
-                .logError('Plugin completion-results dropped due to conflicting'
-                    ' replacement offset/length: ${result.toJson()}');
-            continue;
-          }
-          suggestions.addAll(result.results);
-        }
-      }
+    if (requestToPlugins != null) {
+      await performance.runAsync('waitForPlugins', (_) async {
+        await _addPluginSuggestions(requestToPlugins, suggestions);
+      });
     }
-    //
-    // Return the result.
-    //
-    return CompletionResult(
-        request.replacementOffset, request.replacementLength, suggestions);
+
+    return suggestions;
   }
 
   /// Return the suggestions that should be presented in the YAML [file] at the
@@ -369,10 +340,9 @@
 
       // Compute suggestions in the background
       try {
-        var result = await computeSuggestions(
+        var suggestions = await computeSuggestions(
           perf,
           completionRequest,
-          params,
           includedElementKinds,
           includedElementNames,
           includedSuggestionRelevanceTags,
@@ -396,9 +366,9 @@
         perf.run(SEND_NOTIFICATION_TAG, (_) {
           sendCompletionNotification(
             completionId,
-            result.replacementOffset,
-            result.replacementLength,
-            result.suggestions,
+            completionRequest.replacementOffset,
+            completionRequest.replacementLength,
+            suggestions,
             libraryFile,
             includedSuggestionSets,
             includedElementKinds?.toList(),
@@ -406,7 +376,7 @@
           );
         });
 
-        performance.suggestionCount = result.suggestions.length;
+        performance.suggestionCount = suggestions.length;
       } finally {
         ifMatchesRequestClear(completionRequest);
       }
@@ -495,6 +465,55 @@
       _currentRequest = null;
     }
   }
+
+  /// Add the completions produced by plugins to the server-generated list.
+  Future<void> _addPluginSuggestions(
+    _RequestToPlugins requestToPlugins,
+    List<CompletionSuggestion> suggestions,
+  ) async {
+    var responses = await waitForResponses(
+      requestToPlugins.futures,
+      requestParameters: requestToPlugins.parameters,
+      timeout: 100,
+    );
+    for (var response in responses) {
+      var result = plugin.CompletionGetSuggestionsResult.fromResponse(response);
+      if (result.results.isNotEmpty) {
+        var completionRequest = requestToPlugins.completionRequest;
+        if (completionRequest.replacementOffset != result.replacementOffset &&
+            completionRequest.replacementLength != result.replacementLength) {
+          server.instrumentationService
+              .logError('Plugin completion-results dropped due to conflicting'
+                  ' replacement offset/length: ${result.toJson()}');
+          continue;
+        }
+        suggestions.addAll(result.results);
+      }
+    }
+  }
+
+  /// Send the completion request to plugins, so that they work in other
+  /// isolates in parallel with the server isolate.
+  _RequestToPlugins? _sendRequestToPlugins(
+    DartCompletionRequest completionRequest,
+  ) {
+    var resolvedUnit = completionRequest.result;
+    var analysisContext = resolvedUnit.session.analysisContext;
+
+    var pluginRequestParameters = plugin.CompletionGetSuggestionsParams(
+      resolvedUnit.path,
+      completionRequest.offset,
+    );
+
+    return _RequestToPlugins(
+      completionRequest: completionRequest,
+      parameters: pluginRequestParameters,
+      futures: server.pluginManager.broadcastRequest(
+        pluginRequestParameters,
+        contextRoot: analysisContext.contextRoot,
+      ),
+    );
+  }
 }
 
 /// The result of computing suggestions for code completion.
@@ -516,3 +535,15 @@
   CompletionResult(
       this.replacementOffset, this.replacementLength, this.suggestions);
 }
+
+class _RequestToPlugins {
+  final DartCompletionRequest completionRequest;
+  final plugin.CompletionGetSuggestionsParams parameters;
+  final Map<PluginInfo, Future<plugin.Response>> futures;
+
+  _RequestToPlugins({
+    required this.completionRequest,
+    required this.parameters,
+    required this.futures,
+  });
+}
diff --git a/pkg/analyzer/lib/src/dart/micro/library_graph.dart b/pkg/analyzer/lib/src/dart/micro/library_graph.dart
index a0379af..7bd4630 100644
--- a/pkg/analyzer/lib/src/dart/micro/library_graph.dart
+++ b/pkg/analyzer/lib/src/dart/micro/library_graph.dart
@@ -331,41 +331,27 @@
     required OperationPerformanceImpl performance,
   }) {
     var file = _pathToFile[path];
-    if (file == null) {
-      var fileUri = _resourceProvider.pathContext.toUri(path);
-      var uri = _sourceFactory.restoreUri(
-        _FakeSource(path, fileUri),
-      );
-      if (uri == null) {
-        throw StateError('Unable to convert path to URI: $path');
-      }
-
-      var source = _sourceFactory.forUri2(uri);
-      if (source == null) {
-        throw StateError('Unable to resolve URI: $uri, path: $path');
-      }
-
-      var workspacePackage = _workspace.findPackageFor(path);
-      var featureSet = contextFeatureSet(path, uri, workspacePackage);
-      var packageLanguageVersion =
-          contextLanguageVersion(path, uri, workspacePackage);
-
-      var location = _FileStateLocation._(this, path, uri, source,
-          workspacePackage, featureSet, packageLanguageVersion);
-      file = FileState._(
-        _FileStateUnlinked(
-          location: location,
-          partOfLibrary: null,
-          performance: performance,
-        ),
-      );
-      _pathToFile[path] = file;
-      _uriToFile[uri] = file;
-
-      // Recurse with recording performance.
-      file.files(performance: performance);
+    if (file != null) {
+      return file;
     }
-    return file;
+
+    var fileUri = _resourceProvider.pathContext.toUri(path);
+    var uri = _sourceFactory.restoreUri(
+      _FakeSource(path, fileUri),
+    );
+    if (uri == null) {
+      throw StateError('Unable to convert path to URI: $path');
+    }
+
+    var source = _sourceFactory.forUri2(uri);
+    if (source == null) {
+      throw StateError('Unable to resolve URI: $uri, path: $path');
+    }
+
+    return _newFile(
+      source: source,
+      performance: performance,
+    );
   }
 
   FileState? getFileForUri({
@@ -374,35 +360,19 @@
     required OperationPerformanceImpl performance,
   }) {
     var file = _uriToFile[uri];
-    if (file == null) {
-      var source = _sourceFactory.forUri2(uri);
-      if (source == null) {
-        return null;
-      }
-      var path = source.fullName;
-
-      var workspacePackage = _workspace.findPackageFor(path);
-      var featureSet = contextFeatureSet(path, uri, workspacePackage);
-      var packageLanguageVersion =
-          contextLanguageVersion(path, uri, workspacePackage);
-
-      var location = _FileStateLocation._(this, path, uri, source,
-          workspacePackage, featureSet, packageLanguageVersion);
-      file = FileState._(
-        _FileStateUnlinked(
-          location: location,
-          partOfLibrary: containingLibrary,
-          performance: performance,
-        ),
-      );
-
-      _pathToFile[path] = file;
-      _uriToFile[uri] = file;
-
-      // Recurse with recording performance.
-      file.files(performance: performance);
+    if (file != null) {
+      return file;
     }
-    return file;
+
+    var source = _sourceFactory.forUri2(uri);
+    if (source == null) {
+      return null;
+    }
+
+    return _newFile(
+      source: source,
+      performance: performance,
+    );
   }
 
   /// Returns a list of files whose contents contains the given string.
@@ -488,6 +458,36 @@
 
     return removedFiles;
   }
+
+  FileState _newFile({
+    required Source source,
+    required OperationPerformanceImpl performance,
+  }) {
+    var path = source.fullName;
+    var uri = source.uri;
+
+    var workspacePackage = _workspace.findPackageFor(path);
+    var featureSet = contextFeatureSet(path, uri, workspacePackage);
+    var packageLanguageVersion =
+        contextLanguageVersion(path, uri, workspacePackage);
+
+    var location = _FileStateLocation._(this, path, uri, source,
+        workspacePackage, featureSet, packageLanguageVersion);
+    var file = FileState._(
+      _FileStateUnlinked(
+        location: location,
+        partOfLibrary: null,
+        performance: performance,
+      ),
+    );
+    _pathToFile[path] = file;
+    _uriToFile[uri] = file;
+
+    // Recurse with recording performance.
+    file.files(performance: performance);
+
+    return file;
+  }
 }
 
 class FileSystemStateTestView {
diff --git a/pkg/dartdev/lib/src/commands/compile.dart b/pkg/dartdev/lib/src/commands/compile.dart
index 140b083..4198a79 100644
--- a/pkg/dartdev/lib/src/commands/compile.dart
+++ b/pkg/dartdev/lib/src/commands/compile.dart
@@ -316,14 +316,16 @@
     addSubcommand(CompileJSCommand(verbose: verbose));
     addSubcommand(CompileSnapshotCommand(
       commandName: CompileSnapshotCommand.jitSnapshotCmdName,
-      help: 'to a JIT snapshot.',
+      help: 'to a JIT snapshot.\n'
+          'To run the snapshot use: dart run <JIT file>',
       fileExt: 'jit',
       formatName: 'app-jit',
       verbose: verbose,
     ));
     addSubcommand(CompileSnapshotCommand(
       commandName: CompileSnapshotCommand.kernelCmdName,
-      help: 'to a kernel snapshot.',
+      help: 'to a kernel snapshot.\n'
+          'To run the snapshot use: dart run <kernel file>',
       fileExt: 'dill',
       formatName: 'kernel',
       verbose: verbose,
@@ -336,7 +338,8 @@
     ));
     addSubcommand(CompileNativeCommand(
       commandName: CompileNativeCommand.aotSnapshotCmdName,
-      help: 'to an AOT snapshot.',
+      help: 'to an AOT snapshot.\n'
+          'To run the snapshot use: dartaotruntime <AOT snapshot file>',
       format: 'aot',
       verbose: verbose,
     ));
diff --git a/tools/VERSION b/tools/VERSION
index 15bdd8c..6234999 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 15
 PATCH 0
-PRERELEASE 255
+PRERELEASE 256
 PRERELEASE_PATCH 0
\ No newline at end of file