Skip to content

Commit

Permalink
Explicitly pass formatting options to Roslyn APIs (via External Access)
Browse files Browse the repository at this point in the history
  • Loading branch information
tmat committed Mar 10, 2022
1 parent 89fdcec commit ce4cbe2
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.ExternalAccess.Razor;
using Microsoft.CodeAnalysis.Razor.Workspaces.Extensions;
using Microsoft.CodeAnalysis.Text;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
Expand Down Expand Up @@ -134,15 +135,11 @@ private static async Task<TextEdit[]> FormatOnServerAsync(
Range projectedRange,
CancellationToken cancellationToken)
{
var csharpDocument = context.CSharpWorkspaceDocument;
var csharpSourceText = context.CodeDocument.GetCSharpSourceText();
var spanToFormat = projectedRange.AsTextSpan(csharpSourceText);
var root = await context.CSharpWorkspaceDocument.GetSyntaxRootAsync(cancellationToken);
Assumes.NotNull(root);

var workspace = context.CSharpWorkspace;

// Formatting options will already be set in the workspace.
var changes = CodeAnalysis.Formatting.Formatter.GetFormattedTextChanges(root, spanToFormat, workspace, cancellationToken: cancellationToken);
var changes = await RazorCSharpFormattingInteractionService.GetFormattedTextChangesAsync(csharpDocument, spanToFormat, context.Options.GetIndentationOptions(), cancellationToken).ConfigureAwait(false);

var edits = changes.Select(c => c.AsTextEdit(csharpSourceText)).ToArray();
return edits;
Expand All @@ -158,7 +155,7 @@ private static async Task<Dictionary<int, int>> GetCSharpIndentationCoreAsync(Fo

// At this point, we have added all the necessary markers and attached annotations.
// Let's invoke the C# formatter and hope for the best.
var formattedRoot = CodeAnalysis.Formatting.Formatter.Format(root, context.CSharpWorkspace, cancellationToken: cancellationToken);
var formattedRoot = await RazorCSharpFormattingInteractionService.FormatAsync(context.CSharpWorkspaceDocument, root, context.Options.GetIndentationOptions(), cancellationToken: cancellationToken);
var formattedText = formattedRoot.GetText();

var desiredIndentationMap = new Dictionary<int, int>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.

using Microsoft.CodeAnalysis.ExternalAccess.Razor;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;

namespace Microsoft.AspNetCore.Razor.LanguageServer.Formatting
{
internal static class FormattingOptionsExtensions
{
public static RazorIndentationOptions GetIndentationOptions(this FormattingOptions options)
=> new(
UseTabs: !options.InsertSpaces,
TabSize: options.TabSize,
IndentationSize: options.TabSize);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
using Microsoft.AspNetCore.Razor.LanguageServer.ProjectSystem;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.ExternalAccess.Razor;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.Workspaces.Extensions;
using Microsoft.CodeAnalysis.Text;
Expand All @@ -32,6 +31,7 @@ internal class RazorFormattingEndpoint : IDocumentFormattingHandler, IDocumentRa
private readonly AdhocWorkspaceFactory _adhocWorkspaceFactory;
private readonly IOptionsMonitor<RazorLSPOptions> _optionsMonitor;
private readonly ILogger _logger;
private readonly RazorGlobalOptions _globalOptions;

private static readonly IReadOnlyList<string> s_csharpTriggerCharacters = new[] { "}", ";" };
private static readonly IReadOnlyList<string> s_htmlTriggerCharacters = Array.Empty<string>();
Expand All @@ -44,7 +44,8 @@ public RazorFormattingEndpoint(
RazorDocumentMappingService razorDocumentMappingService,
AdhocWorkspaceFactory adhocWorkspaceFactory,
IOptionsMonitor<RazorLSPOptions> optionsMonitor,
ILoggerFactory loggerFactory)
ILoggerFactory loggerFactory,
RazorGlobalOptions globalOptions)
{
if (projectSnapshotManagerDispatcher is null)
{
Expand Down Expand Up @@ -88,6 +89,7 @@ public RazorFormattingEndpoint(
_adhocWorkspaceFactory = adhocWorkspaceFactory;
_optionsMonitor = optionsMonitor;
_logger = loggerFactory.CreateLogger<RazorFormattingEndpoint>();
_globalOptions = globalOptions;
}

public DocumentFormattingRegistrationOptions GetRegistrationOptions(DocumentFormattingCapability capability, ClientCapabilities clientCapabilities)
Expand Down Expand Up @@ -250,11 +252,16 @@ public async Task<TextEditContainer> Handle(DocumentRangeFormattingParams reques

using var formattingContext = FormattingContext.Create(
request.TextDocument.Uri, documentSnapshot, codeDocument, request.Options, _adhocWorkspaceFactory, isFormatOnType: true, automaticallyAddUsings: false);
var documentOptions = await GetDocumentOptionsAsync(request, formattingContext.CSharpWorkspaceDocument).ConfigureAwait(false);

// Ask C# for formatting changes.
var formattingChanges = await RazorCSharpFormattingInteractionService.GetFormattingChangesAsync(
formattingContext.CSharpWorkspaceDocument, typedChar: request.Character[0], projectedIndex, documentOptions, cancellationToken).ConfigureAwait(false);
formattingContext.CSharpWorkspaceDocument,
typedChar: request.Character[0],
projectedIndex,
request.Options.GetIndentationOptions(),
_globalOptions.GetAutoFormattingOptions(),
cancellationToken).ConfigureAwait(false);

if (formattingChanges.IsEmpty)
{
_logger.LogInformation("Received no results.");
Expand Down Expand Up @@ -292,15 +299,5 @@ private static bool IsApplicableTriggerCharacter(string triggerCharacter, RazorL
// Unknown trigger character.
return false;
}

// Internal for testing
internal static async Task<DocumentOptionSet> GetDocumentOptionsAsync(DocumentOnTypeFormattingParams request, Document document)
{
var documentOptions = await document.GetOptionsAsync().ConfigureAwait(false);
documentOptions = documentOptions.WithChangedOption(CodeAnalysis.Formatting.FormattingOptions.TabSize, request.Options.TabSize)
.WithChangedOption(CodeAnalysis.Formatting.FormattingOptions.IndentationSize, request.Options.TabSize)
.WithChangedOption(CodeAnalysis.Formatting.FormattingOptions.UseTabs, !request.Options.InsertSpaces);
return documentOptions;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
using Microsoft.AspNetCore.Razor.LanguageServer.Extensions;
using Microsoft.AspNetCore.Razor.LanguageServer.Test;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.ExternalAccess.Razor;
using Microsoft.CodeAnalysis.Razor.Workspaces.Extensions;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Text;
Expand Down Expand Up @@ -131,29 +131,24 @@ private RazorDocumentFormattingResponse Format(DocumentFormattingParams @params)
return response;
}

private RazorDocumentFormattingResponse Format(RazorDocumentRangeFormattingParams @params)
private async Task<RazorDocumentFormattingResponse> FormatAsync(RazorDocumentRangeFormattingParams @params)
{
if (@params.Kind == RazorLanguageKind.Razor)
{
throw new InvalidOperationException("We shouldn't be asked to format Razor language kind.");
}

var options = @params.Options;
var response = new RazorDocumentFormattingResponse();

if (@params.Kind == RazorLanguageKind.CSharp)
{
var codeDocument = _documents[@params.HostDocumentFilePath];
var csharpSourceText = codeDocument.GetCSharpSourceText();
var csharpDocument = GetCSharpDocument(codeDocument, @params.Options);
if (!csharpDocument.TryGetSyntaxRoot(out var root))
{
throw new InvalidOperationException("Couldn't get syntax root.");
}

var csharpDocument = GetCSharpDocument(codeDocument);
var spanToFormat = @params.ProjectedRange.AsTextSpan(csharpSourceText);

var changes = Formatter.GetFormattedTextChanges(root, spanToFormat, csharpDocument.Project.Solution.Workspace);
var options = @params.Options.GetIndentationOptions();
var changes = await RazorCSharpFormattingInteractionService.GetFormattedTextChangesAsync(csharpDocument, spanToFormat, options, CancellationToken.None);

response.Edits = changes.Select(c => c.AsTextEdit(csharpSourceText)).ToArray();
}
Expand Down Expand Up @@ -192,15 +187,9 @@ public TextEdit AsTextEdit(SourceText sourceText)
}
}

private static Document GetCSharpDocument(RazorCodeDocument codeDocument, FormattingOptions options)
private static Document GetCSharpDocument(RazorCodeDocument codeDocument)
{
var adhocWorkspace = new AdhocWorkspace();
var csharpOptions = adhocWorkspace.Options
.WithChangedOption(CodeAnalysis.Formatting.FormattingOptions.TabSize, LanguageNames.CSharp, (int)options.TabSize)
.WithChangedOption(CodeAnalysis.Formatting.FormattingOptions.IndentationSize, LanguageNames.CSharp, (int)options.TabSize)
.WithChangedOption(CodeAnalysis.Formatting.FormattingOptions.UseTabs, LanguageNames.CSharp, !options.InsertSpaces);
adhocWorkspace.TryApplyChanges(adhocWorkspace.CurrentSolution.WithOptions(csharpOptions));

var project = adhocWorkspace.AddProject("TestProject", LanguageNames.CSharp);
var csharpSourceText = codeDocument.GetCSharpSourceText();
var csharpDocument = adhocWorkspace.AddDocument(project.Id, "TestDocument", csharpSourceText);
Expand Down Expand Up @@ -237,21 +226,21 @@ public void SendNotification(IRequest request)
throw new NotImplementedException();
}

public override Task<IResponseRouterReturns> SendRequestAsync<T>(string method, T @params)
public override async Task<IResponseRouterReturns> SendRequestAsync<T>(string method, T @params)
{
if (@params is RazorDocumentRangeFormattingParams rangeFormattingParams &&
string.Equals(method, "razor/rangeFormatting", StringComparison.Ordinal))
{
var response = Format(rangeFormattingParams);
var response = await FormatAsync(rangeFormattingParams);

return Task.FromResult(Convert(response));
return Convert(response);
}
else if (@params is DocumentFormattingParams formattingParams &&
string.Equals(method, "textDocument/formatting", StringComparison.Ordinal))
{
var response = Format(formattingParams);
var response = await FormatAsync(formattingParams);

return Task.FromResult(Convert(response));
return Convert(response);
}

throw new NotImplementedException();
Expand Down

0 comments on commit ce4cbe2

Please sign in to comment.