Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wire up updating the LSP background html document #1659

Merged
merged 2 commits into from
Mar 7, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,34 @@ internal void UpdateCSharpBuffer(JToken token)
request.HostDocumentVersion);
}

public override async Task UpdateHtmlBufferAsync(JToken token, CancellationToken cancellationToken)
{
if (token is null)
{
throw new ArgumentNullException(nameof(token));
}

await _joinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);

UpdateHtmlBuffer(token);
}

// Internal for testing
internal void UpdateHtmlBuffer(JToken token)
{
var request = token.ToObject<UpdateBufferRequest>();
if (request == null || request.HostDocumentFilePath == null)
{
return;
}

var hostDocumentUri = ConvertFilePathToUri(request.HostDocumentFilePath);
_documentManager.UpdateVirtualDocument<HtmlVirtualDocument>(
hostDocumentUri,
request.Changes,
request.HostDocumentVersion);
}

private Uri ConvertFilePathToUri(string filePath)
{
if (filePath.StartsWith("/") && RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor
{
internal class HtmlVirtualDocument : VirtualDocument
{
private long? _hostDocumentSyncVersion;
private HtmlVirtualDocumentSnapshot _currentSnapshot;

public HtmlVirtualDocument(Uri uri, ITextBuffer textBuffer)
Expand All @@ -33,13 +34,52 @@ public HtmlVirtualDocument(Uri uri, ITextBuffer textBuffer)

public override ITextBuffer TextBuffer { get; }

public override long? HostDocumentSyncVersion => null;
public override long? HostDocumentSyncVersion => _hostDocumentSyncVersion;

public override VirtualDocumentSnapshot CurrentSnapshot => _currentSnapshot;

public override VirtualDocumentSnapshot Update(IReadOnlyList<TextChange> changes, long hostDocumentVersion)
{
throw new NotImplementedException();
if (changes is null)
{
throw new ArgumentNullException(nameof(changes));
}

_hostDocumentSyncVersion = hostDocumentVersion;

if (changes.Count == 0)
{
_currentSnapshot = UpdateSnapshot();
return _currentSnapshot;
}

var edit = TextBuffer.CreateEdit();
for (var i = 0; i < changes.Count; i++)
{
var change = changes[i];

if (change.IsDelete())
{
edit.Delete(change.Span.Start, change.Span.Length);
}
else if (change.IsReplace())
{
edit.Replace(change.Span.Start, change.Span.Length, change.NewText);
}
else if (change.IsInsert())
{
edit.Insert(change.Span.Start, change.NewText);
}
else
{
throw new InvalidOperationException("Unknown edit type when updating LSP C# buffer.");
ajaybhargavb marked this conversation as resolved.
Show resolved Hide resolved
}
}

edit.Apply();
_currentSnapshot = UpdateSnapshot();

return _currentSnapshot;
}

private HtmlVirtualDocumentSnapshot UpdateSnapshot() => new HtmlVirtualDocumentSnapshot(Uri, TextBuffer.CurrentSnapshot, HostDocumentSyncVersion);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,8 @@ public abstract class RazorLanguageServerCustomMessageTarget
{
[JsonRpcMethod(LanguageServerConstants.RazorUpdateCSharpBufferEndpoint)]
public abstract Task UpdateCSharpBufferAsync(JToken token, CancellationToken cancellationToken);

[JsonRpcMethod(LanguageServerConstants.RazorUpdateHtmlBufferEndpoint)]
public abstract Task UpdateHtmlBufferAsync(JToken token, CancellationToken cancellationToken);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// 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 System;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Text;
using Moq;
using Xunit;

namespace Microsoft.VisualStudio.LanguageServerClient.Razor
{
public class HtmlVirtualDocumentTest
{
public HtmlVirtualDocumentTest()
{
Uri = new Uri("C:/path/to/file.razor__virtual.html");
}

private Uri Uri { get; }

[Fact]
public void Update_AlwaysSetsHostDocumentSyncVersion_AndUpdatesSnapshot()
{
// Arrange
var textBuffer = Mock.Of<ITextBuffer>(buffer => buffer.CurrentSnapshot == Mock.Of<ITextSnapshot>());
var document = new HtmlVirtualDocument(Uri, textBuffer);
var originalSnapshot = document.CurrentSnapshot;

// Act
document.Update(Array.Empty<TextChange>(), hostDocumentVersion: 1337);

// Assert
Assert.NotSame(originalSnapshot, document.CurrentSnapshot);
Assert.Equal(1337, document.HostDocumentSyncVersion);
}

[Fact]
public void Update_Insert()
{
// Arrange
var insert = new TextChange(new TextSpan(123, 0), "inserted text");
var edit = new Mock<ITextEdit>();
edit.Setup(e => e.Insert(insert.Span.Start, insert.NewText)).Verifiable();
edit.Setup(e => e.Apply()).Verifiable();
var textBuffer = CreateTextBuffer(edit.Object);
var document = new HtmlVirtualDocument(Uri, textBuffer);

// Act
document.Update(new[] { insert }, hostDocumentVersion: 1);

// Assert
edit.VerifyAll();
}

[Fact]
public void Update_Replace()
{
// Arrange
var replace = new TextChange(new TextSpan(123, 4), "replaced text");
var edit = new Mock<ITextEdit>();
edit.Setup(e => e.Replace(replace.Span.Start, replace.Span.Length, replace.NewText)).Verifiable();
edit.Setup(e => e.Apply()).Verifiable();
var textBuffer = CreateTextBuffer(edit.Object);
var document = new HtmlVirtualDocument(Uri, textBuffer);

// Act
document.Update(new[] { replace }, hostDocumentVersion: 1);

// Assert
edit.VerifyAll();
}

[Fact]
public void Update_Delete()
{
// Arrange
var delete = new TextChange(new TextSpan(123, 4), string.Empty);
var edit = new Mock<ITextEdit>();
edit.Setup(e => e.Delete(delete.Span.Start, delete.Span.Length)).Verifiable();
edit.Setup(e => e.Apply()).Verifiable();
var textBuffer = CreateTextBuffer(edit.Object);
var document = new HtmlVirtualDocument(Uri, textBuffer);

// Act
document.Update(new[] { delete }, hostDocumentVersion: 1);

// Assert
edit.VerifyAll();
}

[Fact]
public void Update_MultipleEdits()
{
// Arrange
var replace = new TextChange(new TextSpan(123, 4), "replaced text");
var delete = new TextChange(new TextSpan(123, 4), string.Empty);
var edit = new Mock<ITextEdit>();
edit.Setup(e => e.Delete(delete.Span.Start, delete.Span.Length)).Verifiable();
edit.Setup(e => e.Replace(replace.Span.Start, replace.Span.Length, replace.NewText)).Verifiable();
var textBuffer = CreateTextBuffer(edit.Object);
edit.Setup(e => e.Apply())
.Returns(textBuffer.CurrentSnapshot).Verifiable();
var document = new HtmlVirtualDocument(Uri, textBuffer);

// Act
document.Update(new[] { replace, delete }, hostDocumentVersion: 1);

// Assert
edit.VerifyAll();
}

[Fact]
public void Update_RecalculatesSnapshot()
{
// Arrange
var replace = new TextChange(new TextSpan(123, 4), "replaced text");
var edit = new Mock<ITextEdit>();
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())
.Returns(edit.Object);
textBuffer.Setup(buffer => buffer.CurrentSnapshot)
.Returns(() => textBufferSnapshot);
var editedSnapshot = Mock.Of<ITextSnapshot>();
edit.Setup(e => e.Apply())
.Callback(() =>
{
textBufferSnapshot = editedSnapshot;
});
var document = new HtmlVirtualDocument(Uri, textBuffer.Object);

// Act
document.Update(new[] { replace }, hostDocumentVersion: 1);

// Assert
Assert.Same(editedSnapshot, document.CurrentSnapshot.Snapshot);
}

public ITextBuffer CreateTextBuffer(ITextEdit edit)
{
var textBuffer = Mock.Of<ITextBuffer>(buffer => buffer.CreateEdit() == edit && buffer.CurrentSnapshot == Mock.Of<ITextSnapshot>());
return textBuffer;
}
}
}