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 0e41ead45f3ad..0b6d13693ae25 100644 --- a/src/Tools/ExternalAccess/Razor/RazorCSharpFormattingInteractionService.cs +++ b/src/Tools/ExternalAccess/Razor/RazorCSharpFormattingInteractionService.cs @@ -3,10 +3,12 @@ // 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.Formatting; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -15,16 +17,6 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.Razor { - internal readonly record struct RazorFormattingOptions( - bool UseTabs, - int TabSize, - int IndentationSize, - FormattingOptions.IndentStyle IndentStyle, - bool FormatOnReturn, - bool FormatOnTyping, - bool FormatOnSemicolon, - bool FormatOnCloseBrace); - /// /// Enables Razor to utilize Roslyn's C# formatting service. /// @@ -65,26 +57,59 @@ 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 roslynFormattingOptions = await SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false); + + var roslynIndentationOptions = new IndentationOptions( + roslynFormattingOptions.With( + useTabs: indentationOptions.UseTabs, + tabSize: indentationOptions.TabSize, + indentationSize: indentationOptions.IndentationSize), + autoFormattingOptions.UnderlyingObject); + + return await formattingService.GetFormattingChangesAsync(document, typedChar, position, roslynIndentationOptions, cancellationToken).ConfigureAwait(false); + } + + public static async Task> GetFormattedTextChangesAsync( + Document document, + TextSpan span, + RazorIndentationOptions indentationOptions, + CancellationToken cancellationToken) + { + Contract.ThrowIfFalse(document.Project.Language is LanguageNames.CSharp); + var formattingOptions = await SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false); + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var indentationOptions = new IndentationOptions( - formattingOptions.With( - useTabs: options.UseTabs, - tabSize: options.TabSize, - indentationSize: options.IndentationSize), - new AutoFormattingOptions( - IndentStyle: options.IndentStyle, - FormatOnReturn: options.FormatOnReturn, - FormatOnTyping: options.FormatOnTyping, - FormatOnSemicolon: options.FormatOnSemicolon, - FormatOnCloseBrace: options.FormatOnCloseBrace)); - - return await formattingService.GetFormattingChangesAsync(document, typedChar, position, indentationOptions, cancellationToken).ConfigureAwait(false); + formattingOptions = formattingOptions.With( + useTabs: indentationOptions.UseTabs, + tabSize: indentationOptions.TabSize, + indentationSize: indentationOptions.IndentationSize); + + return Formatter.GetFormattedTextChanges(root, span, document.Project.Solution.Workspace.Services, formattingOptions, cancellationToken); + } + + public static async Task FormatAsync( + Document document, + SyntaxNode root, + RazorIndentationOptions indentationOptions, + CancellationToken cancellationToken) + { + Contract.ThrowIfFalse(document.Project.Language is LanguageNames.CSharp); + + var formattingOptions = await SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false); + + formattingOptions = formattingOptions.With( + useTabs: indentationOptions.UseTabs, + tabSize: indentationOptions.TabSize, + indentationSize: indentationOptions.IndentationSize); + + return Formatter.Format(root, document.Project.Solution.Workspace.Services, formattingOptions, cancellationToken: cancellationToken); } } } diff --git a/src/Tools/ExternalAccess/Razor/RazorGlobalOptions.cs b/src/Tools/ExternalAccess/Razor/RazorGlobalOptions.cs new file mode 100644 index 0000000000000..65ba03ff33801 --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/RazorGlobalOptions.cs @@ -0,0 +1,28 @@ +// 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; + +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; + } + + 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); +}