зеркало из https://github.com/dotnet/razor.git
Simplified provisional completion implementation (#1749)
This commit is contained in:
Родитель
4c8dbd0beb
Коммит
43850ca22f
|
@ -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<TextChange> changes, long hostDocumentVersion, bool provisional = false)
|
||||
public override VirtualDocumentSnapshot Update(IReadOnlyList<TextChange> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,14 +58,14 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor
|
|||
}
|
||||
}
|
||||
|
||||
public override LSPDocumentSnapshot UpdateVirtualDocument<TVirtualDocument>(IReadOnlyList<TextChange> changes, long hostDocumentVersion, bool provisional = false)
|
||||
public override LSPDocumentSnapshot UpdateVirtualDocument<TVirtualDocument>(IReadOnlyList<TextChange> changes, long hostDocumentVersion)
|
||||
{
|
||||
if (!TryGetVirtualDocument<TVirtualDocument>(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();
|
||||
|
||||
|
|
|
@ -101,8 +101,7 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor
|
|||
public override void UpdateVirtualDocument<TVirtualDocument>(
|
||||
Uri hostDocumentUri,
|
||||
IReadOnlyList<TextChange> 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<TVirtualDocument>(changes, hostDocumentVersion, provisional);
|
||||
var @new = lspDocument.UpdateVirtualDocument<TVirtualDocument>(changes, hostDocumentVersion);
|
||||
|
||||
if (old == @new)
|
||||
{
|
||||
|
|
|
@ -78,6 +78,24 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor.HtmlCSharp
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
var serverKind = projectionResult.LanguageKind == RazorLanguageKind.CSharp ? LanguageServerKind.CSharp : LanguageServerKind.Html;
|
||||
|
||||
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.
|
||||
}
|
||||
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,
|
||||
|
@ -88,27 +106,12 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor.HtmlCSharp
|
|||
}
|
||||
};
|
||||
|
||||
var serverKind = projectionResult.LanguageKind == RazorLanguageKind.CSharp ? LanguageServerKind.CSharp : LanguageServerKind.Html;
|
||||
|
||||
var provisionalCompletionParams = await GetProvisionalCompletionParamsAsync(request, documentSnapshot, projectionResult, cancellationToken).ConfigureAwait(false);
|
||||
if (provisionalCompletionParams != null)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
var result = await _requestInvoker.RequestServerAsync<CompletionParams, SumType<CompletionItem[], CompletionList>?>(
|
||||
result = await _requestInvoker.RequestServerAsync<CompletionParams, SumType<CompletionItem[], CompletionList>?>(
|
||||
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<CompletionParams> GetProvisionalCompletionParamsAsync(CompletionParams request, LSPDocumentSnapshot documentSnapshot, ProjectionResult projection, CancellationToken cancellationToken)
|
||||
internal async Task<(bool, SumType<CompletionItem[], CompletionList>?)> TryGetProvisionalCompletionsAsync(CompletionParams request, LSPDocumentSnapshot documentSnapshot, ProjectionResult projection, CancellationToken cancellationToken)
|
||||
{
|
||||
SumType<CompletionItem[], CompletionList>? 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<CSharpVirtualDocument>(documentSnapshot.Uri, changes, previousCharacterProjection.HostDocumentVersion, provisional: true);
|
||||
trackingDocumentManager.UpdateVirtualDocument<CSharpVirtualDocument>(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<CompletionParams, SumType<CompletionItem[], CompletionList>?>(
|
||||
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<CSharpVirtualDocument>(documentSnapshot.Uri, new[] { removeProvisionalDot }, previousCharacterProjection.HostDocumentVersion);
|
||||
|
||||
return (true, result);
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor
|
|||
|
||||
public override VirtualDocumentSnapshot CurrentSnapshot => _currentSnapshot;
|
||||
|
||||
public override VirtualDocumentSnapshot Update(IReadOnlyList<TextChange> changes, long hostDocumentVersion, bool provisional = false)
|
||||
public override VirtualDocumentSnapshot Update(IReadOnlyList<TextChange> 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];
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -20,7 +20,7 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor
|
|||
|
||||
public abstract IReadOnlyList<VirtualDocument> VirtualDocuments { get; }
|
||||
|
||||
public abstract LSPDocumentSnapshot UpdateVirtualDocument<TVirtualDocument>(IReadOnlyList<TextChange> changes, long hostDocumentVersion, bool provisional = false) where TVirtualDocument : VirtualDocument;
|
||||
public abstract LSPDocumentSnapshot UpdateVirtualDocument<TVirtualDocument>(IReadOnlyList<TextChange> changes, long hostDocumentVersion) where TVirtualDocument : VirtualDocument;
|
||||
|
||||
public bool TryGetVirtualDocument<TVirtualDocument>(out TVirtualDocument virtualDocument) where TVirtualDocument : VirtualDocument
|
||||
{
|
||||
|
|
|
@ -17,7 +17,6 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor
|
|||
public abstract void UpdateVirtualDocument<TVirtualDocument>(
|
||||
Uri hostDocumentUri,
|
||||
IReadOnlyList<TextChange> changes,
|
||||
long hostDocumentVersion,
|
||||
bool provisional = false) where TVirtualDocument : VirtualDocument;
|
||||
long hostDocumentVersion) where TVirtualDocument : VirtualDocument;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,6 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor
|
|||
|
||||
public abstract long? HostDocumentSyncVersion { get; }
|
||||
|
||||
public abstract VirtualDocumentSnapshot Update(IReadOnlyList<TextChange> changes, long hostDocumentVersion, bool provisional = false);
|
||||
public abstract VirtualDocumentSnapshot Update(IReadOnlyList<TextChange> changes, long hostDocumentVersion);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ITextBuffer>();
|
||||
var textBufferSnapshot = Mock.Of<ITextSnapshot>();
|
||||
textBuffer.Setup(buffer => buffer.CreateEdit())
|
||||
textBuffer.Setup(buffer => buffer.CreateEdit(EditOptions.None, null, It.IsAny<IInviolableEditTag>()))
|
||||
.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<ITextEdit>();
|
||||
edit.Setup(e => e.Insert(insert.Span.Start, insert.NewText)).Verifiable();
|
||||
edit.Setup(e => e.Apply()).Verifiable();
|
||||
|
||||
var revertEdit = new Mock<ITextEdit>();
|
||||
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<ITextChange>();
|
||||
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<ITextBuffer>(
|
||||
buffer => buffer.CreateEdit() == edit &&
|
||||
buffer.CreateEdit(EditOptions.None, It.IsAny<int?>(), It.IsAny<IInviolableEditTag>()) == revertEdit &&
|
||||
buffer.CurrentSnapshot == Mock.Of<ITextSnapshot>(s => s.Version.Changes == changes));
|
||||
var textBuffer = Mock.Of<ITextBuffer>(buffer => buffer.CreateEdit(EditOptions.None, null, It.IsAny<IInviolableEditTag>()) == edit && buffer.CurrentSnapshot == Mock.Of<ITextSnapshot>());
|
||||
return textBuffer;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor
|
|||
document.Uri == Uri &&
|
||||
document.CurrentSnapshot == LSPDocumentSnapshot &&
|
||||
document.VirtualDocuments == new[] { new TestVirtualDocument() } &&
|
||||
document.UpdateVirtualDocument<TestVirtualDocument>(It.IsAny<IReadOnlyList<TextChange>>(), It.IsAny<long>(), It.IsAny<bool>()) == Mock.Of<LSPDocumentSnapshot>());
|
||||
document.UpdateVirtualDocument<TestVirtualDocument>(It.IsAny<IReadOnlyList<TextChange>>(), It.IsAny<long>()) == Mock.Of<LSPDocumentSnapshot>());
|
||||
LSPDocumentFactory = Mock.Of<LSPDocumentFactory>(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<TestVirtualDocument>(Uri, changes, 123, provisional: true);
|
||||
manager.UpdateVirtualDocument<TestVirtualDocument>(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<TextChange> changes, long hostDocumentVersion, bool provisional)
|
||||
public override VirtualDocumentSnapshot Update(IReadOnlyList<TextChange> changes, long hostDocumentVersion)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
|
|
@ -31,12 +31,11 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor
|
|||
var originalSnapshot = document.CurrentSnapshot;
|
||||
|
||||
// Act
|
||||
document.UpdateVirtualDocument<TestVirtualDocument>(changes, hostDocumentVersion: 1337, provisional: true);
|
||||
document.UpdateVirtualDocument<TestVirtualDocument>(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<TextChange> 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<TextChange> changes, long hostDocumentVersion, bool provisional)
|
||||
public override VirtualDocumentSnapshot Update(IReadOnlyList<TextChange> changes, long hostDocumentVersion)
|
||||
{
|
||||
_hostDocumentVersion = hostDocumentVersion;
|
||||
Changes = changes;
|
||||
Provisional = provisional;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor
|
|||
{
|
||||
// Arrange
|
||||
var documentManager = new Mock<TrackingLSPDocumentManager>();
|
||||
documentManager.Setup(manager => manager.UpdateVirtualDocument<CSharpVirtualDocument>(It.IsAny<Uri>(), It.IsAny<IReadOnlyList<TextChange>>(), 1337, false /* UpdateCSharpBuffer request should never be provisional */))
|
||||
documentManager.Setup(manager => manager.UpdateVirtualDocument<CSharpVirtualDocument>(It.IsAny<Uri>(), It.IsAny<IReadOnlyList<TextChange>>(), 1337))
|
||||
.Verifiable();
|
||||
var target = new DefaultRazorLanguageServerCustomMessageTarget(documentManager.Object);
|
||||
var request = new UpdateBufferRequest()
|
||||
|
|
|
@ -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<LSPDocumentSnapshot>(), projectionResult, CancellationToken.None).ConfigureAwait(false);
|
||||
var (succeeded, result) = await completionHandler.TryGetProvisionalCompletionsAsync(completionRequest, Mock.Of<LSPDocumentSnapshot>(), 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<LSPDocumentSnapshot>(), projectionResult, CancellationToken.None).ConfigureAwait(false);
|
||||
var (succeeded, result) = await completionHandler.TryGetProvisionalCompletionsAsync(completionRequest, Mock.Of<LSPDocumentSnapshot>(), 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<LSPDocumentSnapshot>(), projectionResult, CancellationToken.None).ConfigureAwait(false);
|
||||
var (succeeded, result) = await completionHandler.TryGetProvisionalCompletionsAsync(completionRequest, Mock.Of<LSPDocumentSnapshot>(), 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<LSPDocumentSnapshot>(), projectionResult, CancellationToken.None).ConfigureAwait(false);
|
||||
var (succeeded, result) = await completionHandler.TryGetProvisionalCompletionsAsync(completionRequest, Mock.Of<LSPDocumentSnapshot>(), 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<LSPRequestInvoker>();
|
||||
var languageServerCalled = false;
|
||||
var expectedItem = new CompletionItem() { InsertText = "DateTime" };
|
||||
var requestInvoker = new Mock<LSPRequestInvoker>(MockBehavior.Strict);
|
||||
requestInvoker
|
||||
.Setup(r => r.RequestServerAsync<CompletionParams, SumType<CompletionItem[], CompletionList>?>(It.IsAny<string>(), LanguageServerKind.CSharp, It.IsAny<CompletionParams>(), It.IsAny<CancellationToken>()))
|
||||
.Callback<string, LanguageServerKind, CompletionParams, CancellationToken>((method, serverKind, completionParams, ct) =>
|
||||
{
|
||||
Assert.Equal(Methods.TextDocumentCompletionName, method);
|
||||
Assert.Equal(LanguageServerKind.CSharp, serverKind);
|
||||
languageServerCalled = true;
|
||||
})
|
||||
.Returns(Task.FromResult<SumType<CompletionItem[], CompletionList>?>(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<LSPDocumentSnapshot>(), projectionResult, CancellationToken.None).ConfigureAwait(false);
|
||||
var (succeeded, result) = await completionHandler.TryGetProvisionalCompletionsAsync(completionRequest, Mock.Of<LSPDocumentSnapshot>(), 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<LSPDocumentChangeEventArgs> 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<TVirtualDocument>(Uri hostDocumentUri, IReadOnlyList<TextChange> changes, long hostDocumentVersion, bool provisional = false)
|
||||
public override void UpdateVirtualDocument<TVirtualDocument>(Uri hostDocumentUri, IReadOnlyList<TextChange> changes, long hostDocumentVersion)
|
||||
{
|
||||
UpdateVirtualDocumentCalled = true;
|
||||
UpdateVirtualDocumentCallCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ITextBuffer>();
|
||||
var textBufferSnapshot = Mock.Of<ITextSnapshot>();
|
||||
textBuffer.Setup(buffer => buffer.CreateEdit())
|
||||
textBuffer.Setup(buffer => buffer.CreateEdit(EditOptions.None, null, It.IsAny<IInviolableEditTag>()))
|
||||
.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<ITextBuffer>(buffer => buffer.CreateEdit() == edit && buffer.CurrentSnapshot == Mock.Of<ITextSnapshot>());
|
||||
var textBuffer = Mock.Of<ITextBuffer>(buffer => buffer.CreateEdit(EditOptions.None, null, It.IsAny<IInviolableEditTag>()) == edit && buffer.CurrentSnapshot == Mock.Of<ITextSnapshot>());
|
||||
return textBuffer;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor
|
|||
|
||||
public override VirtualDocumentSnapshot CurrentSnapshot => throw new NotImplementedException();
|
||||
|
||||
public override VirtualDocumentSnapshot Update(IReadOnlyList<TextChange> changes, long hostDocumentVersion, bool provisional)
|
||||
public override VirtualDocumentSnapshot Update(IReadOnlyList<TextChange> changes, long hostDocumentVersion)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче