From d742fd0e0a059a5fee8113a6cc4def4f6ca6ce6a Mon Sep 17 00:00:00 2001 From: tmat Date: Tue, 21 Dec 2021 17:32:36 -0800 Subject: [PATCH 01/13] Code cleanup APIs: Propagate options and remove dependency on Workspace --- .../Analyzers/Formatting/FormatterHelper.cs | 1 - .../VisualBasic/LineCommit/CommitFormatter.vb | 38 +----- .../CaseCorrectionServiceTests.vb | 3 +- ...oadBaseCodeFixProvider.AddKeywordAction.vb | 9 +- .../CSharpCaseCorrectionService.cs | 1 - .../AbstractCaseCorrectionService.cs | 14 ++- .../Portable/CaseCorrection/CaseCorrector.cs | 6 +- .../CaseCorrection/ICaseCorrectionService.cs | 3 +- .../CodeCleanup/AbstractCodeCleanerService.cs | 28 +++-- .../Core/Portable/CodeCleanup/CodeCleaner.cs | 17 +-- .../CodeCleanup/ICodeCleanerService.cs | 5 +- .../Providers/FormatCodeCleanupProvider.cs | 49 +++----- .../Providers/ICodeCleanupProvider.cs | 6 +- .../Providers/SimpleCodeCleanupProvider.cs | 72 ------------ .../SimplificationCodeCleanupProvider.cs | 6 +- .../Core/Portable/Formatting/Formatter.cs | 1 - .../CoreTest/CodeCleanup/CodeCleanupTests.cs | 109 +++++++++++------- .../CodeCleanup/MockCodeCleanupProvider.cs | 34 ++++++ .../AbstractSyntaxFormattingService.cs | 29 +---- .../Formatting/ISyntaxFormattingService.cs | 19 ++- .../VisualBasicCaseCorrectionService.vb | 1 - .../AbstractTokensCodeCleanupProvider.vb | 12 +- .../AddMissingTokensCodeCleanupProvider.vb | 2 +- .../CaseCorrectionCodeCleanupProvider.vb | 8 +- .../FixIncorrectTokensCodeCleanupProvider.vb | 2 +- ...ModifiersOrOperatorsCodeCleanupProvider.vb | 8 +- .../ReduceTokensCodeCleanupProvider.vb | 2 +- ...saryLineContinuationCodeCleanupProvider.vb | 8 +- 28 files changed, 226 insertions(+), 267 deletions(-) delete mode 100644 src/Workspaces/Core/Portable/CodeCleanup/Providers/SimpleCodeCleanupProvider.cs create mode 100644 src/Workspaces/CoreTest/CodeCleanup/MockCodeCleanupProvider.cs diff --git a/src/CodeStyle/Core/Analyzers/Formatting/FormatterHelper.cs b/src/CodeStyle/Core/Analyzers/Formatting/FormatterHelper.cs index 7485698d79ac6..b9537b346afeb 100644 --- a/src/CodeStyle/Core/Analyzers/Formatting/FormatterHelper.cs +++ b/src/CodeStyle/Core/Analyzers/Formatting/FormatterHelper.cs @@ -125,7 +125,6 @@ internal static IFormattingResult GetFormattingResult(SyntaxNode node, ISyntaxFo } options ??= DictionaryAnalyzerConfigOptions.Empty; - rules ??= GetDefaultFormattingRules(syntaxFormattingService); spans ??= SpecializedCollections.SingletonEnumerable(node.FullSpan); return syntaxFormattingService.Format(node, spans, shouldUseFormattingSpanCollapse: false, options, rules, cancellationToken); } diff --git a/src/EditorFeatures/VisualBasic/LineCommit/CommitFormatter.vb b/src/EditorFeatures/VisualBasic/LineCommit/CommitFormatter.vb index 8edbfac6ee5b1..00ea25ac23635 100644 --- a/src/EditorFeatures/VisualBasic/LineCommit/CommitFormatter.vb +++ b/src/EditorFeatures/VisualBasic/LineCommit/CommitFormatter.vb @@ -94,7 +94,8 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.LineCommit Dim root = document.GetSyntaxRootSynchronously(cancellationToken) Dim newRoot = CodeCleaner.CleanupAsync(root, textSpanToFormat, - document.Project.Solution.Workspace, + documentOptions, + document.Project.Solution.Workspace.Services, codeCleanups, cancellationToken).WaitAndGetResult(cancellationToken) If root Is newRoot Then @@ -142,40 +143,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.LineCommit ' based on changes made to dirty spans, get right formatting rules to apply Dim rules = GetFormattingRules(document, documentOptions, spanToFormat, oldDirtySpan, oldTree, newDirtySpan, newTree, cancellationToken) - Return New SimpleCodeCleanupProvider(PredefinedCodeCleanupProviderNames.Format, - Function(doc, spans, c) FormatAsync(doc, spans, documentOptions, rules, c), - Function(r, spans, w, c) Format(r, spans, w, documentOptions, rules, c)) - End Function - - Private Shared Async Function FormatAsync(document As Document, spans As ImmutableArray(Of TextSpan), options As OptionSet, rules As IEnumerable(Of AbstractFormattingRule), cancellationToken As CancellationToken) As Task(Of Document) - ' if old text already exist, use fast path for formatting - Dim oldText As SourceText = Nothing - - If document.TryGetText(oldText) Then - Dim root = Await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(False) - Dim newText = oldText.WithChanges(Formatter.GetFormattedTextChanges(root, spans, document.Project.Solution.Workspace, options, rules, cancellationToken)) - Return document.WithText(newText) - End If - - Return Await Formatter.FormatAsync(document, spans, options, rules, cancellationToken).ConfigureAwait(False) - End Function - - Private Shared Function Format(root As SyntaxNode, spans As ImmutableArray(Of TextSpan), workspace As Workspace, options As OptionSet, rules As IEnumerable(Of AbstractFormattingRule), cancellationToken As CancellationToken) As SyntaxNode - ' if old text already exist, use fast path for formatting - Dim oldText As SourceText = Nothing - - If root.SyntaxTree IsNot Nothing AndAlso root.SyntaxTree.TryGetText(oldText) Then - Dim changes = Formatter.GetFormattedTextChanges(root, spans, workspace, options, rules, cancellationToken) - - ' no change - If changes.Count = 0 Then - Return root - End If - - Return root.SyntaxTree.WithChangedText(oldText.WithChanges(changes)).GetRoot(cancellationToken) - End If - - Return Formatter.Format(root, spans, workspace, options, rules, cancellationToken) + Return New FormatCodeCleanupProvider() End Function Private Shared Function GetFormattingRules( diff --git a/src/EditorFeatures/VisualBasicTest/CaseCorrecting/CaseCorrectionServiceTests.vb b/src/EditorFeatures/VisualBasicTest/CaseCorrecting/CaseCorrectionServiceTests.vb index 3435ad3a48814..6c6270829de3a 100644 --- a/src/EditorFeatures/VisualBasicTest/CaseCorrecting/CaseCorrectionServiceTests.vb +++ b/src/EditorFeatures/VisualBasicTest/CaseCorrecting/CaseCorrectionServiceTests.vb @@ -33,10 +33,11 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CaseCorrecting Dim buffer = hostDocument.GetTextBuffer() Dim document = workspace.CurrentSolution.GetDocument(hostDocument.Id) Dim span = (Await document.GetSyntaxRootAsync()).FullSpan + Dim options = Await document.GetOptionsAsync() Dim service = document.GetLanguageService(Of ICodeCleanerService) Dim newDocument = Await service.CleanupAsync( - document, ImmutableArray.Create(span), + document, ImmutableArray.Create(span), options, ImmutableArray.Create(Of ICodeCleanupProvider)(New CaseCorrectionCodeCleanupProvider()), CancellationToken.None) diff --git a/src/Features/VisualBasic/Portable/CodeFixes/OverloadBase/OverloadBaseCodeFixProvider.AddKeywordAction.vb b/src/Features/VisualBasic/Portable/CodeFixes/OverloadBase/OverloadBaseCodeFixProvider.AddKeywordAction.vb index f8dfca13b6c34..8f18177011833 100644 --- a/src/Features/VisualBasic/Portable/CodeFixes/OverloadBase/OverloadBaseCodeFixProvider.AddKeywordAction.vb +++ b/src/Features/VisualBasic/Portable/CodeFixes/OverloadBase/OverloadBaseCodeFixProvider.AddKeywordAction.vb @@ -8,6 +8,7 @@ Imports Microsoft.CodeAnalysis.CodeActions Imports Microsoft.CodeAnalysis.CodeCleanup Imports Microsoft.CodeAnalysis.Editing Imports Microsoft.CodeAnalysis.Formatting +Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.OverloadBase @@ -41,14 +42,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.OverloadBase Protected Overrides Async Function GetChangedDocumentAsync(cancellationToken As CancellationToken) As Task(Of Document) Dim root = Await _document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(False) + Dim options = Await _document.GetOptionsAsync(cancellationToken).ConfigureAwait(False) - Dim newNode = Await GetNewNodeAsync(_document, _node, cancellationToken).ConfigureAwait(False) + Dim newNode = Await GetNewNodeAsync(_document, _node, Options, cancellationToken).ConfigureAwait(False) Dim newRoot = root.ReplaceNode(_node, newNode) Return _document.WithSyntaxRoot(newRoot) End Function - Private Async Function GetNewNodeAsync(document As Document, node As SyntaxNode, cancellationToken As CancellationToken) As Task(Of SyntaxNode) + Private Async Function GetNewNodeAsync(document As Document, node As SyntaxNode, options As OptionSet, cancellationToken As CancellationToken) As Task(Of SyntaxNode) Dim newNode As SyntaxNode = Nothing Dim trivia As SyntaxTriviaList = node.GetLeadingTrivia() node = node.WithoutLeadingTrivia() @@ -70,7 +72,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.OverloadBase Dim cleanupService = document.GetLanguageService(Of ICodeCleanerService) If cleanupService IsNot Nothing AndAlso newNode IsNot Nothing Then - newNode = Await cleanupService.CleanupAsync(newNode, ImmutableArray.Create(newNode.Span), document.Project.Solution.Workspace, cleanupService.GetDefaultProviders(), cancellationToken).ConfigureAwait(False) + Dim services = document.Project.Solution.Workspace.Services + newNode = Await cleanupService.CleanupAsync(newNode, ImmutableArray.Create(newNode.Span), options, services, cleanupService.GetDefaultProviders(), cancellationToken).ConfigureAwait(False) End If Return newNode.WithAdditionalAnnotations(Formatter.Annotation) diff --git a/src/Workspaces/CSharp/Portable/CaseCorrection/CSharpCaseCorrectionService.cs b/src/Workspaces/CSharp/Portable/CaseCorrection/CSharpCaseCorrectionService.cs index 41b41897aaf55..5c905e7d174f0 100644 --- a/src/Workspaces/CSharp/Portable/CaseCorrection/CSharpCaseCorrectionService.cs +++ b/src/Workspaces/CSharp/Portable/CaseCorrection/CSharpCaseCorrectionService.cs @@ -26,7 +26,6 @@ protected override void AddReplacements( SemanticModel? semanticModel, SyntaxNode root, ImmutableArray spans, - Workspace workspace, ConcurrentDictionary replacements, CancellationToken cancellationToken) { diff --git a/src/Workspaces/Core/Portable/CaseCorrection/AbstractCaseCorrectionService.cs b/src/Workspaces/Core/Portable/CaseCorrection/AbstractCaseCorrectionService.cs index 5a138d4d651c1..afca79fe933dc 100644 --- a/src/Workspaces/Core/Portable/CaseCorrection/AbstractCaseCorrectionService.cs +++ b/src/Workspaces/Core/Portable/CaseCorrection/AbstractCaseCorrectionService.cs @@ -8,7 +8,9 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; @@ -17,7 +19,7 @@ namespace Microsoft.CodeAnalysis.CaseCorrection { internal abstract partial class AbstractCaseCorrectionService : ICaseCorrectionService { - protected abstract void AddReplacements(SemanticModel? semanticModel, SyntaxNode root, ImmutableArray spans, Workspace workspace, ConcurrentDictionary replacements, CancellationToken cancellationToken); + protected abstract void AddReplacements(SemanticModel? semanticModel, SyntaxNode root, ImmutableArray spans, ConcurrentDictionary replacements, CancellationToken cancellationToken); public async Task CaseCorrectAsync(Document document, ImmutableArray spans, CancellationToken cancellationToken) { @@ -34,14 +36,14 @@ public async Task CaseCorrectAsync(Document document, ImmutableArray spans, Workspace workspace, CancellationToken cancellationToken) - => CaseCorrect(semanticModel: null, root, spans, workspace, cancellationToken); + public SyntaxNode CaseCorrect(SyntaxNode root, ImmutableArray spans, CancellationToken cancellationToken) + => CaseCorrect(semanticModel: null, root, spans, cancellationToken); - private SyntaxNode CaseCorrect(SemanticModel? semanticModel, SyntaxNode root, ImmutableArray spans, Workspace workspace, CancellationToken cancellationToken) + private SyntaxNode CaseCorrect(SemanticModel? semanticModel, SyntaxNode root, ImmutableArray spans, CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.CaseCorrection_CaseCorrect, cancellationToken)) { @@ -50,7 +52,7 @@ private SyntaxNode CaseCorrect(SemanticModel? semanticModel, SyntaxNode root, Im using (Logger.LogBlock(FunctionId.CaseCorrection_AddReplacements, cancellationToken)) { - AddReplacements(semanticModel, root, normalizedSpanCollection.ToImmutableArray(), workspace, replacements, cancellationToken); + AddReplacements(semanticModel, root, normalizedSpanCollection.ToImmutableArray(), replacements, cancellationToken); } using (Logger.LogBlock(FunctionId.CaseCorrection_ReplaceTokens, cancellationToken)) diff --git a/src/Workspaces/Core/Portable/CaseCorrection/CaseCorrector.cs b/src/Workspaces/Core/Portable/CaseCorrection/CaseCorrector.cs index 6f12d35299271..37eff4be9241b 100644 --- a/src/Workspaces/Core/Portable/CaseCorrection/CaseCorrector.cs +++ b/src/Workspaces/Core/Portable/CaseCorrection/CaseCorrector.cs @@ -7,6 +7,8 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CaseCorrection @@ -64,7 +66,7 @@ public static Task CaseCorrectAsync(Document document, ImmutableArray< /// /// Case correct only things that don't require semantic information /// - internal static SyntaxNode CaseCorrect(SyntaxNode root, ImmutableArray spans, Workspace workspace, CancellationToken cancellationToken = default) - => workspace.Services.GetLanguageServices(root.Language).GetRequiredService().CaseCorrect(root, spans, workspace, cancellationToken); + internal static SyntaxNode CaseCorrect(SyntaxNode root, ImmutableArray spans, HostWorkspaceServices services, CancellationToken cancellationToken = default) + => services.GetLanguageServices(root.Language).GetRequiredService().CaseCorrect(root, spans, cancellationToken); } } diff --git a/src/Workspaces/Core/Portable/CaseCorrection/ICaseCorrectionService.cs b/src/Workspaces/Core/Portable/CaseCorrection/ICaseCorrectionService.cs index d535edbc8d49f..5fcb67f21aa08 100644 --- a/src/Workspaces/Core/Portable/CaseCorrection/ICaseCorrectionService.cs +++ b/src/Workspaces/Core/Portable/CaseCorrection/ICaseCorrectionService.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CaseCorrection @@ -20,6 +21,6 @@ internal interface ICaseCorrectionService : ILanguageService /// /// Case corrects only things that don't require semantic information /// - SyntaxNode CaseCorrect(SyntaxNode root, ImmutableArray spans, Workspace workspace, CancellationToken cancellationToken); + SyntaxNode CaseCorrect(SyntaxNode root, ImmutableArray spans, CancellationToken cancellationToken); } } diff --git a/src/Workspaces/Core/Portable/CodeCleanup/AbstractCodeCleanerService.cs b/src/Workspaces/Core/Portable/CodeCleanup/AbstractCodeCleanerService.cs index c5e41db5b9c28..2f827d24b1cd1 100644 --- a/src/Workspaces/Core/Portable/CodeCleanup/AbstractCodeCleanerService.cs +++ b/src/Workspaces/Core/Portable/CodeCleanup/AbstractCodeCleanerService.cs @@ -12,6 +12,8 @@ using Microsoft.CodeAnalysis.CodeCleanup.Providers; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.LanguageServices; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared; using Microsoft.CodeAnalysis.Shared.Collections; @@ -26,7 +28,7 @@ internal abstract class AbstractCodeCleanerService : ICodeCleanerService public abstract ImmutableArray GetDefaultProviders(); protected abstract ImmutableArray GetSpansToAvoid(SyntaxNode root); - public async Task CleanupAsync(Document document, ImmutableArray spans, ImmutableArray providers, CancellationToken cancellationToken) + public async Task CleanupAsync(Document document, ImmutableArray spans, OptionSet options, ImmutableArray providers, CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.CodeCleanup_CleanupAsync, cancellationToken)) { @@ -45,7 +47,7 @@ public async Task CleanupAsync(Document document, ImmutableArray ImmutableArray.Create(r.FullSpan), codeCleaners, cancellationToken).ConfigureAwait(false); + return await IterateAllCodeCleanupProvidersAsync(document, document, options, r => ImmutableArray.Create(r.FullSpan), codeCleaners, cancellationToken).ConfigureAwait(false); } // We need to track spans between cleaners. Annotate the tree with the provided spans. @@ -55,7 +57,7 @@ public async Task CleanupAsync(Document document, ImmutableArray ImmutableArray.Create(n.FullSpan), codeCleaners, cancellationToken).ConfigureAwait(false); + return await IterateAllCodeCleanupProvidersAsync(document, document, options, n => ImmutableArray.Create(n.FullSpan), codeCleaners, cancellationToken).ConfigureAwait(false); } // Replace the initial node and document with the annotated node. @@ -64,13 +66,13 @@ public async Task CleanupAsync(Document document, ImmutableArray GetTextSpansFromAnnotation(r, annotations, cancellationToken), codeCleaners, cancellationToken).ConfigureAwait(false); } } - public async Task CleanupAsync(SyntaxNode root, ImmutableArray spans, Workspace workspace, ImmutableArray providers, CancellationToken cancellationToken) + public async Task CleanupAsync(SyntaxNode root, ImmutableArray spans, OptionSet options, HostWorkspaceServices services, ImmutableArray providers, CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.CodeCleanup_Cleanup, cancellationToken)) { @@ -87,7 +89,7 @@ public async Task CleanupAsync(SyntaxNode root, ImmutableArray ImmutableArray.Create(r.FullSpan), workspace, codeCleaners, cancellationToken).ConfigureAwait(false); + return await IterateAllCodeCleanupProvidersAsync(root, root, options, r => ImmutableArray.Create(r.FullSpan), services, codeCleaners, cancellationToken).ConfigureAwait(false); } // We need to track spans between cleaners. Annotate the tree with the provided spans. @@ -97,7 +99,7 @@ public async Task CleanupAsync(SyntaxNode root, ImmutableArray ImmutableArray.Create(n.FullSpan), workspace, codeCleaners, cancellationToken).ConfigureAwait(false); + return await IterateAllCodeCleanupProvidersAsync(root, root, options, n => ImmutableArray.Create(n.FullSpan), services, codeCleaners, cancellationToken).ConfigureAwait(false); } // Replace the initial node and document with the annotated node. @@ -105,9 +107,9 @@ public async Task CleanupAsync(SyntaxNode root, ImmutableArray GetTextSpansFromAnnotation(r, annotations, cancellationToken), - workspace, codeCleaners, cancellationToken).ConfigureAwait(false); + services, codeCleaners, cancellationToken).ConfigureAwait(false); } } @@ -454,6 +456,7 @@ private static bool CleanupWholeNode(TextSpan nodeSpan, ImmutableArray private async Task IterateAllCodeCleanupProvidersAsync( Document originalDocument, Document annotatedDocument, + OptionSet options, Func> spanGetter, ImmutableArray codeCleaners, CancellationToken cancellationToken) @@ -492,7 +495,7 @@ private async Task IterateAllCodeCleanupProvidersAsync( using (Logger.LogBlock(FunctionId.CodeCleanup_IterateOneCodeCleanup, GetCodeCleanerTypeName, codeCleaner, cancellationToken)) { - currentDocument = await codeCleaner.CleanupAsync(currentDocument, spans, cancellationToken).ConfigureAwait(false); + currentDocument = await codeCleaner.CleanupAsync(currentDocument, spans, options, cancellationToken).ConfigureAwait(false); } #if DEBUG @@ -534,8 +537,9 @@ private ImmutableArray GetSpans( private async Task IterateAllCodeCleanupProvidersAsync( SyntaxNode originalRoot, SyntaxNode annotatedRoot, + OptionSet options, Func> spanGetter, - Workspace workspace, + HostWorkspaceServices services, ImmutableArray codeCleaners, CancellationToken cancellationToken) { @@ -568,7 +572,7 @@ private async Task IterateAllCodeCleanupProvidersAsync( using (Logger.LogBlock(FunctionId.CodeCleanup_IterateOneCodeCleanup, GetCodeCleanerTypeName, codeCleaner, cancellationToken)) { - currentRoot = await codeCleaner.CleanupAsync(currentRoot, spans, workspace, cancellationToken).ConfigureAwait(false); + currentRoot = await codeCleaner.CleanupAsync(currentRoot, spans, options, services, cancellationToken).ConfigureAwait(false); } } diff --git a/src/Workspaces/Core/Portable/CodeCleanup/CodeCleaner.cs b/src/Workspaces/Core/Portable/CodeCleanup/CodeCleaner.cs index ba2c0ae954d5e..1ae8fd14ee9bf 100644 --- a/src/Workspaces/Core/Portable/CodeCleanup/CodeCleaner.cs +++ b/src/Workspaces/Core/Portable/CodeCleanup/CodeCleaner.cs @@ -9,6 +9,8 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeCleanup.Providers; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CodeCleanup @@ -72,27 +74,28 @@ public static Task CleanupAsync(Document document, TextSpan span, Immu /// Clean up the provided spans in the document. /// Optionally you can provide your own options and code cleaners. Otherwise, the default will be used. /// - public static Task CleanupAsync(Document document, ImmutableArray spans, ImmutableArray providers = default, CancellationToken cancellationToken = default) + public static async Task CleanupAsync(Document document, ImmutableArray spans, ImmutableArray providers = default, CancellationToken cancellationToken = default) { var cleanupService = document.GetRequiredLanguageService(); - return cleanupService.CleanupAsync(document, spans, providers, cancellationToken); + var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); + return await cleanupService.CleanupAsync(document, spans, options, providers, cancellationToken).ConfigureAwait(false); } /// /// Clean up the provided span in the node. /// This will only cleanup stuff that doesn't require semantic information. /// - public static Task CleanupAsync(SyntaxNode root, TextSpan span, Workspace workspace, ImmutableArray providers = default, CancellationToken cancellationToken = default) - => CleanupAsync(root, ImmutableArray.Create(span), workspace, providers, cancellationToken); + public static Task CleanupAsync(SyntaxNode root, TextSpan span, OptionSet options, HostWorkspaceServices services, ImmutableArray providers = default, CancellationToken cancellationToken = default) + => CleanupAsync(root, ImmutableArray.Create(span), options, services, providers, cancellationToken); /// /// Clean up the provided spans in the node. /// This will only cleanup stuff that doesn't require semantic information. /// - public static Task CleanupAsync(SyntaxNode root, ImmutableArray spans, Workspace workspace, ImmutableArray providers = default, CancellationToken cancellationToken = default) + public static Task CleanupAsync(SyntaxNode root, ImmutableArray spans, OptionSet options, HostWorkspaceServices services, ImmutableArray providers = default, CancellationToken cancellationToken = default) { - var cleanupService = workspace.Services.GetLanguageServices(root.Language).GetRequiredService(); - return cleanupService.CleanupAsync(root, spans, workspace, providers, cancellationToken); + var cleanupService = services.GetLanguageServices(root.Language).GetRequiredService(); + return cleanupService.CleanupAsync(root, spans, options, services, providers, cancellationToken); } } } diff --git a/src/Workspaces/Core/Portable/CodeCleanup/ICodeCleanerService.cs b/src/Workspaces/Core/Portable/CodeCleanup/ICodeCleanerService.cs index 68c4e6bf67f97..b144328102b26 100644 --- a/src/Workspaces/Core/Portable/CodeCleanup/ICodeCleanerService.cs +++ b/src/Workspaces/Core/Portable/CodeCleanup/ICodeCleanerService.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeCleanup.Providers; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Text; @@ -26,13 +27,13 @@ internal interface ICodeCleanerService : ILanguageService /// /// This will run all provided code cleaners in an order that is given to the method. /// - Task CleanupAsync(Document document, ImmutableArray spans, ImmutableArray providers, CancellationToken cancellationToken); + Task CleanupAsync(Document document, ImmutableArray spans, OptionSet options, ImmutableArray providers, CancellationToken cancellationToken); /// /// This will run all provided code cleaners in an order that is given to the method. /// /// This will do cleanups that don't require any semantic information. /// - Task CleanupAsync(SyntaxNode root, ImmutableArray spans, Workspace workspace, ImmutableArray providers, CancellationToken cancellationToken); + Task CleanupAsync(SyntaxNode root, ImmutableArray spans, OptionSet options, HostWorkspaceServices services, ImmutableArray providers, CancellationToken cancellationToken); } } diff --git a/src/Workspaces/Core/Portable/CodeCleanup/Providers/FormatCodeCleanupProvider.cs b/src/Workspaces/Core/Portable/CodeCleanup/Providers/FormatCodeCleanupProvider.cs index d18e6f90ebeb1..e60fae987e8fb 100644 --- a/src/Workspaces/Core/Portable/CodeCleanup/Providers/FormatCodeCleanupProvider.cs +++ b/src/Workspaces/Core/Portable/CodeCleanup/Providers/FormatCodeCleanupProvider.cs @@ -5,50 +5,39 @@ using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CodeCleanup.Providers { - internal class FormatCodeCleanupProvider : ICodeCleanupProvider + internal sealed class FormatCodeCleanupProvider : ICodeCleanupProvider { public string Name => PredefinedCodeCleanupProviderNames.Format; - public async Task CleanupAsync(Document document, ImmutableArray spans, CancellationToken cancellationToken) + public async Task CleanupAsync(Document document, ImmutableArray spans, OptionSet options, CancellationToken cancellationToken) { - // If the old text already exists, use the fast path for formatting. - if (document.TryGetText(out var oldText)) - { - var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var textChanges = Formatter.GetFormattedTextChanges(root, spans, document.Project.Solution.Workspace, cancellationToken: cancellationToken); - if (textChanges.Count == 0) - { - return document; - } - - var newText = oldText.WithChanges(textChanges); - return document.WithText(newText); - } - - return await Formatter.FormatAsync(document, spans, cancellationToken: cancellationToken).ConfigureAwait(false); + var formatter = document.GetRequiredLanguageService(); + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var result = formatter.GetFormattingResult(root, spans, options, document.Project.Solution.Workspace.Services, rules: null, cancellationToken); + + // apply changes to an old text if it already exists + return document.TryGetText(out var oldText) ? + document.WithText(oldText.WithChanges(result.GetTextChanges(cancellationToken))) : + document.WithSyntaxRoot(result.GetFormattedRoot(cancellationToken)); } - public async Task CleanupAsync(SyntaxNode root, ImmutableArray spans, Workspace workspace, CancellationToken cancellationToken) + public Task CleanupAsync(SyntaxNode root, ImmutableArray spans, OptionSet options, HostWorkspaceServices services, CancellationToken cancellationToken) { - // If the old text already exists, use the fast path for formatting. - if (root.SyntaxTree != null && root.SyntaxTree.TryGetText(out var oldText)) - { - var changes = Formatter.GetFormattedTextChanges(root, spans, workspace, cancellationToken: cancellationToken); - if (changes.Count == 0) - { - return root; - } - - return await root.SyntaxTree.WithChangedText(oldText.WithChanges(changes)).GetRootAsync(cancellationToken).ConfigureAwait(false); - } + var formatter = services.GetRequiredLanguageService(root.Language); + var result = formatter.GetFormattingResult(root, spans, options, services, rules: null, cancellationToken); - return Formatter.Format(root, spans, workspace, cancellationToken: cancellationToken); + // apply changes to an old text if it already exists + return (root.SyntaxTree != null && root.SyntaxTree.TryGetText(out var oldText)) ? + root.SyntaxTree.WithChangedText(oldText.WithChanges(result.GetTextChanges(cancellationToken))).GetRootAsync(cancellationToken) : + Task.FromResult(result.GetFormattedRoot(cancellationToken)); } } } diff --git a/src/Workspaces/Core/Portable/CodeCleanup/Providers/ICodeCleanupProvider.cs b/src/Workspaces/Core/Portable/CodeCleanup/Providers/ICodeCleanupProvider.cs index cfdb8b814ca76..823b57fb46bda 100644 --- a/src/Workspaces/Core/Portable/CodeCleanup/Providers/ICodeCleanupProvider.cs +++ b/src/Workspaces/Core/Portable/CodeCleanup/Providers/ICodeCleanupProvider.cs @@ -5,6 +5,8 @@ using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CodeCleanup.Providers @@ -22,13 +24,13 @@ internal interface ICodeCleanupProvider /// /// This should apply its code clean up logic to the spans of the document. /// - Task CleanupAsync(Document document, ImmutableArray spans, CancellationToken cancellationToken = default); + Task CleanupAsync(Document document, ImmutableArray spans, OptionSet options, CancellationToken cancellationToken); /// /// This will run all provided code cleaners in an order that is given to the method. /// /// This will do cleanups that don't require any semantic information /// - Task CleanupAsync(SyntaxNode root, ImmutableArray spans, Workspace workspace, CancellationToken cancellationToken = default); + Task CleanupAsync(SyntaxNode root, ImmutableArray spans, OptionSet options, HostWorkspaceServices services, CancellationToken cancellationToken); } } diff --git a/src/Workspaces/Core/Portable/CodeCleanup/Providers/SimpleCodeCleanupProvider.cs b/src/Workspaces/Core/Portable/CodeCleanup/Providers/SimpleCodeCleanupProvider.cs deleted file mode 100644 index e3575eb48e0ba..0000000000000 --- a/src/Workspaces/Core/Portable/CodeCleanup/Providers/SimpleCodeCleanupProvider.cs +++ /dev/null @@ -1,72 +0,0 @@ -// 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.Collections.Immutable; -using System.Diagnostics; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.CodeCleanup.Providers -{ - /// - /// Helper class that implements using delegates passed to its constructor. - /// - internal class SimpleCodeCleanupProvider : ICodeCleanupProvider - { - private readonly Func, CancellationToken, Task>? _documentDelegatee; - private readonly Func, Workspace, CancellationToken, SyntaxNode>? _syntaxDelegatee; - - public SimpleCodeCleanupProvider(string name, - Func, CancellationToken, Task>? documentDelegatee = null, - Func, Workspace, CancellationToken, SyntaxNode>? syntaxDelegatee = null) - { - Debug.Assert(documentDelegatee != null || syntaxDelegatee != null); - - this.Name = name; - _documentDelegatee = documentDelegatee; - _syntaxDelegatee = syntaxDelegatee; - } - - public string Name { get; } - - public Task CleanupAsync(Document document, ImmutableArray spans, CancellationToken cancellationToken) - { - if (_documentDelegatee != null) - { - return _documentDelegatee(document, spans, cancellationToken); - } - - return CleanupCoreAsync(document, spans, cancellationToken); - } - - private async Task CleanupCoreAsync(Document document, ImmutableArray spans, CancellationToken cancellationToken) - { - RoslynDebug.AssertNotNull(_syntaxDelegatee); - - var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var newRoot = _syntaxDelegatee(root, spans, document.Project.Solution.Workspace, cancellationToken); - - if (root != newRoot) - { - return document.WithSyntaxRoot(newRoot); - } - - return document; - } - - public Task CleanupAsync(SyntaxNode root, ImmutableArray spans, Workspace workspace, CancellationToken cancellationToken) - { - if (_syntaxDelegatee != null) - { - return Task.FromResult(_syntaxDelegatee(root, spans, workspace, cancellationToken)); - } - - return Task.FromResult(root); - } - } -} diff --git a/src/Workspaces/Core/Portable/CodeCleanup/Providers/SimplificationCodeCleanupProvider.cs b/src/Workspaces/Core/Portable/CodeCleanup/Providers/SimplificationCodeCleanupProvider.cs index 702f7044dbef3..c4b372a73e608 100644 --- a/src/Workspaces/Core/Portable/CodeCleanup/Providers/SimplificationCodeCleanupProvider.cs +++ b/src/Workspaces/Core/Portable/CodeCleanup/Providers/SimplificationCodeCleanupProvider.cs @@ -5,6 +5,8 @@ using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Text; @@ -14,10 +16,10 @@ internal class SimplificationCodeCleanupProvider : ICodeCleanupProvider { public string Name => PredefinedCodeCleanupProviderNames.Simplification; - public Task CleanupAsync(Document document, ImmutableArray spans, CancellationToken cancellationToken) + public Task CleanupAsync(Document document, ImmutableArray spans, OptionSet options, CancellationToken cancellationToken) => Simplifier.ReduceAsync(document, spans, null, cancellationToken); - public Task CleanupAsync(SyntaxNode root, ImmutableArray spans, Workspace workspace, CancellationToken cancellationToken) + public Task CleanupAsync(SyntaxNode root, ImmutableArray spans, OptionSet options, HostWorkspaceServices services, CancellationToken cancellationToken) { // Simplifier doesn't work without semantic information return Task.FromResult(root); diff --git a/src/Workspaces/Core/Portable/Formatting/Formatter.cs b/src/Workspaces/Core/Portable/Formatting/Formatter.cs index 55b488c8b56a5..aa55d3352eb74 100644 --- a/src/Workspaces/Core/Portable/Formatting/Formatter.cs +++ b/src/Workspaces/Core/Portable/Formatting/Formatter.cs @@ -252,7 +252,6 @@ internal static SyntaxNode Format(SyntaxNode node, IEnumerable? spans, var optionService = workspace.Services.GetRequiredService(); options ??= workspace.Options; - rules ??= GetDefaultFormattingRules(workspace, node.Language); spans ??= SpecializedCollections.SingletonEnumerable(node.FullSpan); var shouldUseFormattingSpanCollapse = options.GetOption(FormattingBehaviorOptions.AllowDisjointSpanMerging); return languageFormatter.Format(node, spans, shouldUseFormattingSpanCollapse, options.AsAnalyzerConfigOptions(optionService, node.Language), rules, cancellationToken); diff --git a/src/Workspaces/CoreTest/CodeCleanup/CodeCleanupTests.cs b/src/Workspaces/CoreTest/CodeCleanup/CodeCleanupTests.cs index 3a2f06a71a284..0465307892c44 100644 --- a/src/Workspaces/CoreTest/CodeCleanup/CodeCleanupTests.cs +++ b/src/Workspaces/CoreTest/CodeCleanup/CodeCleanupTests.cs @@ -4,6 +4,7 @@ #nullable disable +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -11,6 +12,8 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeCleanup; using Microsoft.CodeAnalysis.CodeCleanup.Providers; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; @@ -168,15 +171,18 @@ public void EntireRange_EndOfFile() public void EntireRangeWithTransformation_RemoveClass() { var expectedResult = (IEnumerable)null; - var transformer = new SimpleCodeCleanupProvider("TransformerCleanup", async (doc, spans, cancellationToken) => + var transformer = new MockCodeCleanupProvider() { - var root = await doc.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - root = root.RemoveCSharpMember(0); + CleanupDocumentAsyncImpl = async (document, spans, options, cancellationToken) => + { + var root = await document.GetSyntaxRootAsync(cancellationToken); + root = root.RemoveCSharpMember(0); - expectedResult = SpecializedCollections.SingletonEnumerable(root.FullSpan); + expectedResult = SpecializedCollections.SingletonEnumerable(root.FullSpan); - return doc.WithSyntaxRoot(root); - }); + return document.WithSyntaxRoot(root); + } + }; VerifyRange("{|b:class C {}|}", transformer, ref expectedResult); } @@ -185,17 +191,20 @@ public void EntireRangeWithTransformation_RemoveClass() public void EntireRangeWithTransformation_AddMember() { var expectedResult = (IEnumerable)null; - var transformer = new SimpleCodeCleanupProvider("TransformerCleanup", async (doc, spans, cancellationToken) => + var transformer = new MockCodeCleanupProvider() { - var root = await doc.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var @class = root.GetMember(0); - var classWithMember = @class.AddCSharpMember(CreateCSharpMethod(), 0); - root = root.ReplaceNode(@class, classWithMember); + CleanupDocumentAsyncImpl = async (document, spans, options, cancellationToken) => + { + var root = await document.GetSyntaxRootAsync(cancellationToken); + var @class = root.GetMember(0); + var classWithMember = @class.AddCSharpMember(CreateCSharpMethod(), 0); + root = root.ReplaceNode(@class, classWithMember); - expectedResult = SpecializedCollections.SingletonEnumerable(root.FullSpan); + expectedResult = SpecializedCollections.SingletonEnumerable(root.FullSpan); - return doc.WithSyntaxRoot(root); - }); + return document.WithSyntaxRoot(root); + } + }; VerifyRange("{|b:class C {}|}", transformer, ref expectedResult); } @@ -204,17 +213,20 @@ public void EntireRangeWithTransformation_AddMember() public void RangeWithTransformation_AddMember() { var expectedResult = (IEnumerable)null; - var transformer = new SimpleCodeCleanupProvider("TransformerCleanup", async (doc, spans, cancellationToken) => + var transformer = new MockCodeCleanupProvider() { - var root = await doc.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var @class = root.GetMember(0).GetMember(0); - var classWithMember = @class.AddCSharpMember(CreateCSharpMethod(), 0); - root = root.ReplaceNode(@class, classWithMember); + CleanupDocumentAsyncImpl = async (document, spans, options, cancellationToken) => + { + var root = await document.GetSyntaxRootAsync(cancellationToken); + var @class = root.GetMember(0).GetMember(0); + var classWithMember = @class.AddCSharpMember(CreateCSharpMethod(), 0); + root = root.ReplaceNode(@class, classWithMember); - expectedResult = SpecializedCollections.SingletonEnumerable(root.GetMember(0).GetMember(0).GetCodeCleanupSpan()); + expectedResult = SpecializedCollections.SingletonEnumerable(root.GetMember(0).GetMember(0).GetCodeCleanupSpan()); - return doc.WithSyntaxRoot(root); - }); + return document.WithSyntaxRoot(root); + } + }; VerifyRange("namespace N { {|b:class C {}|} }", transformer, ref expectedResult); } @@ -223,17 +235,20 @@ public void RangeWithTransformation_AddMember() public void RangeWithTransformation_RemoveMember() { var expectedResult = (IEnumerable)null; - var transformer = new SimpleCodeCleanupProvider("TransformerCleanup", async (doc, spans, cancellationToken) => + var transformer = new MockCodeCleanupProvider() { - var root = await doc.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var @class = root.GetMember(0).GetMember(0); - var classWithMember = @class.RemoveCSharpMember(0); - root = root.ReplaceNode(@class, classWithMember); + CleanupDocumentAsyncImpl = async (document, spans, options, cancellationToken) => + { + var root = await document.GetSyntaxRootAsync(cancellationToken); + var @class = root.GetMember(0).GetMember(0); + var classWithMember = @class.RemoveCSharpMember(0); + root = root.ReplaceNode(@class, classWithMember); - expectedResult = SpecializedCollections.SingletonEnumerable(root.GetMember(0).GetMember(0).GetCodeCleanupSpan()); + expectedResult = SpecializedCollections.SingletonEnumerable(root.GetMember(0).GetMember(0).GetCodeCleanupSpan()); - return doc.WithSyntaxRoot(root); - }); + return document.WithSyntaxRoot(root); + } + }; VerifyRange("namespace N { {|b:class C { void Method() { } }|} }", transformer, ref expectedResult); } @@ -335,20 +350,23 @@ End Sub public void RangeWithTransformation_OutsideOfRange() { var expectedResult = (IEnumerable)null; - var transformer = new SimpleCodeCleanupProvider("TransformerCleanup", async (doc, spans, cancellationToken) => + var transformer = new MockCodeCleanupProvider() { - var root = await doc.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var member = root.GetMember(0).GetMember(0).GetMember(0); - var previousToken = member.GetFirstToken().GetPreviousToken().GetPreviousToken(); - var nextToken = member.GetLastToken().GetNextToken().GetNextToken(); + CleanupDocumentAsyncImpl = async (document, spans, options, cancellationToken) => + { + var root = await document.GetSyntaxRootAsync(cancellationToken); + var member = root.GetMember(0).GetMember(0).GetMember(0); + var previousToken = member.GetFirstToken().GetPreviousToken().GetPreviousToken(); + var nextToken = member.GetLastToken().GetNextToken().GetNextToken(); - root = root.ReplaceToken(previousToken, CSharp.SyntaxFactory.Identifier(previousToken.LeadingTrivia, previousToken.ValueText, previousToken.TrailingTrivia)); - root = root.ReplaceToken(nextToken, CSharp.SyntaxFactory.Token(nextToken.LeadingTrivia, CSharp.CSharpExtensions.Kind(nextToken), nextToken.TrailingTrivia)); + root = root.ReplaceToken(previousToken, CSharp.SyntaxFactory.Identifier(previousToken.LeadingTrivia, previousToken.ValueText, previousToken.TrailingTrivia)); + root = root.ReplaceToken(nextToken, CSharp.SyntaxFactory.Token(nextToken.LeadingTrivia, CSharp.CSharpExtensions.Kind(nextToken), nextToken.TrailingTrivia)); - expectedResult = SpecializedCollections.EmptyEnumerable(); + expectedResult = SpecializedCollections.EmptyEnumerable(); - return doc.WithSyntaxRoot(root); - }); + return document.WithSyntaxRoot(root); + } + }; VerifyRange("namespace N { class C { {|b:void Method() { }|} } }", transformer, ref expectedResult); } @@ -377,11 +395,14 @@ private static void VerifyRange(string codeWithMarker, ICodeCleanupProvider tran private static void VerifyRange(string code, ImmutableArray codeCleanups, ImmutableArray spans, ref IEnumerable expectedResult, string language) { var result = (IEnumerable)null; - var spanCodeCleanup = new SimpleCodeCleanupProvider("TestCodeCleanup", (d, s, c) => + var spanCodeCleanup = new MockCodeCleanupProvider() { - result = s; - return Task.FromResult(d); - }); + CleanupDocumentAsyncImpl = (document, spans, options, cancellationToken) => + { + result = spans; + return Task.FromResult(document); + } + }; var document = CreateDocument(code, language); diff --git a/src/Workspaces/CoreTest/CodeCleanup/MockCodeCleanupProvider.cs b/src/Workspaces/CoreTest/CodeCleanup/MockCodeCleanupProvider.cs new file mode 100644 index 0000000000000..575099f00be29 --- /dev/null +++ b/src/Workspaces/CoreTest/CodeCleanup/MockCodeCleanupProvider.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; +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeCleanup.Providers; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.UnitTests.CodeCleanup +{ + internal sealed class MockCodeCleanupProvider : ICodeCleanupProvider + { + public Func, OptionSet, CancellationToken, Task>? CleanupDocumentAsyncImpl { get; set; } + public Func, OptionSet, HostWorkspaceServices, SyntaxNode>? CleanupNodeImpl { get; set; } + + public MockCodeCleanupProvider() + { + } + + public string Name => nameof(MockCodeCleanupProvider); + + public Task CleanupAsync(Document document, ImmutableArray spans, OptionSet options, CancellationToken cancellationToken) + => (CleanupDocumentAsyncImpl ?? throw new NotImplementedException()).Invoke(document, spans, options, cancellationToken); + + public Task CleanupAsync(SyntaxNode root, ImmutableArray spans, OptionSet options, HostWorkspaceServices services, CancellationToken cancellationToken) + => Task.FromResult((CleanupNodeImpl ?? throw new NotImplementedException()).Invoke(root, spans, options, services)); + + } +} diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/AbstractSyntaxFormattingService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/AbstractSyntaxFormattingService.cs index 922481ecaf0ae..514d1a3cb98de 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/AbstractSyntaxFormattingService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/AbstractSyntaxFormattingService.cs @@ -31,10 +31,8 @@ protected AbstractSyntaxFormattingService() protected abstract AbstractFormattingResult Format(SyntaxNode node, AnalyzerConfigOptions options, IEnumerable rules, SyntaxToken token1, SyntaxToken token2, CancellationToken cancellationToken); - public IFormattingResult Format(SyntaxNode node, IEnumerable spans, bool shouldUseFormattingSpanCollapse, AnalyzerConfigOptions options, IEnumerable rules, CancellationToken cancellationToken) + public IFormattingResult Format(SyntaxNode node, IEnumerable spans, bool shouldUseFormattingSpanCollapse, AnalyzerConfigOptions options, IEnumerable? rules, CancellationToken cancellationToken) { - CheckArguments(node, spans, options, rules); - // quick exit check var spansToFormat = new NormalizedTextSpanCollection(spans.Where(s_notEmpty)); if (spansToFormat.Count == 0) @@ -42,6 +40,8 @@ public IFormattingResult Format(SyntaxNode node, IEnumerable spans, bo return CreateAggregatedFormattingResult(node, SpecializedCollections.EmptyList()); } + rules ??= GetDefaultFormattingRules(); + // check what kind of formatting strategy to use if (AllowDisjointSpanMerging(spansToFormat, shouldUseFormattingSpanCollapse)) { @@ -129,28 +129,5 @@ private static bool AllowDisjointSpanMerging(IList list, bool shouldUs // we are formatting more than half of the collapsed span. return (formattingSpan.Length / Math.Max(actualFormattingSize, 1)) < 2; } - - private static void CheckArguments(SyntaxNode node, IEnumerable spans, AnalyzerConfigOptions options, IEnumerable rules) - { - if (node == null) - { - throw new ArgumentNullException(nameof(node)); - } - - if (spans == null) - { - throw new ArgumentNullException(nameof(spans)); - } - - if (options == null) - { - throw new ArgumentNullException(nameof(options)); - } - - if (rules == null) - { - throw new ArgumentNullException(nameof(rules)); - } - } } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/ISyntaxFormattingService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/ISyntaxFormattingService.cs index 48df77ac2c2cd..6b6831f22236b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/ISyntaxFormattingService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/ISyntaxFormattingService.cs @@ -4,11 +4,14 @@ using System.Collections.Generic; using System.Threading; +using System.Threading.Tasks; using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Shared.Extensions; #if !CODE_STYLE +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Host; #endif @@ -20,6 +23,20 @@ internal interface ISyntaxFormattingService #endif { IEnumerable GetDefaultFormattingRules(); - IFormattingResult Format(SyntaxNode node, IEnumerable spans, bool shouldUseFormattingSpanCollapse, AnalyzerConfigOptions options, IEnumerable rules, CancellationToken cancellationToken); + IFormattingResult Format(SyntaxNode node, IEnumerable spans, bool shouldUseFormattingSpanCollapse, AnalyzerConfigOptions options, IEnumerable? rules, CancellationToken cancellationToken); } + +#if !CODE_STYLE + internal static class ISyntaxFormattingServiceExtensions + { + internal static IFormattingResult GetFormattingResult(this ISyntaxFormattingService service, SyntaxNode node, IEnumerable spans, OptionSet options, HostWorkspaceServices services, IEnumerable? rules, CancellationToken cancellationToken) + { + var optionService = services.GetRequiredService(); + var shouldUseFormattingSpanCollapse = options.GetOption(FormattingBehaviorOptions.AllowDisjointSpanMerging); + var configOptions = options.AsAnalyzerConfigOptions(optionService, node.Language); + + return service.Format(node, spans, shouldUseFormattingSpanCollapse, configOptions, rules, cancellationToken); + } + } +#endif } diff --git a/src/Workspaces/VisualBasic/Portable/CaseCorrection/VisualBasicCaseCorrectionService.vb b/src/Workspaces/VisualBasic/Portable/CaseCorrection/VisualBasicCaseCorrectionService.vb index 3e250e53d7e36..a13d6291b8380 100644 --- a/src/Workspaces/VisualBasic/Portable/CaseCorrection/VisualBasicCaseCorrectionService.vb +++ b/src/Workspaces/VisualBasic/Portable/CaseCorrection/VisualBasicCaseCorrectionService.vb @@ -27,7 +27,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CaseCorrection Protected Overrides Sub AddReplacements(semanticModel As SemanticModel, root As SyntaxNode, spans As ImmutableArray(Of TextSpan), - workspace As Workspace, replacements As ConcurrentDictionary(Of SyntaxToken, SyntaxToken), cancellationToken As CancellationToken) For Each span In spans diff --git a/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/AbstractTokensCodeCleanupProvider.vb b/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/AbstractTokensCodeCleanupProvider.vb index 43dc00ea7d320..1420b76084647 100644 --- a/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/AbstractTokensCodeCleanupProvider.vb +++ b/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/AbstractTokensCodeCleanupProvider.vb @@ -5,6 +5,8 @@ Imports System.Collections.Immutable Imports System.Threading Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Host +Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.Shared.Collections Imports Microsoft.CodeAnalysis.Text @@ -15,18 +17,18 @@ Namespace Microsoft.CodeAnalysis.CodeCleanup.Providers Public MustOverride ReadOnly Property Name As String Implements ICodeCleanupProvider.Name Protected MustOverride Function GetRewriterAsync( - document As Document, root As SyntaxNode, spans As ImmutableArray(Of TextSpan), workspace As Workspace, cancellationToken As CancellationToken) As Task(Of Rewriter) + document As Document, root As SyntaxNode, spans As ImmutableArray(Of TextSpan), cancellationToken As CancellationToken) As Task(Of Rewriter) - Public Async Function CleanupAsync(document As Document, spans As ImmutableArray(Of TextSpan), Optional cancellationToken As CancellationToken = Nothing) As Task(Of Document) Implements ICodeCleanupProvider.CleanupAsync + Public Async Function CleanupAsync(document As Document, spans As ImmutableArray(Of TextSpan), options As OptionSet, cancellationToken As CancellationToken) As Task(Of Document) Implements ICodeCleanupProvider.CleanupAsync Dim root = Await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(False) - Dim rewriter As Rewriter = Await GetRewriterAsync(document, root, spans, document.Project.Solution.Workspace, cancellationToken).ConfigureAwait(False) + Dim rewriter As Rewriter = Await GetRewriterAsync(document, root, spans, cancellationToken).ConfigureAwait(False) Dim newRoot = rewriter.Visit(root) Return If(root Is newRoot, document, document.WithSyntaxRoot(newRoot)) End Function - Public Async Function CleanupAsync(root As SyntaxNode, spans As ImmutableArray(Of TextSpan), workspace As Workspace, Optional cancellationToken As CancellationToken = Nothing) As Task(Of SyntaxNode) Implements ICodeCleanupProvider.CleanupAsync - Dim rewriter As Rewriter = Await GetRewriterAsync(Nothing, root, spans, workspace, cancellationToken).ConfigureAwait(False) + Public Async Function CleanupAsync(root As SyntaxNode, spans As ImmutableArray(Of TextSpan), options As OptionSet, services As HostWorkspaceServices, cancellationToken As CancellationToken) As Task(Of SyntaxNode) Implements ICodeCleanupProvider.CleanupAsync + Dim rewriter As Rewriter = Await GetRewriterAsync(Nothing, root, spans, cancellationToken).ConfigureAwait(False) Return rewriter.Visit(root) End Function diff --git a/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/AddMissingTokensCodeCleanupProvider.vb b/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/AddMissingTokensCodeCleanupProvider.vb index d1890270c9e77..5323ec7417111 100644 --- a/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/AddMissingTokensCodeCleanupProvider.vb +++ b/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/AddMissingTokensCodeCleanupProvider.vb @@ -26,7 +26,7 @@ Namespace Microsoft.CodeAnalysis.CodeCleanup.Providers End Get End Property - Protected Overrides Async Function GetRewriterAsync(document As Document, root As SyntaxNode, spans As ImmutableArray(Of TextSpan), workspace As Workspace, cancellationToken As CancellationToken) As Task(Of Rewriter) + Protected Overrides Async Function GetRewriterAsync(document As Document, root As SyntaxNode, spans As ImmutableArray(Of TextSpan), cancellationToken As CancellationToken) As Task(Of Rewriter) Return Await AddMissingTokensRewriter.CreateAsync(document, spans, cancellationToken).ConfigureAwait(False) End Function diff --git a/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/CaseCorrectionCodeCleanupProvider.vb b/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/CaseCorrectionCodeCleanupProvider.vb index 7d3b700d5d1ea..ce096181027b6 100644 --- a/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/CaseCorrectionCodeCleanupProvider.vb +++ b/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/CaseCorrectionCodeCleanupProvider.vb @@ -7,6 +7,8 @@ Imports System.Composition Imports System.Diagnostics.CodeAnalysis Imports System.Threading Imports Microsoft.CodeAnalysis.CaseCorrection +Imports Microsoft.CodeAnalysis.Host +Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.Text Namespace Microsoft.CodeAnalysis.CodeCleanup.Providers @@ -26,12 +28,12 @@ Namespace Microsoft.CodeAnalysis.CodeCleanup.Providers End Get End Property - Public Function CleanupAsync(document As Document, spans As ImmutableArray(Of TextSpan), Optional cancellationToken As CancellationToken = Nothing) As Task(Of Document) Implements ICodeCleanupProvider.CleanupAsync + Public Function CleanupAsync(document As Document, spans As ImmutableArray(Of TextSpan), options As OptionSet, cancellationToken As CancellationToken) As Task(Of Document) Implements ICodeCleanupProvider.CleanupAsync Return CaseCorrector.CaseCorrectAsync(document, spans, cancellationToken) End Function - Public Function CleanupAsync(root As SyntaxNode, spans As ImmutableArray(Of TextSpan), workspace As Workspace, Optional cancellationToken As CancellationToken = Nothing) As Task(Of SyntaxNode) Implements ICodeCleanupProvider.CleanupAsync - Return Task.FromResult(CaseCorrector.CaseCorrect(root, spans, workspace, cancellationToken)) + Public Function CleanupAsync(root As SyntaxNode, spans As ImmutableArray(Of TextSpan), options As OptionSet, services As HostWorkspaceServices, cancellationToken As CancellationToken) As Task(Of SyntaxNode) Implements ICodeCleanupProvider.CleanupAsync + Return Task.FromResult(CaseCorrector.CaseCorrect(root, spans, services, cancellationToken)) End Function End Class End Namespace diff --git a/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/FixIncorrectTokensCodeCleanupProvider.vb b/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/FixIncorrectTokensCodeCleanupProvider.vb index 5cf2a1980917f..80e32d78edc21 100644 --- a/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/FixIncorrectTokensCodeCleanupProvider.vb +++ b/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/FixIncorrectTokensCodeCleanupProvider.vb @@ -35,7 +35,7 @@ Namespace Microsoft.CodeAnalysis.CodeCleanup.Providers End Get End Property - Protected Overrides Function GetRewriterAsync(document As Document, root As SyntaxNode, spans As ImmutableArray(Of TextSpan), workspace As Workspace, cancellationToken As CancellationToken) As Task(Of Rewriter) + Protected Overrides Function GetRewriterAsync(document As Document, root As SyntaxNode, spans As ImmutableArray(Of TextSpan), cancellationToken As CancellationToken) As Task(Of Rewriter) Return FixIncorrectTokensRewriter.CreateAsync(document, spans, cancellationToken) End Function diff --git a/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/NormalizeModifiersOrOperatorsCodeCleanupProvider.vb b/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/NormalizeModifiersOrOperatorsCodeCleanupProvider.vb index 8ee05faf7c3c8..997322098aeda 100644 --- a/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/NormalizeModifiersOrOperatorsCodeCleanupProvider.vb +++ b/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/NormalizeModifiersOrOperatorsCodeCleanupProvider.vb @@ -7,6 +7,8 @@ Imports System.Composition Imports System.Diagnostics.CodeAnalysis Imports System.Threading Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Host +Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.Shared.Collections Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.CodeStyle @@ -29,14 +31,14 @@ Namespace Microsoft.CodeAnalysis.CodeCleanup.Providers End Get End Property - Public Async Function CleanupAsync(document As Document, spans As ImmutableArray(Of TextSpan), Optional cancellationToken As CancellationToken = Nothing) As Task(Of Document) Implements ICodeCleanupProvider.CleanupAsync + Public Async Function CleanupAsync(document As Document, spans As ImmutableArray(Of TextSpan), options As OptionSet, cancellationToken As CancellationToken) As Task(Of Document) Implements ICodeCleanupProvider.CleanupAsync Dim root = Await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(False) - Dim newRoot = Await CleanupAsync(root, spans, document.Project.Solution.Workspace, cancellationToken).ConfigureAwait(False) + Dim newRoot = Await CleanupAsync(root, spans, options, document.Project.Solution.Workspace.Services, cancellationToken).ConfigureAwait(False) Return If(root Is newRoot, document, document.WithSyntaxRoot(newRoot)) End Function - Public Function CleanupAsync(root As SyntaxNode, spans As ImmutableArray(Of TextSpan), workspace As Workspace, Optional cancellationToken As CancellationToken = Nothing) As Task(Of SyntaxNode) Implements ICodeCleanupProvider.CleanupAsync + Public Function CleanupAsync(root As SyntaxNode, spans As ImmutableArray(Of TextSpan), options As OptionSet, services As HostWorkspaceServices, cancellationToken As CancellationToken) As Task(Of SyntaxNode) Implements ICodeCleanupProvider.CleanupAsync Dim rewriter = New Rewriter(spans, cancellationToken) Dim newRoot = rewriter.Visit(root) diff --git a/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/ReduceTokensCodeCleanupProvider.vb b/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/ReduceTokensCodeCleanupProvider.vb index b92ec96f64a81..b6f4bef51bc7b 100644 --- a/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/ReduceTokensCodeCleanupProvider.vb +++ b/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/ReduceTokensCodeCleanupProvider.vb @@ -30,7 +30,7 @@ Namespace Microsoft.CodeAnalysis.CodeCleanup.Providers End Get End Property - Protected Overrides Function GetRewriterAsync(document As Document, root As SyntaxNode, spans As ImmutableArray(Of TextSpan), workspace As Workspace, cancellationToken As CancellationToken) As Task(Of Rewriter) + Protected Overrides Function GetRewriterAsync(document As Document, root As SyntaxNode, spans As ImmutableArray(Of TextSpan), cancellationToken As CancellationToken) As Task(Of Rewriter) Return Task.FromResult(Of Rewriter)(New ReduceTokensRewriter(spans, cancellationToken)) End Function diff --git a/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/RemoveUnnecessaryLineContinuationCodeCleanupProvider.vb b/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/RemoveUnnecessaryLineContinuationCodeCleanupProvider.vb index 7394c1197d429..8954e34f6ee84 100644 --- a/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/RemoveUnnecessaryLineContinuationCodeCleanupProvider.vb +++ b/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/RemoveUnnecessaryLineContinuationCodeCleanupProvider.vb @@ -7,6 +7,8 @@ Imports System.Composition Imports System.Diagnostics.CodeAnalysis Imports System.Threading Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Host +Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -27,7 +29,7 @@ Namespace Microsoft.CodeAnalysis.CodeCleanup.Providers End Get End Property - Public Async Function CleanupAsync(document As Document, spans As ImmutableArray(Of TextSpan), Optional cancellationToken As CancellationToken = Nothing) As Task(Of Document) Implements ICodeCleanupProvider.CleanupAsync + Public Async Function CleanupAsync(document As Document, spans As ImmutableArray(Of TextSpan), options As OptionSet, cancellationToken As CancellationToken) As Task(Of Document) Implements ICodeCleanupProvider.CleanupAsync ' Is this VB 9? If so, we shouldn't remove line continuations because implicit line continuation was introduced in VB 10. Dim parseOptions = TryCast(document.Project.ParseOptions, VisualBasicParseOptions) If parseOptions?.LanguageVersion <= LanguageVersion.VisualBasic9 Then @@ -35,12 +37,12 @@ Namespace Microsoft.CodeAnalysis.CodeCleanup.Providers End If Dim root = Await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(False) - Dim newRoot = Await CleanupAsync(root, spans, document.Project.Solution.Workspace, cancellationToken).ConfigureAwait(False) + Dim newRoot = Await CleanupAsync(root, spans, options, document.Project.Solution.Workspace.Services, cancellationToken).ConfigureAwait(False) Return If(newRoot Is root, document, document.WithSyntaxRoot(newRoot)) End Function - Public Function CleanupAsync(root As SyntaxNode, spans As ImmutableArray(Of TextSpan), workspace As Workspace, Optional cancellationToken As CancellationToken = Nothing) As Task(Of SyntaxNode) Implements ICodeCleanupProvider.CleanupAsync + Public Function CleanupAsync(root As SyntaxNode, spans As ImmutableArray(Of TextSpan), options As OptionSet, services As HostWorkspaceServices, cancellationToken As CancellationToken) As Task(Of SyntaxNode) Implements ICodeCleanupProvider.CleanupAsync Return Task.FromResult(Replacer.Process(root, spans, cancellationToken)) End Function From 0f90d249425c93683bb2cdf2a3c929dd81a5d387 Mon Sep 17 00:00:00 2001 From: tmat Date: Tue, 21 Dec 2021 19:28:42 -0800 Subject: [PATCH 02/13] TODO --- ...stractRemoveUnusedValuesCodeFixProvider.cs | 27 +++++++++++-------- .../AutomaticLineEnderCommandHandler.cs | 9 ++++--- .../Indentation/CSharpFormatterTestsBase.cs | 2 +- .../SmartIndenterEnterOnTokenTests.cs | 4 +-- .../SmartTokenFormatterFormatRangeTests.cs | 4 +-- .../Extensions/ITextSnapshotExtensions.cs | 5 +++- .../Formatting/CoreFormatterTestsBase.cs | 2 +- .../SmartTokenFormatter_FormatTokenTests.vb | 2 +- .../CurlyBraceCompletionService.cs | 9 +++---- .../CSharpFormattingInteractionService.cs | 19 ++++++------- ...actLanguageService`2.IVsLanguageTextOps.cs | 8 +++--- .../Implementation/Venus/ContainedDocument.cs | 7 ++--- .../Indentation/CSharpSmartTokenFormatter.cs | 15 ++++++----- .../Core/Portable/Editing/SyntaxGenerator.cs | 6 +++++ .../Core/Portable/Formatting/Formatter.cs | 27 ++++++++++--------- .../AbstractIndentationService.Indenter.cs | 2 +- .../Indentation/ISmartTokenFormatter.cs | 3 ++- src/Workspaces/CoreTest/FormattingTests.cs | 4 +-- .../VisualBasicSmartTokenFormatter.vb | 6 +++-- 19 files changed, 91 insertions(+), 70 deletions(-) diff --git a/src/Analyzers/Core/CodeFixes/RemoveUnusedParametersAndValues/AbstractRemoveUnusedValuesCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/RemoveUnusedParametersAndValues/AbstractRemoveUnusedValuesCodeFixProvider.cs index 0b133d8db6d5f..c75a0632e47db 100644 --- a/src/Analyzers/Core/CodeFixes/RemoveUnusedParametersAndValues/AbstractRemoveUnusedValuesCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/RemoveUnusedParametersAndValues/AbstractRemoveUnusedValuesCodeFixProvider.cs @@ -20,6 +20,7 @@ using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.MoveDeclarationNearReference; using Microsoft.CodeAnalysis.Operations; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.ReplaceDiscardDeclarationsWithAssignments; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -268,14 +269,15 @@ private static async Task PreprocessDocumentAsync(Document document, I protected sealed override async Task FixAllAsync(Document document, ImmutableArray diagnostics, SyntaxEditor editor, CancellationToken cancellationToken) { - var newRoot = await GetNewRootAsync( - await PreprocessDocumentAsync(document, diagnostics, cancellationToken).ConfigureAwait(false), - diagnostics, cancellationToken).ConfigureAwait(false); + var preprocessedDocument = await PreprocessDocumentAsync(document, diagnostics, cancellationToken).ConfigureAwait(false); + var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); + var newRoot = await GetNewRootAsync(preprocessedDocument, options, diagnostics, cancellationToken).ConfigureAwait(false); editor.ReplaceNode(editor.OriginalRoot, newRoot); } private async Task GetNewRootAsync( Document document, + OptionSet options, ImmutableArray diagnostics, CancellationToken cancellationToken) { @@ -314,7 +316,7 @@ await FixAllAsync(diagnosticId, diagnosticsToFix.Select(d => d), document, seman // Second pass to post process the document. var currentRoot = editor.GetChangedRoot(); - var newRoot = await PostProcessDocumentAsync(document, currentRoot, + var newRoot = await PostProcessDocumentAsync(document, options, currentRoot, diagnosticId, preference, cancellationToken).ConfigureAwait(false); if (currentRoot != newRoot) @@ -719,6 +721,7 @@ bool ShouldRemoveStatement(TLocalDeclarationStatementSyntax localDeclarationStat private async Task PostProcessDocumentAsync( Document document, + OptionSet options, SyntaxNode currentRoot, string diagnosticId, UnusedValuePreference preference, @@ -731,7 +734,7 @@ private async Task PostProcessDocumentAsync( if (preference == UnusedValuePreference.DiscardVariable) { currentRoot = await PostProcessDocumentCoreAsync( - ReplaceDiscardDeclarationsWithAssignmentsAsync, currentRoot, document, cancellationToken).ConfigureAwait(false); + ReplaceDiscardDeclarationsWithAssignmentsAsync, currentRoot, document, options, cancellationToken).ConfigureAwait(false); } // If we added new variable declaration statements, move these as close as possible to their @@ -739,16 +742,17 @@ private async Task PostProcessDocumentAsync( if (NeedsToMoveNewLocalDeclarationsNearReference(diagnosticId)) { currentRoot = await PostProcessDocumentCoreAsync( - AdjustLocalDeclarationsAsync, currentRoot, document, cancellationToken).ConfigureAwait(false); + AdjustLocalDeclarationsAsync, currentRoot, document, options, cancellationToken).ConfigureAwait(false); } return currentRoot; } private static async Task PostProcessDocumentCoreAsync( - Func> processMemberDeclarationAsync, + Func> processMemberDeclarationAsync, SyntaxNode currentRoot, Document document, + OptionSet options, CancellationToken cancellationToken) { // Process each member declaration which had at least one diagnostic reported in the original tree and hence @@ -760,7 +764,7 @@ private static async Task PostProcessDocumentCoreAsync( foreach (var memberDecl in newRoot.DescendantNodes().Where(n => n.HasAnnotation(s_memberAnnotation))) { - var newMemberDecl = await processMemberDeclarationAsync(memberDecl, newDocument, cancellationToken).ConfigureAwait(false); + var newMemberDecl = await processMemberDeclarationAsync(memberDecl, newDocument, options, cancellationToken).ConfigureAwait(false); memberDeclReplacementsMap.Add(memberDecl, newMemberDecl); } @@ -776,7 +780,7 @@ private static async Task PostProcessDocumentCoreAsync( /// This is needed to prevent the code fix/FixAll from generating code with /// multiple local variables named '_', which is a compiler error. /// - private async Task ReplaceDiscardDeclarationsWithAssignmentsAsync(SyntaxNode memberDeclaration, Document document, CancellationToken cancellationToken) + private async Task ReplaceDiscardDeclarationsWithAssignmentsAsync(SyntaxNode memberDeclaration, Document document, OptionSet options, CancellationToken cancellationToken) { var service = document.GetLanguageService(); if (service == null) @@ -795,9 +799,10 @@ private async Task ReplaceDiscardDeclarationsWithAssignmentsAsync(Sy /// local declaration statements annotated with /// whose declared local is no longer used removed. /// - private async Task AdjustLocalDeclarationsAsync( + private static async Task AdjustLocalDeclarationsAsync( SyntaxNode memberDeclaration, Document document, + OptionSet options, CancellationToken cancellationToken) { var moveDeclarationService = document.GetRequiredLanguageService(); @@ -821,7 +826,7 @@ private async Task AdjustLocalDeclarationsAsync( var rootWithTrackedNodes = root.TrackNodes(originalDeclStatementsToMoveOrRemove); // Run formatter prior to invoking IMoveDeclarationNearReferenceService. - rootWithTrackedNodes = Formatter.Format(rootWithTrackedNodes, originalDeclStatementsToMoveOrRemove.Select(s => s.Span), document.Project.Solution.Workspace, cancellationToken: cancellationToken); + rootWithTrackedNodes = Formatter.Format(rootWithTrackedNodes, originalDeclStatementsToMoveOrRemove.Select(s => s.Span), document.Project.Solution.Workspace, options, cancellationToken: cancellationToken); document = document.WithSyntaxRoot(rootWithTrackedNodes); await OnDocumentUpdatedAsync().ConfigureAwait(false); diff --git a/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs b/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs index 3462f253cac28..61453b3de49a5 100644 --- a/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs +++ b/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs @@ -109,13 +109,14 @@ protected override Document FormatAndApplyBasedOnEndToken(Document document, int } var options = document.GetOptionsAsync(cancellationToken).WaitAndGetResult(cancellationToken); - - var changes = Formatter.GetFormattedTextChanges( + var formatter = document.GetRequiredLanguageService(); + var changes = formatter.GetFormattingResult( root, SpecializedCollections.SingletonCollection(CommonFormattingHelpers.GetFormattingSpan(root, span.Value)), - document.Project.Solution.Workspace, options, - cancellationToken: cancellationToken); + document.Project.Solution.Workspace.Services, + rules: null, + cancellationToken).GetTextChanges(cancellationToken); return document.ApplyTextChanges(changes, cancellationToken); } diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs index bbd7278095aa0..9bbed65cd85d1 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs @@ -83,7 +83,7 @@ private static async Task TokenFormatWorkerAsync(TestWorkspace workspace, ITextB var documentOptions = await document.GetOptionsAsync(); var formatter = new CSharpSmartTokenFormatter(documentOptions, rules, root); - var changes = await formatter.FormatTokenAsync(workspace, token, CancellationToken.None); + var changes = await formatter.FormatTokenAsync(workspace.Services, token, CancellationToken.None); ApplyChanges(buffer, changes); } diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs index 00957a31eb98f..c2a1a3fc87512 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs @@ -1519,7 +1519,7 @@ private static async Task AssertIndentUsingSmartTokenFormatterAsync( Assert.True( CSharpIndentationService.ShouldUseSmartTokenFormatterInsteadOfIndenter( - Formatter.GetDefaultFormattingRules(workspace, root.Language), + Formatter.GetDefaultFormattingRules(workspace.Services, root.Language), root, line.AsTextLine(), optionService, await document.GetOptionsAsync(), out _)); var actualIndentation = await GetSmartTokenFormatterIndentationWorkerAsync(workspace, buffer, indentationLine, ch); @@ -1562,7 +1562,7 @@ private static async Task AssertIndentNotUsingSmartTokenFormatterButUsingIndente Assert.False( CSharpIndentationService.ShouldUseSmartTokenFormatterInsteadOfIndenter( - Formatter.GetDefaultFormattingRules(workspace, root.Language), + Formatter.GetDefaultFormattingRules(workspace.Services, root.Language), root, line.AsTextLine(), optionService, await document.GetOptionsAsync(), out _)); TestIndentation(workspace, indentationLine, expectedIndentation); diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartTokenFormatterFormatRangeTests.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartTokenFormatterFormatRangeTests.cs index 4599073a45c36..7db1daf14aa39 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartTokenFormatterFormatRangeTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartTokenFormatterFormatRangeTests.cs @@ -3555,7 +3555,7 @@ private static Tuple> GetService( TestWorkspace workspace) { var options = workspace.Options; - return Tuple.Create(options, Formatter.GetDefaultFormattingRules(workspace, LanguageNames.CSharp)); + return Tuple.Create(options, Formatter.GetDefaultFormattingRules(workspace.Services, LanguageNames.CSharp)); } private static Task AutoFormatOnColonAsync(string codeWithMarker, string expected, SyntaxKind startTokenKind) @@ -3610,7 +3610,7 @@ private static async Task AutoFormatOnMarkerAsync(string initialMarkup, string e return; } - var changes = formatter.FormatRange(workspace, tokenRange.Value.Item1, tokenRange.Value.Item2, CancellationToken.None); + var changes = formatter.FormatRange(workspace.Services, tokenRange.Value.Item1, tokenRange.Value.Item2, CancellationToken.None); var actual = GetFormattedText(buffer, changes); Assert.Equal(expected, actual); } diff --git a/src/EditorFeatures/Core/Shared/Extensions/ITextSnapshotExtensions.cs b/src/EditorFeatures/Core/Shared/Extensions/ITextSnapshotExtensions.cs index 1237d796cacf5..57c2a3b11ab57 100644 --- a/src/EditorFeatures/Core/Shared/Extensions/ITextSnapshotExtensions.cs +++ b/src/EditorFeatures/Core/Shared/Extensions/ITextSnapshotExtensions.cs @@ -41,8 +41,11 @@ public static void FormatAndApplyToBuffer(this ITextSnapshot snapshot, TextSpan rules = document.GetFormattingRules(span, rules); var root = document.GetRequiredSyntaxRootSynchronously(cancellationToken); + var formatter = document.GetRequiredLanguageService(); + var documentOptions = document.GetOptionsAsync(cancellationToken).WaitAndGetResult(cancellationToken); - var changes = Formatter.GetFormattedTextChanges(root, SpecializedCollections.SingletonEnumerable(span), document.Project.Solution.Workspace, documentOptions, rules, cancellationToken); + var result = formatter.GetFormattingResult(root, SpecializedCollections.SingletonEnumerable(span), documentOptions, document.Project.Solution.Workspace.Services, rules, cancellationToken); + var changes = result.GetTextChanges(cancellationToken); using (Logger.LogBlock(FunctionId.Formatting_ApplyResultToBuffer, cancellationToken)) { diff --git a/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs b/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs index 1437707b5a6b4..45d79b78d3098 100644 --- a/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs +++ b/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs @@ -209,7 +209,7 @@ protected async Task AssertFormatAsync(string expected, string code, IEnumerable } var root = await syntaxTree.GetRootAsync(); - var rules = formattingRuleProvider.CreateRule(workspace.CurrentSolution.GetDocument(syntaxTree), 0).Concat(Formatter.GetDefaultFormattingRules(workspace, root.Language)); + var rules = formattingRuleProvider.CreateRule(workspace.CurrentSolution.GetDocument(syntaxTree), 0).Concat(Formatter.GetDefaultFormattingRules(workspace.Services, root.Language)); AssertFormat(workspace, expected, options, rules, clonedBuffer, root, spans); // format with node and transform diff --git a/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartTokenFormatter_FormatTokenTests.vb b/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartTokenFormatter_FormatTokenTests.vb index 13f9f9c369a03..38b5289e89d14 100644 --- a/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartTokenFormatter_FormatTokenTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartTokenFormatter_FormatTokenTests.vb @@ -208,7 +208,7 @@ End Class Nothing, ignoreMissingToken)) Dim smartFormatter = New VisualBasicSmartTokenFormatter(Await document.GetOptionsAsync(CancellationToken.None), formattingRules, root) - Dim changes = Await smartFormatter.FormatTokenAsync(workspace, token, Nothing) + Dim changes = Await smartFormatter.FormatTokenAsync(workspace.Services, token, Nothing) Using edit = buffer.CreateEdit() For Each change In changes diff --git a/src/Features/CSharp/Portable/BraceCompletion/CurlyBraceCompletionService.cs b/src/Features/CSharp/Portable/BraceCompletion/CurlyBraceCompletionService.cs index c4f71d2592a47..32c5da7b5edbe 100644 --- a/src/Features/CSharp/Portable/BraceCompletion/CurlyBraceCompletionService.cs +++ b/src/Features/CSharp/Portable/BraceCompletion/CurlyBraceCompletionService.cs @@ -296,12 +296,9 @@ private static bool ContainsOnlyWhitespace(SourceText text, int openingPosition, var spanToFormat = TextSpan.FromBounds(Math.Max(startPoint, 0), endPoint); var rules = document.GetFormattingRules(spanToFormat, braceFormattingIndentationRules); - var result = Formatter.GetFormattingResult( - root, SpecializedCollections.SingletonEnumerable(spanToFormat), document.Project.Solution.Workspace, documentOptions, rules, cancellationToken); - if (result == null) - { - return (ImmutableArray.Empty, closingPoint); - } + var formatter = document.GetRequiredLanguageService(); + var result = formatter.GetFormattingResult( + root, SpecializedCollections.SingletonEnumerable(spanToFormat), documentOptions, document.Project.Solution.Workspace.Services, rules, cancellationToken); var newRoot = result.GetFormattedRoot(cancellationToken); var newClosingPoint = newRoot.GetAnnotatedTokens(s_closingBraceSyntaxAnnotation).Single().SpanStart + 1; diff --git a/src/Features/CSharp/Portable/Formatting/CSharpFormattingInteractionService.cs b/src/Features/CSharp/Portable/Formatting/CSharpFormattingInteractionService.cs index 366877442ed69..bb725facc4354 100644 --- a/src/Features/CSharp/Portable/Formatting/CSharpFormattingInteractionService.cs +++ b/src/Features/CSharp/Portable/Formatting/CSharpFormattingInteractionService.cs @@ -110,9 +110,9 @@ public async Task> GetFormattingChangesAsync( options = await inferredIndentationService.GetDocumentOptionsWithInferredIndentationAsync(document, explicitFormat: true, cancellationToken: cancellationToken).ConfigureAwait(false); } - return Formatter.GetFormattedTextChanges(root, - SpecializedCollections.SingletonEnumerable(formattingSpan), - document.Project.Solution.Workspace, options, cancellationToken).ToImmutableArray(); + var service = document.GetRequiredLanguageService(); + var result = service.GetFormattingResult(root, SpecializedCollections.SingletonEnumerable(formattingSpan), options, document.Project.Solution.Workspace.Services, rules: null, cancellationToken); + return result.GetTextChanges(cancellationToken).ToImmutableArray(); } public async Task> GetFormattingChangesOnPasteAsync( @@ -124,11 +124,7 @@ public async Task> GetFormattingChangesOnPasteAsync( var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var formattingSpan = CommonFormattingHelpers.GetFormattingSpan(root, textSpan); - var service = document.GetLanguageService(); - if (service == null) - { - return ImmutableArray.Empty; - } + var service = document.GetRequiredLanguageService(); var rules = new List() { new PasteFormattingRule() }; rules.AddRange(service.GetDefaultFormattingRules()); @@ -140,7 +136,8 @@ public async Task> GetFormattingChangesOnPasteAsync( options = await inferredIndentationService.GetDocumentOptionsWithInferredIndentationAsync(document, explicitFormat: false, cancellationToken: cancellationToken).ConfigureAwait(false); } - return Formatter.GetFormattedTextChanges(root, SpecializedCollections.SingletonEnumerable(formattingSpan), document.Project.Solution.Workspace, options, rules, cancellationToken).ToImmutableArray(); + var result = service.GetFormattingResult(root, SpecializedCollections.SingletonEnumerable(formattingSpan), options, document.Project.Solution.Workspace.Services, rules, cancellationToken); + return result.GetTextChanges(cancellationToken).ToImmutableArray(); } private static IEnumerable GetFormattingRules(Document document, int position, SyntaxToken tokenBeforeCaret) @@ -306,7 +303,7 @@ private static async Task> FormatTokenAsync(Document document, { var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var formatter = CreateSmartTokenFormatter(options, formattingRules, root); - var changes = await formatter.FormatTokenAsync(document.Project.Solution.Workspace, token, cancellationToken).ConfigureAwait(false); + var changes = await formatter.FormatTokenAsync(document.Project.Solution.Workspace.Services, token, cancellationToken).ConfigureAwait(false); return changes; } @@ -340,7 +337,7 @@ private static async Task> FormatRangeAsync( var formatter = new CSharpSmartTokenFormatter(options, formattingRules, (CompilationUnitSyntax)root); - var changes = formatter.FormatRange(document.Project.Solution.Workspace, tokenRange.Value.Item1, tokenRange.Value.Item2, cancellationToken); + var changes = formatter.FormatRange(document.Project.Solution.Workspace.Services, tokenRange.Value.Item1, tokenRange.Value.Item2, cancellationToken); return changes.ToImmutableArray(); } diff --git a/src/VisualStudio/Core/Def/Implementation/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs b/src/VisualStudio/Core/Def/Implementation/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs index 62b22fb67ba6b..d454578f3c748 100644 --- a/src/VisualStudio/Core/Def/Implementation/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs +++ b/src/VisualStudio/Core/Def/Implementation/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs @@ -63,11 +63,13 @@ private int FormatWorker(IVsTextLayer textLayer, TextSpan[] selections, Cancella // Since we know we are on the UI thread, lets get the base indentation now, so that there is less // cleanup work to do later in Venus. - var ruleFactory = this.Workspace.Services.GetService(); + var ruleFactory = Workspace.Services.GetService(); var rules = ruleFactory.CreateRule(document, start).Concat(Formatter.GetDefaultFormattingRules(document)); // use formatting that return text changes rather than tree rewrite which is more expensive - var originalChanges = Formatter.GetFormattedTextChanges(root, SpecializedCollections.SingletonEnumerable(adjustedSpan), document.Project.Solution.Workspace, options, rules, cancellationToken); + var formatter = document.GetRequiredLanguageService(); + var originalChanges = formatter.GetFormattingResult(root, SpecializedCollections.SingletonEnumerable(adjustedSpan), options, Workspace.Services, rules, cancellationToken) + .GetTextChanges(cancellationToken); var originalSpan = RoslynTextSpan.FromBounds(start, end); var formattedChanges = ruleFactory.FilterFormattedChanges(document, originalSpan, originalChanges); @@ -78,7 +80,7 @@ private int FormatWorker(IVsTextLayer textLayer, TextSpan[] selections, Cancella // create new formatted document var formattedDocument = document.WithText(text.WithChanges(formattedChanges)); - formattedDocument.Project.Solution.Workspace.ApplyDocumentChanges(formattedDocument, cancellationToken); + Workspace.ApplyDocumentChanges(formattedDocument, cancellationToken); return VSConstants.S_OK; } diff --git a/src/VisualStudio/Core/Def/Implementation/Venus/ContainedDocument.cs b/src/VisualStudio/Core/Def/Implementation/Venus/ContainedDocument.cs index d363c7ba77381..9d7c619349412 100644 --- a/src/VisualStudio/Core/Def/Implementation/Venus/ContainedDocument.cs +++ b/src/VisualStudio/Core/Def/Implementation/Venus/ContainedDocument.cs @@ -800,10 +800,11 @@ private void AdjustIndentationForSpan( var formattingRules = venusFormattingRules.Concat(Formatter.GetDefaultFormattingRules(document)); - var workspace = document.Project.Solution.Workspace; - var changes = Formatter.GetFormattedTextChanges( + var services = document.Project.Solution.Workspace.Services; + var formatter = document.GetRequiredLanguageService(); + var changes = formatter.GetFormattingResult( root, new TextSpan[] { CommonFormattingHelpers.GetFormattingSpan(root, visibleSpan) }, - workspace, options, formattingRules, CancellationToken.None); + options, services, formattingRules, CancellationToken.None).GetTextChanges(CancellationToken.None); visibleSpans.Add(visibleSpan); var newChanges = FilterTextChanges(document.GetTextSynchronously(CancellationToken.None), visibleSpans, changes.ToReadOnlyCollection()).Where(t => visibleSpan.Contains(t.Span)); diff --git a/src/Workspaces/CSharp/Portable/Indentation/CSharpSmartTokenFormatter.cs b/src/Workspaces/CSharp/Portable/Indentation/CSharpSmartTokenFormatter.cs index 713f9a5144969..a6e8b6760756a 100644 --- a/src/Workspaces/CSharp/Portable/Indentation/CSharpSmartTokenFormatter.cs +++ b/src/Workspaces/CSharp/Portable/Indentation/CSharpSmartTokenFormatter.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Formatting.Rules; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -42,7 +43,7 @@ public CSharpSmartTokenFormatter( } public IList FormatRange( - Workspace workspace, SyntaxToken startToken, SyntaxToken endToken, CancellationToken cancellationToken) + HostWorkspaceServices services, SyntaxToken startToken, SyntaxToken endToken, CancellationToken cancellationToken) { Contract.ThrowIfTrue(startToken.Kind() is SyntaxKind.None or SyntaxKind.EndOfFileToken); Contract.ThrowIfTrue(endToken.Kind() is SyntaxKind.None or SyntaxKind.EndOfFileToken); @@ -60,7 +61,9 @@ public IList FormatRange( smartTokenformattingRules = (new NoLineChangeFormattingRule()).Concat(_formattingRules); } - return Formatter.GetFormattedTextChanges(_root, new TextSpan[] { TextSpan.FromBounds(startToken.SpanStart, endToken.Span.End) }, workspace, _optionSet, smartTokenformattingRules, cancellationToken); + var formatter = services.GetRequiredLanguageService(_root.Language); + return formatter.GetFormattingResult(_root, new[] { TextSpan.FromBounds(startToken.SpanStart, endToken.Span.End) }, _optionSet, services, smartTokenformattingRules, cancellationToken). + GetTextChanges(cancellationToken); } private static bool CloseBraceOfTryOrDoBlock(SyntaxToken endToken) @@ -71,7 +74,7 @@ private static bool CloseBraceOfTryOrDoBlock(SyntaxToken endToken) } public async Task> FormatTokenAsync( - Workspace workspace, SyntaxToken token, CancellationToken cancellationToken) + HostWorkspaceServices services, SyntaxToken token, CancellationToken cancellationToken) { Contract.ThrowIfTrue(token.Kind() is SyntaxKind.None or SyntaxKind.EndOfFileToken); @@ -114,9 +117,9 @@ public async Task> FormatTokenAsync( } } - return Formatter.GetFormattedTextChanges(_root, - new TextSpan[] { TextSpan.FromBounds(adjustedStartPosition, adjustedEndPosition) }, - workspace, _optionSet, smartTokenformattingRules, cancellationToken); + var formatter = services.GetRequiredLanguageService(_root.Language); + return formatter.GetFormattingResult(_root, new[] { TextSpan.FromBounds(adjustedStartPosition, adjustedEndPosition) }, _optionSet, services, smartTokenformattingRules, cancellationToken). + GetTextChanges(cancellationToken); } private class NoLineChangeFormattingRule : AbstractFormattingRule diff --git a/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs b/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs index f2ef1efb52eeb..b51c1fe60254b 100644 --- a/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs +++ b/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs @@ -56,6 +56,12 @@ public static SyntaxGenerator GetGenerator(Workspace workspace, string language) internal static SyntaxGenerator GetGenerator(HostWorkspaceServices services, string language) => services.GetLanguageServices(language).GetRequiredService(); + /// + /// Gets the for the specified language. + /// + internal static SyntaxGenerator GetGenerator(HostWorkspaceServices services, string language) + => services.GetLanguageServices(language).GetService(); + /// /// Gets the for the language corresponding to the document. /// diff --git a/src/Workspaces/Core/Portable/Formatting/Formatter.cs b/src/Workspaces/Core/Portable/Formatting/Formatter.cs index aa55d3352eb74..365543f69a410 100644 --- a/src/Workspaces/Core/Portable/Formatting/Formatter.cs +++ b/src/Workspaces/Core/Portable/Formatting/Formatter.cs @@ -6,7 +6,9 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting.Rules; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.OrganizeImports; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -50,19 +52,9 @@ internal static IEnumerable GetDefaultFormattingRules(Do /// /// Gets the formatting rules that would be applied if left unspecified. /// - internal static IEnumerable GetDefaultFormattingRules(Workspace workspace, string language) + internal static IEnumerable GetDefaultFormattingRules(HostWorkspaceServices services, string language) { - if (workspace == null) - { - throw new ArgumentNullException(nameof(workspace)); - } - - if (language == null) - { - throw new ArgumentNullException(nameof(language)); - } - - var service = workspace.Services.GetLanguageServices(language).GetService(); + var service = services.GetLanguageServices(language).GetService(); if (service != null) { return service.GetDefaultFormattingRules(); @@ -163,9 +155,11 @@ internal static async Task FormatAsync(Document document, SyntaxAnnota /// An optional set of formatting options. If these options are not supplied the current set of options from the workspace will be used. /// An optional cancellation token. /// The formatted tree's root node. + [Obsolete] public static SyntaxNode Format(SyntaxNode node, SyntaxAnnotation annotation, Workspace workspace, OptionSet? options = null, CancellationToken cancellationToken = default) => Format(node, annotation, workspace, options, rules: null, cancellationToken: cancellationToken); + [Obsolete] internal static SyntaxNode Format(SyntaxNode node, SyntaxAnnotation annotation, Workspace workspace, OptionSet? options, IEnumerable? rules, CancellationToken cancellationToken) { if (workspace == null) @@ -198,6 +192,7 @@ internal static SyntaxNode Format(SyntaxNode node, SyntaxAnnotation annotation, /// An optional set of formatting options. If these options are not supplied the current set of options from the workspace will be used. /// An optional cancellation token. /// The formatted tree's root node. + [Obsolete] public static SyntaxNode Format(SyntaxNode node, Workspace workspace, OptionSet? options = null, CancellationToken cancellationToken = default) => Format(node, SpecializedCollections.SingletonEnumerable(node.FullSpan), workspace, options, rules: null, cancellationToken: cancellationToken); @@ -210,6 +205,7 @@ public static SyntaxNode Format(SyntaxNode node, Workspace workspace, OptionSet? /// An optional set of formatting options. If these options are not supplied the current set of options from the workspace will be used. /// An optional cancellation token. /// The formatted tree's root node. + [Obsolete] public static SyntaxNode Format(SyntaxNode node, TextSpan span, Workspace workspace, OptionSet? options = null, CancellationToken cancellationToken = default) => Format(node, SpecializedCollections.SingletonEnumerable(span), workspace, options, rules: null, cancellationToken: cancellationToken); @@ -222,15 +218,18 @@ public static SyntaxNode Format(SyntaxNode node, TextSpan span, Workspace worksp /// An optional set of formatting options. If these options are not supplied the current set of options from the workspace will be used. /// An optional cancellation token. /// The formatted tree's root node. + [Obsolete] public static SyntaxNode Format(SyntaxNode node, IEnumerable? spans, Workspace workspace, OptionSet? options = null, CancellationToken cancellationToken = default) => Format(node, spans, workspace, options, rules: null, cancellationToken: cancellationToken); + [Obsolete] internal static SyntaxNode Format(SyntaxNode node, IEnumerable? spans, Workspace workspace, OptionSet? options, IEnumerable? rules, CancellationToken cancellationToken) { var formattingResult = GetFormattingResult(node, spans, workspace, options, rules, cancellationToken); return formattingResult == null ? node : formattingResult.GetFormattedRoot(cancellationToken); } + [Obsolete] internal static IFormattingResult? GetFormattingResult(SyntaxNode node, IEnumerable? spans, Workspace workspace, OptionSet? options, IEnumerable? rules, CancellationToken cancellationToken) { if (workspace == null) @@ -265,6 +264,7 @@ internal static SyntaxNode Format(SyntaxNode node, IEnumerable? spans, /// An optional set of formatting options. If these options are not supplied the current set of options from the workspace will be used. /// An optional cancellation token. /// The changes necessary to format the tree. + [Obsolete] public static IList GetFormattedTextChanges(SyntaxNode node, Workspace workspace, OptionSet? options = null, CancellationToken cancellationToken = default) => GetFormattedTextChanges(node, SpecializedCollections.SingletonEnumerable(node.FullSpan), workspace, options, rules: null, cancellationToken: cancellationToken); @@ -277,6 +277,7 @@ public static IList GetFormattedTextChanges(SyntaxNode node, Workspa /// An optional set of formatting options. If these options are not supplied the current set of options from the workspace will be used. /// An optional cancellation token. /// The changes necessary to format the tree. + [Obsolete] public static IList GetFormattedTextChanges(SyntaxNode node, TextSpan span, Workspace workspace, OptionSet? options = null, CancellationToken cancellationToken = default) => GetFormattedTextChanges(node, SpecializedCollections.SingletonEnumerable(span), workspace, options, rules: null, cancellationToken: cancellationToken); @@ -289,9 +290,11 @@ public static IList GetFormattedTextChanges(SyntaxNode node, TextSpa /// An optional set of formatting options. If these options are not supplied the current set of options from the workspace will be used. /// An optional cancellation token. /// The changes necessary to format the tree. + [Obsolete] public static IList GetFormattedTextChanges(SyntaxNode node, IEnumerable? spans, Workspace workspace, OptionSet? options = null, CancellationToken cancellationToken = default) => GetFormattedTextChanges(node, spans, workspace, options, rules: null, cancellationToken: cancellationToken); + [Obsolete] internal static IList GetFormattedTextChanges(SyntaxNode node, IEnumerable? spans, Workspace workspace, OptionSet? options, IEnumerable? rules, CancellationToken cancellationToken) { var formattingResult = GetFormattingResult(node, spans, workspace, options, rules, cancellationToken); diff --git a/src/Workspaces/Core/Portable/Indentation/AbstractIndentationService.Indenter.cs b/src/Workspaces/Core/Portable/Indentation/AbstractIndentationService.Indenter.cs index 796e7905ca082..86120f72eabc6 100644 --- a/src/Workspaces/Core/Portable/Indentation/AbstractIndentationService.Indenter.cs +++ b/src/Workspaces/Core/Portable/Indentation/AbstractIndentationService.Indenter.cs @@ -170,7 +170,7 @@ public bool TryGetSmartTokenIndentation(out IndentationResult indentationResult) var sourceText = Tree.GetText(CancellationToken); var formatter = _service.CreateSmartTokenFormatter(this); - var changes = formatter.FormatTokenAsync(Document.Project.Solution.Workspace, token, CancellationToken) + var changes = formatter.FormatTokenAsync(Document.Project.Solution.Workspace.Services, token, CancellationToken) .WaitAndGetResult_CanCallOnBackground(CancellationToken); var updatedSourceText = sourceText.WithChanges(changes); diff --git a/src/Workspaces/Core/Portable/Indentation/ISmartTokenFormatter.cs b/src/Workspaces/Core/Portable/Indentation/ISmartTokenFormatter.cs index 1f350134b09ab..2cab6570ce4c6 100644 --- a/src/Workspaces/Core/Portable/Indentation/ISmartTokenFormatter.cs +++ b/src/Workspaces/Core/Portable/Indentation/ISmartTokenFormatter.cs @@ -5,12 +5,13 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.Indentation { internal interface ISmartTokenFormatter { - Task> FormatTokenAsync(Workspace workspace, SyntaxToken token, CancellationToken cancellationToken); + Task> FormatTokenAsync(HostWorkspaceServices services, SyntaxToken token, CancellationToken cancellationToken); } } diff --git a/src/Workspaces/CoreTest/FormattingTests.cs b/src/Workspaces/CoreTest/FormattingTests.cs index 998368664ed42..27dc2270bb6f6 100644 --- a/src/Workspaces/CoreTest/FormattingTests.cs +++ b/src/Workspaces/CoreTest/FormattingTests.cs @@ -28,7 +28,7 @@ public void TestCSharpFormatting() [Fact, Trait(Traits.Feature, Traits.Features.Formatting)] public void TestCSharpDefaultRules() { - var rules = Formatter.GetDefaultFormattingRules(new AdhocWorkspace(), LanguageNames.CSharp); + var rules = Formatter.GetDefaultFormattingRules(new AdhocWorkspace().Services, LanguageNames.CSharp); Assert.NotNull(rules); Assert.NotEmpty(rules); @@ -54,7 +54,7 @@ End Class [Fact, Trait(Traits.Feature, Traits.Features.Formatting)] public void TestVisualBasicDefaultFormattingRules() { - var rules = Formatter.GetDefaultFormattingRules(new AdhocWorkspace(), LanguageNames.VisualBasic); + var rules = Formatter.GetDefaultFormattingRules(new AdhocWorkspace().Services, LanguageNames.VisualBasic); Assert.NotNull(rules); Assert.NotEmpty(rules); diff --git a/src/Workspaces/VisualBasic/Portable/Indentation/VisualBasicSmartTokenFormatter.vb b/src/Workspaces/VisualBasic/Portable/Indentation/VisualBasicSmartTokenFormatter.vb index aa953c464f3ff..9bccbd262c2c0 100644 --- a/src/Workspaces/VisualBasic/Portable/Indentation/VisualBasicSmartTokenFormatter.vb +++ b/src/Workspaces/VisualBasic/Portable/Indentation/VisualBasicSmartTokenFormatter.vb @@ -7,6 +7,7 @@ Imports Microsoft.CodeAnalysis.Formatting Imports Microsoft.CodeAnalysis.Formatting.Rules Imports Microsoft.CodeAnalysis.Indentation Imports Microsoft.CodeAnalysis.Options +Imports Microsoft.CodeAnalysis.Host Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -32,14 +33,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Indentation Me._root = root End Sub - Public Function FormatTokenAsync(workspace As Workspace, token As SyntaxToken, cancellationToken As CancellationToken) As Tasks.Task(Of IList(Of TextChange)) Implements ISmartTokenFormatter.FormatTokenAsync + Public Function FormatTokenAsync(services As HostWorkspaceServices, token As SyntaxToken, cancellationToken As CancellationToken) As Tasks.Task(Of IList(Of TextChange)) Implements ISmartTokenFormatter.FormatTokenAsync Contract.ThrowIfTrue(token.Kind = SyntaxKind.None OrElse token.Kind = SyntaxKind.EndOfFileToken) ' get previous token Dim previousToken = token.GetPreviousToken() Dim spans = SpecializedCollections.SingletonEnumerable(TextSpan.FromBounds(previousToken.SpanStart, token.Span.End)) - Return Task.FromResult(Formatter.GetFormattedTextChanges(_root, spans, workspace, _optionSet, _formattingRules, cancellationToken)) + Dim formatter = services.GetRequiredLanguageService(Of ISyntaxFormattingService)(_root.Language) + Return Task.FromResult(formatter.GetFormattingResult(_root, spans, _optionSet, services, _formattingRules, cancellationToken).GetTextChanges(cancellationToken)) End Function End Class End Namespace From 2f2cac7f83b9df789d4e9fb57173d992d8cd1ac7 Mon Sep 17 00:00:00 2001 From: tmat Date: Wed, 22 Dec 2021 13:21:57 -0800 Subject: [PATCH 03/13] SyntaxFormattingOptions --- ...CSharpRemoveUnusedValuesCodeFixProvider.cs | 7 ++ ...stractRemoveUnusedValuesCodeFixProvider.cs | 33 ++++-- ...UseConditionalExpressionCodeFixProvider.cs | 24 ++-- ...lBasicRemoveUnusedValuesCodeFixProvider.vb | 8 ++ .../Analyzers/AbstractFormattingAnalyzer.cs | 6 +- .../Analyzers/Formatting/FormatterHelper.cs | 106 +++-------------- .../Analyzers/FormattingAnalyzerHelper.cs | 13 +-- .../Core/CodeFixes/FormattingCodeFixHelper.cs | 17 +-- .../CodeFixes/FormattingCodeFixProvider.cs | 16 +-- .../AutomaticLineEnderCommandHandler.cs | 3 +- ...utomaticLineEnderCommandHandler_Helpers.cs | 20 +++- .../CSharpDecompiledSourceService.cs | 6 +- .../ExtractClass/ExtractClassTests.cs | 3 +- .../Formatting/FormattingEngineTests.cs | 17 ++- .../Indentation/CSharpFormatterTestsBase.cs | 4 +- .../SmartIndenterEnterOnTokenTests.cs | 4 +- .../SmartTokenFormatterFormatRangeTests.cs | 13 +-- .../Extensions/ITextSnapshotExtensions.cs | 4 +- .../TestExtractInterfaceOptions.cs | 4 +- .../Formatting/CoreFormatterTestsBase.cs | 43 +++---- .../CaseCorrectionServiceTests.vb | 3 +- .../SmartTokenFormatter_FormatTokenTests.vb | 3 +- .../VisualBasicFormatterTestBase.vb | 7 +- .../CurlyBraceCompletionService.cs | 11 +- .../CSharpSuppressionCodeFixProvider.cs | 17 +-- .../CSharpFormattingInteractionService.cs | 48 ++++---- .../AbstractChangeSignatureService.cs | 5 +- ...rovider.GlobalSuppressMessageCodeAction.cs | 7 +- ...r.GlobalSuppressMessageFixAllCodeAction.cs | 7 +- ...uppressionCodeFixProvider.PragmaHelpers.cs | 18 +-- ...CodeFixProvider.PragmaWarningCodeAction.cs | 17 ++- ...FixProvider.RemoveSuppressionCodeAction.cs | 4 +- ...ider.RemoveSuppressionCodeAction_Pragma.cs | 26 +++-- .../AbstractSuppressionCodeFixProvider.cs | 13 ++- ...AbstractAddMissingImportsFeatureService.cs | 13 +-- .../AbstractChangeNamespaceService.cs | 4 +- .../ExtractClassWithDialogCodeAction.cs | 2 +- .../IExtractClassOptionsService.cs | 3 +- .../AbstractExtractInterfaceService.cs | 10 +- .../IExtractInterfaceOptionsService.cs | 4 +- .../ExtractMethod/ExtractMethodResult.cs | 8 +- .../Formatting/FormattingCodeFixProvider.cs | 4 +- .../FormattingDiagnosticAnalyzer.cs | 6 +- .../AbstractMetadataAsSourceService.cs | 8 +- .../Shared/Utilities/ExtractTypeHelpers.cs | 4 +- .../AbstractUseAutoPropertyCodeFixProvider.cs | 4 +- ...oadBaseCodeFixProvider.AddKeywordAction.vb | 6 +- .../VisualBasicSuppressionCodeFixProvider.vb | 29 +++-- .../OmniSharpExtractClassOptionsService.cs | 3 +- ...OmniSharpExtractInterfaceOptionsService.cs | 12 +- .../IdeBenchmarks/FormatterBenchmarks.cs | 8 +- .../Implementation/AbstractEditorFactory.cs | 6 +- .../VisualStudioExtractClassOptionsService.cs | 13 ++- ...ualStudioExtractInterfaceOptionsService.cs | 6 +- ...actLanguageService`2.IVsLanguageTextOps.cs | 4 +- .../Implementation/Venus/ContainedDocument.cs | 17 +-- .../Venus/ContainedLanguageCodeSupport.cs | 5 +- .../CodeModel/AbstractCodeModelService.cs | 17 ++- .../CSharpIndentationService.Indenter.cs | 8 +- .../Indentation/CSharpSmartTokenFormatter.cs | 19 ++-- .../CodeGeneration/SyntaxGeneratorTests.cs | 3 +- .../FormattingElasticTriviaTests.cs | 18 +-- .../Formatting/FormattingMultipleSpanTests.cs | 3 +- .../CSharpTest/Formatting/FormattingTests.cs | 9 +- .../Formatting/FormattingTreeEditTests.cs | 16 ++- .../Formatting/FormattingTriviaTests.cs | 18 ++- .../CodeCleanup/AbstractCodeCleanerService.cs | 11 +- .../Core/Portable/CodeCleanup/CodeCleaner.cs | 6 +- .../CodeCleanup/ICodeCleanerService.cs | 6 +- .../Providers/FormatCodeCleanupProvider.cs | 9 +- .../Providers/ICodeCleanupProvider.cs | 6 +- .../SimplificationCodeCleanupProvider.cs | 6 +- .../Core/Portable/Editing/SyntaxGenerator.cs | 5 +- .../Formatting/AbstractFormattingService.cs | 8 +- .../Core/Portable/Formatting/Formatter.cs | 107 +++++++++--------- .../CodeCleanup/MockCodeCleanupProvider.cs | 10 +- .../CoreTest/Editing/SyntaxEditorTests.cs | 6 +- src/Workspaces/CoreTest/FormattingTests.cs | 15 ++- .../Formatting/FormattingTestBase.cs | 21 ++-- .../AbstractSyntaxFormattingService.cs | 32 ++++-- .../Core/Formatting/FormattingExtensions.cs | 25 ++-- .../Formatting/ISyntaxFormattingService.cs | 29 +++-- .../Core/Utilities/CommonFormattingHelpers.cs | 2 +- .../CSharpRemoveUnnecessaryImportsService.cs | 35 ++++-- .../AbstractTokensCodeCleanupProvider.vb | 6 +- .../CaseCorrectionCodeCleanupProvider.vb | 6 +- ...ModifiersOrOperatorsCodeCleanupProvider.vb | 6 +- ...saryLineContinuationCodeCleanupProvider.vb | 6 +- .../VisualBasicIndentationService.Indenter.vb | 8 +- .../VisualBasicSmartTokenFormatter.vb | 10 +- .../Formatting/FormattingTests.vb | 30 +++-- .../VisualBasicFormattingTestBase.vb | 10 +- 92 files changed, 685 insertions(+), 563 deletions(-) diff --git a/src/Analyzers/CSharp/CodeFixes/RemoveUnusedParametersAndValues/CSharpRemoveUnusedValuesCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/RemoveUnusedParametersAndValues/CSharpRemoveUnusedValuesCodeFixProvider.cs index 24f21e1674b39..d5479027826e2 100644 --- a/src/Analyzers/CSharp/CodeFixes/RemoveUnusedParametersAndValues/CSharpRemoveUnusedValuesCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/RemoveUnusedParametersAndValues/CSharpRemoveUnusedValuesCodeFixProvider.cs @@ -10,9 +10,11 @@ using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Extensions; +using Microsoft.CodeAnalysis.CSharp.Formatting; using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.RemoveUnusedParametersAndValues; @@ -31,6 +33,11 @@ public CSharpRemoveUnusedValuesCodeFixProvider() { } +#if CODE_STYLE + protected override ISyntaxFormattingService GetSyntaxFormattingService() + => CSharpSyntaxFormattingService.Instance; +#endif + protected override BlockSyntax WrapWithBlockIfNecessary(IEnumerable statements) => SyntaxFactory.Block(statements); diff --git a/src/Analyzers/Core/CodeFixes/RemoveUnusedParametersAndValues/AbstractRemoveUnusedValuesCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/RemoveUnusedParametersAndValues/AbstractRemoveUnusedValuesCodeFixProvider.cs index c75a0632e47db..ca46244be1c03 100644 --- a/src/Analyzers/Core/CodeFixes/RemoveUnusedParametersAndValues/AbstractRemoveUnusedValuesCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/RemoveUnusedParametersAndValues/AbstractRemoveUnusedValuesCodeFixProvider.cs @@ -67,6 +67,9 @@ public sealed override ImmutableArray FixableDiagnosticIds internal sealed override CodeFixCategory CodeFixCategory => CodeFixCategory.CodeQuality; +#if CODE_STYLE + protected abstract ISyntaxFormattingService GetSyntaxFormattingService(); +#endif /// /// Method to update the identifier token for the local/parameter declaration or reference /// that was flagged as an unused value write by the analyzer. @@ -269,15 +272,21 @@ private static async Task PreprocessDocumentAsync(Document document, I protected sealed override async Task FixAllAsync(Document document, ImmutableArray diagnostics, SyntaxEditor editor, CancellationToken cancellationToken) { +#if CODE_STYLE + var provider = GetSyntaxFormattingService(); + var options = SyntaxFormattingOptions.Create(document.Project.AnalyzerOptions.GetAnalyzerOptionSet(editor.OriginalRoot.SyntaxTree, cancellationToken)); +#else + var provider = document.Project.Solution.Workspace.Services; + var options = await SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false); +#endif var preprocessedDocument = await PreprocessDocumentAsync(document, diagnostics, cancellationToken).ConfigureAwait(false); - var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); var newRoot = await GetNewRootAsync(preprocessedDocument, options, diagnostics, cancellationToken).ConfigureAwait(false); editor.ReplaceNode(editor.OriginalRoot, newRoot); } private async Task GetNewRootAsync( Document document, - OptionSet options, + SyntaxFormattingOptions options, ImmutableArray diagnostics, CancellationToken cancellationToken) { @@ -721,7 +730,7 @@ bool ShouldRemoveStatement(TLocalDeclarationStatementSyntax localDeclarationStat private async Task PostProcessDocumentAsync( Document document, - OptionSet options, + SyntaxFormattingOptions options, SyntaxNode currentRoot, string diagnosticId, UnusedValuePreference preference, @@ -749,10 +758,10 @@ private async Task PostProcessDocumentAsync( } private static async Task PostProcessDocumentCoreAsync( - Func> processMemberDeclarationAsync, + Func> processMemberDeclarationAsync, SyntaxNode currentRoot, Document document, - OptionSet options, + SyntaxFormattingOptions options, CancellationToken cancellationToken) { // Process each member declaration which had at least one diagnostic reported in the original tree and hence @@ -780,7 +789,7 @@ private static async Task PostProcessDocumentCoreAsync( /// This is needed to prevent the code fix/FixAll from generating code with /// multiple local variables named '_', which is a compiler error. /// - private async Task ReplaceDiscardDeclarationsWithAssignmentsAsync(SyntaxNode memberDeclaration, Document document, OptionSet options, CancellationToken cancellationToken) + private async Task ReplaceDiscardDeclarationsWithAssignmentsAsync(SyntaxNode memberDeclaration, Document document, SyntaxFormattingOptions options, CancellationToken cancellationToken) { var service = document.GetLanguageService(); if (service == null) @@ -799,10 +808,10 @@ private async Task ReplaceDiscardDeclarationsWithAssignmentsAsync(Sy /// local declaration statements annotated with /// whose declared local is no longer used removed. /// - private static async Task AdjustLocalDeclarationsAsync( + private async Task AdjustLocalDeclarationsAsync( SyntaxNode memberDeclaration, Document document, - OptionSet options, + SyntaxFormattingOptions options, CancellationToken cancellationToken) { var moveDeclarationService = document.GetRequiredLanguageService(); @@ -826,7 +835,13 @@ private static async Task AdjustLocalDeclarationsAsync( var rootWithTrackedNodes = root.TrackNodes(originalDeclStatementsToMoveOrRemove); // Run formatter prior to invoking IMoveDeclarationNearReferenceService. - rootWithTrackedNodes = Formatter.Format(rootWithTrackedNodes, originalDeclStatementsToMoveOrRemove.Select(s => s.Span), document.Project.Solution.Workspace, options, cancellationToken: cancellationToken); +#if CODE_STYLE + var provider = GetSyntaxFormattingService(); + rootWithTrackedNodes = FormatterHelper.Format(rootWithTrackedNodes, originalDeclStatementsToMoveOrRemove.Select(s => s.Span), provider, options, rules: null, cancellationToken); +#else + var provider = document.Project.Solution.Workspace.Services; + rootWithTrackedNodes = Formatter.Format(rootWithTrackedNodes, originalDeclStatementsToMoveOrRemove.Select(s => s.Span), provider, options, rules: null, cancellationToken); +#endif document = document.WithSyntaxRoot(rootWithTrackedNodes); await OnDocumentUpdatedAsync().ConfigureAwait(false); diff --git a/src/Analyzers/Core/CodeFixes/UseConditionalExpression/AbstractUseConditionalExpressionCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseConditionalExpression/AbstractUseConditionalExpressionCodeFixProvider.cs index 385a3eae9ab3d..41a90a1ceb2a6 100644 --- a/src/Analyzers/Core/CodeFixes/UseConditionalExpression/AbstractUseConditionalExpressionCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseConditionalExpression/AbstractUseConditionalExpressionCodeFixProvider.cs @@ -17,6 +17,12 @@ using Microsoft.CodeAnalysis.Simplification; using static Microsoft.CodeAnalysis.UseConditionalExpression.UseConditionalExpressionCodeFixHelpers; +#if CODE_STYLE +using Formatter = Microsoft.CodeAnalysis.Formatting.FormatterHelper; +#else +using Formatter = Microsoft.CodeAnalysis.Formatting.Formatter; +#endif + namespace Microsoft.CodeAnalysis.UseConditionalExpression { internal abstract class AbstractUseConditionalExpressionCodeFixProvider< @@ -70,21 +76,15 @@ await FixOneAsync( // annotation on it. var rules = new List { GetMultiLineFormattingRule() }; - var options = document.Project.AnalyzerOptions.GetAnalyzerOptionSet(root.SyntaxTree, cancellationToken); - #if CODE_STYLE - var formattedRoot = FormatterHelper.Format(changedRoot, - GetSyntaxFormattingService(), - SpecializedFormattingAnnotation, - options, - rules, cancellationToken); + var provider = GetSyntaxFormattingService(); + var options = SyntaxFormattingOptions.Create(document.Project.AnalyzerOptions.GetAnalyzerOptionSet(root.SyntaxTree, cancellationToken)); #else - var formattedRoot = Formatter.Format(changedRoot, - SpecializedFormattingAnnotation, - document.Project.Solution.Workspace, - options, - rules, cancellationToken); + var provider = document.Project.Solution.Workspace.Services; + var options = await SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false); #endif + var formattedRoot = Formatter.Format(changedRoot, SpecializedFormattingAnnotation, provider, options, rules, cancellationToken); + changedRoot = formattedRoot; editor.ReplaceNode(root, changedRoot); diff --git a/src/Analyzers/VisualBasic/CodeFixes/RemoveUnusedParametersAndValues/VisualBasicRemoveUnusedValuesCodeFixProvider.vb b/src/Analyzers/VisualBasic/CodeFixes/RemoveUnusedParametersAndValues/VisualBasicRemoveUnusedValuesCodeFixProvider.vb index 0dabee3281c7b..a751f08efa9a1 100644 --- a/src/Analyzers/VisualBasic/CodeFixes/RemoveUnusedParametersAndValues/VisualBasicRemoveUnusedValuesCodeFixProvider.vb +++ b/src/Analyzers/VisualBasic/CodeFixes/RemoveUnusedParametersAndValues/VisualBasicRemoveUnusedValuesCodeFixProvider.vb @@ -6,8 +6,10 @@ Imports System.Composition Imports System.Diagnostics.CodeAnalysis Imports Microsoft.CodeAnalysis.CodeFixes Imports Microsoft.CodeAnalysis.Editing +Imports Microsoft.CodeAnalysis.Formatting Imports Microsoft.CodeAnalysis.LanguageServices Imports Microsoft.CodeAnalysis.RemoveUnusedParametersAndValues +Imports Microsoft.CodeAnalysis.VisualBasic.Formatting Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnusedParametersAndValues @@ -23,6 +25,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnusedParametersAndValues Public Sub New() End Sub +#If CODE_STYLE Then + Protected Overrides Function GetSyntaxFormattingService() As ISyntaxFormattingService + Return VisualBasicSyntaxFormattingService.Instance + End Function +#End If + Protected Overrides Function WrapWithBlockIfNecessary(statements As IEnumerable(Of StatementSyntax)) As StatementSyntax ' Unreachable code path as VB statements don't need to be wrapped in special BlockSyntax. Throw ExceptionUtilities.Unreachable diff --git a/src/CodeStyle/Core/Analyzers/AbstractFormattingAnalyzer.cs b/src/CodeStyle/Core/Analyzers/AbstractFormattingAnalyzer.cs index 31b9440b912d8..3b51c026c3ec0 100644 --- a/src/CodeStyle/Core/Analyzers/AbstractFormattingAnalyzer.cs +++ b/src/CodeStyle/Core/Analyzers/AbstractFormattingAnalyzer.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Collections.Immutable; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting; @@ -36,8 +34,8 @@ protected sealed override void InitializeWorker(AnalysisContext context) private void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) { - var analyzerConfigOptions = context.Options.AnalyzerConfigOptionsProvider.GetOptions(context.Tree); - FormattingAnalyzerHelper.AnalyzeSyntaxTree(context, SyntaxFormattingService, Descriptor, analyzerConfigOptions); + var options = SyntaxFormattingOptions.Create(context.Options.AnalyzerConfigOptionsProvider.GetOptions(context.Tree)); + FormattingAnalyzerHelper.AnalyzeSyntaxTree(context, SyntaxFormattingService, Descriptor, options); } } } diff --git a/src/CodeStyle/Core/Analyzers/Formatting/FormatterHelper.cs b/src/CodeStyle/Core/Analyzers/Formatting/FormatterHelper.cs index b9537b346afeb..774141963d199 100644 --- a/src/CodeStyle/Core/Analyzers/Formatting/FormatterHelper.cs +++ b/src/CodeStyle/Core/Analyzers/Formatting/FormatterHelper.cs @@ -2,17 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - -using System; using System.Collections.Generic; using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.Text; -using Microsoft.CodeAnalysis.Diagnostics; using Roslyn.Utilities; -using OptionSet = Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptions; using static Microsoft.CodeAnalysis.Formatting.FormattingExtensions; namespace Microsoft.CodeAnalysis.Formatting @@ -26,50 +20,7 @@ internal static class FormatterHelper /// Gets the formatting rules that would be applied if left unspecified. /// internal static IEnumerable GetDefaultFormattingRules(ISyntaxFormattingService syntaxFormattingService) - { - if (syntaxFormattingService != null) - { - return syntaxFormattingService.GetDefaultFormattingRules(); - } - else - { - return SpecializedCollections.EmptyEnumerable(); - } - } - - /// - /// Formats the whitespace in an area of a document corresponding to a text span. - /// - /// The document to format. - /// The span of the document's text to format. - /// An optional set of formatting options. If these options are not supplied the current set of options from the document's workspace will be used. - /// An optional cancellation token. - /// The formatted document. - public static Task FormatAsync(SyntaxTree syntaxTree, ISyntaxFormattingService syntaxFormattingService, TextSpan span, OptionSet options, CancellationToken cancellationToken) - => FormatAsync(syntaxTree, syntaxFormattingService, SpecializedCollections.SingletonEnumerable(span), options, cancellationToken); - - /// - /// Formats the whitespace in areas of a document corresponding to multiple non-overlapping spans. - /// - /// The document to format. - /// The spans of the document's text to format. - /// An optional set of formatting options. If these options are not supplied the current set of options from the document's workspace will be used. - /// An optional cancellation token. - /// The formatted document. - public static Task FormatAsync(SyntaxTree syntaxTree, ISyntaxFormattingService syntaxFormattingService, IEnumerable spans, OptionSet options, CancellationToken cancellationToken) - => FormatAsync(syntaxTree, syntaxFormattingService, spans, options, rules: null, cancellationToken); - - internal static async Task FormatAsync(SyntaxTree syntaxTree, ISyntaxFormattingService syntaxFormattingService, IEnumerable spans, OptionSet options, IEnumerable rules, CancellationToken cancellationToken) - { - if (syntaxTree == null) - { - throw new ArgumentNullException(nameof(syntaxTree)); - } - - var root = await syntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); - var documentOptions = options ?? DictionaryAnalyzerConfigOptions.Empty; - return syntaxTree.WithRootAndOptions(Format(root, syntaxFormattingService, spans, documentOptions, rules, cancellationToken), syntaxTree.Options); - } + => syntaxFormattingService.GetDefaultFormattingRules(); /// /// Formats the whitespace of a syntax tree. @@ -78,8 +29,11 @@ internal static async Task FormatAsync(SyntaxTree syntaxTree, ISynta /// An optional set of formatting options. If these options are not supplied the current set of options from the workspace will be used. /// An optional cancellation token. /// The formatted tree's root node. - public static SyntaxNode Format(SyntaxNode node, ISyntaxFormattingService syntaxFormattingService, OptionSet options, CancellationToken cancellationToken) - => Format(node, syntaxFormattingService, SpecializedCollections.SingletonEnumerable(node.FullSpan), options, rules: null, cancellationToken: cancellationToken); + public static SyntaxNode Format(SyntaxNode node, ISyntaxFormattingService syntaxFormattingService, SyntaxFormattingOptions options, CancellationToken cancellationToken) + => Format(node, SpecializedCollections.SingletonEnumerable(node.FullSpan), syntaxFormattingService, options, rules: null, cancellationToken: cancellationToken); + + public static SyntaxNode Format(SyntaxNode node, TextSpan spanToFormat, ISyntaxFormattingService syntaxFormattingService, SyntaxFormattingOptions options, CancellationToken cancellationToken) + => Format(node, SpecializedCollections.SingletonEnumerable(spanToFormat), syntaxFormattingService, options, rules: null, cancellationToken: cancellationToken); /// /// Formats the whitespace of a syntax tree. @@ -89,45 +43,17 @@ public static SyntaxNode Format(SyntaxNode node, ISyntaxFormattingService syntax /// An optional set of formatting options. If these options are not supplied the current set of options from the workspace will be used. /// An optional cancellation token. /// The formatted tree's root node. - public static SyntaxNode Format(SyntaxNode node, ISyntaxFormattingService syntaxFormattingService, SyntaxAnnotation annotation, OptionSet options, IEnumerable rules, CancellationToken cancellationToken) - { - var spans = (annotation == SyntaxAnnotation.ElasticAnnotation) - ? GetElasticSpans(node) - : GetAnnotatedSpans(node, annotation); - - return Format(node, syntaxFormattingService, spans, options, rules, cancellationToken: cancellationToken); - } - - internal static SyntaxNode Format(SyntaxNode node, ISyntaxFormattingService syntaxFormattingService, IEnumerable spans, OptionSet options, IEnumerable rules, CancellationToken cancellationToken) - { - var formattingResult = GetFormattingResult(node, syntaxFormattingService, spans, options, rules, cancellationToken); - return formattingResult == null ? node : formattingResult.GetFormattedRoot(cancellationToken); - } - - internal static IList GetFormattedTextChanges(SyntaxNode node, ISyntaxFormattingService syntaxFormattingService, IEnumerable spans, OptionSet options, IEnumerable rules, CancellationToken cancellationToken) - { - var formattingResult = GetFormattingResult(node, syntaxFormattingService, spans, options, rules, cancellationToken); - return formattingResult == null - ? SpecializedCollections.EmptyList() - : formattingResult.GetTextChanges(cancellationToken); - } + public static SyntaxNode Format(SyntaxNode node, SyntaxAnnotation annotation, ISyntaxFormattingService syntaxFormattingService, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken) + => Format(node, GetAnnotatedSpans(node, annotation), syntaxFormattingService, options, rules, cancellationToken: cancellationToken); - internal static IFormattingResult GetFormattingResult(SyntaxNode node, ISyntaxFormattingService syntaxFormattingService, IEnumerable spans, OptionSet options, IEnumerable rules, CancellationToken cancellationToken) - { - if (node == null) - { - throw new ArgumentNullException(nameof(node)); - } + internal static SyntaxNode Format(SyntaxNode node, IEnumerable spans, ISyntaxFormattingService syntaxFormattingService, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken) + => GetFormattingResult(node, spans, syntaxFormattingService, options, rules, cancellationToken).GetFormattedRoot(cancellationToken); - if (syntaxFormattingService is null) - { - return null; - } + internal static IList GetFormattedTextChanges(SyntaxNode node, IEnumerable spans, ISyntaxFormattingService syntaxFormattingService, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken) + => GetFormattingResult(node, spans, syntaxFormattingService, options, rules, cancellationToken).GetTextChanges(cancellationToken); - options ??= DictionaryAnalyzerConfigOptions.Empty; - spans ??= SpecializedCollections.SingletonEnumerable(node.FullSpan); - return syntaxFormattingService.Format(node, spans, shouldUseFormattingSpanCollapse: false, options, rules, cancellationToken); - } + internal static IFormattingResult GetFormattingResult(SyntaxNode node, IEnumerable spans, ISyntaxFormattingService syntaxFormattingService, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken) + => syntaxFormattingService.GetFormattingResult(node, spans, options with { ShouldUseFormattingSpanCollapse = false }, rules, cancellationToken); /// /// Determines the changes necessary to format the whitespace of a syntax tree. @@ -136,7 +62,7 @@ internal static IFormattingResult GetFormattingResult(SyntaxNode node, ISyntaxFo /// An optional set of formatting options. If these options are not supplied the current set of options from the workspace will be used. /// An optional cancellation token. /// The changes necessary to format the tree. - public static IList GetFormattedTextChanges(SyntaxNode node, ISyntaxFormattingService syntaxFormattingService, OptionSet options, CancellationToken cancellationToken) - => GetFormattedTextChanges(node, syntaxFormattingService, SpecializedCollections.SingletonEnumerable(node.FullSpan), options, rules: null, cancellationToken: cancellationToken); + public static IList GetFormattedTextChanges(SyntaxNode node, ISyntaxFormattingService syntaxFormattingService, SyntaxFormattingOptions options, CancellationToken cancellationToken) + => GetFormattedTextChanges(node, SpecializedCollections.SingletonEnumerable(node.FullSpan), syntaxFormattingService, options, rules: null, cancellationToken: cancellationToken); } } diff --git a/src/CodeStyle/Core/Analyzers/FormattingAnalyzerHelper.cs b/src/CodeStyle/Core/Analyzers/FormattingAnalyzerHelper.cs index 0913f35235778..e6a0dd174a082 100644 --- a/src/CodeStyle/Core/Analyzers/FormattingAnalyzerHelper.cs +++ b/src/CodeStyle/Core/Analyzers/FormattingAnalyzerHelper.cs @@ -5,29 +5,28 @@ #nullable disable using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Text; #if CODE_STYLE using Formatter = Microsoft.CodeAnalysis.Formatting.FormatterHelper; -using FormatterState = Microsoft.CodeAnalysis.Formatting.ISyntaxFormattingService; -using OptionSet = Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptions; +using FormattingProvider = Microsoft.CodeAnalysis.Formatting.ISyntaxFormattingService; #else -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Formatting; -using FormatterState = Microsoft.CodeAnalysis.Workspace; +using FormattingProvider = Microsoft.CodeAnalysis.Host.HostWorkspaceServices; #endif namespace Microsoft.CodeAnalysis.CodeStyle { internal static class FormattingAnalyzerHelper { - internal static void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context, FormatterState formatterState, DiagnosticDescriptor descriptor, OptionSet options) + internal static void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context, FormattingProvider formattingProvider, DiagnosticDescriptor descriptor, SyntaxFormattingOptions options) { var tree = context.Tree; var cancellationToken = context.CancellationToken; var oldText = tree.GetText(cancellationToken); - var formattingChanges = Formatter.GetFormattedTextChanges(tree.GetRoot(cancellationToken), formatterState, options, cancellationToken); + + var formattingChanges = Formatter.GetFormattedTextChanges(tree.GetRoot(cancellationToken), formattingProvider, options, cancellationToken); // formattingChanges could include changes that impact a larger section of the original document than // necessary. Before reporting diagnostics, process the changes to minimize the span of individual diff --git a/src/CodeStyle/Core/CodeFixes/FormattingCodeFixHelper.cs b/src/CodeStyle/Core/CodeFixes/FormattingCodeFixHelper.cs index e3b7be03eac26..c6f074a545a51 100644 --- a/src/CodeStyle/Core/CodeFixes/FormattingCodeFixHelper.cs +++ b/src/CodeStyle/Core/CodeFixes/FormattingCodeFixHelper.cs @@ -2,27 +2,24 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Text; #if CODE_STYLE using Formatter = Microsoft.CodeAnalysis.Formatting.FormatterHelper; -using FormatterState = Microsoft.CodeAnalysis.Formatting.ISyntaxFormattingService; -using OptionSet = Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptions; +using FormattingProvider = Microsoft.CodeAnalysis.Formatting.ISyntaxFormattingService; #else -using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Options; -using FormatterState = Microsoft.CodeAnalysis.Workspace; +using FormattingProvider = Microsoft.CodeAnalysis.Host.HostWorkspaceServices; #endif namespace Microsoft.CodeAnalysis { internal static class FormattingCodeFixHelper { - internal static async Task FixOneAsync(SyntaxTree syntaxTree, FormatterState formatterState, OptionSet options, Diagnostic diagnostic, CancellationToken cancellationToken) + internal static async Task FixOneAsync(SyntaxTree syntaxTree, FormattingProvider formattingProvider, SyntaxFormattingOptions options, Diagnostic diagnostic, CancellationToken cancellationToken) { // The span to format is the full line(s) containing the diagnostic var text = await syntaxTree.GetTextAsync(cancellationToken).ConfigureAwait(false); @@ -33,11 +30,7 @@ internal static async Task FixOneAsync(SyntaxTree syntaxTree, Format text.Lines[diagnosticLinePositionSpan.End.Line].End); var root = await syntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); -#if CODE_STYLE - var formattedRoot = Formatter.Format(root, formatterState, new[] { spanToFormat }, options, Formatter.GetDefaultFormattingRules(formatterState), cancellationToken); -#else - var formattedRoot = Formatter.Format(root, spanToFormat, formatterState, options, cancellationToken); -#endif + var formattedRoot = Formatter.Format(root, spanToFormat, formattingProvider, options, cancellationToken); return syntaxTree.WithRootAndOptions(formattedRoot, syntaxTree.Options); } diff --git a/src/CodeStyle/Core/CodeFixes/FormattingCodeFixProvider.cs b/src/CodeStyle/Core/CodeFixes/FormattingCodeFixProvider.cs index dbffe192f1881..b2782fc2ace61 100644 --- a/src/CodeStyle/Core/CodeFixes/FormattingCodeFixProvider.cs +++ b/src/CodeStyle/Core/CodeFixes/FormattingCodeFixProvider.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; @@ -11,16 +9,14 @@ using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Shared.Extensions; #if CODE_STYLE -using OptionSet = Microsoft.CodeAnalysis.Diagnostics.AnalyzerConfigOptions; using Formatter = Microsoft.CodeAnalysis.Formatting.FormatterHelper; #endif namespace Microsoft.CodeAnalysis.CodeStyle { - using ISyntaxFormattingService = ISyntaxFormattingService; - internal abstract class AbstractFormattingCodeFixProvider : CodeFixProvider { public sealed override ImmutableArray FixableDiagnosticIds @@ -46,17 +42,17 @@ public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) private async Task FixOneAsync(CodeFixContext context, Diagnostic diagnostic, CancellationToken cancellationToken) { var options = await GetOptionsAsync(context.Document, cancellationToken).ConfigureAwait(false); - var tree = await context.Document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); + var tree = await context.Document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var updatedTree = await FormattingCodeFixHelper.FixOneAsync(tree, SyntaxFormattingService, options, diagnostic, cancellationToken).ConfigureAwait(false); return context.Document.WithText(await updatedTree.GetTextAsync(cancellationToken).ConfigureAwait(false)); } - private static async Task GetOptionsAsync(Document document, CancellationToken cancellationToken) + private static async Task GetOptionsAsync(Document document, CancellationToken cancellationToken) { - var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); + var tree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var analyzerConfigOptions = document.Project.AnalyzerOptions.AnalyzerConfigOptionsProvider.GetOptions(tree); - return analyzerConfigOptions; + return SyntaxFormattingOptions.Create(analyzerConfigOptions); } public sealed override FixAllProvider GetFixAllProvider() @@ -64,7 +60,7 @@ public sealed override FixAllProvider GetFixAllProvider() { var cancellationToken = context.CancellationToken; var options = await GetOptionsAsync(document, cancellationToken).ConfigureAwait(false); - var syntaxRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var syntaxRoot = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var updatedSyntaxRoot = Formatter.Format(syntaxRoot, this.SyntaxFormattingService, options, cancellationToken); return document.WithSyntaxRoot(updatedSyntaxRoot); }); diff --git a/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs b/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs index 61453b3de49a5..0962222aa5239 100644 --- a/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs +++ b/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler.cs @@ -108,13 +108,12 @@ protected override Document FormatAndApplyBasedOnEndToken(Document document, int return document; } - var options = document.GetOptionsAsync(cancellationToken).WaitAndGetResult(cancellationToken); + var options = SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).WaitAndGetResult(cancellationToken); var formatter = document.GetRequiredLanguageService(); var changes = formatter.GetFormattingResult( root, SpecializedCollections.SingletonCollection(CommonFormattingHelpers.GetFormattingSpan(root, span.Value)), options, - document.Project.Solution.Workspace.Services, rules: null, cancellationToken).GetTextChanges(cancellationToken); diff --git a/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler_Helpers.cs b/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler_Helpers.cs index 8584f95d56e60..282c438001c49 100644 --- a/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler_Helpers.cs +++ b/src/EditorFeatures/CSharp/AutomaticCompletion/AutomaticLineEnderCommandHandler_Helpers.cs @@ -27,9 +27,11 @@ private static (SyntaxNode newRoot, int nextCaretPosition) ReplaceStatementOwner SyntaxNode newNode, SyntaxNode anchorNode, ImmutableArray nodesToInsert, + DocumentOptionSet documentOptions, CancellationToken cancellationToken) { - var rootEditor = new SyntaxEditor(root, document.Project.Solution.Workspace.Services); + var services = document.Project.Solution.Workspace.Services; + var rootEditor = new SyntaxEditor(root, services); // 1. Insert the node before anchor node rootEditor.InsertAfter(anchorNode, nodesToInsert); @@ -40,11 +42,15 @@ private static (SyntaxNode newRoot, int nextCaretPosition) ReplaceStatementOwner // 4. Format the new node so that the inserted braces/blocks would have correct indentation and formatting. var newNodeAfterInsertion = newRoot.GetAnnotatedNodes(s_replacementNodeAnnotation).Single(); + + var options = SyntaxFormattingOptions.Create(documentOptions, services, root.Language); + var formattedNewRoot = Formatter.Format( newRoot, newNodeAfterInsertion.Span, - document.Project.Solution.Workspace, - cancellationToken: cancellationToken); + services, + options, + cancellationToken); // 4. Use the annotation to find the end of the open brace, it would be the new caret position var nextCaretPosition = formattedNewRoot.GetAnnotatedTokens(s_openBracePositionAnnotation).Single().Span.End; @@ -70,11 +76,11 @@ private static SyntaxNode ReplaceNodeAndFormat( var newNodeAfterInsertion = newRoot.GetAnnotatedNodes(s_replacementNodeAnnotation).Single(); // 4. Format the new node so that the inserted braces/blocks would have correct indentation and formatting. - var options = document.GetOptionsAsync(cancellationToken).WaitAndGetResult(cancellationToken); + var options = SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).WaitAndGetResult(cancellationToken); var formattedNewRoot = Formatter.Format( newRoot, newNodeAfterInsertion.Span, - document.Project.Solution.Workspace, + document.Project.Solution.Workspace.Services, options, cancellationToken: cancellationToken); return formattedNewRoot; @@ -145,6 +151,7 @@ WhileStatementSyntax or ForEachStatementSyntax or ForStatementSyntax or LockStat newNode: AddBlockToEmbeddedStatementOwner(embeddedStatementOwner, documentOptions), anchorNode: embeddedStatementOwner, nodesToInsert: ImmutableArray.Empty.Add(statement), + documentOptions, cancellationToken), DoStatementSyntax doStatementNode => AddBraceToDoStatement(document, root, doStatementNode, documentOptions, statement, cancellationToken), IfStatementSyntax ifStatementNode => AddBraceToIfStatement(document, root, ifStatementNode, documentOptions, statement, cancellationToken), @@ -184,6 +191,7 @@ private static (SyntaxNode newRoot, int nextCaretPosition) AddBraceToDoStatement newNode: AddBlockToEmbeddedStatementOwner(doStatementNode, documentOptions), anchorNode: doStatementNode, nodesToInsert: ImmutableArray.Empty.Add(innerStatement), + documentOptions, cancellationToken); } @@ -236,6 +244,7 @@ private static (SyntaxNode newRoot, int nextCaretPosition) AddBraceToIfStatement AddBlockToEmbeddedStatementOwner(ifStatementNode, documentOptions), ifStatementNode, ImmutableArray.Empty.Add(innerStatement), + documentOptions, cancellationToken); } @@ -300,6 +309,7 @@ private static (SyntaxNode newRoot, int nextCaretPosition) AddBraceToElseClause( WithBraces(elseClauseNode, documentOptions), elseClauseNode.Parent!, ImmutableArray.Empty.Add(innerStatement), + documentOptions, cancellationToken); } diff --git a/src/EditorFeatures/CSharp/DecompiledSource/CSharpDecompiledSourceService.cs b/src/EditorFeatures/CSharp/DecompiledSource/CSharpDecompiledSourceService.cs index 965cd834f56e8..8aa9d86ee763f 100644 --- a/src/EditorFeatures/CSharp/DecompiledSource/CSharpDecompiledSourceService.cs +++ b/src/EditorFeatures/CSharp/DecompiledSource/CSharpDecompiledSourceService.cs @@ -85,11 +85,13 @@ public async Task AddSourceToAsync(Document document, Compilation symb public static async Task FormatDocumentAsync(Document document, CancellationToken cancellationToken) { var node = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var options = await SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false); // Apply formatting rules var formattedDoc = await Formatter.FormatAsync( - document, SpecializedCollections.SingletonEnumerable(node.FullSpan), - options: null, + document, + SpecializedCollections.SingletonEnumerable(node.FullSpan), + options, CSharpDecompiledSourceFormattingRule.Instance.Concat(Formatter.GetDefaultFormattingRules(document)), cancellationToken).ConfigureAwait(false); diff --git a/src/EditorFeatures/CSharpTest/ExtractClass/ExtractClassTests.cs b/src/EditorFeatures/CSharpTest/ExtractClass/ExtractClassTests.cs index 6bcb5af3edb70..07c98c2a46c2c 100644 --- a/src/EditorFeatures/CSharpTest/ExtractClass/ExtractClassTests.cs +++ b/src/EditorFeatures/CSharpTest/ExtractClass/ExtractClassTests.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.CodeStyle; @@ -2043,7 +2044,7 @@ public TestExtractClassOptionsService(IEnumerable<(string name, bool makeAbstrac public string FileName { get; set; } = "MyBase.cs"; public string BaseName { get; set; } = "MyBase"; - public Task GetExtractClassOptionsAsync(Document document, INamedTypeSymbol originalSymbol, ISymbol? selectedMember) + public Task GetExtractClassOptionsAsync(Document document, INamedTypeSymbol originalSymbol, ISymbol? selectedMember, CancellationToken cancellationToken) { var availableMembers = originalSymbol.GetMembers().Where(member => MemberAndDestinationValidator.IsMemberValid(member)); diff --git a/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs b/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs index c1384e96d1c75..74ddc4a1df01e 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs @@ -6,10 +6,12 @@ using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.BraceCompletion; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Editor.Implementation.Formatting; using Microsoft.CodeAnalysis.Editor.UnitTests.Utilities; @@ -423,8 +425,8 @@ public void M() var document = workspace.CurrentSolution.Projects.Single().Documents.Single(); var syntaxRoot = await document.GetSyntaxRootAsync(); - - var node = Formatter.Format(syntaxRoot, spans, workspace); + var options = await SyntaxFormattingOptions.FromDocumentAsync(document, CancellationToken.None).ConfigureAwait(false); + var node = Formatter.Format(syntaxRoot, spans, workspace.Services, options, rules: null, CancellationToken.None); Assert.Equal(expected, node.ToFullString()); } @@ -518,8 +520,9 @@ public void M() var document = workspace.CurrentSolution.Projects.Single().Documents.Single(); var syntaxRoot = await document.GetSyntaxRootAsync(); + var options = await SyntaxFormattingOptions.FromDocumentAsync(document, CancellationToken.None); - var node = Formatter.Format(syntaxRoot, spans, workspace); + var node = Formatter.Format(syntaxRoot, spans, workspace.Services, options, rules: null, CancellationToken.None); Assert.Equal(expected, node.ToFullString()); } @@ -2284,7 +2287,13 @@ class Test var annotatedMarkerTrivia = markerTrivia.WithAdditionalAnnotations(annotation); root = root.ReplaceTrivia(markerTrivia, annotatedMarkerTrivia); - var formattedRoot = Formatter.Format(root, new AdhocWorkspace()); + using var workspace = new AdhocWorkspace(); + + var options = new SyntaxFormattingOptions( + DictionaryAnalyzerConfigOptions.Empty, + ShouldUseFormattingSpanCollapse: false); + + var formattedRoot = Formatter.Format(root, workspace.Services, options, CancellationToken.None); var annotatedTrivia = formattedRoot.GetAnnotatedTrivia("marker"); Assert.Single(annotatedTrivia); diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs index 9bbed65cd85d1..2d2bbdcc5ae5a 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs @@ -81,8 +81,8 @@ private static async Task TokenFormatWorkerAsync(TestWorkspace workspace, ITextB var rules = formattingRuleProvider.CreateRule(document, position).Concat(Formatter.GetDefaultFormattingRules(document)); - var documentOptions = await document.GetOptionsAsync(); - var formatter = new CSharpSmartTokenFormatter(documentOptions, rules, root); + var options = await SyntaxFormattingOptions.FromDocumentAsync(document, CancellationToken.None); + var formatter = new CSharpSmartTokenFormatter(options, rules, root); var changes = await formatter.FormatTokenAsync(workspace.Services, token, CancellationToken.None); ApplyChanges(buffer, changes); diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs index c2a1a3fc87512..0607bbaa86c1a 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs @@ -1519,7 +1519,7 @@ private static async Task AssertIndentUsingSmartTokenFormatterAsync( Assert.True( CSharpIndentationService.ShouldUseSmartTokenFormatterInsteadOfIndenter( - Formatter.GetDefaultFormattingRules(workspace.Services, root.Language), + Formatter.GetDefaultFormattingRules(document), root, line.AsTextLine(), optionService, await document.GetOptionsAsync(), out _)); var actualIndentation = await GetSmartTokenFormatterIndentationWorkerAsync(workspace, buffer, indentationLine, ch); @@ -1562,7 +1562,7 @@ private static async Task AssertIndentNotUsingSmartTokenFormatterButUsingIndente Assert.False( CSharpIndentationService.ShouldUseSmartTokenFormatterInsteadOfIndenter( - Formatter.GetDefaultFormattingRules(workspace.Services, root.Language), + Formatter.GetDefaultFormattingRules(document), root, line.AsTextLine(), optionService, await document.GetOptionsAsync(), out _)); TestIndentation(workspace, indentationLine, expectedIndentation); diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartTokenFormatterFormatRangeTests.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartTokenFormatterFormatRangeTests.cs index 7db1daf14aa39..cfdf914513aba 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartTokenFormatterFormatRangeTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartTokenFormatterFormatRangeTests.cs @@ -3551,13 +3551,6 @@ internal static void AutoFormatToken(string markup, string expected, bool useTab Assert.Equal(expected, newSnapshot.GetText()); } - private static Tuple> GetService( - TestWorkspace workspace) - { - var options = workspace.Options; - return Tuple.Create(options, Formatter.GetDefaultFormattingRules(workspace.Services, LanguageNames.CSharp)); - } - private static Task AutoFormatOnColonAsync(string codeWithMarker, string expected, SyntaxKind startTokenKind) => AutoFormatOnMarkerAsync(codeWithMarker, expected, SyntaxKind.ColonToken, startTokenKind); @@ -3580,12 +3573,13 @@ private static async Task AutoFormatOnMarkerAsync(string initialMarkup, string e workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options .WithChangedOption(FormattingOptions2.UseTabs, LanguageNames.CSharp, useTabs))); - var tuple = GetService(workspace); + var testDocument = workspace.Documents.Single(); var buffer = testDocument.GetTextBuffer(); var position = testDocument.CursorPosition.Value; var document = workspace.CurrentSolution.GetDocument(testDocument.Id); + var rules = Formatter.GetDefaultFormattingRules(document); var root = (CompilationUnitSyntax)await document.GetSyntaxRootAsync(); var endToken = root.FindToken(position); @@ -3595,7 +3589,8 @@ private static async Task AutoFormatOnMarkerAsync(string initialMarkup, string e } Assert.Equal(tokenKind, endToken.Kind()); - var formatter = new CSharpSmartTokenFormatter(tuple.Item1, tuple.Item2, root); + var options = await SyntaxFormattingOptions.FromDocumentAsync(document, CancellationToken.None); + var formatter = new CSharpSmartTokenFormatter(options, rules, root); var tokenRange = FormattingRangeHelper.FindAppropriateRange(endToken); if (tokenRange == null) diff --git a/src/EditorFeatures/Core/Shared/Extensions/ITextSnapshotExtensions.cs b/src/EditorFeatures/Core/Shared/Extensions/ITextSnapshotExtensions.cs index 57c2a3b11ab57..4e87d9398d842 100644 --- a/src/EditorFeatures/Core/Shared/Extensions/ITextSnapshotExtensions.cs +++ b/src/EditorFeatures/Core/Shared/Extensions/ITextSnapshotExtensions.cs @@ -43,8 +43,8 @@ public static void FormatAndApplyToBuffer(this ITextSnapshot snapshot, TextSpan var root = document.GetRequiredSyntaxRootSynchronously(cancellationToken); var formatter = document.GetRequiredLanguageService(); - var documentOptions = document.GetOptionsAsync(cancellationToken).WaitAndGetResult(cancellationToken); - var result = formatter.GetFormattingResult(root, SpecializedCollections.SingletonEnumerable(span), documentOptions, document.Project.Solution.Workspace.Services, rules, cancellationToken); + var options = SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).WaitAndGetResult(cancellationToken); + var result = formatter.GetFormattingResult(root, SpecializedCollections.SingletonEnumerable(span), options, rules, cancellationToken); var changes = result.GetTextChanges(cancellationToken); using (Logger.LogBlock(FunctionId.Formatting_ApplyResultToBuffer, cancellationToken)) diff --git a/src/EditorFeatures/TestUtilities/ExtractInterface/TestExtractInterfaceOptions.cs b/src/EditorFeatures/TestUtilities/ExtractInterface/TestExtractInterfaceOptions.cs index 22f4c525fae87..e0f799d2b1bc1 100644 --- a/src/EditorFeatures/TestUtilities/ExtractInterface/TestExtractInterfaceOptions.cs +++ b/src/EditorFeatures/TestUtilities/ExtractInterface/TestExtractInterfaceOptions.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Composition; +using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.ExtractInterface; using Microsoft.CodeAnalysis.Host.Mef; @@ -44,7 +45,8 @@ public Task GetExtractInterfaceOptionsAsync( List conflictingTypeNames, string defaultNamespace, string generatedNameTypeParameterSuffix, - string languageName) + string languageName, + CancellationToken cancellationToken) { this.AllExtractableMembers = extractableMembers; this.DefaultInterfaceName = defaultInterfaceName; diff --git a/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs b/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs index 45d79b78d3098..8a6d8c5f491dc 100644 --- a/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs +++ b/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.Implementation.Formatting; using Microsoft.CodeAnalysis.Editor.Implementation.SmartIndent; using Microsoft.CodeAnalysis.Editor.UnitTests.Utilities; @@ -140,27 +141,6 @@ private TestWorkspace CreateWorkspace(string codeWithMarker) ? TestWorkspace.CreateCSharp(codeWithMarker, composition: s_composition) : TestWorkspace.CreateVisualBasic(codeWithMarker, composition: s_composition); - internal void AssertFormatWithTransformation(Workspace workspace, string expected, OptionSet optionSet, IEnumerable rules, SyntaxNode root) - { - var newRootNode = Formatter.Format(root, SpecializedCollections.SingletonEnumerable(root.FullSpan), workspace, optionSet, rules, CancellationToken.None); - - Assert.Equal(expected, newRootNode.ToFullString()); - - // test doesn't use parsing option. add one if needed later - var newRootNodeFromString = ParseCompilationUnit(expected); - - // simple check to see whether two nodes are equivalent each other. - Assert.True(newRootNodeFromString.IsEquivalentTo(newRootNode)); - } - - internal static void AssertFormat(Workspace workspace, string expected, OptionSet optionSet, IEnumerable rules, ITextBuffer clonedBuffer, SyntaxNode root) - { - var changes = Formatter.GetFormattedTextChanges(root, SpecializedCollections.SingletonEnumerable(root.FullSpan), workspace, optionSet, rules, CancellationToken.None); - var actual = ApplyResultAndGetFormattedText(clonedBuffer, changes); - - Assert.Equal(expected, actual); - } - private static string ApplyResultAndGetFormattedText(ITextBuffer buffer, IList changes) { using (var edit = buffer.CreateEdit()) @@ -199,26 +179,29 @@ protected async Task AssertFormatAsync(string expected, string code, IEnumerable factory.TextSpan = spans.First(); } - var options = workspace.Options; + var optionSet = workspace.Options; if (changedOptionSet != null) { foreach (var entry in changedOptionSet) { - options = options.WithChangedOption(entry.Key, entry.Value); + optionSet = optionSet.WithChangedOption(entry.Key, entry.Value); } } var root = await syntaxTree.GetRootAsync(); - var rules = formattingRuleProvider.CreateRule(workspace.CurrentSolution.GetDocument(syntaxTree), 0).Concat(Formatter.GetDefaultFormattingRules(workspace.Services, root.Language)); + var options = SyntaxFormattingOptions.Create(optionSet, workspace.Services, root.Language); + + document = workspace.CurrentSolution.GetDocument(syntaxTree); + var rules = formattingRuleProvider.CreateRule(document, 0).Concat(Formatter.GetDefaultFormattingRules(document)); AssertFormat(workspace, expected, options, rules, clonedBuffer, root, spans); // format with node and transform AssertFormatWithTransformation(workspace, expected, options, rules, root, spans); } - internal void AssertFormatWithTransformation(Workspace workspace, string expected, OptionSet optionSet, IEnumerable rules, SyntaxNode root, IEnumerable spans) + internal void AssertFormatWithTransformation(Workspace workspace, string expected, SyntaxFormattingOptions options, IEnumerable rules, SyntaxNode root, IEnumerable spans) { - var newRootNode = Formatter.Format(root, spans, workspace, optionSet, rules, CancellationToken.None); + var newRootNode = Formatter.Format(root, spans, workspace.Services, options, rules, CancellationToken.None); Assert.Equal(expected, newRootNode.ToFullString()); @@ -229,9 +212,9 @@ internal void AssertFormatWithTransformation(Workspace workspace, string expecte Assert.True(newRootNodeFromString.IsEquivalentTo(newRootNode)); } - internal void AssertFormat(Workspace workspace, string expected, OptionSet optionSet, IEnumerable rules, ITextBuffer clonedBuffer, SyntaxNode root, IEnumerable spans) + internal void AssertFormat(Workspace workspace, string expected, SyntaxFormattingOptions options, IEnumerable rules, ITextBuffer clonedBuffer, SyntaxNode root, IEnumerable spans) { - var result = Formatter.GetFormattedTextChanges(root, spans, workspace, optionSet, rules, CancellationToken.None); + var result = Formatter.GetFormattedTextChanges(root, spans, workspace.Services, options, rules, CancellationToken.None); var actual = ApplyResultAndGetFormattedText(clonedBuffer, result); if (actual != expected) @@ -298,7 +281,9 @@ await AssertFormatAsync( /// uses an for formatting context, since the is not associated with a protected static void AssertFormatOnArbitraryNode(SyntaxNode node, string expected) { - var result = Formatter.Format(node, new AdhocWorkspace()); + using var workspace = new AdhocWorkspace(); + var options = SyntaxFormattingOptions.Create(DictionaryAnalyzerConfigOptions.Empty); + var result = Formatter.Format(node, workspace.Services, options, CancellationToken.None); var actual = result.GetText().ToString(); Assert.Equal(expected, actual); diff --git a/src/EditorFeatures/VisualBasicTest/CaseCorrecting/CaseCorrectionServiceTests.vb b/src/EditorFeatures/VisualBasicTest/CaseCorrecting/CaseCorrectionServiceTests.vb index 6c6270829de3a..3ef31c1fa05ec 100644 --- a/src/EditorFeatures/VisualBasicTest/CaseCorrecting/CaseCorrectionServiceTests.vb +++ b/src/EditorFeatures/VisualBasicTest/CaseCorrecting/CaseCorrectionServiceTests.vb @@ -10,6 +10,7 @@ Imports Microsoft.CodeAnalysis.CodeCleanup Imports Microsoft.CodeAnalysis.CodeCleanup.Providers Imports Microsoft.CodeAnalysis.Editor.UnitTests.Extensions Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces +Imports Microsoft.CodeAnalysis.Formatting Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CaseCorrecting <[UseExportProvider]> @@ -33,7 +34,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CaseCorrecting Dim buffer = hostDocument.GetTextBuffer() Dim document = workspace.CurrentSolution.GetDocument(hostDocument.Id) Dim span = (Await document.GetSyntaxRootAsync()).FullSpan - Dim options = Await document.GetOptionsAsync() + Dim options = Await SyntaxFormattingOptions.FromDocumentAsync(document, CancellationToken.None) Dim service = document.GetLanguageService(Of ICodeCleanerService) Dim newDocument = Await service.CleanupAsync( diff --git a/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartTokenFormatter_FormatTokenTests.vb b/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartTokenFormatter_FormatTokenTests.vb index 38b5289e89d14..e2502f5158345 100644 --- a/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartTokenFormatter_FormatTokenTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartTokenFormatter_FormatTokenTests.vb @@ -207,7 +207,8 @@ End Class formattingRules, root, line.AsTextLine, optionService, workspace.Options, Nothing, ignoreMissingToken)) - Dim smartFormatter = New VisualBasicSmartTokenFormatter(Await document.GetOptionsAsync(CancellationToken.None), formattingRules, root) + Dim formatOptions = Await SyntaxFormattingOptions.FromDocumentAsync(document, CancellationToken.None) + Dim smartFormatter = New VisualBasicSmartTokenFormatter(formatOptions, formattingRules, root) Dim changes = Await smartFormatter.FormatTokenAsync(workspace.Services, token, Nothing) Using edit = buffer.CreateEdit() diff --git a/src/EditorFeatures/VisualBasicTest/Formatting/VisualBasicFormatterTestBase.vb b/src/EditorFeatures/VisualBasicTest/Formatting/VisualBasicFormatterTestBase.vb index 8f292b13e5798..370541bae9212 100644 --- a/src/EditorFeatures/VisualBasicTest/Formatting/VisualBasicFormatterTestBase.vb +++ b/src/EditorFeatures/VisualBasicTest/Formatting/VisualBasicFormatterTestBase.vb @@ -58,11 +58,16 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Formatting End If Dim rules = formattingRuleProvider.CreateRule(document, 0).Concat(Formatter.GetDefaultFormattingRules(document)) + Dim options = Await SyntaxFormattingOptions.FromDocumentAsync(document, CancellationToken.None) Dim changes = Formatter.GetFormattedTextChanges( Await syntaxTree.GetRootAsync(), workspace.Documents.First(Function(d) d.SelectedSpans.Any()).SelectedSpans, - workspace, Await document.GetOptionsAsync(CancellationToken.None), rules, CancellationToken.None) + workspace.Services, + options, + rules, + CancellationToken.None) + AssertResult(expected, clonedBuffer, changes) End Using End Function diff --git a/src/Features/CSharp/Portable/BraceCompletion/CurlyBraceCompletionService.cs b/src/Features/CSharp/Portable/BraceCompletion/CurlyBraceCompletionService.cs index 32c5da7b5edbe..c6f03c8f89987 100644 --- a/src/Features/CSharp/Portable/BraceCompletion/CurlyBraceCompletionService.cs +++ b/src/Features/CSharp/Portable/BraceCompletion/CurlyBraceCompletionService.cs @@ -296,9 +296,14 @@ private static bool ContainsOnlyWhitespace(SourceText text, int openingPosition, var spanToFormat = TextSpan.FromBounds(Math.Max(startPoint, 0), endPoint); var rules = document.GetFormattingRules(spanToFormat, braceFormattingIndentationRules); - var formatter = document.GetRequiredLanguageService(); - var result = formatter.GetFormattingResult( - root, SpecializedCollections.SingletonEnumerable(spanToFormat), documentOptions, document.Project.Solution.Workspace.Services, rules, cancellationToken); + var services = document.Project.Solution.Workspace.Services; + var formattingOptions = SyntaxFormattingOptions.Create(documentOptions, services, document.Project.Language); + var result = Formatter.GetFormattingResult( + root, SpecializedCollections.SingletonEnumerable(spanToFormat), services, formattingOptions, rules, cancellationToken); + if (result == null) + { + return (ImmutableArray.Empty, closingPoint); + } var newRoot = result.GetFormattedRoot(cancellationToken); var newClosingPoint = newRoot.GetAnnotatedTokens(s_closingBraceSyntaxAnnotation).Single().SpanStart + 1; diff --git a/src/Features/CSharp/Portable/CodeFixes/Suppression/CSharpSuppressionCodeFixProvider.cs b/src/Features/CSharp/Portable/CodeFixes/Suppression/CSharpSuppressionCodeFixProvider.cs index 5c7f149b05ce2..60a557cfeb8c3 100644 --- a/src/Features/CSharp/Portable/CodeFixes/Suppression/CSharpSuppressionCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/CodeFixes/Suppression/CSharpSuppressionCodeFixProvider.cs @@ -16,6 +16,7 @@ using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Text; @@ -31,27 +32,27 @@ public CSharpSuppressionCodeFixProvider() { } - protected override SyntaxTriviaList CreatePragmaRestoreDirectiveTrivia(Diagnostic diagnostic, Func formatNode, bool needsLeadingEndOfLine, bool needsTrailingEndOfLine) + protected override SyntaxTriviaList CreatePragmaRestoreDirectiveTrivia(Diagnostic diagnostic, Func formatNode, bool needsLeadingEndOfLine, bool needsTrailingEndOfLine, CancellationToken cancellationToken) { var restoreKeyword = SyntaxFactory.Token(SyntaxKind.RestoreKeyword); - return CreatePragmaDirectiveTrivia(restoreKeyword, diagnostic, formatNode, needsLeadingEndOfLine, needsTrailingEndOfLine); + return CreatePragmaDirectiveTrivia(restoreKeyword, diagnostic, formatNode, needsLeadingEndOfLine, needsTrailingEndOfLine, cancellationToken); } protected override SyntaxTriviaList CreatePragmaDisableDirectiveTrivia( - Diagnostic diagnostic, Func formatNode, bool needsLeadingEndOfLine, bool needsTrailingEndOfLine) + Diagnostic diagnostic, Func formatNode, bool needsLeadingEndOfLine, bool needsTrailingEndOfLine, CancellationToken cancellationToken) { var disableKeyword = SyntaxFactory.Token(SyntaxKind.DisableKeyword); - return CreatePragmaDirectiveTrivia(disableKeyword, diagnostic, formatNode, needsLeadingEndOfLine, needsTrailingEndOfLine); + return CreatePragmaDirectiveTrivia(disableKeyword, diagnostic, formatNode, needsLeadingEndOfLine, needsTrailingEndOfLine, cancellationToken); } private static SyntaxTriviaList CreatePragmaDirectiveTrivia( - SyntaxToken disableOrRestoreKeyword, Diagnostic diagnostic, Func formatNode, bool needsLeadingEndOfLine, bool needsTrailingEndOfLine) + SyntaxToken disableOrRestoreKeyword, Diagnostic diagnostic, Func formatNode, bool needsLeadingEndOfLine, bool needsTrailingEndOfLine, CancellationToken cancellationToken) { var diagnosticId = GetOrMapDiagnosticId(diagnostic, out var includeTitle); var id = SyntaxFactory.IdentifierName(diagnosticId); var ids = new SeparatedSyntaxList().Add(id); var pragmaDirective = SyntaxFactory.PragmaWarningDirectiveTrivia(disableOrRestoreKeyword, ids, true); - pragmaDirective = (PragmaWarningDirectiveTriviaSyntax)formatNode(pragmaDirective); + pragmaDirective = (PragmaWarningDirectiveTriviaSyntax)formatNode(pragmaDirective, cancellationToken); var pragmaDirectiveTrivia = SyntaxFactory.Trivia(pragmaDirective); var endOfLineTrivia = SyntaxFactory.CarriageReturnLineFeed; var triviaList = SyntaxFactory.TriviaList(pragmaDirectiveTrivia); @@ -98,8 +99,8 @@ protected override SyntaxNode AddGlobalSuppressMessageAttribute( ISymbol targetSymbol, INamedTypeSymbol suppressMessageAttribute, Diagnostic diagnostic, - Workspace workspace, - Compilation compilation, + HostWorkspaceServices services, + SyntaxFormattingOptions options, IAddImportsService addImportsService, CancellationToken cancellationToken) { diff --git a/src/Features/CSharp/Portable/Formatting/CSharpFormattingInteractionService.cs b/src/Features/CSharp/Portable/Formatting/CSharpFormattingInteractionService.cs index bb725facc4354..6ac9c3096366f 100644 --- a/src/Features/CSharp/Portable/Formatting/CSharpFormattingInteractionService.cs +++ b/src/Features/CSharp/Portable/Formatting/CSharpFormattingInteractionService.cs @@ -103,16 +103,15 @@ public async Task> GetFormattingChangesAsync( var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var span = textSpan ?? new TextSpan(0, root.FullSpan.Length); var formattingSpan = CommonFormattingHelpers.GetFormattingSpan(root, span); - var options = documentOptions; - if (options == null) + if (documentOptions == null) { var inferredIndentationService = document.Project.Solution.Workspace.Services.GetRequiredService(); - options = await inferredIndentationService.GetDocumentOptionsWithInferredIndentationAsync(document, explicitFormat: true, cancellationToken: cancellationToken).ConfigureAwait(false); + documentOptions = await inferredIndentationService.GetDocumentOptionsWithInferredIndentationAsync(document, explicitFormat: true, cancellationToken: cancellationToken).ConfigureAwait(false); } - var service = document.GetRequiredLanguageService(); - var result = service.GetFormattingResult(root, SpecializedCollections.SingletonEnumerable(formattingSpan), options, document.Project.Solution.Workspace.Services, rules: null, cancellationToken); - return result.GetTextChanges(cancellationToken).ToImmutableArray(); + var services = document.Project.Solution.Workspace.Services; + var formattingOptions = SyntaxFormattingOptions.Create(documentOptions, services, document.Project.Language); + return Formatter.GetFormattedTextChanges(root, SpecializedCollections.SingletonEnumerable(formattingSpan), services, formattingOptions, cancellationToken).ToImmutableArray(); } public async Task> GetFormattingChangesOnPasteAsync( @@ -129,14 +128,14 @@ public async Task> GetFormattingChangesOnPasteAsync( var rules = new List() { new PasteFormattingRule() }; rules.AddRange(service.GetDefaultFormattingRules()); - var options = documentOptions; - if (options == null) + if (documentOptions == null) { var inferredIndentationService = document.Project.Solution.Workspace.Services.GetRequiredService(); - options = await inferredIndentationService.GetDocumentOptionsWithInferredIndentationAsync(document, explicitFormat: false, cancellationToken: cancellationToken).ConfigureAwait(false); + documentOptions = await inferredIndentationService.GetDocumentOptionsWithInferredIndentationAsync(document, explicitFormat: false, cancellationToken: cancellationToken).ConfigureAwait(false); } - var result = service.GetFormattingResult(root, SpecializedCollections.SingletonEnumerable(formattingSpan), options, document.Project.Solution.Workspace.Services, rules, cancellationToken); + var formattingOptions = SyntaxFormattingOptions.Create(documentOptions, document.Project.Solution.Workspace.Services, document.Project.Language); + var result = service.GetFormattingResult(root, SpecializedCollections.SingletonEnumerable(formattingSpan), formattingOptions, rules, cancellationToken); return result.GetTextChanges(cancellationToken).ToImmutableArray(); } @@ -215,13 +214,16 @@ public async Task> GetFormattingChangesAsync( return ImmutableArray.Empty; } - var options = documentOptions; - if (options == null) + var services = document.Project.Solution.Workspace.Services; + + if (documentOptions == null) { - var inferredIndentationService = document.Project.Solution.Workspace.Services.GetRequiredService(); - options = await inferredIndentationService.GetDocumentOptionsWithInferredIndentationAsync(document, explicitFormat: false, cancellationToken: cancellationToken).ConfigureAwait(false); + var inferredIndentationService = services.GetRequiredService(); + documentOptions = await inferredIndentationService.GetDocumentOptionsWithInferredIndentationAsync(document, explicitFormat: false, cancellationToken: cancellationToken).ConfigureAwait(false); } + var options = SyntaxFormattingOptions.Create(documentOptions, services, document.Project.Language); + // Do not attempt to format on open/close brace if autoformat on close brace feature is // off, instead just smart indent. // @@ -273,20 +275,20 @@ public async Task> GetFormattingChangesAsync( return (await FormatTokenAsync(document, options, token, formattingRules, cancellationToken).ConfigureAwait(false)).ToImmutableArray(); } - private static bool OnlySmartIndentCloseBrace(DocumentOptionSet options) + private static bool OnlySmartIndentCloseBrace(in SyntaxFormattingOptions options) { // User does not want auto-formatting (either in general, or for close braces in // specific). So we only smart indent close braces when typed. - return !options.GetOption(BraceCompletionOptions.AutoFormattingOnCloseBrace) || - !options.GetOption(FormattingBehaviorOptions.AutoFormattingOnTyping); + return !options.Options.GetOption(BraceCompletionOptions.AutoFormattingOnCloseBrace) || + !options.Options.GetOption(FormattingBehaviorOptions.AutoFormattingOnTyping); } - private static bool OnlySmartIndentOpenBrace(DocumentOptionSet options) + private static bool OnlySmartIndentOpenBrace(in SyntaxFormattingOptions options) { // User does not want auto-formatting . So we only smart indent open braces when typed. // Note: there is no specific option for controlling formatting on open brace. So we // don't have the symmetry with OnlySmartIndentCloseBrace. - return !options.GetOption(FormattingBehaviorOptions.AutoFormattingOnTyping); + return !options.Options.GetOption(FormattingBehaviorOptions.AutoFormattingOnTyping); } private static async Task GetTokenBeforeTheCaretAsync(Document document, int caretPosition, CancellationToken cancellationToken) @@ -299,7 +301,7 @@ private static async Task GetTokenBeforeTheCaretAsync(Document docu return token; } - private static async Task> FormatTokenAsync(Document document, OptionSet options, SyntaxToken token, IEnumerable formattingRules, CancellationToken cancellationToken) + private static async Task> FormatTokenAsync(Document document, SyntaxFormattingOptions options, SyntaxToken token, IEnumerable formattingRules, CancellationToken cancellationToken) { var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var formatter = CreateSmartTokenFormatter(options, formattingRules, root); @@ -307,12 +309,12 @@ private static async Task> FormatTokenAsync(Document document, return changes; } - private static ISmartTokenFormatter CreateSmartTokenFormatter(OptionSet optionSet, IEnumerable formattingRules, SyntaxNode root) - => new CSharpSmartTokenFormatter(optionSet, formattingRules, (CompilationUnitSyntax)root); + private static ISmartTokenFormatter CreateSmartTokenFormatter(SyntaxFormattingOptions options, IEnumerable formattingRules, SyntaxNode root) + => new CSharpSmartTokenFormatter(options, formattingRules, (CompilationUnitSyntax)root); private static async Task> FormatRangeAsync( Document document, - OptionSet options, + SyntaxFormattingOptions options, SyntaxToken endToken, IEnumerable formattingRules, CancellationToken cancellationToken) diff --git a/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs b/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs index b27637de58c64..f79d2c37b6f84 100644 --- a/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs +++ b/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureService.cs @@ -398,12 +398,13 @@ private static async Task> FindChangeSignatureR }); var annotatedNodes = newRoot.GetAnnotatedNodes(syntaxAnnotation: changeSignatureFormattingAnnotation); + var formattingOptions = await SyntaxFormattingOptions.FromDocumentAsync(doc, cancellationToken).ConfigureAwait(false); var formattedRoot = Formatter.Format( newRoot, changeSignatureFormattingAnnotation, - doc.Project.Solution.Workspace, - options: await doc.GetOptionsAsync(cancellationToken).ConfigureAwait(false), + doc.Project.Solution.Workspace.Services, + options: formattingOptions, rules: GetFormattingRules(doc), cancellationToken: CancellationToken.None); diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.GlobalSuppressMessageCodeAction.cs b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.GlobalSuppressMessageCodeAction.cs index 8a39bb7fd11b5..64e706190a356 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.GlobalSuppressMessageCodeAction.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.GlobalSuppressMessageCodeAction.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.AddImports; +using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.CodeFixes.Suppression @@ -33,13 +34,13 @@ public GlobalSuppressMessageCodeAction( protected override async Task GetChangedSuppressionDocumentAsync(CancellationToken cancellationToken) { var suppressionsDoc = await GetOrCreateSuppressionsDocumentAsync(cancellationToken).ConfigureAwait(false); - var workspace = suppressionsDoc.Project.Solution.Workspace; + var services = suppressionsDoc.Project.Solution.Workspace.Services; var suppressionsRoot = await suppressionsDoc.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var compilation = await suppressionsDoc.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); var addImportsService = suppressionsDoc.GetRequiredLanguageService(); + var options = await SyntaxFormattingOptions.FromDocumentAsync(suppressionsDoc, cancellationToken).ConfigureAwait(false); suppressionsRoot = Fixer.AddGlobalSuppressMessageAttribute( - suppressionsRoot, _targetSymbol, _suppressMessageAttribute, _diagnostic, workspace, compilation, addImportsService, cancellationToken); + suppressionsRoot, _targetSymbol, _suppressMessageAttribute, _diagnostic, services, options, addImportsService, cancellationToken); return suppressionsDoc.WithSyntaxRoot(suppressionsRoot); } diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.GlobalSuppressMessageFixAllCodeAction.cs b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.GlobalSuppressMessageFixAllCodeAction.cs index b6a23d39b77e7..9cb4dbad654bd 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.GlobalSuppressMessageFixAllCodeAction.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.GlobalSuppressMessageFixAllCodeAction.cs @@ -15,6 +15,7 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Formatting; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeFixes.Suppression @@ -120,10 +121,10 @@ private static async Task CreateChangedSolutionAsync(AbstractSuppressi protected override async Task GetChangedSuppressionDocumentAsync(CancellationToken cancellationToken) { var suppressionsDoc = await GetOrCreateSuppressionsDocumentAsync(cancellationToken).ConfigureAwait(false); - var workspace = suppressionsDoc.Project.Solution.Workspace; + var services = suppressionsDoc.Project.Solution.Workspace.Services; var suppressionsRoot = await suppressionsDoc.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var compilation = await suppressionsDoc.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); var addImportsService = suppressionsDoc.GetRequiredLanguageService(); + var options = await SyntaxFormattingOptions.FromDocumentAsync(suppressionsDoc, cancellationToken).ConfigureAwait(false); foreach (var (targetSymbol, diagnostics) in _diagnosticsBySymbol) { @@ -132,7 +133,7 @@ protected override async Task GetChangedSuppressionDocumentAsync(Cance Contract.ThrowIfFalse(!diagnostic.IsSuppressed); suppressionsRoot = Fixer.AddGlobalSuppressMessageAttribute( suppressionsRoot, targetSymbol, _suppressMessageAttribute, diagnostic, - workspace, compilation, addImportsService, cancellationToken); + services, options, addImportsService, cancellationToken); } } diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.PragmaHelpers.cs b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.PragmaHelpers.cs index 5a1bf7eed6ac2..20e102c4bfaab 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.PragmaHelpers.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.PragmaHelpers.cs @@ -104,8 +104,9 @@ internal static SyntaxToken GetNewStartTokenWithAddedPragma( TextSpan currentDiagnosticSpan, Diagnostic diagnostic, AbstractSuppressionCodeFixProvider fixer, - Func formatNode, - bool isRemoveSuppression = false) + Func formatNode, + bool isRemoveSuppression, + CancellationToken cancellationToken) { var trivia = startToken.LeadingTrivia.ToImmutableArray(); var index = GetPositionForPragmaInsertion(trivia, currentDiagnosticSpan, fixer, isStartToken: true, triviaAtIndex: out var insertAfterTrivia); @@ -126,8 +127,8 @@ internal static SyntaxToken GetNewStartTokenWithAddedPragma( } var pragmaTrivia = !isRemoveSuppression - ? fixer.CreatePragmaDisableDirectiveTrivia(diagnostic, formatNode, needsLeadingEOL, needsTrailingEndOfLine: true) - : fixer.CreatePragmaRestoreDirectiveTrivia(diagnostic, formatNode, needsLeadingEOL, needsTrailingEndOfLine: true); + ? fixer.CreatePragmaDisableDirectiveTrivia(diagnostic, formatNode, needsLeadingEOL, needsTrailingEndOfLine: true, cancellationToken) + : fixer.CreatePragmaRestoreDirectiveTrivia(diagnostic, formatNode, needsLeadingEOL, needsTrailingEndOfLine: true, cancellationToken); return startToken.WithLeadingTrivia(trivia.InsertRange(index, pragmaTrivia)); } @@ -155,8 +156,9 @@ internal static SyntaxToken GetNewEndTokenWithAddedPragma( TextSpan currentDiagnosticSpan, Diagnostic diagnostic, AbstractSuppressionCodeFixProvider fixer, - Func formatNode, - bool isRemoveSuppression = false) + Func formatNode, + bool isRemoveSuppression, + CancellationToken cancellationToken) { ImmutableArray trivia; var isEOF = fixer.IsEndOfFileToken(endToken); @@ -186,8 +188,8 @@ internal static SyntaxToken GetNewEndTokenWithAddedPragma( } var pragmaTrivia = !isRemoveSuppression - ? fixer.CreatePragmaRestoreDirectiveTrivia(diagnostic, formatNode, needsLeadingEndOfLine: true, needsTrailingEndOfLine: needsTrailingEOL) - : fixer.CreatePragmaDisableDirectiveTrivia(diagnostic, formatNode, needsLeadingEndOfLine: true, needsTrailingEndOfLine: needsTrailingEOL); + ? fixer.CreatePragmaRestoreDirectiveTrivia(diagnostic, formatNode, needsLeadingEndOfLine: true, needsTrailingEndOfLine: needsTrailingEOL, cancellationToken) + : fixer.CreatePragmaDisableDirectiveTrivia(diagnostic, formatNode, needsLeadingEndOfLine: true, needsTrailingEndOfLine: needsTrailingEOL, cancellationToken); if (isEOF) { diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.PragmaWarningCodeAction.cs b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.PragmaWarningCodeAction.cs index 3ceb8b0e9ddcf..bb2f8de920e15 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.PragmaWarningCodeAction.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.PragmaWarningCodeAction.cs @@ -16,12 +16,14 @@ internal sealed class PragmaWarningCodeAction : AbstractSuppressionCodeAction, I { private readonly SuppressionTargetInfo _suppressionTargetInfo; private readonly Document _document; + private readonly SyntaxFormattingOptions _options; private readonly Diagnostic _diagnostic; private readonly bool _forFixMultipleContext; public static PragmaWarningCodeAction Create( SuppressionTargetInfo suppressionTargetInfo, Document document, + SyntaxFormattingOptions options, Diagnostic diagnostic, AbstractSuppressionCodeFixProvider fixer) { @@ -29,12 +31,13 @@ public static PragmaWarningCodeAction Create( // the trailing trivia on its previous token (and similarly normalize trailing trivia for end token). PragmaHelpers.NormalizeTriviaOnTokens(fixer, ref document, ref suppressionTargetInfo); - return new PragmaWarningCodeAction(suppressionTargetInfo, document, diagnostic, fixer); + return new PragmaWarningCodeAction(suppressionTargetInfo, document, options, diagnostic, fixer); } private PragmaWarningCodeAction( SuppressionTargetInfo suppressionTargetInfo, Document document, + SyntaxFormattingOptions options, Diagnostic diagnostic, AbstractSuppressionCodeFixProvider fixer, bool forFixMultipleContext = false) @@ -42,12 +45,14 @@ private PragmaWarningCodeAction( { _suppressionTargetInfo = suppressionTargetInfo; _document = document; + _options = options; _diagnostic = diagnostic; _forFixMultipleContext = forFixMultipleContext; } public PragmaWarningCodeAction CloneForFixMultipleContext() - => new(_suppressionTargetInfo, _document, _diagnostic, Fixer, forFixMultipleContext: true); + => new(_suppressionTargetInfo, _document, _options, _diagnostic, Fixer, forFixMultipleContext: true); + protected override string DiagnosticIdForEquivalenceKey => _forFixMultipleContext ? string.Empty : _diagnostic.Id; @@ -63,13 +68,13 @@ public async Task GetChangedDocumentAsync(bool includeStartTokenChange (startToken, currentDiagnosticSpan) => { return includeStartTokenChange - ? PragmaHelpers.GetNewStartTokenWithAddedPragma(startToken, currentDiagnosticSpan, _diagnostic, Fixer, FormatNode) + ? PragmaHelpers.GetNewStartTokenWithAddedPragma(startToken, currentDiagnosticSpan, _diagnostic, Fixer, FormatNode, isRemoveSuppression: false, cancellationToken) : startToken; }, (endToken, currentDiagnosticSpan) => { return includeEndTokenChange - ? PragmaHelpers.GetNewEndTokenWithAddedPragma(endToken, currentDiagnosticSpan, _diagnostic, Fixer, FormatNode) + ? PragmaHelpers.GetNewEndTokenWithAddedPragma(endToken, currentDiagnosticSpan, _diagnostic, Fixer, FormatNode, isRemoveSuppression: false, cancellationToken) : endToken; }, cancellationToken).ConfigureAwait(false); @@ -78,8 +83,8 @@ public async Task GetChangedDocumentAsync(bool includeStartTokenChange public SyntaxToken StartToken_TestOnly => _suppressionTargetInfo.StartToken; public SyntaxToken EndToken_TestOnly => _suppressionTargetInfo.EndToken; - private SyntaxNode FormatNode(SyntaxNode node) - => Formatter.Format(node, _document.Project.Solution.Workspace); + private SyntaxNode FormatNode(SyntaxNode node, CancellationToken cancellationToken) + => Formatter.Format(node, _document.Project.Solution.Workspace.Services, _options, cancellationToken); } } } diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.RemoveSuppressionCodeAction.cs b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.RemoveSuppressionCodeAction.cs index 52df1bf737b14..c1d861e38b421 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.RemoveSuppressionCodeAction.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.RemoveSuppressionCodeAction.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Formatting; namespace Microsoft.CodeAnalysis.CodeFixes.Suppression { @@ -35,7 +36,8 @@ public static async Task CreateAsync( } else if (documentOpt != null && !SuppressionHelpers.IsSynthesizedExternalSourceDiagnostic(diagnostic)) { - return PragmaRemoveAction.Create(suppressionTargetInfo, documentOpt, diagnostic, fixer); + var options = await SyntaxFormattingOptions.FromDocumentAsync(documentOpt, cancellationToken).ConfigureAwait(false); + return PragmaRemoveAction.Create(suppressionTargetInfo, documentOpt, options, diagnostic, fixer); } else { diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.RemoveSuppressionCodeAction_Pragma.cs b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.RemoveSuppressionCodeAction_Pragma.cs index 23155cd268212..856de08fb5619 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.RemoveSuppressionCodeAction_Pragma.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.RemoveSuppressionCodeAction_Pragma.cs @@ -25,11 +25,13 @@ internal abstract partial class RemoveSuppressionCodeAction private class PragmaRemoveAction : RemoveSuppressionCodeAction, IPragmaBasedCodeAction { private readonly Document _document; + private readonly SyntaxFormattingOptions _options; private readonly SuppressionTargetInfo _suppressionTargetInfo; public static PragmaRemoveAction Create( SuppressionTargetInfo suppressionTargetInfo, Document document, + SyntaxFormattingOptions options, Diagnostic diagnostic, AbstractSuppressionCodeFixProvider fixer) { @@ -37,23 +39,25 @@ public static PragmaRemoveAction Create( // the trailing trivia on its previous token (and similarly normalize trailing trivia for end token). PragmaHelpers.NormalizeTriviaOnTokens(fixer, ref document, ref suppressionTargetInfo); - return new PragmaRemoveAction(suppressionTargetInfo, document, diagnostic, fixer); + return new PragmaRemoveAction(suppressionTargetInfo, document, options, diagnostic, fixer); } private PragmaRemoveAction( SuppressionTargetInfo suppressionTargetInfo, Document document, + SyntaxFormattingOptions options, Diagnostic diagnostic, AbstractSuppressionCodeFixProvider fixer, bool forFixMultipleContext = false) : base(diagnostic, fixer, forFixMultipleContext) { _document = document; + _options = options; _suppressionTargetInfo = suppressionTargetInfo; } public override RemoveSuppressionCodeAction CloneForFixMultipleContext() - => new PragmaRemoveAction(_suppressionTargetInfo, _document, _diagnostic, Fixer, forFixMultipleContext: true); + => new PragmaRemoveAction(_suppressionTargetInfo, _document, _options, _diagnostic, Fixer, forFixMultipleContext: true); public override SyntaxTree SyntaxTreeToModify => _suppressionTargetInfo.StartToken.SyntaxTree; @@ -81,11 +85,11 @@ public async Task GetChangedDocumentAsync(bool includeStartTokenChange } SyntaxToken getNewStartToken(SyntaxToken startToken, TextSpan currentDiagnosticSpan) => includeStartTokenChange - ? GetNewTokenWithModifiedPragma(startToken, currentDiagnosticSpan, add, toggle, indexOfLeadingPragmaDisableToRemove, isStartToken: true) + ? GetNewTokenWithModifiedPragma(startToken, currentDiagnosticSpan, add, toggle, indexOfLeadingPragmaDisableToRemove, isStartToken: true, cancellationToken) : startToken; SyntaxToken getNewEndToken(SyntaxToken endToken, TextSpan currentDiagnosticSpan) => includeEndTokenChange - ? GetNewTokenWithModifiedPragma(endToken, currentDiagnosticSpan, add, toggle, indexOfTrailingPragmaEnableToRemove, isStartToken: false) + ? GetNewTokenWithModifiedPragma(endToken, currentDiagnosticSpan, add, toggle, indexOfTrailingPragmaEnableToRemove, isStartToken: false, cancellationToken) : endToken; return await PragmaHelpers.GetChangeDocumentWithPragmaAdjustedAsync( @@ -151,22 +155,22 @@ private static bool CanRemovePragmaTrivia(SyntaxToken token, Diagnostic diagnost return false; } - private SyntaxToken GetNewTokenWithModifiedPragma(SyntaxToken token, TextSpan currentDiagnosticSpan, bool add, bool toggle, int indexOfTriviaToRemoveOrToggle, bool isStartToken) + private SyntaxToken GetNewTokenWithModifiedPragma(SyntaxToken token, TextSpan currentDiagnosticSpan, bool add, bool toggle, int indexOfTriviaToRemoveOrToggle, bool isStartToken, CancellationToken cancellationToken) { return add - ? GetNewTokenWithAddedPragma(token, currentDiagnosticSpan, isStartToken) + ? GetNewTokenWithAddedPragma(token, currentDiagnosticSpan, isStartToken, cancellationToken) : GetNewTokenWithRemovedOrToggledPragma(token, indexOfTriviaToRemoveOrToggle, isStartToken, toggle); } - private SyntaxToken GetNewTokenWithAddedPragma(SyntaxToken token, TextSpan currentDiagnosticSpan, bool isStartToken) + private SyntaxToken GetNewTokenWithAddedPragma(SyntaxToken token, TextSpan currentDiagnosticSpan, bool isStartToken, CancellationToken cancellationToken) { if (isStartToken) { - return PragmaHelpers.GetNewStartTokenWithAddedPragma(token, currentDiagnosticSpan, _diagnostic, Fixer, FormatNode, isRemoveSuppression: true); + return PragmaHelpers.GetNewStartTokenWithAddedPragma(token, currentDiagnosticSpan, _diagnostic, Fixer, FormatNode, isRemoveSuppression: true, cancellationToken); } else { - return PragmaHelpers.GetNewEndTokenWithAddedPragma(token, currentDiagnosticSpan, _diagnostic, Fixer, FormatNode, isRemoveSuppression: true); + return PragmaHelpers.GetNewEndTokenWithAddedPragma(token, currentDiagnosticSpan, _diagnostic, Fixer, FormatNode, isRemoveSuppression: true, cancellationToken); } } @@ -213,8 +217,8 @@ private async Task IsDiagnosticSuppressedBeforeLeadingPragmaAsync(int inde public SyntaxToken StartToken_TestOnly => _suppressionTargetInfo.StartToken; public SyntaxToken EndToken_TestOnly => _suppressionTargetInfo.EndToken; - private SyntaxNode FormatNode(SyntaxNode node) - => Formatter.Format(node, _document.Project.Solution.Workspace); + private SyntaxNode FormatNode(SyntaxNode node, CancellationToken cancellationToken) + => Formatter.Format(node, _document.Project.Solution.Workspace.Services, _options, cancellationToken); } } } diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.cs b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.cs index 68c9393666f94..63d8a83d48eec 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.AddImports; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -42,16 +43,16 @@ public FixAllProvider GetFixAllProvider() public bool IsFixableDiagnostic(Diagnostic diagnostic) => SuppressionHelpers.CanBeSuppressed(diagnostic) || SuppressionHelpers.CanBeUnsuppressed(diagnostic); - protected abstract SyntaxTriviaList CreatePragmaDisableDirectiveTrivia(Diagnostic diagnostic, Func formatNode, bool needsLeadingEndOfLine, bool needsTrailingEndOfLine); - protected abstract SyntaxTriviaList CreatePragmaRestoreDirectiveTrivia(Diagnostic diagnostic, Func formatNode, bool needsLeadingEndOfLine, bool needsTrailingEndOfLine); + protected abstract SyntaxTriviaList CreatePragmaDisableDirectiveTrivia(Diagnostic diagnostic, Func formatNode, bool needsLeadingEndOfLine, bool needsTrailingEndOfLine, CancellationToken cancellationToken); + protected abstract SyntaxTriviaList CreatePragmaRestoreDirectiveTrivia(Diagnostic diagnostic, Func formatNode, bool needsLeadingEndOfLine, bool needsTrailingEndOfLine, CancellationToken cancellationToken); protected abstract SyntaxNode AddGlobalSuppressMessageAttribute( SyntaxNode newRoot, ISymbol targetSymbol, INamedTypeSymbol suppressMessageAttribute, Diagnostic diagnostic, - Workspace workspace, - Compilation compilation, + HostWorkspaceServices services, + SyntaxFormattingOptions options, IAddImportsService addImportsService, CancellationToken cancellationToken); @@ -199,6 +200,7 @@ private async Task> GetSuppressionsAsync( skipSuppressMessage = suppressMessageAttribute == null || !suppressMessageAttribute.IsAttribute(); } + var lazyFormattingOptions = (SyntaxFormattingOptions?)null; var result = ArrayBuilder.GetInstance(); foreach (var diagnostic in diagnostics) { @@ -208,7 +210,8 @@ private async Task> GetSuppressionsAsync( if (diagnostic.Location.IsInSource && documentOpt != null) { // pragma warning disable. - nestedActions.Add(PragmaWarningCodeAction.Create(suppressionTargetInfo, documentOpt, diagnostic, this)); + lazyFormattingOptions ??= await SyntaxFormattingOptions.FromDocumentAsync(documentOpt, cancellationToken).ConfigureAwait(false); + nestedActions.Add(PragmaWarningCodeAction.Create(suppressionTargetInfo, documentOpt, lazyFormattingOptions.Value, diagnostic, this)); } // SuppressMessageAttribute suppression is not supported for compiler diagnostics. diff --git a/src/Features/Core/Portable/CodeRefactorings/AddMissingImports/AbstractAddMissingImportsFeatureService.cs b/src/Features/Core/Portable/CodeRefactorings/AddMissingImports/AbstractAddMissingImportsFeatureService.cs index 55d6bdcacbbb5..eaa49c241a508 100644 --- a/src/Features/Core/Portable/CodeRefactorings/AddMissingImports/AbstractAddMissingImportsFeatureService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/AddMissingImports/AbstractAddMissingImportsFeatureService.cs @@ -140,8 +140,7 @@ private static async Task ApplyFixesAsync(Document document, Immutable private static async Task CleanUpNewLinesAsync(Document document, IEnumerable insertSpans, CancellationToken cancellationToken) { - var languageFormatter = document.GetRequiredLanguageService(); - var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); + var options = await SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false); var newDocument = document; @@ -150,21 +149,19 @@ private static async Task CleanUpNewLinesAsync(Document document, IEnu // to separate the import section from the other content. foreach (var insertSpan in insertSpans) { - newDocument = await CleanUpNewLinesAsync(newDocument, insertSpan, languageFormatter, options, cancellationToken).ConfigureAwait(false); + newDocument = await CleanUpNewLinesAsync(newDocument, insertSpan, options, cancellationToken).ConfigureAwait(false); } return newDocument; } - private static async Task CleanUpNewLinesAsync(Document document, TextSpan insertSpan, ISyntaxFormattingService languageFormatter, OptionSet optionSet, CancellationToken cancellationToken) + private static async Task CleanUpNewLinesAsync(Document document, TextSpan insertSpan, SyntaxFormattingOptions options, CancellationToken cancellationToken) { var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); - var optionService = document.Project.Solution.Workspace.Services.GetRequiredService(); - var shouldUseFormattingSpanCollapse = optionSet.GetOption(FormattingBehaviorOptions.AllowDisjointSpanMerging); - var options = optionSet.AsAnalyzerConfigOptions(optionService, root.Language); + var services = document.Project.Solution.Workspace.Services; - var textChanges = languageFormatter.Format(root, new[] { insertSpan }, shouldUseFormattingSpanCollapse, options, new[] { new CleanUpNewLinesFormatter(text) }, cancellationToken).GetTextChanges(cancellationToken); + var textChanges = Formatter.GetFormattedTextChanges(root, new[] { insertSpan }, services, options, rules: new[] { new CleanUpNewLinesFormatter(text) }, cancellationToken); // If there are no changes then, do less work. if (textChanges.Count == 0) diff --git a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs index b2c6802a6bddd..92c99eb637d8c 100644 --- a/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/SyncNamespace/AbstractChangeNamespaceService.cs @@ -623,7 +623,9 @@ private async Task FixDeclarationDocumentAsync( .WithAdditionalAnnotations(Formatter.Annotation); // Need to invoke formatter explicitly since we are doing the diff merge ourselves. - root = Formatter.Format(root, Formatter.Annotation, documentWithAddedImports.Project.Solution.Workspace, optionSet, cancellationToken); + var services = documentWithAddedImports.Project.Solution.Workspace.Services; + var formattingOptions = SyntaxFormattingOptions.Create(optionSet, services, root.Language); + root = Formatter.Format(root, Formatter.Annotation, services, formattingOptions, cancellationToken); root = root.WithAdditionalAnnotations(Simplifier.Annotation); var formattedDocument = documentWithAddedImports.WithSyntaxRoot(root); diff --git a/src/Features/Core/Portable/ExtractClass/ExtractClassWithDialogCodeAction.cs b/src/Features/Core/Portable/ExtractClass/ExtractClassWithDialogCodeAction.cs index bc64f943536de..623c9595854f5 100644 --- a/src/Features/Core/Portable/ExtractClass/ExtractClassWithDialogCodeAction.cs +++ b/src/Features/Core/Portable/ExtractClass/ExtractClassWithDialogCodeAction.cs @@ -52,7 +52,7 @@ public ExtractClassWithDialogCodeAction( public override object? GetOptions(CancellationToken cancellationToken) { var extractClassService = _service ?? _document.Project.Solution.Workspace.Services.GetRequiredService(); - return extractClassService.GetExtractClassOptionsAsync(_document, _selectedType, _selectedMember) + return extractClassService.GetExtractClassOptionsAsync(_document, _selectedType, _selectedMember, cancellationToken) .WaitAndGetResult_CanCallOnBackground(cancellationToken); } diff --git a/src/Features/Core/Portable/ExtractClass/IExtractClassOptionsService.cs b/src/Features/Core/Portable/ExtractClass/IExtractClassOptionsService.cs index 32d5a353dcceb..3a8fb90d91357 100644 --- a/src/Features/Core/Portable/ExtractClass/IExtractClassOptionsService.cs +++ b/src/Features/Core/Portable/ExtractClass/IExtractClassOptionsService.cs @@ -2,6 +2,7 @@ // 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.Host; @@ -9,6 +10,6 @@ namespace Microsoft.CodeAnalysis.ExtractClass { internal interface IExtractClassOptionsService : IWorkspaceService { - Task GetExtractClassOptionsAsync(Document document, INamedTypeSymbol originalType, ISymbol? selectedMember); + Task GetExtractClassOptionsAsync(Document document, INamedTypeSymbol originalType, ISymbol? selectedMember, CancellationToken cancellationToken); } } diff --git a/src/Features/Core/Portable/ExtractInterface/AbstractExtractInterfaceService.cs b/src/Features/Core/Portable/ExtractInterface/AbstractExtractInterfaceService.cs index fbefd43962602..11ca09855708a 100644 --- a/src/Features/Core/Portable/ExtractInterface/AbstractExtractInterfaceService.cs +++ b/src/Features/Core/Portable/ExtractInterface/AbstractExtractInterfaceService.cs @@ -250,7 +250,7 @@ private async Task ExtractInterfaceToSameFileAsync( navigationDocumentId: refactoringResult.DocumentToExtractFrom.Id); } - internal static Task GetExtractInterfaceOptionsAsync( + internal static async Task GetExtractInterfaceOptionsAsync( Document document, INamedTypeSymbol type, IEnumerable extractableMembers, @@ -262,10 +262,11 @@ internal static Task GetExtractInterfaceOptionsAs var defaultInterfaceName = NameGenerator.GenerateUniqueName(candidateInterfaceName, name => !conflictingTypeNames.Contains(name)); var syntaxFactsService = document.GetLanguageService(); var notificationService = document.Project.Solution.Workspace.Services.GetService(); - var generatedNameTypeParameterSuffix = ExtractTypeHelpers.GetTypeParameterSuffix(document, type, extractableMembers); + var formattingOptions = await SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false); + var generatedNameTypeParameterSuffix = ExtractTypeHelpers.GetTypeParameterSuffix(document, formattingOptions, type, extractableMembers, cancellationToken); var service = document.Project.Solution.Workspace.Services.GetService(); - return service.GetExtractInterfaceOptionsAsync( + return await service.GetExtractInterfaceOptionsAsync( syntaxFactsService, notificationService, extractableMembers.ToList(), @@ -273,7 +274,8 @@ internal static Task GetExtractInterfaceOptionsAs conflictingTypeNames.ToList(), containingNamespace, generatedNameTypeParameterSuffix, - document.Project.Language); + document.Project.Language, + cancellationToken).ConfigureAwait(false); } private static async Task GetFormattedSolutionAsync(Solution unformattedSolution, IEnumerable documentIds, CancellationToken cancellationToken) diff --git a/src/Features/Core/Portable/ExtractInterface/IExtractInterfaceOptionsService.cs b/src/Features/Core/Portable/ExtractInterface/IExtractInterfaceOptionsService.cs index 4bf9ed0dd0eb8..025edd6135834 100644 --- a/src/Features/Core/Portable/ExtractInterface/IExtractInterfaceOptionsService.cs +++ b/src/Features/Core/Portable/ExtractInterface/IExtractInterfaceOptionsService.cs @@ -5,6 +5,7 @@ #nullable disable using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageServices; @@ -22,6 +23,7 @@ Task GetExtractInterfaceOptionsAsync( List conflictingTypeNames, string defaultNamespace, string generatedNameTypeParameterSuffix, - string languageName); + string languageName, + CancellationToken cancellationToken); } } diff --git a/src/Features/Core/Portable/ExtractMethod/ExtractMethodResult.cs b/src/Features/Core/Portable/ExtractMethod/ExtractMethodResult.cs index a4344dbd5a215..b47327e345306 100644 --- a/src/Features/Core/Portable/ExtractMethod/ExtractMethodResult.cs +++ b/src/Features/Core/Portable/ExtractMethod/ExtractMethodResult.cs @@ -94,8 +94,12 @@ internal ExtractMethodResult( var simplifiedDocument = await Simplifier.ReduceAsync(annotatedDocument, Simplifier.Annotation, optionSet: null, cancellationToken).ConfigureAwait(false); var simplifiedRoot = await simplifiedDocument.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var documentOptions = await DocumentWithoutFinalFormatting.GetOptionsAsync(cancellationToken).ConfigureAwait(false); - var formattedDocument = simplifiedDocument.WithSyntaxRoot(Formatter.Format(simplifiedRoot, Formatter.Annotation, DocumentWithoutFinalFormatting.Project.Solution.Workspace, documentOptions, FormattingRules, cancellationToken)); + var options = await SyntaxFormattingOptions.FromDocumentAsync(DocumentWithoutFinalFormatting, cancellationToken).ConfigureAwait(false); + var services = DocumentWithoutFinalFormatting.Project.Solution.Workspace.Services; + + var formattedDocument = simplifiedDocument.WithSyntaxRoot( + Formatter.Format(simplifiedRoot, Formatter.Annotation, services, options, FormattingRules, cancellationToken)); + var formattedRoot = await formattedDocument.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); return (formattedDocument, formattedRoot.GetAnnotatedTokens(annotation).Single()); } diff --git a/src/Features/Core/Portable/Formatting/FormattingCodeFixProvider.cs b/src/Features/Core/Portable/Formatting/FormattingCodeFixProvider.cs index 961e87a83bdb5..ba339b3690055 100644 --- a/src/Features/Core/Portable/Formatting/FormattingCodeFixProvider.cs +++ b/src/Features/Core/Portable/Formatting/FormattingCodeFixProvider.cs @@ -46,9 +46,9 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context) private static async Task FixOneAsync(CodeFixContext context, Diagnostic diagnostic, CancellationToken cancellationToken) { - var options = await context.Document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); + var options = await SyntaxFormattingOptions.FromDocumentAsync(context.Document, cancellationToken).ConfigureAwait(false); var tree = await context.Document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); - var formattedTree = await FormattingCodeFixHelper.FixOneAsync(tree, context.Document.Project.Solution.Workspace, options, diagnostic, cancellationToken).ConfigureAwait(false); + var formattedTree = await FormattingCodeFixHelper.FixOneAsync(tree, context.Document.Project.Solution.Workspace.Services, options, diagnostic, cancellationToken).ConfigureAwait(false); return context.Document.WithSyntaxRoot(await formattedTree.GetRootAsync(cancellationToken).ConfigureAwait(false)); } diff --git a/src/Features/Core/Portable/Formatting/FormattingDiagnosticAnalyzer.cs b/src/Features/Core/Portable/Formatting/FormattingDiagnosticAnalyzer.cs index ab0ee07a389fb..61acf0f0ccee5 100644 --- a/src/Features/Core/Portable/Formatting/FormattingDiagnosticAnalyzer.cs +++ b/src/Features/Core/Portable/Formatting/FormattingDiagnosticAnalyzer.cs @@ -38,10 +38,10 @@ private void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) var tree = context.Tree; var cancellationToken = context.CancellationToken; + var optionSet = context.Options.GetAnalyzerOptionSet(tree, cancellationToken); + var options = SyntaxFormattingOptions.Create(optionSet, workspaceAnalyzerOptions.Services, tree.Options.Language); - var options = context.Options.GetAnalyzerOptionSet(tree, cancellationToken); - var workspace = workspaceAnalyzerOptions.Services.Workspace; - FormattingAnalyzerHelper.AnalyzeSyntaxTree(context, workspace, Descriptor, options); + FormattingAnalyzerHelper.AnalyzeSyntaxTree(context, workspaceAnalyzerOptions.Services, Descriptor, options); } } } diff --git a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.cs b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.cs index 3886be3109624..463256884a8c8 100644 --- a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.cs +++ b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.cs @@ -56,8 +56,14 @@ public async Task AddSourceToAsync(Document document, Compilation symb var docWithAssemblyInfo = await AddAssemblyInfoRegionAsync(docWithDocComments, symbolCompilation, symbol.GetOriginalUnreducedDefinition(), cancellationToken).ConfigureAwait(false); var node = await docWithAssemblyInfo.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var options = await SyntaxFormattingOptions.FromDocumentAsync(docWithAssemblyInfo, cancellationToken).ConfigureAwait(false); + var formattedDoc = await Formatter.FormatAsync( - docWithAssemblyInfo, SpecializedCollections.SingletonEnumerable(node.FullSpan), options: null, rules: GetFormattingRules(docWithAssemblyInfo), cancellationToken: cancellationToken).ConfigureAwait(false); + docWithAssemblyInfo, + SpecializedCollections.SingletonEnumerable(node.FullSpan), + options, + GetFormattingRules(docWithAssemblyInfo), + cancellationToken).ConfigureAwait(false); var reducers = GetReducers(); return await Simplifier.ReduceAsync(formattedDoc, reducers, null, cancellationToken).ConfigureAwait(false); diff --git a/src/Features/Core/Portable/Shared/Utilities/ExtractTypeHelpers.cs b/src/Features/Core/Portable/Shared/Utilities/ExtractTypeHelpers.cs index f61368657e11f..c075b5a90e15c 100644 --- a/src/Features/Core/Portable/Shared/Utilities/ExtractTypeHelpers.cs +++ b/src/Features/Core/Portable/Shared/Utilities/ExtractTypeHelpers.cs @@ -109,7 +109,7 @@ internal static class ExtractTypeHelpers return (formattedDocument, typeAnnotation); } - public static string GetTypeParameterSuffix(Document document, INamedTypeSymbol type, IEnumerable extractableMembers) + public static string GetTypeParameterSuffix(Document document, SyntaxFormattingOptions options, INamedTypeSymbol type, IEnumerable extractableMembers, CancellationToken cancellationToken) { var typeParameters = GetRequiredTypeParametersForMembers(type, extractableMembers); @@ -121,7 +121,7 @@ public static string GetTypeParameterSuffix(Document document, INamedTypeSymbol var typeParameterNames = typeParameters.SelectAsArray(p => p.Name); var syntaxGenerator = SyntaxGenerator.GetGenerator(document); - return Formatter.Format(syntaxGenerator.SyntaxGeneratorInternal.TypeParameterList(typeParameterNames), document.Project.Solution.Workspace).ToString(); + return Formatter.Format(syntaxGenerator.SyntaxGeneratorInternal.TypeParameterList(typeParameterNames), document.Project.Solution.Workspace.Services, options, cancellationToken).ToString(); } public static ImmutableArray GetRequiredTypeParametersForMembers(INamedTypeSymbol type, IEnumerable includedMembers) diff --git a/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs b/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs index 795ebf87b57bf..1da942098fcb3 100644 --- a/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs +++ b/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs @@ -280,8 +280,8 @@ private async Task FormatAsync(SyntaxNode newRoot, Document document return newRoot; } - var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); - return Formatter.Format(newRoot, SpecializedFormattingAnnotation, document.Project.Solution.Workspace, options, formattingRules, cancellationToken); + var options = await SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false); + return Formatter.Format(newRoot, SpecializedFormattingAnnotation, document.Project.Solution.Workspace.Services, options, formattingRules, cancellationToken); } private static bool IsWrittenToOutsideOfConstructorOrProperty( diff --git a/src/Features/VisualBasic/Portable/CodeFixes/OverloadBase/OverloadBaseCodeFixProvider.AddKeywordAction.vb b/src/Features/VisualBasic/Portable/CodeFixes/OverloadBase/OverloadBaseCodeFixProvider.AddKeywordAction.vb index 8f18177011833..778a7bbe68d2e 100644 --- a/src/Features/VisualBasic/Portable/CodeFixes/OverloadBase/OverloadBaseCodeFixProvider.AddKeywordAction.vb +++ b/src/Features/VisualBasic/Portable/CodeFixes/OverloadBase/OverloadBaseCodeFixProvider.AddKeywordAction.vb @@ -42,15 +42,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.OverloadBase Protected Overrides Async Function GetChangedDocumentAsync(cancellationToken As CancellationToken) As Task(Of Document) Dim root = Await _document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(False) - Dim options = Await _document.GetOptionsAsync(cancellationToken).ConfigureAwait(False) + Dim options = Await SyntaxFormattingOptions.FromDocumentAsync(_document, cancellationToken).ConfigureAwait(False) - Dim newNode = Await GetNewNodeAsync(_document, _node, Options, cancellationToken).ConfigureAwait(False) + Dim newNode = Await GetNewNodeAsync(_document, _node, options, cancellationToken).ConfigureAwait(False) Dim newRoot = root.ReplaceNode(_node, newNode) Return _document.WithSyntaxRoot(newRoot) End Function - Private Async Function GetNewNodeAsync(document As Document, node As SyntaxNode, options As OptionSet, cancellationToken As CancellationToken) As Task(Of SyntaxNode) + Private Async Function GetNewNodeAsync(document As Document, node As SyntaxNode, options As SyntaxFormattingOptions, cancellationToken As CancellationToken) As Task(Of SyntaxNode) Dim newNode As SyntaxNode = Nothing Dim trivia As SyntaxTriviaList = node.GetLeadingTrivia() node = node.WithoutLeadingTrivia() diff --git a/src/Features/VisualBasic/Portable/CodeFixes/Suppression/VisualBasicSuppressionCodeFixProvider.vb b/src/Features/VisualBasic/Portable/CodeFixes/Suppression/VisualBasicSuppressionCodeFixProvider.vb index ee3efba7ebf94..8e03fe3750f60 100644 --- a/src/Features/VisualBasic/Portable/CodeFixes/Suppression/VisualBasicSuppressionCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/CodeFixes/Suppression/VisualBasicSuppressionCodeFixProvider.vb @@ -10,6 +10,7 @@ Imports Microsoft.CodeAnalysis.AddImports Imports Microsoft.CodeAnalysis.CodeFixes Imports Microsoft.CodeAnalysis.CodeFixes.Suppression Imports Microsoft.CodeAnalysis.Formatting +Imports Microsoft.CodeAnalysis.Host Imports Microsoft.CodeAnalysis.Simplification Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -24,18 +25,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.Suppression Public Sub New() End Sub - Protected Overrides Function CreatePragmaRestoreDirectiveTrivia(diagnostic As Diagnostic, formatNode As Func(Of SyntaxNode, SyntaxNode), needsLeadingEndOfLine As Boolean, needsTrailingEndOfLine As Boolean) As SyntaxTriviaList + Protected Overrides Function CreatePragmaRestoreDirectiveTrivia(diagnostic As Diagnostic, formatNode As Func(Of SyntaxNode, CancellationToken, SyntaxNode), needsLeadingEndOfLine As Boolean, needsTrailingEndOfLine As Boolean, cancellationToken As CancellationToken) As SyntaxTriviaList Dim includeTitle As Boolean Dim errorCodes = GetErrorCodes(diagnostic, includeTitle) Dim pragmaDirective = SyntaxFactory.EnableWarningDirectiveTrivia(errorCodes) - Return CreatePragmaDirectiveTrivia(pragmaDirective, includeTitle, diagnostic, formatNode, needsLeadingEndOfLine, needsTrailingEndOfLine) + Return CreatePragmaDirectiveTrivia(pragmaDirective, includeTitle, diagnostic, formatNode, needsLeadingEndOfLine, needsTrailingEndOfLine, cancellationToken) End Function - Protected Overrides Function CreatePragmaDisableDirectiveTrivia(diagnostic As Diagnostic, formatNode As Func(Of SyntaxNode, SyntaxNode), needsLeadingEndOfLine As Boolean, needsTrailingEndOfLine As Boolean) As SyntaxTriviaList + Protected Overrides Function CreatePragmaDisableDirectiveTrivia(diagnostic As Diagnostic, formatNode As Func(Of SyntaxNode, CancellationToken, SyntaxNode), needsLeadingEndOfLine As Boolean, needsTrailingEndOfLine As Boolean, cancellationToken As CancellationToken) As SyntaxTriviaList Dim includeTitle As Boolean Dim errorCodes = GetErrorCodes(diagnostic, includeTitle) Dim pragmaDirective = SyntaxFactory.DisableWarningDirectiveTrivia(errorCodes) - Return CreatePragmaDirectiveTrivia(pragmaDirective, includeTitle, diagnostic, formatNode, needsLeadingEndOfLine, needsTrailingEndOfLine) + Return CreatePragmaDirectiveTrivia(pragmaDirective, includeTitle, diagnostic, formatNode, needsLeadingEndOfLine, needsTrailingEndOfLine, cancellationToken) End Function Private Shared Function GetErrorCodes(diagnostic As Diagnostic, ByRef includeTitle As Boolean) As SeparatedSyntaxList(Of IdentifierNameSyntax) @@ -47,8 +48,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.Suppression Return New SeparatedSyntaxList(Of IdentifierNameSyntax)().Add(SyntaxFactory.IdentifierName(text)) End Function - Private Shared Function CreatePragmaDirectiveTrivia(enableOrDisablePragmaDirective As StructuredTriviaSyntax, includeTitle As Boolean, diagnostic As Diagnostic, formatNode As Func(Of SyntaxNode, SyntaxNode), needsLeadingEndOfLine As Boolean, needsTrailingEndOfLine As Boolean) As SyntaxTriviaList - enableOrDisablePragmaDirective = CType(formatNode(enableOrDisablePragmaDirective), StructuredTriviaSyntax) + Private Shared Function CreatePragmaDirectiveTrivia( + enableOrDisablePragmaDirective As StructuredTriviaSyntax, + includeTitle As Boolean, + diagnostic As Diagnostic, + formatNode As Func(Of SyntaxNode, CancellationToken, SyntaxNode), + needsLeadingEndOfLine As Boolean, + needsTrailingEndOfLine As Boolean, + cancellationToken As CancellationToken) As SyntaxTriviaList + + enableOrDisablePragmaDirective = CType(formatNode(enableOrDisablePragmaDirective, cancellationToken), StructuredTriviaSyntax) Dim pragmaDirectiveTrivia = SyntaxFactory.Trivia(enableOrDisablePragmaDirective) Dim endOfLineTrivia = SyntaxFactory.ElasticCarriageReturnLineFeed Dim triviaList = SyntaxFactory.TriviaList(pragmaDirectiveTrivia) @@ -109,8 +118,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.Suppression targetSymbol As ISymbol, suppressMessageAttribute As INamedTypeSymbol, diagnostic As Diagnostic, - workspace As Workspace, - compilation As Compilation, + services As HostWorkspaceServices, + options As SyntaxFormattingOptions, addImportsService As IAddImportsService, cancellationToken As CancellationToken) As SyntaxNode Dim compilationRoot = DirectCast(newRoot, CompilationUnitSyntax) @@ -129,7 +138,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.Suppression End If End If - attributeStatement = CType(Formatter.Format(attributeStatement, workspace, cancellationToken:=cancellationToken), AttributesStatementSyntax) + attributeStatement = CType(Formatter.Format(attributeStatement, services, options, cancellationToken), AttributesStatementSyntax) Dim leadingTrivia = If(isFirst AndAlso Not compilationRoot.HasLeadingTrivia, SyntaxFactory.TriviaList(SyntaxFactory.CommentTrivia(GlobalSuppressionsFileHeaderComment)), @@ -252,7 +261,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.Suppression Return SyntaxFactory.Trivia(newPragmaWarning) Case Else - throw ExceptionUtilities.UnexpectedValue(trivia.Kind()) + Throw ExceptionUtilities.UnexpectedValue(trivia.Kind()) End Select End Function End Class diff --git a/src/Tools/ExternalAccess/OmniSharp/Internal/ExtractClass/OmniSharpExtractClassOptionsService.cs b/src/Tools/ExternalAccess/OmniSharp/Internal/ExtractClass/OmniSharpExtractClassOptionsService.cs index a60eda3cfa1d3..6d4abad23938a 100644 --- a/src/Tools/ExternalAccess/OmniSharp/Internal/ExtractClass/OmniSharpExtractClassOptionsService.cs +++ b/src/Tools/ExternalAccess/OmniSharp/Internal/ExtractClass/OmniSharpExtractClassOptionsService.cs @@ -4,6 +4,7 @@ using System; using System.Composition; +using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.ExtractClass; using Microsoft.CodeAnalysis.ExtractClass; @@ -24,7 +25,7 @@ public OmniSharpExtractClassOptionsService(IOmniSharpExtractClassOptionsService _omniSharpExtractClassOptionsService = omniSharpExtractClassOptionsService; } - public async Task GetExtractClassOptionsAsync(Document document, INamedTypeSymbol originalType, ISymbol? selectedMember) + public async Task GetExtractClassOptionsAsync(Document document, INamedTypeSymbol originalType, ISymbol? selectedMember, CancellationToken cancellationToken) { var result = await _omniSharpExtractClassOptionsService.GetExtractClassOptionsAsync(document, originalType, selectedMember).ConfigureAwait(false); return result == null diff --git a/src/Tools/ExternalAccess/OmniSharp/Internal/ExtractInterface/OmniSharpExtractInterfaceOptionsService.cs b/src/Tools/ExternalAccess/OmniSharp/Internal/ExtractInterface/OmniSharpExtractInterfaceOptionsService.cs index ffcb263fbf3d4..08a2264ccfe8c 100644 --- a/src/Tools/ExternalAccess/OmniSharp/Internal/ExtractInterface/OmniSharpExtractInterfaceOptionsService.cs +++ b/src/Tools/ExternalAccess/OmniSharp/Internal/ExtractInterface/OmniSharpExtractInterfaceOptionsService.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Composition; using System.Text; +using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.ExtractInterface; using Microsoft.CodeAnalysis.ExtractInterface; @@ -28,7 +29,16 @@ public OmniSharpExtractInterfaceOptionsService(IOmniSharpExtractInterfaceOptions _omniSharpExtractInterfaceOptionsService = omniSharpExtractInterfaceOptionsService; } - public async Task GetExtractInterfaceOptionsAsync(ISyntaxFactsService syntaxFactsService, INotificationService notificationService, List extractableMembers, string defaultInterfaceName, List conflictingTypeNames, string defaultNamespace, string generatedNameTypeParameterSuffix, string languageName) + public async Task GetExtractInterfaceOptionsAsync( + ISyntaxFactsService syntaxFactsService, + INotificationService notificationService, + List extractableMembers, + string defaultInterfaceName, + List conflictingTypeNames, + string defaultNamespace, + string generatedNameTypeParameterSuffix, + string languageName, + CancellationToken cancellationToken) { var result = await _omniSharpExtractInterfaceOptionsService.GetExtractInterfaceOptionsAsync(extractableMembers, defaultInterfaceName).ConfigureAwait(false); return new( diff --git a/src/Tools/IdeBenchmarks/FormatterBenchmarks.cs b/src/Tools/IdeBenchmarks/FormatterBenchmarks.cs index 2c3dc828bdee3..801a4d2ad8ea4 100644 --- a/src/Tools/IdeBenchmarks/FormatterBenchmarks.cs +++ b/src/Tools/IdeBenchmarks/FormatterBenchmarks.cs @@ -43,7 +43,9 @@ public object FormatCSharp() using var workspace = TestWorkspace.CreateCSharp(text); var document = workspace.CurrentSolution.GetDocument(workspace.Documents.First().Id); - return Formatter.GetFormattedTextChanges(document.GetSyntaxRootSynchronously(CancellationToken.None), workspace); + var root = document.GetSyntaxRootSynchronously(CancellationToken.None); + var options = SyntaxFormattingOptions.Default; + return Formatter.GetFormattedTextChanges(root, workspace.Services, options, CancellationToken.None); } [Benchmark] @@ -54,7 +56,9 @@ public object FormatVisualBasic() using var workspace = TestWorkspace.CreateVisualBasic(text); var document = workspace.CurrentSolution.GetDocument(workspace.Documents.First().Id); - return Formatter.GetFormattedTextChanges(document.GetSyntaxRootSynchronously(CancellationToken.None), workspace); + var root = document.GetSyntaxRootSynchronously(CancellationToken.None); + var options = SyntaxFormattingOptions.Default; + return Formatter.GetFormattedTextChanges(root, workspace.Services, options, CancellationToken.None); } } } diff --git a/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs b/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs index 5d50180cff8ce..6efb4b55f0fba 100644 --- a/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs +++ b/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs @@ -328,15 +328,15 @@ private async Task FormatDocumentCreatedFromTemplateAsync(IVsHierarchy hierarchy } var rootToFormat = await addedDocument.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(true); - var documentOptions = await addedDocument.GetOptionsAsync(cancellationToken).ConfigureAwait(true); + var formattingOptions = await SyntaxFormattingOptions.FromDocumentAsync(addedDocument, cancellationToken).ConfigureAwait(true); // Format document var unformattedText = await addedDocument.GetTextAsync(cancellationToken).ConfigureAwait(true); - var formattedRoot = Formatter.Format(rootToFormat, workspace, documentOptions, cancellationToken); + var formattedRoot = Formatter.Format(rootToFormat, workspace.Services, formattingOptions, cancellationToken); var formattedText = formattedRoot.GetText(unformattedText.Encoding, unformattedText.ChecksumAlgorithm); // Ensure the line endings are normalized. The formatter doesn't touch everything if it doesn't need to. - var targetLineEnding = documentOptions.GetOption(FormattingOptions.NewLine)!; + var targetLineEnding = formattingOptions.Options.GetOption(FormattingOptions.NewLine)!; var originalText = formattedText; foreach (var originalLine in originalText.Lines) diff --git a/src/VisualStudio/Core/Def/Implementation/ExtractClass/VisualStudioExtractClassOptionsService.cs b/src/VisualStudio/Core/Def/Implementation/ExtractClass/VisualStudioExtractClassOptionsService.cs index 47956b2589d6f..8d338faf29c32 100644 --- a/src/VisualStudio/Core/Def/Implementation/ExtractClass/VisualStudioExtractClassOptionsService.cs +++ b/src/VisualStudio/Core/Def/Implementation/ExtractClass/VisualStudioExtractClassOptionsService.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.ExtractClass; +using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.Notification; @@ -44,7 +45,7 @@ public VisualStudioExtractClassOptionsService( _uiThreadOperationExecutor = uiThreadOperationExecutor; } - public async Task GetExtractClassOptionsAsync(Document document, INamedTypeSymbol selectedType, ISymbol? selectedMember) + public async Task GetExtractClassOptionsAsync(Document document, INamedTypeSymbol selectedType, ISymbol? selectedMember, CancellationToken cancellationToken) { var notificationService = document.Project.Solution.Workspace.Services.GetRequiredService(); @@ -62,10 +63,9 @@ public VisualStudioExtractClassOptionsService( IsCheckable = true }); - using var cancellationTokenSource = new CancellationTokenSource(); - var memberToDependentsMap = SymbolDependentsBuilder.FindMemberToDependentsMap(membersInType, document.Project, cancellationTokenSource.Token); + var memberToDependentsMap = SymbolDependentsBuilder.FindMemberToDependentsMap(membersInType, document.Project, cancellationToken); - var conflictingTypeNames = selectedType.ContainingNamespace.GetAllTypes(cancellationTokenSource.Token).Select(t => t.Name); + var conflictingTypeNames = selectedType.ContainingNamespace.GetAllTypes(cancellationToken).Select(t => t.Name); var candidateName = selectedType.Name + "Base"; var defaultTypeName = NameGenerator.GenerateUniqueName(candidateName, name => !conflictingTypeNames.Contains(name)); @@ -73,7 +73,8 @@ public VisualStudioExtractClassOptionsService( ? string.Empty : selectedType.ContainingNamespace.ToDisplayString(); - var generatedNameTypeParameterSuffix = ExtractTypeHelpers.GetTypeParameterSuffix(document, selectedType, membersInType); + var formattingOptions = await SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false); + var generatedNameTypeParameterSuffix = ExtractTypeHelpers.GetTypeParameterSuffix(document, formattingOptions, selectedType, membersInType, cancellationToken); var viewModel = new ExtractClassViewModel( _uiThreadOperationExecutor, @@ -87,7 +88,7 @@ public VisualStudioExtractClassOptionsService( conflictingTypeNames.ToImmutableArray(), document.GetRequiredLanguageService()); - await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(); + await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); var dialog = new ExtractClassDialog(viewModel); var result = dialog.ShowModal(); diff --git a/src/VisualStudio/Core/Def/Implementation/ExtractInterface/VisualStudioExtractInterfaceOptionsService.cs b/src/VisualStudio/Core/Def/Implementation/ExtractInterface/VisualStudioExtractInterfaceOptionsService.cs index 888947e05c4b5..91ac13f70a516 100644 --- a/src/VisualStudio/Core/Def/Implementation/ExtractInterface/VisualStudioExtractInterfaceOptionsService.cs +++ b/src/VisualStudio/Core/Def/Implementation/ExtractInterface/VisualStudioExtractInterfaceOptionsService.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Composition; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; @@ -43,9 +44,10 @@ public async Task GetExtractInterfaceOptionsAsync List allTypeNames, string defaultNamespace, string generatedNameTypeParameterSuffix, - string languageName) + string languageName, + CancellationToken cancellationToken) { - await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(); + await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); var viewModel = new ExtractInterfaceDialogViewModel( syntaxFactsService, diff --git a/src/VisualStudio/Core/Def/Implementation/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs b/src/VisualStudio/Core/Def/Implementation/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs index d454578f3c748..605558b60c14c 100644 --- a/src/VisualStudio/Core/Def/Implementation/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs +++ b/src/VisualStudio/Core/Def/Implementation/LanguageService/AbstractLanguageService`2.IVsLanguageTextOps.cs @@ -54,7 +54,7 @@ private int FormatWorker(IVsTextLayer textLayer, TextSpan[] selections, Cancella var root = document.GetSyntaxRootSynchronously(cancellationToken); var text = root.SyntaxTree.GetText(cancellationToken); - var options = document.GetOptionsAsync(cancellationToken).WaitAndGetResult(cancellationToken); + var options = SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).WaitAndGetResult(cancellationToken); var ts = selections.Single(); var start = text.Lines[ts.iStartLine].Start + ts.iStartIndex; @@ -68,7 +68,7 @@ private int FormatWorker(IVsTextLayer textLayer, TextSpan[] selections, Cancella // use formatting that return text changes rather than tree rewrite which is more expensive var formatter = document.GetRequiredLanguageService(); - var originalChanges = formatter.GetFormattingResult(root, SpecializedCollections.SingletonEnumerable(adjustedSpan), options, Workspace.Services, rules, cancellationToken) + var originalChanges = formatter.GetFormattingResult(root, SpecializedCollections.SingletonEnumerable(adjustedSpan), options, rules, cancellationToken) .GetTextChanges(cancellationToken); var originalSpan = RoslynTextSpan.FromBounds(start, end); diff --git a/src/VisualStudio/Core/Def/Implementation/Venus/ContainedDocument.cs b/src/VisualStudio/Core/Def/Implementation/Venus/ContainedDocument.cs index 9d7c619349412..8caf624088bc9 100644 --- a/src/VisualStudio/Core/Def/Implementation/Venus/ContainedDocument.cs +++ b/src/VisualStudio/Core/Def/Implementation/Venus/ContainedDocument.cs @@ -760,11 +760,14 @@ private void AdjustIndentation(IProjectionBuffer subjectBuffer, IEnumerable var editorOptionsFactory = _componentModel.GetService(); var editorOptions = editorOptionsFactory.GetOptions(DataBuffer); - var options = _workspace.Options - .WithChangedOption(FormattingOptions.NewLine, root.Language, editorOptions.GetNewLineCharacter()) - .WithChangedOption(FormattingOptions.UseTabs, root.Language, !editorOptions.IsConvertTabsToSpacesEnabled()) - .WithChangedOption(FormattingOptions.TabSize, root.Language, editorOptions.GetTabSize()) - .WithChangedOption(FormattingOptions.IndentationSize, root.Language, editorOptions.GetIndentSize()); + var options = SyntaxFormattingOptions.Create( + _workspace.Options + .WithChangedOption(FormattingOptions.NewLine, root.Language, editorOptions.GetNewLineCharacter()) + .WithChangedOption(FormattingOptions.UseTabs, root.Language, !editorOptions.IsConvertTabsToSpacesEnabled()) + .WithChangedOption(FormattingOptions.TabSize, root.Language, editorOptions.GetTabSize()) + .WithChangedOption(FormattingOptions.IndentationSize, root.Language, editorOptions.GetIndentSize()), + _workspace.Services, + document.Project.Language); using var pooledObject = SharedPools.Default>().GetPooledObject(); @@ -785,7 +788,7 @@ private void AdjustIndentation(IProjectionBuffer subjectBuffer, IEnumerable } private void AdjustIndentationForSpan( - Document document, ITextEdit edit, TextSpan visibleSpan, AbstractFormattingRule baseIndentationRule, OptionSet options) + Document document, ITextEdit edit, TextSpan visibleSpan, AbstractFormattingRule baseIndentationRule, SyntaxFormattingOptions options) { var root = document.GetSyntaxRootSynchronously(CancellationToken.None); @@ -804,7 +807,7 @@ private void AdjustIndentationForSpan( var formatter = document.GetRequiredLanguageService(); var changes = formatter.GetFormattingResult( root, new TextSpan[] { CommonFormattingHelpers.GetFormattingSpan(root, visibleSpan) }, - options, services, formattingRules, CancellationToken.None).GetTextChanges(CancellationToken.None); + options, formattingRules, CancellationToken.None).GetTextChanges(CancellationToken.None); visibleSpans.Add(visibleSpan); var newChanges = FilterTextChanges(document.GetTextSynchronously(CancellationToken.None), visibleSpans, changes.ToReadOnlyCollection()).Where(t => visibleSpan.Contains(t.Span)); diff --git a/src/VisualStudio/Core/Def/Implementation/Venus/ContainedLanguageCodeSupport.cs b/src/VisualStudio/Core/Def/Implementation/Venus/ContainedLanguageCodeSupport.cs index 50bb61eda988f..9f2d055f1b8d5 100644 --- a/src/VisualStudio/Core/Def/Implementation/Venus/ContainedLanguageCodeSupport.cs +++ b/src/VisualStudio/Core/Def/Implementation/Venus/ContainedLanguageCodeSupport.cs @@ -229,12 +229,13 @@ public static Tuple EnsureEventHandler( targetDocument.WithSyntaxRoot(newRoot), Simplifier.Annotation, null, cancellationToken).WaitAndGetResult_Venus(cancellationToken).GetSyntaxRootSynchronously(cancellationToken); var formattingRules = additionalFormattingRule.Concat(Formatter.GetDefaultFormattingRules(targetDocument)); + var formattingOptions = SyntaxFormattingOptions.FromDocumentAsync(targetDocument, cancellationToken).WaitAndGetResult_Venus(cancellationToken); newRoot = Formatter.Format( newRoot, Formatter.Annotation, - targetDocument.Project.Solution.Workspace, - targetDocument.GetOptionsAsync(cancellationToken).WaitAndGetResult_Venus(cancellationToken), + targetDocument.Project.Solution.Workspace.Services, + formattingOptions, formattingRules, cancellationToken); diff --git a/src/VisualStudio/Core/Impl/CodeModel/AbstractCodeModelService.cs b/src/VisualStudio/Core/Impl/CodeModel/AbstractCodeModelService.cs index 4bea4cbdbea50..0e496a9538361 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/AbstractCodeModelService.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/AbstractCodeModelService.cs @@ -1036,12 +1036,17 @@ private Document FormatAnnotatedNode(Document document, SyntaxAnnotation annotat formattingRules = additionalRules.Concat(formattingRules); } - return _threadingContext.JoinableTaskFactory.Run(() => Formatter.FormatAsync( - document, - new TextSpan[] { formattingSpan }, - options: null, - rules: formattingRules, - cancellationToken: cancellationToken)); + return _threadingContext.JoinableTaskFactory.Run(async () => + { + var options = await SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false); + + return await Formatter.FormatAsync( + document, + new TextSpan[] { formattingSpan }, + options, + formattingRules, + cancellationToken).ConfigureAwait(false); + }); } private SyntaxNode InsertNode( diff --git a/src/Workspaces/CSharp/Portable/Indentation/CSharpIndentationService.Indenter.cs b/src/Workspaces/CSharp/Portable/Indentation/CSharpIndentationService.Indenter.cs index 9768531512415..2f68fcc43c43a 100644 --- a/src/Workspaces/CSharp/Portable/Indentation/CSharpIndentationService.Indenter.cs +++ b/src/Workspaces/CSharp/Portable/Indentation/CSharpIndentationService.Indenter.cs @@ -24,11 +24,11 @@ protected override bool ShouldUseTokenIndenter(Indenter indenter, out SyntaxToke protected override ISmartTokenFormatter CreateSmartTokenFormatter(Indenter indenter) { - var workspace = indenter.Document.Project.Solution.Workspace; - var formattingRuleFactory = workspace.Services.GetRequiredService(); + var services = indenter.Document.Project.Solution.Workspace.Services; + var formattingRuleFactory = services.GetRequiredService(); var rules = formattingRuleFactory.CreateRule(indenter.Document.Document, indenter.LineToBeIndented.Start).Concat(Formatter.GetDefaultFormattingRules(indenter.Document.Document)); - - return new CSharpSmartTokenFormatter(indenter.OptionSet, rules, indenter.Root); + var formattingOptions = SyntaxFormattingOptions.Create(indenter.OptionSet, services, indenter.Document.Project.Language); + return new CSharpSmartTokenFormatter(formattingOptions, rules, indenter.Root); } protected override IndentationResult? GetDesiredIndentationWorker(Indenter indenter, SyntaxToken? tokenOpt, SyntaxTrivia? triviaOpt) diff --git a/src/Workspaces/CSharp/Portable/Indentation/CSharpSmartTokenFormatter.cs b/src/Workspaces/CSharp/Portable/Indentation/CSharpSmartTokenFormatter.cs index a6e8b6760756a..874a56a3e1410 100644 --- a/src/Workspaces/CSharp/Portable/Indentation/CSharpSmartTokenFormatter.cs +++ b/src/Workspaces/CSharp/Portable/Indentation/CSharpSmartTokenFormatter.cs @@ -22,21 +22,20 @@ namespace Microsoft.CodeAnalysis.CSharp.Indentation { internal class CSharpSmartTokenFormatter : ISmartTokenFormatter { - private readonly OptionSet _optionSet; + private readonly SyntaxFormattingOptions _options; private readonly IEnumerable _formattingRules; private readonly CompilationUnitSyntax _root; public CSharpSmartTokenFormatter( - OptionSet optionSet, + SyntaxFormattingOptions options, IEnumerable formattingRules, CompilationUnitSyntax root) { - Contract.ThrowIfNull(optionSet); Contract.ThrowIfNull(formattingRules); Contract.ThrowIfNull(root); - _optionSet = optionSet; + _options = options; _formattingRules = formattingRules; _root = root; @@ -61,9 +60,7 @@ public IList FormatRange( smartTokenformattingRules = (new NoLineChangeFormattingRule()).Concat(_formattingRules); } - var formatter = services.GetRequiredLanguageService(_root.Language); - return formatter.GetFormattingResult(_root, new[] { TextSpan.FromBounds(startToken.SpanStart, endToken.Span.End) }, _optionSet, services, smartTokenformattingRules, cancellationToken). - GetTextChanges(cancellationToken); + return Formatter.GetFormattedTextChanges(_root, new[] { TextSpan.FromBounds(startToken.SpanStart, endToken.Span.End) }, services, _options, smartTokenformattingRules, cancellationToken); } private static bool CloseBraceOfTryOrDoBlock(SyntaxToken endToken) @@ -103,9 +100,9 @@ public async Task> FormatTokenAsync( } } - var smartTokenformattingRules = (new SmartTokenFormattingRule()).Concat(_formattingRules); + var smartTokenformattingRules = new SmartTokenFormattingRule().Concat(_formattingRules); var adjustedStartPosition = previousToken.SpanStart; - var indentStyle = _optionSet.GetOption(FormattingOptions.SmartIndent, LanguageNames.CSharp); + var indentStyle = _options.Options.GetOption(FormattingOptions.SmartIndent); if (token.IsKind(SyntaxKind.OpenBraceToken) && indentStyle != FormattingOptions.IndentStyle.Smart) { @@ -117,9 +114,7 @@ public async Task> FormatTokenAsync( } } - var formatter = services.GetRequiredLanguageService(_root.Language); - return formatter.GetFormattingResult(_root, new[] { TextSpan.FromBounds(adjustedStartPosition, adjustedEndPosition) }, _optionSet, services, smartTokenformattingRules, cancellationToken). - GetTextChanges(cancellationToken); + return Formatter.GetFormattedTextChanges(_root, new[] { TextSpan.FromBounds(adjustedStartPosition, adjustedEndPosition) }, services, _options, smartTokenformattingRules, cancellationToken); } private class NoLineChangeFormattingRule : AbstractFormattingRule diff --git a/src/Workspaces/CSharpTest/CodeGeneration/SyntaxGeneratorTests.cs b/src/Workspaces/CSharpTest/CodeGeneration/SyntaxGeneratorTests.cs index fe2a1bedba472..43ebf73fbd137 100644 --- a/src/Workspaces/CSharpTest/CodeGeneration/SyntaxGeneratorTests.cs +++ b/src/Workspaces/CSharpTest/CodeGeneration/SyntaxGeneratorTests.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; @@ -3536,7 +3537,7 @@ public class C : IDisposable var newDecl = Generator.AddInterfaceType(decl, Generator.IdentifierName("IDisposable")); var newRoot = root.ReplaceNode(decl, newDecl); - var elasticOnlyFormatted = Formatter.Format(newRoot, SyntaxAnnotation.ElasticAnnotation, _workspace).ToFullString(); + var elasticOnlyFormatted = Formatter.Format(newRoot, SyntaxAnnotation.ElasticAnnotation, _workspace.Services, SyntaxFormattingOptions.Default, CancellationToken.None).ToFullString(); Assert.Equal(expected, elasticOnlyFormatted); } diff --git a/src/Workspaces/CSharpTest/Formatting/FormattingElasticTriviaTests.cs b/src/Workspaces/CSharpTest/Formatting/FormattingElasticTriviaTests.cs index 7b6c3913cac1b..5691bf443d07a 100644 --- a/src/Workspaces/CSharpTest/Formatting/FormattingElasticTriviaTests.cs +++ b/src/Workspaces/CSharpTest/Formatting/FormattingElasticTriviaTests.cs @@ -5,6 +5,7 @@ #nullable disable using System.Linq; +using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; @@ -88,7 +89,8 @@ class B Assert.NotNull(compilation); - var newCompilation = Formatter.Format(compilation, new AdhocWorkspace()); + using var workspace = new AdhocWorkspace(); + var newCompilation = Formatter.Format(compilation, workspace.Services, SyntaxFormattingOptions.Default, CancellationToken.None); Assert.Equal(expected, newCompilation.ToFullString()); } @@ -109,12 +111,13 @@ public class C public class SomeAttribute : System.Attribute { } "; - var ws = new AdhocWorkspace(); - var generator = SyntaxGenerator.GetGenerator(ws, LanguageNames.CSharp); + var workspace = new AdhocWorkspace(); + var generator = SyntaxGenerator.GetGenerator(workspace, LanguageNames.CSharp); var root = SyntaxFactory.ParseCompilationUnit(text); var decl = generator.GetDeclaration(root.DescendantNodes().OfType().First(vd => vd.Identifier.Text == "f2")); var newDecl = generator.AddAttributes(decl, generator.Attribute("Some")).WithAdditionalAnnotations(Formatter.Annotation); var newRoot = root.ReplaceNode(decl, newDecl); + var options = SyntaxFormattingOptions.Default; var expected = @" public class C @@ -129,13 +132,13 @@ public class C public class SomeAttribute : System.Attribute { } "; - var formatted = Formatter.Format(newRoot, ws).ToFullString(); + var formatted = Formatter.Format(newRoot, workspace.Services, options, CancellationToken.None).ToFullString(); Assert.Equal(expected, formatted); - var elasticOnlyFormatted = Formatter.Format(newRoot, SyntaxAnnotation.ElasticAnnotation, ws).ToFullString(); + var elasticOnlyFormatted = Formatter.Format(newRoot, SyntaxAnnotation.ElasticAnnotation, workspace.Services, options, CancellationToken.None).ToFullString(); Assert.Equal(expected, elasticOnlyFormatted); - var annotationFormatted = Formatter.Format(newRoot, Formatter.Annotation, ws).ToFullString(); + var annotationFormatted = Formatter.Format(newRoot, Formatter.Annotation, workspace.Services, options, CancellationToken.None).ToFullString(); Assert.Equal(expected, annotationFormatted); } @@ -191,7 +194,8 @@ public void FormatElasticTriviaBetweenPropertiesWithoutAccessors() Assert.NotNull(compilation); - var newCompilation = Formatter.Format(compilation, new AdhocWorkspace()); + using var workspace = new AdhocWorkspace(); + var newCompilation = Formatter.Format(compilation, workspace.Services, SyntaxFormattingOptions.Default, CancellationToken.None); Assert.Equal(expected, newCompilation.ToFullString()); } } diff --git a/src/Workspaces/CSharpTest/Formatting/FormattingMultipleSpanTests.cs b/src/Workspaces/CSharpTest/Formatting/FormattingMultipleSpanTests.cs index ebe635efbc802..06e04edf64408 100644 --- a/src/Workspaces/CSharpTest/Formatting/FormattingMultipleSpanTests.cs +++ b/src/Workspaces/CSharpTest/Formatting/FormattingMultipleSpanTests.cs @@ -169,7 +169,8 @@ public async Task EmptySpan() var document = project.AddDocument("Document", SourceText.From("")); var syntaxTree = await document.GetSyntaxTreeAsync(); - var result = Formatter.Format(await syntaxTree.GetRootAsync(), TextSpan.FromBounds(0, 0), workspace, cancellationToken: CancellationToken.None); + var root = await syntaxTree.GetRootAsync(); + var result = Formatter.Format(root, TextSpan.FromBounds(0, 0), workspace.Services, SyntaxFormattingOptions.Default, CancellationToken.None); } private Task AssertFormatAsync(string content, string expected, OptionsCollection changedOptionSet = null) diff --git a/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs b/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs index d33aca02deba6..dbe224461206c 100644 --- a/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs +++ b/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.Formatting; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -4626,8 +4627,8 @@ public void FormatArbitaryNode() }))); Assert.NotNull(property); - - var newProperty = Formatter.Format(property, new AdhocWorkspace()); + using var workspace = new AdhocWorkspace(); + var newProperty = Formatter.Format(property, workspace.Services, SyntaxFormattingOptions.Default, CancellationToken.None); Assert.Equal(expected, newProperty.ToFullString()); } @@ -8137,7 +8138,7 @@ static void Main(string[] args) public void DontAssumeCertainNodeAreAlwaysParented() { var block = SyntaxFactory.Block(); - Formatter.Format(block, new AdhocWorkspace()); + Formatter.Format(block, new AdhocWorkspace().Services, SyntaxFormattingOptions.Default, CancellationToken.None); } [WorkItem(776, "https://github.com/dotnet/roslyn/issues/776")] @@ -9212,7 +9213,7 @@ public void M() [Fact, Trait(Traits.Feature, Traits.Features.Formatting)] [WorkItem(25098, "https://github.com/dotnet/roslyn/issues/25098")] public void FormatSingleStructDeclaration() - => Formatter.Format(SyntaxFactory.StructDeclaration("S"), DefaultWorkspace); + => Formatter.Format(SyntaxFactory.StructDeclaration("S"), DefaultWorkspace.Services, SyntaxFormattingOptions.Default, CancellationToken.None); [Fact, Trait(Traits.Feature, Traits.Features.Formatting)] public async Task FormatIndexExpression() diff --git a/src/Workspaces/CSharpTest/Formatting/FormattingTreeEditTests.cs b/src/Workspaces/CSharpTest/Formatting/FormattingTreeEditTests.cs index 5afbf7ff21cae..b179f3762aab8 100644 --- a/src/Workspaces/CSharpTest/Formatting/FormattingTreeEditTests.cs +++ b/src/Workspaces/CSharpTest/Formatting/FormattingTreeEditTests.cs @@ -5,6 +5,7 @@ #nullable disable using System.Linq; +using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; @@ -36,26 +37,33 @@ void M(int? p) { } var g = SyntaxGenerator.GetGenerator(document); var root = await document.GetSyntaxRootAsync(); var attr = g.Attribute("MyAttr"); + var options = SyntaxFormattingOptions.Default; var param = root.DescendantNodes().OfType().First(); + var root1 = root.ReplaceNode(param, g.AddAttributes(param, g.Attribute("MyAttr"))); + + var result1 = Formatter.Format(root1, document.Project.Solution.Workspace.Services, options, CancellationToken.None); + Assert.Equal(@" public class C { void M([MyAttr] int? p) { } } -", Formatter.Format(root.ReplaceNode(param, g.AddAttributes(param, g.Attribute("MyAttr"))), - document.Project.Solution.Workspace).ToFullString()); +", result1.ToFullString()); // verify change doesn't affect how attributes appear before other kinds of declarations var method = root.DescendantNodes().OfType().First(); + + var root2 = root.ReplaceNode(method, g.AddAttributes(method, g.Attribute("MyAttr"))); + var result2 = Formatter.Format(root2, document.Project.Solution.Workspace.Services, options, CancellationToken.None); + Assert.Equal(@" public class C { [MyAttr] void M(int? p) { } } -", Formatter.Format(root.ReplaceNode(method, g.AddAttributes(method, g.Attribute("MyAttr"))), - document.Project.Solution.Workspace).ToFullString()); +", result2.ToFullString()); } } } diff --git a/src/Workspaces/CSharpTest/Formatting/FormattingTriviaTests.cs b/src/Workspaces/CSharpTest/Formatting/FormattingTriviaTests.cs index eb7f5943341f9..cefcd95929b4f 100644 --- a/src/Workspaces/CSharpTest/Formatting/FormattingTriviaTests.cs +++ b/src/Workspaces/CSharpTest/Formatting/FormattingTriviaTests.cs @@ -10,6 +10,7 @@ using Roslyn.Test.Utilities; using Xunit; using System.Threading.Tasks; +using System.Threading; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Formatting { @@ -1744,12 +1745,18 @@ static void Main(string[] args) [Fact, Trait(Traits.Feature, Traits.Features.Formatting)] public void NewLineOptions_LineFeedOnly() { + using var workspace = new AdhocWorkspace(); var tree = SyntaxFactory.ParseCompilationUnit("class C\r\n{\r\n}"); // replace all EOL trivia with elastic markers to force the formatter to add EOL back tree = tree.ReplaceTrivia(tree.DescendantTrivia().Where(tr => tr.IsKind(SyntaxKind.EndOfLineTrivia)), (o, r) => SyntaxFactory.ElasticMarker); - var formatted = Formatter.Format(tree, DefaultWorkspace, DefaultWorkspace.Options.WithChangedOption(FormattingOptions.NewLine, LanguageNames.CSharp, "\n")); + var options = SyntaxFormattingOptions.Create( + workspace.Options.WithChangedOption(FormattingOptions.NewLine, LanguageNames.CSharp, "\n"), + workspace.Services, + tree.Language); + + var formatted = Formatter.Format(tree, workspace.Services, options, CancellationToken.None); var actual = formatted.ToFullString(); var expected = "class C\n{\n}"; @@ -1790,7 +1797,14 @@ class F .WithLeadingTrivia(SyntaxFactory.TriviaList()) .WithAdditionalAnnotations(SyntaxAnnotation.ElasticAnnotation)); - var formatted = Formatter.Format(tree, DefaultWorkspace, DefaultWorkspace.Options.WithChangedOption(FormattingOptions.UseTabs, LanguageNames.CSharp, true)); + using var workspace = new AdhocWorkspace(); + + var options = SyntaxFormattingOptions.Create( + workspace.Options.WithChangedOption(FormattingOptions.UseTabs, LanguageNames.CSharp, true), + workspace.Services, + tree.Language); + + var formatted = Formatter.Format(tree, workspace.Services, options, CancellationToken.None); var actual = formatted.ToFullString(); Assert.Equal(expected, actual); diff --git a/src/Workspaces/Core/Portable/CodeCleanup/AbstractCodeCleanerService.cs b/src/Workspaces/Core/Portable/CodeCleanup/AbstractCodeCleanerService.cs index 2f827d24b1cd1..b9a3a9f39cf3c 100644 --- a/src/Workspaces/Core/Portable/CodeCleanup/AbstractCodeCleanerService.cs +++ b/src/Workspaces/Core/Portable/CodeCleanup/AbstractCodeCleanerService.cs @@ -11,15 +11,14 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeCleanup.Providers; using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared; using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; +using Microsoft.CodeAnalysis.Formatting; namespace Microsoft.CodeAnalysis.CodeCleanup { @@ -28,7 +27,7 @@ internal abstract class AbstractCodeCleanerService : ICodeCleanerService public abstract ImmutableArray GetDefaultProviders(); protected abstract ImmutableArray GetSpansToAvoid(SyntaxNode root); - public async Task CleanupAsync(Document document, ImmutableArray spans, OptionSet options, ImmutableArray providers, CancellationToken cancellationToken) + public async Task CleanupAsync(Document document, ImmutableArray spans, SyntaxFormattingOptions options, ImmutableArray providers, CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.CodeCleanup_CleanupAsync, cancellationToken)) { @@ -72,7 +71,7 @@ public async Task CleanupAsync(Document document, ImmutableArray CleanupAsync(SyntaxNode root, ImmutableArray spans, OptionSet options, HostWorkspaceServices services, ImmutableArray providers, CancellationToken cancellationToken) + public async Task CleanupAsync(SyntaxNode root, ImmutableArray spans, SyntaxFormattingOptions options, HostWorkspaceServices services, ImmutableArray providers, CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.CodeCleanup_Cleanup, cancellationToken)) { @@ -456,7 +455,7 @@ private static bool CleanupWholeNode(TextSpan nodeSpan, ImmutableArray private async Task IterateAllCodeCleanupProvidersAsync( Document originalDocument, Document annotatedDocument, - OptionSet options, + SyntaxFormattingOptions options, Func> spanGetter, ImmutableArray codeCleaners, CancellationToken cancellationToken) @@ -537,7 +536,7 @@ private ImmutableArray GetSpans( private async Task IterateAllCodeCleanupProvidersAsync( SyntaxNode originalRoot, SyntaxNode annotatedRoot, - OptionSet options, + SyntaxFormattingOptions options, Func> spanGetter, HostWorkspaceServices services, ImmutableArray codeCleaners, diff --git a/src/Workspaces/Core/Portable/CodeCleanup/CodeCleaner.cs b/src/Workspaces/Core/Portable/CodeCleanup/CodeCleaner.cs index 1ae8fd14ee9bf..aee43fe399a36 100644 --- a/src/Workspaces/Core/Portable/CodeCleanup/CodeCleaner.cs +++ b/src/Workspaces/Core/Portable/CodeCleanup/CodeCleaner.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Formatting; namespace Microsoft.CodeAnalysis.CodeCleanup { @@ -77,7 +78,7 @@ public static Task CleanupAsync(Document document, TextSpan span, Immu public static async Task CleanupAsync(Document document, ImmutableArray spans, ImmutableArray providers = default, CancellationToken cancellationToken = default) { var cleanupService = document.GetRequiredLanguageService(); - var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); + var options = await SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false); return await cleanupService.CleanupAsync(document, spans, options, providers, cancellationToken).ConfigureAwait(false); } @@ -95,7 +96,8 @@ public static Task CleanupAsync(SyntaxNode root, TextSpan span, Opti public static Task CleanupAsync(SyntaxNode root, ImmutableArray spans, OptionSet options, HostWorkspaceServices services, ImmutableArray providers = default, CancellationToken cancellationToken = default) { var cleanupService = services.GetLanguageServices(root.Language).GetRequiredService(); - return cleanupService.CleanupAsync(root, spans, options, services, providers, cancellationToken); + var formattingOptions = SyntaxFormattingOptions.Create(options, services, root.Language); + return cleanupService.CleanupAsync(root, spans, formattingOptions, services, providers, cancellationToken); } } } diff --git a/src/Workspaces/Core/Portable/CodeCleanup/ICodeCleanerService.cs b/src/Workspaces/Core/Portable/CodeCleanup/ICodeCleanerService.cs index b144328102b26..f9f47a1551ea9 100644 --- a/src/Workspaces/Core/Portable/CodeCleanup/ICodeCleanerService.cs +++ b/src/Workspaces/Core/Portable/CodeCleanup/ICodeCleanerService.cs @@ -6,7 +6,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeCleanup.Providers; -using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Text; @@ -27,13 +27,13 @@ internal interface ICodeCleanerService : ILanguageService /// /// This will run all provided code cleaners in an order that is given to the method. /// - Task CleanupAsync(Document document, ImmutableArray spans, OptionSet options, ImmutableArray providers, CancellationToken cancellationToken); + Task CleanupAsync(Document document, ImmutableArray spans, SyntaxFormattingOptions options, ImmutableArray providers, CancellationToken cancellationToken); /// /// This will run all provided code cleaners in an order that is given to the method. /// /// This will do cleanups that don't require any semantic information. /// - Task CleanupAsync(SyntaxNode root, ImmutableArray spans, OptionSet options, HostWorkspaceServices services, ImmutableArray providers, CancellationToken cancellationToken); + Task CleanupAsync(SyntaxNode root, ImmutableArray spans, SyntaxFormattingOptions options, HostWorkspaceServices services, ImmutableArray providers, CancellationToken cancellationToken); } } diff --git a/src/Workspaces/Core/Portable/CodeCleanup/Providers/FormatCodeCleanupProvider.cs b/src/Workspaces/Core/Portable/CodeCleanup/Providers/FormatCodeCleanupProvider.cs index e60fae987e8fb..073fb0fc3eafa 100644 --- a/src/Workspaces/Core/Portable/CodeCleanup/Providers/FormatCodeCleanupProvider.cs +++ b/src/Workspaces/Core/Portable/CodeCleanup/Providers/FormatCodeCleanupProvider.cs @@ -5,7 +5,6 @@ using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -17,11 +16,11 @@ internal sealed class FormatCodeCleanupProvider : ICodeCleanupProvider { public string Name => PredefinedCodeCleanupProviderNames.Format; - public async Task CleanupAsync(Document document, ImmutableArray spans, OptionSet options, CancellationToken cancellationToken) + public async Task CleanupAsync(Document document, ImmutableArray spans, SyntaxFormattingOptions options, CancellationToken cancellationToken) { var formatter = document.GetRequiredLanguageService(); var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var result = formatter.GetFormattingResult(root, spans, options, document.Project.Solution.Workspace.Services, rules: null, cancellationToken); + var result = formatter.GetFormattingResult(root, spans, options, rules: null, cancellationToken); // apply changes to an old text if it already exists return document.TryGetText(out var oldText) ? @@ -29,10 +28,10 @@ public async Task CleanupAsync(Document document, ImmutableArray CleanupAsync(SyntaxNode root, ImmutableArray spans, OptionSet options, HostWorkspaceServices services, CancellationToken cancellationToken) + public Task CleanupAsync(SyntaxNode root, ImmutableArray spans, SyntaxFormattingOptions options, HostWorkspaceServices services, CancellationToken cancellationToken) { var formatter = services.GetRequiredLanguageService(root.Language); - var result = formatter.GetFormattingResult(root, spans, options, services, rules: null, cancellationToken); + var result = formatter.GetFormattingResult(root, spans, options, rules: null, cancellationToken); // apply changes to an old text if it already exists return (root.SyntaxTree != null && root.SyntaxTree.TryGetText(out var oldText)) ? diff --git a/src/Workspaces/Core/Portable/CodeCleanup/Providers/ICodeCleanupProvider.cs b/src/Workspaces/Core/Portable/CodeCleanup/Providers/ICodeCleanupProvider.cs index 823b57fb46bda..52f2f2959a1be 100644 --- a/src/Workspaces/Core/Portable/CodeCleanup/Providers/ICodeCleanupProvider.cs +++ b/src/Workspaces/Core/Portable/CodeCleanup/Providers/ICodeCleanupProvider.cs @@ -5,8 +5,8 @@ using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CodeCleanup.Providers @@ -24,13 +24,13 @@ internal interface ICodeCleanupProvider /// /// This should apply its code clean up logic to the spans of the document. /// - Task CleanupAsync(Document document, ImmutableArray spans, OptionSet options, CancellationToken cancellationToken); + Task CleanupAsync(Document document, ImmutableArray spans, SyntaxFormattingOptions options, CancellationToken cancellationToken); /// /// This will run all provided code cleaners in an order that is given to the method. /// /// This will do cleanups that don't require any semantic information /// - Task CleanupAsync(SyntaxNode root, ImmutableArray spans, OptionSet options, HostWorkspaceServices services, CancellationToken cancellationToken); + Task CleanupAsync(SyntaxNode root, ImmutableArray spans, SyntaxFormattingOptions options, HostWorkspaceServices services, CancellationToken cancellationToken); } } diff --git a/src/Workspaces/Core/Portable/CodeCleanup/Providers/SimplificationCodeCleanupProvider.cs b/src/Workspaces/Core/Portable/CodeCleanup/Providers/SimplificationCodeCleanupProvider.cs index c4b372a73e608..70ea4aefd3909 100644 --- a/src/Workspaces/Core/Portable/CodeCleanup/Providers/SimplificationCodeCleanupProvider.cs +++ b/src/Workspaces/Core/Portable/CodeCleanup/Providers/SimplificationCodeCleanupProvider.cs @@ -6,7 +6,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Text; @@ -16,10 +16,10 @@ internal class SimplificationCodeCleanupProvider : ICodeCleanupProvider { public string Name => PredefinedCodeCleanupProviderNames.Simplification; - public Task CleanupAsync(Document document, ImmutableArray spans, OptionSet options, CancellationToken cancellationToken) + public Task CleanupAsync(Document document, ImmutableArray spans, SyntaxFormattingOptions options, CancellationToken cancellationToken) => Simplifier.ReduceAsync(document, spans, null, cancellationToken); - public Task CleanupAsync(SyntaxNode root, ImmutableArray spans, OptionSet options, HostWorkspaceServices services, CancellationToken cancellationToken) + public Task CleanupAsync(SyntaxNode root, ImmutableArray spans, SyntaxFormattingOptions options, HostWorkspaceServices services, CancellationToken cancellationToken) { // Simplifier doesn't work without semantic information return Task.FromResult(root); diff --git a/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs b/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs index b51c1fe60254b..96fb7047d135b 100644 --- a/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs +++ b/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs @@ -53,14 +53,11 @@ public abstract class SyntaxGenerator : ILanguageService public static SyntaxGenerator GetGenerator(Workspace workspace, string language) => GetGenerator(workspace.Services, language); - internal static SyntaxGenerator GetGenerator(HostWorkspaceServices services, string language) - => services.GetLanguageServices(language).GetRequiredService(); - /// /// Gets the for the specified language. /// internal static SyntaxGenerator GetGenerator(HostWorkspaceServices services, string language) - => services.GetLanguageServices(language).GetService(); + => services.GetLanguageServices(language).GetRequiredService(); /// /// Gets the for the language corresponding to the document. diff --git a/src/Workspaces/Core/Portable/Formatting/AbstractFormattingService.cs b/src/Workspaces/Core/Portable/Formatting/AbstractFormattingService.cs index f1968be47a0e8..047a6418fcdc7 100644 --- a/src/Workspaces/Core/Portable/Formatting/AbstractFormattingService.cs +++ b/src/Workspaces/Core/Portable/Formatting/AbstractFormattingService.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting { @@ -15,7 +16,10 @@ namespace Microsoft.CodeAnalysis.Formatting /// internal abstract class AbstractFormattingService : IFormattingService { - public Task FormatAsync(Document document, IEnumerable? spans, OptionSet options, CancellationToken cancellationToken) - => Formatter.FormatAsync(document, spans, options, rules: null, cancellationToken: cancellationToken); + public async Task FormatAsync(Document document, IEnumerable? spans, OptionSet options, CancellationToken cancellationToken) + { + var formattingOptions = await SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false); + return await Formatter.FormatAsync(document, spans, formattingOptions, rules: null, cancellationToken).ConfigureAwait(false); + } } } diff --git a/src/Workspaces/Core/Portable/Formatting/Formatter.cs b/src/Workspaces/Core/Portable/Formatting/Formatter.cs index 365543f69a410..95327a7f3fbe5 100644 --- a/src/Workspaces/Core/Portable/Formatting/Formatter.cs +++ b/src/Workspaces/Core/Portable/Formatting/Formatter.cs @@ -49,22 +49,6 @@ internal static IEnumerable GetDefaultFormattingRules(Do } } - /// - /// Gets the formatting rules that would be applied if left unspecified. - /// - internal static IEnumerable GetDefaultFormattingRules(HostWorkspaceServices services, string language) - { - var service = services.GetLanguageServices(language).GetService(); - if (service != null) - { - return service.GetDefaultFormattingRules(); - } - else - { - return SpecializedCollections.EmptyEnumerable(); - } - } - /// /// Formats the whitespace in a document. /// @@ -106,16 +90,11 @@ public static async Task FormatAsync(Document document, IEnumerable FormatAsync(Document document, IEnumerable? spans, OptionSet? options, IEnumerable? rules, CancellationToken cancellationToken) + internal static async Task FormatAsync(Document document, IEnumerable? spans, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken) { - if (document == null) - { - throw new ArgumentNullException(nameof(document)); - } - var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var documentOptions = options ?? await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); - return document.WithSyntaxRoot(Format(root, spans, document.Project.Solution.Workspace, documentOptions, rules, cancellationToken)); + var services = document.Project.Solution.Workspace.Services; + return document.WithSyntaxRoot(Format(root, spans, services, options, rules, cancellationToken)); } /// @@ -143,7 +122,10 @@ internal static async Task FormatAsync(Document document, SyntaxAnnota var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var documentOptions = options ?? await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); - return document.WithSyntaxRoot(Format(root, annotation, document.Project.Solution.Workspace, documentOptions, rules, cancellationToken)); + var services = document.Project.Solution.Workspace.Services; + var formattingOptions = SyntaxFormattingOptions.Create(documentOptions, services, root.Language); + + return document.WithSyntaxRoot(Format(root, annotation, services, formattingOptions, rules, cancellationToken)); } /// @@ -155,12 +137,13 @@ internal static async Task FormatAsync(Document document, SyntaxAnnota /// An optional set of formatting options. If these options are not supplied the current set of options from the workspace will be used. /// An optional cancellation token. /// The formatted tree's root node. - [Obsolete] public static SyntaxNode Format(SyntaxNode node, SyntaxAnnotation annotation, Workspace workspace, OptionSet? options = null, CancellationToken cancellationToken = default) - => Format(node, annotation, workspace, options, rules: null, cancellationToken: cancellationToken); + => Format(node, annotation, workspace, options, rules: null, cancellationToken); + + internal static SyntaxNode Format(SyntaxNode node, SyntaxAnnotation annotation, HostWorkspaceServices services, SyntaxFormattingOptions options, CancellationToken cancellationToken) + => Format(node, annotation, services, options, rules: null, cancellationToken); - [Obsolete] - internal static SyntaxNode Format(SyntaxNode node, SyntaxAnnotation annotation, Workspace workspace, OptionSet? options, IEnumerable? rules, CancellationToken cancellationToken) + private static SyntaxNode Format(SyntaxNode node, SyntaxAnnotation annotation, Workspace workspace, OptionSet? options, IEnumerable? rules, CancellationToken cancellationToken) { if (workspace == null) { @@ -177,13 +160,12 @@ internal static SyntaxNode Format(SyntaxNode node, SyntaxAnnotation annotation, throw new ArgumentNullException(nameof(annotation)); } - var spans = (annotation == SyntaxAnnotation.ElasticAnnotation) - ? GetElasticSpans(node) - : GetAnnotatedSpans(node, annotation); - - return Format(node, spans, workspace, options, rules, cancellationToken); + return Format(node, GetAnnotatedSpans(node, annotation), workspace, options, rules, cancellationToken); } + internal static SyntaxNode Format(SyntaxNode node, SyntaxAnnotation annotation, HostWorkspaceServices services, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken) + => Format(node, GetAnnotatedSpans(node, annotation), services, options, rules, cancellationToken); + /// /// Formats the whitespace of a syntax tree. /// @@ -192,9 +174,11 @@ internal static SyntaxNode Format(SyntaxNode node, SyntaxAnnotation annotation, /// An optional set of formatting options. If these options are not supplied the current set of options from the workspace will be used. /// An optional cancellation token. /// The formatted tree's root node. - [Obsolete] public static SyntaxNode Format(SyntaxNode node, Workspace workspace, OptionSet? options = null, CancellationToken cancellationToken = default) - => Format(node, SpecializedCollections.SingletonEnumerable(node.FullSpan), workspace, options, rules: null, cancellationToken: cancellationToken); + => Format(node, SpecializedCollections.SingletonEnumerable(node.FullSpan), workspace, options, rules: null, cancellationToken); + + internal static SyntaxNode Format(SyntaxNode node, HostWorkspaceServices services, SyntaxFormattingOptions options, CancellationToken cancellationToken) + => Format(node, SpecializedCollections.SingletonEnumerable(node.FullSpan), services, options, rules: null, cancellationToken); /// /// Formats the whitespace in areas of a syntax tree identified by a span. @@ -205,10 +189,12 @@ public static SyntaxNode Format(SyntaxNode node, Workspace workspace, OptionSet? /// An optional set of formatting options. If these options are not supplied the current set of options from the workspace will be used. /// An optional cancellation token. /// The formatted tree's root node. - [Obsolete] public static SyntaxNode Format(SyntaxNode node, TextSpan span, Workspace workspace, OptionSet? options = null, CancellationToken cancellationToken = default) => Format(node, SpecializedCollections.SingletonEnumerable(span), workspace, options, rules: null, cancellationToken: cancellationToken); + internal static SyntaxNode Format(SyntaxNode node, TextSpan span, HostWorkspaceServices services, SyntaxFormattingOptions options, CancellationToken cancellationToken) + => Format(node, SpecializedCollections.SingletonEnumerable(span), services, options, rules: null, cancellationToken: cancellationToken); + /// /// Formats the whitespace in areas of a syntax tree identified by multiple non-overlapping spans. /// @@ -218,19 +204,19 @@ public static SyntaxNode Format(SyntaxNode node, TextSpan span, Workspace worksp /// An optional set of formatting options. If these options are not supplied the current set of options from the workspace will be used. /// An optional cancellation token. /// The formatted tree's root node. - [Obsolete] public static SyntaxNode Format(SyntaxNode node, IEnumerable? spans, Workspace workspace, OptionSet? options = null, CancellationToken cancellationToken = default) => Format(node, spans, workspace, options, rules: null, cancellationToken: cancellationToken); - [Obsolete] - internal static SyntaxNode Format(SyntaxNode node, IEnumerable? spans, Workspace workspace, OptionSet? options, IEnumerable? rules, CancellationToken cancellationToken) + private static SyntaxNode Format(SyntaxNode node, IEnumerable? spans, Workspace workspace, OptionSet? options, IEnumerable? rules, CancellationToken cancellationToken) { var formattingResult = GetFormattingResult(node, spans, workspace, options, rules, cancellationToken); return formattingResult == null ? node : formattingResult.GetFormattedRoot(cancellationToken); } - [Obsolete] - internal static IFormattingResult? GetFormattingResult(SyntaxNode node, IEnumerable? spans, Workspace workspace, OptionSet? options, IEnumerable? rules, CancellationToken cancellationToken) + internal static SyntaxNode Format(SyntaxNode node, IEnumerable? spans, HostWorkspaceServices services, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken) + => GetFormattingResult(node, spans, services, options, rules, cancellationToken).GetFormattedRoot(cancellationToken); + + private static IFormattingResult? GetFormattingResult(SyntaxNode node, IEnumerable? spans, Workspace workspace, OptionSet? options, IEnumerable? rules, CancellationToken cancellationToken) { if (workspace == null) { @@ -248,12 +234,16 @@ internal static SyntaxNode Format(SyntaxNode node, IEnumerable? spans, return null; } - var optionService = workspace.Services.GetRequiredService(); - options ??= workspace.Options; spans ??= SpecializedCollections.SingletonEnumerable(node.FullSpan); - var shouldUseFormattingSpanCollapse = options.GetOption(FormattingBehaviorOptions.AllowDisjointSpanMerging); - return languageFormatter.Format(node, spans, shouldUseFormattingSpanCollapse, options.AsAnalyzerConfigOptions(optionService, node.Language), rules, cancellationToken); + var formattingOptions = SyntaxFormattingOptions.Create(options, workspace.Services, node.Language); + return languageFormatter.GetFormattingResult(node, spans, formattingOptions, rules, cancellationToken); + } + + internal static IFormattingResult GetFormattingResult(SyntaxNode node, IEnumerable? spans, HostWorkspaceServices services, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken) + { + var formatter = services.GetRequiredLanguageService(node.Language); + return formatter.GetFormattingResult(node, spans, options, rules, cancellationToken); } /// @@ -264,10 +254,12 @@ internal static SyntaxNode Format(SyntaxNode node, IEnumerable? spans, /// An optional set of formatting options. If these options are not supplied the current set of options from the workspace will be used. /// An optional cancellation token. /// The changes necessary to format the tree. - [Obsolete] public static IList GetFormattedTextChanges(SyntaxNode node, Workspace workspace, OptionSet? options = null, CancellationToken cancellationToken = default) => GetFormattedTextChanges(node, SpecializedCollections.SingletonEnumerable(node.FullSpan), workspace, options, rules: null, cancellationToken: cancellationToken); + internal static IList GetFormattedTextChanges(SyntaxNode node, HostWorkspaceServices services, SyntaxFormattingOptions options, CancellationToken cancellationToken) + => GetFormattedTextChanges(node, SpecializedCollections.SingletonEnumerable(node.FullSpan), services, options, rules: null, cancellationToken: cancellationToken); + /// /// Determines the changes necessary to format the whitespace of a syntax tree. /// @@ -277,9 +269,11 @@ public static IList GetFormattedTextChanges(SyntaxNode node, Workspa /// An optional set of formatting options. If these options are not supplied the current set of options from the workspace will be used. /// An optional cancellation token. /// The changes necessary to format the tree. - [Obsolete] public static IList GetFormattedTextChanges(SyntaxNode node, TextSpan span, Workspace workspace, OptionSet? options = null, CancellationToken cancellationToken = default) - => GetFormattedTextChanges(node, SpecializedCollections.SingletonEnumerable(span), workspace, options, rules: null, cancellationToken: cancellationToken); + => GetFormattedTextChanges(node, SpecializedCollections.SingletonEnumerable(span), workspace, options, rules: null, cancellationToken); + + internal static IList GetFormattedTextChanges(SyntaxNode node, TextSpan span, HostWorkspaceServices services, SyntaxFormattingOptions options, CancellationToken cancellationToken = default) + => GetFormattedTextChanges(node, SpecializedCollections.SingletonEnumerable(span), services, options, rules: null, cancellationToken); /// /// Determines the changes necessary to format the whitespace of a syntax tree. @@ -290,12 +284,13 @@ public static IList GetFormattedTextChanges(SyntaxNode node, TextSpa /// An optional set of formatting options. If these options are not supplied the current set of options from the workspace will be used. /// An optional cancellation token. /// The changes necessary to format the tree. - [Obsolete] public static IList GetFormattedTextChanges(SyntaxNode node, IEnumerable? spans, Workspace workspace, OptionSet? options = null, CancellationToken cancellationToken = default) - => GetFormattedTextChanges(node, spans, workspace, options, rules: null, cancellationToken: cancellationToken); + => GetFormattedTextChanges(node, spans, workspace, options, rules: null, cancellationToken); - [Obsolete] - internal static IList GetFormattedTextChanges(SyntaxNode node, IEnumerable? spans, Workspace workspace, OptionSet? options, IEnumerable? rules, CancellationToken cancellationToken) + internal static IList GetFormattedTextChanges(SyntaxNode node, IEnumerable? spans, HostWorkspaceServices services, SyntaxFormattingOptions options, CancellationToken cancellationToken = default) + => GetFormattedTextChanges(node, spans, services, options, rules: null, cancellationToken); + + private static IList GetFormattedTextChanges(SyntaxNode node, IEnumerable? spans, Workspace workspace, OptionSet? options, IEnumerable? rules, CancellationToken cancellationToken) { var formattingResult = GetFormattingResult(node, spans, workspace, options, rules, cancellationToken); return formattingResult == null @@ -303,6 +298,12 @@ internal static IList GetFormattedTextChanges(SyntaxNode node, IEnum : formattingResult.GetTextChanges(cancellationToken); } + internal static IList GetFormattedTextChanges(SyntaxNode node, IEnumerable? spans, HostWorkspaceServices services, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken = default) + { + var formatter = services.GetRequiredLanguageService(node.Language); + return formatter.GetFormattingResult(node, spans, options, rules, cancellationToken).GetTextChanges(cancellationToken); + } + /// /// Organizes the imports in the document. /// diff --git a/src/Workspaces/CoreTest/CodeCleanup/MockCodeCleanupProvider.cs b/src/Workspaces/CoreTest/CodeCleanup/MockCodeCleanupProvider.cs index 575099f00be29..15195e451f58d 100644 --- a/src/Workspaces/CoreTest/CodeCleanup/MockCodeCleanupProvider.cs +++ b/src/Workspaces/CoreTest/CodeCleanup/MockCodeCleanupProvider.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeCleanup.Providers; +using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; @@ -15,8 +16,8 @@ namespace Microsoft.CodeAnalysis.UnitTests.CodeCleanup { internal sealed class MockCodeCleanupProvider : ICodeCleanupProvider { - public Func, OptionSet, CancellationToken, Task>? CleanupDocumentAsyncImpl { get; set; } - public Func, OptionSet, HostWorkspaceServices, SyntaxNode>? CleanupNodeImpl { get; set; } + public Func, SyntaxFormattingOptions, CancellationToken, Task>? CleanupDocumentAsyncImpl { get; set; } + public Func, SyntaxFormattingOptions, HostWorkspaceServices, SyntaxNode>? CleanupNodeImpl { get; set; } public MockCodeCleanupProvider() { @@ -24,11 +25,10 @@ public MockCodeCleanupProvider() public string Name => nameof(MockCodeCleanupProvider); - public Task CleanupAsync(Document document, ImmutableArray spans, OptionSet options, CancellationToken cancellationToken) + public Task CleanupAsync(Document document, ImmutableArray spans, SyntaxFormattingOptions options, CancellationToken cancellationToken) => (CleanupDocumentAsyncImpl ?? throw new NotImplementedException()).Invoke(document, spans, options, cancellationToken); - public Task CleanupAsync(SyntaxNode root, ImmutableArray spans, OptionSet options, HostWorkspaceServices services, CancellationToken cancellationToken) + public Task CleanupAsync(SyntaxNode root, ImmutableArray spans, SyntaxFormattingOptions options, HostWorkspaceServices services, CancellationToken cancellationToken) => Task.FromResult((CleanupNodeImpl ?? throw new NotImplementedException()).Invoke(root, spans, options, services)); - } } diff --git a/src/Workspaces/CoreTest/Editing/SyntaxEditorTests.cs b/src/Workspaces/CoreTest/Editing/SyntaxEditorTests.cs index 0f5ff6ab4be6c..35e0620b0c951 100644 --- a/src/Workspaces/CoreTest/Editing/SyntaxEditorTests.cs +++ b/src/Workspaces/CoreTest/Editing/SyntaxEditorTests.cs @@ -6,7 +6,9 @@ using System; using System.Linq; +using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Test.Utilities; @@ -25,7 +27,9 @@ private Workspace EmptyWorkspace private void VerifySyntax(SyntaxNode node, string expectedText) where TSyntax : SyntaxNode { Assert.IsAssignableFrom(node); - var formatted = Formatter.Format(node, EmptyWorkspace); + + var options = SyntaxFormattingOptions.Create(DictionaryAnalyzerConfigOptions.Empty); + var formatted = Formatter.Format(node, EmptyWorkspace.Services, options, CancellationToken.None); var actualText = formatted.ToFullString(); Assert.Equal(expectedText, actualText); } diff --git a/src/Workspaces/CoreTest/FormattingTests.cs b/src/Workspaces/CoreTest/FormattingTests.cs index 27dc2270bb6f6..ad909ea3af59d 100644 --- a/src/Workspaces/CoreTest/FormattingTests.cs +++ b/src/Workspaces/CoreTest/FormattingTests.cs @@ -4,6 +4,8 @@ #nullable disable +using System.Threading; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; @@ -28,7 +30,10 @@ public void TestCSharpFormatting() [Fact, Trait(Traits.Feature, Traits.Features.Formatting)] public void TestCSharpDefaultRules() { - var rules = Formatter.GetDefaultFormattingRules(new AdhocWorkspace().Services, LanguageNames.CSharp); + using var workspace = new AdhocWorkspace(); + + var service = workspace.Services.GetLanguageServices(LanguageNames.CSharp).GetService(); + var rules = service.GetDefaultFormattingRules(); Assert.NotNull(rules); Assert.NotEmpty(rules); @@ -54,7 +59,9 @@ End Class [Fact, Trait(Traits.Feature, Traits.Features.Formatting)] public void TestVisualBasicDefaultFormattingRules() { - var rules = Formatter.GetDefaultFormattingRules(new AdhocWorkspace().Services, LanguageNames.VisualBasic); + using var workspace = new AdhocWorkspace(); + var service = workspace.Services.GetLanguageServices(LanguageNames.VisualBasic).GetService(); + var rules = service.GetDefaultFormattingRules(); Assert.NotNull(rules); Assert.NotEmpty(rules); @@ -75,7 +82,9 @@ private static void AssertFormatVB(string expected, string input) private static void AssertFormat(string expected, SyntaxTree tree) { using var workspace = new AdhocWorkspace(); - var formattedRoot = Formatter.Format(tree.GetRoot(), workspace); + + var options = SyntaxFormattingOptions.Create(DictionaryAnalyzerConfigOptions.Empty); + var formattedRoot = Formatter.Format(tree.GetRoot(), workspace.Services, options, CancellationToken.None); var actualFormattedText = formattedRoot.ToFullString(); Assert.Equal(expected, actualFormattedText); diff --git a/src/Workspaces/CoreTestUtilities/Formatting/FormattingTestBase.cs b/src/Workspaces/CoreTestUtilities/Formatting/FormattingTestBase.cs index adf07b02d6f06..8d7d718b351a7 100644 --- a/src/Workspaces/CoreTestUtilities/Formatting/FormattingTestBase.cs +++ b/src/Workspaces/CoreTestUtilities/Formatting/FormattingTestBase.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; @@ -54,29 +55,31 @@ private protected async Task AssertFormatAsync( var syntaxTree = await document.GetRequiredSyntaxTreeAsync(CancellationToken.None); - var options = workspace.Options; + var optionSet = workspace.Options; if (changedOptionSet != null) { foreach (var entry in changedOptionSet) { - options = options.WithChangedOption(entry.Key, entry.Value); + optionSet = optionSet.WithChangedOption(entry.Key, entry.Value); } } var root = await syntaxTree.GetRootAsync(); - AssertFormat(workspace, expected, root, spans, options, await document.GetTextAsync()); + var options = SyntaxFormattingOptions.Create(optionSet, workspace.Services, root.Language); + + AssertFormat(workspace.Services, expected, root, spans, options, await document.GetTextAsync()); // format with node and transform - AssertFormatWithTransformation(workspace, expected, root, spans, options, treeCompare, parseOptions); + AssertFormatWithTransformation(workspace.Services, expected, root, spans, options, treeCompare, parseOptions); } } protected abstract SyntaxNode ParseCompilation(string text, ParseOptions? parseOptions); - protected void AssertFormatWithTransformation( - Workspace workspace, string expected, SyntaxNode root, IEnumerable spans, OptionSet optionSet, bool treeCompare = true, ParseOptions? parseOptions = null) + internal void AssertFormatWithTransformation( + HostWorkspaceServices services, string expected, SyntaxNode root, IEnumerable spans, SyntaxFormattingOptions options, bool treeCompare = true, ParseOptions? parseOptions = null) { - var newRootNode = Formatter.Format(root, spans, workspace, optionSet, CancellationToken.None); + var newRootNode = Formatter.Format(root, spans, services, options, rules: null, CancellationToken.None); Assert.Equal(expected, newRootNode.ToFullString()); @@ -90,9 +93,9 @@ protected void AssertFormatWithTransformation( } } - protected static void AssertFormat(Workspace workspace, string expected, SyntaxNode root, IEnumerable spans, OptionSet optionSet, SourceText sourceText) + internal static void AssertFormat(HostWorkspaceServices services, string expected, SyntaxNode root, IEnumerable spans, SyntaxFormattingOptions options, SourceText sourceText) { - var result = Formatter.GetFormattedTextChanges(root, spans, workspace, optionSet); + var result = Formatter.GetFormattedTextChanges(root, spans, services, options); AssertResult(expected, sourceText, result); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/AbstractSyntaxFormattingService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/AbstractSyntaxFormattingService.cs index 514d1a3cb98de..9f0e166bfd5b6 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/AbstractSyntaxFormattingService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/AbstractSyntaxFormattingService.cs @@ -31,10 +31,21 @@ protected AbstractSyntaxFormattingService() protected abstract AbstractFormattingResult Format(SyntaxNode node, AnalyzerConfigOptions options, IEnumerable rules, SyntaxToken token1, SyntaxToken token2, CancellationToken cancellationToken); - public IFormattingResult Format(SyntaxNode node, IEnumerable spans, bool shouldUseFormattingSpanCollapse, AnalyzerConfigOptions options, IEnumerable? rules, CancellationToken cancellationToken) + public IFormattingResult GetFormattingResult(SyntaxNode node, IEnumerable? spans, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken) { - // quick exit check - var spansToFormat = new NormalizedTextSpanCollection(spans.Where(s_notEmpty)); + IReadOnlyList spansToFormat; + + if (spans == null) + { + spansToFormat = node.FullSpan.IsEmpty ? + SpecializedCollections.EmptyReadOnlyList() : + SpecializedCollections.SingletonReadOnlyList(node.FullSpan); + } + else + { + spansToFormat = new NormalizedTextSpanCollection(spans.Where(s_notEmpty)); + } + if (spansToFormat.Count == 0) { return CreateAggregatedFormattingResult(node, SpecializedCollections.EmptyList()); @@ -43,16 +54,15 @@ public IFormattingResult Format(SyntaxNode node, IEnumerable spans, bo rules ??= GetDefaultFormattingRules(); // check what kind of formatting strategy to use - if (AllowDisjointSpanMerging(spansToFormat, shouldUseFormattingSpanCollapse)) - { - return FormatMergedSpan(node, options, rules, spansToFormat, cancellationToken); - } + var result = AllowDisjointSpanMerging(spansToFormat, options.ShouldUseFormattingSpanCollapse) ? + FormatMergedSpan(node, options.Options, rules, spansToFormat, cancellationToken) : + FormatIndividually(node, options.Options, rules, spansToFormat, cancellationToken); - return FormatIndividually(node, options, rules, spansToFormat, cancellationToken); + return result; } private IFormattingResult FormatMergedSpan( - SyntaxNode node, AnalyzerConfigOptions options, IEnumerable rules, IList spansToFormat, CancellationToken cancellationToken) + SyntaxNode node, AnalyzerConfigOptions options, IEnumerable rules, IReadOnlyList spansToFormat, CancellationToken cancellationToken) { var spanToFormat = TextSpan.FromBounds(spansToFormat[0].Start, spansToFormat[spansToFormat.Count - 1].End); var pair = node.ConvertToTokenPair(spanToFormat); @@ -68,7 +78,7 @@ private IFormattingResult FormatMergedSpan( } private IFormattingResult FormatIndividually( - SyntaxNode node, AnalyzerConfigOptions options, IEnumerable rules, IList spansToFormat, CancellationToken cancellationToken) + SyntaxNode node, AnalyzerConfigOptions options, IEnumerable rules, IReadOnlyList spansToFormat, CancellationToken cancellationToken) { List? results = null; foreach (var pair in node.ConvertToTokenPairs(spansToFormat)) @@ -97,7 +107,7 @@ private IFormattingResult FormatIndividually( return CreateAggregatedFormattingResult(node, results); } - private static bool AllowDisjointSpanMerging(IList list, bool shouldUseFormattingSpanCollapse) + private static bool AllowDisjointSpanMerging(IReadOnlyList list, bool shouldUseFormattingSpanCollapse) { // If the user is specific about the formatting specific spans then honor users settings if (!shouldUseFormattingSpanCollapse) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/FormattingExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/FormattingExtensions.cs index e7c049d8defec..3308e64ece2da 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/FormattingExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/FormattingExtensions.cs @@ -284,11 +284,22 @@ public static TextChange SimpleDiff(this TextChange textChange, string text) internal static IEnumerable GetAnnotatedSpans(SyntaxNode node, SyntaxAnnotation annotation) { - foreach (var nodeOrToken in node.GetAnnotatedNodesAndTokens(annotation)) + if (annotation == SyntaxAnnotation.ElasticAnnotation) { - var firstToken = nodeOrToken.IsNode ? nodeOrToken.AsNode()!.GetFirstToken(includeZeroWidth: true) : nodeOrToken.AsToken(); - var lastToken = nodeOrToken.IsNode ? nodeOrToken.AsNode()!.GetLastToken(includeZeroWidth: true) : nodeOrToken.AsToken(); - yield return GetSpan(firstToken, lastToken); + var tokens = node.GetAnnotatedTrivia(SyntaxAnnotation.ElasticAnnotation).Select(tr => tr.Token).Distinct(); + return AggregateSpans(tokens.Select(t => GetElasticSpan(t))); + } + + return EnumerateAnnotatedSpans(node, annotation); + + static IEnumerable EnumerateAnnotatedSpans(SyntaxNode node, SyntaxAnnotation annotation) + { + foreach (var nodeOrToken in node.GetAnnotatedNodesAndTokens(annotation)) + { + var firstToken = nodeOrToken.IsNode ? nodeOrToken.AsNode()!.GetFirstToken(includeZeroWidth: true) : nodeOrToken.AsToken(); + var lastToken = nodeOrToken.IsNode ? nodeOrToken.AsNode()!.GetLastToken(includeZeroWidth: true) : nodeOrToken.AsToken(); + yield return GetSpan(firstToken, lastToken); + } } } @@ -310,12 +321,6 @@ internal static TextSpan GetSpan(SyntaxToken firstToken, SyntaxToken lastToken) return TextSpan.FromBounds(firstToken.SpanStart, lastToken.Span.End); } - internal static IEnumerable GetElasticSpans(SyntaxNode root) - { - var tokens = root.GetAnnotatedTrivia(SyntaxAnnotation.ElasticAnnotation).Select(tr => tr.Token).Distinct(); - return AggregateSpans(tokens.Select(t => GetElasticSpan(t))); - } - internal static TextSpan GetElasticSpan(SyntaxToken token) => GetSpan(token, token); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/ISyntaxFormattingService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/ISyntaxFormattingService.cs index 6b6831f22236b..a2801f5caed39 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/ISyntaxFormattingService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/ISyntaxFormattingService.cs @@ -23,20 +23,29 @@ internal interface ISyntaxFormattingService #endif { IEnumerable GetDefaultFormattingRules(); - IFormattingResult Format(SyntaxNode node, IEnumerable spans, bool shouldUseFormattingSpanCollapse, AnalyzerConfigOptions options, IEnumerable? rules, CancellationToken cancellationToken); + IFormattingResult GetFormattingResult(SyntaxNode node, IEnumerable? spans, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken); } -#if !CODE_STYLE - internal static class ISyntaxFormattingServiceExtensions + internal readonly record struct SyntaxFormattingOptions( + AnalyzerConfigOptions Options, + bool ShouldUseFormattingSpanCollapse) { - internal static IFormattingResult GetFormattingResult(this ISyntaxFormattingService service, SyntaxNode node, IEnumerable spans, OptionSet options, HostWorkspaceServices services, IEnumerable? rules, CancellationToken cancellationToken) - { - var optionService = services.GetRequiredService(); - var shouldUseFormattingSpanCollapse = options.GetOption(FormattingBehaviorOptions.AllowDisjointSpanMerging); - var configOptions = options.AsAnalyzerConfigOptions(optionService, node.Language); + public static readonly SyntaxFormattingOptions Default = Create(DictionaryAnalyzerConfigOptions.Empty); + + public static SyntaxFormattingOptions Create(AnalyzerConfigOptions options) + => new(options, ShouldUseFormattingSpanCollapse: false); + +#if !CODE_STYLE + public static SyntaxFormattingOptions Create(OptionSet options, HostWorkspaceServices services, string language, bool? shouldUseFormattingSpanCollapse = null) + => new( + options.AsAnalyzerConfigOptions(services.GetRequiredService(), language), + shouldUseFormattingSpanCollapse ?? options.GetOption(FormattingBehaviorOptions.AllowDisjointSpanMerging)); - return service.Format(node, spans, shouldUseFormattingSpanCollapse, configOptions, rules, cancellationToken); + public static async Task FromDocumentAsync(Document document, CancellationToken cancellationToken) + { + var documentOptions = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); + return Create(documentOptions, document.Project.Solution.Workspace.Services, document.Project.Language); } - } #endif + } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/CommonFormattingHelpers.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/CommonFormattingHelpers.cs index afe5e11504038..f46795c20ac61 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/CommonFormattingHelpers.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/CommonFormattingHelpers.cs @@ -39,7 +39,7 @@ internal static class CommonFormattingHelpers return 0; }; - public static IEnumerable> ConvertToTokenPairs(this SyntaxNode root, IList spans) + public static IEnumerable> ConvertToTokenPairs(this SyntaxNode root, IReadOnlyList spans) { Contract.ThrowIfNull(root); Contract.ThrowIfFalse(spans.Count > 0); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.cs index 3a9ac27d3fa8e..eba472f45ddea 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.cs @@ -9,7 +9,9 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.Formatting; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; @@ -18,6 +20,12 @@ using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; +#if CODE_STYLE +using Formatter = Microsoft.CodeAnalysis.Formatting.FormatterHelper; +#else +using Formatter = Microsoft.CodeAnalysis.Formatting.Formatter; +#endif + namespace Microsoft.CodeAnalysis.CSharp.RemoveUnnecessaryImports { [ExportLanguageService(typeof(IRemoveUnnecessaryImportsService), LanguageNames.CSharp), Shared] @@ -30,6 +38,11 @@ public CSharpRemoveUnnecessaryImportsService() { } +#if CODE_STYLE + private static ISyntaxFormattingService GetSyntaxFormattingService() + => CSharpSyntaxFormattingService.Instance; +#endif + protected override IUnnecessaryImportsProvider UnnecessaryImportsProvider => CSharpUnnecessaryImportsProvider.Instance; @@ -54,16 +67,20 @@ public override async Task RemoveUnnecessaryImportsAsync( var newRoot = (CompilationUnitSyntax)new Rewriter(document, unnecessaryImports, cancellationToken).Visit(oldRoot); cancellationToken.ThrowIfCancellationRequested(); - return document.WithSyntaxRoot(await FormatResultAsync(document, newRoot, cancellationToken).ConfigureAwait(false)); - } - } +#if CODE_STYLE + var provider = GetSyntaxFormattingService(); + var optionSet = document.Project.AnalyzerOptions.AnalyzerConfigOptionsProvider.GetOptions(oldRoot.SyntaxTree); + var options = SyntaxFormattingOptions.Create(optionSet); +#else + var provider = document.Project.Solution.Workspace.Services; + var options = await SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false); +#endif + var spans = new List(); + AddFormattingSpans(newRoot, spans, cancellationToken); + var formattedRoot = Formatter.Format(newRoot, spans, provider, options, rules: null, cancellationToken); - private async Task FormatResultAsync(Document document, CompilationUnitSyntax newRoot, CancellationToken cancellationToken) - { - var spans = new List(); - AddFormattingSpans(newRoot, spans, cancellationToken); - var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); - return Formatter.Format(newRoot, spans, document.Project.Solution.Workspace, options, cancellationToken: cancellationToken); + return document.WithSyntaxRoot(formattedRoot); + } } private void AddFormattingSpans( diff --git a/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/AbstractTokensCodeCleanupProvider.vb b/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/AbstractTokensCodeCleanupProvider.vb index 1420b76084647..9eef93cdd24c1 100644 --- a/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/AbstractTokensCodeCleanupProvider.vb +++ b/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/AbstractTokensCodeCleanupProvider.vb @@ -5,8 +5,8 @@ Imports System.Collections.Immutable Imports System.Threading Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Formatting Imports Microsoft.CodeAnalysis.Host -Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.Shared.Collections Imports Microsoft.CodeAnalysis.Text @@ -19,7 +19,7 @@ Namespace Microsoft.CodeAnalysis.CodeCleanup.Providers Protected MustOverride Function GetRewriterAsync( document As Document, root As SyntaxNode, spans As ImmutableArray(Of TextSpan), cancellationToken As CancellationToken) As Task(Of Rewriter) - Public Async Function CleanupAsync(document As Document, spans As ImmutableArray(Of TextSpan), options As OptionSet, cancellationToken As CancellationToken) As Task(Of Document) Implements ICodeCleanupProvider.CleanupAsync + Public Async Function CleanupAsync(document As Document, spans As ImmutableArray(Of TextSpan), options As SyntaxFormattingOptions, cancellationToken As CancellationToken) As Task(Of Document) Implements ICodeCleanupProvider.CleanupAsync Dim root = Await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(False) Dim rewriter As Rewriter = Await GetRewriterAsync(document, root, spans, cancellationToken).ConfigureAwait(False) Dim newRoot = rewriter.Visit(root) @@ -27,7 +27,7 @@ Namespace Microsoft.CodeAnalysis.CodeCleanup.Providers Return If(root Is newRoot, document, document.WithSyntaxRoot(newRoot)) End Function - Public Async Function CleanupAsync(root As SyntaxNode, spans As ImmutableArray(Of TextSpan), options As OptionSet, services As HostWorkspaceServices, cancellationToken As CancellationToken) As Task(Of SyntaxNode) Implements ICodeCleanupProvider.CleanupAsync + Public Async Function CleanupAsync(root As SyntaxNode, spans As ImmutableArray(Of TextSpan), options As SyntaxFormattingOptions, services As HostWorkspaceServices, cancellationToken As CancellationToken) As Task(Of SyntaxNode) Implements ICodeCleanupProvider.CleanupAsync Dim rewriter As Rewriter = Await GetRewriterAsync(Nothing, root, spans, cancellationToken).ConfigureAwait(False) Return rewriter.Visit(root) End Function diff --git a/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/CaseCorrectionCodeCleanupProvider.vb b/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/CaseCorrectionCodeCleanupProvider.vb index ce096181027b6..e4cd067bbaf8f 100644 --- a/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/CaseCorrectionCodeCleanupProvider.vb +++ b/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/CaseCorrectionCodeCleanupProvider.vb @@ -7,8 +7,8 @@ Imports System.Composition Imports System.Diagnostics.CodeAnalysis Imports System.Threading Imports Microsoft.CodeAnalysis.CaseCorrection +Imports Microsoft.CodeAnalysis.Formatting Imports Microsoft.CodeAnalysis.Host -Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.Text Namespace Microsoft.CodeAnalysis.CodeCleanup.Providers @@ -28,11 +28,11 @@ Namespace Microsoft.CodeAnalysis.CodeCleanup.Providers End Get End Property - Public Function CleanupAsync(document As Document, spans As ImmutableArray(Of TextSpan), options As OptionSet, cancellationToken As CancellationToken) As Task(Of Document) Implements ICodeCleanupProvider.CleanupAsync + Public Function CleanupAsync(document As Document, spans As ImmutableArray(Of TextSpan), options As SyntaxFormattingOptions, cancellationToken As CancellationToken) As Task(Of Document) Implements ICodeCleanupProvider.CleanupAsync Return CaseCorrector.CaseCorrectAsync(document, spans, cancellationToken) End Function - Public Function CleanupAsync(root As SyntaxNode, spans As ImmutableArray(Of TextSpan), options As OptionSet, services As HostWorkspaceServices, cancellationToken As CancellationToken) As Task(Of SyntaxNode) Implements ICodeCleanupProvider.CleanupAsync + Public Function CleanupAsync(root As SyntaxNode, spans As ImmutableArray(Of TextSpan), options As SyntaxFormattingOptions, services As HostWorkspaceServices, cancellationToken As CancellationToken) As Task(Of SyntaxNode) Implements ICodeCleanupProvider.CleanupAsync Return Task.FromResult(CaseCorrector.CaseCorrect(root, spans, services, cancellationToken)) End Function End Class diff --git a/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/NormalizeModifiersOrOperatorsCodeCleanupProvider.vb b/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/NormalizeModifiersOrOperatorsCodeCleanupProvider.vb index 997322098aeda..7ad4b805deded 100644 --- a/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/NormalizeModifiersOrOperatorsCodeCleanupProvider.vb +++ b/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/NormalizeModifiersOrOperatorsCodeCleanupProvider.vb @@ -7,8 +7,8 @@ Imports System.Composition Imports System.Diagnostics.CodeAnalysis Imports System.Threading Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Formatting Imports Microsoft.CodeAnalysis.Host -Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.Shared.Collections Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.CodeStyle @@ -31,14 +31,14 @@ Namespace Microsoft.CodeAnalysis.CodeCleanup.Providers End Get End Property - Public Async Function CleanupAsync(document As Document, spans As ImmutableArray(Of TextSpan), options As OptionSet, cancellationToken As CancellationToken) As Task(Of Document) Implements ICodeCleanupProvider.CleanupAsync + Public Async Function CleanupAsync(document As Document, spans As ImmutableArray(Of TextSpan), options As SyntaxFormattingOptions, cancellationToken As CancellationToken) As Task(Of Document) Implements ICodeCleanupProvider.CleanupAsync Dim root = Await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(False) Dim newRoot = Await CleanupAsync(root, spans, options, document.Project.Solution.Workspace.Services, cancellationToken).ConfigureAwait(False) Return If(root Is newRoot, document, document.WithSyntaxRoot(newRoot)) End Function - Public Function CleanupAsync(root As SyntaxNode, spans As ImmutableArray(Of TextSpan), options As OptionSet, services As HostWorkspaceServices, cancellationToken As CancellationToken) As Task(Of SyntaxNode) Implements ICodeCleanupProvider.CleanupAsync + Public Function CleanupAsync(root As SyntaxNode, spans As ImmutableArray(Of TextSpan), options As SyntaxFormattingOptions, services As HostWorkspaceServices, cancellationToken As CancellationToken) As Task(Of SyntaxNode) Implements ICodeCleanupProvider.CleanupAsync Dim rewriter = New Rewriter(spans, cancellationToken) Dim newRoot = rewriter.Visit(root) diff --git a/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/RemoveUnnecessaryLineContinuationCodeCleanupProvider.vb b/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/RemoveUnnecessaryLineContinuationCodeCleanupProvider.vb index 8954e34f6ee84..9bcd1c84bfdfc 100644 --- a/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/RemoveUnnecessaryLineContinuationCodeCleanupProvider.vb +++ b/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/RemoveUnnecessaryLineContinuationCodeCleanupProvider.vb @@ -7,8 +7,8 @@ Imports System.Composition Imports System.Diagnostics.CodeAnalysis Imports System.Threading Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Formatting Imports Microsoft.CodeAnalysis.Host -Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -29,7 +29,7 @@ Namespace Microsoft.CodeAnalysis.CodeCleanup.Providers End Get End Property - Public Async Function CleanupAsync(document As Document, spans As ImmutableArray(Of TextSpan), options As OptionSet, cancellationToken As CancellationToken) As Task(Of Document) Implements ICodeCleanupProvider.CleanupAsync + Public Async Function CleanupAsync(document As Document, spans As ImmutableArray(Of TextSpan), options As SyntaxFormattingOptions, cancellationToken As CancellationToken) As Task(Of Document) Implements ICodeCleanupProvider.CleanupAsync ' Is this VB 9? If so, we shouldn't remove line continuations because implicit line continuation was introduced in VB 10. Dim parseOptions = TryCast(document.Project.ParseOptions, VisualBasicParseOptions) If parseOptions?.LanguageVersion <= LanguageVersion.VisualBasic9 Then @@ -42,7 +42,7 @@ Namespace Microsoft.CodeAnalysis.CodeCleanup.Providers Return If(newRoot Is root, document, document.WithSyntaxRoot(newRoot)) End Function - Public Function CleanupAsync(root As SyntaxNode, spans As ImmutableArray(Of TextSpan), options As OptionSet, services As HostWorkspaceServices, cancellationToken As CancellationToken) As Task(Of SyntaxNode) Implements ICodeCleanupProvider.CleanupAsync + Public Function CleanupAsync(root As SyntaxNode, spans As ImmutableArray(Of TextSpan), options As SyntaxFormattingOptions, services As HostWorkspaceServices, cancellationToken As CancellationToken) As Task(Of SyntaxNode) Implements ICodeCleanupProvider.CleanupAsync Return Task.FromResult(Replacer.Process(root, spans, cancellationToken)) End Function diff --git a/src/Workspaces/VisualBasic/Portable/Indentation/VisualBasicIndentationService.Indenter.vb b/src/Workspaces/VisualBasic/Portable/Indentation/VisualBasicIndentationService.Indenter.vb index bd37ccfb9a1a2..d4006252b31a4 100644 --- a/src/Workspaces/VisualBasic/Portable/Indentation/VisualBasicIndentationService.Indenter.vb +++ b/src/Workspaces/VisualBasic/Portable/Indentation/VisualBasicIndentationService.Indenter.vb @@ -17,11 +17,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Indentation End Function Protected Overrides Function CreateSmartTokenFormatter(indenter As Indenter) As ISmartTokenFormatter - Dim workspace = indenter.Document.Project.Solution.Workspace - Dim formattingRuleFactory = workspace.Services.GetService(Of IHostDependentFormattingRuleFactoryService)() + Dim services = indenter.Document.Project.Solution.Workspace.Services + Dim formattingRuleFactory = services.GetService(Of IHostDependentFormattingRuleFactoryService)() Dim rules = {New SpecialFormattingRule(indenter.OptionSet.GetOption(FormattingOptions.SmartIndent, indenter.Document.Root.Language)), formattingRuleFactory.CreateRule(indenter.Document.Document, indenter.LineToBeIndented.Start)}.Concat(Formatter.GetDefaultFormattingRules(indenter.Document.Document)) - - Return New VisualBasicSmartTokenFormatter(indenter.OptionSet, rules, indenter.Root) + Dim options = SyntaxFormattingOptions.Create(indenter.OptionSet, services, indenter.Document.Project.Language) + Return New VisualBasicSmartTokenFormatter(options, rules, indenter.Root) End Function Protected Overrides Function GetDesiredIndentationWorker( diff --git a/src/Workspaces/VisualBasic/Portable/Indentation/VisualBasicSmartTokenFormatter.vb b/src/Workspaces/VisualBasic/Portable/Indentation/VisualBasicSmartTokenFormatter.vb index 9bccbd262c2c0..c6df0a006973d 100644 --- a/src/Workspaces/VisualBasic/Portable/Indentation/VisualBasicSmartTokenFormatter.vb +++ b/src/Workspaces/VisualBasic/Portable/Indentation/VisualBasicSmartTokenFormatter.vb @@ -15,19 +15,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Indentation Friend Class VisualBasicSmartTokenFormatter Implements ISmartTokenFormatter - Private ReadOnly _optionSet As OptionSet + Private ReadOnly _options As SyntaxFormattingOptions Private ReadOnly _formattingRules As IEnumerable(Of AbstractFormattingRule) Private ReadOnly _root As CompilationUnitSyntax - Public Sub New(optionSet As OptionSet, + Public Sub New(options As SyntaxFormattingOptions, formattingRules As IEnumerable(Of AbstractFormattingRule), root As CompilationUnitSyntax) - Contract.ThrowIfNull(optionSet) Contract.ThrowIfNull(formattingRules) Contract.ThrowIfNull(root) - Me._optionSet = optionSet + Me._options = options Me._formattingRules = formattingRules Me._root = root @@ -40,8 +39,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Indentation Dim previousToken = token.GetPreviousToken() Dim spans = SpecializedCollections.SingletonEnumerable(TextSpan.FromBounds(previousToken.SpanStart, token.Span.End)) - Dim formatter = services.GetRequiredLanguageService(Of ISyntaxFormattingService)(_root.Language) - Return Task.FromResult(formatter.GetFormattingResult(_root, spans, _optionSet, services, _formattingRules, cancellationToken).GetTextChanges(cancellationToken)) + Return Task.FromResult(Formatter.GetFormattedTextChanges(_root, spans, services, _options, _formattingRules, cancellationToken)) End Function End Class End Namespace diff --git a/src/Workspaces/VisualBasicTest/Formatting/FormattingTests.vb b/src/Workspaces/VisualBasicTest/Formatting/FormattingTests.vb index 6004b5d2fa16a..72de1f2d67263 100644 --- a/src/Workspaces/VisualBasicTest/Formatting/FormattingTests.vb +++ b/src/Workspaces/VisualBasicTest/Formatting/FormattingTests.vb @@ -2,6 +2,8 @@ ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. +Imports System.Threading +Imports Microsoft.CodeAnalysis.Diagnostics Imports Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions Imports Microsoft.CodeAnalysis.Formatting Imports Microsoft.CodeAnalysis.Test.Utilities @@ -3027,16 +3029,17 @@ End Module" Dim project = workspace.CurrentSolution.AddProject("Project", "Project.dll", LanguageNames.VisualBasic) Dim document = project.AddDocument("Document", SourceText.From(inputOutput)) Dim root = Await document.GetSyntaxRootAsync() + Dim options = SyntaxFormattingOptions.Default ' format first time - Dim result = Formatter.GetFormattedTextChanges(root, workspace) + Dim result = Formatter.GetFormattedTextChanges(root, workspace.Services, options, CancellationToken.None) AssertResult(inputOutput, Await document.GetTextAsync(), result) Dim document2 = document.WithText((Await document.GetTextAsync()).WithChanges(result)) Dim root2 = Await document2.GetSyntaxRootAsync() ' format second time - Dim result2 = Formatter.GetFormattedTextChanges(root, workspace) + Dim result2 = Formatter.GetFormattedTextChanges(root, workspace.Services, options, CancellationToken.None) AssertResult(inputOutput, Await document2.GetTextAsync(), result2) End Using End Function @@ -3854,7 +3857,7 @@ End Class.Value.Replace(vbLf, vbCrLf) root = root.ReplaceNode(method, method.NormalizeWhitespace(elasticTrivia:=True).WithAdditionalAnnotations(goo)) Using workspace = New AdhocWorkspace() - Dim result = Formatter.Format(root, goo, workspace).ToString() + Dim result = Formatter.Format(root, goo, workspace.Services, SyntaxFormattingOptions.Default, CancellationToken.None).ToString() Assert.Equal(expected, result) End Using End Sub @@ -4672,17 +4675,24 @@ End Class Public Sub NewLineOption_LineFeedOnly() - Dim tree = SyntaxFactory.ParseCompilationUnit("Class C" & vbCrLf & "End Class") + Using workspace = New AdhocWorkspace() + Dim tree = SyntaxFactory.ParseCompilationUnit("Class C" & vbCrLf & "End Class") + + ' replace all EOL trivia with elastic markers to force the formatter to add EOL back + tree = tree.ReplaceTrivia(tree.DescendantTrivia().Where(Function(tr) tr.IsKind(SyntaxKind.EndOfLineTrivia)), Function(o, r) SyntaxFactory.ElasticMarker) - ' replace all EOL trivia with elastic markers to force the formatter to add EOL back - tree = tree.ReplaceTrivia(tree.DescendantTrivia().Where(Function(tr) tr.IsKind(SyntaxKind.EndOfLineTrivia)), Function(o, r) SyntaxFactory.ElasticMarker) + Dim options = SyntaxFormattingOptions.Create( + workspace.Options.WithChangedOption(FormattingOptions.NewLine, LanguageNames.VisualBasic, vbLf), + workspace.Services, + tree.Language) - Dim formatted = Formatter.Format(tree, DefaultWorkspace, DefaultWorkspace.Options.WithChangedOption(FormattingOptions.NewLine, LanguageNames.VisualBasic, vbLf)) - Dim actual = formatted.ToFullString() + Dim formatted = Formatter.Format(tree, workspace.Services, options, CancellationToken.None) + Dim actual = formatted.ToFullString() - Dim expected = "Class C" & vbLf & "End Class" + Dim expected = "Class C" & vbLf & "End Class" - Assert.Equal(expected, actual) + Assert.Equal(expected, actual) + End Using End Sub diff --git a/src/Workspaces/VisualBasicTest/Formatting/VisualBasicFormattingTestBase.vb b/src/Workspaces/VisualBasicTest/Formatting/VisualBasicFormattingTestBase.vb index 5f8769c83e773..15dd331e1fe35 100644 --- a/src/Workspaces/VisualBasicTest/Formatting/VisualBasicFormattingTestBase.vb +++ b/src/Workspaces/VisualBasicTest/Formatting/VisualBasicFormattingTestBase.vb @@ -4,6 +4,7 @@ Imports System.Collections.Immutable Imports System.Threading +Imports Microsoft.CodeAnalysis.Diagnostics Imports Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions Imports Microsoft.CodeAnalysis.Formatting Imports Microsoft.CodeAnalysis.Text @@ -55,26 +56,27 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.Formatting Dim project = workspace.CurrentSolution.AddProject("Project", "Project.dll", LanguageNames.VisualBasic) Dim document = project.AddDocument("Document", SourceText.From(code)) Dim syntaxTree = Await document.GetSyntaxTreeAsync() + Dim options = SyntaxFormattingOptions.Create(DictionaryAnalyzerConfigOptions.Empty) ' Test various entry points into the formatter Dim spans = New List(Of TextSpan)() spans.Add(syntaxTree.GetRoot().FullSpan) - Dim changes = Formatter.GetFormattedTextChanges(Await syntaxTree.GetRootAsync(), workspace, cancellationToken:=CancellationToken.None) + Dim changes = Formatter.GetFormattedTextChanges(Await syntaxTree.GetRootAsync(), workspace.Services, options, CancellationToken.None) AssertResult(expected, Await document.GetTextAsync(), changes) - changes = Formatter.GetFormattedTextChanges(Await syntaxTree.GetRootAsync(), (Await syntaxTree.GetRootAsync()).FullSpan, workspace, cancellationToken:=CancellationToken.None) + changes = Formatter.GetFormattedTextChanges(Await syntaxTree.GetRootAsync(), (Await syntaxTree.GetRootAsync()).FullSpan, workspace.Services, options, CancellationToken.None) AssertResult(expected, Await document.GetTextAsync(), changes) spans = New List(Of TextSpan)() spans.Add(syntaxTree.GetRoot().FullSpan) - changes = Formatter.GetFormattedTextChanges(Await syntaxTree.GetRootAsync(), spans, workspace, cancellationToken:=CancellationToken.None) + changes = Formatter.GetFormattedTextChanges(Await syntaxTree.GetRootAsync(), spans, workspace.Services, options, CancellationToken.None) AssertResult(expected, Await document.GetTextAsync(), changes) ' format with node and transform - AssertFormatWithTransformation(workspace, expected, syntaxTree.GetRoot(), spans, Nothing, False) + AssertFormatWithTransformation(workspace.Services, expected, syntaxTree.GetRoot(), spans, Nothing, False) End Using End Function From cb19e81233eb876ea26e68f666b66087bde8b524 Mon Sep 17 00:00:00 2001 From: tmat Date: Thu, 13 Jan 2022 13:45:51 -0800 Subject: [PATCH 04/13] Remove AllowDisjointSpanMerging --- .../Formatting/FormattingEngineTests.cs | 100 +----------------- .../Formatting/FormattingBehaviorOptions.cs | 7 -- .../AbstractSyntaxFormattingService.cs | 66 +----------- .../Formatting/ISyntaxFormattingService.cs | 11 +- .../Core/Utilities/CommonFormattingHelpers.cs | 2 +- 5 files changed, 9 insertions(+), 177 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs b/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs index 74ddc4a1df01e..9405a023ae8af 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs @@ -430,102 +430,6 @@ public void M() Assert.Equal(expected, node.ToFullString()); } - [WorkItem(987373, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/987373")] - [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] - public async Task FormatSpansWithCollapsing() - { - var code = @"class C -{ - public void M() - { - [|if(true){}|] - [|if(true){}|] - [|if(true){}|] - [|if(true){}|] - [|if(true){}|] - [|if(true){}|] - [|if(true){}|] - [|if(true){}|] - [|if(true){}|] - [|if(true){}|] - [|if(true){}|] - [|if(true){}|] - [|if(true){}|] - [|if(true){}|] - [|if(true){}|] - [|if(true){}|] - [|if(true){}|] - [|if(true){}|] - [|if(true){}|] - [|if(true){}|] - [|if(true){}|] - [|if(true){}|] - [|if(true){}|] - [|if(true){}|] - [|if(true){}|] - [|if(true){}|] - [|if(true){}|] - [|if(true){}|] - [|if(true){}|] - [|if(true){}|] - [|if(true){}|] - while(true){} - [|if(true){}|] - } -}"; - var expected = @"class C -{ - public void M() - { - if (true) { } - if (true) { } - if (true) { } - if (true) { } - if (true) { } - if (true) { } - if (true) { } - if (true) { } - if (true) { } - if (true) { } - if (true) { } - if (true) { } - if (true) { } - if (true) { } - if (true) { } - if (true) { } - if (true) { } - if (true) { } - if (true) { } - if (true) { } - if (true) { } - if (true) { } - if (true) { } - if (true) { } - if (true) { } - if (true) { } - if (true) { } - if (true) { } - if (true) { } - if (true) { } - if (true) { } - while (true) { } - if (true) { } - } -}"; - using var workspace = TestWorkspace.CreateCSharp(code); - var subjectDocument = workspace.Documents.Single(); - var spans = subjectDocument.SelectedSpans; - workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options - .WithChangedOption(FormattingBehaviorOptions.AllowDisjointSpanMerging, true))); - - var document = workspace.CurrentSolution.Projects.Single().Documents.Single(); - var syntaxRoot = await document.GetSyntaxRootAsync(); - var options = await SyntaxFormattingOptions.FromDocumentAsync(document, CancellationToken.None); - - var node = Formatter.Format(syntaxRoot, spans, workspace.Services, options, rules: null, CancellationToken.None); - Assert.Equal(expected, node.ToFullString()); - } - [WorkItem(1044118, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1044118")] [WpfFact, Trait(Traits.Feature, Traits.Features.Formatting)] public void SemicolonInCommentOnLastLineDoesNotFormat() @@ -2289,9 +2193,7 @@ class Test using var workspace = new AdhocWorkspace(); - var options = new SyntaxFormattingOptions( - DictionaryAnalyzerConfigOptions.Empty, - ShouldUseFormattingSpanCollapse: false); + var options = SyntaxFormattingOptions.Default; var formattedRoot = Formatter.Format(root, workspace.Services, options, CancellationToken.None); var annotatedTrivia = formattedRoot.GetAnnotatedTrivia("marker"); diff --git a/src/Workspaces/Core/Portable/Formatting/FormattingBehaviorOptions.cs b/src/Workspaces/Core/Portable/Formatting/FormattingBehaviorOptions.cs index 3c9cdbd7e6f52..6a9b67da7346f 100644 --- a/src/Workspaces/Core/Portable/Formatting/FormattingBehaviorOptions.cs +++ b/src/Workspaces/Core/Portable/Formatting/FormattingBehaviorOptions.cs @@ -28,7 +28,6 @@ public Provider() public ImmutableArray Options { get; } = ImmutableArray.Create( SmartIndent, PreferredWrappingColumn, - AllowDisjointSpanMerging, AutoFormattingOnReturn, AutoFormattingOnTyping, AutoFormattingOnSemicolon, @@ -54,12 +53,6 @@ public Provider() internal static Option2 PreferredWrappingColumn { get; } = new(FeatureName, FormattingOptionGroups.NewLine, nameof(PreferredWrappingColumn), defaultValue: 120); - /// - /// TODO: Currently the option has no storage and always has its default value. - /// - internal static Option2 AllowDisjointSpanMerging { get; } = - new(FeatureName, OptionGroup.Default, nameof(AllowDisjointSpanMerging), defaultValue: false); - internal static readonly PerLanguageOption2 AutoFormattingOnReturn = new(FeatureName, OptionGroup.Default, nameof(AutoFormattingOnReturn), defaultValue: true, storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.Auto Formatting On Return")); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/AbstractSyntaxFormattingService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/AbstractSyntaxFormattingService.cs index 9f0e166bfd5b6..9de1f9bd9cd06 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/AbstractSyntaxFormattingService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/AbstractSyntaxFormattingService.cs @@ -53,43 +53,16 @@ public IFormattingResult GetFormattingResult(SyntaxNode node, IEnumerable rules, IReadOnlyList spansToFormat, CancellationToken cancellationToken) - { - var spanToFormat = TextSpan.FromBounds(spansToFormat[0].Start, spansToFormat[spansToFormat.Count - 1].End); - var pair = node.ConvertToTokenPair(spanToFormat); - - if (node.IsInvalidTokenRange(pair.Item1, pair.Item2)) - { - return CreateAggregatedFormattingResult(node, SpecializedCollections.EmptyList()); - } - - // more expensive case - var result = Format(node, options, rules, pair.Item1, pair.Item2, cancellationToken); - return CreateAggregatedFormattingResult(node, new List(1) { result }, SimpleIntervalTree.Create(new TextSpanIntervalIntrospector(), spanToFormat)); - } - - private IFormattingResult FormatIndividually( - SyntaxNode node, AnalyzerConfigOptions options, IEnumerable rules, IReadOnlyList spansToFormat, CancellationToken cancellationToken) - { List? results = null; - foreach (var pair in node.ConvertToTokenPairs(spansToFormat)) + foreach (var (startToken, endToken) in node.ConvertToTokenPairs(spansToFormat)) { - if (node.IsInvalidTokenRange(pair.Item1, pair.Item2)) + if (node.IsInvalidTokenRange(startToken, endToken)) { continue; } results ??= new List(); - results.Add(Format(node, options, rules, pair.Item1, pair.Item2, cancellationToken)); + results.Add(Format(node, options.Options, rules, startToken, endToken, cancellationToken)); } // quick simple case check @@ -106,38 +79,5 @@ private IFormattingResult FormatIndividually( // more expensive case return CreateAggregatedFormattingResult(node, results); } - - private static bool AllowDisjointSpanMerging(IReadOnlyList list, bool shouldUseFormattingSpanCollapse) - { - // If the user is specific about the formatting specific spans then honor users settings - if (!shouldUseFormattingSpanCollapse) - { - return false; - } - - // most common case. it is either just formatting a whole file, a selection or some generate XXX refactoring. - if (list.Count <= 3) - { - // don't collapse formatting spans - return false; - } - - // too many formatting spans, just collapse them and format at once - if (list.Count > 30) - { - // figuring out base indentation at random place in a file takes about 2ms. - // doing 30 times will make it cost about 60ms. that is about same cost, for the same file, engine will - // take to create full formatting context. basically after that, creating full context is cheaper than - // doing bottom up base indentation calculation for each span. - return true; - } - - // check how much area we are formatting - var formattingSpan = TextSpan.FromBounds(list[0].Start, list[list.Count - 1].End); - var actualFormattingSize = list.Sum(s_spanLength); - - // we are formatting more than half of the collapsed span. - return (formattingSpan.Length / Math.Max(actualFormattingSize, 1)) < 2; - } } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/ISyntaxFormattingService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/ISyntaxFormattingService.cs index a2801f5caed39..9ee260b539894 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/ISyntaxFormattingService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/ISyntaxFormattingService.cs @@ -27,19 +27,16 @@ internal interface ISyntaxFormattingService } internal readonly record struct SyntaxFormattingOptions( - AnalyzerConfigOptions Options, - bool ShouldUseFormattingSpanCollapse) + AnalyzerConfigOptions Options) { public static readonly SyntaxFormattingOptions Default = Create(DictionaryAnalyzerConfigOptions.Empty); public static SyntaxFormattingOptions Create(AnalyzerConfigOptions options) - => new(options, ShouldUseFormattingSpanCollapse: false); + => new(options); #if !CODE_STYLE - public static SyntaxFormattingOptions Create(OptionSet options, HostWorkspaceServices services, string language, bool? shouldUseFormattingSpanCollapse = null) - => new( - options.AsAnalyzerConfigOptions(services.GetRequiredService(), language), - shouldUseFormattingSpanCollapse ?? options.GetOption(FormattingBehaviorOptions.AllowDisjointSpanMerging)); + public static SyntaxFormattingOptions Create(OptionSet options, HostWorkspaceServices services, string language) + => new(options.AsAnalyzerConfigOptions(services.GetRequiredService(), language)); public static async Task FromDocumentAsync(Document document, CancellationToken cancellationToken) { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/CommonFormattingHelpers.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/CommonFormattingHelpers.cs index f46795c20ac61..54ae88b5b443a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/CommonFormattingHelpers.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/CommonFormattingHelpers.cs @@ -39,7 +39,7 @@ internal static class CommonFormattingHelpers return 0; }; - public static IEnumerable> ConvertToTokenPairs(this SyntaxNode root, IReadOnlyList spans) + public static IEnumerable<(SyntaxToken, SyntaxToken)> ConvertToTokenPairs(this SyntaxNode root, IReadOnlyList spans) { Contract.ThrowIfNull(root); Contract.ThrowIfFalse(spans.Count > 0); From 650652780cf9c9273e61b94ecdd1239c8b8d42c5 Mon Sep 17 00:00:00 2001 From: tmat Date: Thu, 13 Jan 2022 13:59:00 -0800 Subject: [PATCH 05/13] Cleanup --- .../VisualBasicTest/Formatting/VisualBasicFormattingTestBase.vb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/VisualBasicTest/Formatting/VisualBasicFormattingTestBase.vb b/src/Workspaces/VisualBasicTest/Formatting/VisualBasicFormattingTestBase.vb index 15dd331e1fe35..b08f8169f7f0c 100644 --- a/src/Workspaces/VisualBasicTest/Formatting/VisualBasicFormattingTestBase.vb +++ b/src/Workspaces/VisualBasicTest/Formatting/VisualBasicFormattingTestBase.vb @@ -56,7 +56,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.Formatting Dim project = workspace.CurrentSolution.AddProject("Project", "Project.dll", LanguageNames.VisualBasic) Dim document = project.AddDocument("Document", SourceText.From(code)) Dim syntaxTree = Await document.GetSyntaxTreeAsync() - Dim options = SyntaxFormattingOptions.Create(DictionaryAnalyzerConfigOptions.Empty) + Dim options = SyntaxFormattingOptions.Default ' Test various entry points into the formatter From 51a874057cea4113f0605b2508361634536d7f53 Mon Sep 17 00:00:00 2001 From: tmat Date: Thu, 13 Jan 2022 14:18:02 -0800 Subject: [PATCH 06/13] Cleanup --- .../TestUtilities/Formatting/CoreFormatterTestsBase.cs | 2 +- src/Workspaces/CoreTest/Editing/SyntaxEditorTests.cs | 2 +- src/Workspaces/CoreTest/FormattingTests.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs b/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs index 8a6d8c5f491dc..94a610785dc8e 100644 --- a/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs +++ b/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs @@ -282,7 +282,7 @@ await AssertFormatAsync( protected static void AssertFormatOnArbitraryNode(SyntaxNode node, string expected) { using var workspace = new AdhocWorkspace(); - var options = SyntaxFormattingOptions.Create(DictionaryAnalyzerConfigOptions.Empty); + var options = SyntaxFormattingOptions.Default; var result = Formatter.Format(node, workspace.Services, options, CancellationToken.None); var actual = result.GetText().ToString(); diff --git a/src/Workspaces/CoreTest/Editing/SyntaxEditorTests.cs b/src/Workspaces/CoreTest/Editing/SyntaxEditorTests.cs index 35e0620b0c951..a316c6c451988 100644 --- a/src/Workspaces/CoreTest/Editing/SyntaxEditorTests.cs +++ b/src/Workspaces/CoreTest/Editing/SyntaxEditorTests.cs @@ -28,7 +28,7 @@ private void VerifySyntax(SyntaxNode node, string expectedText) where T { Assert.IsAssignableFrom(node); - var options = SyntaxFormattingOptions.Create(DictionaryAnalyzerConfigOptions.Empty); + var options = SyntaxFormattingOptions.Default; var formatted = Formatter.Format(node, EmptyWorkspace.Services, options, CancellationToken.None); var actualText = formatted.ToFullString(); Assert.Equal(expectedText, actualText); diff --git a/src/Workspaces/CoreTest/FormattingTests.cs b/src/Workspaces/CoreTest/FormattingTests.cs index ad909ea3af59d..ff56861851559 100644 --- a/src/Workspaces/CoreTest/FormattingTests.cs +++ b/src/Workspaces/CoreTest/FormattingTests.cs @@ -83,7 +83,7 @@ private static void AssertFormat(string expected, SyntaxTree tree) { using var workspace = new AdhocWorkspace(); - var options = SyntaxFormattingOptions.Create(DictionaryAnalyzerConfigOptions.Empty); + var options = SyntaxFormattingOptions.Default; var formattedRoot = Formatter.Format(tree.GetRoot(), workspace.Services, options, CancellationToken.None); var actualFormattedText = formattedRoot.ToFullString(); From 8a9f26243e3844da4dc7eb62b759681f852aaa7f Mon Sep 17 00:00:00 2001 From: tmat Date: Thu, 13 Jan 2022 14:27:17 -0800 Subject: [PATCH 07/13] Remove unused --- .../Compiler/Core/Formatting/AbstractSyntaxFormattingService.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/AbstractSyntaxFormattingService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/AbstractSyntaxFormattingService.cs index 9de1f9bd9cd06..d1de744dc12a7 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/AbstractSyntaxFormattingService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/AbstractSyntaxFormattingService.cs @@ -19,7 +19,6 @@ namespace Microsoft.CodeAnalysis.Formatting internal abstract class AbstractSyntaxFormattingService : ISyntaxFormattingService { private static readonly Func s_notEmpty = s => !s.IsEmpty; - private static readonly Func s_spanLength = s => s.Length; protected AbstractSyntaxFormattingService() { From 1df4502aa8b8b04c41797f610bb5d950830fa165 Mon Sep 17 00:00:00 2001 From: tmat Date: Thu, 13 Jan 2022 14:45:26 -0800 Subject: [PATCH 08/13] Fix --- src/CodeStyle/Core/Analyzers/Formatting/FormatterHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CodeStyle/Core/Analyzers/Formatting/FormatterHelper.cs b/src/CodeStyle/Core/Analyzers/Formatting/FormatterHelper.cs index 774141963d199..54db1c6918b95 100644 --- a/src/CodeStyle/Core/Analyzers/Formatting/FormatterHelper.cs +++ b/src/CodeStyle/Core/Analyzers/Formatting/FormatterHelper.cs @@ -53,7 +53,7 @@ internal static IList GetFormattedTextChanges(SyntaxNode node, IEnum => GetFormattingResult(node, spans, syntaxFormattingService, options, rules, cancellationToken).GetTextChanges(cancellationToken); internal static IFormattingResult GetFormattingResult(SyntaxNode node, IEnumerable spans, ISyntaxFormattingService syntaxFormattingService, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken) - => syntaxFormattingService.GetFormattingResult(node, spans, options with { ShouldUseFormattingSpanCollapse = false }, rules, cancellationToken); + => syntaxFormattingService.GetFormattingResult(node, spans, options, rules, cancellationToken); /// /// Determines the changes necessary to format the whitespace of a syntax tree. From 4cf8ae4b696290cd80b9a0732162a890b05b1e5e Mon Sep 17 00:00:00 2001 From: tmat Date: Fri, 14 Jan 2022 11:25:53 -0800 Subject: [PATCH 09/13] Fix --- .../Indentation/CSharpFormatterTestsBase.cs | 2 +- .../SmartTokenFormatterFormatRangeTests.cs | 2 +- .../CSharpFormattingInteractionService.cs | 28 +++++++++---------- .../CSharpIndentationService.Indenter.cs | 3 +- .../Indentation/CSharpSmartTokenFormatter.cs | 13 +++++---- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs index 2d2bbdcc5ae5a..a69d3d6ab615d 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs @@ -81,7 +81,7 @@ private static async Task TokenFormatWorkerAsync(TestWorkspace workspace, ITextB var rules = formattingRuleProvider.CreateRule(document, position).Concat(Formatter.GetDefaultFormattingRules(document)); - var options = await SyntaxFormattingOptions.FromDocumentAsync(document, CancellationToken.None); + var options = await document.GetOptionsAsync(); var formatter = new CSharpSmartTokenFormatter(options, rules, root); var changes = await formatter.FormatTokenAsync(workspace.Services, token, CancellationToken.None); diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartTokenFormatterFormatRangeTests.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartTokenFormatterFormatRangeTests.cs index cfdf914513aba..d0aa8db7ddfc3 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartTokenFormatterFormatRangeTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartTokenFormatterFormatRangeTests.cs @@ -3589,7 +3589,7 @@ private static async Task AutoFormatOnMarkerAsync(string initialMarkup, string e } Assert.Equal(tokenKind, endToken.Kind()); - var options = await SyntaxFormattingOptions.FromDocumentAsync(document, CancellationToken.None); + var options = await document.GetOptionsAsync(); var formatter = new CSharpSmartTokenFormatter(options, rules, root); var tokenRange = FormattingRangeHelper.FindAppropriateRange(endToken); diff --git a/src/Features/CSharp/Portable/Formatting/CSharpFormattingInteractionService.cs b/src/Features/CSharp/Portable/Formatting/CSharpFormattingInteractionService.cs index 6ac9c3096366f..205f41e2e3885 100644 --- a/src/Features/CSharp/Portable/Formatting/CSharpFormattingInteractionService.cs +++ b/src/Features/CSharp/Portable/Formatting/CSharpFormattingInteractionService.cs @@ -222,8 +222,6 @@ public async Task> GetFormattingChangesAsync( documentOptions = await inferredIndentationService.GetDocumentOptionsWithInferredIndentationAsync(document, explicitFormat: false, cancellationToken: cancellationToken).ConfigureAwait(false); } - var options = SyntaxFormattingOptions.Create(documentOptions, services, document.Project.Language); - // Do not attempt to format on open/close brace if autoformat on close brace feature is // off, instead just smart indent. // @@ -253,42 +251,42 @@ public async Task> GetFormattingChangesAsync( // However, we won't touch any of the other code in that block, unlike if we were // formatting. var onlySmartIndent = - (token.IsKind(SyntaxKind.CloseBraceToken) && OnlySmartIndentCloseBrace(options)) || - (token.IsKind(SyntaxKind.OpenBraceToken) && OnlySmartIndentOpenBrace(options)); + (token.IsKind(SyntaxKind.CloseBraceToken) && OnlySmartIndentCloseBrace(documentOptions)) || + (token.IsKind(SyntaxKind.OpenBraceToken) && OnlySmartIndentOpenBrace(documentOptions)); if (onlySmartIndent) { // if we're only doing smart indent, then ignore all edits to this token that occur before // the span of the token. They're irrelevant and may screw up other code the user doesn't // want touched. - var tokenEdits = await FormatTokenAsync(document, options, token, formattingRules, cancellationToken).ConfigureAwait(false); + var tokenEdits = await FormatTokenAsync(document, documentOptions, token, formattingRules, cancellationToken).ConfigureAwait(false); return tokenEdits.Where(t => t.Span.Start >= token.FullSpan.Start).ToImmutableArray(); } // if formatting range fails, do format token one at least - var changes = await FormatRangeAsync(document, options, token, formattingRules, cancellationToken).ConfigureAwait(false); + var changes = await FormatRangeAsync(document, documentOptions, token, formattingRules, cancellationToken).ConfigureAwait(false); if (changes.Length > 0) { return changes; } - return (await FormatTokenAsync(document, options, token, formattingRules, cancellationToken).ConfigureAwait(false)).ToImmutableArray(); + return (await FormatTokenAsync(document, documentOptions, token, formattingRules, cancellationToken).ConfigureAwait(false)).ToImmutableArray(); } - private static bool OnlySmartIndentCloseBrace(in SyntaxFormattingOptions options) + private static bool OnlySmartIndentCloseBrace(DocumentOptionSet options) { // User does not want auto-formatting (either in general, or for close braces in // specific). So we only smart indent close braces when typed. - return !options.Options.GetOption(BraceCompletionOptions.AutoFormattingOnCloseBrace) || - !options.Options.GetOption(FormattingBehaviorOptions.AutoFormattingOnTyping); + return !options.GetOption(BraceCompletionOptions.AutoFormattingOnCloseBrace) || + !options.GetOption(FormattingBehaviorOptions.AutoFormattingOnTyping); } - private static bool OnlySmartIndentOpenBrace(in SyntaxFormattingOptions options) + private static bool OnlySmartIndentOpenBrace(DocumentOptionSet options) { // User does not want auto-formatting . So we only smart indent open braces when typed. // Note: there is no specific option for controlling formatting on open brace. So we // don't have the symmetry with OnlySmartIndentCloseBrace. - return !options.Options.GetOption(FormattingBehaviorOptions.AutoFormattingOnTyping); + return !options.GetOption(FormattingBehaviorOptions.AutoFormattingOnTyping); } private static async Task GetTokenBeforeTheCaretAsync(Document document, int caretPosition, CancellationToken cancellationToken) @@ -301,7 +299,7 @@ private static async Task GetTokenBeforeTheCaretAsync(Document docu return token; } - private static async Task> FormatTokenAsync(Document document, SyntaxFormattingOptions options, SyntaxToken token, IEnumerable formattingRules, CancellationToken cancellationToken) + private static async Task> FormatTokenAsync(Document document, OptionSet options, SyntaxToken token, IEnumerable formattingRules, CancellationToken cancellationToken) { var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var formatter = CreateSmartTokenFormatter(options, formattingRules, root); @@ -309,12 +307,12 @@ private static async Task> FormatTokenAsync(Document document, return changes; } - private static ISmartTokenFormatter CreateSmartTokenFormatter(SyntaxFormattingOptions options, IEnumerable formattingRules, SyntaxNode root) + private static ISmartTokenFormatter CreateSmartTokenFormatter(OptionSet options, IEnumerable formattingRules, SyntaxNode root) => new CSharpSmartTokenFormatter(options, formattingRules, (CompilationUnitSyntax)root); private static async Task> FormatRangeAsync( Document document, - SyntaxFormattingOptions options, + OptionSet options, SyntaxToken endToken, IEnumerable formattingRules, CancellationToken cancellationToken) diff --git a/src/Workspaces/CSharp/Portable/Indentation/CSharpIndentationService.Indenter.cs b/src/Workspaces/CSharp/Portable/Indentation/CSharpIndentationService.Indenter.cs index 2f68fcc43c43a..0c9deac40330b 100644 --- a/src/Workspaces/CSharp/Portable/Indentation/CSharpIndentationService.Indenter.cs +++ b/src/Workspaces/CSharp/Portable/Indentation/CSharpIndentationService.Indenter.cs @@ -27,8 +27,7 @@ protected override ISmartTokenFormatter CreateSmartTokenFormatter(Indenter inden var services = indenter.Document.Project.Solution.Workspace.Services; var formattingRuleFactory = services.GetRequiredService(); var rules = formattingRuleFactory.CreateRule(indenter.Document.Document, indenter.LineToBeIndented.Start).Concat(Formatter.GetDefaultFormattingRules(indenter.Document.Document)); - var formattingOptions = SyntaxFormattingOptions.Create(indenter.OptionSet, services, indenter.Document.Project.Language); - return new CSharpSmartTokenFormatter(formattingOptions, rules, indenter.Root); + return new CSharpSmartTokenFormatter(indenter.OptionSet, rules, indenter.Root); } protected override IndentationResult? GetDesiredIndentationWorker(Indenter indenter, SyntaxToken? tokenOpt, SyntaxTrivia? triviaOpt) diff --git a/src/Workspaces/CSharp/Portable/Indentation/CSharpSmartTokenFormatter.cs b/src/Workspaces/CSharp/Portable/Indentation/CSharpSmartTokenFormatter.cs index 874a56a3e1410..f767038d07f6b 100644 --- a/src/Workspaces/CSharp/Portable/Indentation/CSharpSmartTokenFormatter.cs +++ b/src/Workspaces/CSharp/Portable/Indentation/CSharpSmartTokenFormatter.cs @@ -22,16 +22,17 @@ namespace Microsoft.CodeAnalysis.CSharp.Indentation { internal class CSharpSmartTokenFormatter : ISmartTokenFormatter { - private readonly SyntaxFormattingOptions _options; + private readonly OptionSet _options; private readonly IEnumerable _formattingRules; private readonly CompilationUnitSyntax _root; public CSharpSmartTokenFormatter( - SyntaxFormattingOptions options, + OptionSet options, IEnumerable formattingRules, CompilationUnitSyntax root) { + Contract.ThrowIfNull(options); Contract.ThrowIfNull(formattingRules); Contract.ThrowIfNull(root); @@ -60,7 +61,8 @@ public IList FormatRange( smartTokenformattingRules = (new NoLineChangeFormattingRule()).Concat(_formattingRules); } - return Formatter.GetFormattedTextChanges(_root, new[] { TextSpan.FromBounds(startToken.SpanStart, endToken.Span.End) }, services, _options, smartTokenformattingRules, cancellationToken); + var formattingOptions = SyntaxFormattingOptions.Create(_options, services, _root.Language); + return Formatter.GetFormattedTextChanges(_root, new[] { TextSpan.FromBounds(startToken.SpanStart, endToken.Span.End) }, services, formattingOptions, smartTokenformattingRules, cancellationToken); } private static bool CloseBraceOfTryOrDoBlock(SyntaxToken endToken) @@ -102,7 +104,7 @@ public async Task> FormatTokenAsync( var smartTokenformattingRules = new SmartTokenFormattingRule().Concat(_formattingRules); var adjustedStartPosition = previousToken.SpanStart; - var indentStyle = _options.Options.GetOption(FormattingOptions.SmartIndent); + var indentStyle = _options.GetOption(FormattingOptions.SmartIndent, LanguageNames.CSharp); if (token.IsKind(SyntaxKind.OpenBraceToken) && indentStyle != FormattingOptions.IndentStyle.Smart) { @@ -114,7 +116,8 @@ public async Task> FormatTokenAsync( } } - return Formatter.GetFormattedTextChanges(_root, new[] { TextSpan.FromBounds(adjustedStartPosition, adjustedEndPosition) }, services, _options, smartTokenformattingRules, cancellationToken); + var formattingOptions = SyntaxFormattingOptions.Create(_options, services, _root.Language); + return Formatter.GetFormattedTextChanges(_root, new[] { TextSpan.FromBounds(adjustedStartPosition, adjustedEndPosition) }, services, formattingOptions, smartTokenformattingRules, cancellationToken); } private class NoLineChangeFormattingRule : AbstractFormattingRule From 4a302543661310650e86462120a5d13f8e9d878e Mon Sep 17 00:00:00 2001 From: tmat Date: Fri, 14 Jan 2022 12:44:50 -0800 Subject: [PATCH 10/13] FIx --- .../VisualBasic/LineCommit/CommitFormatter.vb | 2 +- .../Providers/FormatCodeCleanupProvider.cs | 13 +++++++++++-- .../Formatting/VisualBasicFormattingTestBase.vb | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/EditorFeatures/VisualBasic/LineCommit/CommitFormatter.vb b/src/EditorFeatures/VisualBasic/LineCommit/CommitFormatter.vb index 00ea25ac23635..33467f212507f 100644 --- a/src/EditorFeatures/VisualBasic/LineCommit/CommitFormatter.vb +++ b/src/EditorFeatures/VisualBasic/LineCommit/CommitFormatter.vb @@ -143,7 +143,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.LineCommit ' based on changes made to dirty spans, get right formatting rules to apply Dim rules = GetFormattingRules(document, documentOptions, spanToFormat, oldDirtySpan, oldTree, newDirtySpan, newTree, cancellationToken) - Return New FormatCodeCleanupProvider() + Return New FormatCodeCleanupProvider(rules) End Function Private Shared Function GetFormattingRules( diff --git a/src/Workspaces/Core/Portable/CodeCleanup/Providers/FormatCodeCleanupProvider.cs b/src/Workspaces/Core/Portable/CodeCleanup/Providers/FormatCodeCleanupProvider.cs index 073fb0fc3eafa..707c1d34a32fa 100644 --- a/src/Workspaces/Core/Portable/CodeCleanup/Providers/FormatCodeCleanupProvider.cs +++ b/src/Workspaces/Core/Portable/CodeCleanup/Providers/FormatCodeCleanupProvider.cs @@ -2,10 +2,12 @@ // 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.Collections.Generic; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; @@ -14,13 +16,20 @@ namespace Microsoft.CodeAnalysis.CodeCleanup.Providers { internal sealed class FormatCodeCleanupProvider : ICodeCleanupProvider { + private readonly IEnumerable? _rules; + + public FormatCodeCleanupProvider(IEnumerable? rules = null) + { + _rules = rules; + } + public string Name => PredefinedCodeCleanupProviderNames.Format; public async Task CleanupAsync(Document document, ImmutableArray spans, SyntaxFormattingOptions options, CancellationToken cancellationToken) { var formatter = document.GetRequiredLanguageService(); var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var result = formatter.GetFormattingResult(root, spans, options, rules: null, cancellationToken); + var result = formatter.GetFormattingResult(root, spans, options, _rules, cancellationToken); // apply changes to an old text if it already exists return document.TryGetText(out var oldText) ? @@ -31,7 +40,7 @@ public async Task CleanupAsync(Document document, ImmutableArray CleanupAsync(SyntaxNode root, ImmutableArray spans, SyntaxFormattingOptions options, HostWorkspaceServices services, CancellationToken cancellationToken) { var formatter = services.GetRequiredLanguageService(root.Language); - var result = formatter.GetFormattingResult(root, spans, options, rules: null, cancellationToken); + var result = formatter.GetFormattingResult(root, spans, options, _rules, cancellationToken); // apply changes to an old text if it already exists return (root.SyntaxTree != null && root.SyntaxTree.TryGetText(out var oldText)) ? diff --git a/src/Workspaces/VisualBasicTest/Formatting/VisualBasicFormattingTestBase.vb b/src/Workspaces/VisualBasicTest/Formatting/VisualBasicFormattingTestBase.vb index b08f8169f7f0c..d854e8d556bf2 100644 --- a/src/Workspaces/VisualBasicTest/Formatting/VisualBasicFormattingTestBase.vb +++ b/src/Workspaces/VisualBasicTest/Formatting/VisualBasicFormattingTestBase.vb @@ -76,7 +76,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.Formatting AssertResult(expected, Await document.GetTextAsync(), changes) ' format with node and transform - AssertFormatWithTransformation(workspace.Services, expected, syntaxTree.GetRoot(), spans, Nothing, False) + AssertFormatWithTransformation(workspace.Services, expected, syntaxTree.GetRoot(), spans, SyntaxFormattingOptions.Default, False) End Using End Function From 0ec787609d4c8a7873e469529c040177f652e1a9 Mon Sep 17 00:00:00 2001 From: tmat Date: Fri, 14 Jan 2022 14:15:27 -0800 Subject: [PATCH 11/13] Fix --- .../Core/Portable/Formatting/AbstractFormattingService.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Workspaces/Core/Portable/Formatting/AbstractFormattingService.cs b/src/Workspaces/Core/Portable/Formatting/AbstractFormattingService.cs index 047a6418fcdc7..e697edafd98c5 100644 --- a/src/Workspaces/Core/Portable/Formatting/AbstractFormattingService.cs +++ b/src/Workspaces/Core/Portable/Formatting/AbstractFormattingService.cs @@ -7,7 +7,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting { @@ -16,10 +15,10 @@ namespace Microsoft.CodeAnalysis.Formatting /// internal abstract class AbstractFormattingService : IFormattingService { - public async Task FormatAsync(Document document, IEnumerable? spans, OptionSet options, CancellationToken cancellationToken) + public Task FormatAsync(Document document, IEnumerable? spans, OptionSet options, CancellationToken cancellationToken) { - var formattingOptions = await SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false); - return await Formatter.FormatAsync(document, spans, formattingOptions, rules: null, cancellationToken).ConfigureAwait(false); + var formattingOptions = SyntaxFormattingOptions.Create(options, document.Project.Solution.Workspace.Services, document.Project.Language); + return Formatter.FormatAsync(document, spans, formattingOptions, rules: null, cancellationToken); } } } From cf634046cf80bcbb97f935efd5c6dd7847f2e71a Mon Sep 17 00:00:00 2001 From: tmat Date: Fri, 14 Jan 2022 15:35:43 -0800 Subject: [PATCH 12/13] Fix --- .../MetadataAsSource/AbstractMetadataAsSourceService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.cs b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.cs index 463256884a8c8..3854307a217fc 100644 --- a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.cs +++ b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.cs @@ -62,7 +62,7 @@ public async Task AddSourceToAsync(Document document, Compilation symb docWithAssemblyInfo, SpecializedCollections.SingletonEnumerable(node.FullSpan), options, - GetFormattingRules(docWithAssemblyInfo), + GetFormattingRules(docWithAssemblyInfo), cancellationToken).ConfigureAwait(false); var reducers = GetReducers(); From 01877933322248ae459e794addc799a8d0b50c5c Mon Sep 17 00:00:00 2001 From: tmat Date: Fri, 14 Jan 2022 18:02:55 -0800 Subject: [PATCH 13/13] Fix --- .../Indentation/SmartTokenFormatterFormatRangeTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartTokenFormatterFormatRangeTests.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartTokenFormatterFormatRangeTests.cs index d0aa8db7ddfc3..335d635ebff08 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartTokenFormatterFormatRangeTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartTokenFormatterFormatRangeTests.cs @@ -3573,7 +3573,6 @@ private static async Task AutoFormatOnMarkerAsync(string initialMarkup, string e workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options .WithChangedOption(FormattingOptions2.UseTabs, LanguageNames.CSharp, useTabs))); - var testDocument = workspace.Documents.Single(); var buffer = testDocument.GetTextBuffer(); var position = testDocument.CursorPosition.Value;