From e35a879c0722f9881fe60a49b1f3707d29e09c40 Mon Sep 17 00:00:00 2001 From: tmat Date: Wed, 23 Feb 2022 18:53:56 -0800 Subject: [PATCH] Razor --- .../Core/Formatting/FormatCommandHandler.cs | 5 ++- .../IndentationManagerExtensions.cs | 34 ++++++++++++++ ...t.CodeAnalysis.ExternalAccess.Razor.csproj | 6 ++- .../Razor/RazorAutoFormattingOptions.cs | 31 +++++++++++++ ...RazorCSharpFormattingInteractionService.cs | 45 ++++++++++++++----- .../Razor/RazorGlobalOptions.cs | 35 +++++++++++++++ .../Razor/RazorIndentationOptions.cs | 11 +++++ ...soft.CodeAnalysis.CSharp.Workspaces.csproj | 1 + 8 files changed, 155 insertions(+), 13 deletions(-) create mode 100644 src/EditorFeatures/Core/Formatting/IndentationManagerExtensions.cs create mode 100644 src/Tools/ExternalAccess/Razor/RazorAutoFormattingOptions.cs create mode 100644 src/Tools/ExternalAccess/Razor/RazorGlobalOptions.cs create mode 100644 src/Tools/ExternalAccess/Razor/RazorIndentationOptions.cs diff --git a/src/EditorFeatures/Core/Formatting/FormatCommandHandler.cs b/src/EditorFeatures/Core/Formatting/FormatCommandHandler.cs index 04ae614c486bc..40ce24fdba0f1 100644 --- a/src/EditorFeatures/Core/Formatting/FormatCommandHandler.cs +++ b/src/EditorFeatures/Core/Formatting/FormatCommandHandler.cs @@ -176,10 +176,11 @@ private void ExecuteReturnOrTypeCommandWorker(EditorCommandArgs args, Cancellati return; } - var options = _indentationManager.GetInferredIndentationOptionsAsync(document, _globalOptions, explicitFormat: false, cancellationToken).WaitAndGetResult(cancellationToken); + var formattingOptions = _indentationManager.GetInferredFormattingOptionsAsync(document, explicitFormat: false, cancellationToken).WaitAndGetResult(cancellationToken); + var indentationOptions = new IndentationOptions(formattingOptions, autoFormattingOptions); textChanges = service.GetFormattingChangesAsync( - document, typeCharArgs.TypedChar, caretPosition.Value, options, cancellationToken).WaitAndGetResult(cancellationToken); + document, typeCharArgs.TypedChar, caretPosition.Value, indentationOptions, cancellationToken).WaitAndGetResult(cancellationToken); } else { diff --git a/src/EditorFeatures/Core/Formatting/IndentationManagerExtensions.cs b/src/EditorFeatures/Core/Formatting/IndentationManagerExtensions.cs new file mode 100644 index 0000000000000..29030f0817f35 --- /dev/null +++ b/src/EditorFeatures/Core/Formatting/IndentationManagerExtensions.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.Text.Editor; + +namespace Microsoft.CodeAnalysis.Formatting +{ + internal static class IndentationManagerExtensions + { + public static async Task GetInferredFormattingOptionsAsync(this IIndentationManagerService indentationManager, Document document, bool explicitFormat, CancellationToken cancellationToken) + { + var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + var snapshot = text.FindCorrespondingEditorTextSnapshot(); + + var options = await SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false); + if (snapshot == null) + { + return options; + } + + indentationManager.GetIndentation(snapshot.TextBuffer, explicitFormat, out var convertTabsToSpaces, out var tabSize, out var indentSize); + + return options.With( + useTabs: !convertTabsToSpaces, + indentationSize: indentSize, + tabSize: tabSize); + } + } +} diff --git a/src/Tools/ExternalAccess/Razor/Microsoft.CodeAnalysis.ExternalAccess.Razor.csproj b/src/Tools/ExternalAccess/Razor/Microsoft.CodeAnalysis.ExternalAccess.Razor.csproj index a1434a15de1f6..e56e00c7d76cf 100644 --- a/src/Tools/ExternalAccess/Razor/Microsoft.CodeAnalysis.ExternalAccess.Razor.csproj +++ b/src/Tools/ExternalAccess/Razor/Microsoft.CodeAnalysis.ExternalAccess.Razor.csproj @@ -14,6 +14,10 @@ + + + + @@ -22,7 +26,7 @@ --> - + diff --git a/src/Tools/ExternalAccess/Razor/RazorAutoFormattingOptions.cs b/src/Tools/ExternalAccess/Razor/RazorAutoFormattingOptions.cs new file mode 100644 index 0000000000000..8862f8eefb4b7 --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/RazorAutoFormattingOptions.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.Formatting; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor +{ + internal readonly struct RazorAutoFormattingOptions + { + internal readonly AutoFormattingOptions UnderlyingObject; + + public RazorAutoFormattingOptions(AutoFormattingOptions underlyingObject) + => UnderlyingObject = underlyingObject; + + public RazorAutoFormattingOptions( + FormattingOptions.IndentStyle indentStyle, + bool formatOnReturn, + bool formatOnTyping, + bool formatOnSemicolon, + bool formatOnCloseBrace) + : this(new AutoFormattingOptions( + indentStyle, + FormatOnReturn: formatOnReturn, + FormatOnTyping: formatOnTyping, + FormatOnSemicolon: formatOnSemicolon, + FormatOnCloseBrace: formatOnCloseBrace)) + { + } + } +} diff --git a/src/Tools/ExternalAccess/Razor/RazorCSharpFormattingInteractionService.cs b/src/Tools/ExternalAccess/Razor/RazorCSharpFormattingInteractionService.cs index 17cbcfedf4f45..8b64658116ddd 100644 --- a/src/Tools/ExternalAccess/Razor/RazorCSharpFormattingInteractionService.cs +++ b/src/Tools/ExternalAccess/Razor/RazorCSharpFormattingInteractionService.cs @@ -3,10 +3,13 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.Formatting; using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -15,8 +18,6 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.Razor { - internal readonly record struct RazorFormattingOptions(bool UseTabs, int TabSize, int IndentationSize); - /// /// Enables Razor to utilize Roslyn's C# formatting service. /// @@ -57,21 +58,45 @@ public static async Task> GetFormattingChangesAsync( Document document, char typedChar, int position, - RazorFormattingOptions options, + RazorIndentationOptions indentationOptions, + RazorAutoFormattingOptions autoFormattingOptions, CancellationToken cancellationToken) { Contract.ThrowIfFalse(document.Project.Language is LanguageNames.CSharp); var formattingService = document.GetRequiredLanguageService(); - var formattingOptions = await SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false); - // TODO: get auto-formatting options from Razor - var globalOptions = document.Project.Solution.Workspace.Services.GetRequiredService(); + var formattingOptions = GetFormattingOptions(document.Project.Solution, indentationOptions); + var roslynIndentationOptions = new IndentationOptions(formattingOptions, autoFormattingOptions.UnderlyingObject); - var indentationOptions = new IndentationOptions( - formattingOptions.With(options.UseTabs, options.TabSize, options.IndentationSize), - globalOptions.GlobalOptions.GetAutoFormattingOptions(document.Project.Language)); + return await formattingService.GetFormattingChangesAsync(document, typedChar, position, roslynIndentationOptions, cancellationToken).ConfigureAwait(false); + } - return await formattingService.GetFormattingChangesAsync(document, typedChar, position, indentationOptions, cancellationToken).ConfigureAwait(false); + public static IList GetFormattedTextChanges( + Solution solution, + SyntaxNode root, + TextSpan span, + RazorIndentationOptions indentationOptions, + CancellationToken cancellationToken) + { + Contract.ThrowIfFalse(root.Language is LanguageNames.CSharp); + return Formatter.GetFormattedTextChanges(root, span, solution.Workspace.Services, GetFormattingOptions(solution, indentationOptions), cancellationToken); } + + public static SyntaxNode Format( + Solution solution, + SyntaxNode root, + RazorIndentationOptions indentationOptions, + CancellationToken cancellationToken) + { + Contract.ThrowIfFalse(root.Language is LanguageNames.CSharp); + return Formatter.Format(root, solution.Workspace.Services, GetFormattingOptions(solution, indentationOptions), cancellationToken: cancellationToken); + } + + // Razor does not support editor config but it should still use formatting options stored on Solution: + private static SyntaxFormattingOptions GetFormattingOptions(Solution solution, RazorIndentationOptions indentationOptions) + => SyntaxFormattingOptions.Create(solution.Options, solution.Workspace.Services, LanguageNames.CSharp).With( + useTabs: indentationOptions.UseTabs, + tabSize: indentationOptions.TabSize, + indentationSize: indentationOptions.IndentationSize); } } diff --git a/src/Tools/ExternalAccess/Razor/RazorGlobalOptions.cs b/src/Tools/ExternalAccess/Razor/RazorGlobalOptions.cs new file mode 100644 index 0000000000000..aff73c5aa9c12 --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/RazorGlobalOptions.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Composition; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Formatting; +using System.Linq; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor +{ + [Export(typeof(RazorGlobalOptions)), Shared] + internal sealed class RazorGlobalOptions + { + private readonly IGlobalOptionService _globalOptions; + + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public RazorGlobalOptions(IGlobalOptionService globalOptions) + { + _globalOptions = globalOptions; + } + + /// + /// For testing purposes. + /// + public static RazorGlobalOptions GetGlobalOptions(Workspace workspace) + => ((IMefHostExportProvider)workspace.Services.HostServices).GetExports().Single().Value; + + public RazorAutoFormattingOptions GetAutoFormattingOptions() + => new(_globalOptions.GetAutoFormattingOptions(LanguageNames.CSharp)); + } +} diff --git a/src/Tools/ExternalAccess/Razor/RazorIndentationOptions.cs b/src/Tools/ExternalAccess/Razor/RazorIndentationOptions.cs new file mode 100644 index 0000000000000..d8097d9737517 --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/RazorIndentationOptions.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor +{ + internal readonly record struct RazorIndentationOptions( + bool UseTabs, + int TabSize, + int IndentationSize); +} diff --git a/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj b/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj index fb0483f25a55e..edae5aac3bae6 100644 --- a/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj +++ b/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj @@ -47,6 +47,7 @@ +