Version 2.13.0-105.0.dev

Merge commit '9d803a52630ecae94e5afe60fbcb8fb8d281ce86' into 'dev'
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index 3ce1a567e..ec8d2fa 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -11,7 +11,7 @@
     "constraint, update this by running tools/generate_package_config.dart."
   ],
   "configVersion": 2,
-  "generated": "2021-03-03T09:46:04.757962",
+  "generated": "2021-03-04T11:31:12.464706",
   "generator": "tools/generate_package_config.dart",
   "packages": [
     {
@@ -300,6 +300,12 @@
       "languageVersion": "2.7"
     },
     {
+      "name": "func",
+      "rootUri": "../third_party/pkg/func",
+      "packageUri": "lib/",
+      "languageVersion": "1.9"
+    },
+    {
       "name": "glob",
       "rootUri": "../third_party/pkg/glob",
       "packageUri": "lib/",
@@ -443,6 +449,12 @@
       "languageVersion": "2.0"
     },
     {
+      "name": "mustache4dart",
+      "rootUri": "../third_party/pkg/mustache4dart",
+      "packageUri": "lib/",
+      "languageVersion": "0.8"
+    },
+    {
       "name": "native_stack_traces",
       "rootUri": "../pkg/native_stack_traces",
       "packageUri": "lib/",
@@ -512,6 +524,12 @@
       "languageVersion": "2.12"
     },
     {
+      "name": "plugin",
+      "rootUri": "../third_party/pkg/plugin",
+      "packageUri": "lib/",
+      "languageVersion": "1.0"
+    },
+    {
       "name": "pool",
       "rootUri": "../third_party/pkg/pool",
       "packageUri": "lib/",
@@ -734,6 +752,12 @@
       "languageVersion": "2.12"
     },
     {
+      "name": "utf",
+      "rootUri": "../third_party/pkg/utf",
+      "packageUri": "lib/",
+      "languageVersion": "2.0"
+    },
+    {
       "name": "vector_math",
       "rootUri": "../third_party/pkg/vector_math",
       "packageUri": "lib/",
diff --git a/pkg/analysis_server/benchmark/perf/memory_tests.dart b/pkg/analysis_server/benchmark/perf/memory_tests.dart
index 8f8a097..c78c614 100644
--- a/pkg/analysis_server/benchmark/perf/memory_tests.dart
+++ b/pkg/analysis_server/benchmark/perf/memory_tests.dart
@@ -7,7 +7,7 @@
 import 'dart:io';
 
 import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
-import 'package:analysis_server/src/lsp/handlers/handler_completion.dart';
+import 'package:analysis_server/src/lsp/client_capabilities.dart';
 import 'package:analysis_server/src/protocol_server.dart';
 import 'package:analyzer/instrumentation/instrumentation.dart';
 import 'package:test/test.dart';
@@ -188,7 +188,7 @@
       textDocumentCapabilities: withCompletionItemSnippetSupport(
         withCompletionItemKinds(
           emptyTextDocumentClientCapabilities,
-          defaultSupportedCompletionKinds.toList(),
+          LspClientCapabilities.defaultSupportedCompletionKinds.toList(),
         ),
       ),
       workspaceCapabilities: withDocumentChangesSupport(
diff --git a/pkg/analysis_server/lib/src/lsp/client_capabilities.dart b/pkg/analysis_server/lib/src/lsp/client_capabilities.dart
new file mode 100644
index 0000000..2c16de4
--- /dev/null
+++ b/pkg/analysis_server/lib/src/lsp/client_capabilities.dart
@@ -0,0 +1,160 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// 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 'package:analysis_server/lsp_protocol/protocol_generated.dart';
+
+/// Wraps the client (editor) capabilities to improve performance.
+///
+/// Sets transferred as arrays in JSON will be converted to Sets for faster
+/// lookups and default values and nulls will be handled here.
+class LspClientCapabilities {
+  /// If the client does not provide capabilities.completion.completionItemKind.valueSet
+  /// then we must never send a kind that's not in this list.
+  static final Set<CompletionItemKind> defaultSupportedCompletionKinds = {
+    CompletionItemKind.Text,
+    CompletionItemKind.Method,
+    CompletionItemKind.Function,
+    CompletionItemKind.Constructor,
+    CompletionItemKind.Field,
+    CompletionItemKind.Variable,
+    CompletionItemKind.Class,
+    CompletionItemKind.Interface,
+    CompletionItemKind.Module,
+    CompletionItemKind.Property,
+    CompletionItemKind.Unit,
+    CompletionItemKind.Value,
+    CompletionItemKind.Enum,
+    CompletionItemKind.Keyword,
+    CompletionItemKind.Snippet,
+    CompletionItemKind.Color,
+    CompletionItemKind.File,
+    CompletionItemKind.Reference,
+  };
+
+  /// If the client does not provide capabilities.documentSymbol.symbolKind.valueSet
+  /// then we must never send a kind that's not in this list.
+  static final Set<SymbolKind> defaultSupportedSymbolKinds = {
+    SymbolKind.File,
+    SymbolKind.Module,
+    SymbolKind.Namespace,
+    SymbolKind.Package,
+    SymbolKind.Class,
+    SymbolKind.Method,
+    SymbolKind.Property,
+    SymbolKind.Field,
+    SymbolKind.Constructor,
+    SymbolKind.Enum,
+    SymbolKind.Interface,
+    SymbolKind.Function,
+    SymbolKind.Variable,
+    SymbolKind.Constant,
+    SymbolKind.Str,
+    SymbolKind.Number,
+    SymbolKind.Boolean,
+    SymbolKind.Array,
+  };
+
+  final ClientCapabilities raw;
+  final bool documentChanges;
+  final bool configuration;
+  final bool createResourceOperations;
+  final bool completionDeprecatedFlag;
+  final bool applyEdit;
+  final bool workDoneProgress;
+  final bool completionSnippets;
+  final bool renameValidation;
+  final bool literalCodeActions;
+  final bool insertReplaceCompletionRanges;
+  final bool definitionLocationLink;
+  final bool hierarchicalSymbols;
+  final Set<CodeActionKind> codeActionKinds;
+  final Set<CompletionItemTag> completionItemTags;
+  final Set<DiagnosticTag> diagnosticTags;
+  final Set<MarkupKind> completionDocumentationFormats;
+  final Set<MarkupKind> signatureHelpDocumentationFormats;
+  final Set<MarkupKind> hoverContentFormats;
+  final Set<SymbolKind> documentSymbolKinds;
+  final Set<SymbolKind> workspaceSymbolKinds;
+  final Set<CompletionItemKind> completionItemKinds;
+  final Set<InsertTextMode> completionInsertTextModes;
+
+  LspClientCapabilities(this.raw)
+      : applyEdit = raw?.workspace?.applyEdit ?? false,
+        codeActionKinds = _listToSet(raw?.textDocument?.codeAction
+            ?.codeActionLiteralSupport?.codeActionKind?.valueSet),
+        completionDeprecatedFlag =
+            raw.textDocument?.completion?.completionItem?.deprecatedSupport ??
+                false,
+        completionDocumentationFormats = _completionDocumentationFormats(raw),
+        completionInsertTextModes = _listToSet(raw.textDocument?.completion
+            ?.completionItem?.insertTextModeSupport?.valueSet),
+        completionItemKinds = _listToSet(
+            raw?.textDocument?.completion?.completionItemKind?.valueSet,
+            defaults: defaultSupportedCompletionKinds),
+        completionSnippets =
+            raw.textDocument?.completion?.completionItem?.snippetSupport ??
+                false,
+        configuration = raw?.workspace?.configuration ?? false,
+        createResourceOperations = raw
+                ?.workspace?.workspaceEdit?.resourceOperations
+                ?.contains(ResourceOperationKind.Create) ??
+            false,
+        definitionLocationLink =
+            raw?.textDocument?.definition?.linkSupport ?? false,
+        completionItemTags = _listToSet(raw
+            ?.textDocument?.completion?.completionItem?.tagSupport?.valueSet),
+        diagnosticTags = _listToSet(
+            raw?.textDocument?.publishDiagnostics?.tagSupport?.valueSet),
+        documentChanges =
+            raw?.workspace?.workspaceEdit?.documentChanges ?? false,
+        documentSymbolKinds = _listToSet(
+            raw?.textDocument?.documentSymbol?.symbolKind?.valueSet,
+            defaults: defaultSupportedSymbolKinds),
+        hierarchicalSymbols = raw?.textDocument?.documentSymbol
+                ?.hierarchicalDocumentSymbolSupport ??
+            false,
+        hoverContentFormats = _hoverContentFormats(raw),
+        insertReplaceCompletionRanges = raw?.textDocument?.completion
+                ?.completionItem?.insertReplaceSupport ??
+            false,
+        literalCodeActions =
+            raw?.textDocument?.codeAction?.codeActionLiteralSupport != null,
+        renameValidation = raw.textDocument?.rename?.prepareSupport ?? false,
+        signatureHelpDocumentationFormats = _sigHelpDocumentationFormats(raw),
+        workDoneProgress = raw.window?.workDoneProgress ?? false,
+        workspaceSymbolKinds = _listToSet(
+            raw?.workspace?.symbol?.symbolKind?.valueSet,
+            defaults: defaultSupportedSymbolKinds);
+
+  static Set<MarkupKind> _completionDocumentationFormats(
+      ClientCapabilities raw) {
+    // For formats, null is valid (which means only raw strings are supported,
+    // not [MarkupContent]), so use null as default.
+    return _listToSet(
+        raw?.textDocument?.completion?.completionItem?.documentationFormat,
+        defaults: null);
+  }
+
+  static Set<MarkupKind> _hoverContentFormats(ClientCapabilities raw) {
+    // For formats, null is valid (which means only raw strings are supported,
+    // not [MarkupContent]), so use null as default.
+    return _listToSet(raw?.textDocument?.hover?.contentFormat, defaults: null);
+  }
+
+  /// Converts a list to a `Set`, returning [defaults] if the list is null.
+  ///
+  /// If [defaults] is not supplied, will return an empty set.
+  static Set<T> _listToSet<T>(List<T> items, {Set<T> defaults = const {}}) {
+    return items != null ? {...items} : defaults;
+  }
+
+  static Set<MarkupKind> _sigHelpDocumentationFormats(ClientCapabilities raw) {
+    // For formats, null is valid (which means only raw strings are supported,
+    // not [MarkupContent]), so use null as default.
+    return _listToSet(
+        raw?.textDocument?.signatureHelp?.signatureInformation
+            ?.documentationFormat,
+        defaults: null);
+  }
+}
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/commands/simple_edit_handler.dart b/pkg/analysis_server/lib/src/lsp/handlers/commands/simple_edit_handler.dart
index 8a5727a..c8fa759 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/commands/simple_edit_handler.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/commands/simple_edit_handler.dart
@@ -38,7 +38,7 @@
     }
 
     final workspaceEdit = toWorkspaceEdit(
-      server.clientCapabilities?.workspace,
+      server.clientCapabilities,
       [FileEditInformation(docIdentifier, unit.lineInfo, edits)],
     );
 
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
index 762a4f8..f51367f 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
@@ -2,8 +2,6 @@
 // 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/lsp_protocol/protocol_generated.dart';
 import 'package:analysis_server/lsp_protocol/protocol_special.dart';
 import 'package:analysis_server/plugin/edit/assist/assist_core.dart';
@@ -50,21 +48,14 @@
       return success(const []);
     }
 
-    final capabilities = server?.clientCapabilities?.textDocument;
+    final supportsApplyEdit = server.clientCapabilities.applyEdit;
 
-    final clientSupportsWorkspaceApplyEdit =
-        server?.clientCapabilities?.workspace?.applyEdit == true;
+    final supportsLiteralCodeActions =
+        server.clientCapabilities.literalCodeActions;
 
-    final clientSupportsLiteralCodeActions =
-        capabilities?.codeAction?.codeActionLiteralSupport != null;
+    final supportedKinds = server.clientCapabilities.codeActionKinds;
 
-    final clientSupportedCodeActionKinds = HashSet<CodeActionKind>.of(
-        capabilities?.codeAction?.codeActionLiteralSupport?.codeActionKind
-                ?.valueSet ??
-            []);
-
-    final clientSupportedDiagnosticTags = HashSet<DiagnosticTag>.of(
-        capabilities?.publishDiagnostics?.tagSupport?.valueSet ?? []);
+    final supportedDiagnosticTags = server.clientCapabilities.diagnosticTags;
 
     final unit = await path.mapResult(requireResolvedUnit);
 
@@ -85,8 +76,7 @@
 
       // Otherwise, filter out anything not supported by the client (if they
       // advertised that they provided the kinds).
-      if (clientSupportsLiteralCodeActions &&
-          !clientSupportedCodeActionKinds.any(isMatch)) {
+      if (supportsLiteralCodeActions && !supportedKinds.any(isMatch)) {
         return false;
       }
 
@@ -102,9 +92,9 @@
           final length = endOffset - startOffset;
           return _getCodeActions(
               shouldIncludeKind,
-              clientSupportsLiteralCodeActions,
-              clientSupportsWorkspaceApplyEdit,
-              clientSupportedDiagnosticTags,
+              supportsLiteralCodeActions,
+              supportsApplyEdit,
+              supportedDiagnosticTags,
               path.result,
               params.range,
               offset,
@@ -133,11 +123,11 @@
   /// Wraps a command in a CodeAction if the client supports it so that a
   /// CodeActionKind can be supplied.
   Either2<Command, CodeAction> _commandOrCodeAction(
-    bool clientSupportsLiteralCodeActions,
+    bool supportsLiteralCodeActions,
     CodeActionKind kind,
     Command command,
   ) {
-    return clientSupportsLiteralCodeActions
+    return supportsLiteralCodeActions
         ? Either2<Command, CodeAction>.t2(
             CodeAction(title: command.title, kind: kind, command: command),
           )
@@ -222,7 +212,7 @@
 
   Future<List<Either2<Command, CodeAction>>> _getAssistActions(
     bool Function(CodeActionKind) shouldIncludeKind,
-    bool clientSupportsLiteralCodeActions,
+    bool supportsLiteralCodeActions,
     Range range,
     int offset,
     int length,
@@ -259,7 +249,7 @@
     bool Function(CodeActionKind) shouldIncludeKind,
     bool supportsLiterals,
     bool supportsWorkspaceApplyEdit,
-    HashSet<DiagnosticTag> supportedDiagnosticTags,
+    Set<DiagnosticTag> supportedDiagnosticTags,
     String path,
     Range range,
     int offset,
@@ -283,8 +273,8 @@
 
   Future<List<Either2<Command, CodeAction>>> _getFixActions(
     bool Function(CodeActionKind) shouldIncludeKind,
-    bool clientSupportsLiteralCodeActions,
-    HashSet<DiagnosticTag> supportedDiagnosticTags,
+    bool supportsLiteralCodeActions,
+    Set<DiagnosticTag> supportedDiagnosticTags,
     Range range,
     ResolvedUnitResult unit,
   ) async {
@@ -347,7 +337,7 @@
 
   Future<List<Either2<Command, CodeAction>>> _getRefactorActions(
     bool Function(CodeActionKind) shouldIncludeKind,
-    bool clientSupportsLiteralCodeActions,
+    bool supportsLiteralCodeActions,
     String path,
     int offset,
     int length,
@@ -368,7 +358,7 @@
       Map<String, dynamic> options,
     ]) {
       return _commandOrCodeAction(
-          clientSupportsLiteralCodeActions,
+          supportsLiteralCodeActions,
           actionKind,
           Command(
             title: name,
@@ -425,8 +415,8 @@
   /// source such as Sort Members and Organise Imports.
   Future<List<Either2<Command, CodeAction>>> _getSourceActions(
     bool Function(CodeActionKind) shouldIncludeKind,
-    bool clientSupportsLiteralCodeActions,
-    bool clientSupportsWorkspaceApplyEdit,
+    bool supportsLiteralCodeActions,
+    bool supportsApplyEdit,
     String path,
   ) async {
     // The source actions supported are only valid for Dart files.
@@ -437,14 +427,14 @@
 
     // If the client does not support workspace/applyEdit, we won't be able to
     // run any of these.
-    if (!clientSupportsWorkspaceApplyEdit) {
+    if (!supportsApplyEdit) {
       return const [];
     }
 
     return [
       if (shouldIncludeKind(DartCodeActionKind.SortMembers))
         _commandOrCodeAction(
-          clientSupportsLiteralCodeActions,
+          supportsLiteralCodeActions,
           DartCodeActionKind.SortMembers,
           Command(
               title: 'Sort Members',
@@ -453,7 +443,7 @@
         ),
       if (shouldIncludeKind(CodeActionKind.SourceOrganizeImports))
         _commandOrCodeAction(
-          clientSupportsLiteralCodeActions,
+          supportsLiteralCodeActions,
           CodeActionKind.SourceOrganizeImports,
           Command(
               title: 'Organize Imports',
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
index e565b85..f6671c6 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion.dart
@@ -2,13 +2,13 @@
 // 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 'dart:math' as math;
 
 import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
 import 'package:analysis_server/lsp_protocol/protocol_special.dart';
 import 'package:analysis_server/protocol/protocol_generated.dart';
 import 'package:analysis_server/src/domains/completion/available_suggestions.dart';
+import 'package:analysis_server/src/lsp/client_capabilities.dart';
 import 'package:analysis_server/src/lsp/handlers/handlers.dart';
 import 'package:analysis_server/src/lsp/lsp_analysis_server.dart';
 import 'package:analysis_server/src/lsp/mapping.dart';
@@ -28,29 +28,6 @@
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
 
-/// If the client does not provide capabilities.completion.completionItemKind.valueSet
-/// then we must never send a kind that's not in this list.
-final defaultSupportedCompletionKinds = HashSet<CompletionItemKind>.of([
-  CompletionItemKind.Text,
-  CompletionItemKind.Method,
-  CompletionItemKind.Function,
-  CompletionItemKind.Constructor,
-  CompletionItemKind.Field,
-  CompletionItemKind.Variable,
-  CompletionItemKind.Class,
-  CompletionItemKind.Interface,
-  CompletionItemKind.Module,
-  CompletionItemKind.Property,
-  CompletionItemKind.Unit,
-  CompletionItemKind.Value,
-  CompletionItemKind.Enum,
-  CompletionItemKind.Keyword,
-  CompletionItemKind.Snippet,
-  CompletionItemKind.Color,
-  CompletionItemKind.File,
-  CompletionItemKind.Reference,
-]);
-
 class CompletionHandler
     extends MessageHandler<CompletionParams, List<CompletionItem>>
     with LspPluginRequestHandlerMixin {
@@ -69,17 +46,8 @@
   @override
   Future<ErrorOr<List<CompletionItem>>> handle(
       CompletionParams params, CancellationToken token) async {
-    final completionCapabilities =
-        server?.clientCapabilities?.textDocument?.completion;
-
-    final clientSupportedCompletionKinds =
-        completionCapabilities?.completionItemKind?.valueSet != null
-            ? HashSet<CompletionItemKind>.of(
-                completionCapabilities.completionItemKind.valueSet)
-            : defaultSupportedCompletionKinds;
-
-    final includeSuggestionSets = suggestFromUnimportedLibraries &&
-        server?.clientCapabilities?.workspace?.applyEdit == true;
+    final includeSuggestionSets =
+        suggestFromUnimportedLibraries && server.clientCapabilities.applyEdit;
 
     final pos = params.position;
     final path = pathOfDoc(params.textDocument);
@@ -101,8 +69,7 @@
 
       if (fileExtension == '.dart' && !unit.isError) {
         serverResultsFuture = _getServerDartItems(
-          completionCapabilities,
-          clientSupportedCompletionKinds,
+          server.clientCapabilities,
           includeSuggestionSets,
           unit.result,
           offset,
@@ -121,8 +88,7 @@
         if (generator != null) {
           serverResultsFuture = _getServerYamlItems(
             generator,
-            completionCapabilities,
-            clientSupportedCompletionKinds,
+            server.clientCapabilities,
             path.result,
             lineInfo.result,
             offset,
@@ -133,8 +99,8 @@
 
       serverResultsFuture ??= Future.value(success(const <CompletionItem>[]));
 
-      final pluginResultsFuture = _getPluginResults(completionCapabilities,
-          clientSupportedCompletionKinds, lineInfo.result, path.result, offset);
+      final pluginResultsFuture = _getPluginResults(
+          server.clientCapabilities, lineInfo.result, path.result, offset);
 
       // Await both server + plugin results together to allow async/IO to
       // overlap.
@@ -195,8 +161,7 @@
       '$name/$declaringUri';
 
   Future<ErrorOr<List<CompletionItem>>> _getPluginResults(
-    CompletionClientCapabilities completionCapabilities,
-    HashSet<CompletionItemKind> clientSupportedCompletionKinds,
+    LspClientCapabilities capabilities,
     LineInfo lineInfo,
     String path,
     int offset,
@@ -210,8 +175,7 @@
         .toList();
 
     return success(_pluginResultsToItems(
-      completionCapabilities,
-      clientSupportedCompletionKinds,
+      capabilities,
       lineInfo,
       offset,
       pluginResults,
@@ -219,8 +183,7 @@
   }
 
   Future<ErrorOr<List<CompletionItem>>> _getServerDartItems(
-    CompletionClientCapabilities completionCapabilities,
-    HashSet<CompletionItemKind> clientSupportedCompletionKinds,
+    LspClientCapabilities capabilities,
     bool includeSuggestionSets,
     ResolvedUnitResult unit,
     int offset,
@@ -288,8 +251,7 @@
             }
 
             return toCompletionItem(
-              completionCapabilities,
-              clientSupportedCompletionKinds,
+              capabilities,
               unit.lineInfo,
               item,
               itemReplacementOffset,
@@ -378,8 +340,7 @@
             return importingUris == null ||
                 importingUris.contains('${library.uri}');
           }).map((item) => declarationToCompletionItem(
-                    completionCapabilities,
-                    clientSupportedCompletionKinds,
+                    capabilities,
                     unit.path,
                     offset,
                     includedSet,
@@ -424,8 +385,7 @@
 
   Future<ErrorOr<List<CompletionItem>>> _getServerYamlItems(
     YamlCompletionGenerator generator,
-    CompletionClientCapabilities completionCapabilities,
-    HashSet<CompletionItemKind> clientSupportedCompletionKinds,
+    LspClientCapabilities capabilities,
     String path,
     LineInfo lineInfo,
     int offset,
@@ -440,8 +400,7 @@
     final completionItems = suggestions.suggestions
         .map(
           (item) => toCompletionItem(
-            completionCapabilities,
-            clientSupportedCompletionKinds,
+            capabilities,
             lineInfo,
             item,
             suggestions.replacementOffset,
@@ -456,8 +415,7 @@
   }
 
   Iterable<CompletionItem> _pluginResultsToItems(
-    CompletionClientCapabilities completionCapabilities,
-    HashSet<CompletionItemKind> clientSupportedCompletionKinds,
+    LspClientCapabilities capabilities,
     LineInfo lineInfo,
     int offset,
     List<plugin.CompletionGetSuggestionsResult> pluginResults,
@@ -465,8 +423,7 @@
     return pluginResults.expand((result) {
       return result.results.map(
         (item) => toCompletionItem(
-          completionCapabilities,
-          clientSupportedCompletionKinds,
+          capabilities,
           lineInfo,
           item,
           result.replacementOffset,
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart
index f2698dd..3c3ea2c 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart
@@ -154,11 +154,10 @@
         }
 
         // Documentation is added on during resolve for LSP.
-        final formats = server.clientCapabilities?.textDocument?.completion
-            ?.completionItem?.documentationFormat;
-        final supportsInsertReplace = server.clientCapabilities?.textDocument
-                ?.completion?.completionItem?.insertReplaceSupport ==
-            true;
+        final formats =
+            server.clientCapabilities.completionDocumentationFormats;
+        final supportsInsertReplace =
+            server.clientCapabilities.insertReplaceCompletionRanges;
         final dartDoc =
             analyzer.getDartDocPlainText(requestedElement.documentationComment);
         final documentation = asStringOrMarkupContent(formats, dartDoc);
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart
index a6399a9..e3599b6 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_definition.dart
@@ -46,9 +46,9 @@
   }
 
   Future<AnalysisNavigationParams> getServerResult(
-      bool clientSupportsLocationLink, String path, int offset) async {
-    final collector = NavigationCollectorImpl(
-        collectCodeLocations: clientSupportsLocationLink);
+      bool supportsLocationLink, String path, int offset) async {
+    final collector =
+        NavigationCollectorImpl(collectCodeLocations: supportsLocationLink);
 
     final result = await server.getResolvedUnit(path);
     if (result?.state == ResultState.VALID) {
@@ -64,11 +64,8 @@
   @override
   Future<ErrorOr<Either2<List<Location>, List<LocationLink>>>> handle(
       TextDocumentPositionParams params, CancellationToken token) async {
-    final definitionCapabilities =
-        server?.clientCapabilities?.textDocument?.definition;
-
-    final clientSupportsLocationLink =
-        definitionCapabilities?.linkSupport == true;
+    final supportsLocationLink =
+        server.clientCapabilities.definitionLocationLink;
 
     final pos = params.position;
     final path = pathOfDoc(params.textDocument);
@@ -87,7 +84,7 @@
 
       return offset.mapResult((offset) async {
         final allResults = [
-          await getServerResult(clientSupportsLocationLink, path, offset),
+          await getServerResult(supportsLocationLink, path, offset),
           ...await getPluginResults(path, offset),
         ];
 
@@ -97,7 +94,7 @@
 
         // Convert and filter the results using the correct type of Location class
         // depending on the client capabilities.
-        if (clientSupportsLocationLink) {
+        if (supportsLocationLink) {
           final convertedResults = convert(
             mergedTargets,
             (target) => _toLocationLink(mergedResults, lineInfo, target),
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_document_symbols.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_document_symbols.dart
index 2e383a0..f8ad0d6 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_document_symbols.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_document_symbols.dart
@@ -2,11 +2,10 @@
 // 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/lsp_protocol/protocol_generated.dart';
 import 'package:analysis_server/lsp_protocol/protocol_special.dart';
 import 'package:analysis_server/src/computer/computer_outline.dart';
+import 'package:analysis_server/src/lsp/client_capabilities.dart';
 import 'package:analysis_server/src/lsp/handlers/handlers.dart';
 import 'package:analysis_server/src/lsp/lsp_analysis_server.dart';
 import 'package:analysis_server/src/lsp/mapping.dart';
@@ -14,29 +13,6 @@
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/source/line_info.dart';
 
-// If the client does not provide capabilities.documentSymbol.symbolKind.valueSet
-// then we must never send a kind that's not in this list.
-final defaultSupportedSymbolKinds = HashSet<SymbolKind>.of([
-  SymbolKind.File,
-  SymbolKind.Module,
-  SymbolKind.Namespace,
-  SymbolKind.Package,
-  SymbolKind.Class,
-  SymbolKind.Method,
-  SymbolKind.Property,
-  SymbolKind.Field,
-  SymbolKind.Constructor,
-  SymbolKind.Enum,
-  SymbolKind.Interface,
-  SymbolKind.Function,
-  SymbolKind.Variable,
-  SymbolKind.Constant,
-  SymbolKind.Str,
-  SymbolKind.Number,
-  SymbolKind.Boolean,
-  SymbolKind.Array,
-]);
-
 class DocumentSymbolHandler extends MessageHandler<DocumentSymbolParams,
     Either2<List<DocumentSymbol>, List<SymbolInformation>>> {
   DocumentSymbolHandler(LspAnalysisServer server) : super(server);
@@ -56,55 +32,41 @@
       );
     }
 
-    final symbolCapabilities =
-        server?.clientCapabilities?.textDocument?.documentSymbol;
-
-    final clientSupportedSymbolKinds =
-        symbolCapabilities?.symbolKind?.valueSet != null
-            ? HashSet<SymbolKind>.of(symbolCapabilities.symbolKind.valueSet)
-            : defaultSupportedSymbolKinds;
-
-    final clientSupportsDocumentSymbol =
-        symbolCapabilities?.hierarchicalDocumentSymbolSupport ?? false;
-
     final path = pathOfDoc(params.textDocument);
     final unit = await path.mapResult(requireResolvedUnit);
-    return unit.mapResult((unit) => _getSymbols(clientSupportedSymbolKinds,
-        clientSupportsDocumentSymbol, path.result, unit));
+    return unit.mapResult(
+        (unit) => _getSymbols(server.clientCapabilities, path.result, unit));
   }
 
   DocumentSymbol _asDocumentSymbol(
-    HashSet<SymbolKind> clientSupportedSymbolKinds,
+    Set<SymbolKind> supportedKinds,
     LineInfo lineInfo,
     Outline outline,
   ) {
     return DocumentSymbol(
       name: toElementName(outline.element),
       detail: outline.element.parameters,
-      kind: elementKindToSymbolKind(
-          clientSupportedSymbolKinds, outline.element.kind),
+      kind: elementKindToSymbolKind(supportedKinds, outline.element.kind),
       deprecated: outline.element.isDeprecated,
       range: toRange(lineInfo, outline.codeOffset, outline.codeLength),
       selectionRange: toRange(lineInfo, outline.element.location.offset,
           outline.element.location.length),
       children: outline.children
-          ?.map((child) =>
-              _asDocumentSymbol(clientSupportedSymbolKinds, lineInfo, child))
+          ?.map((child) => _asDocumentSymbol(supportedKinds, lineInfo, child))
           ?.toList(),
     );
   }
 
   SymbolInformation _asSymbolInformation(
     String containerName,
-    HashSet<SymbolKind> clientSupportedSymbolKinds,
+    Set<SymbolKind> supportedKinds,
     String documentUri,
     LineInfo lineInfo,
     Outline outline,
   ) {
     return SymbolInformation(
       name: toElementName(outline.element),
-      kind: elementKindToSymbolKind(
-          clientSupportedSymbolKinds, outline.element.kind),
+      kind: elementKindToSymbolKind(supportedKinds, outline.element.kind),
       deprecated: outline.element.isDeprecated,
       location: Location(
         uri: documentUri,
@@ -116,22 +78,21 @@
   }
 
   ErrorOr<Either2<List<DocumentSymbol>, List<SymbolInformation>>> _getSymbols(
-    HashSet<SymbolKind> clientSupportedSymbolKinds,
-    bool clientSupportsDocumentSymbol,
+    LspClientCapabilities capabilities,
     String path,
     ResolvedUnitResult unit,
   ) {
     final computer = DartUnitOutlineComputer(unit);
     final outline = computer.compute();
 
-    if (clientSupportsDocumentSymbol) {
+    if (capabilities.hierarchicalSymbols) {
       // Return a tree of DocumentSymbol only if the client shows explicit support
       // for it.
       return success(
         Either2<List<DocumentSymbol>, List<SymbolInformation>>.t1(
           outline?.children
               ?.map((child) => _asDocumentSymbol(
-                  clientSupportedSymbolKinds, unit.lineInfo, child))
+                  capabilities.documentSymbolKinds, unit.lineInfo, child))
               ?.toList(),
         ),
       );
@@ -145,7 +106,7 @@
       void addSymbol(Outline outline, {String parentName}) {
         allSymbols.add(_asSymbolInformation(
           parentName,
-          clientSupportedSymbolKinds,
+          capabilities.documentSymbolKinds,
           documentUri,
           unit.lineInfo,
           outline,
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_execute_command.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_execute_command.dart
index 967e8e8..43d877e 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_execute_command.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_execute_command.dart
@@ -45,7 +45,7 @@
 
     final progress = params.workDoneToken != null
         ? ProgressReporter.clientProvided(server, params.workDoneToken)
-        : server.clientCapabilities.window?.workDoneProgress == true
+        : server.clientCapabilities.workDoneProgress
             ? ProgressReporter.serverCreated(server)
             : ProgressReporter.noop;
     return handler.handle(params.arguments, progress, cancellationToken);
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_hover.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_hover.dart
index 3b5561e..4e57b46 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_hover.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_hover.dart
@@ -73,8 +73,7 @@
       content.writeln(cleanDartdoc(hover.dartdoc));
     }
 
-    final formats =
-        server?.clientCapabilities?.textDocument?.hover?.contentFormat;
+    final formats = server.clientCapabilities.hoverContentFormats;
     return Hover(
       contents:
           asStringOrMarkupContent(formats, content.toString().trimRight()),
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_initialize.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_initialize.dart
index 4a74f56..9174fb8 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_initialize.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_initialize.dart
@@ -60,7 +60,7 @@
     );
 
     server.capabilities = server.capabilitiesComputer
-        .computeServerCapabilities(params.capabilities);
+        .computeServerCapabilities(server.clientCapabilities);
 
     var sdkVersion = Platform.version;
     if (sdkVersion.contains(' ')) {
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_signature_help.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_signature_help.dart
index de886e7..d01e33d 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_signature_help.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_signature_help.dart
@@ -67,8 +67,8 @@
         return success();
       }
 
-      final formats = server?.clientCapabilities?.textDocument?.signatureHelp
-          ?.signatureInformation?.documentationFormat;
+      final formats =
+          server.clientCapabilities.signatureHelpDocumentationFormats;
       return success(toSignatureHelp(formats, signature));
     });
   }
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_workspace_symbols.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_workspace_symbols.dart
index fece08b..ef781d1 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_workspace_symbols.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_workspace_symbols.dart
@@ -2,12 +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/lsp_protocol/protocol_generated.dart';
 import 'package:analysis_server/lsp_protocol/protocol_special.dart';
-import 'package:analysis_server/src/lsp/handlers/handler_document_symbols.dart'
-    show defaultSupportedSymbolKinds;
 import 'package:analysis_server/src/lsp/handlers/handlers.dart';
 import 'package:analysis_server/src/lsp/lsp_analysis_server.dart';
 import 'package:analysis_server/src/lsp/mapping.dart';
@@ -34,12 +30,7 @@
       return success([]);
     }
 
-    final symbolCapabilities = server?.clientCapabilities?.workspace?.symbol;
-
-    final clientSupportedSymbolKinds =
-        symbolCapabilities?.symbolKind?.valueSet != null
-            ? HashSet<SymbolKind>.of(symbolCapabilities.symbolKind.valueSet)
-            : defaultSupportedSymbolKinds;
+    final supportedSymbolKinds = server.clientCapabilities.workspaceSymbolKinds;
 
     // Convert the string input into a case-insensitive regex that has wildcards
     // between every character and at start/end to allow for fuzzy matching.
@@ -67,7 +58,7 @@
     final symbols = declarations
         .map((declaration) => _asSymbolInformation(
               declaration,
-              clientSupportedSymbolKinds,
+              supportedSymbolKinds,
               filePaths,
             ))
         .toList();
@@ -77,13 +68,13 @@
 
   SymbolInformation _asSymbolInformation(
     search.Declaration declaration,
-    HashSet<SymbolKind> clientSupportedSymbolKinds,
+    Set<SymbolKind> supportedKinds,
     List<String> filePaths,
   ) {
     final filePath = filePaths[declaration.fileIndex];
 
     final kind = declarationKindToSymbolKind(
-      clientSupportedSymbolKinds,
+      supportedKinds,
       declaration.kind,
     );
     final range = toRange(
diff --git a/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart b/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
index 46e1fe1..19afc27 100644
--- a/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
+++ b/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.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/lsp_protocol/protocol_custom_generated.dart';
 import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
@@ -18,6 +17,7 @@
     show CompletionDomainHandler;
 import 'package:analysis_server/src/flutter/flutter_outline_computer.dart';
 import 'package:analysis_server/src/lsp/channel/lsp_channel.dart';
+import 'package:analysis_server/src/lsp/client_capabilities.dart';
 import 'package:analysis_server/src/lsp/client_configuration.dart';
 import 'package:analysis_server/src/lsp/constants.dart';
 import 'package:analysis_server/src/lsp/handlers/handler_states.dart';
@@ -55,7 +55,7 @@
 /// them.
 class LspAnalysisServer extends AbstractAnalysisServer {
   /// The capabilities of the LSP client. Will be null prior to initialization.
-  ClientCapabilities _clientCapabilities;
+  LspClientCapabilities _clientCapabilities;
 
   /// Initialization options provided by the LSP client. Allows opting in/out of
   /// specific server functionality. Will be null prior to initialization.
@@ -115,7 +115,7 @@
   final _temporaryAnalysisRoots = <String, String>{};
 
   /// The set of analysis roots explicitly added to the workspace.
-  final _explicitAnalysisRoots = HashSet<String>();
+  final _explicitAnalysisRoots = <String>{};
 
   /// A progress reporter for analysis status.
   ProgressReporter analyzingProgressReporter;
@@ -164,7 +164,7 @@
   }
 
   /// The capabilities of the LSP client. Will be null prior to initialization.
-  ClientCapabilities get clientCapabilities => _clientCapabilities;
+  LspClientCapabilities get clientCapabilities => _clientCapabilities;
 
   Future<void> get exited => channel.closed;
 
@@ -214,7 +214,7 @@
   /// Fetches configuration from the client (if supported) and then sends
   /// register/unregister requests for any supported/enabled dynamic registrations.
   Future<void> fetchClientConfigurationAndPerformDynamicRegistration() async {
-    if (clientCapabilities.workspace?.configuration ?? false) {
+    if (clientCapabilities.configuration) {
       // Fetch all configuration we care about from the client. This is just
       // "dart" for now, but in future this may be extended to include
       // others (for example "flutter").
@@ -271,7 +271,7 @@
 
   void handleClientConnection(
       ClientCapabilities capabilities, dynamic initializationOptions) {
-    _clientCapabilities = capabilities;
+    _clientCapabilities = LspClientCapabilities(capabilities);
     _initializationOptions = LspInitializationOptions(initializationOptions);
 
     performanceAfterStartup = ServerPerformance();
@@ -555,7 +555,7 @@
     // Send old custom notifications to clients that do not support $/progress.
     // TODO(dantup): Remove this custom notification (and related classes) when
     // it's unlikely to be in use by any clients.
-    if (clientCapabilities.window?.workDoneProgress != true) {
+    if (clientCapabilities.workDoneProgress != true) {
       channel.sendNotification(NotificationMessage(
         method: CustomMethods.analyzerStatus,
         params: AnalyzerStatusParams(isAnalyzing: status.isAnalyzing),
@@ -809,11 +809,8 @@
     analysisServer.declarationsTracker
         ?.addContext(analysisDriver.analysisContext);
 
-    final textDocumentCapabilities =
-        analysisServer.clientCapabilities?.textDocument;
-    final supportedDiagnosticTags = HashSet<DiagnosticTag>.of(
-        textDocumentCapabilities?.publishDiagnostics?.tagSupport?.valueSet ??
-            []);
+    final supportedDiagnosticTags =
+        analysisServer.clientCapabilities.diagnosticTags;
     analysisDriver.results.listen((result) {
       var path = result.path;
       filesToFlush.add(path);
diff --git a/pkg/analysis_server/lib/src/lsp/mapping.dart b/pkg/analysis_server/lib/src/lsp/mapping.dart
index 1b8e024..298e997 100644
--- a/pkg/analysis_server/lib/src/lsp/mapping.dart
+++ b/pkg/analysis_server/lib/src/lsp/mapping.dart
@@ -2,7 +2,6 @@
 // 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 'dart:math';
 
 import 'package:analysis_server/lsp_protocol/protocol_custom_generated.dart'
@@ -11,6 +10,8 @@
 import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
 import 'package:analysis_server/lsp_protocol/protocol_special.dart';
 import 'package:analysis_server/lsp_protocol/protocol_special.dart' as lsp;
+import 'package:analysis_server/src/collections.dart';
+import 'package:analysis_server/src/lsp/client_capabilities.dart';
 import 'package:analysis_server/src/lsp/constants.dart' as lsp;
 import 'package:analysis_server/src/lsp/constants.dart';
 import 'package:analysis_server/src/lsp/dartdoc.dart';
@@ -47,7 +48,7 @@
 const languageSourceName = 'dart';
 
 lsp.Either2<String, lsp.MarkupContent> asStringOrMarkupContent(
-    List<lsp.MarkupKind> preferredFormats, String content) {
+    Set<lsp.MarkupKind> preferredFormats, String content) {
   if (content == null) {
     return null;
   }
@@ -113,7 +114,7 @@
 lsp.WorkspaceEdit createWorkspaceEdit(
     lsp.LspAnalysisServer server, List<server.SourceFileEdit> edits) {
   return toWorkspaceEdit(
-      server.clientCapabilities?.workspace,
+      server.clientCapabilities,
       edits
           .map((e) => FileEditInformation(
                 server.getVersionedDocumentIdentifier(e.file),
@@ -126,11 +127,11 @@
 }
 
 lsp.CompletionItemKind declarationKindToCompletionItemKind(
-  HashSet<lsp.CompletionItemKind> clientSupportedCompletionKinds,
+  Set<lsp.CompletionItemKind> supportedCompletionKinds,
   dec.DeclarationKind kind,
 ) {
   bool isSupported(lsp.CompletionItemKind kind) =>
-      clientSupportedCompletionKinds.contains(kind);
+      supportedCompletionKinds.contains(kind);
 
   List<lsp.CompletionItemKind> getKindPreferences() {
     switch (kind) {
@@ -162,11 +163,10 @@
 }
 
 lsp.SymbolKind declarationKindToSymbolKind(
-  HashSet<lsp.SymbolKind> clientSupportedSymbolKinds,
+  Set<lsp.SymbolKind> supportedSymbolKinds,
   server.DeclarationKind kind,
 ) {
-  bool isSupported(lsp.SymbolKind kind) =>
-      clientSupportedSymbolKinds.contains(kind);
+  bool isSupported(lsp.SymbolKind kind) => supportedSymbolKinds.contains(kind);
 
   List<lsp.SymbolKind> getKindPreferences() {
     switch (kind) {
@@ -210,8 +210,7 @@
 }
 
 lsp.CompletionItem declarationToCompletionItem(
-  lsp.CompletionClientCapabilities completionCapabilities,
-  HashSet<lsp.CompletionItemKind> supportedCompletionItemKinds,
+  LspClientCapabilities capabilities,
   String file,
   int offset,
   server.IncludedSuggestionSet includedSuggestionSet,
@@ -225,8 +224,7 @@
   @required bool includeCommitCharacters,
   @required bool completeFunctionCalls,
 }) {
-  final supportsSnippets =
-      completionCapabilities?.completionItem?.snippetSupport == true;
+  final supportsSnippets = capabilities.completionSnippets;
 
   String completion;
   switch (declaration.kind) {
@@ -286,19 +284,14 @@
   final insertTextFormat = insertTextInfo.last;
   final isMultilineCompletion = insertText.contains('\n');
 
-  final supportsDeprecatedFlag =
-      completionCapabilities?.completionItem?.deprecatedSupport == true;
-  final supportedTags =
-      completionCapabilities?.completionItem?.tagSupport?.valueSet ?? const [];
-  final supportsDeprecatedTag =
-      supportedTags.contains(lsp.CompletionItemTag.Deprecated);
-  final supportsAsIsInsertMode = completionCapabilities
-          ?.completionItem?.insertTextModeSupport?.valueSet
-          ?.contains(InsertTextMode.asIs) ==
-      true;
+  final supportsDeprecatedFlag = capabilities.completionDeprecatedFlag;
+  final supportsDeprecatedTag = capabilities.completionItemTags
+      .contains(lsp.CompletionItemTag.Deprecated);
+  final supportsAsIsInsertMode =
+      capabilities.completionInsertTextModes.contains(InsertTextMode.asIs);
 
   final completionKind = declarationKindToCompletionItemKind(
-      supportedCompletionItemKinds, declaration.kind);
+      capabilities.completionItemKinds, declaration.kind);
 
   var relevanceBoost = 0;
   if (declaration.relevanceTags != null) {
@@ -354,11 +347,11 @@
 }
 
 lsp.CompletionItemKind elementKindToCompletionItemKind(
-  HashSet<lsp.CompletionItemKind> clientSupportedCompletionKinds,
+  Set<lsp.CompletionItemKind> supportedCompletionKinds,
   server.ElementKind kind,
 ) {
   bool isSupported(lsp.CompletionItemKind kind) =>
-      clientSupportedCompletionKinds.contains(kind);
+      supportedCompletionKinds.contains(kind);
 
   List<lsp.CompletionItemKind> getKindPreferences() {
     switch (kind) {
@@ -419,11 +412,10 @@
 }
 
 lsp.SymbolKind elementKindToSymbolKind(
-  HashSet<lsp.SymbolKind> clientSupportedSymbolKinds,
+  Set<lsp.SymbolKind> supportedSymbolKinds,
   server.ElementKind kind,
 ) {
-  bool isSupported(lsp.SymbolKind kind) =>
-      clientSupportedSymbolKinds.contains(kind);
+  bool isSupported(lsp.SymbolKind kind) => supportedSymbolKinds.contains(kind);
 
   List<lsp.SymbolKind> getKindPreferences() {
     switch (kind) {
@@ -496,7 +488,7 @@
 String getCompletionDetail(
   server.CompletionSuggestion suggestion,
   lsp.CompletionItemKind completionKind,
-  bool clientSupportsDeprecated,
+  bool supportsDeprecated,
 ) {
   final hasElement = suggestion.element != null;
   final hasParameters = hasElement &&
@@ -508,9 +500,8 @@
   final hasParameterType =
       suggestion.parameterType != null && suggestion.parameterType.isNotEmpty;
 
-  final prefix = clientSupportsDeprecated || !suggestion.isDeprecated
-      ? ''
-      : '(Deprecated) ';
+  final prefix =
+      supportsDeprecated || !suggestion.isDeprecated ? '' : '(Deprecated) ';
 
   if (completionKind == lsp.CompletionItemKind.Property) {
     // Setters appear as methods with one arg but they also cause getters to not
@@ -542,16 +533,15 @@
 String getDeclarationCompletionDetail(
   dec.Declaration declaration,
   lsp.CompletionItemKind completionKind,
-  bool clientSupportsDeprecated,
+  bool supportsDeprecated,
 ) {
   final hasParameters =
       declaration.parameters != null && declaration.parameters.isNotEmpty;
   final hasReturnType =
       declaration.returnType != null && declaration.returnType.isNotEmpty;
 
-  final prefix = clientSupportsDeprecated || !declaration.isDeprecated
-      ? ''
-      : '(Deprecated) ';
+  final prefix =
+      supportsDeprecated || !declaration.isDeprecated ? '' : '(Deprecated) ';
 
   if (completionKind == lsp.CompletionItemKind.Property) {
     // Setters appear as methods with one arg but they also cause getters to not
@@ -583,7 +573,7 @@
 }
 
 List<lsp.DiagnosticTag> getDiagnosticTags(
-    HashSet<lsp.DiagnosticTag> supportedTags, server.AnalysisError error) {
+    Set<lsp.DiagnosticTag> supportedTags, server.AnalysisError error) {
   if (supportedTags == null) {
     return null;
   }
@@ -637,8 +627,6 @@
   );
 }
 
-List<T> nullIfEmpty<T>(List<T> items) => items.isEmpty ? null : items;
-
 /// Returns the file system path for a TextDocumentIdentifier.
 ErrorOr<String> pathOfDoc(lsp.TextDocumentIdentifier doc) =>
     pathOfUri(Uri.tryParse(doc?.uri));
@@ -753,12 +741,12 @@
 }
 
 lsp.CompletionItemKind suggestionKindToCompletionItemKind(
-  HashSet<lsp.CompletionItemKind> clientSupportedCompletionKinds,
+  Set<lsp.CompletionItemKind> supportedCompletionKinds,
   server.CompletionSuggestionKind kind,
   String label,
 ) {
   bool isSupported(lsp.CompletionItemKind kind) =>
-      clientSupportedCompletionKinds.contains(kind);
+      supportedCompletionKinds.contains(kind);
 
   List<lsp.CompletionItemKind> getKindPreferences() {
     switch (kind) {
@@ -823,8 +811,7 @@
 }
 
 lsp.CompletionItem toCompletionItem(
-  lsp.CompletionClientCapabilities completionCapabilities,
-  HashSet<lsp.CompletionItemKind> supportedCompletionItemKinds,
+  LspClientCapabilities capabilities,
   server.LineInfo lineInfo,
   server.CompletionSuggestion suggestion,
   int replacementOffset,
@@ -865,27 +852,21 @@
     label += suggestion.parameterNames?.isNotEmpty ?? false ? '(…)' : '()';
   }
 
-  final supportsDeprecatedFlag =
-      completionCapabilities?.completionItem?.deprecatedSupport == true;
-  final supportedTags =
-      completionCapabilities?.completionItem?.tagSupport?.valueSet ?? const [];
-  final supportsDeprecatedTag =
-      supportedTags.contains(lsp.CompletionItemTag.Deprecated);
-  final formats = completionCapabilities?.completionItem?.documentationFormat;
-  final supportsSnippets =
-      completionCapabilities?.completionItem?.snippetSupport == true;
-  final supportsInsertReplace =
-      completionCapabilities?.completionItem?.insertReplaceSupport == true;
-  final supportsAsIsInsertMode = completionCapabilities
-          ?.completionItem?.insertTextModeSupport?.valueSet
-          ?.contains(InsertTextMode.asIs) ==
-      true;
+  final supportsCompletionDeprecatedFlag =
+      capabilities.completionDeprecatedFlag;
+  final supportsDeprecatedTag = capabilities.completionItemTags
+      .contains(lsp.CompletionItemTag.Deprecated);
+  final formats = capabilities.completionDocumentationFormats;
+  final supportsSnippets = capabilities.completionSnippets;
+  final supportsInsertReplace = capabilities.insertReplaceCompletionRanges;
+  final supportsAsIsInsertMode =
+      capabilities.completionInsertTextModes.contains(InsertTextMode.asIs);
 
   final completionKind = suggestion.element != null
       ? elementKindToCompletionItemKind(
-          supportedCompletionItemKinds, suggestion.element.kind)
+          capabilities.completionItemKinds, suggestion.element.kind)
       : suggestionKindToCompletionItemKind(
-          supportedCompletionItemKinds, suggestion.kind, label);
+          capabilities.completionItemKinds, suggestion.kind, label);
 
   final insertTextInfo = _buildInsertText(
     supportsSnippets: supportsSnippets,
@@ -917,10 +898,12 @@
         includeCommitCharacters ? dartCompletionCommitCharacters : null,
     data: resolutionData,
     detail: getCompletionDetail(suggestion, completionKind,
-        supportsDeprecatedFlag || supportsDeprecatedTag),
+        supportsCompletionDeprecatedFlag || supportsDeprecatedTag),
     documentation:
         asStringOrMarkupContent(formats, cleanDartdoc(suggestion.docComplete)),
-    deprecated: supportsDeprecatedFlag && suggestion.isDeprecated ? true : null,
+    deprecated: supportsCompletionDeprecatedFlag && suggestion.isDeprecated
+        ? true
+        : null,
     // Relevance is a number, highest being best. LSP does text sort so subtract
     // from a large number so that a text sort will result in the correct order.
     // 555 -> 999455
@@ -959,7 +942,7 @@
 lsp.Diagnostic toDiagnostic(
   server.ResolvedUnitResult result,
   server.AnalysisError error, {
-  @required HashSet<lsp.DiagnosticTag> supportedTags,
+  @required Set<lsp.DiagnosticTag> supportedTags,
   server.ErrorSeverity errorSeverity,
 }) {
   var errorCode = error.errorCode;
@@ -1168,7 +1151,7 @@
   );
 }
 
-lsp.SignatureHelp toSignatureHelp(List<lsp.MarkupKind> preferredFormats,
+lsp.SignatureHelp toSignatureHelp(Set<lsp.MarkupKind> preferredFormats,
     server.AnalysisGetSignatureResult signature) {
   // For now, we only support returning one (though we may wish to use named
   // args. etc. to provide one for each possible "next" option when the cursor
@@ -1282,15 +1265,12 @@
 }
 
 lsp.WorkspaceEdit toWorkspaceEdit(
-  lsp.ClientCapabilitiesWorkspace capabilities,
+  LspClientCapabilities capabilities,
   List<FileEditInformation> edits,
 ) {
-  final clientSupportsTextDocumentEdits =
-      capabilities?.workspaceEdit?.documentChanges == true;
-  if (clientSupportsTextDocumentEdits) {
-    final clientSupportsCreate = capabilities?.workspaceEdit?.resourceOperations
-            ?.contains(ResourceOperationKind.Create) ??
-        false;
+  final supportsDocumentChanges = capabilities.documentChanges;
+  if (supportsDocumentChanges) {
+    final supportsCreate = capabilities.createResourceOperations;
     final changes = <
         Either4<lsp.TextDocumentEdit, lsp.CreateFile, lsp.RenameFile,
             lsp.DeleteFile>>[];
@@ -1299,7 +1279,7 @@
     // CreateFile + a TextDocumentEdit depending on whether it's a new
     // file.
     for (final edit in edits) {
-      if (clientSupportsCreate && edit.newFile) {
+      if (supportsCreate && edit.newFile) {
         final create = lsp.CreateFile(uri: edit.doc.uri);
         final createUnion = Either4<lsp.TextDocumentEdit, lsp.CreateFile,
             lsp.RenameFile, lsp.DeleteFile>.t2(create);
@@ -1335,7 +1315,7 @@
 }
 
 lsp.MarkupContent _asMarkup(
-    List<lsp.MarkupKind> preferredFormats, String content) {
+    Set<lsp.MarkupKind> preferredFormats, String content) {
   // It's not valid to call this function with a null format, as null formats
   // do not support MarkupContent. [asStringOrMarkupContent] is probably the
   // better choice.
diff --git a/pkg/analysis_server/lib/src/lsp/server_capabilities_computer.dart b/pkg/analysis_server/lib/src/lsp/server_capabilities_computer.dart
index f67feaa..808446a 100644
--- a/pkg/analysis_server/lib/src/lsp/server_capabilities_computer.dart
+++ b/pkg/analysis_server/lib/src/lsp/server_capabilities_computer.dart
@@ -4,6 +4,7 @@
 
 import 'package:analysis_server/lsp_protocol/protocol_generated.dart';
 import 'package:analysis_server/lsp_protocol/protocol_special.dart';
+import 'package:analysis_server/src/lsp/client_capabilities.dart';
 import 'package:analysis_server/src/lsp/constants.dart';
 import 'package:analysis_server/src/lsp/lsp_analysis_server.dart';
 import 'package:analysis_server/src/lsp/semantic_tokens/legend.dart';
@@ -128,18 +129,17 @@
   ServerCapabilitiesComputer(this._server);
 
   ServerCapabilities computeServerCapabilities(
-      ClientCapabilities clientCapabilities) {
-    final codeActionLiteralSupport =
-        clientCapabilities.textDocument?.codeAction?.codeActionLiteralSupport;
+      LspClientCapabilities clientCapabilities) {
+    final codeActionLiteralSupport = clientCapabilities.literalCodeActions;
 
-    final renameOptionsSupport =
-        clientCapabilities.textDocument?.rename?.prepareSupport ?? false;
+    final renameOptionsSupport = clientCapabilities.renameValidation;
 
     final enableFormatter = _server.clientConfiguration.enableSdkFormatter;
     final previewCommitCharacters =
         _server.clientConfiguration.previewCommitCharacters;
 
-    final dynamicRegistrations = ClientDynamicRegistrations(clientCapabilities);
+    final dynamicRegistrations =
+        ClientDynamicRegistrations(clientCapabilities.raw);
 
     // When adding new capabilities to the server that may apply to specific file
     // types, it's important to update
@@ -328,7 +328,7 @@
     }
 
     final dynamicRegistrations =
-        ClientDynamicRegistrations(_server.clientCapabilities);
+        ClientDynamicRegistrations(_server.clientCapabilities.raw);
 
     register(
       dynamicRegistrations.textSync,
diff --git a/pkg/analysis_server/lib/src/services/completion/dart/completion_ranking_internal.dart b/pkg/analysis_server/lib/src/services/completion/dart/completion_ranking_internal.dart
deleted file mode 100644
index c853976..0000000
--- a/pkg/analysis_server/lib/src/services/completion/dart/completion_ranking_internal.dart
+++ /dev/null
@@ -1,172 +0,0 @@
-// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
-// 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 'package:_fe_analyzer_shared/src/scanner/scanner.dart';
-import 'package:analysis_server/src/protocol_server.dart';
-import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
-import 'package:analyzer/dart/ast/ast.dart';
-
-/// Fuzzy matching between static analysis and model-predicted lexemes
-/// that considers pairs like "main" and "main()" to be equal.
-bool areCompletionsEquivalent(String dasCompletion, String modelCompletion) {
-  if (dasCompletion == null || modelCompletion == null) {
-    return false;
-  }
-  if (dasCompletion == modelCompletion) {
-    return true;
-  }
-
-  final index = dasCompletion.indexOf(RegExp(r'[^0-9A-Za-z_]'));
-  // Disallow '.' since this gives all methods under a model-predicted token
-  // the same probability.
-  if (index == -1 || dasCompletion[index] == '.') {
-    return false;
-  }
-
-  // Checks that the strings are equal until the first non-alphanumeric token.
-  return dasCompletion.substring(0, index) == modelCompletion;
-}
-
-/// Finds the previous n tokens occurring before the cursor.
-List<String> constructQuery(DartCompletionRequest request, int n) {
-  var token = getCursorToken(request);
-  if (request.offset == null) {
-    return null;
-  }
-
-  while (!isStopToken(token, request.offset)) {
-    token = token.previous;
-  }
-
-  if (token?.offset == null || token?.type == null) {
-    return null;
-  }
-
-  final result = <String>[];
-  for (var size = 0;
-      size < n && token != null && !token.isEof;
-      token = token.previous) {
-    if (!token.isSynthetic && token is! ErrorToken) {
-      // Omit the optional new keyword as we remove it at training time to
-      // prevent model from suggesting it.
-      if (token.lexeme == 'new') {
-        continue;
-      }
-
-      result.add(token.lexeme);
-      size += 1;
-    }
-  }
-
-  return result.reversed.toList(growable: false);
-}
-
-/// Maps included relevance tags formatted as
-/// '${element.librarySource.uri}::${element.name}' to element.name.
-String elementNameFromRelevanceTag(String tag) {
-  final index = tag.lastIndexOf('::');
-  if (index == -1) {
-    return null;
-  }
-
-  return tag.substring(index + 2);
-}
-
-Token getCurrentToken(DartCompletionRequest request) {
-  var token = getCursorToken(request);
-  while (!isStopToken(token.previous, request.offset)) {
-    token = token.previous;
-  }
-  return token;
-}
-
-/// Finds the token at which completion was requested.
-Token getCursorToken(DartCompletionRequest request) {
-  final entity = request.target.entity;
-  if (entity is AstNode) {
-    return entity.endToken;
-  }
-  return entity is Token ? entity : null;
-}
-
-bool isLiteral(String lexeme) {
-  if (lexeme == null || lexeme.isEmpty) {
-    return false;
-  }
-  if (RegExp(r'^[0-9]+$').hasMatch(lexeme)) {
-    // Check for number lexeme.
-    return true;
-  }
-  return isStringLiteral(lexeme);
-}
-
-bool isNotWhitespace(String lexeme) {
-  return lexeme
-      .replaceAll("'", '')
-      .split('')
-      .any((String chr) => !RegExp(r'\s').hasMatch(chr));
-}
-
-/// Step to previous token until we are at the first token before where the
-/// cursor is located.
-bool isStopToken(Token token, int cursorOffset) {
-  if (token == null) {
-    return true;
-  }
-  if (token.isSynthetic && !token.isEof) {
-    return false;
-  }
-  final position = token.offset + token.length;
-  if (position == cursorOffset) {
-    // If we are at the end of some token, step to previous only if the
-    // token is NOT an identifier, keyword, or literal. The rationale is that
-    // we want to keep moving if we have a situation like
-    // FooBar foo^ since foo is not a previous token to pass to the model.
-    return !token.lexeme[token.lexeme.length - 1]
-        .contains(RegExp(r'[0-9A-Za-z_]'));
-  }
-  // Stop if the token's location is strictly before the cursor, continue
-  // if the token's location is strictly after.
-  return position < cursorOffset;
-}
-
-bool isStringLiteral(String lexeme) {
-  if (lexeme == null || lexeme.isEmpty) {
-    return false;
-  }
-  return (lexeme.length > 1 && lexeme[0] == "'") ||
-      (lexeme.length > 2 && lexeme[0] == 'r' && lexeme[1] == "'");
-}
-
-bool isTokenDot(Token token) {
-  return token != null && !token.isSynthetic && token.lexeme.endsWith('.');
-}
-
-/// Filters the entries list down to only those which represent string literals
-/// and then strips quotes.
-List<MapEntry<String, double>> selectStringLiterals(List<MapEntry> entries) {
-  return entries
-      .where((MapEntry entry) =>
-          isStringLiteral(entry.key) && isNotWhitespace(entry.key))
-      .map<MapEntry<String, double>>(
-          (MapEntry entry) => MapEntry(trimQuotes(entry.key), entry.value))
-      .toList();
-}
-
-/// Tests whether all completion suggestions are for named arguments.
-bool testNamedArgument(List<CompletionSuggestion> suggestions) {
-  if (suggestions == null) {
-    return false;
-  }
-
-  return suggestions.any((CompletionSuggestion suggestion) =>
-      suggestion.kind == CompletionSuggestionKind.NAMED_ARGUMENT);
-}
-
-String trimQuotes(String input) {
-  final result = input[0] == '\'' ? input.substring(1) : input;
-  return result[result.length - 1] == '\''
-      ? result.substring(0, result.length - 1)
-      : result;
-}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/use_eq_eq_null.dart b/pkg/analysis_server/lib/src/services/correction/dart/use_eq_eq_null.dart
index 58fa8e6..86ad614 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/use_eq_eq_null.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/use_eq_eq_null.dart
@@ -14,6 +14,9 @@
   FixKind get fixKind => DartFixKind.USE_EQ_EQ_NULL;
 
   @override
+  FixKind get multiFixKind => DartFixKind.USE_EQ_EQ_NULL_MULTI;
+
+  @override
   Future<void> compute(ChangeBuilder builder) async {
     if (coveredNode is IsExpression) {
       var isExpression = coveredNode as IsExpression;
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index 270e10d..df6febc 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -323,6 +323,15 @@
         ],
       ),
     ],
+    HintCode.TYPE_CHECK_IS_NULL: [
+      FixInfo(
+        canBeAppliedToFile: true,
+        canBeBulkApplied: false,
+        generators: [
+          UseEqEqNull.newInstance,
+        ],
+      ),
+    ],
     CompileTimeErrorCode.UNDEFINED_CLASS_BOOLEAN: [
       FixInfo(
         canBeAppliedToFile: true,
diff --git a/pkg/analysis_server/lib/src/status/diagnostics.dart b/pkg/analysis_server/lib/src/status/diagnostics.dart
index da5a030..368b129 100644
--- a/pkg/analysis_server/lib/src/status/diagnostics.dart
+++ b/pkg/analysis_server/lib/src/status/diagnostics.dart
@@ -964,7 +964,7 @@
     if (server.clientCapabilities == null) {
       p('Client capabilities have not yet been received.');
     } else {
-      prettyJson(server.clientCapabilities.toJson());
+      prettyJson(server.clientCapabilities.raw.toJson());
     }
     buf.writeln('</div>');
 
diff --git a/pkg/analysis_server/pubspec.yaml b/pkg/analysis_server/pubspec.yaml
index 855f489..c968f5b 100644
--- a/pkg/analysis_server/pubspec.yaml
+++ b/pkg/analysis_server/pubspec.yaml
@@ -37,6 +37,5 @@
     path: ../analyzer_utilities
   logging: any
   matcher: any
-  mockito: any
   pedantic: ^1.9.0
   test_reflective_loader: any
diff --git a/pkg/analysis_server/test/lsp/mapping_test.dart b/pkg/analysis_server/test/lsp/mapping_test.dart
index 9d88d76..ce08941 100644
--- a/pkg/analysis_server/test/lsp/mapping_test.dart
+++ b/pkg/analysis_server/test/lsp/mapping_test.dart
@@ -2,8 +2,6 @@
 // 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/lsp_protocol/protocol_generated.dart' as lsp;
 import 'package:analysis_server/src/lsp/mapping.dart' as lsp;
 import 'package:analysis_server/src/protocol_server.dart' as server;
@@ -24,10 +22,10 @@
     // TYPE_PARAMETER maps to TypeParameter first, but since originally LSP
     // did not support it, it'll map to Variable if the client doesn't support
     // that.
-    var supportedKinds = HashSet.of([
+    var supportedKinds = {
       lsp.CompletionItemKind.TypeParameter,
       lsp.CompletionItemKind.Variable,
-    ]);
+    };
     var result = lsp.elementKindToCompletionItemKind(
       supportedKinds,
       server.ElementKind.TYPE_PARAMETER,
@@ -39,7 +37,7 @@
     // TYPE_PARAMETER maps to TypeParameter first, but since originally LSP
     // did not support it, it'll map to Variable if the client doesn't support
     // that.
-    var supportedKinds = HashSet.of([lsp.CompletionItemKind.Variable]);
+    var supportedKinds = {lsp.CompletionItemKind.Variable};
     var result = lsp.elementKindToCompletionItemKind(
       supportedKinds,
       server.ElementKind.TYPE_PARAMETER,
@@ -48,7 +46,7 @@
   }
 
   Future<void> test_completionItemKind_knownMapping() async {
-    final supportedKinds = HashSet.of([lsp.CompletionItemKind.Class]);
+    final supportedKinds = {lsp.CompletionItemKind.Class};
     final result = lsp.elementKindToCompletionItemKind(
       supportedKinds,
       server.ElementKind.CLASS,
@@ -57,7 +55,7 @@
   }
 
   Future<void> test_completionItemKind_notMapped() async {
-    var supportedKinds = HashSet<lsp.CompletionItemKind>();
+    var supportedKinds = <lsp.CompletionItemKind>{};
     var result = lsp.elementKindToCompletionItemKind(
       supportedKinds,
       server.ElementKind.UNKNOWN, // Unknown is not mapped.
@@ -66,7 +64,7 @@
   }
 
   Future<void> test_completionItemKind_notSupported() async {
-    var supportedKinds = HashSet<lsp.CompletionItemKind>();
+    var supportedKinds = <lsp.CompletionItemKind>{};
     var result = lsp.elementKindToCompletionItemKind(
       supportedKinds,
       server.ElementKind.CLASS,
diff --git a/pkg/analysis_server/test/services/completion/dart/completion_ranking_internal_test.dart b/pkg/analysis_server/test/services/completion/dart/completion_ranking_internal_test.dart
deleted file mode 100644
index 1510b22..0000000
--- a/pkg/analysis_server/test/services/completion/dart/completion_ranking_internal_test.dart
+++ /dev/null
@@ -1,269 +0,0 @@
-// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
-// 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 'package:analysis_server/src/protocol_server.dart';
-import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
-import 'package:analysis_server/src/services/completion/dart/completion_ranking_internal.dart';
-import 'package:analyzer/dart/ast/ast.dart';
-import 'package:analyzer/dart/ast/token.dart';
-import 'package:analyzer_plugin/src/utilities/completion/completion_target.dart';
-import 'package:mockito/mockito.dart';
-import 'package:test/test.dart';
-
-void main() {
-  group('getCursorToken', () {
-    MockCompletionRequest request;
-    MockCompletionTarget target;
-
-    setUp(() {
-      request = MockCompletionRequest();
-      target = MockCompletionTarget();
-      when(request.target).thenReturn(target);
-    });
-
-    test('areCompletionsEquivalent exact match', () {
-      expect(areCompletionsEquivalent('foo', 'foo'), equals(true));
-    });
-
-    test('areCompletionsEquivalent function call', () {
-      expect(areCompletionsEquivalent('foo()', 'foo'), equals(true));
-    });
-
-    test('areCompletionsEquivalent field name', () {
-      expect(areCompletionsEquivalent('foo: ,', 'foo'), equals(true));
-    });
-
-    test('areCompletionsEquivalent different name', () {
-      expect(areCompletionsEquivalent('foo', 'fooBar'), equals(false));
-    });
-
-    test('areCompletionsEquivalent method invocation', () {
-      expect(areCompletionsEquivalent('foo.bar()', 'foo'), equals(false));
-    });
-
-    test('getCursorToken AstNode', () {
-      final node = MockAstNode();
-      final token = MockToken();
-      when(target.entity).thenReturn(node);
-      when(node.endToken).thenReturn(token);
-      expect(getCursorToken(request), equals(token));
-    });
-
-    test('getCursorToken Token', () {
-      final token = MockToken();
-      when(target.entity).thenReturn(token);
-      expect(getCursorToken(request), equals(token));
-    });
-
-    test('getCursorToken null', () {
-      when(target.entity).thenReturn(null);
-      expect(getCursorToken(request), equals(null));
-    });
-  });
-
-  test('isStopToken null', () {
-    expect(isStopToken(null, 5), equals(true));
-  });
-
-  test('isStopToken synthetic', () {
-    final token = MockToken();
-    when(token.isSynthetic).thenReturn(true);
-    when(token.isEof).thenReturn(false);
-    expect(isStopToken(token, 5), equals(false));
-  });
-
-  test('isStopToken punctuation', () {
-    final token = MockToken();
-    when(token.isSynthetic).thenReturn(false);
-    when(token.offset).thenReturn(4);
-    when(token.length).thenReturn(1);
-    when(token.lexeme).thenReturn(')');
-    expect(isStopToken(token, 5), equals(true));
-  });
-
-  test('isStopToken alphabetic', () {
-    final token = MockToken();
-    when(token.isSynthetic).thenReturn(false);
-    when(token.offset).thenReturn(2);
-    when(token.length).thenReturn(3);
-    when(token.lexeme).thenReturn('foo');
-    expect(isStopToken(token, 5), equals(false));
-  });
-
-  test('isStringLiteral null', () {
-    expect(isStringLiteral(null), equals(false));
-  });
-
-  test('isStringLiteral empty string', () {
-    expect(isStringLiteral(''), equals(false));
-  });
-
-  test('isStringLiteral basic', () {
-    expect(isStringLiteral("'foo'"), equals(true));
-  });
-
-  test('isStringLiteral raw', () {
-    expect(isStringLiteral("r'foo'"), equals(true));
-  });
-
-  test('isLiteral string', () {
-    expect(isLiteral("'foo'"), equals(true));
-  });
-
-  test('isLiteral numeric', () {
-    expect(isLiteral('12345'), equals(true));
-  });
-
-  test('isLiteral not literal', () {
-    expect(isLiteral('foo'), equals(false));
-  });
-
-  test('isTokenDot dot', () {
-    final token = MockToken();
-    when(token.isSynthetic).thenReturn(false);
-    when(token.lexeme).thenReturn('.');
-    expect(isTokenDot(token), equals(true));
-  });
-
-  test('isTokenDot not dot', () {
-    final token = MockToken();
-    when(token.isSynthetic).thenReturn(false);
-    when(token.lexeme).thenReturn('foo');
-    expect(isTokenDot(token), equals(false));
-  });
-
-  test('isTokenDot synthetic', () {
-    final token = MockToken();
-    when(token.isSynthetic).thenReturn(true);
-    expect(isTokenDot(token), false);
-  });
-
-  test('getCurrentToken', () {
-    final one = MockToken();
-    final two = MockToken();
-    final three = MockToken();
-    when(three.previous).thenReturn(two);
-    when(two.previous).thenReturn(one);
-    final request = MockCompletionRequest();
-    final target = MockCompletionTarget();
-    when(request.offset).thenReturn(2);
-    when(request.target).thenReturn(target);
-    when(target.entity).thenReturn(three);
-    when(two.isSynthetic).thenReturn(true);
-    when(two.isEof).thenReturn(false);
-    when(one.isSynthetic).thenReturn(false);
-    when(one.offset).thenReturn(1);
-    when(one.length).thenReturn(3);
-    when(one.lexeme).thenReturn('foo');
-    expect(getCurrentToken(request), equals(one));
-  });
-
-  test('constructQuery', () {
-    final start = MockToken();
-    when(start.isSynthetic).thenReturn(true);
-    when(start.isEof).thenReturn(true);
-    final one = MockToken();
-    when(one.lexeme).thenReturn('class');
-    when(one.offset).thenReturn(0);
-    when(one.length).thenReturn(5);
-    when(one.isSynthetic).thenReturn(false);
-    when(one.isEof).thenReturn(false);
-    when(one.type).thenReturn(Keyword.CLASS);
-    final two = MockToken();
-    when(two.lexeme).thenReturn('Animal');
-    when(two.offset).thenReturn(6);
-    when(two.length).thenReturn(6);
-    when(two.isSynthetic).thenReturn(false);
-    when(one.previous).thenReturn(start);
-    when(two.previous).thenReturn(one);
-    when(two.type).thenReturn(TokenType.IDENTIFIER);
-    when(two.isEof).thenReturn(false);
-    final request = MockCompletionRequest();
-    final target = MockCompletionTarget();
-    when(request.offset).thenReturn(13);
-    when(request.target).thenReturn(target);
-    when(target.entity).thenReturn(two);
-    expect(constructQuery(request, 100), equals(['class', 'Animal']));
-  });
-
-  test('constructQuery cursor over paren', () {
-    final start = MockToken();
-    when(start.isSynthetic).thenReturn(true);
-    when(start.isEof).thenReturn(true);
-    final one = MockToken();
-    when(one.lexeme).thenReturn('main');
-    when(one.offset).thenReturn(0);
-    when(one.length).thenReturn(4);
-    when(one.isSynthetic).thenReturn(false);
-    when(one.isEof).thenReturn(false);
-    when(one.type).thenReturn(TokenType.IDENTIFIER);
-    final two = MockToken();
-    when(two.lexeme).thenReturn('(');
-    when(two.offset).thenReturn(5);
-    when(two.length).thenReturn(1);
-    when(two.isSynthetic).thenReturn(false);
-    when(one.previous).thenReturn(start);
-    when(two.previous).thenReturn(one);
-    when(two.type).thenReturn(TokenType.OPEN_PAREN);
-    when(two.isEof).thenReturn(false);
-    final request = MockCompletionRequest();
-    final target = MockCompletionTarget();
-    when(request.offset).thenReturn(6);
-    when(request.target).thenReturn(target);
-    when(target.entity).thenReturn(two);
-    expect(constructQuery(request, 50), equals(['main', '(']));
-  });
-
-  test('elementNameFromRelevanceTag', () {
-    final tag =
-        'package::flutter/src/widgets/preferred_size.dart::::PreferredSizeWidget';
-    expect(elementNameFromRelevanceTag(tag), equals('PreferredSizeWidget'));
-  });
-
-  test('selectStringLiterals', () {
-    final result = selectStringLiterals([
-      MapEntry('foo', 0.2),
-      MapEntry("'bar'", 0.3),
-      MapEntry('\'baz\'', 0.1),
-      MapEntry("'qu\'ux'", 0.4),
-    ]);
-    expect(result[0].key, equals('bar'));
-    expect(result[1].key, equals('baz'));
-    expect(result[2].key, equals('qu\'ux'));
-    expect(result, hasLength(3));
-  });
-
-  test('testNamedArgument', () {
-    expect(testNamedArgument([]), equals(false));
-    expect(testNamedArgument(null), equals(false));
-    expect(
-        testNamedArgument([
-          CompletionSuggestion(CompletionSuggestionKind.NAMED_ARGUMENT, 1,
-              'title: ,', 8, 0, false, false)
-        ]),
-        equals(true));
-    expect(
-        testNamedArgument([
-          CompletionSuggestion(
-              CompletionSuggestionKind.IDENTIFIER, 1, 'foo', 3, 0, false, false)
-        ]),
-        equals(false));
-    expect(
-        testNamedArgument([
-          CompletionSuggestion(CompletionSuggestionKind.NAMED_ARGUMENT, 1,
-              'title: ,', 8, 0, false, false),
-          CompletionSuggestion(CompletionSuggestionKind.IDENTIFIER, 1, 'foo', 3,
-              0, false, false),
-        ]),
-        equals(true));
-  });
-}
-
-class MockAstNode extends Mock implements AstNode {}
-
-class MockCompletionRequest extends Mock implements DartCompletionRequest {}
-
-class MockCompletionTarget extends Mock implements CompletionTarget {}
-
-class MockToken extends Mock implements Token {}
diff --git a/pkg/analysis_server/test/services/completion/dart/test_all.dart b/pkg/analysis_server/test/services/completion/dart/test_all.dart
index bf9d30b..c8ccce6 100644
--- a/pkg/analysis_server/test/services/completion/dart/test_all.dart
+++ b/pkg/analysis_server/test/services/completion/dart/test_all.dart
@@ -7,8 +7,6 @@
 import 'arglist_contributor_test.dart' as arglist_test;
 import 'combinator_contributor_test.dart' as combinator_test;
 import 'completion_manager_test.dart' as completion_manager;
-import 'completion_ranking_internal_test.dart'
-    as completion_ranking_internal_test;
 import 'extension_member_contributor_test.dart' as extension_member_contributor;
 import 'field_formal_contributor_test.dart' as field_formal_contributor_test;
 import 'imported_reference_contributor_test.dart' as imported_ref_test;
@@ -31,7 +29,6 @@
     arglist_test.main();
     combinator_test.main();
     completion_manager.main();
-    completion_ranking_internal_test.main();
     extension_member_contributor.main();
     field_formal_contributor_test.main();
     imported_ref_test.main();
diff --git a/pkg/analysis_server/test/src/services/correction/fix/fix_in_file_test.dart b/pkg/analysis_server/test/src/services/correction/fix/fix_in_file_test.dart
index c03d633b..482894f 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/fix_in_file_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/fix_in_file_test.dart
@@ -88,7 +88,6 @@
 ''');
   }
 
-  @FailingTest(reason: 'enable once nonLintProducers are supported')
   Future<void> test_fix_nonLint_isNull() async {
     await resolveTestCode('''
 bool f(p, q) {
diff --git a/pkg/analysis_server/test/src/services/correction/fix/use_eq_eq_null_test.dart b/pkg/analysis_server/test/src/services/correction/fix/use_eq_eq_null_test.dart
index 9a6d0ed..2e53d54 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/use_eq_eq_null_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/use_eq_eq_null_test.dart
@@ -11,11 +11,33 @@
 
 void main() {
   defineReflectiveSuite(() {
+    defineReflectiveTests(UseEqEqNullMultiTest);
     defineReflectiveTests(UseEqEqNullTest);
   });
 }
 
 @reflectiveTest
+class UseEqEqNullMultiTest extends FixProcessorTest {
+  @override
+  FixKind get kind => DartFixKind.USE_EQ_EQ_NULL_MULTI;
+
+  Future<void> test_isNull_all() async {
+    await resolveTestCode('''
+main(p, q) {
+  p is Null;
+  q is Null;
+}
+''');
+    await assertHasFixAllFix(HintCode.TYPE_CHECK_IS_NULL, '''
+main(p, q) {
+  p == null;
+  q == null;
+}
+''');
+  }
+}
+
+@reflectiveTest
 class UseEqEqNullTest extends FixProcessorTest {
   @override
   FixKind get kind => DartFixKind.USE_EQ_EQ_NULL;
@@ -32,20 +54,4 @@
 }
 ''');
   }
-
-  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/45026')
-  Future<void> test_isNull_all() async {
-    await resolveTestCode('''
-main(p, q) {
-  p is Null;
-  q is Null;
-}
-''');
-    await assertHasFixAllFix(HintCode.TYPE_CHECK_IS_NULL, '''
-main(p, q) {
-  p == null;
-  q == null;
-}
-''');
-  }
 }
diff --git a/pkg/analyzer/lib/src/command_line/arguments.dart b/pkg/analyzer/lib/src/command_line/arguments.dart
index 4b72704..4d71ec1 100644
--- a/pkg/analyzer/lib/src/command_line/arguments.dart
+++ b/pkg/analyzer/lib/src/command_line/arguments.dart
@@ -4,13 +4,17 @@
 
 import 'dart:collection';
 
+import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/src/context/builder.dart';
+import 'package:analyzer/src/dart/analysis/experiments.dart';
 import 'package:analyzer/src/generated/engine.dart';
 import 'package:args/args.dart';
 
 const String analysisOptionsFileOption = 'options';
+const String defaultLanguageVersionOption = 'default-language-version';
 const String defineVariableOption = 'D';
+const String enableExperimentOption = 'enable-experiment';
 const String enableInitializingFormalAccessFlag = 'initializing-formal-access';
 @deprecated
 const String enableSuperMixinFlag = 'supermixin';
@@ -34,6 +38,14 @@
     }
   }
 
+  if (args.wasParsed(enableExperimentOption)) {
+    var flags = args[enableExperimentOption] as List<String>;
+    options.contextFeatures = FeatureSet.fromEnableFlags2(
+      sdkLanguageVersion: ExperimentStatus.currentVersion,
+      flags: flags,
+    );
+  }
+
   if (args.wasParsed(implicitCastsFlag)) {
     options.implicitCasts = args[implicitCastsFlag];
     verbose('$implicitCastsFlag = ${options.implicitCasts}');
@@ -136,6 +148,10 @@
       help: 'Disable declaration casts in strong mode (https://goo.gl/cTLz40)\n'
           'This option is now ignored and will be removed in a future release.',
       hide: ddc && hide);
+  parser.addMultiOption(enableExperimentOption,
+      help: 'Enable one or more experimental features. If multiple features '
+          'are being added, they should be comma separated.',
+      splitCommas: true);
   parser.addFlag(implicitCastsFlag,
       negatable: true,
       help: 'Disable implicit casts in strong mode (https://goo.gl/cTLz40).',
diff --git a/pkg/analyzer/lib/src/context/builder.dart b/pkg/analyzer/lib/src/context/builder.dart
index cb27bce..f8ac509 100644
--- a/pkg/analyzer/lib/src/context/builder.dart
+++ b/pkg/analyzer/lib/src/context/builder.dart
@@ -102,10 +102,15 @@
 
   /// Return an analysis driver that is configured correctly to analyze code in
   /// the directory with the given [path].
-  AnalysisDriver buildDriver(ContextRoot contextRoot, Workspace workspace) {
+  AnalysisDriver buildDriver(ContextRoot contextRoot, Workspace workspace,
+      {void Function(AnalysisOptionsImpl)? updateAnalysisOptions}) {
     String path = contextRoot.root;
 
     var options = getAnalysisOptions(path, workspace, contextRoot: contextRoot);
+
+    if (updateAnalysisOptions != null) {
+      updateAnalysisOptions(options);
+    }
     //_processAnalysisOptions(context, optionMap);
 
     SummaryDataStore? summaryData;
diff --git a/pkg/analyzer/lib/src/dart/analysis/analysis_context_collection.dart b/pkg/analyzer/lib/src/dart/analysis/analysis_context_collection.dart
index d610cce..4de4a89 100644
--- a/pkg/analyzer/lib/src/dart/analysis/analysis_context_collection.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/analysis_context_collection.dart
@@ -13,6 +13,7 @@
 import 'package:analyzer/src/dart/analysis/driver.dart';
 import 'package:analyzer/src/dart/analysis/file_state.dart';
 import 'package:analyzer/src/dart/analysis/performance_logger.dart';
+import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl;
 import 'package:cli_util/cli_util.dart';
 
 /// An implementation of [AnalysisContextCollection].
@@ -39,6 +40,7 @@
     bool retainDataForTesting = false,
     String? sdkPath,
     AnalysisDriverScheduler? scheduler,
+    void Function(AnalysisOptionsImpl)? updateAnalysisOptions,
   }) : resourceProvider =
             resourceProvider ?? PhysicalResourceProvider.INSTANCE {
     sdkPath ??= getSdkPath();
@@ -71,6 +73,7 @@
         retainDataForTesting: retainDataForTesting,
         sdkPath: sdkPath,
         scheduler: scheduler,
+        updateAnalysisOptions: updateAnalysisOptions,
       );
       contexts.add(context);
     }
diff --git a/pkg/analyzer/lib/src/dart/analysis/context_builder.dart b/pkg/analyzer/lib/src/dart/analysis/context_builder.dart
index 4fcd8c8..ffead36 100644
--- a/pkg/analyzer/lib/src/dart/analysis/context_builder.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/context_builder.dart
@@ -20,6 +20,7 @@
     show FileContentOverlay;
 import 'package:analyzer/src/dart/analysis/performance_logger.dart'
     show PerformanceLog;
+import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl;
 import 'package:analyzer/src/generated/sdk.dart' show DartSdkManager;
 import 'package:analyzer/src/generated/source.dart' show ContentCache;
 import 'package:cli_util/cli_util.dart';
@@ -37,19 +38,21 @@
             resourceProvider ?? PhysicalResourceProvider.INSTANCE;
 
   @override
-  AnalysisContext createContext(
-      {ByteStore? byteStore,
-      required ContextRoot contextRoot,
-      DeclaredVariables? declaredVariables,
-      bool drainStreams = true,
-      bool enableIndex = false,
-      FileContentOverlay? fileContentOverlay,
-      List<String>? librarySummaryPaths,
-      PerformanceLog? performanceLog,
-      bool retainDataForTesting = false,
-      AnalysisDriverScheduler? scheduler,
-      String? sdkPath,
-      String? sdkSummaryPath}) {
+  AnalysisContext createContext({
+    ByteStore? byteStore,
+    required ContextRoot contextRoot,
+    DeclaredVariables? declaredVariables,
+    bool drainStreams = true,
+    bool enableIndex = false,
+    FileContentOverlay? fileContentOverlay,
+    List<String>? librarySummaryPaths,
+    PerformanceLog? performanceLog,
+    bool retainDataForTesting = false,
+    AnalysisDriverScheduler? scheduler,
+    String? sdkPath,
+    String? sdkSummaryPath,
+    void Function(AnalysisOptionsImpl)? updateAnalysisOptions,
+  }) {
     // TODO(scheglov) Remove this, and make `sdkPath` required.
     sdkPath ??= getSdkPath();
     ArgumentError.checkNotNull(sdkPath, 'sdkPath');
@@ -93,8 +96,11 @@
     old.ContextRoot oldContextRoot = old.ContextRoot(
         contextRoot.root.path, contextRoot.excludedPaths.toList(),
         pathContext: resourceProvider.pathContext);
-    AnalysisDriver driver =
-        builder.buildDriver(oldContextRoot, contextRoot.workspace);
+    AnalysisDriver driver = builder.buildDriver(
+      oldContextRoot,
+      contextRoot.workspace,
+      updateAnalysisOptions: updateAnalysisOptions,
+    );
 
     // AnalysisDriver reports results into streams.
     // We need to drain these streams to avoid memory leak.
diff --git a/pkg/analyzer/test/src/command_line/arguments_test.dart b/pkg/analyzer/test/src/command_line/arguments_test.dart
index 83b2bda..139dcda5 100644
--- a/pkg/analyzer/test/src/command_line/arguments_test.dart
+++ b/pkg/analyzer/test/src/command_line/arguments_test.dart
@@ -83,7 +83,7 @@
   void test_defineAnalysisArguments() {
     ArgParser parser = ArgParser();
     defineAnalysisArguments(parser);
-    expect(parser.options, hasLength(11));
+    expect(parser.options, hasLength(12));
   }
 
   void test_extractDefinedVariables() {
diff --git a/pkg/analyzer/test/src/dart/analysis/context_locator_test.dart b/pkg/analyzer/test/src/dart/analysis/context_locator_test.dart
index 5869b63..709a567 100644
--- a/pkg/analyzer/test/src/dart/analysis/context_locator_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/context_locator_test.dart
@@ -1293,13 +1293,15 @@
     _assertAnalyzedFiles(root, pathList);
   }
 
-  void _assertBasicWorkspace(Workspace _workspace, String root) {
+  void _assertBasicWorkspace(Workspace _workspace, String posixRoot) {
     var workspace = _workspace as BasicWorkspace;
+    var root = convertPath(posixRoot);
     expect(workspace.root, root);
   }
 
-  void _assertBazelWorkspace(Workspace _workspace, String root) {
+  void _assertBazelWorkspace(Workspace _workspace, String posixRoot) {
     var workspace = _workspace as BazelWorkspace;
+    var root = convertPath(posixRoot);
     expect(workspace.root, root);
   }
 
@@ -1310,8 +1312,9 @@
     }
   }
 
-  void _assertPubWorkspace(Workspace _workspace, String root) {
+  void _assertPubWorkspace(Workspace _workspace, String posixRoot) {
     var workspace = _workspace as PubWorkspace;
+    var root = convertPath(posixRoot);
     expect(workspace.root, root);
   }
 
diff --git a/pkg/analyzer/test/src/dart/analysis/context_root_test.dart b/pkg/analyzer/test/src/dart/analysis/context_root_test.dart
index 3ba688b..90eb23e 100644
--- a/pkg/analyzer/test/src/dart/analysis/context_root_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/context_root_test.dart
@@ -61,7 +61,7 @@
 
     var root = _createContextRoot(rootPath);
     root.included.add(includedFile);
-    _addGlob(root, '$rootPath/lib/a*.dart');
+    _addGlob(root, 'lib/a*.dart');
 
     // Explicitly included, so analyzed even if excluded by a glob.
     expect(root.isAnalyzed(includedFile.path), isTrue);
@@ -91,8 +91,8 @@
 
     var root = _createContextRoot(rootPath);
     root.included.add(includedFolder);
-    _addGlob(root, '$rootPath/lib/src/**');
-    _addGlob(root, '$rootPath/lib/**.g.dart');
+    _addGlob(root, 'lib/src/**');
+    _addGlob(root, 'lib/**.g.dart');
 
     // Explicitly included, so analyzed even if excluded by a glob.
     expect(root.isAnalyzed(includedFolder.path), isTrue);
@@ -198,8 +198,13 @@
     expect(contextRoot.isAnalyzed(folderPath), isTrue);
   }
 
-  void _addGlob(ContextRootImpl root, String pattern) {
-    var glob = Glob(pattern, context: resourceProvider.pathContext);
+  void _addGlob(ContextRootImpl root, String posixPattern) {
+    var pathContext = root.resourceProvider.pathContext;
+    var pattern = posix.joinAll([
+      ...pathContext.split(root.root.path),
+      ...posix.split(posixPattern),
+    ]);
+    var glob = Glob(pattern, context: pathContext);
     root.excludedGlobs.add(glob);
   }
 
diff --git a/pkg/analyzer_cli/lib/src/driver.dart b/pkg/analyzer_cli/lib/src/driver.dart
index a98502e..dde233c 100644
--- a/pkg/analyzer_cli/lib/src/driver.dart
+++ b/pkg/analyzer_cli/lib/src/driver.dart
@@ -11,13 +11,15 @@
 import 'package:analyzer/error/listener.dart';
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/file_system/physical_file_system.dart';
+import 'package:analyzer/src/command_line/arguments.dart'
+    show applyAnalysisOptionFlags;
 import 'package:analyzer/src/dart/analysis/analysis_context_collection.dart';
 import 'package:analyzer/src/dart/analysis/byte_store.dart';
 import 'package:analyzer/src/dart/analysis/driver.dart';
 import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart';
-import 'package:analyzer/src/dart/analysis/experiments.dart';
 import 'package:analyzer/src/dart/analysis/file_state.dart';
 import 'package:analyzer/src/dart/analysis/results.dart';
+import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl;
 import 'package:analyzer/src/generated/engine.dart';
 import 'package:analyzer/src/generated/interner.dart';
 import 'package:analyzer/src/generated/java_engine.dart';
@@ -573,13 +575,9 @@
       packagesFile: _commandLineOptions.packageConfigPath,
       resourceProvider: _resourceProvider,
       sdkPath: _commandLineOptions.dartSdkPath,
+      updateAnalysisOptions: _updateAnalysisOptions,
     );
 
-    for (var _analysisContext in _collection.contexts) {
-      var analysisContext = _analysisContext as DriverBasedAnalysisContext;
-      _configureAnalysisOptionsFromCommandLineOptions(analysisContext);
-    }
-
     _setContextForPath(path);
     _folderContexts[parentFolder] = _analysisContext;
   }
@@ -597,36 +595,22 @@
     _pathList = pathList;
   }
 
-  /// TODO(scheglov) Patch options before we create the driver.
-  void _configureAnalysisOptionsFromCommandLineOptions(
-    DriverBasedAnalysisContext analysisContext,
-  ) {
-    var analysisDriver = analysisContext.driver;
-    var optionsImpl = analysisDriver.analysisOptions as AnalysisOptionsImpl;
-
-    if (_commandLineOptions.enabledExperiments != null) {
-      optionsImpl.contextFeatures = FeatureSet.fromEnableFlags2(
-        sdkLanguageVersion: ExperimentStatus.currentVersion,
-        flags: _commandLineOptions.enabledExperiments,
-      );
-    }
-
-    if (_commandLineOptions.defaultLanguageVersion != null) {
-      var nonPackageLanguageVersion = Version.parse(
-        _commandLineOptions.defaultLanguageVersion + '.0',
-      );
-      optionsImpl.nonPackageLanguageVersion = nonPackageLanguageVersion;
-      optionsImpl.nonPackageFeatureSet = FeatureSet.latestLanguageVersion()
-          .restrictToVersion(nonPackageLanguageVersion);
-    }
-
-    analysisDriver.configure(
-      analysisOptions: optionsImpl,
-    );
-  }
-
   void _setContextForPath(String path) {
     var analysisContext = _collection.contextFor(path);
     _analysisContext = analysisContext as DriverBasedAnalysisContext;
   }
+
+  void _updateAnalysisOptions(AnalysisOptionsImpl analysisOptions) {
+    var args = _commandLineOptions.contextBuilderOptions.argResults;
+    applyAnalysisOptionFlags(analysisOptions, args);
+
+    var defaultLanguageVersion = _commandLineOptions.defaultLanguageVersion;
+    if (defaultLanguageVersion != null) {
+      var nonPackageLanguageVersion =
+          Version.parse('$defaultLanguageVersion.0');
+      analysisOptions.nonPackageLanguageVersion = nonPackageLanguageVersion;
+      analysisOptions.nonPackageFeatureSet = FeatureSet.latestLanguageVersion()
+          .restrictToVersion(nonPackageLanguageVersion);
+    }
+  }
 }
diff --git a/pkg/analyzer_cli/lib/src/options.dart b/pkg/analyzer_cli/lib/src/options.dart
index fc2ad75..faba081 100644
--- a/pkg/analyzer_cli/lib/src/options.dart
+++ b/pkg/analyzer_cli/lib/src/options.dart
@@ -308,11 +308,6 @@
           help: 'Print the analyzer version.',
           defaultsTo: false,
           negatable: false)
-      ..addMultiOption('enable-experiment',
-          help:
-              'Enable one or more experimental features. If multiple features '
-              'are being added, they should be comma separated.',
-          splitCommas: true)
       ..addFlag('no-hints',
           help: 'Do not show hint results.',
           defaultsTo: false,
diff --git a/runtime/vm/virtual_memory_posix.cc b/runtime/vm/virtual_memory_posix.cc
index 9ae49f6..dd07c76 100644
--- a/runtime/vm/virtual_memory_posix.cc
+++ b/runtime/vm/virtual_memory_posix.cc
@@ -76,10 +76,18 @@
 void VirtualMemory::Init() {
 #if defined(DART_COMPRESSED_POINTERS)
   if (VirtualMemoryCompressedHeap::GetRegion() == nullptr) {
-    VirtualMemoryCompressedHeap::Init(GenericMapAligned(
+    void* address = GenericMapAligned(
         PROT_READ | PROT_WRITE, kCompressedHeapSize, kCompressedHeapAlignment,
         kCompressedHeapSize + kCompressedHeapAlignment,
-        MAP_PRIVATE | MAP_ANONYMOUS));
+        MAP_PRIVATE | MAP_ANONYMOUS);
+    if (address == nullptr) {
+      int error = errno;
+      const int kBufferSize = 1024;
+      char error_buf[kBufferSize];
+      FATAL2("Failed to reserve region for compressed heap: %d (%s)", error,
+             Utils::StrError(error, error_buf, kBufferSize));
+    }
+    VirtualMemoryCompressedHeap::Init(address);
   }
 #endif  // defined(DART_COMPRESSED_POINTERS)
 
@@ -328,7 +336,7 @@
 #endif  // defined(HOST_OS_MACOS)
   void* address =
       GenericMapAligned(prot, size, alignment, allocated_size, map_flags);
-  if (address == MAP_FAILED) {
+  if (address == nullptr) {
     return nullptr;
   }
 
diff --git a/runtime/vm/virtual_memory_win.cc b/runtime/vm/virtual_memory_win.cc
index f7322fb..994833d 100644
--- a/runtime/vm/virtual_memory_win.cc
+++ b/runtime/vm/virtual_memory_win.cc
@@ -55,9 +55,20 @@
   page_size_ = CalculatePageSize();
 
 #if defined(DART_COMPRESSED_POINTERS)
-  VirtualMemoryCompressedHeap::Init(AllocateAlignedImpl(
-      kCompressedHeapSize, kCompressedHeapAlignment,
-      kCompressedHeapSize + kCompressedHeapAlignment, PAGE_READWRITE, nullptr));
+  if (VirtualMemoryCompressedHeap::GetRegion() == nullptr) {
+    void* address =
+        AllocateAlignedImpl(kCompressedHeapSize, kCompressedHeapAlignment,
+                            kCompressedHeapSize + kCompressedHeapAlignment,
+                            PAGE_READWRITE, nullptr);
+    if (address == nullptr) {
+      int error = GetLastError();
+      const int kBufferSize = 1024;
+      char error_buf[kBufferSize];
+      FATAL2("Failed to reserve region for compressed heap: %d (%s)", error,
+             Utils::StrError(error, error_buf, kBufferSize));
+    }
+    VirtualMemoryCompressedHeap::Init(address);
+  }
 #endif  // defined(DART_COMPRESSED_POINTERS)
 }
 
diff --git a/tools/VERSION b/tools/VERSION
index 3a05acd..1a5366c 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 13
 PATCH 0
-PRERELEASE 104
+PRERELEASE 105
 PRERELEASE_PATCH 0
\ No newline at end of file