Skip to content

Commit

Permalink
Add support for acquiring formatting options from client.
Browse files Browse the repository at this point in the history
- This is the final part to #6618 to enable completion resolve to acquire formatting options for complex text edits so it can properly format text edits
- We now expose a new endpoint on the client under `razor/formatting/options` that will return the most recent understanding of formatting options
- To make this possible updated the `FormattingOptionsProvider` API to take a `Uri` instead of a document snapshot.
- Updated tests accordingly

Final part of #6618
  • Loading branch information
NTaylorMullen committed Jul 22, 2022
1 parent f8041b9 commit 1c599e9
Show file tree
Hide file tree
Showing 14 changed files with 124 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ public static class LanguageServerConstants
public const string RazorCompletionEndpointName = "razor/completion";

public const string RazorCompletionResolveEndpointName = "razor/completionItem/resolve";

public const string RazorGetFormattingOptionsEndpointName = "razor/formatting/options";

// RZLS Custom Message Targets
public const string RazorUpdateCSharpBufferEndpoint = "razor/updateCSharpBuffer";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,13 @@ private async Task<VSInternalCompletionItem> PostProcessCompletionItemAsync(
{
return resolvedCompletionItem;
}

// TODO: Pull active formatting options from client.
var formattingOptions = new FormattingOptions()

var delegatedRequest = await _languageServer.SendRequestAsync(LanguageServerConstants.RazorGetFormattingOptionsEndpointName, documentContext.Identifier).ConfigureAwait(false);
var formattingOptions = await delegatedRequest.Returning<FormattingOptions?>(cancellationToken).ConfigureAwait(false);
if (formattingOptions is null)
{
InsertSpaces = true,
TabSize = 4,
};
return resolvedCompletionItem;
}

if (resolvedCompletionItem.TextEdit is not null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,36 @@ namespace Microsoft.VisualStudio.LanguageServer.ContainedLanguage
[Export(typeof(FormattingOptionsProvider))]
internal class DefaultFormattingOptionsProvider : FormattingOptionsProvider
{
private readonly LSPDocumentManager _documentManager;
private readonly IIndentationManagerService _indentationManagerService;

[ImportingConstructor]
public DefaultFormattingOptionsProvider(IIndentationManagerService indentationManagerService)
public DefaultFormattingOptionsProvider(
LSPDocumentManager documentManager,
IIndentationManagerService indentationManagerService)
{
if (indentationManagerService is null)
{
throw new ArgumentNullException(nameof(indentationManagerService));
}

_documentManager = documentManager;
_indentationManagerService = indentationManagerService;
}

public override FormattingOptions GetOptions(LSPDocumentSnapshot documentSnapshot)
public override FormattingOptions? GetOptions(Uri uri)
{
_indentationManagerService.GetIndentation(documentSnapshot.Snapshot.TextBuffer, explicitFormat: false, out var insertSpaces, out var tabSize, out _);
if (!_documentManager.TryGetDocument(uri, out var documentSnapshot))
{
return null;
}

_indentationManagerService.GetIndentation(
documentSnapshot.Snapshot.TextBuffer,
explicitFormat: false,
out var insertSpaces,
out var tabSize,
out _);
var formattingOptions = new FormattingOptions()
{
InsertSpaces = insertSpaces,
TabSize = tabSize,
};

return formattingOptions;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.

using System;
using Microsoft.VisualStudio.LanguageServer.Protocol;

namespace Microsoft.VisualStudio.LanguageServer.ContainedLanguage
{
public abstract class FormattingOptionsProvider
{
public abstract FormattingOptions GetOptions(LSPDocumentSnapshot documentSnapshot);
public abstract FormattingOptions? GetOptions(Uri uri);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ internal class DefaultRazorLanguageServerCustomMessageTarget : RazorLanguageServ
private readonly LSPRequestInvoker _requestInvoker;
private readonly RazorUIContextManager _uIContextManager;
private readonly IDisposable _razorReadyListener;
private readonly FormattingOptionsProvider _formattingOptionsProvider;
private readonly EditorSettingsManager _editorSettingsManager;
private readonly LSPDocumentSynchronizer _documentSynchronizer;

Expand All @@ -52,6 +53,7 @@ public DefaultRazorLanguageServerCustomMessageTarget(
LSPRequestInvoker requestInvoker,
RazorUIContextManager uIContextManager,
IRazorAsynchronousOperationListenerProviderAccessor asyncOpListenerProvider,
FormattingOptionsProvider formattingOptionsProvider,
EditorSettingsManager editorSettingsManager,
LSPDocumentSynchronizer documentSynchronizer)
: this(
Expand All @@ -60,6 +62,7 @@ public DefaultRazorLanguageServerCustomMessageTarget(
requestInvoker,
uIContextManager,
asyncOpListenerProvider.GetListener(RazorReadyFeature).BeginAsyncOperation(RazorReadyFeature),
formattingOptionsProvider,
editorSettingsManager,
documentSynchronizer)
{
Expand All @@ -72,8 +75,8 @@ internal DefaultRazorLanguageServerCustomMessageTarget(
LSPRequestInvoker requestInvoker,
RazorUIContextManager uIContextManager,
IDisposable razorReadyListener,
EditorSettingsManager editorSettingsManager,
LSPDocumentSynchronizer documentSynchronizer)
FormattingOptionsProvider formattingOptionsProvider,
EditorSettingsManager editorSettingsManager, LSPDocumentSynchronizer documentSynchronizer)
{
if (documentManager is null)
{
Expand All @@ -100,6 +103,11 @@ internal DefaultRazorLanguageServerCustomMessageTarget(
throw new ArgumentNullException(nameof(razorReadyListener));
}

if (formattingOptionsProvider is null)
{
throw new ArgumentNullException(nameof(formattingOptionsProvider));
}

if (editorSettingsManager is null)
{
throw new ArgumentNullException(nameof(editorSettingsManager));
Expand All @@ -121,6 +129,7 @@ internal DefaultRazorLanguageServerCustomMessageTarget(
_requestInvoker = requestInvoker;
_uIContextManager = uIContextManager;
_razorReadyListener = razorReadyListener;
_formattingOptionsProvider = formattingOptionsProvider;
_editorSettingsManager = editorSettingsManager;
_documentSynchronizer = documentSynchronizer;
}
Expand Down Expand Up @@ -1035,5 +1044,11 @@ private void UpdateVirtualDocument(
cancellationToken).ConfigureAwait(false);
return response?.Response;
}

public override Task<FormattingOptions?> GetFormattingOptionsAsync(TextDocumentIdentifier document, CancellationToken cancellationToken)
{
var formattingOptions = _formattingOptionsProvider.GetOptions(document.Uri);
return Task.FromResult(formattingOptions);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,11 @@ private CompletionList PostProcessCSharpCompletionList(
TextExtent wordExtent,
CompletionList completionList)
{
var formattingOptions = _formattingOptionsProvider.GetOptions(documentSnapshot);
var formattingOptions = _formattingOptionsProvider.GetOptions(documentSnapshot.Uri);
if (formattingOptions is null)
{
return completionList;
}

if (IsSimpleImplicitExpression(request, documentSnapshot, wordExtent))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,12 @@ private async Task<CompletionItem> PostProcessCompletionItemAsync(

_logger.LogInformation("Start formatting text edit.");

var formattingOptions = _formattingOptionsProvider.GetOptions(documentSnapshot);
var formattingOptions = _formattingOptionsProvider.GetOptions(documentSnapshot.Uri);
if (formattingOptions is null)
{
return resolvedCompletionItem;
}

if (resolvedCompletionItem.TextEdit != null)
{
var containsSnippet = resolvedCompletionItem.InsertTextFormat == InsertTextFormat.Snippet;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -90,5 +91,8 @@ internal abstract class RazorLanguageServerCustomMessageTarget

[JsonRpcMethod(LanguageServerConstants.RazorCompletionResolveEndpointName, UseSingleObjectParameterDeserialization = true)]
public abstract Task<JToken?> ProvideResolvedCompletionItemAsync(DelegatedCompletionItemResolveParams completionResolveParams, CancellationToken cancellationToken);

[JsonRpcMethod(LanguageServerConstants.RazorGetFormattingOptionsEndpointName, UseSingleObjectParameterDeserialization = true)]
public abstract Task<FormattingOptions?> GetFormattingOptionsAsync(TextDocumentIdentifier document, CancellationToken cancellationToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ internal class TestDelegatedCompletionItemResolverServer : TestOmnisharpLanguage
private TestDelegatedCompletionItemResolverServer(CompletionResolveRequestResponseFactory requestHandler) : base(new Dictionary<string, Func<object, Task<object>>>()
{
[LanguageServerConstants.RazorCompletionResolveEndpointName] = requestHandler.OnCompletionResolveDelegationAsync,
[LanguageServerConstants.RazorGetFormattingOptionsEndpointName] = requestHandler.OnGetFormattingOptionsAsync,
})
{
_requestHandler = requestHandler;
Expand Down Expand Up @@ -337,6 +338,16 @@ private abstract class CompletionResolveRequestResponseFactory
public abstract DelegatedCompletionItemResolveParams DelegatedParams { get; }

public abstract Task<object> OnCompletionResolveDelegationAsync(object parameters);

public Task<object> OnGetFormattingOptionsAsync(object parameters)
{
var formattingOptions = new FormattingOptions()
{
InsertSpaces = true,
TabSize = 4,
};
return Task.FromResult<object>(formattingOptions);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,39 @@ public void GetOptions_UsesIndentationManagerInformation()
// Arrange
var documentUri = new Uri("C:/path/to/razorfile.razor");
var documentSnapshot = new TestLSPDocumentSnapshot(documentUri, version: 0);
var documentManager = new TestLSPDocumentManager(documentSnapshot);
var expectedInsertSpaces = true;
var expectedTabSize = 1337;
var unneededIndentSize = 123;
var indentationManagerService = new Mock<IIndentationManagerService>(MockBehavior.Strict);
indentationManagerService
.Setup(service => service.GetIndentation(documentSnapshot.Snapshot.TextBuffer, false, out expectedInsertSpaces, out expectedTabSize, out unneededIndentSize))
.Verifiable();
var provider = new DefaultFormattingOptionsProvider(indentationManagerService.Object);
var provider = new DefaultFormattingOptionsProvider(documentManager, indentationManagerService.Object);

// Act
var options = provider.GetOptions(documentSnapshot);
var options = provider.GetOptions(documentUri);

// Assert
indentationManagerService.VerifyAll();
Assert.Equal(expectedInsertSpaces, options.InsertSpaces);
Assert.Equal(expectedTabSize, options.TabSize);
}

private class TestLSPDocumentManager : LSPDocumentManager
{
private readonly LSPDocumentSnapshot _snapshot;

public TestLSPDocumentManager(LSPDocumentSnapshot snapshot)
{
_snapshot = snapshot;
}

public override bool TryGetDocument(Uri uri, out LSPDocumentSnapshot lspDocumentSnapshot)
{
lspDocumentSnapshot = _snapshot;
return true;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public async Task RazorRangeFormattingAsync_LanguageKindRazor_ReturnsEmpty()

var target = new DefaultRazorLanguageServerCustomMessageTarget(
documentManager, JoinableTaskContext, requestInvoker.Object,
uIContextManager.Object, disposable.Object, EditorSettingsManager, documentSynchronizer.Object);
uIContextManager.Object, disposable.Object, TestFormattingOptionsProvider.Default, EditorSettingsManager, documentSynchronizer.Object);

var request = new RazorDocumentRangeFormattingParams()
{
Expand Down Expand Up @@ -128,7 +128,7 @@ public async Task RazorRangeFormattingAsync_DocumentNotFound_ReturnsEmpty()

var target = new DefaultRazorLanguageServerCustomMessageTarget(
documentManager, JoinableTaskContext, requestInvoker.Object,
uIContextManager.Object, disposable.Object, EditorSettingsManager, documentSynchronizer.Object);
uIContextManager.Object, disposable.Object, TestFormattingOptionsProvider.Default, EditorSettingsManager, documentSynchronizer.Object);

var request = new RazorDocumentRangeFormattingParams()
{
Expand Down Expand Up @@ -183,7 +183,7 @@ public async Task RazorRangeFormattingAsync_ValidRequest_InvokesLanguageServer()

var target = new DefaultRazorLanguageServerCustomMessageTarget(
documentManager.Object, JoinableTaskContext, requestInvoker.Object,
uIContextManager.Object, disposable.Object, EditorSettingsManager, documentSynchronizer.Object);
uIContextManager.Object, disposable.Object, TestFormattingOptionsProvider.Default, EditorSettingsManager, documentSynchronizer.Object);

var request = new RazorDocumentRangeFormattingParams()
{
Expand Down Expand Up @@ -303,7 +303,7 @@ async IAsyncEnumerable<ReinvocationResponse<IReadOnlyList<VSInternalCodeAction>>

var target = new DefaultRazorLanguageServerCustomMessageTarget(
documentManager.Object, JoinableTaskContext, requestInvoker.Object,
uIContextManager.Object, disposable.Object, EditorSettingsManager, documentSynchronizer.Object);
uIContextManager.Object, disposable.Object, TestFormattingOptionsProvider.Default, EditorSettingsManager, documentSynchronizer.Object);
var request = new CodeActionParams()
{
TextDocument = new LanguageServer.Protocol.TextDocumentIdentifier()
Expand Down Expand Up @@ -364,7 +364,7 @@ async IAsyncEnumerable<ReinvocationResponse<VSInternalCodeAction>> GetExpectedRe

var target = new DefaultRazorLanguageServerCustomMessageTarget(
documentManager, JoinableTaskContext, requestInvoker.Object,
uIContextManager.Object, disposable.Object, EditorSettingsManager, documentSynchronizer.Object);
uIContextManager.Object, disposable.Object, TestFormattingOptionsProvider.Default, EditorSettingsManager, documentSynchronizer.Object);
var codeAction = new VSInternalCodeAction()
{
Title = "Something",
Expand Down Expand Up @@ -463,7 +463,7 @@ public async Task ProvideSemanticTokensAsync_ReturnsSemanticTokensAsync()

var target = new DefaultRazorLanguageServerCustomMessageTarget(
documentManager.Object, JoinableTaskContext, requestInvoker.Object,
uIContextManager.Object, disposable.Object, EditorSettingsManager, documentSynchronizer.Object);
uIContextManager.Object, disposable.Object, TestFormattingOptionsProvider.Default, EditorSettingsManager, documentSynchronizer.Object);
var request = new ProvideSemanticTokensRangeParams(
textDocument: new TextDocumentIdentifier()
{
Expand Down Expand Up @@ -518,7 +518,7 @@ public async Task RazorServerReadyAsync_ReportsReadyAsync()

var target = new DefaultRazorLanguageServerCustomMessageTarget(
documentManager.Object, JoinableTaskContext, requestInvoker.Object,
uIContextManager.Object, disposable.Object, EditorSettingsManager, documentSynchronizer.Object);
uIContextManager.Object, disposable.Object, TestFormattingOptionsProvider.Default, EditorSettingsManager, documentSynchronizer.Object);

// Act
await target.RazorServerReadyAsync(CancellationToken.None);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1307,23 +1307,5 @@ @using System
var result = await completionHandler.HandleRequestAsync(completionParams, new ClientCapabilities(), CancellationToken.None).ConfigureAwait(false);
return result;
}

private class TestFormattingOptionsProvider : FormattingOptionsProvider
{
public static readonly TestFormattingOptionsProvider Default = new(
new FormattingOptions()
{
InsertSpaces = true,
TabSize = 4,
});
private readonly FormattingOptions _options;

public TestFormattingOptionsProvider(FormattingOptions options)
{
_options = options;
}

public override FormattingOptions GetOptions(LSPDocumentSnapshot documentSnapshot) => _options;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
using CompletionOptions = Microsoft.VisualStudio.LanguageServer.Protocol.CompletionOptions;
using CompletionParams = Microsoft.VisualStudio.LanguageServer.Protocol.CompletionParams;
using CompletionTriggerKind = Microsoft.VisualStudio.LanguageServer.Protocol.CompletionTriggerKind;
using FormattingOptions = Microsoft.VisualStudio.LanguageServer.Protocol.FormattingOptions;
using Position = Microsoft.VisualStudio.LanguageServer.Protocol.Position;
using TextDocumentIdentifier = Microsoft.VisualStudio.LanguageServer.Protocol.TextDocumentIdentifier;

Expand Down Expand Up @@ -61,7 +60,7 @@ public CompletionResolveHandlerTest()

private TestLSPDocumentMappingProvider DocumentMappingProvider { get; } = new();

private TestFormattingOptionsProvider FormattingOptionsProvider { get; } = new();
private FormattingOptionsProvider FormattingOptionsProvider { get; } = TestFormattingOptionsProvider.Default;

private CompletionRequestContextCache CompletionRequestContextCache { get; } = new();

Expand Down Expand Up @@ -318,11 +317,6 @@ private static void AssociateRequest(LanguageServerKind requestKind, CompletionI
item.Data = data;
}

private class TestFormattingOptionsProvider : FormattingOptionsProvider
{
public override FormattingOptions GetOptions(LSPDocumentSnapshot documentSnapshot) => new FormattingOptions();
}

private record CompletionResolveResponse(VSInternalCompletionItem UnresolvedItem, VSInternalCompletionItem ResolvedItem, int TextEditRemapCount);
}
}
Loading

0 comments on commit 1c599e9

Please sign in to comment.