diff --git a/src/EditorFeatures/CSharpTest/Formatting/FormattingAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/Formatting/FormattingAnalyzerTests.cs new file mode 100644 index 0000000000000000000000000000000000000000..922b8884ce8c76a07e4fd92bf207197782f5464c --- /dev/null +++ b/src/EditorFeatures/CSharpTest/Formatting/FormattingAnalyzerTests.cs @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics; +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Formatting +{ + public class FormattingAnalyzerTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest + { + internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) + => (new FormattingDiagnosticAnalyzer(), new FormattingCodeFixProvider()); + + [Fact, Trait(Traits.Feature, Traits.Features.Formatting)] + public async Task TrailingWhitespace() + { + var testCode = + "class X[| |]" + Environment.NewLine + + "{" + Environment.NewLine + + "}" + Environment.NewLine; + var expected = + "class X" + Environment.NewLine + + "{" + Environment.NewLine + + "}" + Environment.NewLine; + await TestInRegularAndScriptAsync(testCode, expected); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Formatting)] + public async Task TestMissingSpace() + { + var testCode = @" +class TypeName +{ + void Method() + { + if$$(true)return; + } +} +"; + var expected = @" +class TypeName +{ + void Method() + { + if (true)return; + } +} +"; + + await TestInRegularAndScriptAsync(testCode, expected); + } + } +} diff --git a/src/Features/Core/Portable/CodeFixes/PredefinedCodeFixProviderNames.cs b/src/Features/Core/Portable/CodeFixes/PredefinedCodeFixProviderNames.cs index 9744103ff5f0aada23398f65416a71c8939e5e90..e62a530915c850fd12e1d3604d378b02bacbd705 100644 --- a/src/Features/Core/Portable/CodeFixes/PredefinedCodeFixProviderNames.cs +++ b/src/Features/Core/Portable/CodeFixes/PredefinedCodeFixProviderNames.cs @@ -21,6 +21,7 @@ internal static class PredefinedCodeFixProviderNames public const string AddMissingReference = nameof(AddMissingReference); public const string AddImport = nameof(AddImport); public const string FullyQualify = nameof(FullyQualify); + public const string FixFormatting = nameof(FixFormatting); public const string FixIncorrectFunctionReturnType = nameof(FixIncorrectFunctionReturnType); public const string FixIncorrectExitContinue = nameof(FixIncorrectExitContinue); public const string GenerateConstructor = nameof(GenerateConstructor); diff --git a/src/Features/Core/Portable/Diagnostics/Analyzers/IDEDiagnosticIds.cs b/src/Features/Core/Portable/Diagnostics/Analyzers/IDEDiagnosticIds.cs index 0c642546038291ac9efd52342287661214f0fb15..e4c9a43d68e5de9c8f9e90ccab2aa1be77e40758 100644 --- a/src/Features/Core/Portable/Diagnostics/Analyzers/IDEDiagnosticIds.cs +++ b/src/Features/Core/Portable/Diagnostics/Analyzers/IDEDiagnosticIds.cs @@ -84,6 +84,8 @@ internal static class IDEDiagnosticIds public const string UseExpressionBodyForLambdaExpressionsDiagnosticId = "IDE0053"; + public const string FormattingDiagnosticId = "IDE0054"; + // Analyzer error Ids public const string AnalyzerChangedId = "IDE1001"; public const string AnalyzerDependencyConflictId = "IDE1002"; diff --git a/src/Features/Core/Portable/FeaturesResources.Designer.cs b/src/Features/Core/Portable/FeaturesResources.Designer.cs index 215b6e91a65a977c750ed42ab6dad16db1a71bef..73d0f87558e9964815b056b494209e0f80d1a88e 100644 --- a/src/Features/Core/Portable/FeaturesResources.Designer.cs +++ b/src/Features/Core/Portable/FeaturesResources.Designer.cs @@ -1369,6 +1369,33 @@ internal class FeaturesResources { } } + /// + /// Looks up a localized string similar to Fix formatting. + /// + internal static string Formatting_analyzer_code_fix { + get { + return ResourceManager.GetString("Formatting_analyzer_code_fix", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Fix formatting. + /// + internal static string Formatting_analyzer_message { + get { + return ResourceManager.GetString("Formatting_analyzer_message", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Fix formatting. + /// + internal static string Formatting_analyzer_title { + get { + return ResourceManager.GetString("Formatting_analyzer_title", resourceCulture); + } + } + /// /// Looks up a localized string similar to Formatting document. /// diff --git a/src/Features/Core/Portable/FeaturesResources.resx b/src/Features/Core/Portable/FeaturesResources.resx index 6b374a45bfc19d144770bc609f127feed2d97a1e..b5e03eb48f1248c955dab9d6fddd60c0e24fbc39 100644 --- a/src/Features/Core/Portable/FeaturesResources.resx +++ b/src/Features/Core/Portable/FeaturesResources.resx @@ -1430,4 +1430,13 @@ This version used in: {2} Type '{0}' has a private member '{1}' that can be removed as the value assigned to it is never read. + + Fix formatting + + + Fix formatting + + + Fix formatting + \ No newline at end of file diff --git a/src/Features/Core/Portable/Formatting/FormattingCodeFixProvider.cs b/src/Features/Core/Portable/Formatting/FormattingCodeFixProvider.cs new file mode 100644 index 0000000000000000000000000000000000000000..f43831e23dee21b1b08de6a2f3dcbcf035d6b87c --- /dev/null +++ b/src/Features/Core/Portable/Formatting/FormattingCodeFixProvider.cs @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Formatting +{ + [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic, Name = PredefinedCodeFixProviderNames.FixFormatting)] + [Shared] + internal class FormattingCodeFixProvider : SyntaxEditorBasedCodeFixProvider + { + public override ImmutableArray FixableDiagnosticIds + => ImmutableArray.Create(IDEDiagnosticIds.FormattingDiagnosticId); + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + context.RegisterCodeFix( + new MyCodeAction(c => FixOneAsync(context.Document, context.Diagnostics, c)), + context.Diagnostics); + + return Task.CompletedTask; + } + + protected async Task FixOneAsync(Document document, ImmutableArray diagnostics, CancellationToken cancellationToken) + { + var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); + var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + var changes = new List(); + foreach (var diagnostic in diagnostics) + { + if (!tree.Equals(diagnostic.Location.SourceTree) + || !diagnostic.Properties.TryGetValue(FormattingDiagnosticAnalyzer.ReplaceTextKey, out var replacement)) + { + continue; + } + + changes.Add(new TextChange(diagnostic.Location.SourceSpan, replacement)); + } + + changes.Sort((left, right) => left.Span.Start.CompareTo(right.Span.Start)); + + return document.WithText(text.WithChanges(changes)); + } + + protected override async Task FixAllAsync(Document document, ImmutableArray diagnostics, SyntaxEditor editor, CancellationToken cancellationToken) + { + var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); + var updatedDocument = await Formatter.FormatAsync(document, options, cancellationToken).ConfigureAwait(false); + editor.ReplaceNode(editor.OriginalRoot, await updatedDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false)); + } + + private sealed class MyCodeAction : CodeAction.DocumentChangeAction + { + public MyCodeAction(Func> createChangedDocument) + : base(FeaturesResources.Formatting_analyzer_code_fix, createChangedDocument, FeaturesResources.Formatting_analyzer_code_fix) + { + } + } + } +} diff --git a/src/Features/Core/Portable/Formatting/FormattingDiagnosticAnalyzer.cs b/src/Features/Core/Portable/Formatting/FormattingDiagnosticAnalyzer.cs new file mode 100644 index 0000000000000000000000000000000000000000..1f19d8f55a2892399967f8eb9d9106267d7bfc8d --- /dev/null +++ b/src/Features/Core/Portable/Formatting/FormattingDiagnosticAnalyzer.cs @@ -0,0 +1,101 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Formatting +{ + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + internal class FormattingDiagnosticAnalyzer + : AbstractCodeStyleDiagnosticAnalyzer + { + public static readonly string ReplaceTextKey = nameof(ReplaceTextKey); + + public static readonly ImmutableDictionary RemoveTextProperties = + ImmutableDictionary.Create().Add(ReplaceTextKey, ""); + + public FormattingDiagnosticAnalyzer() + : base( + IDEDiagnosticIds.FormattingDiagnosticId, + new LocalizableResourceString(nameof(FeaturesResources.Formatting_analyzer_title), FeaturesResources.ResourceManager, typeof(FeaturesResources)), + new LocalizableResourceString(nameof(FeaturesResources.Formatting_analyzer_message), FeaturesResources.ResourceManager, typeof(FeaturesResources))) + { + } + + public override DiagnosticAnalyzerCategory GetAnalyzerCategory() + => DiagnosticAnalyzerCategory.SyntaxAnalysis; + + public override bool OpenFileOnly(Workspace workspace) + => false; + + protected override void InitializeWorker(AnalysisContext context) + => context.RegisterSyntaxTreeAction(AnalyzeSyntaxTree); + + private void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) + { + if (!(context.Options is WorkspaceAnalyzerOptions workspaceAnalyzerOptions)) + { + return; + } + + var options = context.Options.GetDocumentOptionSetAsync(context.Tree, context.CancellationToken).GetAwaiter().GetResult(); + if (options == null) + { + return; + } + + var workspace = workspaceAnalyzerOptions.Services.Workspace; + var formattingChanges = Formatter.GetFormattedTextChanges(context.Tree.GetRoot(context.CancellationToken), workspace, options, context.CancellationToken); + foreach (var formattingChange in formattingChanges) + { + var change = formattingChange; + if (change.NewText.Length > 0 && !change.Span.IsEmpty) + { + var oldText = context.Tree.GetText(context.CancellationToken); + + // Handle cases where the change is a substring removal from the beginning + var offset = change.Span.Length - change.NewText.Length; + if (offset >= 0 && oldText.GetSubText(new TextSpan(change.Span.Start + offset, change.NewText.Length)).ContentEquals(SourceText.From(change.NewText))) + { + change = new TextChange(new TextSpan(change.Span.Start, offset), ""); + } + else + { + // Handle cases where the change is a substring removal from the end + if (change.NewText.Length < change.Span.Length + && oldText.GetSubText(new TextSpan(change.Span.Start, change.NewText.Length)).ContentEquals(SourceText.From(change.NewText))) + { + change = new TextChange(new TextSpan(change.Span.Start + change.NewText.Length, change.Span.Length - change.NewText.Length), ""); + } + } + } + + if (change.NewText.Length == 0 && change.Span.IsEmpty) + { + // No actual change + continue; + } + + ImmutableDictionary properties; + if (change.NewText.Length == 0) + { + properties = RemoveTextProperties; + } + else + { + properties = ImmutableDictionary.Create().Add(ReplaceTextKey, change.NewText); + } + + var location = Location.Create(context.Tree, change.Span); + context.ReportDiagnostic(DiagnosticHelper.Create( + Descriptor, + location, + ReportDiagnostic.Default, + additionalLocations: null, + properties)); + } + } + } +} diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf index 089dfd5fee28c3c05ff2a0cbf7ee577fff79633a..c895ec2a0b8d1434bd12c9e4d7229d335ecbc4d0 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf @@ -62,6 +62,21 @@ Opravit překlep {0} + + Fix formatting + Fix formatting + + + + Fix formatting + Fix formatting + + + + Fix formatting + Fix formatting + + Formatting document Formátuje se dokument. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf index 77a5b454eb105fc40122318ba27ae57a5b6735a5..0da8017d26d35e268e3978e64e871a77441bf68e 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf @@ -62,6 +62,21 @@ Tippfehler "{0}" korrigieren + + Fix formatting + Fix formatting + + + + Fix formatting + Fix formatting + + + + Fix formatting + Fix formatting + + Formatting document Dokument wird formatiert diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf index aaa7bd169fb106cf3063fe5347ea1618025eb3d7..d8f88c6205ff1e2a4b5e465ca297fade1c6d0c18 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf @@ -62,6 +62,21 @@ Corregir error de escritura "{0}" + + Fix formatting + Fix formatting + + + + Fix formatting + Fix formatting + + + + Fix formatting + Fix formatting + + Formatting document Aplicando formato al documento diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf index 4fd55ad860bf5212c86cca7a4da557e1d5c1f728..887c572e7570d30a927a25ea60d1697048312ae2 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf @@ -62,6 +62,21 @@ Corriger la faute de frappe '{0}' + + Fix formatting + Fix formatting + + + + Fix formatting + Fix formatting + + + + Fix formatting + Fix formatting + + Formatting document Mise en forme du document diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf index 9aed9dc638808e65ec2dcfd590ead74326a8e3e5..378965d4eefa51417d7e0a8d0da161408ff7a357 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf @@ -62,6 +62,21 @@ Correggere l'errore di ortografia '{0}' + + Fix formatting + Fix formatting + + + + Fix formatting + Fix formatting + + + + Fix formatting + Fix formatting + + Formatting document Formattazione del documento diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf index 8009b5e33d1993f41bb14ec7577f51821769ee18..f150d1b8bd77143a8243f9c15d47c51f4562515b 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf @@ -62,6 +62,21 @@ '{0}' の入力ミスを修正します + + Fix formatting + Fix formatting + + + + Fix formatting + Fix formatting + + + + Fix formatting + Fix formatting + + Formatting document ドキュメントの書式設定 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf index b03ab463232e8c9afdf190f736cdba4c3dccc32d..9519547453e87c68c287ea8c4e576f37fdefa761 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf @@ -62,6 +62,21 @@ 오타 '{0}' 수정 + + Fix formatting + Fix formatting + + + + Fix formatting + Fix formatting + + + + Fix formatting + Fix formatting + + Formatting document 문서 서식 지정 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf index d6a2615bef8c9e5240565b2162d7e1cbd4ee0fb0..2bfd6534ac63001d2ff66775cfe0befa095200c8 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf @@ -62,6 +62,21 @@ Popraw błąd pisowni „{0}” + + Fix formatting + Fix formatting + + + + Fix formatting + Fix formatting + + + + Fix formatting + Fix formatting + + Formatting document Formatowanie dokumentu diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf index 7c3d9aed55c5a2843b65205dd3de4554e1e4e53b..fa3a51336e90f1472c914a3fbdf02cfccde803fb 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf @@ -62,6 +62,21 @@ Corrigir erro de digitação '{0}' + + Fix formatting + Fix formatting + + + + Fix formatting + Fix formatting + + + + Fix formatting + Fix formatting + + Formatting document Formatando documento diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf index 7dd2e43c518abd25c9cfb45125a6d9036f43caf6..16b87ab4fa3db81666b17c6dfc6774e878b54032 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf @@ -62,6 +62,21 @@ Исправьте опечатку "{0}" + + Fix formatting + Fix formatting + + + + Fix formatting + Fix formatting + + + + Fix formatting + Fix formatting + + Formatting document Форматирование документа diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf index b4d87e6f3451c7464a68e990d6731b048cec1bab..ccd4f7a40db4570ab2d171d72209bafe11d09ff0 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf @@ -62,6 +62,21 @@ '{0}' yazım hatasını düzeltin + + Fix formatting + Fix formatting + + + + Fix formatting + Fix formatting + + + + Fix formatting + Fix formatting + + Formatting document Belge biçimlendiriliyor diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf index c729b2e2d69152eeae5116a86ec4cd288117da9f..a1233d9f88429ff85a5305e94c23f9f903c66087 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf @@ -62,6 +62,21 @@ 修正笔误“{0}” + + Fix formatting + Fix formatting + + + + Fix formatting + Fix formatting + + + + Fix formatting + Fix formatting + + Formatting document 设置文档格式 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf index 1c99e47d2096892c5c6068c85a369a26a6635a7d..17400e64cba5112f186bcd5ef00147611fd50f33 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf @@ -62,6 +62,21 @@ 修正錯字 '{0}' + + Fix formatting + Fix formatting + + + + Fix formatting + Fix formatting + + + + Fix formatting + Fix formatting + + Formatting document 正在將文件格式化 diff --git a/src/Workspaces/Core/Portable/Formatting/Engine/AbstractFormatEngine.cs b/src/Workspaces/Core/Portable/Formatting/Engine/AbstractFormatEngine.cs index 482fc7a0488fa4457c427aae56bbe6c60d111ff6..c5e158c672c637ffc574a3bcfdbbecd1ee469e80 100644 --- a/src/Workspaces/Core/Portable/Formatting/Engine/AbstractFormatEngine.cs +++ b/src/Workspaces/Core/Portable/Formatting/Engine/AbstractFormatEngine.cs @@ -92,9 +92,13 @@ internal abstract partial class AbstractFormatEngine _language = token1.Language; } - // set synchronous task executor if it is debug mode or if there is not many things to format - this.TaskExecutor = optionSet.GetOption(FormattingOptions.DebugMode, _language) ? TaskExecutor.Synchronous : - (SpanToFormat.Length < ConcurrentThreshold) ? TaskExecutor.Synchronous : executor; + // set synchronous task executor if it is enabled (explicitly or as part of debug mode) or if there is not + // many things to format + var synchronousExecutorAllowed = + !optionSet.GetOption(FormattingOptions.AllowConcurrent) + || optionSet.GetOption(FormattingOptions.DebugMode, _language); + var useSynchronousExecutor = synchronousExecutorAllowed || SpanToFormat.Length < ConcurrentThreshold; + TaskExecutor = useSynchronousExecutor ? TaskExecutor.Synchronous : executor; } protected abstract AbstractTriviaDataFactory CreateTriviaFactory(); diff --git a/src/Workspaces/Core/Portable/Formatting/Formatter.cs b/src/Workspaces/Core/Portable/Formatting/Formatter.cs index 2a26fe9af4a6affb3d2f29c2df579114c2f27821..3fd87091971139bb4561d2e0c92ae12b0949f768 100644 --- a/src/Workspaces/Core/Portable/Formatting/Formatter.cs +++ b/src/Workspaces/Core/Portable/Formatting/Formatter.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -281,7 +282,13 @@ internal static Task GetFormattingResult(SyntaxNode node, IEn /// An optional cancellation token. /// The changes necessary to format the tree. public static IList GetFormattedTextChanges(SyntaxNode node, Workspace workspace, OptionSet options = null, CancellationToken cancellationToken = default) - => GetFormattedTextChangesAsync(node, workspace, options, cancellationToken).WaitAndGetResult_CanCallOnBackground(cancellationToken); + { + options = options ?? workspace.Options; + options = options.WithChangedOption(FormattingOptions.AllowConcurrent, false); + var resultTask = GetFormattedTextChangesAsync(node, workspace, options, cancellationToken); + Debug.Assert(resultTask.IsCompleted); + return resultTask.WaitAndGetResult_CanCallOnBackground(cancellationToken); + } internal static Task> GetFormattedTextChangesAsync(SyntaxNode node, Workspace workspace, OptionSet options = null, CancellationToken cancellationToken = default) => GetFormattedTextChangesAsync(node, SpecializedCollections.SingletonEnumerable(node.FullSpan), workspace, options, rules: null, cancellationToken: cancellationToken); diff --git a/src/Workspaces/Core/Portable/Formatting/FormattingOptions.cs b/src/Workspaces/Core/Portable/Formatting/FormattingOptions.cs index a56a7b233f8be16c57d0e4435f11e9732856ac0e..2686547d2322c4e738a50ee0bdda85930dee13c2 100644 --- a/src/Workspaces/Core/Portable/Formatting/FormattingOptions.cs +++ b/src/Workspaces/Core/Portable/Formatting/FormattingOptions.cs @@ -45,6 +45,7 @@ private static Optional ParseEditorConfigEndOfLine(string endOfLineValue } internal static PerLanguageOption DebugMode { get; } = new PerLanguageOption(nameof(FormattingOptions), nameof(DebugMode), defaultValue: false); + internal static Option AllowConcurrent { get; } = new Option(nameof(FormattingOptions), nameof(AllowConcurrent), defaultValue: true); internal static Option AllowDisjointSpanMerging { get; } = new Option(nameof(FormattingOptions), nameof(AllowDisjointSpanMerging), defaultValue: false);