Enable declarations tracker for LSP to support dart doc macros

Change-Id: I614be91ecf7a1afc377afeee70e559cff324dc61
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/102368
Commit-Queue: Danny Tuppeny <dantup@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analysis_server/lib/src/analysis_server.dart b/pkg/analysis_server/lib/src/analysis_server.dart
index a48bca9..ca242a8 100644
--- a/pkg/analysis_server/lib/src/analysis_server.dart
+++ b/pkg/analysis_server/lib/src/analysis_server.dart
@@ -54,7 +54,6 @@
 import 'package:analyzer/instrumentation/instrumentation.dart';
 import 'package:analyzer/src/context/builder.dart';
 import 'package:analyzer/src/context/context_root.dart';
-import 'package:analyzer/src/dart/analysis/byte_store.dart';
 import 'package:analyzer/src/dart/analysis/driver.dart' as nd;
 import 'package:analyzer/src/dart/analysis/file_state.dart' as nd;
 import 'package:analyzer/src/dart/analysis/performance_logger.dart';
@@ -147,12 +146,6 @@
 
   PerformanceLog _analysisPerformanceLogger;
 
-  ByteStore byteStore;
-  nd.AnalysisDriverScheduler analysisDriverScheduler;
-
-  DeclarationsTracker declarationsTracker;
-  DeclarationsTrackerData declarationsTrackerData;
-
   /// The controller for [onAnalysisSetChanged].
   final StreamController _onAnalysisSetChangedController =
       new StreamController.broadcast(sync: true);
@@ -374,13 +367,6 @@
         resourceProvider.pathContext.normalize(path) == path;
   }
 
-  /// 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);
-    analysisDriverScheduler.notify(null);
-  }
-
   /// Read all files, resolve all URIs, and perform required analysis in
   /// all current analysis drivers.
   void reanalyze() {
@@ -509,7 +495,7 @@
       throw new RequestFailure(
           new Response.unsupportedFeature(requestId, e.message));
     }
-    _addContextsToDeclarationsTracker();
+    addContextsToDeclarationsTracker();
   }
 
   /// Implementation for `analysis.setSubscriptions`.
@@ -691,13 +677,6 @@
 //    });
   }
 
-  void _addContextsToDeclarationsTracker() {
-    for (var driver in driverMap.values) {
-      declarationsTracker.addContext(driver.analysisContext);
-      driver.resetUriResolution();
-    }
-  }
-
   /// Return the path to the location of the byte store on disk, or `null` if
   /// there is no on-disk byte store.
   String _getByteStorePath() {
diff --git a/pkg/analysis_server/lib/src/analysis_server_abstract.dart b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
index 3ed2774..c56a762 100644
--- a/pkg/analysis_server/lib/src/analysis_server_abstract.dart
+++ b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
@@ -8,6 +8,7 @@
 import 'package:analysis_server/src/analysis_server.dart';
 import 'package:analysis_server/src/collections.dart';
 import 'package:analysis_server/src/context_manager.dart';
+import 'package:analysis_server/src/domains/completion/available_suggestions.dart';
 import 'package:analysis_server/src/server/diagnostic_server.dart';
 import 'package:analysis_server/src/services/correction/namespace.dart';
 import 'package:analysis_server/src/services/search/element_visitors.dart';
@@ -27,7 +28,9 @@
 import 'package:analyzer/src/dart/analysis/status.dart' as nd;
 import 'package:analyzer/src/dart/ast/element_locator.dart';
 import 'package:analyzer/src/dart/ast/utilities.dart';
+import 'package:analyzer/src/dartdoc/dartdoc_directive_info.dart';
 import 'package:analyzer/src/generated/engine.dart';
+import 'package:analyzer/src/services/available_declarations.dart';
 import 'package:analyzer/src/util/glob.dart';
 
 /// Implementations of [AbstractAnalysisServer] implement a server that listens
@@ -40,6 +43,13 @@
   /// context directories.
   ContextManager contextManager;
 
+  ByteStore byteStore;
+
+  nd.AnalysisDriverScheduler analysisDriverScheduler;
+
+  DeclarationsTracker declarationsTracker;
+  DeclarationsTrackerData declarationsTrackerData;
+
   /// The DiagnosticServer for this AnalysisServer. If available, it can be used
   /// to start an http diagnostics server or return the port for an existing
   /// server.
@@ -124,6 +134,13 @@
     return new DateTime.now().difference(start);
   }
 
+  void addContextsToDeclarationsTracker() {
+    for (var driver in driverMap.values) {
+      declarationsTracker.addContext(driver.analysisContext);
+      driver.resetUriResolution();
+    }
+  }
+
   /// If the state location can be accessed, return the file byte store,
   /// otherwise return the memory byte store.
   ByteStore createByteStore(ResourceProvider resourceProvider) {
@@ -170,6 +187,13 @@
     return null;
   }
 
+  DartdocDirectiveInfo getDartdocDirectiveInfoFor(ResolvedUnitResult result) {
+    return declarationsTracker
+            .getContext(result.session.analysisContext)
+            ?.dartdocDirectiveInfo ??
+        new DartdocDirectiveInfo();
+  }
+
   /// Return a [Future] that completes with the [Element] at the given
   /// [offset] of the given [file], or with `null` if there is no node at the
   /// [offset] or the node does not have an element.
@@ -260,4 +284,11 @@
         .getResult(path, sendCachedToStream: sendCachedToStream)
         .catchError((_) => null);
   }
+
+  /// 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);
+    analysisDriverScheduler.notify(null);
+  }
 }
diff --git a/pkg/analysis_server/lib/src/domain_analysis.dart b/pkg/analysis_server/lib/src/domain_analysis.dart
index e967188..78f7301 100644
--- a/pkg/analysis_server/lib/src/domain_analysis.dart
+++ b/pkg/analysis_server/lib/src/domain_analysis.dart
@@ -21,7 +21,6 @@
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/error/error.dart' as engine;
 import 'package:analyzer/src/dart/analysis/driver.dart';
-import 'package:analyzer/src/dartdoc/dartdoc_directive_info.dart';
 import 'package:analyzer/src/generated/engine.dart' as engine;
 import 'package:analyzer_plugin/protocol/protocol.dart' as plugin;
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
@@ -89,7 +88,7 @@
     List<HoverInformation> hovers = <HoverInformation>[];
     if (unit != null) {
       HoverInformation hoverInformation = new DartUnitHoverComputer(
-              _getDartdocDirectiveInfoFor(result), unit, params.offset)
+              server.getDartdocDirectiveInfoFor(result), unit, params.offset)
           .compute();
       if (hoverInformation != null) {
         hovers.add(hoverInformation);
@@ -279,7 +278,7 @@
     // Ensure the offset provided is a valid location in the file.
     final unit = result.unit;
     final computer = new DartUnitSignatureComputer(
-        _getDartdocDirectiveInfoFor(result), unit, params.offset);
+        server.getDartdocDirectiveInfoFor(result), unit, params.offset);
     if (!computer.offsetIsValid) {
       server.sendResponse(new Response.getSignatureInvalidOffset(request));
       return;
@@ -509,12 +508,4 @@
     server.updateOptions(updaters);
     return new AnalysisUpdateOptionsResult().toResponse(request.id);
   }
-
-  DartdocDirectiveInfo _getDartdocDirectiveInfoFor(ResolvedUnitResult result) {
-    // TODO(brianwilkerson) Consider moving this to AnalysisServer.
-    return server.declarationsTracker
-            .getContext(result.session.analysisContext)
-            ?.dartdocDirectiveInfo ??
-        new DartdocDirectiveInfo();
-  }
 }
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 6e2ee05..bd70948 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_hover.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_hover.dart
@@ -14,7 +14,6 @@
 import 'package:analysis_server/src/lsp/mapping.dart';
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/source/line_info.dart';
-import 'package:analyzer/src/dartdoc/dartdoc_directive_info.dart';
 
 class HoverHandler extends MessageHandler<TextDocumentPositionParams, Hover> {
   HoverHandler(LspAnalysisServer server) : super(server);
@@ -88,14 +87,7 @@
 
   ErrorOr<Hover> _getHover(ResolvedUnitResult unit, int offset) {
     final hover = new DartUnitHoverComputer(
-            // TODO(brianwilkerson) Add declarationsTracker to server in order to
-            //  enable dartdoc processing.
-//            server.declarationsTracker
-//                .getContext(unit.session.analysisContext)
-//                .dartdocDirectiveInfo,
-            new DartdocDirectiveInfo(),
-            unit.unit,
-            offset)
+            server.getDartdocDirectiveInfoFor(unit), unit.unit, offset)
         .compute();
     return success(toHover(unit.lineInfo, hover));
   }
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 90ef021..cde88c9 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
@@ -10,7 +10,6 @@
 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';
-import 'package:analyzer/src/dartdoc/dartdoc_directive_info.dart';
 
 class SignatureHelpHandler
     extends MessageHandler<TextDocumentPositionParams, SignatureHelp> {
@@ -34,12 +33,7 @@
 
     return offset.mapResult((offset) {
       final computer = new DartUnitSignatureComputer(
-          // TODO(brianwilkerson) Add declarationsTracker to server in order to
-          //  enable dartdoc processing.
-//          server.declarationsTracker
-//              .getContext(unit.result.session.analysisContext)
-//              .dartdocDirectiveInfo,
-          new DartdocDirectiveInfo(),
+          server.getDartdocDirectiveInfoFor(unit.result),
           unit.result.unit,
           offset);
       if (!computer.offsetIsValid) {
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 d543a25..e8f5112 100644
--- a/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
+++ b/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
@@ -16,6 +16,7 @@
 import 'package:analysis_server/src/context_manager.dart';
 import 'package:analysis_server/src/domain_completion.dart'
     show CompletionDomainHandler;
+import 'package:analysis_server/src/domains/completion/available_suggestions.dart';
 import 'package:analysis_server/src/lsp/channel/lsp_channel.dart';
 import 'package:analysis_server/src/lsp/constants.dart';
 import 'package:analysis_server/src/lsp/handlers/handler_states.dart';
@@ -36,7 +37,6 @@
 import 'package:analyzer/source/line_info.dart';
 import 'package:analyzer/src/context/builder.dart';
 import 'package:analyzer/src/context/context_root.dart';
-import 'package:analyzer/src/dart/analysis/byte_store.dart';
 import 'package:analyzer/src/dart/analysis/driver.dart' as nd;
 import 'package:analyzer/src/dart/analysis/file_state.dart' as nd;
 import 'package:analyzer/src/dart/analysis/performance_logger.dart';
@@ -44,6 +44,7 @@
 import 'package:analyzer/src/generated/engine.dart';
 import 'package:analyzer/src/generated/sdk.dart';
 import 'package:analyzer/src/plugin/resolver_provider.dart';
+import 'package:analyzer/src/services/available_declarations.dart';
 import 'package:watcher/watcher.dart';
 
 /**
@@ -115,10 +116,6 @@
 
   PerformanceLog _analysisPerformanceLogger;
 
-  ByteStore byteStore;
-
-  nd.AnalysisDriverScheduler analysisDriverScheduler;
-
   ServerStateMessageHandler messageHandler;
 
   int nextRequestId = 1;
@@ -151,6 +148,8 @@
     ResolverProvider packageResolverProvider: null,
   }) : super(options, diagnosticServer, baseResourceProvider) {
     messageHandler = new UninitializedStateMessageHandler(this);
+    // TODO(dantup): This code is almost identical to AnalysisServer, consider
+    // moving it the base class that already holds many of these fields.
     defaultContextOptions.generateImplicitErrors = false;
     defaultContextOptions.useFastaParser = options.useFastaParser;
 
@@ -173,6 +172,11 @@
     analysisDriverScheduler.status.listen(sendStatusNotification);
     analysisDriverScheduler.start();
 
+    declarationsTracker = DeclarationsTracker(byteStore, resourceProvider);
+    declarationsTrackerData = DeclarationsTrackerData(declarationsTracker);
+    analysisDriverScheduler.outOfBandWorker =
+        CompletionLibrariesWorker(declarationsTracker);
+
     contextManager = new ContextManagerImpl(
         resourceProvider,
         sdkManager,
@@ -443,8 +447,10 @@
   }
 
   void setAnalysisRoots(List<String> includedPaths) {
+    declarationsTracker.discardContexts();
     final uniquePaths = HashSet<String>.of(includedPaths ?? const []);
     contextManager.setRoots(uniquePaths.toList(), [], {});
+    addContextsToDeclarationsTracker();
   }
 
   /**
@@ -488,7 +494,7 @@
           ..addAll(addedPaths ?? const [])
           ..removeAll(removedPaths ?? const []);
 
-    contextManager.setRoots(newPaths.toList(), [], {});
+    setAnalysisRoots(newPaths.toList());
   }
 
   void updateOverlay(String path, String contents) {
@@ -499,6 +505,8 @@
       resourceProvider.removeOverlay(path);
     }
     driverMap.values.forEach((driver) => driver.changeFile(path));
+
+    notifyDeclarationsTracker(path);
   }
 
   _updateDriversPriorityFiles() {
@@ -589,7 +597,8 @@
 
   @override
   void broadcastWatchEvent(WatchEvent event) {
-    // TODO: implement broadcastWatchEvent
+    analysisServer.notifyDeclarationsTracker(event.path);
+    // TODO: implement plugin broadcastWatchEvent
   }
 
   @override
diff --git a/pkg/analysis_server/test/lsp/hover_test.dart b/pkg/analysis_server/test/lsp/hover_test.dart
index fa3e520..6b9be51 100644
--- a/pkg/analysis_server/test/lsp/hover_test.dart
+++ b/pkg/analysis_server/test/lsp/hover_test.dart
@@ -18,6 +18,27 @@
 
 @reflectiveTest
 class HoverTest extends AbstractLspAnalysisServerTest {
+  test_dartDoc_macros() async {
+    final content = '''
+    /// {@template template_name}
+    /// This is shared content.
+    /// {@endtemplate}
+    const String foo = null;
+
+    /// {@macro template_name}
+    const String [[f^oo2]] = null;
+    ''';
+
+    final initialAnalysis = waitForAnalysisComplete();
+    await initialize();
+    await openFile(mainFileUri, withoutMarkers(content));
+    await initialAnalysis;
+    var hover = await getHover(mainFileUri, positionFromMarker(content));
+    expect(hover, isNotNull);
+    expect(hover.range, equals(rangeFromMarkers(content)));
+    expect(_getStringContents(hover), endsWith('This is shared content.'));
+  }
+
   test_hover_bad_position() async {
     await initialize();
     await openFile(mainFileUri, '');
diff --git a/pkg/analysis_server/test/lsp/signature_help_test.dart b/pkg/analysis_server/test/lsp/signature_help_test.dart
index 4471683..b791ddd 100644
--- a/pkg/analysis_server/test/lsp/signature_help_test.dart
+++ b/pkg/analysis_server/test/lsp/signature_help_test.dart
@@ -262,6 +262,37 @@
     );
   }
 
+  test_dartDocMacro() async {
+    final content = '''
+    /// {@template template_name}
+    /// This is shared content.
+    /// {@endtemplate}
+    const String bar = null;
+
+    /// {@macro template_name}
+    foo(String s, int i) {
+      foo(^);
+    }
+    ''';
+    final expectedLabel = 'foo(String s, int i)';
+    final expectedDoc = 'This is shared content.';
+
+    final initialAnalysis = waitForAnalysisComplete();
+    await initialize();
+    await openFile(mainFileUri, withoutMarkers(content));
+    await initialAnalysis;
+    await testSignature(
+      content,
+      expectedLabel,
+      expectedDoc,
+      [
+        new ParameterInformation('String s', null),
+        new ParameterInformation('int i', null),
+      ],
+      expectedFormat: null,
+    );
+  }
+
   test_unopenFile() async {
     final content = '''
     /// Does foo.