Introduce a new flag to the Dart Analysis Server: --ux-experiment-1 which changes analysis roots to the be the current set of priority files.

Change-Id: I3a2aaec08b6529c6cbd23d8d16b4b9ca8dd4da59
Reviewed-on: https://dart-review.googlesource.com/75402
Reviewed-by: Jaime Wren <jwren@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Jaime Wren <jwren@google.com>
diff --git a/pkg/analysis_server/lib/src/analysis_server.dart b/pkg/analysis_server/lib/src/analysis_server.dart
index 88d5252..9b26c7f 100644
--- a/pkg/analysis_server/lib/src/analysis_server.dart
+++ b/pkg/analysis_server/lib/src/analysis_server.dart
@@ -1190,6 +1190,13 @@
    * Whether to enable parsing via the Fasta parser.
    */
   bool useFastaParser = true;
+
+  /**
+   * User Experience, Experiment #1. This experiment changes the notion of
+   * what analysis roots are and priority files: the analysis root is set to be
+   * the priority files' containing directory.
+   */
+  bool enableUXExperiment1 = false;
 }
 
 /**
diff --git a/pkg/analysis_server/lib/src/domain_analysis.dart b/pkg/analysis_server/lib/src/domain_analysis.dart
index a8e0598..79c4663 100644
--- a/pkg/analysis_server/lib/src/domain_analysis.dart
+++ b/pkg/analysis_server/lib/src/domain_analysis.dart
@@ -28,6 +28,7 @@
 import 'package:analyzer_plugin/protocol/protocol_constants.dart' as plugin;
 import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
 import 'package:analyzer_plugin/src/utilities/navigation/navigation.dart';
+import 'package:path/path.dart';
 
 // TODO(devoncarew): See #31456 for the tracking issue to remove this flag.
 final bool disableManageImportsOnPaste = true;
@@ -361,6 +362,10 @@
    * Implement the 'analysis.setAnalysisRoots' request.
    */
   Response setAnalysisRoots(Request request) {
+    if (server.options.enableUXExperiment1) {
+      return new AnalysisSetAnalysisRootsResult().toResponse(request.id);
+    }
+
     var params = new AnalysisSetAnalysisRootsParams.fromRequest(request);
     List<String> includedPathList = params.included;
     List<String> excludedPathList = params.excluded;
@@ -400,6 +405,46 @@
    */
   Response setPriorityFiles(Request request) {
     var params = new AnalysisSetPriorityFilesParams.fromRequest(request);
+
+    if (server.options.enableUXExperiment1) {
+      // If this experiment is enabled, set the analysis root to be the
+      // containing directory.
+
+      List<String> includedPathList = new List<String>();
+
+      // Reference the priority files, remove files that don't end in dart, yaml
+      // or html suffixes and sort from shortest to longest file paths.
+      List<String> priorityFiles = params.files;
+      priorityFiles.removeWhere((s) =>
+          !s.endsWith('.dart') && !s.endsWith('.yaml') && !s.endsWith('.html'));
+
+      Context pathContext = server.resourceProvider.pathContext;
+      List<String> containingDirectories = <String>[];
+      for (String filePath in priorityFiles) {
+        containingDirectories.add(pathContext.dirname(filePath));
+      }
+      containingDirectories.sort();
+
+      // For each file, add the contained directory to includedPathList iff
+      // some other parent containing directory has not already been added.
+      for (String containedDir in containingDirectories) {
+        // Check that no parent directories have already been added (we have
+        // guarantees here as the list was sorted above.)
+        bool parentDirectoryInListAlready = false;
+        for (int i = 0; i < includedPathList.length; i++) {
+          if (containedDir.startsWith(includedPathList[i])) {
+            parentDirectoryInListAlready = true;
+          }
+        }
+        if (!parentDirectoryInListAlready) {
+          includedPathList.add(containedDir);
+        }
+      }
+
+      server.setAnalysisRoots(
+          request.id, includedPathList, <String>[], <String, String>{});
+    }
+
     server.setPriorityFiles(request.id, params.files);
     //
     // Forward the request to the plugins.
diff --git a/pkg/analysis_server/lib/src/server/driver.dart b/pkg/analysis_server/lib/src/server/driver.dart
index 59a43f0..2fbce49 100644
--- a/pkg/analysis_server/lib/src/server/driver.dart
+++ b/pkg/analysis_server/lib/src/server/driver.dart
@@ -265,6 +265,13 @@
   static const String TRAIN_USING = "train-using";
 
   /**
+   * User Experience, Experiment #1. This experiment changes the notion of
+   * what analysis roots are and priority files: the analysis root is set to be
+   * the priority files' containing directory.
+   */
+  static const String UX_EXPERIMENT_1 = "ux-experiment-1";
+
+  /**
    * The instrumentation server that is to be used by the analysis server.
    */
   InstrumentationServer instrumentationServer;
@@ -308,6 +315,7 @@
     analysisServerOptions.clientVersion = results[CLIENT_VERSION];
     analysisServerOptions.cacheFolder = results[CACHE_FOLDER];
     analysisServerOptions.useFastaParser = results[USE_FASTA_PARSER];
+    analysisServerOptions.enableUXExperiment1 = results[UX_EXPERIMENT_1];
 
     bool disableAnalyticsForSession = results[SUPPRESS_ANALYTICS_FLAG];
     if (results.wasParsed(TRAIN_USING)) {
@@ -588,6 +596,11 @@
     parser.addOption(TRAIN_USING,
         help: "Pass in a directory to analyze for purposes of training an "
             "analysis server snapshot.");
+    parser.addFlag(UX_EXPERIMENT_1,
+        help: "User Experience, Experiment #1, "
+            "this experiment changes the notion of analysis roots and priority "
+            "files.",
+        hide: true);
 
     return parser;
   }