[Analyzer] Handle parsing v3.16 LSP Spec

Change-Id: I6afd5c32673ba580cdf6691bdfa521766f65c3b0
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/158392
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Danny Tuppeny
2020-12-09 20:29:30 +00:00
committed by commit-bot@chromium.org
parent 4adebc6d2a
commit 4525525a9d
20 changed files with 12345 additions and 851 deletions
File diff suppressed because it is too large Load Diff
@@ -78,17 +78,18 @@ abstract class Commands {
}
abstract class CustomMethods {
static const DiagnosticServer = Method('dart/diagnosticServer');
static const Reanalyze = Method('dart/reanalyze');
static const PublishClosingLabels =
static const diagnosticServer = Method('dart/diagnosticServer');
static const reanalyze = Method('dart/reanalyze');
static const publishClosingLabels =
Method('dart/textDocument/publishClosingLabels');
static const PublishOutline = Method('dart/textDocument/publishOutline');
static const PublishFlutterOutline =
static const publishOutline = Method('dart/textDocument/publishOutline');
static const publishFlutterOutline =
Method('dart/textDocument/publishFlutterOutline');
static const Super = Method('dart/textDocument/super');
static const super_ = Method('dart/textDocument/super');
// TODO(dantup): Remove custom AnalyzerStatus status method soon as no clients
// should be relying on it and we now support proper $/progress events.
static const AnalyzerStatus = Method(r'$/analyzerStatus');
static const analyzerStatus = Method(r'$/analyzerStatus');
}
/// CodeActionKinds supported by the server that are not declared in the LSP spec.
@@ -28,7 +28,7 @@ abstract class SimpleEditCommandHandler
}
Future<ErrorOr<void>> sendSourceEditsToClient(
VersionedTextDocumentIdentifier docIdentifier,
OptionalVersionedTextDocumentIdentifier docIdentifier,
CompilationUnit unit,
List<SourceEdit> edits) async {
// If there are no edits to apply, just complete the command without going
@@ -13,7 +13,7 @@ class DiagnosticServerHandler
extends MessageHandler<void, DartDiagnosticServer> {
DiagnosticServerHandler(LspAnalysisServer server) : super(server);
@override
Method get handlesMessage => CustomMethods.DiagnosticServer;
Method get handlesMessage => CustomMethods.diagnosticServer;
@override
LspJsonHandler<void> get jsonHandler => NullJsonHandler;
@@ -11,7 +11,7 @@ import 'package:analysis_server/src/lsp/lsp_analysis_server.dart';
class ReanalyzeHandler extends MessageHandler<void, void> {
ReanalyzeHandler(LspAnalysisServer server) : super(server);
@override
Method get handlesMessage => CustomMethods.Reanalyze;
Method get handlesMessage => CustomMethods.reanalyze;
@override
LspJsonHandler<void> get jsonHandler => NullJsonHandler;
@@ -14,7 +14,7 @@ class SuperHandler
extends MessageHandler<TextDocumentPositionParams, Location> {
SuperHandler(LspAnalysisServer server) : super(server);
@override
Method get handlesMessage => CustomMethods.Super;
Method get handlesMessage => CustomMethods.super_;
@override
LspJsonHandler<TextDocumentPositionParams> get jsonHandler =>
@@ -87,6 +87,7 @@ class RenameHandler extends MessageHandler<RenameParams, WorkspaceEdit> {
}
final pos = params.position;
final textDocument = params.textDocument;
final path = pathOfDoc(params.textDocument);
// If the client provided us a version doc identifier, we'll use it to ensure
// we're not computing a rename for an old document. If not, we'll just assume
@@ -94,9 +95,12 @@ class RenameHandler extends MessageHandler<RenameParams, WorkspaceEdit> {
// and then use it to verify the document hadn't changed again before we
// send the edits.
final docIdentifier = await path.mapResult((path) => success(
params.textDocument is VersionedTextDocumentIdentifier
? params.textDocument as VersionedTextDocumentIdentifier
: server.getVersionedDocumentIdentifier(path)));
textDocument is OptionalVersionedTextDocumentIdentifier
? textDocument
: textDocument is VersionedTextDocumentIdentifier
? OptionalVersionedTextDocumentIdentifier(
uri: textDocument.uri, version: textDocument.version)
: server.getVersionedDocumentIdentifier(path)));
final unit = await path.mapResult(requireResolvedUnit);
final offset = await unit.mapResult((unit) => toOffset(unit.lineInfo, pos));
@@ -118,7 +118,7 @@ class TextDocumentOpenHandler
final doc = params.textDocument;
final path = pathOfDocItem(doc);
return path.mapResult((path) {
// We don't get a VersionedTextDocumentIdentifier with a didOpen but we
// We don't get a OptionalVersionedTextDocumentIdentifier with a didOpen but we
// do get the necessary info to create one.
server.documentVersions[path] = VersionedTextDocumentIdentifier(
version: params.textDocument.version,
@@ -248,11 +248,13 @@ class LspAnalysisServer extends AbstractAnalysisServer {
}
/// Gets the version of a document known to the server, returning a
/// [VersionedTextDocumentIdentifier] with a version of `null` if the document
/// [OptionalVersionedTextDocumentIdentifier] with a version of `null` if the document
/// version is not known.
VersionedTextDocumentIdentifier getVersionedDocumentIdentifier(String path) {
return documentVersions[path] ??
VersionedTextDocumentIdentifier(uri: Uri.file(path).toString());
OptionalVersionedTextDocumentIdentifier getVersionedDocumentIdentifier(
String path) {
return OptionalVersionedTextDocumentIdentifier(
uri: Uri.file(path).toString(),
version: documentVersions[path]?.version);
}
void handleClientConnection(
@@ -424,7 +426,7 @@ class LspAnalysisServer extends AbstractAnalysisServer {
final params = PublishClosingLabelsParams(
uri: Uri.file(path).toString(), labels: labels);
final message = NotificationMessage(
method: CustomMethods.PublishClosingLabels,
method: CustomMethods.publishClosingLabels,
params: params,
jsonrpc: jsonRpcVersion,
);
@@ -446,7 +448,7 @@ class LspAnalysisServer extends AbstractAnalysisServer {
final params = PublishFlutterOutlineParams(
uri: Uri.file(path).toString(), outline: outline);
final message = NotificationMessage(
method: CustomMethods.PublishFlutterOutline,
method: CustomMethods.publishFlutterOutline,
params: params,
jsonrpc: jsonRpcVersion,
);
@@ -457,7 +459,7 @@ class LspAnalysisServer extends AbstractAnalysisServer {
final params =
PublishOutlineParams(uri: Uri.file(path).toString(), outline: outline);
final message = NotificationMessage(
method: CustomMethods.PublishOutline,
method: CustomMethods.publishOutline,
params: params,
jsonrpc: jsonRpcVersion,
);
@@ -552,7 +554,7 @@ class LspAnalysisServer extends AbstractAnalysisServer {
// it's unlikely to be in use by any clients.
if (clientCapabilities.window?.workDoneProgress != true) {
channel.sendNotification(NotificationMessage(
method: CustomMethods.AnalyzerStatus,
method: CustomMethods.analyzerStatus,
params: AnalyzerStatusParams(isAnalyzing: status.isAnalyzing),
jsonrpc: jsonRpcVersion,
));
+4 -1
View File
@@ -1211,7 +1211,10 @@ lsp.SignatureHelp toSignatureHelp(List<lsp.MarkupKind> preferredFormats,
lsp.TextDocumentEdit toTextDocumentEdit(FileEditInformation edit) {
return lsp.TextDocumentEdit(
textDocument: edit.doc,
edits: edit.edits.map((e) => toTextEdit(edit.lineInfo, e)).toList(),
edits: edit.edits
.map((e) => Either2<TextEdit, AnnotatedTextEdit>.t1(
toTextEdit(edit.lineInfo, e)))
.toList(),
);
}
@@ -13,7 +13,7 @@ class ClientDynamicRegistrations {
/// All dynamic registrations supported by the Dart LSP server.
///
/// Anything listed here and supported by the client will not send a static
/// registration but intead dynamically register (usually only for a subset of
/// registration but instead dynamically register (usually only for a subset of
/// files such as for .dart/pubspec.yaml/etc).
///
/// When adding new capabilities that will be registered dynamically, the
@@ -124,7 +124,8 @@ class ServerCapabilitiesComputer {
return ServerCapabilities(
textDocumentSync: dynamicRegistrations.textSync
? null
: Either2<TextDocumentSyncOptions, num>.t1(TextDocumentSyncOptions(
: Either2<TextDocumentSyncOptions, TextDocumentSyncKind>.t1(
TextDocumentSyncOptions(
// The open/close and sync kind flags are registered dynamically if the
// client supports them, so these static registrations are based on whether
// the client supports dynamic registration.
@@ -302,7 +302,7 @@ Token _parse(String s, FeatureSet featureSet) {
/// Helper class that bundles up all information required when converting server
/// SourceEdits into LSP-compatible WorkspaceEdits.
class FileEditInformation {
final VersionedTextDocumentIdentifier doc;
final OptionalVersionedTextDocumentIdentifier doc;
final LineInfo lineInfo;
final List<server.SourceEdit> edits;
@@ -44,7 +44,7 @@ class InitializationTest extends AbstractLspAnalysisServerTest {
emptyTextDocumentClientCapabilities))),
);
// Because we support dynamic registration for synchronisation, we won't send
// Because we support dynamic registration for synchronization, we won't send
// static registrations for them.
// https://github.com/dart-lang/sdk/issues/38490
final initResult = InitializeResult.fromJson(initResponse.result);
@@ -53,7 +53,7 @@ class InitializationTest extends AbstractLspAnalysisServerTest {
expect(initResult.capabilities, isNotNull);
expect(initResult.capabilities.textDocumentSync, isNull);
// Should container Hover, DidOpen, DidClose, DidChange.
// Should contain Hover, DidOpen, DidClose, DidChange.
expect(registrations, hasLength(4));
final hover =
registrationOptionsFor(registrations, Method.textDocument_hover);
@@ -565,13 +565,16 @@ mixin LspAnalysisServerTestMixin implements ClientCapabilitiesHelperMixin {
});
}
String applyTextEdit(String content, TextEdit change) {
final startPos = change.range.start;
final endPos = change.range.end;
String applyTextEdit(
String content, Either2<TextEdit, AnnotatedTextEdit> change) {
// Both sites of the union can cast to TextEdit.
final edit = change.map((e) => e, (e) => e);
final startPos = edit.range.start;
final endPos = edit.range.end;
final lineInfo = LineInfo.fromContent(content);
final start = lineInfo.getOffsetOfLine(startPos.line) + startPos.character;
final end = lineInfo.getOffsetOfLine(endPos.line) + endPos.character;
return content.replaceRange(start, end, change.newText);
return content.replaceRange(start, end, edit.newText);
}
String applyTextEdits(String oldContent, List<TextEdit> changes) {
@@ -626,7 +629,8 @@ mixin LspAnalysisServerTestMixin implements ClientCapabilitiesHelperMixin {
);
for (final change in sortedChanges) {
newContent = applyTextEdit(newContent, change);
newContent = applyTextEdit(
newContent, Either2<TextEdit, AnnotatedTextEdit>.t1(change));
}
return newContent;
@@ -702,7 +706,7 @@ mixin LspAnalysisServerTestMixin implements ClientCapabilitiesHelperMixin {
final path = Uri.parse(edit.textDocument.uri).toFilePath();
final expectedVersion = expectedVersions[path];
if (edit.textDocument is VersionedTextDocumentIdentifier) {
if (edit.textDocument is OptionalVersionedTextDocumentIdentifier) {
expect(edit.textDocument.version, equals(expectedVersion));
} else {
throw 'Document identifier for $path was not versioned (expected version $expectedVersion)';
@@ -901,7 +905,7 @@ mixin LspAnalysisServerTestMixin implements ClientCapabilitiesHelperMixin {
Future<DartDiagnosticServer> getDiagnosticServer() {
final request = makeRequest(
CustomMethods.DiagnosticServer,
CustomMethods.diagnosticServer,
null,
);
return expectSuccessfulResponseTo(request, DartDiagnosticServer.fromJson);
@@ -1009,7 +1013,7 @@ mixin LspAnalysisServerTestMixin implements ClientCapabilitiesHelperMixin {
Position pos,
) {
final request = makeRequest(
CustomMethods.Super,
CustomMethods.super_,
TextDocumentPositionParams(
textDocument: TextDocumentIdentifier(uri: uri.toString()),
position: pos,
@@ -1428,10 +1432,10 @@ mixin LspAnalysisServerTestMixin implements ClientCapabilitiesHelperMixin {
Future<void> waitForAnalysisStatus(bool analyzing) async {
await serverToClient.firstWhere((message) {
if (message is NotificationMessage) {
if (message.method == CustomMethods.AnalyzerStatus) {
if (message.method == CustomMethods.analyzerStatus) {
if (_clientCapabilities.window?.workDoneProgress == true) {
throw Exception(
'Recieved ${CustomMethods.AnalyzerStatus} notification '
'Recieved ${CustomMethods.analyzerStatus} notification '
'but client supports workDoneProgress');
}
@@ -1440,7 +1444,7 @@ mixin LspAnalysisServerTestMixin implements ClientCapabilitiesHelperMixin {
} else if (message.method == Method.progress) {
if (_clientCapabilities.window?.workDoneProgress != true) {
throw Exception(
'Recieved ${CustomMethods.AnalyzerStatus} notification '
'Recieved ${CustomMethods.analyzerStatus} notification '
'but client supports workDoneProgress');
}
@@ -1473,7 +1477,7 @@ mixin LspAnalysisServerTestMixin implements ClientCapabilitiesHelperMixin {
PublishClosingLabelsParams closingLabelsParams;
await serverToClient.firstWhere((message) {
if (message is NotificationMessage &&
message.method == CustomMethods.PublishClosingLabels) {
message.method == CustomMethods.publishClosingLabels) {
closingLabelsParams =
PublishClosingLabelsParams.fromJson(message.params);
@@ -1501,7 +1505,7 @@ mixin LspAnalysisServerTestMixin implements ClientCapabilitiesHelperMixin {
PublishFlutterOutlineParams outlineParams;
await serverToClient.firstWhere((message) {
if (message is NotificationMessage &&
message.method == CustomMethods.PublishFlutterOutline) {
message.method == CustomMethods.publishFlutterOutline) {
outlineParams = PublishFlutterOutlineParams.fromJson(message.params);
return outlineParams.uri == uri.toString();
@@ -1515,7 +1519,7 @@ mixin LspAnalysisServerTestMixin implements ClientCapabilitiesHelperMixin {
PublishOutlineParams outlineParams;
await serverToClient.firstWhere((message) {
if (message is NotificationMessage &&
message.method == CustomMethods.PublishOutline) {
message.method == CustomMethods.publishOutline) {
outlineParams = PublishOutlineParams.fromJson(message.params);
return outlineParams.uri == uri.toString();
@@ -167,7 +167,7 @@ void main() {
// Value whether expected to parse
const testTraceValues = {
'off': true,
'messages': true,
'message': true,
'verbose': true,
null: true,
'invalid': false,
@@ -68,7 +68,7 @@ final Uri specLicenseUri = Uri.parse(
/// The URI of the version of the spec to generate from. This should be periodically updated as
/// there's no longer a stable URI for the latest published version.
final Uri specUri = Uri.parse(
'https://raw.githubusercontent.com/microsoft/language-server-protocol/gh-pages/_specifications/specification-3-15.md');
'https://raw.githubusercontent.com/microsoft/language-server-protocol/gh-pages/_specifications/specification-3-16.md');
/// Pattern to extract inline types from the `result: {xx, yy }` notes in the spec.
/// Doesn't parse past full stops as some of these have english sentences tagged on
File diff suppressed because it is too large Load Diff
@@ -6,7 +6,7 @@ final _methodNamesPattern = RegExp(
r'''_(?:Notification|Request):?_:?(?:\r?\n)+\* method: ['`](.*?)[`'],?\r?\n''',
multiLine: true);
final _typeScriptBlockPattern =
RegExp(r'\B```typescript([\S\s]*?)\n```', multiLine: true);
RegExp(r'\B```typescript([\S\s]*?)\n\s*```', multiLine: true);
List<String> extractMethodNames(String spec) {
return _methodNamesPattern
@@ -76,6 +76,7 @@ String getImprovedType(String interfaceName, String fieldName) {
'Diagnostic': {
'severity': 'DiagnosticSeverity',
'code': 'String',
'data': 'object',
},
'TextDocumentSyncOptions': {
'change': 'TextDocumentSyncKind',
@@ -89,6 +90,10 @@ String getImprovedType(String interfaceName, String fieldName) {
'CompletionItem': {
'kind': 'CompletionItemKind',
'data': 'CompletionItemResolutionInfo',
'textEdit': 'TextEdit',
},
'CallHierarchyItem': {
'data': 'object',
},
'DocumentHighlight': {
'kind': 'DocumentHighlightKind',
@@ -287,6 +287,9 @@ class Parser {
/// Ensures the next token is [type] and moves to the next, throwing [message]
/// if not.
Token _consume(TokenType type, String message) {
// Skip over any inline comments when looking for a specific token.
_match([TokenType.COMMENT]);
if (_check(type)) {
return _advance();
}