Allow disabling individual server features.

Change-Id: I4d06eaf76300c6784b88bd64ad322c306a276488
Bug: https://github.com/dart-lang/sdk/issues/36881
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/103182
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/lib/src/analysis_server.dart b/pkg/analysis_server/lib/src/analysis_server.dart
index ca242a8..d24223c 100644
--- a/pkg/analysis_server/lib/src/analysis_server.dart
+++ b/pkg/analysis_server/lib/src/analysis_server.dart
@@ -42,6 +42,7 @@
 import 'package:analysis_server/src/search/search_domain.dart';
 import 'package:analysis_server/src/server/detachable_filesystem_manager.dart';
 import 'package:analysis_server/src/server/diagnostic_server.dart';
+import 'package:analysis_server/src/server/features.dart';
 import 'package:analysis_server/src/services/search/search_engine.dart';
 import 'package:analysis_server/src/services/search/search_engine_internal.dart';
 import 'package:analysis_server/src/utilities/null_string_sink.dart';
@@ -206,10 +207,12 @@
     analysisDriverScheduler.status.listen(sendStatusNotificationNew);
     analysisDriverScheduler.start();
 
-    declarationsTracker = DeclarationsTracker(byteStore, resourceProvider);
-    declarationsTrackerData = DeclarationsTrackerData(declarationsTracker);
-    analysisDriverScheduler.outOfBandWorker =
-        CompletionLibrariesWorker(declarationsTracker);
+    if (options.featureSet.completion) {
+      declarationsTracker = DeclarationsTracker(byteStore, resourceProvider);
+      declarationsTrackerData = DeclarationsTrackerData(declarationsTracker);
+      analysisDriverScheduler.outOfBandWorker =
+          CompletionLibrariesWorker(declarationsTracker);
+    }
 
     contextManager = new ContextManagerImpl(
         resourceProvider,
@@ -485,7 +488,7 @@
   /// projects/contexts support.
   void setAnalysisRoots(String requestId, List<String> includedPaths,
       List<String> excludedPaths, Map<String, String> packageRoots) {
-    declarationsTracker.discardContexts();
+    declarationsTracker?.discardContexts();
     if (notificationManager != null) {
       notificationManager.setAnalysisRoots(includedPaths, excludedPaths);
     }
@@ -739,6 +742,9 @@
 
   /// Whether to enable parsing via the Fasta parser.
   bool useFastaParser = true;
+
+  /// The set of enabled features.
+  FeatureSet featureSet = FeatureSet();
 }
 
 class ServerContextManagerCallbacks extends ContextManagerCallbacks {
diff --git a/pkg/analysis_server/lib/src/analysis_server_abstract.dart b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
index c56a762..f307718 100644
--- a/pkg/analysis_server/lib/src/analysis_server_abstract.dart
+++ b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
@@ -136,7 +136,7 @@
 
   void addContextsToDeclarationsTracker() {
     for (var driver in driverMap.values) {
-      declarationsTracker.addContext(driver.analysisContext);
+      declarationsTracker?.addContext(driver.analysisContext);
       driver.resetUriResolution();
     }
   }
@@ -189,7 +189,7 @@
 
   DartdocDirectiveInfo getDartdocDirectiveInfoFor(ResolvedUnitResult result) {
     return declarationsTracker
-            .getContext(result.session.analysisContext)
+            ?.getContext(result.session.analysisContext)
             ?.dartdocDirectiveInfo ??
         new DartdocDirectiveInfo();
   }
@@ -288,7 +288,7 @@
   /// Notify the declarations tracker that the file with the given [path] was
   /// changed - added, updated, or removed.  Schedule processing of the file.
   void notifyDeclarationsTracker(String path) {
-    declarationsTracker.changeFile(path);
+    declarationsTracker?.changeFile(path);
     analysisDriverScheduler.notify(null);
   }
 }
diff --git a/pkg/analysis_server/lib/src/domain_completion.dart b/pkg/analysis_server/lib/src/domain_completion.dart
index 26461fc..295d23c 100644
--- a/pkg/analysis_server/lib/src/domain_completion.dart
+++ b/pkg/analysis_server/lib/src/domain_completion.dart
@@ -264,6 +264,14 @@
 
   @override
   Response handleRequest(Request request) {
+    if (!server.options.featureSet.completion) {
+      return Response.invalidParameter(
+        request,
+        'request',
+        'The completion feature is not enabled',
+      );
+    }
+
     return runZoned(() {
       String requestName = request.method;
 
@@ -335,8 +343,6 @@
    * Process a `completion.getSuggestions` request.
    */
   Future<void> processRequest(Request request) async {
-    // TODO(brianwilkerson) Determine whether this await is necessary.
-    await null;
     performance = new CompletionPerformance();
 
     // extract and validate params
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 e8f5112..47bf58e 100644
--- a/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
+++ b/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
@@ -172,10 +172,12 @@
     analysisDriverScheduler.status.listen(sendStatusNotification);
     analysisDriverScheduler.start();
 
-    declarationsTracker = DeclarationsTracker(byteStore, resourceProvider);
-    declarationsTrackerData = DeclarationsTrackerData(declarationsTracker);
-    analysisDriverScheduler.outOfBandWorker =
-        CompletionLibrariesWorker(declarationsTracker);
+    if (options.featureSet.completion) {
+      declarationsTracker = DeclarationsTracker(byteStore, resourceProvider);
+      declarationsTrackerData = DeclarationsTrackerData(declarationsTracker);
+      analysisDriverScheduler.outOfBandWorker =
+          CompletionLibrariesWorker(declarationsTracker);
+    }
 
     contextManager = new ContextManagerImpl(
         resourceProvider,
@@ -447,7 +449,7 @@
   }
 
   void setAnalysisRoots(List<String> includedPaths) {
-    declarationsTracker.discardContexts();
+    declarationsTracker?.discardContexts();
     final uniquePaths = HashSet<String>.of(includedPaths ?? const []);
     contextManager.setRoots(uniquePaths.toList(), [], {});
     addContextsToDeclarationsTracker();
diff --git a/pkg/analysis_server/lib/src/server/driver.dart b/pkg/analysis_server/lib/src/server/driver.dart
index 2937dee..7e8e322 100644
--- a/pkg/analysis_server/lib/src/server/driver.dart
+++ b/pkg/analysis_server/lib/src/server/driver.dart
@@ -14,6 +14,7 @@
 import 'package:analysis_server/src/server/detachable_filesystem_manager.dart';
 import 'package:analysis_server/src/server/dev_server.dart';
 import 'package:analysis_server/src/server/diagnostic_server.dart';
+import 'package:analysis_server/src/server/features.dart';
 import 'package:analysis_server/src/server/http_server.dart';
 import 'package:analysis_server/src/server/lsp_stdio_server.dart';
 import 'package:analysis_server/src/server/stdio_server.dart';
@@ -195,6 +196,18 @@
   static const String DARTPAD_OPTION = "dartpad";
 
   /**
+   * The name of the option to disable the completion feature.
+   */
+  static const String DISABLE_SERVER_FEATURE_COMPLETION =
+      "disable-server-feature-completion";
+
+  /**
+   * The name of the option to disable the completion feature.
+   */
+  static const String DISABLE_SERVER_FEATURE_SEARCH =
+      "disable-server-feature-search";
+
+  /**
    * The name of the option used to enable instrumentation.
    */
   static const String ENABLE_INSTRUMENTATION_OPTION = "enable-instrumentation";
@@ -354,6 +367,17 @@
       UriContributor.suggestFilePaths = false;
     }
 
+    {
+      bool disableCompletion = results[DISABLE_SERVER_FEATURE_COMPLETION];
+      bool disableSearch = results[DISABLE_SERVER_FEATURE_SEARCH];
+      if (disableCompletion || disableSearch) {
+        analysisServerOptions.featureSet = FeatureSet(
+          completion: !disableCompletion,
+          search: !disableSearch,
+        );
+      }
+    }
+
     if (results[HELP_OPTION]) {
       _printUsage(parser.parser, analytics, fromHelp: true);
       return null;
@@ -632,6 +656,10 @@
         help: 'enable DartPad specific functionality',
         defaultsTo: false,
         hide: true);
+    parser.addFlag(DISABLE_SERVER_FEATURE_COMPLETION,
+        help: 'disable all completion features', defaultsTo: false, hide: true);
+    parser.addFlag(DISABLE_SERVER_FEATURE_SEARCH,
+        help: 'disable all search features', defaultsTo: false, hide: true);
     parser.addFlag(ENABLE_INSTRUMENTATION_OPTION,
         help: "enable sending instrumentation information to a server",
         defaultsTo: false,
diff --git a/pkg/analysis_server/lib/src/server/features.dart b/pkg/analysis_server/lib/src/server/features.dart
new file mode 100644
index 0000000..fd02aa1
--- /dev/null
+++ b/pkg/analysis_server/lib/src/server/features.dart
@@ -0,0 +1,17 @@
+// 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.
+
+/// The set of features enabled in a server session.
+///
+/// When some features are not enabled, the server might avoid doing work
+/// that is only required for these features.
+class FeatureSet {
+  final bool completion;
+  final bool search;
+
+  FeatureSet({
+    this.completion = true,
+    this.search = true,
+  });
+}