diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/CSharpVirtualDocument.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/CSharpVirtualDocument.cs index e436d0ebdd..79d882e3d6 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/CSharpVirtualDocument.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/CSharpVirtualDocument.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text; @@ -12,11 +11,8 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor internal class CSharpVirtualDocument : VirtualDocument { private long? _hostDocumentSyncVersion; - private CSharpVirtualDocumentSnapshot _previousSnapshot; private CSharpVirtualDocumentSnapshot _currentSnapshot; - private bool _hasProvisionalChanges = false; - public CSharpVirtualDocument(Uri uri, ITextBuffer textBuffer) { if (uri is null) @@ -31,7 +27,6 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor Uri = uri; TextBuffer = textBuffer; - _previousSnapshot = null; _currentSnapshot = UpdateSnapshot(); } @@ -43,7 +38,7 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor public override VirtualDocumentSnapshot CurrentSnapshot => _currentSnapshot; - public override VirtualDocumentSnapshot Update(IReadOnlyList changes, long hostDocumentVersion, bool provisional = false) + public override VirtualDocumentSnapshot Update(IReadOnlyList changes, long hostDocumentVersion) { if (changes is null) { @@ -52,16 +47,13 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor _hostDocumentSyncVersion = hostDocumentVersion; - TryRevertProvisionalChanges(); - _hasProvisionalChanges = provisional; - if (changes.Count == 0) { _currentSnapshot = UpdateSnapshot(); return _currentSnapshot; } - using var edit = TextBuffer.CreateEdit(); + using var edit = TextBuffer.CreateEdit(EditOptions.None, reiteratedVersionNumber: null, InviolableEditTag.Instance); for (var i = 0; i < changes.Count; i++) { var change = changes[i]; @@ -90,42 +82,6 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor return _currentSnapshot; } - private bool TryRevertProvisionalChanges() - { - if (!_hasProvisionalChanges) - { - return false; - } - - Debug.Assert(_previousSnapshot != null); - - using var revertEdit = TextBuffer.CreateEdit(EditOptions.None, _previousSnapshot.Snapshot.Version.VersionNumber, InviolableEditTag.Instance); - var previousChanges = _previousSnapshot.Snapshot.Version.Changes; - for (var i = 0; i < previousChanges.Count; i++) - { - var change = previousChanges[i]; - revertEdit.Replace(change.NewSpan, change.OldText); - } - - revertEdit.Apply(); - - _hasProvisionalChanges = false; - - return true; - } - - private CSharpVirtualDocumentSnapshot UpdateSnapshot() - { - _previousSnapshot = _currentSnapshot; - return new CSharpVirtualDocumentSnapshot(Uri, TextBuffer.CurrentSnapshot, HostDocumentSyncVersion); - } - - // This indicates that no other entity should respond to the edit event associated with this tag. - private class InviolableEditTag : IInviolableEditTag - { - private InviolableEditTag() { } - - public readonly static IInviolableEditTag Instance = new InviolableEditTag(); - } + private CSharpVirtualDocumentSnapshot UpdateSnapshot() => new CSharpVirtualDocumentSnapshot(Uri, TextBuffer.CurrentSnapshot, HostDocumentSyncVersion); } } diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/DefaultLSPDocument.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/DefaultLSPDocument.cs index 319a46e060..77ccff71ad 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/DefaultLSPDocument.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/DefaultLSPDocument.cs @@ -58,14 +58,14 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor } } - public override LSPDocumentSnapshot UpdateVirtualDocument(IReadOnlyList changes, long hostDocumentVersion, bool provisional = false) + public override LSPDocumentSnapshot UpdateVirtualDocument(IReadOnlyList changes, long hostDocumentVersion) { if (!TryGetVirtualDocument(out var virtualDocument)) { throw new InvalidOperationException($"Cannot update virtual document of type {typeof(TVirtualDocument)} because LSP document {Uri} does not contain a virtual document of that type."); } - virtualDocument.Update(changes, hostDocumentVersion, provisional); + virtualDocument.Update(changes, hostDocumentVersion); _currentSnapshot = UpdateSnapshot(); diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/DefaultLSPDocumentManager.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/DefaultLSPDocumentManager.cs index 59a9b1c5a5..f8308a16b8 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/DefaultLSPDocumentManager.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/DefaultLSPDocumentManager.cs @@ -101,8 +101,7 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor public override void UpdateVirtualDocument( Uri hostDocumentUri, IReadOnlyList changes, - long hostDocumentVersion, - bool provisional = false) + long hostDocumentVersion) { if (hostDocumentUri is null) { @@ -133,7 +132,7 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor var old = lspDocument.CurrentSnapshot; var oldVirtual = virtualDocument.CurrentSnapshot; - var @new = lspDocument.UpdateVirtualDocument(changes, hostDocumentVersion, provisional); + var @new = lspDocument.UpdateVirtualDocument(changes, hostDocumentVersion); if (old == @new) { diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/HtmlCSharp/CompletionHandler.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/HtmlCSharp/CompletionHandler.cs index 4aeccb69ec..6776f849ac 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/HtmlCSharp/CompletionHandler.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/HtmlCSharp/CompletionHandler.cs @@ -78,37 +78,40 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor.HtmlCSharp return null; } - var completionParams = new CompletionParams() - { - Context = request.Context, - Position = projectionResult.Position, - TextDocument = new TextDocumentIdentifier() - { - Uri = projectionResult.Uri - } - }; + var serverKind = projectionResult.LanguageKind == RazorLanguageKind.CSharp ? LanguageServerKind.CSharp : LanguageServerKind.Html; - var provisionalCompletionParams = await GetProvisionalCompletionParamsAsync(request, documentSnapshot, projectionResult, cancellationToken).ConfigureAwait(false); - if (provisionalCompletionParams != null) + var (succeeded, result) = await TryGetProvisionalCompletionsAsync(request, documentSnapshot, projectionResult, cancellationToken).ConfigureAwait(false); + if (succeeded) { // This means the user has just typed a dot after some identifier such as (cursor is pipe): "DateTime.| " // In this case Razor interprets after the dot as Html and before it as C#. // We use this criteria to provide a better completion experience for what we call provisional changes. - completionParams = provisionalCompletionParams; - serverKind = LanguageServerKind.CSharp; } else if (!TriggerAppliesToProjection(request.Context, projectionResult.LanguageKind)) { return null; } + else + { + // This is a valid non-provisional completion request. + var completionParams = new CompletionParams() + { + Context = request.Context, + Position = projectionResult.Position, + TextDocument = new TextDocumentIdentifier() + { + Uri = projectionResult.Uri + } + }; - var result = await _requestInvoker.RequestServerAsync?>( - Methods.TextDocumentCompletionName, - serverKind, - completionParams, - cancellationToken).ConfigureAwait(false); + result = await _requestInvoker.RequestServerAsync?>( + Methods.TextDocumentCompletionName, + serverKind, + completionParams, + cancellationToken).ConfigureAwait(false); + } if (result.HasValue) { @@ -119,43 +122,43 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor.HtmlCSharp return result; } - internal async Task GetProvisionalCompletionParamsAsync(CompletionParams request, LSPDocumentSnapshot documentSnapshot, ProjectionResult projection, CancellationToken cancellationToken) + internal async Task<(bool, SumType?)> TryGetProvisionalCompletionsAsync(CompletionParams request, LSPDocumentSnapshot documentSnapshot, ProjectionResult projection, CancellationToken cancellationToken) { + SumType? result = null; if (projection.LanguageKind != RazorLanguageKind.Html || request.Context.TriggerKind != CompletionTriggerKind.TriggerCharacter || request.Context.TriggerCharacter != ".") { - return null; + return (false, result); } if (projection.Position.Character == 0) { // We're at the start of line. Can't have provisional completions here. - return null; + return (false, result); } var previousCharacterPosition = new Position(projection.Position.Line, projection.Position.Character - 1); var previousCharacterProjection = await _projectionProvider.GetProjectionAsync(documentSnapshot, previousCharacterPosition, cancellationToken).ConfigureAwait(false); if (previousCharacterProjection == null || previousCharacterProjection.LanguageKind != RazorLanguageKind.CSharp) { - return null; + return (false, result); } if (!(_documentManager is TrackingLSPDocumentManager trackingDocumentManager)) { - return null; + return (false, result); } // Edit the CSharp projected document to contain a '.'. This allows C# completion to provide valid // completion items for moments when a user has typed a '.' that's typically interpreted as Html. - var changes = new[] - { - new TextChange(TextSpan.FromBounds(previousCharacterProjection.PositionIndex, previousCharacterProjection.PositionIndex), "."), - }; + var addProvisionalDot = new TextChange( + TextSpan.FromBounds(previousCharacterProjection.PositionIndex, previousCharacterProjection.PositionIndex), + "."); await _joinableTaskFactory.SwitchToMainThreadAsync(); - trackingDocumentManager.UpdateVirtualDocument(documentSnapshot.Uri, changes, previousCharacterProjection.HostDocumentVersion, provisional: true); + trackingDocumentManager.UpdateVirtualDocument(documentSnapshot.Uri, new[] { addProvisionalDot }, previousCharacterProjection.HostDocumentVersion); var provisionalCompletionParams = new CompletionParams() { @@ -164,7 +167,20 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor.HtmlCSharp TextDocument = new TextDocumentIdentifier() { Uri = previousCharacterProjection.Uri } }; - return provisionalCompletionParams; + result = await _requestInvoker.RequestServerAsync?>( + Methods.TextDocumentCompletionName, + LanguageServerKind.CSharp, + provisionalCompletionParams, + cancellationToken).ConfigureAwait(true); + + // We have now obtained the necessary completion items. We no longer need the provisional change. Revert. + var removeProvisionalDot = new TextChange( + TextSpan.FromBounds(previousCharacterProjection.PositionIndex, previousCharacterProjection.PositionIndex + 1), + string.Empty); + + trackingDocumentManager.UpdateVirtualDocument(documentSnapshot.Uri, new[] { removeProvisionalDot }, previousCharacterProjection.HostDocumentVersion); + + return (true, result); } // Internal for testing diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/HtmlVirtualDocument.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/HtmlVirtualDocument.cs index 58e017eb5e..ec9a169a4b 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/HtmlVirtualDocument.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/HtmlVirtualDocument.cs @@ -38,7 +38,7 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor public override VirtualDocumentSnapshot CurrentSnapshot => _currentSnapshot; - public override VirtualDocumentSnapshot Update(IReadOnlyList changes, long hostDocumentVersion, bool provisional = false) + public override VirtualDocumentSnapshot Update(IReadOnlyList changes, long hostDocumentVersion) { if (changes is null) { @@ -53,7 +53,7 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor return _currentSnapshot; } - using var edit = TextBuffer.CreateEdit(); + using var edit = TextBuffer.CreateEdit(EditOptions.None, reiteratedVersionNumber: null, InviolableEditTag.Instance); for (var i = 0; i < changes.Count; i++) { var change = changes[i]; diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/InviolableEditTag.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/InviolableEditTag.cs new file mode 100644 index 0000000000..6a55da8e30 --- /dev/null +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/InviolableEditTag.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.VisualStudio.Text; + +namespace Microsoft.VisualStudio.LanguageServerClient.Razor +{ + // Used to indicate that no other entity should respond to the edit event associated with this tag. + internal class InviolableEditTag : IInviolableEditTag + { + private InviolableEditTag() { } + + public readonly static IInviolableEditTag Instance = new InviolableEditTag(); + } +} diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/LSPDocument.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/LSPDocument.cs index 516022924d..4cfdc9fc22 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/LSPDocument.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/LSPDocument.cs @@ -20,7 +20,7 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor public abstract IReadOnlyList VirtualDocuments { get; } - public abstract LSPDocumentSnapshot UpdateVirtualDocument(IReadOnlyList changes, long hostDocumentVersion, bool provisional = false) where TVirtualDocument : VirtualDocument; + public abstract LSPDocumentSnapshot UpdateVirtualDocument(IReadOnlyList changes, long hostDocumentVersion) where TVirtualDocument : VirtualDocument; public bool TryGetVirtualDocument(out TVirtualDocument virtualDocument) where TVirtualDocument : VirtualDocument { diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/TrackingLSPDocumentManager.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/TrackingLSPDocumentManager.cs index 168c456f28..5c65b08d6f 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/TrackingLSPDocumentManager.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/TrackingLSPDocumentManager.cs @@ -17,7 +17,6 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor public abstract void UpdateVirtualDocument( Uri hostDocumentUri, IReadOnlyList changes, - long hostDocumentVersion, - bool provisional = false) where TVirtualDocument : VirtualDocument; + long hostDocumentVersion) where TVirtualDocument : VirtualDocument; } } diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/VirtualDocument.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/VirtualDocument.cs index 06fea9fe0a..3c7a54c5d6 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/VirtualDocument.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/VirtualDocument.cs @@ -18,6 +18,6 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor public abstract long? HostDocumentSyncVersion { get; } - public abstract VirtualDocumentSnapshot Update(IReadOnlyList changes, long hostDocumentVersion, bool provisional = false); + public abstract VirtualDocumentSnapshot Update(IReadOnlyList changes, long hostDocumentVersion); } } diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServerClient.Razor.Test/CSharpVirtualDocumentTest.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServerClient.Razor.Test/CSharpVirtualDocumentTest.cs index 17280f7ea0..845beb05f4 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LanguageServerClient.Razor.Test/CSharpVirtualDocumentTest.cs +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServerClient.Razor.Test/CSharpVirtualDocumentTest.cs @@ -119,7 +119,7 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor edit.Setup(e => e.Replace(replace.Span.Start, replace.Span.Length, replace.NewText)); var textBuffer = new Mock(); var textBufferSnapshot = Mock.Of(); - textBuffer.Setup(buffer => buffer.CreateEdit()) + textBuffer.Setup(buffer => buffer.CreateEdit(EditOptions.None, null, It.IsAny())) .Returns(edit.Object); textBuffer.Setup(buffer => buffer.CurrentSnapshot) .Returns(() => textBufferSnapshot); @@ -138,55 +138,9 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor Assert.Same(editedSnapshot, document.CurrentSnapshot.Snapshot); } - [Fact] - public void Update_Provisional_AppliesAndRevertsProvisionalChanges() + public static ITextBuffer CreateTextBuffer(ITextEdit edit) { - // Arrange - var insert = new TextChange(new TextSpan(123, 0), "."); - var edit = new Mock(); - edit.Setup(e => e.Insert(insert.Span.Start, insert.NewText)).Verifiable(); - edit.Setup(e => e.Apply()).Verifiable(); - - var revertEdit = new Mock(); - revertEdit.Setup(e => e.Replace(new Span(123, 1), string.Empty)).Verifiable(); - revertEdit.Setup(e => e.Apply()).Verifiable(); - - var textBuffer = CreateTextBuffer(edit.Object, revertEdit.Object, new[] { insert }); - var document = new CSharpVirtualDocument(Uri, textBuffer); - - // Make a provisional edit followed by another edit. - - // Act 1 - document.Update(new[] { insert }, hostDocumentVersion: 1, provisional: true); - - // Assert 1 - edit.VerifyAll(); - - // Act 2 - document.Update(new[] { new TextChange(new TextSpan(125, 0), "Some other edit") }, hostDocumentVersion: 2, provisional: false); - - // Assert 2 - revertEdit.VerifyAll(); - } - - public static ITextBuffer CreateTextBuffer(ITextEdit edit, ITextEdit revertEdit = null, TextChange[] provisionalChanges = null) - { - var changes = new TestTextChangeCollection(); - if (provisionalChanges != null) - { - foreach (var provisionalChange in provisionalChanges) - { - var change = new Mock(); - change.SetupGet(c => c.NewSpan).Returns(new Span(provisionalChange.Span.Start, provisionalChange.NewText.Length)); - change.SetupGet(c => c.OldText).Returns(string.Empty); - changes.Add(change.Object); - } - } - - var textBuffer = Mock.Of( - buffer => buffer.CreateEdit() == edit && - buffer.CreateEdit(EditOptions.None, It.IsAny(), It.IsAny()) == revertEdit && - buffer.CurrentSnapshot == Mock.Of(s => s.Version.Changes == changes)); + var textBuffer = Mock.Of(buffer => buffer.CreateEdit(EditOptions.None, null, It.IsAny()) == edit && buffer.CurrentSnapshot == Mock.Of()); return textBuffer; } diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServerClient.Razor.Test/DefaultLSPDocumentManagerTest.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServerClient.Razor.Test/DefaultLSPDocumentManagerTest.cs index 87842a71ff..8691e35c1e 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LanguageServerClient.Razor.Test/DefaultLSPDocumentManagerTest.cs +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServerClient.Razor.Test/DefaultLSPDocumentManagerTest.cs @@ -27,7 +27,7 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor document.Uri == Uri && document.CurrentSnapshot == LSPDocumentSnapshot && document.VirtualDocuments == new[] { new TestVirtualDocument() } && - document.UpdateVirtualDocument(It.IsAny>(), It.IsAny(), It.IsAny()) == Mock.Of()); + document.UpdateVirtualDocument(It.IsAny>(), It.IsAny()) == Mock.Of()); LSPDocumentFactory = Mock.Of(factory => factory.Create(TextBuffer) == LSPDocument); } @@ -146,7 +146,7 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor var changes = new[] { new TextChange(new TextSpan(1, 1), string.Empty) }; // Act - manager.UpdateVirtualDocument(Uri, changes, 123, provisional: true); + manager.UpdateVirtualDocument(Uri, changes, 123); // Assert Assert.True(changedCalled); @@ -207,7 +207,7 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor public override long? HostDocumentSyncVersion => 123; - public override VirtualDocumentSnapshot Update(IReadOnlyList changes, long hostDocumentVersion, bool provisional) + public override VirtualDocumentSnapshot Update(IReadOnlyList changes, long hostDocumentVersion) { throw new NotImplementedException(); } diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServerClient.Razor.Test/DefaultLSPDocumentTest.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServerClient.Razor.Test/DefaultLSPDocumentTest.cs index 1edcb10302..85864be4c9 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LanguageServerClient.Razor.Test/DefaultLSPDocumentTest.cs +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServerClient.Razor.Test/DefaultLSPDocumentTest.cs @@ -31,12 +31,11 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor var originalSnapshot = document.CurrentSnapshot; // Act - document.UpdateVirtualDocument(changes, hostDocumentVersion: 1337, provisional: true); + document.UpdateVirtualDocument(changes, hostDocumentVersion: 1337); // Assert Assert.Equal(1337, virtualDocument.HostDocumentSyncVersion); Assert.Same(changes, virtualDocument.Changes); - Assert.True(virtualDocument.Provisional); Assert.NotEqual(originalSnapshot, document.CurrentSnapshot); } @@ -46,8 +45,6 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor public IReadOnlyList Changes { get; private set; } - public bool Provisional { get; private set; } - public override Uri Uri => throw new NotImplementedException(); public override ITextBuffer TextBuffer => throw new NotImplementedException(); @@ -56,11 +53,10 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor public override long? HostDocumentSyncVersion => _hostDocumentVersion; - public override VirtualDocumentSnapshot Update(IReadOnlyList changes, long hostDocumentVersion, bool provisional) + public override VirtualDocumentSnapshot Update(IReadOnlyList changes, long hostDocumentVersion) { _hostDocumentVersion = hostDocumentVersion; Changes = changes; - Provisional = provisional; return null; } diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServerClient.Razor.Test/DefaultRazorLanguageServerCustomMessageTargetTest.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServerClient.Razor.Test/DefaultRazorLanguageServerCustomMessageTargetTest.cs index 14d6cfc858..bbb6330e4b 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LanguageServerClient.Razor.Test/DefaultRazorLanguageServerCustomMessageTargetTest.cs +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServerClient.Razor.Test/DefaultRazorLanguageServerCustomMessageTargetTest.cs @@ -53,7 +53,7 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor { // Arrange var documentManager = new Mock(); - documentManager.Setup(manager => manager.UpdateVirtualDocument(It.IsAny(), It.IsAny>(), 1337, false /* UpdateCSharpBuffer request should never be provisional */)) + documentManager.Setup(manager => manager.UpdateVirtualDocument(It.IsAny(), It.IsAny>(), 1337)) .Verifiable(); var target = new DefaultRazorLanguageServerCustomMessageTarget(documentManager.Object); var request = new UpdateBufferRequest() diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServerClient.Razor.Test/HtmlCSharp/CompletionHandlerTest.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServerClient.Razor.Test/HtmlCSharp/CompletionHandlerTest.cs index 61d80bf000..9d4ba39111 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LanguageServerClient.Razor.Test/HtmlCSharp/CompletionHandlerTest.cs +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServerClient.Razor.Test/HtmlCSharp/CompletionHandlerTest.cs @@ -343,7 +343,7 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor.HtmlCSharp } [Fact] - public async Task GetProvisionalCompletionParamsAsync_CSharpProjection_ReturnsNull() + public async Task TryGetProvisionalCompletionsAsync_CSharpProjection_ReturnsFalse() { // Arrange var completionRequest = new CompletionParams() @@ -371,14 +371,15 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor.HtmlCSharp var completionHandler = new CompletionHandler(JoinableTaskContext, requestInvoker.Object, documentManager, projectionProvider.Object); // Act - var result = await completionHandler.GetProvisionalCompletionParamsAsync(completionRequest, Mock.Of(), projectionResult, CancellationToken.None).ConfigureAwait(false); + var (succeeded, result) = await completionHandler.TryGetProvisionalCompletionsAsync(completionRequest, Mock.Of(), projectionResult, CancellationToken.None).ConfigureAwait(false); // Assert + Assert.False(succeeded); Assert.Null(result); } [Fact] - public async Task GetProvisionalCompletionParamsAsync_TriggerCharacterNotDot_ReturnsNull() + public async Task TryGetProvisionalCompletionsAsync_TriggerCharacterNotDot_ReturnsFalse() { // Arrange var completionRequest = new CompletionParams() @@ -406,14 +407,15 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor.HtmlCSharp var completionHandler = new CompletionHandler(JoinableTaskContext, requestInvoker.Object, documentManager, projectionProvider.Object); // Act - var result = await completionHandler.GetProvisionalCompletionParamsAsync(completionRequest, Mock.Of(), projectionResult, CancellationToken.None).ConfigureAwait(false); + var (succeeded, result) = await completionHandler.TryGetProvisionalCompletionsAsync(completionRequest, Mock.Of(), projectionResult, CancellationToken.None).ConfigureAwait(false); // Assert + Assert.False(succeeded); Assert.Null(result); } [Fact] - public async Task GetProvisionalCompletionParamsAsync_PreviousCharacterHtml_ReturnsNull() + public async Task TryGetProvisionalCompletionsAsync_PreviousCharacterHtml_ReturnsFalse() { // Arrange var completionRequest = new CompletionParams() @@ -447,14 +449,15 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor.HtmlCSharp var completionHandler = new CompletionHandler(JoinableTaskContext, requestInvoker.Object, documentManager, projectionProvider.Object); // Act - var result = await completionHandler.GetProvisionalCompletionParamsAsync(completionRequest, Mock.Of(), projectionResult, CancellationToken.None).ConfigureAwait(false); + var (succeeded, result) = await completionHandler.TryGetProvisionalCompletionsAsync(completionRequest, Mock.Of(), projectionResult, CancellationToken.None).ConfigureAwait(false); // Assert + Assert.False(succeeded); Assert.Null(result); } [Fact] - public async Task GetProvisionalCompletionParamsAsync_ProjectionAtStartOfLine_ReturnsNull() + public async Task TryGetProvisionalCompletionsAsync_ProjectionAtStartOfLine_ReturnsFalse() { // Arrange var completionRequest = new CompletionParams() @@ -488,14 +491,15 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor.HtmlCSharp var completionHandler = new CompletionHandler(JoinableTaskContext, requestInvoker.Object, documentManager, projectionProvider.Object); // Act - var result = await completionHandler.GetProvisionalCompletionParamsAsync(completionRequest, Mock.Of(), projectionResult, CancellationToken.None).ConfigureAwait(false); + var (succeeded, result) = await completionHandler.TryGetProvisionalCompletionsAsync(completionRequest, Mock.Of(), projectionResult, CancellationToken.None).ConfigureAwait(false); // Assert + Assert.False(succeeded); Assert.Null(result); } [Fact] - public async Task GetProvisionalCompletionParamsAsync_AtCorrectProvisionalCompletionPoint_ReturnsCorrectParams() + public async Task TryGetProvisionalCompletionsAsync_AtCorrectProvisionalCompletionPoint_ReturnsExpectedResult() { // Arrange var completionRequest = new CompletionParams() @@ -513,7 +517,18 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor.HtmlCSharp var documentManager = new TestDocumentManager(); - var requestInvoker = new Mock(); + var languageServerCalled = false; + var expectedItem = new CompletionItem() { InsertText = "DateTime" }; + var requestInvoker = new Mock(MockBehavior.Strict); + requestInvoker + .Setup(r => r.RequestServerAsync?>(It.IsAny(), LanguageServerKind.CSharp, It.IsAny(), It.IsAny())) + .Callback((method, serverKind, completionParams, ct) => + { + Assert.Equal(Methods.TextDocumentCompletionName, method); + Assert.Equal(LanguageServerKind.CSharp, serverKind); + languageServerCalled = true; + }) + .Returns(Task.FromResult?>(new[] { expectedItem })); var projectionResult = new ProjectionResult() { @@ -533,15 +548,15 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor.HtmlCSharp var completionHandler = new CompletionHandler(JoinableTaskContext, requestInvoker.Object, documentManager, projectionProvider.Object); // Act - var result = await completionHandler.GetProvisionalCompletionParamsAsync(completionRequest, Mock.Of(), projectionResult, CancellationToken.None).ConfigureAwait(false); + var (succeeded, result) = await completionHandler.TryGetProvisionalCompletionsAsync(completionRequest, Mock.Of(), projectionResult, CancellationToken.None).ConfigureAwait(false); // Assert - Assert.True(documentManager.UpdateVirtualDocumentCalled); + Assert.True(succeeded); + Assert.True(languageServerCalled); + Assert.Equal(2, documentManager.UpdateVirtualDocumentCallCount); Assert.NotNull(result); - Assert.Same(completionRequest.Context, result.Context); - Assert.Equal(virtualDocumentUri, result.TextDocument.Uri); - Assert.Equal(previousCharacterProjection.Position.Line, result.Position.Line); - Assert.Equal(previousCharacterProjection.Position.Character + 1, result.Position.Character); + var item = Assert.Single((CompletionItem[])result.Value); + Assert.Equal(expectedItem.InsertText, item.InsertText); } private class TestDocumentManager : TrackingLSPDocumentManager @@ -550,7 +565,7 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor.HtmlCSharp public override event EventHandler Changed; - public bool UpdateVirtualDocumentCalled { get; private set; } + public int UpdateVirtualDocumentCallCount { get; private set; } public override bool TryGetDocument(Uri uri, out LSPDocumentSnapshot lspDocumentSnapshot) { @@ -574,9 +589,9 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor.HtmlCSharp throw new NotImplementedException(); } - public override void UpdateVirtualDocument(Uri hostDocumentUri, IReadOnlyList changes, long hostDocumentVersion, bool provisional = false) + public override void UpdateVirtualDocument(Uri hostDocumentUri, IReadOnlyList changes, long hostDocumentVersion) { - UpdateVirtualDocumentCalled = true; + UpdateVirtualDocumentCallCount++; } } } diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServerClient.Razor.Test/HtmlVirtualDocumentTest.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServerClient.Razor.Test/HtmlVirtualDocumentTest.cs index f58c330f2b..e0eb2439e1 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LanguageServerClient.Razor.Test/HtmlVirtualDocumentTest.cs +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServerClient.Razor.Test/HtmlVirtualDocumentTest.cs @@ -118,7 +118,7 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor edit.Setup(e => e.Replace(replace.Span.Start, replace.Span.Length, replace.NewText)); var textBuffer = new Mock(); var textBufferSnapshot = Mock.Of(); - textBuffer.Setup(buffer => buffer.CreateEdit()) + textBuffer.Setup(buffer => buffer.CreateEdit(EditOptions.None, null, It.IsAny())) .Returns(edit.Object); textBuffer.Setup(buffer => buffer.CurrentSnapshot) .Returns(() => textBufferSnapshot); @@ -139,7 +139,7 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor public static ITextBuffer CreateTextBuffer(ITextEdit edit) { - var textBuffer = Mock.Of(buffer => buffer.CreateEdit() == edit && buffer.CurrentSnapshot == Mock.Of()); + var textBuffer = Mock.Of(buffer => buffer.CreateEdit(EditOptions.None, null, It.IsAny()) == edit && buffer.CurrentSnapshot == Mock.Of()); return textBuffer; } } diff --git a/src/Razor/test/Microsoft.VisualStudio.LanguageServerClient.Razor.Test/LSPDocumentTest.cs b/src/Razor/test/Microsoft.VisualStudio.LanguageServerClient.Razor.Test/LSPDocumentTest.cs index 1450d66590..d7e4d5fc28 100644 --- a/src/Razor/test/Microsoft.VisualStudio.LanguageServerClient.Razor.Test/LSPDocumentTest.cs +++ b/src/Razor/test/Microsoft.VisualStudio.LanguageServerClient.Razor.Test/LSPDocumentTest.cs @@ -58,7 +58,7 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor public override VirtualDocumentSnapshot CurrentSnapshot => throw new NotImplementedException(); - public override VirtualDocumentSnapshot Update(IReadOnlyList changes, long hostDocumentVersion, bool provisional) + public override VirtualDocumentSnapshot Update(IReadOnlyList changes, long hostDocumentVersion) { throw new NotImplementedException(); }