Skip to content

Commit 2782ae6

Browse files
committed
Store client's version for open docs in LSP
1 parent b467cfb commit 2782ae6

File tree

9 files changed

+157
-47
lines changed

9 files changed

+157
-47
lines changed

src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/RequestExecutionQueue.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ private async Task ProcessQueueAsync()
290290
var message = $"Error occurred processing queue: {ex.Message}.";
291291
if (lspServices is not null)
292292
{
293-
await _languageServer.ShutdownAsync("Error processing queue, shutting down").ConfigureAwait(false);
293+
await _languageServer.ShutdownAsync(message).ConfigureAwait(false);
294294
await _languageServer.ExitAsync().ConfigureAwait(false);
295295
}
296296

src/LanguageServer/Protocol.TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -359,9 +359,35 @@ private protected async Task<TestLspServer> CreateXmlTestLspServerAsync(
359359
return await TestLspServer.CreateAsync(workspace, lspOptions, TestOutputLspLogger);
360360
}
361361

362+
private void CheckForCompositionErrors(TestComposition composition)
363+
{
364+
var config = composition.GetCompositionConfiguration();
365+
var hasLanguageServerErrors = config.CompositionErrors.Flatten().Any(error => error.Parts.Any(IsRelevantPartError));
366+
367+
if (hasLanguageServerErrors)
368+
{
369+
try
370+
{
371+
config.ThrowOnErrors();
372+
}
373+
catch (CompositionFailedException ex)
374+
{
375+
// The ToString for the composition failed exception doesn't output a nice set of errors by default, so log it separately
376+
this.TestOutputLspLogger.LogError($"Encountered errors in the MEF composition: {ex.Message}{Environment.NewLine}{ex.ErrorsAsString}");
377+
throw;
378+
}
379+
}
380+
381+
bool IsRelevantPartError(ComposedPart part)
382+
{
383+
return part.Definition.Type.FullName?.Contains("Microsoft.CodeAnalysis") == true && part.Definition.Type.FullName?.Contains("Microsoft.CodeAnalysis.ExternalAccess") == false;
384+
}
385+
}
386+
362387
internal async Task<LspTestWorkspace> CreateWorkspaceAsync(
363388
InitializationOptions? options, string? workspaceKind, bool mutatingLspWorkspace, TestComposition? composition = null)
364389
{
390+
CheckForCompositionErrors(composition ?? Composition);
365391
var workspace = new LspTestWorkspace(
366392
composition?.ExportProviderFactory.CreateExportProvider() ?? await CreateExportProviderAsync(),
367393
workspaceKind,
@@ -494,7 +520,8 @@ private protected static LSP.Location GetLocationPlusOne(LSP.Location originalLo
494520

495521
private static LSP.DidChangeTextDocumentParams CreateDidChangeTextDocumentParams(
496522
DocumentUri documentUri,
497-
ImmutableArray<(LSP.Range Range, string Text)> changes)
523+
ImmutableArray<(LSP.Range Range, string Text)> changes,
524+
int version = 0)
498525
{
499526
var changeEvents = changes.Select(change => new LSP.TextDocumentContentChangeEvent
500527
{
@@ -506,20 +533,22 @@ private static LSP.DidChangeTextDocumentParams CreateDidChangeTextDocumentParams
506533
{
507534
TextDocument = new LSP.VersionedTextDocumentIdentifier
508535
{
509-
DocumentUri = documentUri
536+
DocumentUri = documentUri,
537+
Version = version
510538
},
511539
ContentChanges = changeEvents
512540
};
513541
}
514542

515-
private static LSP.DidOpenTextDocumentParams CreateDidOpenTextDocumentParams(DocumentUri uri, string source, string languageId = "")
543+
private static LSP.DidOpenTextDocumentParams CreateDidOpenTextDocumentParams(DocumentUri uri, string source, string languageId = "", int version = 0)
516544
=> new LSP.DidOpenTextDocumentParams
517545
{
518546
TextDocument = new LSP.TextDocumentItem
519547
{
520548
Text = source,
521549
DocumentUri = uri,
522-
LanguageId = languageId
550+
LanguageId = languageId,
551+
Version = version
523552
}
524553
};
525554

@@ -704,7 +733,7 @@ public Task ExecutePreSerializedRequestAsync(string methodName, JsonDocument ser
704733
return _clientRpc.InvokeWithParameterObjectAsync(methodName, serializedRequest);
705734
}
706735

707-
public async Task OpenDocumentAsync(DocumentUri documentUri, string? text = null, string languageId = "")
736+
public async Task OpenDocumentAsync(DocumentUri documentUri, string? text = null, string languageId = "", int version = 0)
708737
{
709738
if (text == null)
710739
{
@@ -714,13 +743,13 @@ public async Task OpenDocumentAsync(DocumentUri documentUri, string? text = null
714743
text = sourceText.ToString();
715744
}
716745

717-
var didOpenParams = CreateDidOpenTextDocumentParams(documentUri, text.ToString(), languageId);
746+
var didOpenParams = CreateDidOpenTextDocumentParams(documentUri, text.ToString(), languageId, version);
718747
await ExecuteRequestAsync<LSP.DidOpenTextDocumentParams, object>(LSP.Methods.TextDocumentDidOpenName, didOpenParams, CancellationToken.None);
719748
}
720749

721750
/// <summary>
722751
/// Opens a document in the workspace only, and waits for workspace operations.
723-
/// Use <see cref="OpenDocumentAsync(DocumentUri, string, string)"/> if the document should be opened in LSP"/>
752+
/// Use <see cref="OpenDocumentAsync(DocumentUri, string, string, int)"/> if the document should be opened in LSP"/>
724753
/// </summary>
725754
public async Task OpenDocumentInWorkspaceAsync(DocumentId documentId, bool openAllLinkedDocuments, SourceText? text = null)
726755
{
@@ -745,23 +774,34 @@ public async Task OpenDocumentInWorkspaceAsync(DocumentId documentId, bool openA
745774
await WaitForWorkspaceOperationsAsync(TestWorkspace);
746775
}
747776

748-
public Task ReplaceTextAsync(DocumentUri documentUri, params (LSP.Range Range, string Text)[] changes)
777+
public Task ReplaceTextAsync(DocumentUri documentUri, int version, params (LSP.Range Range, string Text)[] changes)
749778
{
750779
var didChangeParams = CreateDidChangeTextDocumentParams(
751780
documentUri,
752-
[.. changes]);
781+
[.. changes],
782+
version);
753783
return ExecuteRequestAsync<LSP.DidChangeTextDocumentParams, object>(LSP.Methods.TextDocumentDidChangeName, didChangeParams, CancellationToken.None);
754784
}
755785

756-
public Task InsertTextAsync(DocumentUri documentUri, params (int Line, int Column, string Text)[] changes)
786+
public Task ReplaceTextAsync(DocumentUri documentUri, params (LSP.Range Range, string Text)[] changes)
757787
{
758-
return ReplaceTextAsync(documentUri, [.. changes.Select(change => (new LSP.Range
788+
return ReplaceTextAsync(documentUri, version: 0, changes);
789+
}
790+
791+
public Task InsertTextAsync(DocumentUri documentUri, int version, params (int Line, int Column, string Text)[] changes)
792+
{
793+
return ReplaceTextAsync(documentUri, version, [.. changes.Select(change => (new LSP.Range
759794
{
760795
Start = new LSP.Position { Line = change.Line, Character = change.Column },
761796
End = new LSP.Position { Line = change.Line, Character = change.Column }
762797
}, change.Text))]);
763798
}
764799

800+
public Task InsertTextAsync(DocumentUri documentUri, params (int Line, int Column, string Text)[] changes)
801+
{
802+
return InsertTextAsync(documentUri, version: 0, changes);
803+
}
804+
765805
public Task DeleteTextAsync(DocumentUri documentUri, params (int StartLine, int StartColumn, int EndLine, int EndColumn)[] changes)
766806
{
767807
return ReplaceTextAsync(documentUri, [.. changes.Select(change => (new LSP.Range

src/LanguageServer/Protocol/Handler/DocumentChanges/DidChangeHandler.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ public TextDocumentIdentifier GetTextDocumentIdentifier(DidChangeTextDocumentPar
2828

2929
public Task<object?> HandleRequestAsync(DidChangeTextDocumentParams request, RequestContext context, CancellationToken cancellationToken)
3030
{
31-
var text = context.GetTrackedDocumentSourceText(request.TextDocument.DocumentUri);
31+
var text = context.GetTrackedDocumentInfo(request.TextDocument.DocumentUri).SourceText;
3232

3333
text = GetUpdatedSourceText(request.ContentChanges, text);
3434

35-
context.UpdateTrackedDocument(request.TextDocument.DocumentUri, text);
35+
context.UpdateTrackedDocument(request.TextDocument.DocumentUri, text, request.TextDocument.Version);
3636

3737
return SpecializedTasks.Default<object>();
3838
}

src/LanguageServer/Protocol/Handler/DocumentChanges/DidOpenHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,6 @@ public async Task HandleNotificationAsync(LSP.DidOpenTextDocumentParams request,
3535
// Create SourceText from binary representation of the document, retrieve encoding from the request and checksum algorithm from the project.
3636
var sourceText = SourceText.From(request.TextDocument.Text, System.Text.Encoding.UTF8, SourceHashAlgorithms.OpenDocumentChecksumAlgorithm);
3737

38-
await context.StartTrackingAsync(request.TextDocument.DocumentUri, sourceText, request.TextDocument.LanguageId, cancellationToken).ConfigureAwait(false);
38+
await context.StartTrackingAsync(request.TextDocument.DocumentUri, sourceText, request.TextDocument.LanguageId, request.TextDocument.Version, cancellationToken).ConfigureAwait(false);
3939
}
4040
}

src/LanguageServer/Protocol/Handler/IDocumentChangeTracker.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler;
1717
/// </summary>
1818
internal interface IDocumentChangeTracker
1919
{
20-
ValueTask StartTrackingAsync(DocumentUri documentUri, SourceText initialText, string languageId, CancellationToken cancellationToken);
21-
void UpdateTrackedDocument(DocumentUri documentUri, SourceText text);
20+
ValueTask StartTrackingAsync(DocumentUri documentUri, SourceText initialText, string languageId, int lspVersion, CancellationToken cancellationToken);
21+
void UpdateTrackedDocument(DocumentUri documentUri, SourceText text, int lspVersion);
2222
ValueTask StopTrackingAsync(DocumentUri documentUri, CancellationToken cancellationToken);
2323
}
2424

2525
internal sealed class NonMutatingDocumentChangeTracker : IDocumentChangeTracker
2626
{
27-
public ValueTask StartTrackingAsync(DocumentUri documentUri, SourceText initialText, string languageId, CancellationToken cancellationToken)
27+
public ValueTask StartTrackingAsync(DocumentUri documentUri, SourceText initialText, string languageId, int lspVersion, CancellationToken cancellationToken)
2828
{
2929
throw new InvalidOperationException("Mutating documents not allowed in a non-mutating request handler");
3030
}
@@ -34,7 +34,7 @@ public ValueTask StopTrackingAsync(DocumentUri documentUri, CancellationToken ca
3434
throw new InvalidOperationException("Mutating documents not allowed in a non-mutating request handler");
3535
}
3636

37-
public void UpdateTrackedDocument(DocumentUri documentUri, SourceText text)
37+
public void UpdateTrackedDocument(DocumentUri documentUri, SourceText text, int lspVersion)
3838
{
3939
throw new InvalidOperationException("Mutating documents not allowed in a non-mutating request handler");
4040
}

src/LanguageServer/Protocol/Handler/RequestContext.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ internal readonly struct RequestContext
4242
/// It contains text that is consistent with all prior LSP text sync notifications, but LSP text sync requests
4343
/// which are ordered after this one in the queue are not reflected here.
4444
/// </remarks>
45-
private readonly ImmutableDictionary<DocumentUri, (SourceText Text, string LanguageId)> _trackedDocuments;
45+
private readonly ImmutableDictionary<DocumentUri, TrackedDocumentInfo> _trackedDocuments;
4646

4747
private readonly ILspServices _lspServices;
4848

@@ -171,7 +171,7 @@ public RequestContext(
171171
WellKnownLspServerKinds serverKind,
172172
TextDocument? document,
173173
IDocumentChangeTracker documentChangeTracker,
174-
ImmutableDictionary<DocumentUri, (SourceText Text, string LanguageId)> trackedDocuments,
174+
ImmutableDictionary<DocumentUri, TrackedDocumentInfo> trackedDocuments,
175175
ImmutableArray<string> supportedLanguages,
176176
ILspServices lspServices,
177177
CancellationToken queueCancellationToken)
@@ -299,20 +299,20 @@ public static async Task<RequestContext> CreateAsync(
299299
/// Allows a mutating request to open a document and start it being tracked.
300300
/// Mutating requests are serialized by the execution queue in order to prevent concurrent access.
301301
/// </summary>
302-
public ValueTask StartTrackingAsync(DocumentUri uri, SourceText initialText, string languageId, CancellationToken cancellationToken)
303-
=> _documentChangeTracker.StartTrackingAsync(uri, initialText, languageId, cancellationToken);
302+
public ValueTask StartTrackingAsync(DocumentUri uri, SourceText initialText, string languageId, int lspVersion, CancellationToken cancellationToken)
303+
=> _documentChangeTracker.StartTrackingAsync(uri, initialText, languageId, lspVersion, cancellationToken);
304304

305305
/// <summary>
306306
/// Allows a mutating request to update the contents of a tracked document.
307307
/// Mutating requests are serialized by the execution queue in order to prevent concurrent access.
308308
/// </summary>
309-
public void UpdateTrackedDocument(DocumentUri uri, SourceText changedText)
310-
=> _documentChangeTracker.UpdateTrackedDocument(uri, changedText);
309+
public void UpdateTrackedDocument(DocumentUri uri, SourceText changedText, int lspVersion)
310+
=> _documentChangeTracker.UpdateTrackedDocument(uri, changedText, lspVersion);
311311

312-
public SourceText GetTrackedDocumentSourceText(DocumentUri documentUri)
312+
public TrackedDocumentInfo GetTrackedDocumentInfo(DocumentUri documentUri)
313313
{
314314
Contract.ThrowIfFalse(_trackedDocuments.ContainsKey(documentUri), $"Attempted to get text for {documentUri} which is not open.");
315-
return _trackedDocuments[documentUri].Text;
315+
return _trackedDocuments[documentUri];
316316
}
317317

318318
/// <summary>

0 commit comments

Comments
 (0)