Enable the analyzer Diagnostics server for LSP
Change-Id: I3a555c301e8c09b1d8af8f952f9f65102961d3b7
Reviewed-on: https://dart-review.googlesource.com/c/88801
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 03fd356..3e0946e8 100644
--- a/pkg/analysis_server/lib/src/analysis_server.dart
+++ b/pkg/analysis_server/lib/src/analysis_server.dart
@@ -16,7 +16,6 @@
import 'package:analysis_server/src/analysis_logger.dart';
import 'package:analysis_server/src/analysis_server_abstract.dart';
import 'package:analysis_server/src/channel/channel.dart';
-import 'package:analysis_server/src/collections.dart';
import 'package:analysis_server/src/computer/computer_highlights.dart';
import 'package:analysis_server/src/computer/computer_highlights2.dart';
import 'package:analysis_server/src/computer/computer_outline.dart';
@@ -76,9 +75,6 @@
/// Instances of the class [AnalysisServer] implement a server that listens on a
/// [CommunicationChannel] for analysis requests and process them.
class AnalysisServer extends AbstractAnalysisServer {
- /// The options of this server instance.
- AnalysisServerOptions options;
-
/// The channel from which requests are received and to which responses should
/// be sent.
final ServerCommunicationChannel channel;
@@ -124,22 +120,6 @@
/// notifications should be sent.
Map<FlutterService, Set<String>> flutterServices = {};
- /// Performance information before initial analysis is complete.
- final ServerPerformance performanceDuringStartup = new ServerPerformance();
-
- /// Performance information after initial analysis is complete
- /// or `null` if the initial analysis is not yet complete
- ServerPerformance performanceAfterStartup;
-
- /// A [RecentBuffer] of the most recent exceptions encountered by the analysis
- /// server.
- final RecentBuffer<ServerException> exceptions = new RecentBuffer(10);
-
- /// The class into which performance information is currently being recorded.
- /// During startup, this will be the same as [performanceDuringStartup]
- /// and after startup is complete, this switches to [performanceAfterStartup].
- ServerPerformance _performance;
-
/// The [Completer] that completes when analysis is complete.
Completer _onAnalysisCompleteCompleter;
@@ -191,18 +171,16 @@
AnalysisServer(
this.channel,
ResourceProvider baseResourceProvider,
- this.options,
+ AnalysisServerOptions options,
this.sdkManager,
this.instrumentationService, {
this.diagnosticServer,
ResolverProvider fileResolverProvider: null,
ResolverProvider packageResolverProvider: null,
this.detachableFileSystemManager: null,
- }) : super(baseResourceProvider) {
+ }) : super(options, baseResourceProvider) {
notificationManager = new NotificationManager(channel, resourceProvider);
- _performance = performanceDuringStartup;
-
pluginManager = new PluginManager(
resourceProvider,
_getByteStorePath(),
@@ -252,7 +230,7 @@
onAnalysisStarted.first.then((_) {
onAnalysisComplete.then((_) {
performanceAfterStartup = new ServerPerformance();
- _performance = performanceAfterStartup;
+ performance = performanceAfterStartup;
});
});
searchEngine = new SearchEngineImpl(driverMap.values);
@@ -302,13 +280,6 @@
return _onAnalysisStartedController.stream;
}
- /// Return the total time the server's been alive.
- Duration get uptime {
- DateTime start = new DateTime.fromMillisecondsSinceEpoch(
- performanceDuringStartup.startTime);
- return new DateTime.now().difference(start);
- }
-
/// The socket from which requests are being read has been closed.
void done() {}
@@ -340,7 +311,7 @@
/// Handle a [request] that was read from the communication channel.
void handleRequest(Request request) {
- _performance.logRequest(request);
+ performance.logRequestTiming(request.clientRequestTime);
runZoned(() {
ServerPerformanceStatistics.serverRequests.makeCurrentWhile(() {
int count = handlers.length;
@@ -1080,12 +1051,12 @@
/// The number of requests with latency > 150 milliseconds.
int slowRequestCount = 0;
- /// Log performance information about the given request.
- void logRequest(Request request) {
+ /// Log timing information for a request.
+ void logRequestTiming(int clientRequestTime) {
++requestCount;
- if (request.clientRequestTime != null) {
+ if (clientRequestTime != null) {
int latency =
- new DateTime.now().millisecondsSinceEpoch - request.clientRequestTime;
+ new DateTime.now().millisecondsSinceEpoch - clientRequestTime;
requestLatency += latency;
maxLatency = max(maxLatency, latency);
if (latency > 150) {
diff --git a/pkg/analysis_server/lib/src/analysis_server_abstract.dart b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
index bdc364e..cc27c636 100644
--- a/pkg/analysis_server/lib/src/analysis_server_abstract.dart
+++ b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
@@ -5,6 +5,8 @@
import 'dart:async';
import 'dart:core';
+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/services/correction/namespace.dart';
import 'package:analysis_server/src/services/search/element_visitors.dart';
@@ -29,10 +31,29 @@
/// Implementations of [AbstractAnalysisServer] implement a server that listens
/// on a [CommunicationChannel] for analysis messages and process them.
abstract class AbstractAnalysisServer {
+ /// The options of this server instance.
+ AnalysisServerOptions options;
+
/// The [ContextManager] that handles the mapping from analysis roots to
/// context directories.
ContextManager contextManager;
+ /// A [RecentBuffer] of the most recent exceptions encountered by the analysis
+ /// server.
+ final RecentBuffer<ServerException> exceptions = new RecentBuffer(10);
+
+ /// Performance information after initial analysis is complete
+ /// or `null` if the initial analysis is not yet complete
+ ServerPerformance performanceAfterStartup;
+
+ /// The class into which performance information is currently being recorded.
+ /// During startup, this will be the same as [performanceDuringStartup]
+ /// and after startup is complete, this switches to [performanceAfterStartup].
+ ServerPerformance performance;
+
+ /// Performance information before initial analysis is complete.
+ final ServerPerformance performanceDuringStartup = new ServerPerformance();
+
/// The set of the files that are currently priority.
final Set<String> priorityFiles = new Set<String>();
@@ -52,8 +73,10 @@
/// list is lazily created and should be accessed using [analyzedFilesGlobs].
List<Glob> _analyzedFilesGlobs = null;
- AbstractAnalysisServer(ResourceProvider baseResourceProvider)
- : resourceProvider = OverlayResourceProvider(baseResourceProvider);
+ AbstractAnalysisServer(this.options, ResourceProvider baseResourceProvider)
+ : resourceProvider = OverlayResourceProvider(baseResourceProvider) {
+ performance = performanceDuringStartup;
+ }
/// Return a list of the globs used to determine which files should be
/// analyzed.
@@ -82,6 +105,13 @@
/// A table mapping [Folder]s to the [AnalysisDriver]s associated with them.
Map<Folder, nd.AnalysisDriver> get driverMap => contextManager.driverMap;
+ /// Return the total time the server's been alive.
+ Duration get uptime {
+ DateTime start = new DateTime.fromMillisecondsSinceEpoch(
+ performanceDuringStartup.startTime);
+ return new DateTime.now().difference(start);
+ }
+
/// If the state location can be accessed, return the file byte store,
/// otherwise return the memory byte store.
ByteStore createByteStore(ResourceProvider resourceProvider) {
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 cdee37d..b5afe93 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_initialize.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_initialize.dart
@@ -34,7 +34,7 @@
openWorkspacePaths.add(params.rootUri);
}
- server.setClientCapabilities(params.capabilities);
+ server.handleClientConnection(params.capabilities);
server.messageHandler =
new InitializingStateMessageHandler(server, openWorkspacePaths);
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 245c116a..22f5abb 100644
--- a/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
+++ b/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart
@@ -21,6 +21,7 @@
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';
+import 'package:analyzer/exception/exception.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/instrumentation/instrumentation.dart';
import 'package:analyzer/source/line_info.dart';
@@ -45,11 +46,6 @@
ClientCapabilities _clientCapabilities;
/**
- * The options of this server instance.
- */
- AnalysisServerOptions options;
-
- /**
* The channel from which messages are received and to which responses should
* be sent.
*/
@@ -118,11 +114,11 @@
LspAnalysisServer(
this.channel,
ResourceProvider baseResourceProvider,
- this.options,
+ AnalysisServerOptions options,
this.sdkManager,
this.instrumentationService, {
ResolverProvider packageResolverProvider: null,
- }) : super(baseResourceProvider) {
+ }) : super(options, baseResourceProvider) {
messageHandler = new UninitializedStateMessageHandler(this);
defaultContextOptions.generateImplicitErrors = false;
defaultContextOptions.useFastaParser = options.useFastaParser;
@@ -235,8 +231,7 @@
* Handle a [message] that was read from the communication channel.
*/
void handleMessage(Message message) {
- // TODO(dantup): Put in all the things this server is missing, like:
- // _performance.logRequest(message);
+ performance.logRequestTiming(null);
runZoned(() {
ServerPerformanceStatistics.serverRequests.makeCurrentWhile(() async {
try {
@@ -375,6 +370,17 @@
// Log the full message since showMessage above may be truncated or formatted
// badly (eg. VS Code takes the newlines out).
logError(fullError.toString());
+
+ // remember the last few exceptions
+ if (exception is CaughtException) {
+ stackTrace ??= exception.stackTrace;
+ }
+ exceptions.add(new ServerException(
+ message,
+ exception,
+ stackTrace is StackTrace ? stackTrace : null,
+ false,
+ ));
}
void setAnalysisRoots(List<String> includedPaths, List<String> excludedPaths,
@@ -382,8 +388,11 @@
contextManager.setRoots(includedPaths, excludedPaths, packageRoots);
}
- void setClientCapabilities(ClientCapabilities capabilities) {
+ void handleClientConnection(ClientCapabilities capabilities) {
_clientCapabilities = capabilities;
+
+ performanceAfterStartup = new ServerPerformance();
+ performance = performanceAfterStartup;
}
/**
diff --git a/pkg/analysis_server/lib/src/lsp/lsp_socket_server.dart b/pkg/analysis_server/lib/src/lsp/lsp_socket_server.dart
index 47261dc..25a15e1 100644
--- a/pkg/analysis_server/lib/src/lsp/lsp_socket_server.dart
+++ b/pkg/analysis_server/lib/src/lsp/lsp_socket_server.dart
@@ -19,7 +19,7 @@
* the SocketServer is to manage the lifetime of the AnalysisServer and to
* encode and decode the JSON messages exchanged with the client.
*/
-class LspSocketServer {
+class LspSocketServer implements AbstractSocketServer {
final AnalysisServerOptions analysisServerOptions;
/**
* The analysis server that was created when a client established a
diff --git a/pkg/analysis_server/lib/src/server/driver.dart b/pkg/analysis_server/lib/src/server/driver.dart
index 21d5cc8..b204cf5 100644
--- a/pkg/analysis_server/lib/src/server/driver.dart
+++ b/pkg/analysis_server/lib/src/server/driver.dart
@@ -407,37 +407,10 @@
defaultSdk.sdkVersion);
AnalysisEngine.instance.instrumentationService = instrumentationService;
- if (analysisServerOptions.useLanguageServerProtocol) {
- startLspServer(results, analysisServerOptions, dartSdkManager,
- instrumentationService);
- } else {
- startAnalysisServer(results, analysisServerOptions, parser,
- dartSdkManager, instrumentationService, analytics);
- }
- }
-
- void startAnalysisServer(
- ArgResults results,
- AnalysisServerOptions analysisServerOptions,
- CommandLineParser parser,
- DartSdkManager dartSdkManager,
- InstrumentationService instrumentationService,
- telemetry.Analytics analytics) {
- String trainDirectory = results[TRAIN_USING];
- if (trainDirectory != null) {
- if (!FileSystemEntity.isDirectorySync(trainDirectory)) {
- print("Training directory '$trainDirectory' not found.\n");
- exitCode = 1;
- return null;
- }
- }
-
- int port;
- bool serve_http = false;
+ int diagnosticServerPort;
if (results[PORT_OPTION] != null) {
try {
- port = int.parse(results[PORT_OPTION]);
- serve_http = true;
+ diagnosticServerPort = int.parse(results[PORT_OPTION]);
} on FormatException {
print('Invalid port number: ${results[PORT_OPTION]}');
print('');
@@ -447,6 +420,40 @@
}
}
+ if (analysisServerOptions.useLanguageServerProtocol) {
+ startLspServer(results, analysisServerOptions, dartSdkManager,
+ instrumentationService, diagnosticServerPort);
+ } else {
+ startAnalysisServer(
+ results,
+ analysisServerOptions,
+ parser,
+ dartSdkManager,
+ instrumentationService,
+ analytics,
+ diagnosticServerPort);
+ }
+ }
+
+ void startAnalysisServer(
+ ArgResults results,
+ AnalysisServerOptions analysisServerOptions,
+ CommandLineParser parser,
+ DartSdkManager dartSdkManager,
+ InstrumentationService instrumentationService,
+ telemetry.Analytics analytics,
+ int diagnosticServerPort,
+ ) {
+ String trainDirectory = results[TRAIN_USING];
+ if (trainDirectory != null) {
+ if (!FileSystemEntity.isDirectorySync(trainDirectory)) {
+ print("Training directory '$trainDirectory' not found.\n");
+ exitCode = 1;
+ return null;
+ }
+ }
+ final serve_http = diagnosticServerPort != null;
+
//
// Process all of the plugins so that extensions are registered.
//
@@ -474,7 +481,7 @@
diagnosticServer.httpServer = httpServer;
if (serve_http) {
- diagnosticServer.startOnPort(port);
+ diagnosticServer.startOnPort(diagnosticServerPort);
}
if (trainDirectory != null) {
@@ -539,13 +546,25 @@
AnalysisServerOptions analysisServerOptions,
DartSdkManager dartSdkManager,
InstrumentationService instrumentationService,
+ int diagnosticServerPort,
) {
+ final serve_http = diagnosticServerPort != null;
+
+ _DiagnosticServerImpl diagnosticServer = new _DiagnosticServerImpl();
+
final socketServer = new LspSocketServer(
analysisServerOptions,
dartSdkManager,
instrumentationService,
);
+ httpServer = new HttpAnalysisServer(socketServer);
+
+ diagnosticServer.httpServer = httpServer;
+ if (serve_http) {
+ diagnosticServer.startOnPort(diagnosticServerPort);
+ }
+
_captureLspExceptions(socketServer, instrumentationService, () {
LspStdioAnalysisServer stdioServer =
new LspStdioAnalysisServer(socketServer);
diff --git a/pkg/analysis_server/lib/src/server/http_server.dart b/pkg/analysis_server/lib/src/server/http_server.dart
index 5b51304..8cb1391 100644
--- a/pkg/analysis_server/lib/src/server/http_server.dart
+++ b/pkg/analysis_server/lib/src/server/http_server.dart
@@ -35,7 +35,7 @@
* An object that can handle either a WebSocket connection or a connection
* to the client over stdio.
*/
- SocketServer socketServer;
+ AbstractSocketServer socketServer;
/**
* An object that can handle GET requests.
diff --git a/pkg/analysis_server/lib/src/socket_server.dart b/pkg/analysis_server/lib/src/socket_server.dart
index 9e22d01..ad77e8a 100644
--- a/pkg/analysis_server/lib/src/socket_server.dart
+++ b/pkg/analysis_server/lib/src/socket_server.dart
@@ -5,6 +5,7 @@
import 'package:analysis_server/protocol/protocol.dart';
import 'package:analysis_server/protocol/protocol_generated.dart';
import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/analysis_server_abstract.dart';
import 'package:analysis_server/src/channel/channel.dart';
import 'package:analysis_server/src/server/detachable_filesystem_manager.dart';
import 'package:analysis_server/src/server/diagnostic_server.dart';
@@ -13,13 +14,18 @@
import 'package:analyzer/src/generated/sdk.dart';
import 'package:analyzer/src/plugin/resolver_provider.dart';
+abstract class AbstractSocketServer {
+ AnalysisServerOptions get analysisServerOptions;
+ AbstractAnalysisServer get analysisServer;
+}
+
/**
* Instances of the class [SocketServer] implement the common parts of
* http-based and stdio-based analysis servers. The primary responsibility of
* the SocketServer is to manage the lifetime of the AnalysisServer and to
* encode and decode the JSON messages exchanged with the client.
*/
-class SocketServer {
+class SocketServer implements AbstractSocketServer {
final AnalysisServerOptions analysisServerOptions;
/**
diff --git a/pkg/analysis_server/lib/src/status/diagnostics.dart b/pkg/analysis_server/lib/src/status/diagnostics.dart
index 44fff0a..be7a383 100644
--- a/pkg/analysis_server/lib/src/status/diagnostics.dart
+++ b/pkg/analysis_server/lib/src/status/diagnostics.dart
@@ -11,6 +11,7 @@
show PROTOCOL_VERSION;
import 'package:analysis_server/protocol/protocol_generated.dart';
import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/analysis_server_abstract.dart';
import 'package:analysis_server/src/domain_completion.dart';
import 'package:analysis_server/src/plugin/plugin_manager.dart';
import 'package:analysis_server/src/server/http_server.dart';
@@ -222,6 +223,11 @@
Future generateContent(Map<String, String> params) async {
// TODO(brianwilkerson) Determine whether this await is necessary.
await null;
+
+ // TODO(dantup): The latency stats are never populated for LSP because the
+ // client will never send timestamps (it's also misleading for non-LSP
+ // if the client does not send timestamps (VS Code doesn't) as it shows 0ms).
+
void writeRow(List<String> data, {List<String> classes}) {
buf.write("<tr>");
for (int i = 0; i < data.length; i++) {
@@ -312,6 +318,16 @@
Future generateContent(Map<String, String> params) async {
// TODO(brianwilkerson) Determine whether this await is necessary.
await null;
+
+ // TODO(dantup): This one should be supported, just isn't yet.
+ if (this.server is! AnalysisServer) {
+ generateNotApplicablePage();
+ return;
+ }
+
+ // TODO(dantup): ...
+ final server = this.server as AnalysisServer;
+
CompletionDomainHandler completionDomain = server.handlers
.firstWhere((handler) => handler is CompletionDomainHandler);
@@ -582,7 +598,7 @@
bool get isNavPage => false;
- AnalysisServer get server =>
+ AbstractAnalysisServer get server =>
(site as DiagnosticsSite).socketServer.analysisServer;
Future<void> generateContainer(Map<String, String> params) async {
@@ -706,12 +722,16 @@
buf.writeln('</div>');
}
+
+ void generateNotApplicablePage() {
+ buf.write('This page is not applicable for this server.');
+ }
}
class DiagnosticsSite extends Site implements AbstractGetHandler {
/// An object that can handle either a WebSocket connection or a connection
/// to the client over stdio.
- SocketServer socketServer;
+ AbstractSocketServer socketServer;
/// The last few lines printed.
final List<String> lastPrintedLines;
@@ -1037,6 +1057,18 @@
Future generateContent(Map<String, String> params) async {
// TODO(brianwilkerson) Determine whether this await is necessary.
await null;
+
+ // TODO(dantup): Maybe we shouldn't show this in the nav at all?
+ if (this.server is! AnalysisServer) {
+ generateNotApplicablePage();
+ return;
+ }
+
+ // TODO(dantup): Is there a better way? The is! above doesn't seem to
+ // work, possible because 'server' is not immutable (it's a better) and maybe
+ // because the code below is in a callback?
+ final server = this.server as AnalysisServer;
+
h3('Analysis plugins');
List<PluginInfo> analysisPlugins = server.pluginManager.plugins;
@@ -1293,6 +1325,18 @@
Future generateContent(Map<String, String> params) async {
// TODO(brianwilkerson) Determine whether this await is necessary.
await null;
+
+ // TODO(dantup): Maybe we shouldn't show this in the nav at all?
+ if (this.server is! AnalysisServer) {
+ generateNotApplicablePage();
+ return;
+ }
+
+ // TODO(dantup): Is there a better way? The is! above doesn't seem to
+ // work, possible because 'server' is not immutable (it's a better) and maybe
+ // because the code below is in a callback?
+ final server = this.server as AnalysisServer;
+
// server domain
h3('Server domain subscriptions');
ul(ServerService.VALUES, (item) {