From 5fd5e5b6e431442f07b8ec47a9435729dafcdba2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Tue, 15 Mar 2022 09:53:56 -0700 Subject: [PATCH] Global indentation options (#59679) * Make options global * Fixes * Razor * Fixes * Fix tests * Simplify Razor API * Fix whitespace --- .../ConvertNamespaceCommandHandler.cs | 3 +- .../DocumentationCommentCommandHandler.cs | 8 +- .../RawStringLiteralCommandHandler_Return.cs | 5 +- .../SplitStringLiteralCommandHandler.cs | 5 +- .../AutomaticBraceCompletionTests.cs | 34 +++---- .../Formatting/FormattingEngineTests.cs | 58 ++++++------ .../Indentation/CSharpFormatterTestsBase.cs | 5 +- .../SmartIndenterEnterOnTokenTests.cs | 25 +++--- .../Indentation/SmartIndenterTests.cs | 25 +++--- .../SmartTokenFormatterFormatRangeTests.cs | 10 ++- .../SplitStringLiteralCommandHandlerTests.cs | 4 +- ...nSessionProvider.BraceCompletionSession.cs | 34 ++++--- .../BraceCompletionSessionProvider.cs | 8 +- ...tractDocumentationCommentCommandHandler.cs | 11 ++- ...STypeScriptFormattingInteractionService.cs | 17 ++-- .../Formatting/FormatCommandHandler.Paste.cs | 8 +- .../Core/Formatting/FormatCommandHandler.cs | 20 +++-- .../IndentationManagerExtensions.cs | 34 +++++++ .../EditorLayerInferredIndentationService.cs | 49 ---------- .../AsyncCompletion/CommitManager.cs | 15 +++- .../AsyncCompletion/CommitManagerProvider.cs | 10 ++- .../Core/SmartIndent/SmartIndent.cs | 14 ++- .../Core/SmartIndent/SmartIndentProvider.cs | 2 +- .../AbstractAutomaticBraceCompletionTests.cs | 1 + .../AbstractDocumentationCommentTests.cs | 4 +- ...stractNewDocumentFormattingServiceTests.cs | 3 +- .../Formatting/CoreFormatterTestsBase.cs | 22 +++-- .../DocumentationCommentCommandHandler.vb | 6 +- .../VisualBasic/LineCommit/CommitFormatter.vb | 51 ++++++----- .../Indentation/SmartIndenterTests.vb | 20 +---- ...actCurlyBraceOrBracketCompletionService.cs | 6 +- .../ConvertNamespaceCodeFixProvider.cs | 4 +- ...ConvertNamespaceCodeRefactoringProvider.cs | 5 +- .../ConvertNamespaceTransform.cs | 22 +++-- ...tringToRawStringCodeRefactoringProvider.cs | 32 +++---- ...yModifiersNewDocumentFormattingProvider.cs | 2 +- .../CSharpFormattingInteractionService.cs | 35 ++------ ...eclarationNewDocumentFormattingProvider.cs | 4 +- ...nizeUsingsNewDocumentFormattingProvider.cs | 2 +- .../InterpolatedStringSplitter.cs | 5 +- .../SimpleStringSplitter.cs | 5 +- .../SplitStringLiteral/StringSplitter.cs | 18 ++-- .../Wrapping/CSharpSyntaxWrappingOptions.cs | 11 +-- ...FileBannerNewDocumentFormattingProvider.cs | 5 +- .../DocumentationCommentOptions.cs | 46 ++-------- .../AbstractNewDocumentFormattingService.cs | 4 +- .../IFormattingInteractionService.cs | 11 +-- .../INewDocumentFormattingProvider.cs | 4 +- .../INewDocumentFormattingService.cs | 2 +- .../AbstractGenerateTypeService.Editor.cs | 3 +- .../Options/AutoFormattingOptionsStorage.cs | 38 ++++++++ .../DocumentationCommentOptionsStorage.cs | 32 +++++++ .../Options/IndentationOptionsStorage.cs | 21 +++++ .../Shared/Utilities/ExtractTypeHelpers.cs | 7 +- .../Wrapping/AbstractCodeActionComputer.cs | 18 ++-- ...AbstractWrappingCodeRefactoringProvider.cs | 1 + .../BinaryExpressionCodeActionComputer.cs | 3 +- .../AbstractChainedExpressionWrapper.cs | 1 + .../ChainedExpressionCodeActionComputer.cs | 3 +- .../Core/Portable/Wrapping/ISyntaxWrapper.cs | 1 + .../SeparatedSyntaxListCodeActionComputer.cs | 5 +- .../Wrapping/SyntaxWrappingOptions.cs | 14 +-- .../Extensions/ProtocolConversions.cs | 16 ++-- .../AbstractFormatDocumentHandlerBase.cs | 16 +--- .../Formatting/FormatDocumentHandler.cs | 2 +- .../Formatting/FormatDocumentOnTypeHandler.cs | 41 +++------ .../Formatting/FormatDocumentRangeHandler.cs | 2 +- .../InlineCompletionsHandler.cs | 10 +-- .../OnAutoInsert/OnAutoInsertHandler.cs | 18 ++-- ...nizeUsingsNewDocumentFormattingProvider.vb | 2 +- .../VisualBasicSyntaxWrappingOptions.vb | 12 +-- .../Editor/FSharpEditorFormattingService.cs | 25 +++--- .../FSharpSynchronousIndentationService.cs | 11 ++- ...SharpDocumentationCommentOptionsWrapper.cs | 10 ++- ...t.CodeAnalysis.ExternalAccess.Razor.csproj | 6 +- .../Razor/RazorAutoFormattingOptions.cs | 31 +++++++ ...RazorCSharpFormattingInteractionService.cs | 89 ++++++++++--------- .../Razor/RazorGlobalOptions.cs | 35 ++++++++ .../Razor/RazorIndentationOptions.cs | 11 +++ .../Options/AdvancedOptionPageControl.xaml.cs | 2 +- .../AutomationObject.BraceCompletion.cs | 4 +- .../AutomationObject.DocumentationComment.cs | 4 +- .../AutomationObject.Formatting.cs | 8 +- .../FormattingOptionPageControl.xaml.cs | 8 +- .../Implementation/AbstractEditorFactory.cs | 5 +- .../Options/AdvancedOptionPageControl.xaml.vb | 2 +- .../AutomationObject.DocumentationComment.vb | 4 +- ...soft.CodeAnalysis.CSharp.Workspaces.csproj | 1 + .../Formatting/AutoFormattingOptions.cs | 84 ++++------------- .../Portable/Formatting/FormattingOptions.cs | 13 ++- .../Indentation/AbstractIndentationService.cs | 11 ++- .../DefaultInferredIndentationService.cs | 31 ------- .../Indentation/IIndentationService.cs | 30 ++----- .../IInferredIndentationService.cs | 20 ----- .../Indentation/IndentationOptions.cs | 20 +---- .../Portable/Options/DocumentOptionSet.cs | 2 + .../CSharpSyntaxFormattingOptions.cs | 30 +++++++ .../Formatting/ISyntaxFormattingService.cs | 17 +++- .../VisualBasicSyntaxFormattingOptions.vb | 11 +++ 99 files changed, 803 insertions(+), 722 deletions(-) create mode 100644 src/EditorFeatures/Core/Formatting/IndentationManagerExtensions.cs delete mode 100644 src/EditorFeatures/Core/Indentation/EditorLayerInferredIndentationService.cs create mode 100644 src/Features/Core/Portable/Options/AutoFormattingOptionsStorage.cs create mode 100644 src/Features/Core/Portable/Options/DocumentationCommentOptionsStorage.cs create mode 100644 src/Features/Core/Portable/Options/IndentationOptionsStorage.cs create mode 100644 src/Tools/ExternalAccess/Razor/RazorAutoFormattingOptions.cs create mode 100644 src/Tools/ExternalAccess/Razor/RazorGlobalOptions.cs create mode 100644 src/Tools/ExternalAccess/Razor/RazorIndentationOptions.cs delete mode 100644 src/Workspaces/Core/Portable/Indentation/DefaultInferredIndentationService.cs delete mode 100644 src/Workspaces/Core/Portable/Indentation/IInferredIndentationService.cs diff --git a/src/EditorFeatures/CSharp/ConvertNamespace/ConvertNamespaceCommandHandler.cs b/src/EditorFeatures/CSharp/ConvertNamespace/ConvertNamespaceCommandHandler.cs index 4b49ddcb28b6d..b775977ceb8b0 100644 --- a/src/EditorFeatures/CSharp/ConvertNamespace/ConvertNamespaceCommandHandler.cs +++ b/src/EditorFeatures/CSharp/ConvertNamespace/ConvertNamespaceCommandHandler.cs @@ -151,7 +151,8 @@ public void ExecuteCommand(TypeCharCommandArgs args, Action nextCommandHandler, if (!ConvertNamespaceAnalysis.CanOfferUseFileScoped(s_optionSet, root, namespaceDecl, forAnalyzer: true, LanguageVersion.CSharp10)) return default; - var (converted, semicolonSpan) = ConvertNamespaceTransform.ConvertNamespaceDeclarationAsync(document, namespaceDecl, cancellationToken).WaitAndGetResult(cancellationToken); + var formattingOptions = SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).WaitAndGetResult(cancellationToken); + var (converted, semicolonSpan) = ConvertNamespaceTransform.ConvertNamespaceDeclarationAsync(document, namespaceDecl, formattingOptions, cancellationToken).WaitAndGetResult(cancellationToken); var text = converted.GetTextSynchronously(cancellationToken); return (text, semicolonSpan); } diff --git a/src/EditorFeatures/CSharp/DocumentationComments/DocumentationCommentCommandHandler.cs b/src/EditorFeatures/CSharp/DocumentationComments/DocumentationCommentCommandHandler.cs index ab1bd395c9d52..de54c6c2ef306 100644 --- a/src/EditorFeatures/CSharp/DocumentationComments/DocumentationCommentCommandHandler.cs +++ b/src/EditorFeatures/CSharp/DocumentationComments/DocumentationCommentCommandHandler.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.DocumentationComments; using Microsoft.CodeAnalysis.Editor.Host; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Options; using Microsoft.VisualStudio.Commanding; using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion; using Microsoft.VisualStudio.Text.Operations; @@ -20,7 +21,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.DocumentationComments [Name(PredefinedCommandHandlerNames.DocumentationComments)] [Order(After = PredefinedCommandHandlerNames.Rename)] [Order(After = PredefinedCompletionNames.CompletionCommandHandler)] - internal class DocumentationCommentCommandHandler + internal sealed class DocumentationCommentCommandHandler : AbstractDocumentationCommentCommandHandler { [ImportingConstructor] @@ -28,8 +29,9 @@ internal class DocumentationCommentCommandHandler public DocumentationCommentCommandHandler( IUIThreadOperationExecutor uiThreadOperationExecutor, ITextUndoHistoryRegistry undoHistoryRegistry, - IEditorOperationsFactoryService editorOperationsFactoryService) - : base(uiThreadOperationExecutor, undoHistoryRegistry, editorOperationsFactoryService) + IEditorOperationsFactoryService editorOperationsFactoryService, + IGlobalOptionService globalOptions) + : base(uiThreadOperationExecutor, undoHistoryRegistry, editorOperationsFactoryService, globalOptions) { } diff --git a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs index 7429ddfdb13e4..c3d80c50fff74 100644 --- a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs +++ b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_Return.cs @@ -97,9 +97,10 @@ SyntaxKind.InterpolatedSingleLineRawStringStartToken or return false; } - var indentation = token.GetPreferredIndentation(document, cancellationToken); + var indentationOptions = _globalOptions.GetIndentationOptionsAsync(document, cancellationToken).WaitAndGetResult(cancellationToken); + var indentation = token.GetPreferredIndentation(document, indentationOptions, cancellationToken); - var newLine = document.Project.Solution.Options.GetOption(FormattingOptions.NewLine, LanguageNames.CSharp); + var newLine = indentationOptions.FormattingOptions.NewLine; using var transaction = CaretPreservingEditTransaction.TryCreate( CSharpEditorResources.Split_string, textView, _undoHistoryRegistry, _editorOperationsFactoryService); diff --git a/src/EditorFeatures/CSharp/SplitStringLiteral/SplitStringLiteralCommandHandler.cs b/src/EditorFeatures/CSharp/SplitStringLiteral/SplitStringLiteralCommandHandler.cs index 38d70b2805397..105b872f0a785 100644 --- a/src/EditorFeatures/CSharp/SplitStringLiteral/SplitStringLiteralCommandHandler.cs +++ b/src/EditorFeatures/CSharp/SplitStringLiteral/SplitStringLiteralCommandHandler.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Commanding; @@ -120,12 +121,12 @@ private bool SplitString(ITextView textView, ITextBuffer subjectBuffer, int posi } // TODO: read option from textView.Options (https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1412138) - var indentStyle = document.Project.Solution.Options.GetOption(FormattingOptions.SmartIndent, LanguageNames.CSharp); + var options = _globalOptions.GetIndentationOptionsAsync(document, cancellationToken).WaitAndGetResult(cancellationToken); using var transaction = CaretPreservingEditTransaction.TryCreate( CSharpEditorResources.Split_string, textView, _undoHistoryRegistry, _editorOperationsFactoryService); - var splitter = StringSplitter.TryCreate(document, position, useTabs, tabSize, indentStyle, cancellationToken); + var splitter = StringSplitter.TryCreate(document, position, options, useTabs, tabSize, cancellationToken); if (splitter?.TrySplit(out var newDocument, out var newPosition) != true) { return false; diff --git a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticBraceCompletionTests.cs b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticBraceCompletionTests.cs index d9a223fec282c..96153086229fa 100644 --- a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticBraceCompletionTests.cs +++ b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticBraceCompletionTests.cs @@ -1117,14 +1117,12 @@ public void X() } }"; - var optionSet = new Dictionary - { - { new OptionKey2(AutoFormattingOptions.Metadata.AutoFormattingOnCloseBrace, LanguageNames.CSharp), false }, - { new OptionKey2(AutoFormattingOptions.Metadata.SmartIndent, LanguageNames.CSharp), FormattingOptions.IndentStyle.Block } - }; - using var session = CreateSession(code, optionSet); + using var session = CreateSession(code); Assert.NotNull(session); + session.Workspace.GlobalOptions.SetGlobalOption(new OptionKey(AutoFormattingOptionsStorage.FormatOnCloseBrace, LanguageNames.CSharp), false); + session.Workspace.GlobalOptions.SetGlobalOption(new OptionKey(FormattingOptions.SmartIndent, LanguageNames.CSharp), FormattingOptions.IndentStyle.Block); + CheckStart(session.Session); Assert.Equal(expected, session.Session.SubjectBuffer.CurrentSnapshot.GetText()); @@ -1147,13 +1145,11 @@ public class C1 { } }"; - var optionSet = new Dictionary - { - { new OptionKey2(AutoFormattingOptions.Metadata.SmartIndent, LanguageNames.CSharp), FormattingOptions.IndentStyle.None } - }; - using var session = CreateSession(code, optionSet); + using var session = CreateSession(code); Assert.NotNull(session); + session.Workspace.GlobalOptions.SetGlobalOption(new OptionKey(FormattingOptions.SmartIndent, LanguageNames.CSharp), FormattingOptions.IndentStyle.None); + CheckStart(session.Session); Assert.Equal(expected, session.Session.SubjectBuffer.CurrentSnapshot.GetText()); } @@ -1182,13 +1178,11 @@ public class C1 } }"; - var optionSet = new Dictionary - { - { new OptionKey2(AutoFormattingOptions.Metadata.SmartIndent, LanguageNames.CSharp), FormattingOptions.IndentStyle.Block } - }; - using var session = CreateSession(code, optionSet); + using var session = CreateSession(code); Assert.NotNull(session); + session.Workspace.GlobalOptions.SetGlobalOption(new OptionKey(FormattingOptions.SmartIndent, LanguageNames.CSharp), FormattingOptions.IndentStyle.Block); + CheckStart(session.Session); Assert.Equal(expected, session.Session.SubjectBuffer.CurrentSnapshot.GetText()); @@ -1225,13 +1219,11 @@ public class C1 } }"; - var optionSet = new Dictionary - { - { new OptionKey2(AutoFormattingOptions.Metadata.SmartIndent, LanguageNames.CSharp), FormattingOptions.IndentStyle.Block } - }; - using var session = CreateSession(code, optionSet); + using var session = CreateSession(code); Assert.NotNull(session); + session.Workspace.GlobalOptions.SetGlobalOption(new OptionKey(FormattingOptions.SmartIndent, LanguageNames.CSharp), FormattingOptions.IndentStyle.Block); + CheckStart(session.Session); Assert.Equal(expected, session.Session.SubjectBuffer.CurrentSnapshot.GetText()); diff --git a/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs b/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs index 65149d894dba2..95a373678ce01 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/FormattingEngineTests.cs @@ -34,9 +34,9 @@ private static Dictionary SmartIndentButDoNotFormatWhileTypi { return new Dictionary { - { new OptionKey2(AutoFormattingOptions.Metadata.SmartIndent, LanguageNames.CSharp), FormattingOptions.IndentStyle.Smart }, - { new OptionKey2(AutoFormattingOptions.Metadata.AutoFormattingOnTyping, LanguageNames.CSharp), false }, - { new OptionKey2(AutoFormattingOptions.Metadata.AutoFormattingOnCloseBrace, LanguageNames.CSharp), false }, + { new OptionKey2(AutoFormattingOptionsStorage.SmartIndent, LanguageNames.CSharp), FormattingOptions.IndentStyle.Smart }, + { new OptionKey2(AutoFormattingOptionsStorage.FormatOnTyping, LanguageNames.CSharp), false }, + { new OptionKey2(AutoFormattingOptionsStorage.FormatOnCloseBrace, LanguageNames.CSharp), false }, }; } @@ -1070,11 +1070,11 @@ class C1 class C1 { }"; - var optionSet = new Dictionary - { - { new OptionKey2(AutoFormattingOptions.Metadata.SmartIndent, LanguageNames.CSharp), FormattingOptions.IndentStyle.None } - }; - AssertFormatAfterTypeChar(code, expected, optionSet); + var globalOptions = new Dictionary + { + { new OptionKey2(AutoFormattingOptionsStorage.SmartIndent, LanguageNames.CSharp), FormattingOptions.IndentStyle.None } + }; + AssertFormatAfterTypeChar(code, expected, globalOptions); } [WpfFact] @@ -1101,12 +1101,12 @@ class C } "; - var optionSet = new Dictionary + var globalOptions = new Dictionary { - { new OptionKey2(AutoFormattingOptions.Metadata.AutoFormattingOnCloseBrace, LanguageNames.CSharp), false } + { new OptionKey2(AutoFormattingOptionsStorage.FormatOnCloseBrace, LanguageNames.CSharp), false } }; - AssertFormatAfterTypeChar(code, expected, optionSet); + AssertFormatAfterTypeChar(code, expected, globalOptions); } [WpfFact] @@ -1133,12 +1133,12 @@ class C } "; - var optionSet = new Dictionary + var globalOptions = new Dictionary { - { new OptionKey2(AutoFormattingOptions.Metadata.AutoFormattingOnTyping, LanguageNames.CSharp), false } + { new OptionKey2(AutoFormattingOptionsStorage.FormatOnTyping, LanguageNames.CSharp), false } }; - AssertFormatAfterTypeChar(code, expected, optionSet); + AssertFormatAfterTypeChar(code, expected, globalOptions); } [WorkItem(5873, "https://github.com/dotnet/roslyn/issues/5873")] @@ -1165,12 +1165,12 @@ static void Main() } }"; - var optionSet = new Dictionary + var globalOptions = new Dictionary { - { new OptionKey2(AutoFormattingOptions.Metadata.AutoFormattingOnTyping, LanguageNames.CSharp), false } + { new OptionKey2(AutoFormattingOptionsStorage.FormatOnTyping, LanguageNames.CSharp), false } }; - AssertFormatAfterTypeChar(code, expected, optionSet); + AssertFormatAfterTypeChar(code, expected, globalOptions); } [WorkItem(5873, "https://github.com/dotnet/roslyn/issues/5873")] @@ -1223,12 +1223,12 @@ class C } "; - var optionSet = new Dictionary + var globalOptions = new Dictionary { - { new OptionKey2(AutoFormattingOptions.Metadata.AutoFormattingOnSemicolon, LanguageNames.CSharp), false } + { new OptionKey2(AutoFormattingOptionsStorage.FormatOnSemicolon, LanguageNames.CSharp), false } }; - AssertFormatAfterTypeChar(code, expected, optionSet); + AssertFormatAfterTypeChar(code, expected, globalOptions); } [WpfFact] @@ -1255,12 +1255,12 @@ class C } "; - var optionSet = new Dictionary + var globalOptions = new Dictionary { - { new OptionKey2(AutoFormattingOptions.Metadata.AutoFormattingOnTyping, LanguageNames.CSharp), false } + { new OptionKey2(AutoFormattingOptionsStorage.FormatOnTyping, LanguageNames.CSharp), false } }; - AssertFormatAfterTypeChar(code, expected, optionSet); + AssertFormatAfterTypeChar(code, expected, globalOptions); } [WpfFact, WorkItem(4435, "https://github.com/dotnet/roslyn/issues/4435")] @@ -2289,18 +2289,16 @@ class Test Assert.Single(annotatedTrivia); } - private static void AssertFormatAfterTypeChar(string code, string expected, Dictionary changedOptionSet = null) + private static void AssertFormatAfterTypeChar(string code, string expected, Dictionary globalOptions = null) { using var workspace = TestWorkspace.CreateCSharp(code); - if (changedOptionSet != null) + if (globalOptions != null) { - var options = workspace.Options; - foreach (var entry in changedOptionSet) + var options = workspace.GlobalOptions; + foreach (var entry in globalOptions) { - options = options.WithChangedOption(entry.Key, entry.Value); + options.SetGlobalOption((OptionKey)entry.Key, entry.Value); } - - workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(options)); } var subjectDocument = workspace.Documents.Single(); diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs index ab19697d14108..c732f951c2094 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/CSharpFormatterTestsBase.cs @@ -82,7 +82,10 @@ private static async Task TokenFormatWorkerAsync(TestWorkspace workspace, ITextB var rules = formattingRuleProvider.CreateRule(document, position).Concat(Formatter.GetDefaultFormattingRules(document)); - var options = await IndentationOptions.FromDocumentAsync(document, CancellationToken.None); + var options = new IndentationOptions( + await SyntaxFormattingOptions.FromDocumentAsync(document, CancellationToken.None).ConfigureAwait(false), + AutoFormattingOptions.Default); + 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/SmartIndenterEnterOnTokenTests.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs index f8d087ef82fef..27fcbecb9d42e 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterEnterOnTokenTests.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.Formatting; using Microsoft.CodeAnalysis.CSharp.Indentation; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; @@ -1501,21 +1502,17 @@ private static async Task AssertIndentUsingSmartTokenFormatterAsync( // create tree service using var workspace = TestWorkspace.CreateCSharp(code); - workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options - .WithChangedOption(UseTabs, LanguageNames.CSharp, useTabs))); - var hostdoc = workspace.Documents.First(); - var buffer = hostdoc.GetTextBuffer(); - var snapshot = buffer.CurrentSnapshot; var line = snapshot.GetLineFromLineNumber(indentationLine); - var document = workspace.CurrentSolution.GetDocument(hostdoc.Id); var root = (await document.GetSyntaxRootAsync()) as CompilationUnitSyntax; - var options = await IndentationOptions.FromDocumentAsync(document, CancellationToken.None); + var options = new IndentationOptions( + CSharpSyntaxFormattingOptions.Default.With(useTabs: useTabs, tabSize: 4, indentationSize: 4), + AutoFormattingOptions.Default); Assert.True( CSharpIndentationService.ShouldUseSmartTokenFormatterInsteadOfIndenter( @@ -1526,7 +1523,7 @@ private static async Task AssertIndentUsingSmartTokenFormatterAsync( Assert.Equal(expectedIndentation.Value, actualIndentation); } - private static async Task AssertIndentNotUsingSmartTokenFormatterButUsingIndenterAsync( + private async Task AssertIndentNotUsingSmartTokenFormatterButUsingIndenterAsync( string code, int indentationLine, int? expectedIndentation, @@ -1536,7 +1533,7 @@ private static async Task AssertIndentNotUsingSmartTokenFormatterButUsingIndente await AssertIndentNotUsingSmartTokenFormatterButUsingIndenterAsync(code.Replace(" ", "\t"), indentationLine, expectedIndentation, useTabs: true, indentStyle).ConfigureAwait(false); } - private static async Task AssertIndentNotUsingSmartTokenFormatterButUsingIndenterAsync( + private async Task AssertIndentNotUsingSmartTokenFormatterButUsingIndenterAsync( string code, int indentationLine, int? expectedIndentation, @@ -1545,9 +1542,7 @@ private static async Task AssertIndentNotUsingSmartTokenFormatterButUsingIndente { // create tree service using var workspace = TestWorkspace.CreateCSharp(code); - workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options - .WithChangedOption(AutoFormattingOptions.Metadata.SmartIndent, LanguageNames.CSharp, indentStyle) - .WithChangedOption(UseTabs, LanguageNames.CSharp, useTabs))); + var hostdoc = workspace.Documents.First(); var buffer = hostdoc.GetTextBuffer(); var snapshot = buffer.CurrentSnapshot; @@ -1558,14 +1553,16 @@ private static async Task AssertIndentNotUsingSmartTokenFormatterButUsingIndente var root = (await document.GetSyntaxRootAsync()) as CompilationUnitSyntax; - var options = await IndentationOptions.FromDocumentAsync(document, CancellationToken.None); + var options = new IndentationOptions( + CSharpSyntaxFormattingOptions.Default.With(useTabs: useTabs, tabSize: 4, indentationSize: 4), + new AutoFormattingOptions(IndentStyle: indentStyle)); Assert.False( CSharpIndentationService.ShouldUseSmartTokenFormatterInsteadOfIndenter( Formatter.GetDefaultFormattingRules(document), root, line.AsTextLine(), options, out _)); - TestIndentation(workspace, indentationLine, expectedIndentation); + TestIndentation(workspace, indentationLine, expectedIndentation, indentStyle, useTabs); } } } diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs index eaac1999653f6..79a0a56823f20 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartIndenterTests.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Formatting.Rules; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.VisualStudio.Text; using Roslyn.Test.Utilities; @@ -3013,8 +3014,10 @@ private static void AssertSmartIndentInProjection( using var workspace = TestWorkspace.CreateCSharp(markup, parseOptions: option, composition: s_compositionWithTestFormattingRules); workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options - .WithChangedOption(AutoFormattingOptions.Metadata.SmartIndent, LanguageNames.CSharp, indentStyle) .WithChangedOption(UseTabs, LanguageNames.CSharp, useTabs))); + + workspace.GlobalOptions.SetGlobalOption(new OptionKey(AutoFormattingOptionsStorage.SmartIndent, LanguageNames.CSharp), indentStyle); + var subjectDocument = workspace.Documents.Single(); var projectedDocument = @@ -3029,11 +3032,11 @@ private static void AssertSmartIndentInProjection( TestIndentation( point.Value, expectedIndentation, - projectedDocument.GetTextView(), subjectDocument); + projectedDocument.GetTextView(), subjectDocument, workspace.GlobalOptions); } } - private static void AssertSmartIndent( + private void AssertSmartIndent( string code, int indentationLine, int? expectedIndentation, @@ -3044,7 +3047,7 @@ private static void AssertSmartIndent( AssertSmartIndent(code.Replace(" ", "\t"), indentationLine, expectedIndentation, useTabs: true, options, indentStyle); } - private static void AssertSmartIndent( + private void AssertSmartIndent( string code, int indentationLine, int? expectedIndentation, @@ -3060,14 +3063,11 @@ private static void AssertSmartIndent( { using var workspace = TestWorkspace.CreateCSharp(code, parseOptions: option); - workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options - .WithChangedOption(AutoFormattingOptions.Metadata.SmartIndent, LanguageNames.CSharp, indentStyle) - .WithChangedOption(UseTabs, LanguageNames.CSharp, useTabs))); - TestIndentation(workspace, indentationLine, expectedIndentation); + TestIndentation(workspace, indentationLine, expectedIndentation, indentStyle, useTabs); } } - private static void AssertSmartIndent( + private void AssertSmartIndent( string code, int? expectedIndentation, CSharpParseOptions options = null, @@ -3077,7 +3077,7 @@ private static void AssertSmartIndent( AssertSmartIndent(code.Replace(" ", "\t"), expectedIndentation, useTabs: true, options, indentStyle); } - private static void AssertSmartIndent( + private void AssertSmartIndent( string code, int? expectedIndentation, bool useTabs, @@ -3092,12 +3092,9 @@ private static void AssertSmartIndent( { using var workspace = TestWorkspace.CreateCSharp(code, parseOptions: option); - workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options - .WithChangedOption(AutoFormattingOptions.Metadata.SmartIndent, LanguageNames.CSharp, indentStyle) - .WithChangedOption(UseTabs, LanguageNames.CSharp, useTabs))); var wpfTextView = workspace.Documents.First().GetTextView(); var line = wpfTextView.TextBuffer.CurrentSnapshot.GetLineFromPosition(wpfTextView.Caret.Position.BufferPosition).LineNumber; - TestIndentation(workspace, line, expectedIndentation); + TestIndentation(workspace, line, expectedIndentation, indentStyle, useTabs); } } } diff --git a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartTokenFormatterFormatRangeTests.cs b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartTokenFormatterFormatRangeTests.cs index 3f5ef74ec4eaf..19104a2b4ef3a 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartTokenFormatterFormatRangeTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/Indentation/SmartTokenFormatterFormatRangeTests.cs @@ -10,6 +10,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Formatting; using Microsoft.CodeAnalysis.CSharp.Indentation; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Utilities; @@ -3570,9 +3571,6 @@ private static async Task AutoFormatOnMarkerAsync(string initialMarkup, string e { using var workspace = TestWorkspace.CreateCSharp(initialMarkup); - 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; @@ -3588,7 +3586,11 @@ private static async Task AutoFormatOnMarkerAsync(string initialMarkup, string e } Assert.Equal(tokenKind, endToken.Kind()); - var options = await IndentationOptions.FromDocumentAsync(document, CancellationToken.None); + + var options = new IndentationOptions( + CSharpSyntaxFormattingOptions.Default.With(useTabs: useTabs, tabSize: 4, indentationSize: 4), + AutoFormattingOptions.Default); + var formatter = new CSharpSmartTokenFormatter(options, rules, root); var tokenRange = FormattingRangeHelper.FindAppropriateRange(endToken); diff --git a/src/EditorFeatures/CSharpTest/SplitStringLiteral/SplitStringLiteralCommandHandlerTests.cs b/src/EditorFeatures/CSharpTest/SplitStringLiteral/SplitStringLiteralCommandHandlerTests.cs index c44723ac7b064..5d5f47ef8dc61 100644 --- a/src/EditorFeatures/CSharpTest/SplitStringLiteral/SplitStringLiteralCommandHandlerTests.cs +++ b/src/EditorFeatures/CSharpTest/SplitStringLiteral/SplitStringLiteralCommandHandlerTests.cs @@ -14,6 +14,7 @@ using Microsoft.CodeAnalysis.Editor.UnitTests.Utilities; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; @@ -48,8 +49,7 @@ private static void TestWorker( using var workspace = TestWorkspace.CreateCSharp(inputMarkup); // TODO: set SmartIndent to textView.Options (https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1412138) - workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options - .WithChangedOption(AutoFormattingOptions.Metadata.SmartIndent, LanguageNames.CSharp, indentStyle))); + workspace.GlobalOptions.SetGlobalOption(new OptionKey(AutoFormattingOptionsStorage.SmartIndent, LanguageNames.CSharp), indentStyle); if (useTabs && expectedOutputMarkup != null) { diff --git a/src/EditorFeatures/Core/AutomaticCompletion/BraceCompletionSessionProvider.BraceCompletionSession.cs b/src/EditorFeatures/Core/AutomaticCompletion/BraceCompletionSessionProvider.BraceCompletionSession.cs index 62dcf63715945..31a906bee5b3b 100644 --- a/src/EditorFeatures/Core/AutomaticCompletion/BraceCompletionSessionProvider.BraceCompletionSession.cs +++ b/src/EditorFeatures/Core/AutomaticCompletion/BraceCompletionSessionProvider.BraceCompletionSession.cs @@ -12,7 +12,9 @@ using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Indentation; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; using Microsoft.VisualStudio.Text; @@ -33,8 +35,6 @@ internal partial class BraceCompletionSessionProvider // modified it little bit so that we can use it as base class. private class BraceCompletionSession : ForegroundThreadAffinitizedObject, IBraceCompletionSession { - #region Private Members - public char OpeningBrace { get; } public char ClosingBrace { get; } public ITrackingPoint OpeningPoint { get; private set; } @@ -45,30 +45,26 @@ private class BraceCompletionSession : ForegroundThreadAffinitizedObject, IBrace private readonly ITextUndoHistory _undoHistory; private readonly IEditorOperations _editorOperations; private readonly IBraceCompletionService _service; - - #endregion - - #region Constructors + private readonly IGlobalOptionService _globalOptions; public BraceCompletionSession( ITextView textView, ITextBuffer subjectBuffer, SnapshotPoint openingPoint, char openingBrace, char closingBrace, ITextUndoHistory undoHistory, IEditorOperationsFactoryService editorOperationsFactoryService, IBraceCompletionService service, - IThreadingContext threadingContext) + IGlobalOptionService globalOptions, IThreadingContext threadingContext) : base(threadingContext, assertIsForeground: true) { - this.TextView = textView; - this.SubjectBuffer = subjectBuffer; - this.OpeningBrace = openingBrace; - this.ClosingBrace = closingBrace; - this.ClosingPoint = SubjectBuffer.CurrentSnapshot.CreateTrackingPoint(openingPoint.Position, PointTrackingMode.Positive); + TextView = textView; + SubjectBuffer = subjectBuffer; + OpeningBrace = openingBrace; + ClosingBrace = closingBrace; + ClosingPoint = SubjectBuffer.CurrentSnapshot.CreateTrackingPoint(openingPoint.Position, PointTrackingMode.Positive); _undoHistory = undoHistory; _editorOperations = editorOperationsFactoryService.GetEditorOperations(textView); _service = service; + _globalOptions = globalOptions; } - #endregion - #region IBraceCompletionSession Methods public void Start() @@ -127,8 +123,10 @@ private bool TryStart(CancellationToken cancellationToken) var contextAfterStart = GetBraceCompletionContext(); if (contextAfterStart != null) { - var options = IndentationOptions.FromDocumentAsync(contextAfterStart.Value.Document, cancellationToken).WaitAndGetResult(cancellationToken); - var changesAfterStart = _service.GetTextChangesAfterCompletionAsync(contextAfterStart.Value, options, cancellationToken).WaitAndGetResult(cancellationToken); + var document = contextAfterStart.Value.Document; + var indentationOptions = _globalOptions.GetIndentationOptionsAsync(document, cancellationToken).WaitAndGetResult(cancellationToken); + + var changesAfterStart = _service.GetTextChangesAfterCompletionAsync(contextAfterStart.Value, indentationOptions, cancellationToken).WaitAndGetResult(cancellationToken); if (changesAfterStart != null) { ApplyBraceCompletionResult(changesAfterStart.Value); @@ -286,8 +284,8 @@ public void PostReturn() return; } - var options = IndentationOptions.FromDocumentAsync(context.Value.Document, CancellationToken.None).WaitAndGetResult(CancellationToken.None); - var changesAfterReturn = _service.GetTextChangeAfterReturnAsync(context.Value, options, CancellationToken.None).WaitAndGetResult(CancellationToken.None); + var indentationOptions = _globalOptions.GetIndentationOptionsAsync(context.Value.Document, CancellationToken.None).WaitAndGetResult(CancellationToken.None); + var changesAfterReturn = _service.GetTextChangeAfterReturnAsync(context.Value, indentationOptions, CancellationToken.None).WaitAndGetResult(CancellationToken.None); if (changesAfterReturn != null) { using var caretPreservingTransaction = new CaretPreservingEditTransaction(EditorFeaturesResources.Brace_Completion, _undoHistory, _editorOperations); diff --git a/src/EditorFeatures/Core/AutomaticCompletion/BraceCompletionSessionProvider.cs b/src/EditorFeatures/Core/AutomaticCompletion/BraceCompletionSessionProvider.cs index 865562f5963e5..e89f809f20c3b 100644 --- a/src/EditorFeatures/Core/AutomaticCompletion/BraceCompletionSessionProvider.cs +++ b/src/EditorFeatures/Core/AutomaticCompletion/BraceCompletionSessionProvider.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text; @@ -34,17 +35,20 @@ internal partial class BraceCompletionSessionProvider : ForegroundThreadAffiniti { private readonly ITextBufferUndoManagerProvider _undoManager; private readonly IEditorOperationsFactoryService _editorOperationsFactoryService; + private readonly IGlobalOptionService _globalOptions; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public BraceCompletionSessionProvider( IThreadingContext threadingContext, ITextBufferUndoManagerProvider undoManager, - IEditorOperationsFactoryService editorOperationsFactoryService) + IEditorOperationsFactoryService editorOperationsFactoryService, + IGlobalOptionService globalOptions) : base(threadingContext) { _undoManager = undoManager; _editorOperationsFactoryService = editorOperationsFactoryService; + _globalOptions = globalOptions; } public bool TryCreateSession(ITextView textView, SnapshotPoint openingPoint, char openingBrace, char closingBrace, out IBraceCompletionSession session) @@ -67,7 +71,7 @@ public bool TryCreateSession(ITextView textView, SnapshotPoint openingPoint, cha session = new BraceCompletionSession( textView, openingPoint.Snapshot.TextBuffer, openingPoint, openingBrace, closingBrace, undoHistory, _editorOperationsFactoryService, - editorSession, ThreadingContext); + editorSession, _globalOptions, ThreadingContext); return true; } } diff --git a/src/EditorFeatures/Core/DocumentationComments/AbstractDocumentationCommentCommandHandler.cs b/src/EditorFeatures/Core/DocumentationComments/AbstractDocumentationCommentCommandHandler.cs index feeaa42df39e2..968f3e3a85fd2 100644 --- a/src/EditorFeatures/Core/DocumentationComments/AbstractDocumentationCommentCommandHandler.cs +++ b/src/EditorFeatures/Core/DocumentationComments/AbstractDocumentationCommentCommandHandler.cs @@ -30,11 +30,13 @@ internal abstract class AbstractDocumentationCommentCommandHandler : private readonly IUIThreadOperationExecutor _uiThreadOperationExecutor; private readonly ITextUndoHistoryRegistry _undoHistoryRegistry; private readonly IEditorOperationsFactoryService _editorOperationsFactoryService; + private readonly IGlobalOptionService _globalOptions; protected AbstractDocumentationCommentCommandHandler( IUIThreadOperationExecutor uiThreadOperationExecutor, ITextUndoHistoryRegistry undoHistoryRegistry, - IEditorOperationsFactoryService editorOperationsFactoryService) + IEditorOperationsFactoryService editorOperationsFactoryService, + IGlobalOptionService globalOptions) { Contract.ThrowIfNull(uiThreadOperationExecutor); Contract.ThrowIfNull(undoHistoryRegistry); @@ -43,6 +45,7 @@ protected AbstractDocumentationCommentCommandHandler( _uiThreadOperationExecutor = uiThreadOperationExecutor; _undoHistoryRegistry = undoHistoryRegistry; _editorOperationsFactoryService = editorOperationsFactoryService; + _globalOptions = globalOptions; } protected abstract string ExteriorTriviaText { get; } @@ -70,7 +73,7 @@ private static void ApplySnippet(DocumentationCommentSnippet snippet, ITextBuffe textView.TryMoveCaretToAndEnsureVisible(subjectBuffer.CurrentSnapshot.GetPoint(replaceSpan.Start + snippet.CaretOffset)); } - private static bool CompleteComment( + private bool CompleteComment( ITextBuffer subjectBuffer, ITextView textView, Func getSnippetAction, @@ -92,7 +95,7 @@ private static bool CompleteComment( var syntaxTree = document.GetRequiredSyntaxTreeSynchronously(cancellationToken); var text = syntaxTree.GetText(cancellationToken); var documentOptions = document.GetOptionsAsync(cancellationToken).WaitAndGetResult(cancellationToken); - var options = DocumentationCommentOptions.From(documentOptions); + var options = _globalOptions.GetDocumentationCommentOptions(documentOptions); // Apply snippet in reverse order so that the first applied snippet doesn't affect span of next snippets. var snapshots = textView.Selection.GetSnapshotSpansOnBuffer(subjectBuffer).OrderByDescending(s => s.Span.Start); @@ -335,7 +338,7 @@ private void InsertExteriorTriviaIfNeeded(IDocumentationCommentSnippetService se } var documentOptions = document.GetOptionsAsync(CancellationToken.None).WaitAndGetResult(CancellationToken.None); - var options = DocumentationCommentOptions.From(documentOptions); + var options = _globalOptions.GetDocumentationCommentOptions(documentOptions); var snippet = service.GetDocumentationCommentSnippetFromPreviousLine(options, currentLine, previousLine); if (snippet != null) diff --git a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptFormattingInteractionService.cs b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptFormattingInteractionService.cs index 6326622d89390..b4c044bfa1918 100644 --- a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptFormattingInteractionService.cs +++ b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptFormattingInteractionService.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.Indentation; namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript { @@ -34,16 +35,16 @@ public VSTypeScriptFormattingInteractionService(IVSTypeScriptFormattingInteracti public bool SupportsFormattingOnTypedCharacter(Document document, AutoFormattingOptions options, char ch) => _implementation.SupportsFormattingOnTypedCharacter(document, ch); - public Task> GetFormattingChangesAsync(Document document, TextSpan? textSpan, DocumentOptionSet? documentOptions, CancellationToken cancellationToken) - => _implementation.GetFormattingChangesAsync(document, textSpan, documentOptions, cancellationToken); + public Task> GetFormattingChangesAsync(Document document, TextSpan? textSpan, SyntaxFormattingOptions options, CancellationToken cancellationToken) + => _implementation.GetFormattingChangesAsync(document, textSpan, documentOptions: null, cancellationToken); - public Task> GetFormattingChangesOnPasteAsync(Document document, TextSpan textSpan, DocumentOptionSet? documentOptions, CancellationToken cancellationToken) - => _implementation.GetFormattingChangesOnPasteAsync(document, textSpan, documentOptions, cancellationToken); + public Task> GetFormattingChangesOnPasteAsync(Document document, TextSpan textSpan, SyntaxFormattingOptions options, CancellationToken cancellationToken) + => _implementation.GetFormattingChangesOnPasteAsync(document, textSpan, documentOptions: null, cancellationToken); - public Task> GetFormattingChangesAsync(Document document, char typedChar, int position, DocumentOptionSet? documentOptions, CancellationToken cancellationToken) - => _implementation.GetFormattingChangesAsync(document, typedChar, position, documentOptions, cancellationToken); + public Task> GetFormattingChangesAsync(Document document, char typedChar, int position, IndentationOptions options, CancellationToken cancellationToken) + => _implementation.GetFormattingChangesAsync(document, typedChar, position, documentOptions: null, cancellationToken); - public Task> GetFormattingChangesOnReturnAsync(Document document, int position, DocumentOptionSet? documentOptions, CancellationToken cancellationToken) - => _implementation.GetFormattingChangesOnReturnAsync(document, position, documentOptions, cancellationToken); + public Task> GetFormattingChangesOnReturnAsync(Document document, int position, CancellationToken cancellationToken) + => _implementation.GetFormattingChangesOnReturnAsync(document, position, documentOptions: null, cancellationToken); } } diff --git a/src/EditorFeatures/Core/Formatting/FormatCommandHandler.Paste.cs b/src/EditorFeatures/Core/Formatting/FormatCommandHandler.Paste.cs index f64c7034ec5b5..3bb866cb6e7bb 100644 --- a/src/EditorFeatures/Core/Formatting/FormatCommandHandler.Paste.cs +++ b/src/EditorFeatures/Core/Formatting/FormatCommandHandler.Paste.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Options; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Formatting.Rules; +using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Commanding; @@ -71,7 +72,8 @@ private void ExecuteCommandWorker(PasteCommandArgs args, SnapshotPoint? caretPos return; } - var formattingRuleService = solution.Workspace.Services.GetService(); + var services = solution.Workspace.Services; + var formattingRuleService = services.GetService(); if (formattingRuleService != null && formattingRuleService.ShouldNotFormatOrCommitOnPaste(document)) { return; @@ -85,8 +87,10 @@ private void ExecuteCommandWorker(PasteCommandArgs args, SnapshotPoint? caretPos var trackingSpan = caretPosition.Value.Snapshot.CreateTrackingSpan(caretPosition.Value.Position, 0, SpanTrackingMode.EdgeInclusive); var span = trackingSpan.GetSpan(args.SubjectBuffer.CurrentSnapshot).Span.ToTextSpan(); + var formattingOptions = _indentationManager.GetInferredFormattingOptionsAsync(document, explicitFormat: false, cancellationToken).WaitAndGetResult(cancellationToken); + var changes = formattingService.GetFormattingChangesOnPasteAsync( - document, span, documentOptions: null, cancellationToken).WaitAndGetResult(cancellationToken); + document, span, formattingOptions, cancellationToken).WaitAndGetResult(cancellationToken); if (changes.IsEmpty) { return; diff --git a/src/EditorFeatures/Core/Formatting/FormatCommandHandler.cs b/src/EditorFeatures/Core/Formatting/FormatCommandHandler.cs index 5ea96bd45e286..40ce24fdba0f1 100644 --- a/src/EditorFeatures/Core/Formatting/FormatCommandHandler.cs +++ b/src/EditorFeatures/Core/Formatting/FormatCommandHandler.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -44,6 +45,7 @@ internal partial class FormatCommandHandler : { private readonly ITextUndoHistoryRegistry _undoHistoryRegistry; private readonly IEditorOperationsFactoryService _editorOperationsFactoryService; + private readonly IIndentationManagerService _indentationManager; private readonly IGlobalOptionService _globalOptions; public string DisplayName => EditorFeaturesResources.Automatic_Formatting; @@ -53,10 +55,12 @@ internal partial class FormatCommandHandler : public FormatCommandHandler( ITextUndoHistoryRegistry undoHistoryRegistry, IEditorOperationsFactoryService editorOperationsFactoryService, + IIndentationManagerService indentationManager, IGlobalOptionService globalOptions) { _undoHistoryRegistry = undoHistoryRegistry; _editorOperationsFactoryService = editorOperationsFactoryService; + _indentationManager = indentationManager; _globalOptions = globalOptions; } @@ -67,8 +71,8 @@ private void Format(ITextView textView, Document document, TextSpan? selectionOp using (Logger.LogBlock(FunctionId.CommandHandler_FormatCommand, KeyValueLogMessage.Create(LogType.UserAction, m => m["Span"] = selectionOpt?.Length ?? -1), cancellationToken)) using (var transaction = CreateEditTransaction(textView, EditorFeaturesResources.Formatting)) { - var changes = formattingService.GetFormattingChangesAsync( - document, selectionOpt, documentOptions: null, cancellationToken).WaitAndGetResult(cancellationToken); + var formattingOptions = _indentationManager.GetInferredFormattingOptionsAsync(document, explicitFormat: true, cancellationToken).WaitAndGetResult(cancellationToken); + var changes = formattingService.GetFormattingChangesAsync(document, selectionOpt, formattingOptions, cancellationToken).WaitAndGetResult(cancellationToken); if (changes.IsEmpty) { return; @@ -162,19 +166,21 @@ private void ExecuteReturnOrTypeCommandWorker(EditorCommandArgs args, Cancellati return; } - textChanges = service.GetFormattingChangesOnReturnAsync( - document, caretPosition.Value, documentOptions: null, cancellationToken).WaitAndGetResult(cancellationToken); + textChanges = service.GetFormattingChangesOnReturnAsync(document, caretPosition.Value, cancellationToken).WaitAndGetResult(cancellationToken); } else if (args is TypeCharCommandArgs typeCharArgs) { - var options = AutoFormattingOptions.From(document.Project); - if (!service.SupportsFormattingOnTypedCharacter(document, options, typeCharArgs.TypedChar)) + var autoFormattingOptions = _globalOptions.GetAutoFormattingOptions(document.Project.Language); + if (!service.SupportsFormattingOnTypedCharacter(document, autoFormattingOptions, typeCharArgs.TypedChar)) { return; } + var formattingOptions = _indentationManager.GetInferredFormattingOptionsAsync(document, explicitFormat: false, cancellationToken).WaitAndGetResult(cancellationToken); + var indentationOptions = new IndentationOptions(formattingOptions, autoFormattingOptions); + textChanges = service.GetFormattingChangesAsync( - document, typeCharArgs.TypedChar, caretPosition.Value, documentOptions: null, cancellationToken).WaitAndGetResult(cancellationToken); + document, typeCharArgs.TypedChar, caretPosition.Value, indentationOptions, cancellationToken).WaitAndGetResult(cancellationToken); } else { diff --git a/src/EditorFeatures/Core/Formatting/IndentationManagerExtensions.cs b/src/EditorFeatures/Core/Formatting/IndentationManagerExtensions.cs new file mode 100644 index 0000000000000..29030f0817f35 --- /dev/null +++ b/src/EditorFeatures/Core/Formatting/IndentationManagerExtensions.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.Text.Editor; + +namespace Microsoft.CodeAnalysis.Formatting +{ + internal static class IndentationManagerExtensions + { + public static async Task GetInferredFormattingOptionsAsync(this IIndentationManagerService indentationManager, Document document, bool explicitFormat, CancellationToken cancellationToken) + { + var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + var snapshot = text.FindCorrespondingEditorTextSnapshot(); + + var options = await SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false); + if (snapshot == null) + { + return options; + } + + indentationManager.GetIndentation(snapshot.TextBuffer, explicitFormat, out var convertTabsToSpaces, out var tabSize, out var indentSize); + + return options.With( + useTabs: !convertTabsToSpaces, + indentationSize: indentSize, + tabSize: tabSize); + } + } +} diff --git a/src/EditorFeatures/Core/Indentation/EditorLayerInferredIndentationService.cs b/src/EditorFeatures/Core/Indentation/EditorLayerInferredIndentationService.cs deleted file mode 100644 index 3c80a220bc69e..0000000000000 --- a/src/EditorFeatures/Core/Indentation/EditorLayerInferredIndentationService.cs +++ /dev/null @@ -1,49 +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.Composition; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Formatting; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Indentation; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Text; -using Microsoft.VisualStudio.Text.Editor; - -namespace Microsoft.VisualStudio.LanguageServices.Indentation -{ - [ExportWorkspaceService(typeof(IInferredIndentationService), ServiceLayer.Editor), Shared] - internal sealed class EditorLayerInferredIndentationService : IInferredIndentationService - { - private readonly IIndentationManagerService _indentationManagerService; - - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public EditorLayerInferredIndentationService(IIndentationManagerService indentationManagerService) - { - _indentationManagerService = indentationManagerService; - } - - public async Task GetDocumentOptionsWithInferredIndentationAsync(Document document, bool explicitFormat, CancellationToken cancellationToken) - { - var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); - var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); - var snapshot = text.FindCorrespondingEditorTextSnapshot(); - - if (snapshot != null) - { - _indentationManagerService.GetIndentation(snapshot.TextBuffer, explicitFormat, out var convertTabsToSpaces, out var tabSize, out var indentSize); - - options = options.WithChangedOption(FormattingOptions.UseTabs, !convertTabsToSpaces) - .WithChangedOption(FormattingOptions.IndentationSize, indentSize) - .WithChangedOption(FormattingOptions.TabSize, tabSize); - } - - return options; - } - } -} diff --git a/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/CommitManager.cs b/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/CommitManager.cs index 1059605bec8ed..4a0861810e1a9 100644 --- a/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/CommitManager.cs +++ b/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/CommitManager.cs @@ -25,6 +25,7 @@ using AsyncCompletionData = Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data; using RoslynCompletionItem = Microsoft.CodeAnalysis.Completion.CompletionItem; using VSCompletionItem = Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data.CompletionItem; +using Microsoft.CodeAnalysis.Indentation; namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.AsyncCompletion { @@ -35,6 +36,7 @@ internal sealed class CommitManager : ForegroundThreadAffinitizedObject, IAsyncC private readonly RecentItemsManager _recentItemsManager; private readonly ITextView _textView; + private readonly IIndentationManagerService _indentationManager; private readonly IGlobalOptionService _globalOptions; public IEnumerable PotentialCommitCharacters @@ -53,12 +55,18 @@ public IEnumerable PotentialCommitCharacters } } - internal CommitManager(ITextView textView, RecentItemsManager recentItemsManager, IGlobalOptionService globalOptions, IThreadingContext threadingContext) + internal CommitManager( + ITextView textView, + RecentItemsManager recentItemsManager, + IThreadingContext threadingContext, + IIndentationManagerService indentationManager, + IGlobalOptionService globalOptions) : base(threadingContext) { _globalOptions = globalOptions; _recentItemsManager = recentItemsManager; _textView = textView; + _indentationManager = indentationManager; } /// @@ -281,10 +289,11 @@ private AsyncCompletionData.CommitResult Commit( if (currentDocument != null && formattingService != null) { + var formattingOptions = _indentationManager.GetInferredFormattingOptionsAsync(document, explicitFormat: true, cancellationToken).WaitAndGetResult(cancellationToken); var spanToFormat = triggerSnapshotSpan.TranslateTo(subjectBuffer.CurrentSnapshot, SpanTrackingMode.EdgeInclusive); var changes = formattingService.GetFormattingChangesAsync( - currentDocument, spanToFormat.Span.ToTextSpan(), documentOptions: null, CancellationToken.None).WaitAndGetResult(CancellationToken.None); - currentDocument.Project.Solution.Workspace.ApplyTextChanges(currentDocument.Id, changes, CancellationToken.None); + currentDocument, spanToFormat.Span.ToTextSpan(), formattingOptions, cancellationToken).WaitAndGetResult(cancellationToken); + currentDocument.Project.Solution.Workspace.ApplyTextChanges(currentDocument.Id, changes, cancellationToken); } } } diff --git a/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/CommitManagerProvider.cs b/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/CommitManagerProvider.cs index 0e3bd7e6af515..3aa7f2cf580b2 100644 --- a/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/CommitManagerProvider.cs +++ b/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/CommitManagerProvider.cs @@ -21,14 +21,20 @@ internal class CommitManagerProvider : IAsyncCompletionCommitManagerProvider { private readonly IThreadingContext _threadingContext; private readonly RecentItemsManager _recentItemsManager; + private readonly IIndentationManagerService _indentationManager; private readonly IGlobalOptionService _globalOptions; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CommitManagerProvider(IThreadingContext threadingContext, RecentItemsManager recentItemsManager, IGlobalOptionService globalOptions) + public CommitManagerProvider( + IThreadingContext threadingContext, + RecentItemsManager recentItemsManager, + IIndentationManagerService indentationManager, + IGlobalOptionService globalOptions) { _threadingContext = threadingContext; _recentItemsManager = recentItemsManager; + _indentationManager = indentationManager; _globalOptions = globalOptions; } @@ -39,7 +45,7 @@ public CommitManagerProvider(IThreadingContext threadingContext, RecentItemsMana return null; } - return new CommitManager(textView, _recentItemsManager, _globalOptions, _threadingContext); + return new CommitManager(textView, _recentItemsManager, _threadingContext, _indentationManager, _globalOptions); } } } diff --git a/src/EditorFeatures/Core/SmartIndent/SmartIndent.cs b/src/EditorFeatures/Core/SmartIndent/SmartIndent.cs index 7e6395e17545d..647423a2b3615 100644 --- a/src/EditorFeatures/Core/SmartIndent/SmartIndent.cs +++ b/src/EditorFeatures/Core/SmartIndent/SmartIndent.cs @@ -5,21 +5,28 @@ using System; using System.Threading; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; +using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.SmartIndent { internal partial class SmartIndent : ISmartIndent { private readonly ITextView _textView; + private readonly IGlobalOptionService _globalOptions; - public SmartIndent(ITextView textView) - => _textView = textView ?? throw new ArgumentNullException(nameof(textView)); + public SmartIndent(ITextView textView, IGlobalOptionService globalOptions) + { + _textView = textView; + _globalOptions = globalOptions; + } public int? GetDesiredIndentation(ITextSnapshotLine line) => GetDesiredIndentation(line, CancellationToken.None); @@ -43,7 +50,8 @@ public void Dispose() if (newService == null) return null; - var result = newService.GetIndentation(document, line.LineNumber, cancellationToken); + var indentationOptions = _globalOptions.GetIndentationOptionsAsync(document, cancellationToken).WaitAndGetResult_CanCallOnBackground(cancellationToken); + var result = newService.GetIndentation(document, line.LineNumber, indentationOptions, cancellationToken); return result.GetIndentation(_textView, line); } } diff --git a/src/EditorFeatures/Core/SmartIndent/SmartIndentProvider.cs b/src/EditorFeatures/Core/SmartIndent/SmartIndentProvider.cs index 91fa421930441..e8f1a12a740c8 100644 --- a/src/EditorFeatures/Core/SmartIndent/SmartIndentProvider.cs +++ b/src/EditorFeatures/Core/SmartIndent/SmartIndentProvider.cs @@ -40,7 +40,7 @@ public ISmartIndent CreateSmartIndent(ITextView textView) return null; } - return new SmartIndent(textView); + return new SmartIndent(textView, _globalOptions); } } } diff --git a/src/EditorFeatures/TestUtilities/AutomaticCompletion/AbstractAutomaticBraceCompletionTests.cs b/src/EditorFeatures/TestUtilities/AutomaticCompletion/AbstractAutomaticBraceCompletionTests.cs index 0f3190db3c2cd..4daaf3a9f50d1 100644 --- a/src/EditorFeatures/TestUtilities/AutomaticCompletion/AbstractAutomaticBraceCompletionTests.cs +++ b/src/EditorFeatures/TestUtilities/AutomaticCompletion/AbstractAutomaticBraceCompletionTests.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.AutomaticCompletion; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; +using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.VisualStudio.Text; diff --git a/src/EditorFeatures/TestUtilities/DocumentationComments/AbstractDocumentationCommentTests.cs b/src/EditorFeatures/TestUtilities/DocumentationComments/AbstractDocumentationCommentTests.cs index 565974ba4316b..ffd9d7d86417e 100644 --- a/src/EditorFeatures/TestUtilities/DocumentationComments/AbstractDocumentationCommentTests.cs +++ b/src/EditorFeatures/TestUtilities/DocumentationComments/AbstractDocumentationCommentTests.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.Editor.UnitTests.Utilities; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.VisualStudio.Commanding; using Microsoft.VisualStudio.Text; @@ -126,13 +127,12 @@ private void Verify(string initialMarkup, string expectedMarkup, bool useTabs, b using (var workspace = CreateTestWorkspace(initialMarkup)) { var testDocument = workspace.Documents.Single(); + workspace.GlobalOptions.SetGlobalOption(new OptionKey(DocumentationCommentOptionsStorage.AutoXmlDocCommentGeneration, testDocument.Project.Language), autoGenerateXmlDocComments); var options = workspace.Options; options = options.WithChangedOption(FormattingOptions.UseTabs, testDocument.Project.Language, useTabs); - options = options.WithChangedOption(DocumentationCommentOptions.Metadata.AutoXmlDocCommentGeneration, testDocument.Project.Language, autoGenerateXmlDocComments); options = options.WithChangedOption(FormattingOptions.NewLine, testDocument.Project.Language, newLine); - workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(options)); setOptionsOpt?.Invoke(workspace); diff --git a/src/EditorFeatures/TestUtilities/Formatting/AbstractNewDocumentFormattingServiceTests.cs b/src/EditorFeatures/TestUtilities/Formatting/AbstractNewDocumentFormattingServiceTests.cs index c51a70e87b39a..432ae717e94cc 100644 --- a/src/EditorFeatures/TestUtilities/Formatting/AbstractNewDocumentFormattingServiceTests.cs +++ b/src/EditorFeatures/TestUtilities/Formatting/AbstractNewDocumentFormattingServiceTests.cs @@ -66,7 +66,8 @@ private async Task TestCoreAsync(string testCode, string expected, (OptionKey var document = workspace.CurrentSolution.Projects.First().Documents.First(); var formattingService = document.GetRequiredLanguageService(); - var formattedDocument = await formattingService.FormatNewDocumentAsync(document, hintDocument: null, CancellationToken.None); + var formattingOptions = await SyntaxFormattingOptions.FromDocumentAsync(document, CancellationToken.None).ConfigureAwait(false); + var formattedDocument = await formattingService.FormatNewDocumentAsync(document, hintDocument: null, formattingOptions, CancellationToken.None); var actual = await formattedDocument.GetTextAsync(); AssertEx.EqualOrDiff(expected, actual.ToString()); diff --git a/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs b/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs index 8907d93f80f28..3c5b3c66ea5eb 100644 --- a/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs +++ b/src/EditorFeatures/TestUtilities/Formatting/CoreFormatterTestsBase.cs @@ -47,8 +47,8 @@ protected CoreFormatterTestsBase(ITestOutputHelper output) protected abstract string GetLanguageName(); protected abstract SyntaxNode ParseCompilationUnit(string expected); - protected static void TestIndentation( - int point, int? expectedIndentation, ITextView textView, TestHostDocument subjectDocument) + internal static void TestIndentation( + int point, int? expectedIndentation, ITextView textView, TestHostDocument subjectDocument, IGlobalOptionService globalOptions) { var textUndoHistory = new Mock(); var editorOperationsFactory = new Mock(); @@ -58,14 +58,25 @@ protected static void TestIndentation( var snapshot = subjectDocument.GetTextBuffer().CurrentSnapshot; var indentationLineFromBuffer = snapshot.GetLineFromPosition(point); - var provider = new SmartIndent(textView); + var provider = new SmartIndent(textView, globalOptions); var actualIndentation = provider.GetDesiredIndentation(indentationLineFromBuffer); Assert.Equal(expectedIndentation, actualIndentation.Value); } - protected static void TestIndentation(TestWorkspace workspace, int indentationLine, int? expectedIndentation) + protected void TestIndentation( + TestWorkspace workspace, + int indentationLine, + int? expectedIndentation, + FormattingOptions.IndentStyle indentStyle, + bool useTabs) { + var language = GetLanguageName(); + workspace.GlobalOptions.SetGlobalOption(new OptionKey(AutoFormattingOptionsStorage.SmartIndent, language), indentStyle); + + workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options + .WithChangedOption(FormattingOptions2.UseTabs, language, useTabs))); + var snapshot = workspace.Documents.First().GetTextBuffer().CurrentSnapshot; var bufferGraph = new Mock(MockBehavior.Strict); bufferGraph.Setup(x => x.MapUpToSnapshot(It.IsAny(), @@ -74,7 +85,6 @@ protected static void TestIndentation(TestWorkspace workspace, int indentationLi It.IsAny())) .Returns((p, m, a, s) => { - if (workspace.Services.GetService() is TestFormattingRuleFactoryServiceFactory.Factory factory && factory.BaseIndentation != 0 && factory.TextSpan.Contains(p.Position)) { var line = p.GetContainingLine(); @@ -93,7 +103,7 @@ protected static void TestIndentation(TestWorkspace workspace, int indentationLi textView.Setup(x => x.BufferGraph).Returns(bufferGraph.Object); textView.SetupGet(x => x.TextSnapshot.TextBuffer).Returns(projectionBuffer.Object); - var provider = new SmartIndent(textView.Object); + var provider = new SmartIndent(textView.Object, workspace.GlobalOptions); var indentationLineFromBuffer = snapshot.GetLineFromLineNumber(indentationLine); var actualIndentation = provider.GetDesiredIndentation(indentationLineFromBuffer); diff --git a/src/EditorFeatures/VisualBasic/DocumentationComments/DocumentationCommentCommandHandler.vb b/src/EditorFeatures/VisualBasic/DocumentationComments/DocumentationCommentCommandHandler.vb index a312477816cfa..395db41ad26b6 100644 --- a/src/EditorFeatures/VisualBasic/DocumentationComments/DocumentationCommentCommandHandler.vb +++ b/src/EditorFeatures/VisualBasic/DocumentationComments/DocumentationCommentCommandHandler.vb @@ -10,6 +10,7 @@ Imports Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion Imports Microsoft.VisualStudio.Text.Operations Imports Microsoft.VisualStudio.Utilities Imports Microsoft.CodeAnalysis.DocumentationComments +Imports Microsoft.CodeAnalysis.Options Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.DocumentationComments @@ -25,9 +26,10 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.DocumentationComments Public Sub New( uiThreadOperationExecutor As IUIThreadOperationExecutor, undoHistoryRegistry As ITextUndoHistoryRegistry, - editorOperationsFactoryService As IEditorOperationsFactoryService) + editorOperationsFactoryService As IEditorOperationsFactoryService, + globalOptions As IGlobalOptionService) - MyBase.New(uiThreadOperationExecutor, undoHistoryRegistry, editorOperationsFactoryService) + MyBase.New(uiThreadOperationExecutor, undoHistoryRegistry, editorOperationsFactoryService, globalOptions) End Sub Protected Overrides ReadOnly Property ExteriorTriviaText As String diff --git a/src/EditorFeatures/VisualBasic/LineCommit/CommitFormatter.vb b/src/EditorFeatures/VisualBasic/LineCommit/CommitFormatter.vb index 66d92f0a3fde4..f5889434f3474 100644 --- a/src/EditorFeatures/VisualBasic/LineCommit/CommitFormatter.vb +++ b/src/EditorFeatures/VisualBasic/LineCommit/CommitFormatter.vb @@ -15,6 +15,7 @@ Imports Microsoft.CodeAnalysis.Internal.Log Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.Text Imports Microsoft.VisualStudio.Text +Imports Microsoft.VisualStudio.Text.Editor Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.LineCommit @@ -28,11 +29,13 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.LineCommit End Function Private ReadOnly _globalOptions As IGlobalOptionService + Private ReadOnly _indentationManager As IIndentationManagerService - Public Sub New(globalOptions As IGlobalOptionService) + Public Sub New(globalOptions As IGlobalOptionService, indentationManager As IIndentationManagerService) _globalOptions = globalOptions + _indentationManager = indentationManager End Sub Public Sub CommitRegion(spanToFormat As SnapshotSpan, @@ -69,36 +72,42 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.LineCommit End If ' create commit formatting cleanup provider that has line commit specific behavior - Dim inferredIndentationService = document.Project.Solution.Workspace.Services.GetRequiredService(Of IInferredIndentationService)() - Dim documentOptions = inferredIndentationService.GetDocumentOptionsWithInferredIndentationAsync(document, isExplicitFormat, cancellationToken).WaitAndGetResult(cancellationToken) - Dim formattingOptions = SyntaxFormattingOptions.Create(documentOptions, document.Project.Solution.Workspace.Services, document.Project.Language) + Dim formattingOptions = _indentationManager.GetInferredFormattingOptionsAsync(document, isExplicitFormat, cancellationToken).WaitAndGetResult(cancellationToken) Dim commitFormattingCleanup = GetCommitFormattingCleanupProvider( - document, - formattingOptions, - spanToFormat, - baseSnapshot, baseTree, - dirtyRegion, - document.GetSyntaxTreeSynchronously(cancellationToken), - cancellationToken) + document, + formattingOptions, + spanToFormat, + baseSnapshot, + baseTree, + dirtyRegion, + document.GetSyntaxTreeSynchronously(cancellationToken), + cancellationToken) Dim codeCleanups = CodeCleaner.GetDefaultProviders(document). WhereAsArray(s_codeCleanupPredicate). Concat(commitFormattingCleanup) + Dim cleanupService = document.GetRequiredLanguageService(Of ICodeCleanerService) + Dim finalDocument As Document If useSemantics OrElse isExplicitFormat Then - finalDocument = CodeCleaner.CleanupAsync(document, - textSpanToFormat, - codeCleanups, - cancellationToken).WaitAndGetResult(cancellationToken) + finalDocument = cleanupService.CleanupAsync( + document, + ImmutableArray.Create(textSpanToFormat), + formattingOptions, + codeCleanups, + cancellationToken).WaitAndGetResult(cancellationToken) Else Dim root = document.GetSyntaxRootSynchronously(cancellationToken) - Dim newRoot = CodeCleaner.CleanupAsync(root, - textSpanToFormat, - documentOptions, - document.Project.Solution.Workspace.Services, - codeCleanups, - cancellationToken).WaitAndGetResult(cancellationToken) + + Dim newRoot = cleanupService.CleanupAsync( + root, + ImmutableArray.Create(textSpanToFormat), + formattingOptions, + document.Project.Solution.Workspace.Services, + codeCleanups, + cancellationToken).WaitAndGetResult(cancellationToken) + If root Is newRoot Then finalDocument = document Else diff --git a/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartIndenterTests.vb b/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartIndenterTests.vb index 48c82b27459ca..04f17878100b7 100644 --- a/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartIndenterTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Formatting/Indentation/SmartIndenterTests.vb @@ -2,7 +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. -Imports Microsoft.CodeAnalysis.Editor.Implementation.SmartIndent Imports Microsoft.CodeAnalysis.Editor.UnitTests Imports Microsoft.CodeAnalysis.Editor.UnitTests.Extensions Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces @@ -2097,13 +2096,6 @@ End Module.Value End Sub #End Region - - - Public Sub TestSmartIndenterConstructorThrows1() - Assert.Throws(Of ArgumentNullException)( - Function() New SmartIndent(Nothing)) - End Sub - Public Sub TestParameter1() @@ -3015,12 +3007,12 @@ end class" Dim point = projectedDocument.GetTextView().BufferGraph.MapDownToBuffer(indentationLine.Start, PointTrackingMode.Negative, subjectDocument.GetTextBuffer(), PositionAffinity.Predecessor) TestIndentation( - point.Value, expectedIndentation, projectedDocument.GetTextView(), subjectDocument) + point.Value, expectedIndentation, projectedDocument.GetTextView(), subjectDocument, workspace.GlobalOptions) End Using End Sub ''' 0-based. The line number in code to get indentation for. - Private Shared Sub AssertSmartIndent( + Private Sub AssertSmartIndent( code As String, indentationLine As Integer, expectedIndentation As Integer?, Optional indentStyle As FormattingOptions.IndentStyle = FormattingOptions.IndentStyle.Smart) @@ -3029,17 +3021,13 @@ end class" End Sub ''' 0-based. The line number in code to get indentation for. - Private Shared Sub AssertSmartIndent( + Private Sub AssertSmartIndent( code As String, indentationLine As Integer, expectedIndentation As Integer?, useTabs As Boolean, indentStyle As FormattingOptions.IndentStyle) Using workspace = TestWorkspace.CreateVisualBasic(code) - workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options _ - .WithChangedOption(FormattingOptions.SmartIndent, LanguageNames.VisualBasic, indentStyle) _ - .WithChangedOption(FormattingOptions.UseTabs, LanguageNames.VisualBasic, useTabs))) - - TestIndentation(workspace, indentationLine, expectedIndentation) + TestIndentation(workspace, indentationLine, expectedIndentation, indentStyle, useTabs) End Using End Sub End Class diff --git a/src/Features/CSharp/Portable/BraceCompletion/AbstractCurlyBraceOrBracketCompletionService.cs b/src/Features/CSharp/Portable/BraceCompletion/AbstractCurlyBraceOrBracketCompletionService.cs index 59f1d7906edb5..6c87012ea5d75 100644 --- a/src/Features/CSharp/Portable/BraceCompletion/AbstractCurlyBraceOrBracketCompletionService.cs +++ b/src/Features/CSharp/Portable/BraceCompletion/AbstractCurlyBraceOrBracketCompletionService.cs @@ -144,7 +144,7 @@ private static bool ContainsOnlyWhitespace(SourceText text, int openingPosition, // Set the caret position to the properly indented column in the desired line. var newDocument = document.WithText(formattedText); var newDocumentText = await newDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); - var caretPosition = GetIndentedLinePosition(newDocument, newDocumentText, desiredCaretLine.LineNumber, cancellationToken); + var caretPosition = GetIndentedLinePosition(newDocument, newDocumentText, desiredCaretLine.LineNumber, options, cancellationToken); // The new line edit is calculated against the original text, d0, to get text d1. // The formatting edits are calculated against d1 to get text d2. @@ -158,10 +158,10 @@ static TextLine GetLineBetweenCurlys(int closingPosition, SourceText text) return text.Lines[closingBraceLineNumber - 1]; } - static LinePosition GetIndentedLinePosition(Document document, SourceText sourceText, int lineNumber, CancellationToken cancellationToken) + static LinePosition GetIndentedLinePosition(Document document, SourceText sourceText, int lineNumber, IndentationOptions options, CancellationToken cancellationToken) { var indentationService = document.GetRequiredLanguageService(); - var indentation = indentationService.GetIndentation(document, lineNumber, cancellationToken); + var indentation = indentationService.GetIndentation(document, lineNumber, options, cancellationToken); var baseLinePosition = sourceText.Lines.GetLinePosition(indentation.BasePosition); var offsetOfBacePosition = baseLinePosition.Character; diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs index 23f3c9dd11369..ac4cbcdc24c4b 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeFixProvider.cs @@ -14,6 +14,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; @@ -62,7 +63,8 @@ protected override async Task FixAllAsync( var diagnostic = diagnostics.First(); var namespaceDecl = (BaseNamespaceDeclarationSyntax)diagnostic.AdditionalLocations[0].FindNode(cancellationToken); - var converted = await ConvertAsync(document, namespaceDecl, cancellationToken).ConfigureAwait(false); + var options = await SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false); + var converted = await ConvertAsync(document, namespaceDecl, options, cancellationToken).ConfigureAwait(false); editor.ReplaceNode( editor.OriginalRoot, diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs index d6a30423155f0..1d305b357a6f6 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceCodeRefactoringProvider.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; @@ -52,8 +53,10 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte if (info == null) return; + var formattingOptions = await SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false); + context.RegisterRefactoring(new MyCodeAction( - info.Value.title, c => ConvertAsync(document, namespaceDecl, c), info.Value.equivalenceKey)); + info.Value.title, c => ConvertAsync(document, namespaceDecl, formattingOptions, c), info.Value.equivalenceKey)); } private static bool IsValidPosition(BaseNamespaceDeclarationSyntax baseDeclaration, int position) diff --git a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceTransform.cs b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceTransform.cs index f46abe8ab84ad..b4d770f28cb3c 100644 --- a/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceTransform.cs +++ b/src/Features/CSharp/Portable/ConvertNamespace/ConvertNamespaceTransform.cs @@ -31,7 +31,7 @@ namespace Microsoft.CodeAnalysis.CSharp.ConvertNamespace { internal static class ConvertNamespaceTransform { - public static async Task ConvertAsync(Document document, BaseNamespaceDeclarationSyntax baseNamespace, CancellationToken cancellationToken) + public static async Task ConvertAsync(Document document, BaseNamespaceDeclarationSyntax baseNamespace, SyntaxFormattingOptions options, CancellationToken cancellationToken) { switch (baseNamespace) { @@ -39,7 +39,7 @@ public static async Task ConvertAsync(Document document, BaseNamespace return await ConvertFileScopedNamespaceAsync(document, fileScopedNamespace, cancellationToken).ConfigureAwait(false); case NamespaceDeclarationSyntax namespaceDeclaration: - var (doc, _) = await ConvertNamespaceDeclarationAsync(document, namespaceDeclaration, cancellationToken).ConfigureAwait(false); + var (doc, _) = await ConvertNamespaceDeclarationAsync(document, namespaceDeclaration, options, cancellationToken).ConfigureAwait(false); return doc; default: @@ -47,12 +47,13 @@ public static async Task ConvertAsync(Document document, BaseNamespace } } - public static async Task<(Document document, TextSpan semicolonSpan)> ConvertNamespaceDeclarationAsync(Document document, NamespaceDeclarationSyntax namespaceDeclaration, CancellationToken cancellationToken) + public static async Task<(Document document, TextSpan semicolonSpan)> ConvertNamespaceDeclarationAsync(Document document, NamespaceDeclarationSyntax namespaceDeclaration, SyntaxFormattingOptions options, CancellationToken cancellationToken) { // First, determine how much indentation we had inside the original block namespace. We'll attempt to remove // that much indentation from each applicable line after we conver the block namespace to a file scoped // namespace. - var indentation = await GetIndentationAsync(document, namespaceDeclaration, cancellationToken).ConfigureAwait(false); + + var indentation = await GetIndentationAsync(document, namespaceDeclaration, options, cancellationToken).ConfigureAwait(false); // Next, actually replace the block namespace with the file scoped namespace. var annotation = new SyntaxAnnotation(); @@ -77,7 +78,7 @@ public static async Task ConvertAsync(Document document, BaseNamespace return (document.WithSyntaxRoot(updatedRoot), fileScopedNamespace.SemicolonToken.Span); } - private static async Task GetIndentationAsync(Document document, NamespaceDeclarationSyntax namespaceDeclaration, CancellationToken cancellationToken) + private static async Task GetIndentationAsync(Document document, NamespaceDeclarationSyntax namespaceDeclaration, SyntaxFormattingOptions options, CancellationToken cancellationToken) { var indentationService = document.GetRequiredLanguageService(); var sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); @@ -87,15 +88,12 @@ public static async Task ConvertAsync(Document document, BaseNamespace if (openBraceLine == closeBraceLine) return null; - var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); - var style = options.GetOption(FormattingOptions.SmartIndent, document.Project.Language); - - var indentation = indentationService.GetIndentation(document, openBraceLine + 1, style, cancellationToken); + // Auto-formatting options are not relevant since they only control behavior on typing. + var indentationOptions = new IndentationOptions(options, AutoFormattingOptions.Default); - var useTabs = options.GetOption(FormattingOptions.UseTabs); - var tabSize = options.GetOption(FormattingOptions.TabSize); + var indentation = indentationService.GetIndentation(document, openBraceLine + 1, indentationOptions, cancellationToken); - return indentation.GetIndentationString(sourceText, useTabs, tabSize); + return indentation.GetIndentationString(sourceText, options.UseTabs, options.TabSize); } private static async Task<(Document document, TextSpan semicolonSpan)> DedentNamespaceAsync( diff --git a/src/Features/CSharp/Portable/ConvertToRawString/ConvertRegularStringToRawStringCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/ConvertToRawString/ConvertRegularStringToRawStringCodeRefactoringProvider.cs index f65ebc0594a6d..a1848a77a25ff 100644 --- a/src/Features/CSharp/Portable/ConvertToRawString/ConvertRegularStringToRawStringCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ConvertToRawString/ConvertRegularStringToRawStringCodeRefactoringProvider.cs @@ -71,12 +71,14 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte var canBeSingleLine = CanBeSingleLine(characters); + var formattingOptions = await SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false); + if (canBeSingleLine) { context.RegisterRefactoring( new MyCodeAction( CSharpFeaturesResources.Convert_to_raw_string, - c => UpdateDocumentAsync(document, span, ConvertToRawKind.SingleLine, c), + c => UpdateDocumentAsync(document, span, ConvertToRawKind.SingleLine, formattingOptions, c), nameof(CSharpFeaturesResources.Convert_to_raw_string) + "-" + ConvertToRawKind.SingleLine, priority), token.Span); @@ -89,7 +91,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte context.RegisterRefactoring( new MyCodeAction( CSharpFeaturesResources.Convert_to_raw_string, - c => UpdateDocumentAsync(document, span, ConvertToRawKind.MultiLineIndented, c), + c => UpdateDocumentAsync(document, span, ConvertToRawKind.MultiLineIndented, formattingOptions, c), nameof(CSharpFeaturesResources.Convert_to_raw_string), priority), token.Span); @@ -99,7 +101,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte context.RegisterRefactoring( new MyCodeAction( CSharpFeaturesResources.Convert_to_raw_string_no_indent, - c => UpdateDocumentAsync(document, span, ConvertToRawKind.MultiLine, c), + c => UpdateDocumentAsync(document, span, ConvertToRawKind.MultiLine, formattingOptions, c), nameof(CSharpFeaturesResources.Convert_to_raw_string_no_indent), priority), token.Span); @@ -108,17 +110,14 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte } private static async Task UpdateDocumentAsync( - Document document, TextSpan span, ConvertToRawKind kind, CancellationToken cancellationToken) + Document document, TextSpan span, ConvertToRawKind kind, SyntaxFormattingOptions options, CancellationToken cancellationToken) { - var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); - var newLine = options.GetOption(FormattingOptions.NewLine); - var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var token = root.FindToken(span.Start); Contract.ThrowIfFalse(span.IntersectsWith(token.Span)); Contract.ThrowIfFalse(token.Kind() == SyntaxKind.StringLiteralToken); - var replacement = GetReplacementToken(document, token, kind, newLine, cancellationToken); + var replacement = GetReplacementToken(document, token, kind, options, cancellationToken); return document.WithSyntaxRoot(root.ReplaceToken(token, replacement)); } @@ -126,19 +125,19 @@ private static SyntaxToken GetReplacementToken( Document document, SyntaxToken token, ConvertToRawKind kind, - string newLine, + SyntaxFormattingOptions options, CancellationToken cancellationToken) { return kind switch { ConvertToRawKind.SingleLine => ConvertToSingleLineRawString(token), - ConvertToRawKind.MultiLine => ConvertToMultiLineRawString(token, newLine), - ConvertToRawKind.MultiLineIndented => ConvertToMultiLineRawIndentedString(document, token, newLine, cancellationToken), + ConvertToRawKind.MultiLine => ConvertToMultiLineRawString(token, options.NewLine), + ConvertToRawKind.MultiLineIndented => ConvertToMultiLineRawIndentedString(document, token, options, cancellationToken), _ => throw ExceptionUtilities.UnexpectedValue(kind), }; } - private static SyntaxToken ConvertToMultiLineRawIndentedString(Document document, SyntaxToken token, string newLine, CancellationToken cancellationToken) + private static SyntaxToken ConvertToMultiLineRawIndentedString(Document document, SyntaxToken token, SyntaxFormattingOptions formattingOptions, CancellationToken cancellationToken) { var characters = CSharpVirtualCharService.Instance.TryConvertToVirtualChars(token); Contract.ThrowIfTrue(characters.IsDefaultOrEmpty); @@ -146,12 +145,15 @@ private static SyntaxToken ConvertToMultiLineRawIndentedString(Document document // Have to make sure we have a delimiter longer than any quote sequence in the string. var longestQuoteSequence = GetLongestQuoteSequence(characters); var quoteDelimeterCount = Math.Max(3, longestQuoteSequence + 1); - var indentation = token.GetPreferredIndentation(document, cancellationToken); + + // Auto-formatting options are not relevant since they only control behavior on typing. + var indentationOptions = new IndentationOptions(formattingOptions, AutoFormattingOptions.Default); + var indentation = token.GetPreferredIndentation(document, indentationOptions, cancellationToken); using var _ = PooledStringBuilder.GetInstance(out var builder); builder.Append('"', quoteDelimeterCount); - builder.Append(newLine); + builder.Append(formattingOptions.NewLine); var atStartOfLine = true; for (int i = 0, n = characters.Length; i < n; i++) @@ -173,7 +175,7 @@ private static SyntaxToken ConvertToMultiLineRawIndentedString(Document document ch.AppendTo(builder); } - builder.Append(newLine); + builder.Append(formattingOptions.NewLine); builder.Append(indentation); builder.Append('"', quoteDelimeterCount); diff --git a/src/Features/CSharp/Portable/Formatting/CSharpAccessibilityModifiersNewDocumentFormattingProvider.cs b/src/Features/CSharp/Portable/Formatting/CSharpAccessibilityModifiersNewDocumentFormattingProvider.cs index c0256ecb8e2c2..ba53dbbc9a20f 100644 --- a/src/Features/CSharp/Portable/Formatting/CSharpAccessibilityModifiersNewDocumentFormattingProvider.cs +++ b/src/Features/CSharp/Portable/Formatting/CSharpAccessibilityModifiersNewDocumentFormattingProvider.cs @@ -26,7 +26,7 @@ public CSharpAccessibilityModifiersNewDocumentFormattingProvider() { } - public async Task FormatNewDocumentAsync(Document document, Document? hintDocument, CancellationToken cancellationToken) + public async Task FormatNewDocumentAsync(Document document, Document? hintDocument, SyntaxFormattingOptions options, CancellationToken cancellationToken) { var documentOptions = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); var accessibilityPreferences = documentOptions.GetOption(CodeStyleOptions2.RequireAccessibilityModifiers, document.Project.Language); diff --git a/src/Features/CSharp/Portable/Formatting/CSharpFormattingInteractionService.cs b/src/Features/CSharp/Portable/Formatting/CSharpFormattingInteractionService.cs index 5f292e5ad6d07..026fee4708a1d 100644 --- a/src/Features/CSharp/Portable/Formatting/CSharpFormattingInteractionService.cs +++ b/src/Features/CSharp/Portable/Formatting/CSharpFormattingInteractionService.cs @@ -91,27 +91,21 @@ public bool SupportsFormattingOnTypedCharacter(Document document, AutoFormatting public async Task> GetFormattingChangesAsync( Document document, TextSpan? textSpan, - DocumentOptionSet? documentOptions, + SyntaxFormattingOptions options, CancellationToken cancellationToken) { var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var span = textSpan ?? new TextSpan(0, root.FullSpan.Length); var formattingSpan = CommonFormattingHelpers.GetFormattingSpan(root, span); - if (documentOptions == null) - { - var inferredIndentationService = document.Project.Solution.Workspace.Services.GetRequiredService(); - documentOptions = await inferredIndentationService.GetDocumentOptionsWithInferredIndentationAsync(document, explicitFormat: true, cancellationToken: cancellationToken).ConfigureAwait(false); - } 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(); + return Formatter.GetFormattedTextChanges(root, SpecializedCollections.SingletonEnumerable(formattingSpan), services, options, cancellationToken).ToImmutableArray(); } public async Task> GetFormattingChangesOnPasteAsync( Document document, TextSpan textSpan, - DocumentOptionSet? documentOptions, + SyntaxFormattingOptions options, CancellationToken cancellationToken) { var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); @@ -122,14 +116,7 @@ public async Task> GetFormattingChangesOnPasteAsync( var rules = new List() { new PasteFormattingRule() }; rules.AddRange(service.GetDefaultFormattingRules()); - if (documentOptions == null) - { - var inferredIndentationService = document.Project.Solution.Workspace.Services.GetRequiredService(); - documentOptions = await inferredIndentationService.GetDocumentOptionsWithInferredIndentationAsync(document, explicitFormat: false, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - var formattingOptions = SyntaxFormattingOptions.Create(documentOptions, document.Project.Solution.Workspace.Services, document.Project.Language); - var result = service.GetFormattingResult(root, SpecializedCollections.SingletonEnumerable(formattingSpan), formattingOptions, rules, cancellationToken); + var result = service.GetFormattingResult(root, SpecializedCollections.SingletonEnumerable(formattingSpan), options, rules, cancellationToken); return result.GetTextChanges(cancellationToken).ToImmutableArray(); } @@ -141,7 +128,7 @@ private static IEnumerable GetFormattingRules(Document d } Task> IFormattingInteractionService.GetFormattingChangesOnReturnAsync( - Document document, int caretPosition, DocumentOptionSet? documentOptions, CancellationToken cancellationToken) + Document document, int caretPosition, CancellationToken cancellationToken) => SpecializedTasks.EmptyImmutableArray(); private static async Task TokenShouldNotFormatOnTypeCharAsync( @@ -181,7 +168,7 @@ public async Task> GetFormattingChangesAsync( Document document, char typedChar, int caretPosition, - DocumentOptionSet? documentOptions, + IndentationOptions options, CancellationToken cancellationToken) { // first, find the token user just typed. @@ -208,16 +195,6 @@ public async Task> GetFormattingChangesAsync( return ImmutableArray.Empty; } - var services = document.Project.Solution.Workspace.Services; - - if (documentOptions == null) - { - var inferredIndentationService = services.GetRequiredService(); - documentOptions = await inferredIndentationService.GetDocumentOptionsWithInferredIndentationAsync(document, explicitFormat: false, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - var options = IndentationOptions.From(documentOptions, document.Project.Solution.Workspace.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. // diff --git a/src/Features/CSharp/Portable/Formatting/CSharpNamespaceDeclarationNewDocumentFormattingProvider.cs b/src/Features/CSharp/Portable/Formatting/CSharpNamespaceDeclarationNewDocumentFormattingProvider.cs index d2d8d33740221..050e3b16ed36a 100644 --- a/src/Features/CSharp/Portable/Formatting/CSharpNamespaceDeclarationNewDocumentFormattingProvider.cs +++ b/src/Features/CSharp/Portable/Formatting/CSharpNamespaceDeclarationNewDocumentFormattingProvider.cs @@ -27,7 +27,7 @@ public CSharpNamespaceDeclarationNewDocumentFormattingProvider() { } - public async Task FormatNewDocumentAsync(Document document, Document? hintDocument, CancellationToken cancellationToken) + public async Task FormatNewDocumentAsync(Document document, Document? hintDocument, SyntaxFormattingOptions options, CancellationToken cancellationToken) { var optionSet = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); var root = (CompilationUnitSyntax)await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); @@ -36,7 +36,7 @@ public async Task FormatNewDocumentAsync(Document document, Document? if (namespaces.Count != 1) return document; - return await ConvertNamespaceTransform.ConvertAsync(document, namespaces[0], cancellationToken).ConfigureAwait(false); + return await ConvertNamespaceTransform.ConvertAsync(document, namespaces[0], options, cancellationToken).ConfigureAwait(false); } private static IEnumerable GetNamespacesToReplace(Document document, CompilationUnitSyntax root, DocumentOptionSet optionSet) diff --git a/src/Features/CSharp/Portable/Formatting/CSharpOrganizeUsingsNewDocumentFormattingProvider.cs b/src/Features/CSharp/Portable/Formatting/CSharpOrganizeUsingsNewDocumentFormattingProvider.cs index eb8d0f0291811..ed370a01dd77a 100644 --- a/src/Features/CSharp/Portable/Formatting/CSharpOrganizeUsingsNewDocumentFormattingProvider.cs +++ b/src/Features/CSharp/Portable/Formatting/CSharpOrganizeUsingsNewDocumentFormattingProvider.cs @@ -21,7 +21,7 @@ public CSharpOrganizeUsingsNewDocumentFormattingProvider() { } - public async Task FormatNewDocumentAsync(Document document, Document? hintDocument, CancellationToken cancellationToken) + public async Task FormatNewDocumentAsync(Document document, Document? hintDocument, SyntaxFormattingOptions options, CancellationToken cancellationToken) { var organizedDocument = await Formatter.OrganizeImportsAsync(document, cancellationToken).ConfigureAwait(false); return await MisplacedUsingDirectivesCodeFixProvider.TransformDocumentIfRequiredAsync(organizedDocument, cancellationToken).ConfigureAwait(false); diff --git a/src/Features/CSharp/Portable/SplitStringLiteral/InterpolatedStringSplitter.cs b/src/Features/CSharp/Portable/SplitStringLiteral/InterpolatedStringSplitter.cs index af391391ad71f..c6e2382ac162a 100644 --- a/src/Features/CSharp/Portable/SplitStringLiteral/InterpolatedStringSplitter.cs +++ b/src/Features/CSharp/Portable/SplitStringLiteral/InterpolatedStringSplitter.cs @@ -9,6 +9,7 @@ using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp.SplitStringLiteral @@ -25,11 +26,11 @@ public InterpolatedStringSplitter( SyntaxNode root, SourceText sourceText, InterpolatedStringExpressionSyntax interpolatedStringExpression, + IndentationOptions options, bool useTabs, int tabSize, - FormattingOptions.IndentStyle indentStyle, CancellationToken cancellationToken) - : base(document, position, root, sourceText, useTabs, tabSize, indentStyle, cancellationToken) + : base(document, position, root, sourceText, options, useTabs, tabSize, cancellationToken) { _interpolatedStringExpression = interpolatedStringExpression; } diff --git a/src/Features/CSharp/Portable/SplitStringLiteral/SimpleStringSplitter.cs b/src/Features/CSharp/Portable/SplitStringLiteral/SimpleStringSplitter.cs index 4d66f047857a8..5ed690b31fe9c 100644 --- a/src/Features/CSharp/Portable/SplitStringLiteral/SimpleStringSplitter.cs +++ b/src/Features/CSharp/Portable/SplitStringLiteral/SimpleStringSplitter.cs @@ -7,6 +7,7 @@ using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp.SplitStringLiteral @@ -21,8 +22,8 @@ private sealed class SimpleStringSplitter : StringSplitter public SimpleStringSplitter( Document document, int position, SyntaxNode root, SourceText sourceText, SyntaxToken token, - bool useTabs, int tabSize, FormattingOptions.IndentStyle indentStyle, CancellationToken cancellationToken) - : base(document, position, root, sourceText, useTabs, tabSize, indentStyle, cancellationToken) + in IndentationOptions options, bool useTabs, int tabSize, CancellationToken cancellationToken) + : base(document, position, root, sourceText, options, useTabs, tabSize, cancellationToken) { _token = token; } diff --git a/src/Features/CSharp/Portable/SplitStringLiteral/StringSplitter.cs b/src/Features/CSharp/Portable/SplitStringLiteral/StringSplitter.cs index 0592a8a440474..9062054e2faa0 100644 --- a/src/Features/CSharp/Portable/SplitStringLiteral/StringSplitter.cs +++ b/src/Features/CSharp/Portable/SplitStringLiteral/StringSplitter.cs @@ -27,17 +27,15 @@ internal abstract partial class StringSplitter protected readonly int CursorPosition; protected readonly SourceText SourceText; protected readonly SyntaxNode Root; + protected readonly IndentationOptions Options; protected readonly int TabSize; protected readonly bool UseTabs; protected readonly CancellationToken CancellationToken; - private readonly FormattingOptions.IndentStyle _indentStyle; - public StringSplitter( Document document, int position, SyntaxNode root, SourceText sourceText, - bool useTabs, int tabSize, - FormattingOptions.IndentStyle indentStyle, + in IndentationOptions options, bool useTabs, int tabSize, CancellationToken cancellationToken) { Document = document; @@ -46,13 +44,13 @@ public StringSplitter( SourceText = sourceText; UseTabs = useTabs; TabSize = tabSize; - _indentStyle = indentStyle; + Options = options; CancellationToken = cancellationToken; } public static StringSplitter TryCreate( Document document, int position, - bool useTabs, int tabSize, FormattingOptions.IndentStyle indentStyle, + in IndentationOptions options, bool useTabs, int tabSize, CancellationToken cancellationToken) { var root = document.GetSyntaxRootSynchronously(cancellationToken); @@ -64,8 +62,8 @@ public static StringSplitter TryCreate( { return new SimpleStringSplitter( document, position, root, - sourceText, token, useTabs, tabSize, - indentStyle, cancellationToken); + sourceText, token, options, useTabs, tabSize, + cancellationToken); } var interpolatedStringExpression = TryGetInterpolatedStringExpression(token, position); @@ -74,7 +72,7 @@ public static StringSplitter TryCreate( return new InterpolatedStringSplitter( document, position, root, sourceText, interpolatedStringExpression, - useTabs, tabSize, indentStyle, cancellationToken); + options, useTabs, tabSize, cancellationToken); } return null; @@ -154,7 +152,7 @@ private string GetIndentString(SyntaxNode newRoot) var originalLineNumber = SourceText.Lines.GetLineFromPosition(CursorPosition).LineNumber; var desiredIndentation = indentationService.GetIndentation( - newDocument, originalLineNumber + 1, _indentStyle, CancellationToken); + newDocument, originalLineNumber + 1, Options, CancellationToken); var newSourceText = newDocument.GetSyntaxRootSynchronously(CancellationToken).SyntaxTree.GetText(CancellationToken); var baseLine = newSourceText.Lines.GetLineFromPosition(desiredIndentation.BasePosition); diff --git a/src/Features/CSharp/Portable/Wrapping/CSharpSyntaxWrappingOptions.cs b/src/Features/CSharp/Portable/Wrapping/CSharpSyntaxWrappingOptions.cs index 467df8966f241..5717a334ffa98 100644 --- a/src/Features/CSharp/Portable/Wrapping/CSharpSyntaxWrappingOptions.cs +++ b/src/Features/CSharp/Portable/Wrapping/CSharpSyntaxWrappingOptions.cs @@ -6,7 +6,6 @@ using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Formatting; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Wrapping; namespace Microsoft.CodeAnalysis.CSharp.Wrapping @@ -16,22 +15,18 @@ internal sealed class CSharpSyntaxWrappingOptions : SyntaxWrappingOptions public readonly bool NewLinesForBracesInObjectCollectionArrayInitializers; public CSharpSyntaxWrappingOptions( - bool useTabs, - int tabSize, - string newLine, + CSharpSyntaxFormattingOptions formattingOptions, int wrappingColumn, OperatorPlacementWhenWrappingPreference operatorPlacement, bool newLinesForBracesInObjectCollectionArrayInitializers) - : base(useTabs, tabSize, newLine, wrappingColumn, operatorPlacement) + : base(formattingOptions, wrappingColumn, operatorPlacement) { NewLinesForBracesInObjectCollectionArrayInitializers = newLinesForBracesInObjectCollectionArrayInitializers; } public static CSharpSyntaxWrappingOptions Create(AnalyzerConfigOptions options, CodeActionOptions ideOptions) => new( - useTabs: options.GetOption(FormattingOptions2.UseTabs), - tabSize: options.GetOption(FormattingOptions2.TabSize), - newLine: options.GetOption(FormattingOptions2.NewLine), + CSharpSyntaxFormattingOptions.Create(options), operatorPlacement: options.GetOption(CodeStyleOptions2.OperatorPlacementWhenWrapping), wrappingColumn: ideOptions.WrappingColumn, newLinesForBracesInObjectCollectionArrayInitializers: options.GetOption(CSharpFormattingOptions2.NewLinesForBracesInObjectCollectionArrayInitializers)); diff --git a/src/Features/Core/Portable/AddFileBanner/AbstractAddFileBannerNewDocumentFormattingProvider.cs b/src/Features/Core/Portable/AddFileBanner/AbstractAddFileBannerNewDocumentFormattingProvider.cs index 3051a24034ddf..0e723e3457129 100644 --- a/src/Features/Core/Portable/AddFileBanner/AbstractAddFileBannerNewDocumentFormattingProvider.cs +++ b/src/Features/Core/Portable/AddFileBanner/AbstractAddFileBannerNewDocumentFormattingProvider.cs @@ -19,7 +19,7 @@ internal abstract class AbstractAddFileBannerNewDocumentFormattingProvider : INe protected abstract SyntaxGeneratorInternal SyntaxGeneratorInternal { get; } protected abstract AbstractFileHeaderHelper FileHeaderHelper { get; } - public async Task FormatNewDocumentAsync(Document document, Document? hintDocument, CancellationToken cancellationToken) + public async Task FormatNewDocumentAsync(Document document, Document? hintDocument, SyntaxFormattingOptions options, CancellationToken cancellationToken) { var rootToFormat = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var documentOptions = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); @@ -28,8 +28,7 @@ public async Task FormatNewDocumentAsync(Document document, Document? var fileHeaderTemplate = documentOptions.GetOption(CodeStyleOptions2.FileHeaderTemplate); if (!string.IsNullOrEmpty(fileHeaderTemplate)) { - var newLineText = documentOptions.GetOption(FormattingOptions.NewLine, rootToFormat.Language); - var newLineTrivia = SyntaxGeneratorInternal.EndOfLine(newLineText); + var newLineTrivia = SyntaxGeneratorInternal.EndOfLine(options.NewLine); var rootWithFileHeader = await AbstractFileHeaderCodeFixProvider.GetTransformedSyntaxRootAsync( SyntaxGenerator.SyntaxFacts, FileHeaderHelper, diff --git a/src/Features/Core/Portable/DocumentationComments/DocumentationCommentOptions.cs b/src/Features/Core/Portable/DocumentationComments/DocumentationCommentOptions.cs index 3612d368496da..e29ba4043d6f9 100644 --- a/src/Features/Core/Portable/DocumentationComments/DocumentationCommentOptions.cs +++ b/src/Features/Core/Portable/DocumentationComments/DocumentationCommentOptions.cs @@ -2,43 +2,13 @@ // 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.Composition; -using Microsoft.CodeAnalysis.Formatting; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Options.Providers; +using System.Runtime.Serialization; -namespace Microsoft.CodeAnalysis.DocumentationComments -{ - internal readonly record struct DocumentationCommentOptions( - bool AutoXmlDocCommentGeneration, - int TabSize, - bool UseTabs, - string NewLine) - { - [ExportSolutionOptionProvider, Shared] - internal sealed class Metadata : IOptionProvider - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public Metadata() - { - } +namespace Microsoft.CodeAnalysis.DocumentationComments; - public ImmutableArray Options { get; } = ImmutableArray.Create( - AutoXmlDocCommentGeneration); - - public static readonly PerLanguageOption2 AutoXmlDocCommentGeneration = new(nameof(DocumentationCommentOptions), nameof(AutoXmlDocCommentGeneration), defaultValue: true, - storageLocation: new RoamingProfileStorageLocation(language => language == LanguageNames.VisualBasic ? "TextEditor.%LANGUAGE%.Specific.AutoComment" : "TextEditor.%LANGUAGE%.Specific.Automatic XML Doc Comment Generation")); - } - - public static DocumentationCommentOptions From(DocumentOptionSet options) - => new( - AutoXmlDocCommentGeneration: options.GetOption(Metadata.AutoXmlDocCommentGeneration), - TabSize: options.GetOption(FormattingOptions.TabSize), - UseTabs: options.GetOption(FormattingOptions.UseTabs), - NewLine: options.GetOption(FormattingOptions.NewLine)); - } -} +[DataContract] +internal readonly record struct DocumentationCommentOptions( + [property: DataMember(Order = 0)] bool AutoXmlDocCommentGeneration, + [property: DataMember(Order = 1)] int TabSize, + [property: DataMember(Order = 2)] bool UseTabs, + [property: DataMember(Order = 3)] string NewLine); diff --git a/src/Features/Core/Portable/Formatting/AbstractNewDocumentFormattingService.cs b/src/Features/Core/Portable/Formatting/AbstractNewDocumentFormattingService.cs index 8be7bfe176e3e..e7f59e6c693dc 100644 --- a/src/Features/Core/Portable/Formatting/AbstractNewDocumentFormattingService.cs +++ b/src/Features/Core/Portable/Formatting/AbstractNewDocumentFormattingService.cs @@ -32,7 +32,7 @@ private IEnumerable GetProviders() return _providerValues; } - public async Task FormatNewDocumentAsync(Document document, Document? hintDocument, CancellationToken cancellationToken) + public async Task FormatNewDocumentAsync(Document document, Document? hintDocument, SyntaxFormattingOptions options, CancellationToken cancellationToken) { foreach (var provider in GetProviders()) { @@ -46,7 +46,7 @@ public async Task FormatNewDocumentAsync(Document document, Document? // First we ask the provider to "format" the document. This could be formatting in terms // of adjusting block scopes to file scopes etc., but it could also be more akin to fixers // like adding access modifiers, or adding .ConfigureAwait() calls etc. - document = await provider.FormatNewDocumentAsync(document, hintDocument, cancellationToken).ConfigureAwait(false); + document = await provider.FormatNewDocumentAsync(document, hintDocument, options, cancellationToken).ConfigureAwait(false); // Now that the above has changed the document, we use the code action engine to clean up the document // before we call the next provider, otherwise they might not see things as they are meant to be. diff --git a/src/Features/Core/Portable/Formatting/IFormattingInteractionService.cs b/src/Features/Core/Portable/Formatting/IFormattingInteractionService.cs index 84296fa0cb7c2..685e79d7d7ac6 100644 --- a/src/Features/Core/Portable/Formatting/IFormattingInteractionService.cs +++ b/src/Features/Core/Portable/Formatting/IFormattingInteractionService.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; @@ -25,26 +26,26 @@ internal interface IFormattingInteractionService : ILanguageService bool SupportsFormattingOnTypedCharacter(Document document, AutoFormattingOptions options, char ch); /// - /// Returns the text changes necessary to format the document. If "textSpan" is provided, + /// Returns the text changes necessary to format the document. If is provided, /// only the text changes necessary to format that span are needed. /// - Task> GetFormattingChangesAsync(Document document, TextSpan? textSpan, DocumentOptionSet? documentOptions, CancellationToken cancellationToken); + Task> GetFormattingChangesAsync(Document document, TextSpan? textSpan, SyntaxFormattingOptions options, CancellationToken cancellationToken); /// /// Returns the text changes necessary to format the document on paste operation. /// - Task> GetFormattingChangesOnPasteAsync(Document document, TextSpan textSpan, DocumentOptionSet? documentOptions, CancellationToken cancellationToken); + Task> GetFormattingChangesOnPasteAsync(Document document, TextSpan textSpan, SyntaxFormattingOptions options, CancellationToken cancellationToken); /// /// Returns the text changes necessary to format the document after the user enters a /// character. The position provided is the position of the caret in the document after /// the character been inserted into the document. /// - Task> GetFormattingChangesAsync(Document document, char typedChar, int position, DocumentOptionSet? documentOptions, CancellationToken cancellationToken); + Task> GetFormattingChangesAsync(Document document, char typedChar, int position, IndentationOptions options, CancellationToken cancellationToken); /// /// Returns the text changes necessary to format the document after the user enters a Return /// The position provided is the position of the caret in the document after Return. - Task> GetFormattingChangesOnReturnAsync(Document document, int position, DocumentOptionSet? documentOptions, CancellationToken cancellationToken); + Task> GetFormattingChangesOnReturnAsync(Document document, int position, CancellationToken cancellationToken); } } diff --git a/src/Features/Core/Portable/Formatting/INewDocumentFormattingProvider.cs b/src/Features/Core/Portable/Formatting/INewDocumentFormattingProvider.cs index c75a47fb9e328..3012091569619 100644 --- a/src/Features/Core/Portable/Formatting/INewDocumentFormattingProvider.cs +++ b/src/Features/Core/Portable/Formatting/INewDocumentFormattingProvider.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.Formatting { internal interface INewDocumentFormattingProvider { - /// - Task FormatNewDocumentAsync(Document document, Document? hintDocument, CancellationToken cancellationToken); + /// + Task FormatNewDocumentAsync(Document document, Document? hintDocument, SyntaxFormattingOptions options, CancellationToken cancellationToken); } } diff --git a/src/Features/Core/Portable/Formatting/INewDocumentFormattingService.cs b/src/Features/Core/Portable/Formatting/INewDocumentFormattingService.cs index e22870c4e27f8..74b3452f825f7 100644 --- a/src/Features/Core/Portable/Formatting/INewDocumentFormattingService.cs +++ b/src/Features/Core/Portable/Formatting/INewDocumentFormattingService.cs @@ -16,6 +16,6 @@ internal interface INewDocumentFormattingService : ILanguageService /// The document to format. /// An optional additional document that can be used to inform the formatting operation. /// A cancellation token. - Task FormatNewDocumentAsync(Document document, Document? hintDocument, CancellationToken cancellationToken); + Task FormatNewDocumentAsync(Document document, Document? hintDocument, SyntaxFormattingOptions options, CancellationToken cancellationToken); } } diff --git a/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs b/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs index 0a34d81af7323..4daf9502e6281 100644 --- a/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs +++ b/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs @@ -312,7 +312,8 @@ private async Task> GetGenerateInNewFileOperati var formattingService = newDocument.GetLanguageService(); if (formattingService is not null) { - codeGenResult = await formattingService.FormatNewDocumentAsync(codeGenResult, _semanticDocument.Document, _cancellationToken).ConfigureAwait(false); + var formattingOptions = await SyntaxFormattingOptions.FromDocumentAsync(_semanticDocument.Document, _cancellationToken).ConfigureAwait(false); + codeGenResult = await formattingService.FormatNewDocumentAsync(codeGenResult, _semanticDocument.Document, formattingOptions, _cancellationToken).ConfigureAwait(false); } } diff --git a/src/Features/Core/Portable/Options/AutoFormattingOptionsStorage.cs b/src/Features/Core/Portable/Options/AutoFormattingOptionsStorage.cs new file mode 100644 index 0000000000000..1386ea45a8783 --- /dev/null +++ b/src/Features/Core/Portable/Options/AutoFormattingOptionsStorage.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.Options; + +namespace Microsoft.CodeAnalysis.Formatting; + +// TODO: move to LSP layer + +internal static class AutoFormattingOptionsStorage +{ + public static AutoFormattingOptions GetAutoFormattingOptions(this IGlobalOptionService globalOptions, string language) + => new( + IndentStyle: globalOptions.GetOption(SmartIndent, language), + FormatOnReturn: globalOptions.GetOption(FormatOnReturn, language), + FormatOnTyping: globalOptions.GetOption(FormatOnTyping, language), + FormatOnSemicolon: globalOptions.GetOption(FormatOnSemicolon, language), + FormatOnCloseBrace: globalOptions.GetOption(FormatOnCloseBrace, language)); + + internal static PerLanguageOption2 SmartIndent => FormattingOptions.SmartIndent2; + + internal static readonly PerLanguageOption2 FormatOnReturn = new( + "FormattingOptions", OptionGroup.Default, "AutoFormattingOnReturn", AutoFormattingOptions.Default.FormatOnReturn, + storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.Auto Formatting On Return")); + + public static readonly PerLanguageOption2 FormatOnTyping = new( + "FormattingOptions", OptionGroup.Default, "AutoFormattingOnTyping", AutoFormattingOptions.Default.FormatOnTyping, + storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.Auto Formatting On Typing")); + + public static readonly PerLanguageOption2 FormatOnSemicolon = new( + "FormattingOptions", OptionGroup.Default, "AutoFormattingOnSemicolon", AutoFormattingOptions.Default.FormatOnSemicolon, + storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.Auto Formatting On Semicolon")); + + public static readonly PerLanguageOption2 FormatOnCloseBrace = new( + "BraceCompletionOptions", "AutoFormattingOnCloseBrace", defaultValue: AutoFormattingOptions.Default.FormatOnCloseBrace, + storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.Auto Formatting On Close Brace")); +} diff --git a/src/Features/Core/Portable/Options/DocumentationCommentOptionsStorage.cs b/src/Features/Core/Portable/Options/DocumentationCommentOptionsStorage.cs new file mode 100644 index 0000000000000..249bb5c4c3a24 --- /dev/null +++ b/src/Features/Core/Portable/Options/DocumentationCommentOptionsStorage.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Options; + +namespace Microsoft.CodeAnalysis.DocumentationComments; + +// TODO: move to LSP layer + +internal static class DocumentationCommentOptionsStorage +{ + public static DocumentationCommentOptions GetDocumentationCommentOptions(this IGlobalOptionService globalOptions, DocumentOptionSet documentOptions) + => new( + AutoXmlDocCommentGeneration: globalOptions.GetOption(AutoXmlDocCommentGeneration, documentOptions.Language), + TabSize: documentOptions.GetOption(FormattingOptions.TabSize), + UseTabs: documentOptions.GetOption(FormattingOptions.UseTabs), + NewLine: documentOptions.GetOption(FormattingOptions.NewLine)); + + public static DocumentationCommentOptions GetDocumentationCommentOptions(this IGlobalOptionService globalOptions, SyntaxFormattingOptions formattingOptions, string language) + => new( + AutoXmlDocCommentGeneration: globalOptions.GetOption(AutoXmlDocCommentGeneration, language), + TabSize: formattingOptions.TabSize, + UseTabs: formattingOptions.UseTabs, + NewLine: formattingOptions.NewLine); + + public static readonly PerLanguageOption2 AutoXmlDocCommentGeneration = new( + "DocumentationCommentOptions", "AutoXmlDocCommentGeneration", defaultValue: true, + storageLocation: new RoamingProfileStorageLocation(language => language == LanguageNames.VisualBasic ? "TextEditor.%LANGUAGE%.Specific.AutoComment" : "TextEditor.%LANGUAGE%.Specific.Automatic XML Doc Comment Generation")); + +} diff --git a/src/Features/Core/Portable/Options/IndentationOptionsStorage.cs b/src/Features/Core/Portable/Options/IndentationOptionsStorage.cs new file mode 100644 index 0000000000000..44df45a4dccd0 --- /dev/null +++ b/src/Features/Core/Portable/Options/IndentationOptionsStorage.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Options; + +namespace Microsoft.CodeAnalysis.Indentation; + +internal static class IndentationOptionsStorage +{ + // TODO: move to LSP layer + public static async Task GetIndentationOptionsAsync(this IGlobalOptionService globalOptions, Document document, CancellationToken cancellationToken) + { + var formattingOptions = await SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false); + var autoFormattingOptions = globalOptions.GetAutoFormattingOptions(document.Project.Language); + return new(formattingOptions, autoFormattingOptions); + } +} diff --git a/src/Features/Core/Portable/Shared/Utilities/ExtractTypeHelpers.cs b/src/Features/Core/Portable/Shared/Utilities/ExtractTypeHelpers.cs index c075b5a90e15c..09cb120ce4fe4 100644 --- a/src/Features/Core/Portable/Shared/Utilities/ExtractTypeHelpers.cs +++ b/src/Features/Core/Portable/Shared/Utilities/ExtractTypeHelpers.cs @@ -87,10 +87,11 @@ internal static class ExtractTypeHelpers context, cancellationToken).ConfigureAwait(false); - var formattingSerivce = newTypeDocument.GetLanguageService(); - if (formattingSerivce is not null) + var formattingService = newTypeDocument.GetLanguageService(); + if (formattingService is not null) { - newTypeDocument = await formattingSerivce.FormatNewDocumentAsync(newTypeDocument, hintDocument, cancellationToken).ConfigureAwait(false); + var formattingOptions = await SyntaxFormattingOptions.FromDocumentAsync(newTypeDocument, cancellationToken).ConfigureAwait(false); + newTypeDocument = await formattingService.FormatNewDocumentAsync(newTypeDocument, hintDocument, formattingOptions, cancellationToken).ConfigureAwait(false); } var syntaxRoot = await newTypeDocument.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); diff --git a/src/Features/Core/Portable/Wrapping/AbstractCodeActionComputer.cs b/src/Features/Core/Portable/Wrapping/AbstractCodeActionComputer.cs index 09f3e7c4a5fe3..65a568a47b31a 100644 --- a/src/Features/Core/Portable/Wrapping/AbstractCodeActionComputer.cs +++ b/src/Features/Core/Portable/Wrapping/AbstractCodeActionComputer.cs @@ -4,6 +4,7 @@ #nullable disable +using System.CodeDom.Compiler; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -77,7 +78,7 @@ public AbstractCodeActionComputer( var generator = SyntaxGenerator.GetGenerator(document); var generatorInternal = document.GetRequiredLanguageService(); - NewLineTrivia = new SyntaxTriviaList(generatorInternal.EndOfLine(options.NewLine)); + NewLineTrivia = new SyntaxTriviaList(generatorInternal.EndOfLine(options.FormattingOptions.NewLine)); SingleWhitespaceTrivia = new SyntaxTriviaList(generator.Whitespace(" ")); } @@ -88,19 +89,26 @@ protected string GetSmartIndentationAfter(SyntaxNodeOrToken nodeOrToken) protected string GetIndentationAfter(SyntaxNodeOrToken nodeOrToken, FormattingOptions.IndentStyle indentStyle) { - var newSourceText = OriginalSourceText.WithChanges(new TextChange(new TextSpan(nodeOrToken.Span.End, 0), Options.NewLine)); + var newLine = Options.FormattingOptions.NewLine; + var newSourceText = OriginalSourceText.WithChanges(new TextChange(new TextSpan(nodeOrToken.Span.End, 0), newLine)); newSourceText = newSourceText.WithChanges( - new TextChange(TextSpan.FromBounds(nodeOrToken.Span.End + Options.NewLine.Length, newSourceText.Length), "")); + new TextChange(TextSpan.FromBounds(nodeOrToken.Span.End + newLine.Length, newSourceText.Length), "")); var newDocument = OriginalDocument.WithText(newSourceText); + // The only auto-formatting option that's relevant is indent style. Others only control behavior on typing. + var indentationOptions = new IndentationOptions( + Options.FormattingOptions, + new AutoFormattingOptions( + IndentStyle: indentStyle)); + var indentationService = Wrapper.IndentationService; var originalLineNumber = newSourceText.Lines.GetLineFromPosition(nodeOrToken.Span.End).LineNumber; var desiredIndentation = indentationService.GetIndentation( newDocument, originalLineNumber + 1, - indentStyle, + indentationOptions, CancellationToken); - return desiredIndentation.GetIndentationString(newSourceText, Options.UseTabs, Options.TabSize); + return desiredIndentation.GetIndentationString(newSourceText, Options.FormattingOptions.UseTabs, Options.FormattingOptions.TabSize); } /// diff --git a/src/Features/Core/Portable/Wrapping/AbstractWrappingCodeRefactoringProvider.cs b/src/Features/Core/Portable/Wrapping/AbstractWrappingCodeRefactoringProvider.cs index 00498c2fa0bcd..e252c490e5dd1 100644 --- a/src/Features/Core/Portable/Wrapping/AbstractWrappingCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/Wrapping/AbstractWrappingCodeRefactoringProvider.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.Wrapping diff --git a/src/Features/Core/Portable/Wrapping/BinaryExpression/BinaryExpressionCodeActionComputer.cs b/src/Features/Core/Portable/Wrapping/BinaryExpression/BinaryExpressionCodeActionComputer.cs index ed160c6a93512..3f93d916b82b7 100644 --- a/src/Features/Core/Portable/Wrapping/BinaryExpression/BinaryExpressionCodeActionComputer.cs +++ b/src/Features/Core/Portable/Wrapping/BinaryExpression/BinaryExpressionCodeActionComputer.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -61,7 +62,7 @@ public BinaryExpressionCodeActionComputer( _indentAndAlignTrivia = new SyntaxTriviaList(generator.Whitespace( OriginalSourceText.GetOffset(binaryExpression.Span.Start) - .CreateIndentationString(options.UseTabs, options.TabSize))); + .CreateIndentationString(options.FormattingOptions.UseTabs, options.FormattingOptions.TabSize))); _smartIndentTrivia = new SyntaxTriviaList(generator.Whitespace( GetSmartIndentationAfter(_exprsAndOperators[1]))); diff --git a/src/Features/Core/Portable/Wrapping/ChainedExpression/AbstractChainedExpressionWrapper.cs b/src/Features/Core/Portable/Wrapping/ChainedExpression/AbstractChainedExpressionWrapper.cs index 1305d5462075a..cd346692eedd7 100644 --- a/src/Features/Core/Portable/Wrapping/ChainedExpression/AbstractChainedExpressionWrapper.cs +++ b/src/Features/Core/Portable/Wrapping/ChainedExpression/AbstractChainedExpressionWrapper.cs @@ -6,6 +6,7 @@ using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.PooledObjects; diff --git a/src/Features/Core/Portable/Wrapping/ChainedExpression/ChainedExpressionCodeActionComputer.cs b/src/Features/Core/Portable/Wrapping/ChainedExpression/ChainedExpressionCodeActionComputer.cs index de9f4dfbf9433..ff7448f389004 100644 --- a/src/Features/Core/Portable/Wrapping/ChainedExpression/ChainedExpressionCodeActionComputer.cs +++ b/src/Features/Core/Portable/Wrapping/ChainedExpression/ChainedExpressionCodeActionComputer.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -84,7 +85,7 @@ public CallExpressionCodeActionComputer( var firstPeriod = chunks[0][0]; _firstPeriodIndentationTrivia = new SyntaxTriviaList(generator.Whitespace( - OriginalSourceText.GetOffset(firstPeriod.SpanStart).CreateIndentationString(options.UseTabs, options.TabSize))); + OriginalSourceText.GetOffset(firstPeriod.SpanStart).CreateIndentationString(options.FormattingOptions.UseTabs, options.FormattingOptions.TabSize))); _smartIndentTrivia = new SyntaxTriviaList(generator.Whitespace( GetSmartIndentationAfter(firstPeriod))); diff --git a/src/Features/Core/Portable/Wrapping/ISyntaxWrapper.cs b/src/Features/Core/Portable/Wrapping/ISyntaxWrapper.cs index 95f1879510e79..fe9eeb2513788 100644 --- a/src/Features/Core/Portable/Wrapping/ISyntaxWrapper.cs +++ b/src/Features/Core/Portable/Wrapping/ISyntaxWrapper.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Indentation; namespace Microsoft.CodeAnalysis.Wrapping { diff --git a/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/SeparatedSyntaxListCodeActionComputer.cs b/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/SeparatedSyntaxListCodeActionComputer.cs index 25bd17606fe15..6415516633812 100644 --- a/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/SeparatedSyntaxListCodeActionComputer.cs +++ b/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/SeparatedSyntaxListCodeActionComputer.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -107,7 +108,7 @@ private string GetAfterOpenTokenIdentation() var openToken = _listSyntax.GetFirstToken(); var afterOpenTokenOffset = OriginalSourceText.GetOffset(openToken.Span.End); - var indentString = afterOpenTokenOffset.CreateIndentationString(Options.UseTabs, Options.TabSize); + var indentString = afterOpenTokenOffset.CreateIndentationString(Options.FormattingOptions.UseTabs, Options.FormattingOptions.TabSize); return indentString; } @@ -133,7 +134,7 @@ private string GetBraceTokenIndentation() var previousToken = _listSyntax.GetFirstToken().GetPreviousToken(); // Block indentation is the only style that correctly indents across all initializer expressions - return GetIndentationAfter(previousToken, Formatting.FormattingOptions.IndentStyle.Block); + return GetIndentationAfter(previousToken, FormattingOptions.IndentStyle.Block); } protected override async Task> ComputeWrappingGroupsAsync() diff --git a/src/Features/Core/Portable/Wrapping/SyntaxWrappingOptions.cs b/src/Features/Core/Portable/Wrapping/SyntaxWrappingOptions.cs index a7a2883923fd4..2b00e46f68970 100644 --- a/src/Features/Core/Portable/Wrapping/SyntaxWrappingOptions.cs +++ b/src/Features/Core/Portable/Wrapping/SyntaxWrappingOptions.cs @@ -2,31 +2,23 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeStyle; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting; namespace Microsoft.CodeAnalysis.Wrapping { internal abstract class SyntaxWrappingOptions { - public readonly bool UseTabs; - public readonly int TabSize; - public readonly string NewLine; + public readonly SyntaxFormattingOptions FormattingOptions; public readonly int WrappingColumn; public readonly OperatorPlacementWhenWrappingPreference OperatorPlacement; protected SyntaxWrappingOptions( - bool useTabs, - int tabSize, - string newLine, + SyntaxFormattingOptions formattingOptions, int wrappingColumn, OperatorPlacementWhenWrappingPreference operatorPlacement) { - UseTabs = useTabs; - TabSize = tabSize; - NewLine = newLine; + FormattingOptions = formattingOptions; WrappingColumn = wrappingColumn; OperatorPlacement = operatorPlacement; } diff --git a/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs b/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs index 4b8bb4e256a50..4d6181585f501 100644 --- a/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs +++ b/src/Features/LanguageServer/Protocol/Extensions/ProtocolConversions.cs @@ -11,7 +11,9 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.DocumentHighlighting; +using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.NavigateTo; @@ -657,24 +659,24 @@ public static ProjectId ProjectContextToProjectId(LSP.VSProjectContext projectCo debugName: projectContext.Id.Substring(delimiter + 1)); } - public static async Task FormattingOptionsToDocumentOptionsAsync( + public static async Task GetFormattingOptionsAsync( LSP.FormattingOptions? options, Document document, CancellationToken cancellationToken) { - var documentOptions = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); + var formattingOptions = await SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false); if (options != null) { // LSP doesn't currently support indent size as an option. However, except in special // circumstances, indent size is usually equivalent to tab size, so we'll just set it. - documentOptions = documentOptions - .WithChangedOption(Formatting.FormattingOptions.UseTabs, !options.InsertSpaces) - .WithChangedOption(Formatting.FormattingOptions.TabSize, options.TabSize) - .WithChangedOption(Formatting.FormattingOptions.IndentationSize, options.TabSize); + formattingOptions = formattingOptions.With( + useTabs: !options.InsertSpaces, + tabSize: options.TabSize, + indentationSize: options.TabSize); } - return documentOptions; + return formattingOptions; } public static LSP.MarkupContent GetDocumentationMarkupContent(ImmutableArray tags, Document document, bool featureSupportsMarkdown) diff --git a/src/Features/LanguageServer/Protocol/Handler/Formatting/AbstractFormatDocumentHandlerBase.cs b/src/Features/LanguageServer/Protocol/Handler/Formatting/AbstractFormatDocumentHandlerBase.cs index 5e5717f3d7de5..602e0e05bd027 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Formatting/AbstractFormatDocumentHandlerBase.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Formatting/AbstractFormatDocumentHandlerBase.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; @@ -20,7 +21,7 @@ internal abstract class AbstractFormatDocumentHandlerBase false; public override bool RequiresLSPSolution => true; - protected async Task GetTextEditsAsync( + protected static async Task GetTextEditsAsync( RequestContext context, LSP.FormattingOptions options, CancellationToken cancellationToken, @@ -41,21 +42,12 @@ internal abstract class AbstractFormatDocumentHandlerBase ProtocolConversions.TextChangeToTextEdit(change, text))); return edits.ToArrayAndFree(); } - - protected virtual Task> GetFormattingChangesAsync( - IFormattingInteractionService formattingService, - Document document, - TextSpan? textSpan, - DocumentOptionSet documentOptions, - CancellationToken cancellationToken) - => formattingService.GetFormattingChangesAsync(document, textSpan, documentOptions, cancellationToken); } } diff --git a/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentHandler.cs index 4c4d5f3f3bc40..e435b8ce46d8e 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentHandler.cs @@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler { [ExportRoslynLanguagesLspRequestHandlerProvider(typeof(FormatDocumentHandler)), Shared] [Method(LSP.Methods.TextDocumentFormattingName)] - internal class FormatDocumentHandler : AbstractFormatDocumentHandlerBase + internal sealed class FormatDocumentHandler : AbstractFormatDocumentHandlerBase { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] diff --git a/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs index 003d8462a93d2..88970caf249f1 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentOnTypeHandler.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; @@ -21,15 +22,18 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler { [ExportRoslynLanguagesLspRequestHandlerProvider(typeof(FormatDocumentOnTypeHandler)), Shared] [Method(Methods.TextDocumentOnTypeFormattingName)] - internal class FormatDocumentOnTypeHandler : AbstractStatelessRequestHandler + internal sealed class FormatDocumentOnTypeHandler : AbstractStatelessRequestHandler { + private readonly IGlobalOptionService _globalOptions; + public override bool MutatesSolutionState => false; public override bool RequiresLSPSolution => true; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public FormatDocumentOnTypeHandler() + public FormatDocumentOnTypeHandler(IGlobalOptionService globalOptions) { + _globalOptions = globalOptions; } public override TextDocumentIdentifier? GetTextDocumentIdentifier(DocumentOnTypeFormattingParams request) => request.TextDocument; @@ -53,20 +57,20 @@ public FormatDocumentOnTypeHandler() return edits.ToArrayAndFree(); } - // We should use the options passed in by LSP instead of the document's options. - var documentOptions = await ProtocolConversions.FormattingOptionsToDocumentOptionsAsync( - request.Options, document, cancellationToken).ConfigureAwait(false); - IList? textChanges; if (SyntaxFacts.IsNewLine(request.Character[0])) { - textChanges = await GetFormattingChangesOnReturnAsync( - formattingService, document, position, documentOptions, cancellationToken).ConfigureAwait(false); + textChanges = await formattingService.GetFormattingChangesOnReturnAsync( + document, position, cancellationToken).ConfigureAwait(false); } else { - textChanges = await GetFormattingChangesAsync( - formattingService, document, request.Character[0], position, documentOptions, cancellationToken).ConfigureAwait(false); + // We should use the options passed in by LSP instead of the document's options. + var formattingOptions = await ProtocolConversions.GetFormattingOptionsAsync(request.Options, document, cancellationToken).ConfigureAwait(false); + var indentationOptions = new IndentationOptions(formattingOptions, _globalOptions.GetAutoFormattingOptions(document.Project.Language)); + + textChanges = await formattingService.GetFormattingChangesAsync( + document, request.Character[0], position, indentationOptions, cancellationToken).ConfigureAwait(false); } var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); @@ -77,22 +81,5 @@ public FormatDocumentOnTypeHandler() return edits.ToArrayAndFree(); } - - protected virtual async Task?> GetFormattingChangesOnReturnAsync( - IFormattingInteractionService formattingService, - Document document, - int position, - DocumentOptionSet documentOptions, - CancellationToken cancellationToken) - => await formattingService.GetFormattingChangesOnReturnAsync(document, position, documentOptions, cancellationToken).ConfigureAwait(false); - - protected virtual async Task?> GetFormattingChangesAsync( - IFormattingInteractionService formattingService, - Document document, - char typedChar, - int position, - DocumentOptionSet documentOptions, - CancellationToken cancellationToken) - => await formattingService.GetFormattingChangesAsync(document, typedChar, position, documentOptions, cancellationToken).ConfigureAwait(false); } } diff --git a/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentRangeHandler.cs b/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentRangeHandler.cs index a83f246b48926..646b0c7f318a8 100644 --- a/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentRangeHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/Formatting/FormatDocumentRangeHandler.cs @@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler { [ExportRoslynLanguagesLspRequestHandlerProvider(typeof(FormatDocumentRangeHandler)), Shared] [Method(Methods.TextDocumentRangeFormattingName)] - internal class FormatDocumentRangeHandler : AbstractFormatDocumentHandlerBase + internal sealed class FormatDocumentRangeHandler : AbstractFormatDocumentHandlerBase { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] diff --git a/src/Features/LanguageServer/Protocol/Handler/InlineCompletions/InlineCompletionsHandler.cs b/src/Features/LanguageServer/Protocol/Handler/InlineCompletions/InlineCompletionsHandler.cs index 1b6234e8997fc..99540d07f0711 100644 --- a/src/Features/LanguageServer/Protocol/Handler/InlineCompletions/InlineCompletionsHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/InlineCompletions/InlineCompletionsHandler.cs @@ -11,6 +11,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; @@ -97,10 +98,9 @@ public InlineCompletionsHandler(XmlSnippetParser xmlSnippetParser) } // Use the formatting options specified by the client to format the snippet. - var documentOptions = await ProtocolConversions.FormattingOptionsToDocumentOptionsAsync( - request.Options, context.Document, cancellationToken).ConfigureAwait(false); + var formattingOptions = await ProtocolConversions.GetFormattingOptionsAsync(request.Options, context.Document, cancellationToken).ConfigureAwait(false); - var formattedLspSnippet = await GetFormattedLspSnippetAsync(parsedSnippet, wordOnLeft.Value, context.Document, sourceText, documentOptions, cancellationToken).ConfigureAwait(false); + var formattedLspSnippet = await GetFormattedLspSnippetAsync(parsedSnippet, wordOnLeft.Value, context.Document, sourceText, formattingOptions, cancellationToken).ConfigureAwait(false); return new VSInternalInlineCompletionList { @@ -122,7 +122,7 @@ public InlineCompletionsHandler(XmlSnippetParser xmlSnippetParser) /// /// Note that the operations in this method are sensitive to the context in the document and so must be calculated on each request. /// - private static async Task GetFormattedLspSnippetAsync(ParsedXmlSnippet parsedSnippet, TextSpan snippetShortcut, Document originalDocument, SourceText originalSourceText, DocumentOptionSet documentOptions, CancellationToken cancellationToken) + private static async Task GetFormattedLspSnippetAsync(ParsedXmlSnippet parsedSnippet, TextSpan snippetShortcut, Document originalDocument, SourceText originalSourceText, SyntaxFormattingOptions options, CancellationToken cancellationToken) { // Calculate the snippet text with defaults + snippet function results. var (snippetFullText, fields, caretSpan) = await GetReplacedSnippetTextAsync( @@ -136,7 +136,7 @@ private static async Task GetFormattedLspSnippetAsync(ParsedXmlSnippet p var root = await originalDocument.WithText(documentWithSnippetText).GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var spanToFormat = TextSpan.FromBounds(textChange.Span.Start, snippetEndPosition); - var formattingChanges = Formatter.GetFormattedTextChanges(root, spanToFormat, originalDocument.Project.Solution.Workspace, options: documentOptions, cancellationToken: cancellationToken) + var formattingChanges = Formatter.GetFormattedTextChanges(root, spanToFormat, originalDocument.Project.Solution.Workspace.Services, options, cancellationToken: cancellationToken) ?.ToImmutableArray() ?? ImmutableArray.Empty; var formattedText = documentWithSnippetText.WithChanges(formattingChanges); diff --git a/src/Features/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs b/src/Features/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs index 3a9b8d4488c5a..8e380c29cb8a6 100644 --- a/src/Features/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs +++ b/src/Features/LanguageServer/Protocol/Handler/OnAutoInsert/OnAutoInsertHandler.cs @@ -26,10 +26,11 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Handler { [ExportRoslynLanguagesLspRequestHandlerProvider(typeof(OnAutoInsertHandler)), Shared] [Method(LSP.VSInternalMethods.OnAutoInsertName)] - internal class OnAutoInsertHandler : AbstractStatelessRequestHandler + internal sealed class OnAutoInsertHandler : AbstractStatelessRequestHandler { private readonly ImmutableArray _csharpBraceCompletionServices; private readonly ImmutableArray _visualBasicBraceCompletionServices; + private readonly IGlobalOptionService _globalOptions; public override bool MutatesSolutionState => false; public override bool RequiresLSPSolution => true; @@ -38,10 +39,12 @@ internal class OnAutoInsertHandler : AbstractStatelessRequestHandler csharpBraceCompletionServices, - [ImportMany(LanguageNames.VisualBasic)] IEnumerable visualBasicBraceCompletionServices) + [ImportMany(LanguageNames.VisualBasic)] IEnumerable visualBasicBraceCompletionServices, + IGlobalOptionService globalOptions) { _csharpBraceCompletionServices = csharpBraceCompletionServices.ToImmutableArray(); _visualBasicBraceCompletionServices = visualBasicBraceCompletionServices.ToImmutableArray(); + _globalOptions = globalOptions; } public override LSP.TextDocumentIdentifier? GetTextDocumentIdentifier(LSP.VSInternalDocumentOnAutoInsertParams request) => request.TextDocument; @@ -58,16 +61,15 @@ public OnAutoInsertHandler( var service = document.GetRequiredLanguageService(); // We should use the options passed in by LSP instead of the document's options. - var documentOptions = await ProtocolConversions.FormattingOptionsToDocumentOptionsAsync( - request.Options, document, cancellationToken).ConfigureAwait(false); - - var options = DocumentationCommentOptions.From(documentOptions); + var formattingOptions = await ProtocolConversions.GetFormattingOptionsAsync(request.Options, document, cancellationToken).ConfigureAwait(false); // The editor calls this handler for C# and VB comment characters, but we only need to process the one for the language that matches the document if (request.Character == "\n" || request.Character == service.DocumentationCommentCharacter) { + var docCommentOptions = _globalOptions.GetDocumentationCommentOptions(formattingOptions, document.Project.Language); + var documentationCommentResponse = await GetDocumentationCommentResponseAsync( - request, document, service, options, cancellationToken).ConfigureAwait(false); + request, document, service, docCommentOptions, cancellationToken).ConfigureAwait(false); if (documentationCommentResponse != null) { return documentationCommentResponse; @@ -79,7 +81,7 @@ public OnAutoInsertHandler( // Once LSP supports overtype we can move all of brace completion to LSP. if (request.Character == "\n" && context.ClientName == document.Services.GetService()?.DiagnosticsLspClientName) { - var indentationOptions = IndentationOptions.From(documentOptions, document.Project.Solution.Workspace.Services, document.Project.Language); + var indentationOptions = new IndentationOptions(formattingOptions, _globalOptions.GetAutoFormattingOptions(document.Project.Language)); var braceCompletionAfterReturnResponse = await GetBraceCompletionAfterReturnResponseAsync( request, document, indentationOptions, cancellationToken).ConfigureAwait(false); diff --git a/src/Features/VisualBasic/Portable/Formatting/VisualBasicOrganizeUsingsNewDocumentFormattingProvider.vb b/src/Features/VisualBasic/Portable/Formatting/VisualBasicOrganizeUsingsNewDocumentFormattingProvider.vb index bc75860e9b89a..7199cf45bd65a 100644 --- a/src/Features/VisualBasic/Portable/Formatting/VisualBasicOrganizeUsingsNewDocumentFormattingProvider.vb +++ b/src/Features/VisualBasic/Portable/Formatting/VisualBasicOrganizeUsingsNewDocumentFormattingProvider.vb @@ -17,7 +17,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting Public Sub New() End Sub - Public Function FormatNewDocumentAsync(document As Document, hintDocument As Document, cancellationToken As CancellationToken) As Task(Of Document) Implements INewDocumentFormattingProvider.FormatNewDocumentAsync + Public Function FormatNewDocumentAsync(document As Document, hintDocument As Document, options As SyntaxFormattingOptions, cancellationToken As CancellationToken) As Task(Of Document) Implements INewDocumentFormattingProvider.FormatNewDocumentAsync Return Formatter.OrganizeImportsAsync(document, cancellationToken) End Function End Class diff --git a/src/Features/VisualBasic/Portable/Wrapping/VisualBasicSyntaxWrappingOptions.vb b/src/Features/VisualBasic/Portable/Wrapping/VisualBasicSyntaxWrappingOptions.vb index 5d843197b09b7..9c1023c82aa28 100644 --- a/src/Features/VisualBasic/Portable/Wrapping/VisualBasicSyntaxWrappingOptions.vb +++ b/src/Features/VisualBasic/Portable/Wrapping/VisualBasicSyntaxWrappingOptions.vb @@ -6,7 +6,7 @@ Imports Microsoft.CodeAnalysis.CodeActions Imports Microsoft.CodeAnalysis.CodeStyle Imports Microsoft.CodeAnalysis.Wrapping Imports Microsoft.CodeAnalysis.Diagnostics -Imports Microsoft.CodeAnalysis.Formatting +Imports Microsoft.CodeAnalysis.VisualBasic.Formatting Namespace Microsoft.CodeAnalysis.VisualBasic.Wrapping @@ -14,20 +14,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Wrapping Inherits SyntaxWrappingOptions Public Sub New( - useTabs As Boolean, - tabSize As Integer, - newLine As String, + formattingOptions As VisualBasicSyntaxFormattingOptions, wrappingColumn As Integer, operatorPlacement As OperatorPlacementWhenWrappingPreference) - MyBase.New(useTabs, tabSize, newLine, wrappingColumn, operatorPlacement) + MyBase.New(formattingOptions, wrappingColumn, operatorPlacement) End Sub Public Shared Function Create(options As AnalyzerConfigOptions, ideOptions As CodeActionOptions) As VisualBasicSyntaxWrappingOptions Return New VisualBasicSyntaxWrappingOptions( - useTabs:=options.GetOption(FormattingOptions2.UseTabs), - tabSize:=options.GetOption(FormattingOptions2.TabSize), - newLine:=options.GetOption(FormattingOptions2.NewLine), + VisualBasicSyntaxFormattingOptions.Create(options), operatorPlacement:=options.GetOption(CodeStyleOptions2.OperatorPlacementWhenWrapping), wrappingColumn:=ideOptions.WrappingColumn) End Function diff --git a/src/Tools/ExternalAccess/FSharp/Internal/Editor/FSharpEditorFormattingService.cs b/src/Tools/ExternalAccess/FSharp/Internal/Editor/FSharpEditorFormattingService.cs index cdbd528c71867..f8ccc3218be90 100644 --- a/src/Tools/ExternalAccess/FSharp/Internal/Editor/FSharpEditorFormattingService.cs +++ b/src/Tools/ExternalAccess/FSharp/Internal/Editor/FSharpEditorFormattingService.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.ExternalAccess.FSharp.Editor; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; @@ -37,22 +38,22 @@ public FSharpEditorFormattingService(IFSharpEditorFormattingService service) public bool SupportsFormatOnReturn => _service.SupportsFormatOnReturn; - public Task> GetFormattingChangesAsync(Document document, TextSpan? textSpan, DocumentOptionSet? documentOptions, CancellationToken cancellationToken) + public Task> GetFormattingChangesAsync(Document document, TextSpan? textSpan, CancellationToken cancellationToken) { return _service.GetFormattingChangesAsync(document, textSpan, cancellationToken); } - public Task?> GetFormattingChangesAsync(Document document, char typedChar, int position, DocumentOptionSet? documentOptions, CancellationToken cancellationToken) + public Task?> GetFormattingChangesAsync(Document document, char typedChar, int position, CancellationToken cancellationToken) { return _service.GetFormattingChangesAsync(document, typedChar, position, cancellationToken); } - public Task> GetFormattingChangesOnPasteAsync(Document document, TextSpan textSpan, DocumentOptionSet? documentOptions, CancellationToken cancellationToken) + public Task> GetFormattingChangesOnPasteAsync(Document document, TextSpan textSpan, CancellationToken cancellationToken) { return _service.GetFormattingChangesOnPasteAsync(document, textSpan, cancellationToken); } - public Task?> GetFormattingChangesOnReturnAsync(Document document, int position, DocumentOptionSet? documentOptions, CancellationToken cancellationToken) + public Task?> GetFormattingChangesOnReturnAsync(Document document, int position, CancellationToken cancellationToken) { return _service.GetFormattingChangesOnReturnAsync(document, position, cancellationToken); } @@ -64,27 +65,27 @@ public bool SupportsFormattingOnTypedCharacter(Document document, AutoFormatting _service.SupportsFormattingOnTypedCharacter(document, ch); } - async Task> IFormattingInteractionService.GetFormattingChangesAsync(Document document, TextSpan? textSpan, DocumentOptionSet? documentOptions, CancellationToken cancellationToken) + async Task> IFormattingInteractionService.GetFormattingChangesAsync(Document document, TextSpan? textSpan, SyntaxFormattingOptions options, CancellationToken cancellationToken) { - var changes = await GetFormattingChangesAsync(document, textSpan, documentOptions, cancellationToken).ConfigureAwait(false); + var changes = await GetFormattingChangesAsync(document, textSpan, cancellationToken).ConfigureAwait(false); return changes?.ToImmutableArray() ?? ImmutableArray.Empty; } - async Task> IFormattingInteractionService.GetFormattingChangesAsync(Document document, char typedChar, int position, DocumentOptionSet? documentOptions, CancellationToken cancellationToken) + async Task> IFormattingInteractionService.GetFormattingChangesAsync(Document document, char typedChar, int position, IndentationOptions options, CancellationToken cancellationToken) { - var changes = await GetFormattingChangesAsync(document, typedChar, position, documentOptions, cancellationToken).ConfigureAwait(false); + var changes = await GetFormattingChangesAsync(document, typedChar, position, cancellationToken).ConfigureAwait(false); return changes?.ToImmutableArray() ?? ImmutableArray.Empty; } - async Task> IFormattingInteractionService.GetFormattingChangesOnPasteAsync(Document document, TextSpan textSpan, DocumentOptionSet? documentOptions, CancellationToken cancellationToken) + async Task> IFormattingInteractionService.GetFormattingChangesOnPasteAsync(Document document, TextSpan textSpan, SyntaxFormattingOptions options, CancellationToken cancellationToken) { - var changes = await GetFormattingChangesOnPasteAsync(document, textSpan, documentOptions, cancellationToken).ConfigureAwait(false); + var changes = await GetFormattingChangesOnPasteAsync(document, textSpan, cancellationToken).ConfigureAwait(false); return changes?.ToImmutableArray() ?? ImmutableArray.Empty; } - async Task> IFormattingInteractionService.GetFormattingChangesOnReturnAsync(Document document, int position, DocumentOptionSet? documentOptions, CancellationToken cancellationToken) + async Task> IFormattingInteractionService.GetFormattingChangesOnReturnAsync(Document document, int position, CancellationToken cancellationToken) { - var changes = await GetFormattingChangesOnReturnAsync(document, position, documentOptions, cancellationToken).ConfigureAwait(false); + var changes = await GetFormattingChangesOnReturnAsync(document, position, cancellationToken).ConfigureAwait(false); return changes?.ToImmutableArray() ?? ImmutableArray.Empty; } } diff --git a/src/Tools/ExternalAccess/FSharp/Internal/Editor/FSharpSynchronousIndentationService.cs b/src/Tools/ExternalAccess/FSharp/Internal/Editor/FSharpSynchronousIndentationService.cs index ace8ede5fb0a5..9ef917f04242a 100644 --- a/src/Tools/ExternalAccess/FSharp/Internal/Editor/FSharpSynchronousIndentationService.cs +++ b/src/Tools/ExternalAccess/FSharp/Internal/Editor/FSharpSynchronousIndentationService.cs @@ -34,7 +34,7 @@ public FSharpSynchronousIndentationService( _service = service; } - public IndentationResult GetIndentation(Document document, int lineNumber, FormattingOptions.IndentStyle indentStyle, CancellationToken cancellationToken) + public IndentationResult GetIndentation(Document document, int lineNumber, IndentationOptions options, CancellationToken cancellationToken) { // all F# documents should have a file path if (document.FilePath == null) @@ -46,13 +46,12 @@ public IndentationResult GetIndentation(Document document, int lineNumber, Forma if (_service != null) { var text = document.GetTextSynchronously(cancellationToken); - var documentOptions = document.GetOptionsAsync(cancellationToken).WaitAndGetResult(cancellationToken); - var options = new FSharpIndentationOptions( - TabSize: documentOptions.GetOption(FormattingOptions.TabSize, LanguageNames.FSharp), - IndentStyle: indentStyle); + var fsharpOptions = new FSharpIndentationOptions( + TabSize: options.FormattingOptions.TabSize, + IndentStyle: options.AutoFormattingOptions.IndentStyle); - result = _service.GetDesiredIndentation(document.Project.LanguageServices, text, document.Id, document.FilePath, lineNumber, options); + result = _service.GetDesiredIndentation(document.Project.LanguageServices, text, document.Id, document.FilePath, lineNumber, fsharpOptions); } else { diff --git a/src/Tools/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentOptionsWrapper.cs b/src/Tools/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentOptionsWrapper.cs index 29cd5c5a935a4..2a1259aa52ca9 100644 --- a/src/Tools/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentOptionsWrapper.cs +++ b/src/Tools/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentOptionsWrapper.cs @@ -5,6 +5,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.DocumentationComments; +using Microsoft.CodeAnalysis.Formatting; namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.DocumentationComments { @@ -29,8 +30,13 @@ public static async ValueTask FromD bool autoXmlDocCommentGeneration, CancellationToken cancellationToken) { - var documentOptions = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); - return new(DocumentationCommentOptions.From(documentOptions) with { AutoXmlDocCommentGeneration = autoXmlDocCommentGeneration }); + var formattingOptions = await SyntaxFormattingOptions.FromDocumentAsync(document, cancellationToken).ConfigureAwait(false); + + return new(new DocumentationCommentOptions( + AutoXmlDocCommentGeneration: autoXmlDocCommentGeneration, + TabSize: formattingOptions.TabSize, + UseTabs: formattingOptions.UseTabs, + NewLine: formattingOptions.NewLine)); } } } diff --git a/src/Tools/ExternalAccess/Razor/Microsoft.CodeAnalysis.ExternalAccess.Razor.csproj b/src/Tools/ExternalAccess/Razor/Microsoft.CodeAnalysis.ExternalAccess.Razor.csproj index a1434a15de1f6..e56e00c7d76cf 100644 --- a/src/Tools/ExternalAccess/Razor/Microsoft.CodeAnalysis.ExternalAccess.Razor.csproj +++ b/src/Tools/ExternalAccess/Razor/Microsoft.CodeAnalysis.ExternalAccess.Razor.csproj @@ -14,6 +14,10 @@ + + + + @@ -22,7 +26,7 @@ --> - + diff --git a/src/Tools/ExternalAccess/Razor/RazorAutoFormattingOptions.cs b/src/Tools/ExternalAccess/Razor/RazorAutoFormattingOptions.cs new file mode 100644 index 0000000000000..8862f8eefb4b7 --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/RazorAutoFormattingOptions.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.Formatting; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor +{ + internal readonly struct RazorAutoFormattingOptions + { + internal readonly AutoFormattingOptions UnderlyingObject; + + public RazorAutoFormattingOptions(AutoFormattingOptions underlyingObject) + => UnderlyingObject = underlyingObject; + + public RazorAutoFormattingOptions( + FormattingOptions.IndentStyle indentStyle, + bool formatOnReturn, + bool formatOnTyping, + bool formatOnSemicolon, + bool formatOnCloseBrace) + : this(new AutoFormattingOptions( + indentStyle, + FormatOnReturn: formatOnReturn, + FormatOnTyping: formatOnTyping, + FormatOnSemicolon: formatOnSemicolon, + FormatOnCloseBrace: formatOnCloseBrace)) + { + } + } +} diff --git a/src/Tools/ExternalAccess/Razor/RazorCSharpFormattingInteractionService.cs b/src/Tools/ExternalAccess/Razor/RazorCSharpFormattingInteractionService.cs index 164758b6f3031..016a619c673be 100644 --- a/src/Tools/ExternalAccess/Razor/RazorCSharpFormattingInteractionService.cs +++ b/src/Tools/ExternalAccess/Razor/RazorCSharpFormattingInteractionService.cs @@ -2,10 +2,15 @@ // 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.Generic; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.Formatting; using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; @@ -19,30 +24,29 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.Razor internal static class RazorCSharpFormattingInteractionService { /// - /// True if this service would like to format the document based on the user typing the - /// provided character. - /// - public static bool SupportsFormattingOnTypedCharacter(Document document, char ch) - { - Contract.ThrowIfFalse(document.Project.Language is LanguageNames.CSharp); - var formattingService = document.GetRequiredLanguageService(); - var options = AutoFormattingOptions.From(document.Project); - return formattingService.SupportsFormattingOnTypedCharacter(document, options, ch); - } - - /// - /// Returns the text changes necessary to format the document. If "textSpan" is provided, - /// only the text changes necessary to format that span are needed. + /// Returns the text changes necessary to format the document after the user enters a + /// character. The position provided is the position of the caret in the document after + /// the character been inserted into the document. /// + [Obsolete("Use the other overload")] public static Task> GetFormattingChangesAsync( Document document, - TextSpan? textSpan, - DocumentOptionSet? documentOptions, + char typedChar, + int position, + DocumentOptionSet documentOptions, CancellationToken cancellationToken) { Contract.ThrowIfFalse(document.Project.Language is LanguageNames.CSharp); var formattingService = document.GetRequiredLanguageService(); - return formattingService.GetFormattingChangesAsync(document, textSpan, documentOptions, cancellationToken); + var services = document.Project.Solution.Workspace.Services; + + var globalOptions = document.Project.Solution.Workspace.Services.GetRequiredService(); + + var indentationOptions = new IndentationOptions( + SyntaxFormattingOptions.Create(documentOptions, services, document.Project.Language), + globalOptions.GlobalOptions.GetAutoFormattingOptions(document.Project.Language)); + + return formattingService.GetFormattingChangesAsync(document, typedChar, position, indentationOptions, cancellationToken); } /// @@ -50,45 +54,48 @@ public static Task> GetFormattingChangesAsync( /// character. The position provided is the position of the caret in the document after /// the character been inserted into the document. /// - public static Task> GetFormattingChangesAsync( + public static async Task> GetFormattingChangesAsync( Document document, char typedChar, int position, - DocumentOptionSet? documentOptions, + RazorIndentationOptions indentationOptions, + RazorAutoFormattingOptions autoFormattingOptions, CancellationToken cancellationToken) { Contract.ThrowIfFalse(document.Project.Language is LanguageNames.CSharp); var formattingService = document.GetRequiredLanguageService(); - return formattingService.GetFormattingChangesAsync(document, typedChar, position, documentOptions, cancellationToken); + + var formattingOptions = GetFormattingOptions(indentationOptions); + var roslynIndentationOptions = new IndentationOptions(formattingOptions, autoFormattingOptions.UnderlyingObject); + + return await formattingService.GetFormattingChangesAsync(document, typedChar, position, roslynIndentationOptions, cancellationToken).ConfigureAwait(false); } - /// - /// Returns the text changes necessary to format the document on paste operation. - /// - public static Task> GetFormattingChangesOnPasteAsync( - Document document, - TextSpan textSpan, - DocumentOptionSet? documentOptions, + public static IList GetFormattedTextChanges( + HostWorkspaceServices services, + SyntaxNode root, + TextSpan span, + RazorIndentationOptions indentationOptions, CancellationToken cancellationToken) { - Contract.ThrowIfFalse(document.Project.Language is LanguageNames.CSharp); - var formattingService = document.GetRequiredLanguageService(); - return formattingService.GetFormattingChangesOnPasteAsync(document, textSpan, documentOptions, cancellationToken); + Contract.ThrowIfFalse(root.Language is LanguageNames.CSharp); + return Formatter.GetFormattedTextChanges(root, span, services, GetFormattingOptions(indentationOptions), cancellationToken); } - /// - /// Returns the text changes necessary to format the document after the user enters a Return - /// The position provided is the position of the caret in the document after Return. - /// - public static Task> GetFormattingChangesOnReturnAsync( - Document document, - int position, - DocumentOptionSet? documentOptions, + public static SyntaxNode Format( + HostWorkspaceServices services, + SyntaxNode root, + RazorIndentationOptions indentationOptions, CancellationToken cancellationToken) { - Contract.ThrowIfFalse(document.Project.Language is LanguageNames.CSharp); - var formattingService = document.GetRequiredLanguageService(); - return formattingService.GetFormattingChangesOnReturnAsync(document, position, documentOptions, cancellationToken); + Contract.ThrowIfFalse(root.Language is LanguageNames.CSharp); + return Formatter.Format(root, services, GetFormattingOptions(indentationOptions), cancellationToken: cancellationToken); } + + private static SyntaxFormattingOptions GetFormattingOptions(RazorIndentationOptions indentationOptions) + => CSharpSyntaxFormattingOptions.Default.With( + useTabs: indentationOptions.UseTabs, + tabSize: indentationOptions.TabSize, + indentationSize: indentationOptions.IndentationSize); } } diff --git a/src/Tools/ExternalAccess/Razor/RazorGlobalOptions.cs b/src/Tools/ExternalAccess/Razor/RazorGlobalOptions.cs new file mode 100644 index 0000000000000..aff73c5aa9c12 --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/RazorGlobalOptions.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Composition; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Formatting; +using System.Linq; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor +{ + [Export(typeof(RazorGlobalOptions)), Shared] + internal sealed class RazorGlobalOptions + { + private readonly IGlobalOptionService _globalOptions; + + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public RazorGlobalOptions(IGlobalOptionService globalOptions) + { + _globalOptions = globalOptions; + } + + /// + /// For testing purposes. + /// + public static RazorGlobalOptions GetGlobalOptions(Workspace workspace) + => ((IMefHostExportProvider)workspace.Services.HostServices).GetExports().Single().Value; + + public RazorAutoFormattingOptions GetAutoFormattingOptions() + => new(_globalOptions.GetAutoFormattingOptions(LanguageNames.CSharp)); + } +} diff --git a/src/Tools/ExternalAccess/Razor/RazorIndentationOptions.cs b/src/Tools/ExternalAccess/Razor/RazorIndentationOptions.cs new file mode 100644 index 0000000000000..d8097d9737517 --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/RazorIndentationOptions.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor +{ + internal readonly record struct RazorIndentationOptions( + bool UseTabs, + int TabSize, + int IndentationSize); +} diff --git a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs index 16158626d8710..97f185ae57102 100644 --- a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs +++ b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs @@ -85,7 +85,7 @@ public AdvancedOptionPageControl(OptionStore optionStore, IComponentModel compon BindToOption(Show_guides_for_declaration_level_constructs, BlockStructureOptionsStorage.ShowBlockStructureGuidesForDeclarationLevelConstructs, LanguageNames.CSharp); BindToOption(Show_guides_for_code_level_constructs, BlockStructureOptionsStorage.ShowBlockStructureGuidesForCodeLevelConstructs, LanguageNames.CSharp); - BindToOption(GenerateXmlDocCommentsForTripleSlash, DocumentationCommentOptions.Metadata.AutoXmlDocCommentGeneration, LanguageNames.CSharp); + BindToOption(GenerateXmlDocCommentsForTripleSlash, DocumentationCommentOptionsStorage.AutoXmlDocCommentGeneration, LanguageNames.CSharp); BindToOption(InsertSlashSlashAtTheStartOfNewLinesWhenWritingSingleLineComments, SplitStringLiteralOptions.Enabled, LanguageNames.CSharp); BindToOption(InsertAsteriskAtTheStartOfNewLinesWhenWritingBlockComments, FeatureOnOffOptions.AutoInsertBlockCommentStartString, LanguageNames.CSharp); diff --git a/src/VisualStudio/CSharp/Impl/Options/AutomationObject/AutomationObject.BraceCompletion.cs b/src/VisualStudio/CSharp/Impl/Options/AutomationObject/AutomationObject.BraceCompletion.cs index 4e2bea903475a..08609e84d67c3 100644 --- a/src/VisualStudio/CSharp/Impl/Options/AutomationObject/AutomationObject.BraceCompletion.cs +++ b/src/VisualStudio/CSharp/Impl/Options/AutomationObject/AutomationObject.BraceCompletion.cs @@ -10,8 +10,8 @@ public partial class AutomationObject { public int Formatting_TriggerOnBlockCompletion { - get { return GetBooleanOption(AutoFormattingOptions.Metadata.AutoFormattingOnCloseBrace); } - set { SetBooleanOption(AutoFormattingOptions.Metadata.AutoFormattingOnCloseBrace, value); } + get { return GetBooleanOption(AutoFormattingOptionsStorage.FormatOnCloseBrace); } + set { SetBooleanOption(AutoFormattingOptionsStorage.FormatOnCloseBrace, value); } } } } diff --git a/src/VisualStudio/CSharp/Impl/Options/AutomationObject/AutomationObject.DocumentationComment.cs b/src/VisualStudio/CSharp/Impl/Options/AutomationObject/AutomationObject.DocumentationComment.cs index bbb6774dfce9a..07edfda789d98 100644 --- a/src/VisualStudio/CSharp/Impl/Options/AutomationObject/AutomationObject.DocumentationComment.cs +++ b/src/VisualStudio/CSharp/Impl/Options/AutomationObject/AutomationObject.DocumentationComment.cs @@ -10,8 +10,8 @@ public partial class AutomationObject { public int AutoComment { - get { return GetBooleanOption(DocumentationCommentOptions.Metadata.AutoXmlDocCommentGeneration); } - set { SetBooleanOption(DocumentationCommentOptions.Metadata.AutoXmlDocCommentGeneration, value); } + get { return GetBooleanOption(DocumentationCommentOptionsStorage.AutoXmlDocCommentGeneration); } + set { SetBooleanOption(DocumentationCommentOptionsStorage.AutoXmlDocCommentGeneration, value); } } } } diff --git a/src/VisualStudio/CSharp/Impl/Options/AutomationObject/AutomationObject.Formatting.cs b/src/VisualStudio/CSharp/Impl/Options/AutomationObject/AutomationObject.Formatting.cs index 237bb96935b39..3eb0b9da1ba1d 100644 --- a/src/VisualStudio/CSharp/Impl/Options/AutomationObject/AutomationObject.Formatting.cs +++ b/src/VisualStudio/CSharp/Impl/Options/AutomationObject/AutomationObject.Formatting.cs @@ -311,14 +311,14 @@ public int Formatting_TriggerOnPaste public int Formatting_TriggerOnStatementCompletion { - get { return GetBooleanOption(AutoFormattingOptions.Metadata.AutoFormattingOnSemicolon); } - set { SetBooleanOption(AutoFormattingOptions.Metadata.AutoFormattingOnSemicolon, value); } + get { return GetBooleanOption(AutoFormattingOptionsStorage.FormatOnSemicolon); } + set { SetBooleanOption(AutoFormattingOptionsStorage.FormatOnSemicolon, value); } } public int AutoFormattingOnTyping { - get { return GetBooleanOption(AutoFormattingOptions.Metadata.AutoFormattingOnTyping); } - set { SetBooleanOption(AutoFormattingOptions.Metadata.AutoFormattingOnTyping, value); } + get { return GetBooleanOption(AutoFormattingOptionsStorage.FormatOnTyping); } + set { SetBooleanOption(AutoFormattingOptionsStorage.FormatOnTyping, value); } } } } diff --git a/src/VisualStudio/CSharp/Impl/Options/Formatting/FormattingOptionPageControl.xaml.cs b/src/VisualStudio/CSharp/Impl/Options/Formatting/FormattingOptionPageControl.xaml.cs index 63d3f9f7544c9..ce557577dabdf 100644 --- a/src/VisualStudio/CSharp/Impl/Options/Formatting/FormattingOptionPageControl.xaml.cs +++ b/src/VisualStudio/CSharp/Impl/Options/Formatting/FormattingOptionPageControl.xaml.cs @@ -32,10 +32,10 @@ public FormattingOptionPageControl(OptionStore optionStore) : base(optionStore) FormatOnReturnCheckBox.Content = CSharpVSResources.Automatically_format_on_return; FormatOnPasteCheckBox.Content = CSharpVSResources.Automatically_format_on_paste; - BindToOption(FormatWhenTypingCheckBox, AutoFormattingOptions.Metadata.AutoFormattingOnTyping, LanguageNames.CSharp); - BindToOption(FormatOnCloseBraceCheckBox, AutoFormattingOptions.Metadata.AutoFormattingOnCloseBrace, LanguageNames.CSharp); - BindToOption(FormatOnSemicolonCheckBox, AutoFormattingOptions.Metadata.AutoFormattingOnSemicolon, LanguageNames.CSharp); - BindToOption(FormatOnReturnCheckBox, AutoFormattingOptions.Metadata.AutoFormattingOnReturn, LanguageNames.CSharp); + BindToOption(FormatWhenTypingCheckBox, AutoFormattingOptionsStorage.FormatOnTyping, LanguageNames.CSharp); + BindToOption(FormatOnCloseBraceCheckBox, AutoFormattingOptionsStorage.FormatOnCloseBrace, LanguageNames.CSharp); + BindToOption(FormatOnSemicolonCheckBox, AutoFormattingOptionsStorage.FormatOnSemicolon, LanguageNames.CSharp); + BindToOption(FormatOnReturnCheckBox, AutoFormattingOptionsStorage.FormatOnReturn, LanguageNames.CSharp); BindToOption(FormatOnPasteCheckBox, FormattingOptionsMetadata.FormatOnPaste, LanguageNames.CSharp); } } diff --git a/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs b/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs index 90c281549b465..e24212d375677 100644 --- a/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs +++ b/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs @@ -320,15 +320,16 @@ private async Task FormatDocumentCreatedFromTemplateAsync(IVsHierarchy hierarchy var forkedSolution = projectToAddTo.Solution.AddDocument(DocumentInfo.Create(documentId, filePath, loader: new FileTextLoader(filePath, defaultEncoding: null), filePath: filePath)); var addedDocument = forkedSolution.GetDocument(documentId)!; + var formattingOptions = await SyntaxFormattingOptions.FromDocumentAsync(addedDocument, cancellationToken).ConfigureAwait(true); + // Call out to various new document formatters to tweak what they want var formattingService = addedDocument.GetLanguageService(); if (formattingService is not null) { - addedDocument = await formattingService.FormatNewDocumentAsync(addedDocument, hintDocument: null, cancellationToken).ConfigureAwait(true); + addedDocument = await formattingService.FormatNewDocumentAsync(addedDocument, hintDocument: null, formattingOptions, cancellationToken).ConfigureAwait(true); } var rootToFormat = await addedDocument.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(true); - var formattingOptions = await SyntaxFormattingOptions.FromDocumentAsync(addedDocument, cancellationToken).ConfigureAwait(true); // Format document var unformattedText = await addedDocument.GetTextAsync(cancellationToken).ConfigureAwait(true); diff --git a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb index fc5084c04f87c..0bd8b030dcaf7 100644 --- a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb +++ b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb @@ -96,7 +96,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options BindToOption(Show_guides_for_code_level_constructs, BlockStructureOptionsStorage.ShowBlockStructureGuidesForCodeLevelConstructs, LanguageNames.VisualBasic) ' Comments - BindToOption(GenerateXmlDocCommentsForTripleApostrophes, DocumentationCommentOptions.Metadata.AutoXmlDocCommentGeneration, LanguageNames.VisualBasic) + BindToOption(GenerateXmlDocCommentsForTripleApostrophes, DocumentationCommentOptionsStorage.AutoXmlDocCommentGeneration, LanguageNames.VisualBasic) BindToOption(InsertApostropheAtTheStartOfNewLinesWhenWritingApostropheComments, SplitCommentOptions.Enabled, LanguageNames.VisualBasic) ' Editor help diff --git a/src/VisualStudio/VisualBasic/Impl/Options/AutomationObject/AutomationObject.DocumentationComment.vb b/src/VisualStudio/VisualBasic/Impl/Options/AutomationObject/AutomationObject.DocumentationComment.vb index c86201b189caa..ce8e742f389ff 100644 --- a/src/VisualStudio/VisualBasic/Impl/Options/AutomationObject/AutomationObject.DocumentationComment.vb +++ b/src/VisualStudio/VisualBasic/Impl/Options/AutomationObject/AutomationObject.DocumentationComment.vb @@ -8,10 +8,10 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options Partial Public Class AutomationObject Public Property AutoComment As Boolean Get - Return GetBooleanOption(DocumentationCommentOptions.Metadata.AutoXmlDocCommentGeneration) + Return GetBooleanOption(DocumentationCommentOptionsStorage.AutoXmlDocCommentGeneration) End Get Set(value As Boolean) - SetBooleanOption(DocumentationCommentOptions.Metadata.AutoXmlDocCommentGeneration, value) + SetBooleanOption(DocumentationCommentOptionsStorage.AutoXmlDocCommentGeneration, value) End Set End Property End Class diff --git a/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj b/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj index fb0483f25a55e..edae5aac3bae6 100644 --- a/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj +++ b/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj @@ -47,6 +47,7 @@ + diff --git a/src/Workspaces/Core/Portable/Formatting/AutoFormattingOptions.cs b/src/Workspaces/Core/Portable/Formatting/AutoFormattingOptions.cs index edc1df9610d0e..ecaa58d401bf8 100644 --- a/src/Workspaces/Core/Portable/Formatting/AutoFormattingOptions.cs +++ b/src/Workspaces/Core/Portable/Formatting/AutoFormattingOptions.cs @@ -2,73 +2,25 @@ // 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.Composition; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Options.Providers; - -namespace Microsoft.CodeAnalysis.Formatting +using System.Runtime.Serialization; + +namespace Microsoft.CodeAnalysis.Formatting; + +/// +/// Automatic formatting options. +/// +[DataContract] +internal readonly record struct AutoFormattingOptions( + [property: DataMember(Order = 0)] FormattingOptions.IndentStyle IndentStyle = FormattingOptions.IndentStyle.Smart, + [property: DataMember(Order = 1)] bool FormatOnReturn = true, + [property: DataMember(Order = 2)] bool FormatOnTyping = true, + [property: DataMember(Order = 3)] bool FormatOnSemicolon = true, + [property: DataMember(Order = 4)] bool FormatOnCloseBrace = true) { - /// - /// Solution-wide formatting options. - /// - internal readonly record struct AutoFormattingOptions( - FormattingOptions.IndentStyle IndentStyle, - bool FormatOnReturn, - bool FormatOnTyping, - bool FormatOnSemicolon, - bool FormatOnCloseBrace) + public AutoFormattingOptions() + : this(IndentStyle: FormattingOptions.IndentStyle.Smart) { - public static AutoFormattingOptions From(Project project) - => From(project.Solution.Options, project.Language); - - public static AutoFormattingOptions From(OptionSet options, string language) - => new( - IndentStyle: options.GetOption(Metadata.SmartIndent, language), - FormatOnReturn: options.GetOption(Metadata.AutoFormattingOnReturn, language), - FormatOnTyping: options.GetOption(Metadata.AutoFormattingOnTyping, language), - FormatOnSemicolon: options.GetOption(Metadata.AutoFormattingOnSemicolon, language), - FormatOnCloseBrace: options.GetOption(Metadata.AutoFormattingOnCloseBrace, language)); - - [ExportSolutionOptionProvider, Shared] - internal sealed class Metadata : IOptionProvider - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public Metadata() - { - } - - public ImmutableArray Options { get; } = ImmutableArray.Create( - SmartIndent, - AutoFormattingOnReturn, - AutoFormattingOnTyping, - AutoFormattingOnSemicolon, - AutoFormattingOnCloseBrace); - - private const string FeatureName = "FormattingOptions"; - - // This is also serialized by the Visual Studio-specific LanguageSettingsPersister - public static PerLanguageOption2 SmartIndent { get; } = - new(FeatureName, FormattingOptionGroups.IndentationAndSpacing, nameof(SmartIndent), defaultValue: FormattingOptions.IndentStyle.Smart); - - internal static readonly PerLanguageOption2 AutoFormattingOnReturn = - new(FeatureName, OptionGroup.Default, nameof(AutoFormattingOnReturn), defaultValue: true, - storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.Auto Formatting On Return")); - - public static readonly PerLanguageOption2 AutoFormattingOnTyping = - new(FeatureName, OptionGroup.Default, nameof(AutoFormattingOnTyping), defaultValue: true, - storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.Auto Formatting On Typing")); - - public static readonly PerLanguageOption2 AutoFormattingOnSemicolon = - new(FeatureName, OptionGroup.Default, nameof(AutoFormattingOnSemicolon), defaultValue: true, - storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.Auto Formatting On Semicolon")); - - public static readonly PerLanguageOption2 AutoFormattingOnCloseBrace = new( - "BraceCompletionOptions", nameof(AutoFormattingOnCloseBrace), defaultValue: true, - storageLocation: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.Auto Formatting On Close Brace")); - } } + + public static readonly AutoFormattingOptions Default = new(); } diff --git a/src/Workspaces/Core/Portable/Formatting/FormattingOptions.cs b/src/Workspaces/Core/Portable/Formatting/FormattingOptions.cs index 25ddf34780753..1fe0b8c37206c 100644 --- a/src/Workspaces/Core/Portable/Formatting/FormattingOptions.cs +++ b/src/Workspaces/Core/Portable/Formatting/FormattingOptions.cs @@ -21,12 +21,17 @@ public static partial class FormattingOptions // Suppression due to https://github.com/dotnet/roslyn/issues/42614 public static PerLanguageOption IndentationSize { get; } = ((PerLanguageOption)FormattingOptions2.IndentationSize)!; - /// - // Suppression due to https://github.com/dotnet/roslyn/issues/42614 - public static PerLanguageOption SmartIndent { get; } = ((PerLanguageOption)AutoFormattingOptions.Metadata.SmartIndent)!; - /// // Suppression due to https://github.com/dotnet/roslyn/issues/42614 public static PerLanguageOption NewLine { get; } = ((PerLanguageOption)FormattingOptions2.NewLine)!; + + // This is also serialized by the Visual Studio-specific LanguageSettingsPersister + + internal static readonly PerLanguageOption2 SmartIndent2 = new( + "FormattingOptions", FormattingOptionGroups.IndentationAndSpacing, "SmartIndent", defaultValue: IndentStyle.Smart); + + /// + // Suppression due to https://github.com/dotnet/roslyn/issues/42614 + public static PerLanguageOption SmartIndent { get; } = (PerLanguageOption)SmartIndent2!; } } diff --git a/src/Workspaces/Core/Portable/Indentation/AbstractIndentationService.cs b/src/Workspaces/Core/Portable/Indentation/AbstractIndentationService.cs index 3c6655866c34f..717cf7af07146 100644 --- a/src/Workspaces/Core/Portable/Indentation/AbstractIndentationService.cs +++ b/src/Workspaces/Core/Portable/Indentation/AbstractIndentationService.cs @@ -30,10 +30,10 @@ private IEnumerable GetFormattingRules(Document document } public IndentationResult GetIndentation( - Document document, int lineNumber, - FormattingOptions.IndentStyle indentStyle, CancellationToken cancellationToken) + Document document, int lineNumber, IndentationOptions options, CancellationToken cancellationToken) { - var indenter = GetIndenter(document, lineNumber, indentStyle, cancellationToken); + var indenter = GetIndenter(document, lineNumber, options, cancellationToken); + var indentStyle = options.AutoFormattingOptions.IndentStyle; if (indentStyle == FormattingOptions.IndentStyle.None) { @@ -51,15 +51,14 @@ public IndentationResult GetIndentation( return indenter.GetDesiredIndentation(indentStyle) ?? default; } - private Indenter GetIndenter(Document document, int lineNumber, FormattingOptions.IndentStyle indentStyle, CancellationToken cancellationToken) + private Indenter GetIndenter(Document document, int lineNumber, IndentationOptions options, CancellationToken cancellationToken) { - var options = IndentationOptions.FromDocumentAsync(document, cancellationToken).WaitAndGetResult_CanCallOnBackground(cancellationToken); var syntacticDoc = SyntacticDocument.CreateAsync(document, cancellationToken).WaitAndGetResult_CanCallOnBackground(cancellationToken); var sourceText = syntacticDoc.Root.SyntaxTree.GetText(cancellationToken); var lineToBeIndented = sourceText.Lines[lineNumber]; - var formattingRules = GetFormattingRules(document, lineToBeIndented.Start, indentStyle); + var formattingRules = GetFormattingRules(document, lineToBeIndented.Start, options.AutoFormattingOptions.IndentStyle); return new Indenter(this, syntacticDoc, formattingRules, options, lineToBeIndented, cancellationToken); } diff --git a/src/Workspaces/Core/Portable/Indentation/DefaultInferredIndentationService.cs b/src/Workspaces/Core/Portable/Indentation/DefaultInferredIndentationService.cs deleted file mode 100644 index df5b50dce3a25..0000000000000 --- a/src/Workspaces/Core/Portable/Indentation/DefaultInferredIndentationService.cs +++ /dev/null @@ -1,31 +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.Composition; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; - -namespace Microsoft.CodeAnalysis.Indentation -{ - [ExportWorkspaceService(typeof(IInferredIndentationService), ServiceLayer.Default), Shared] - internal sealed class DefaultInferredIndentationService - : IInferredIndentationService - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public DefaultInferredIndentationService() - { - } - - public Task GetDocumentOptionsWithInferredIndentationAsync(Document document, bool explicitFormat, CancellationToken cancellationToken) - { - // The workspaces layer doesn't have any smarts to infer spaces/tabs settings without an editorconfig, so just return - // the document's options. - return document.GetOptionsAsync(cancellationToken); - } - } -} diff --git a/src/Workspaces/Core/Portable/Indentation/IIndentationService.cs b/src/Workspaces/Core/Portable/Indentation/IIndentationService.cs index bded4bc6cc168..2f61835c62174 100644 --- a/src/Workspaces/Core/Portable/Indentation/IIndentationService.cs +++ b/src/Workspaces/Core/Portable/Indentation/IIndentationService.cs @@ -50,28 +50,16 @@ internal interface IIndentationService : ILanguageService /// /// Determines the desired indentation of a given line. /// - IndentationResult GetIndentation( - Document document, int lineNumber, - FormattingOptions.IndentStyle indentStyle, CancellationToken cancellationToken); + IndentationResult GetIndentation(Document document, int lineNumber, IndentationOptions options, CancellationToken cancellationToken); } internal static class IIndentationServiceExtensions { - public static IndentationResult GetIndentation( - this IIndentationService service, Document document, - int lineNumber, CancellationToken cancellationToken) - { - var options = document.GetOptionsAsync(cancellationToken).WaitAndGetResult_CanCallOnBackground(cancellationToken); - var style = options.GetOption(FormattingOptions.SmartIndent, document.Project.Language); - - return service.GetIndentation(document, lineNumber, style, cancellationToken); - } - /// /// Get's the preferred indentation for if that token were on its own line. This /// effectively simulates where the token would be if the user hit enter at the start of the token. /// - public static string GetPreferredIndentation(this SyntaxToken token, Document document, CancellationToken cancellationToken) + public static string GetPreferredIndentation(this SyntaxToken token, Document document, IndentationOptions options, CancellationToken cancellationToken) { var sourceText = document.GetTextSynchronously(cancellationToken); var tokenLine = sourceText.Lines.GetLineFromPosition(token.SpanStart); @@ -86,15 +74,11 @@ public static string GetPreferredIndentation(this SyntaxToken token, Document do // Token was on a line with something else. Determine where we would indent the token if it was on the next // line and use that to determine the indentation of the final line. - var options = document.Project.Solution.Options; - var languageName = document.Project.Language; - var newLine = options.GetOption(FormattingOptions.NewLine, languageName); - var annotation = new SyntaxAnnotation(); var newToken = token.WithAdditionalAnnotations(annotation); var syntaxGenerator = document.GetRequiredLanguageService(); - newToken = newToken.WithLeadingTrivia(newToken.LeadingTrivia.Add(syntaxGenerator.EndOfLine(newLine))); + newToken = newToken.WithLeadingTrivia(newToken.LeadingTrivia.Add(syntaxGenerator.EndOfLine(options.FormattingOptions.NewLine))); var root = document.GetRequiredSyntaxRootSynchronously(cancellationToken); var newRoot = root.ReplaceToken(token, newToken); @@ -103,15 +87,13 @@ public static string GetPreferredIndentation(this SyntaxToken token, Document do var newTokenLine = newText.Lines.GetLineFromPosition(newRoot.GetAnnotatedTokens(annotation).Single().SpanStart); - var indentStyle = document.Project.Solution.Options.GetOption(FormattingOptions.SmartIndent, languageName); var indenter = document.GetRequiredLanguageService(); - - var indentation = indenter.GetIndentation(newDocument, newTokenLine.LineNumber, indentStyle, cancellationToken); + var indentation = indenter.GetIndentation(newDocument, newTokenLine.LineNumber, options, cancellationToken); return indentation.GetIndentationString( newText, - options.GetOption(FormattingOptions.UseTabs, languageName), - options.GetOption(FormattingOptions.TabSize, languageName)); + options.FormattingOptions.UseTabs, + options.FormattingOptions.TabSize); } } diff --git a/src/Workspaces/Core/Portable/Indentation/IInferredIndentationService.cs b/src/Workspaces/Core/Portable/Indentation/IInferredIndentationService.cs deleted file mode 100644 index 1acc3d60ff49f..0000000000000 --- a/src/Workspaces/Core/Portable/Indentation/IInferredIndentationService.cs +++ /dev/null @@ -1,20 +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.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Options; - -namespace Microsoft.CodeAnalysis.Indentation -{ - /// - /// Gets the correct indentation to be used for the document. Depending on the host, there may be smarts to compensate for lack of an editorconfig if there - /// isn't one present. - /// - internal interface IInferredIndentationService : IWorkspaceService - { - Task GetDocumentOptionsWithInferredIndentationAsync(Document document, bool explicitFormat, CancellationToken cancellationToken); - } -} diff --git a/src/Workspaces/Core/Portable/Indentation/IndentationOptions.cs b/src/Workspaces/Core/Portable/Indentation/IndentationOptions.cs index b60fa4f7bc537..53da18d122bde 100644 --- a/src/Workspaces/Core/Portable/Indentation/IndentationOptions.cs +++ b/src/Workspaces/Core/Portable/Indentation/IndentationOptions.cs @@ -4,25 +4,13 @@ using System.Threading; using System.Threading.Tasks; +using System.Runtime.Serialization; using Microsoft.CodeAnalysis.Formatting; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Options; namespace Microsoft.CodeAnalysis.Indentation { + [DataContract] internal readonly record struct IndentationOptions( - SyntaxFormattingOptions FormattingOptions, - AutoFormattingOptions AutoFormattingOptions) - { - public static async Task FromDocumentAsync(Document document, CancellationToken cancellationToken) - { - var documentOptions = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); - return From(documentOptions, document.Project.Solution.Workspace.Services, document.Project.Language); - } - - public static IndentationOptions From(OptionSet options, HostWorkspaceServices services, string language) - => new( - SyntaxFormattingOptions.Create(options, services, language), - AutoFormattingOptions.From(options, language)); - } + [property: DataMember(Order = 0)] SyntaxFormattingOptions FormattingOptions, + [property: DataMember(Order = 1)] AutoFormattingOptions AutoFormattingOptions); } diff --git a/src/Workspaces/Core/Portable/Options/DocumentOptionSet.cs b/src/Workspaces/Core/Portable/Options/DocumentOptionSet.cs index 68f7362c24c13..1dba460027b2a 100644 --- a/src/Workspaces/Core/Portable/Options/DocumentOptionSet.cs +++ b/src/Workspaces/Core/Portable/Options/DocumentOptionSet.cs @@ -24,6 +24,8 @@ internal DocumentOptionSet(OptionSet backingOptionSet, string language) _language = language; } + internal string Language => _language; + private protected override object? GetOptionCore(OptionKey optionKey) => _backingOptionSet.GetOption(optionKey); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/CSharpSyntaxFormattingOptions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/CSharpSyntaxFormattingOptions.cs index 2f9451e7e3fff..79f0c214c8550 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/CSharpSyntaxFormattingOptions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/CSharpSyntaxFormattingOptions.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Runtime.Serialization; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; @@ -67,14 +68,28 @@ internal enum IndentationPlacement SwitchSection = 1 << 4 } + [DataContract] internal sealed class CSharpSyntaxFormattingOptions : SyntaxFormattingOptions { + [DataMember(Order = BaseMemberCount + 0)] public readonly SpacePlacement Spacing; + + [DataMember(Order = BaseMemberCount + 1)] public readonly BinaryOperatorSpacingOptions SpacingAroundBinaryOperator; + + [DataMember(Order = BaseMemberCount + 2)] public readonly NewLinePlacement NewLines; + + [DataMember(Order = BaseMemberCount + 3)] public readonly LabelPositionOptions LabelPositioning; + + [DataMember(Order = BaseMemberCount + 4)] public readonly IndentationPlacement Indentation; + + [DataMember(Order = BaseMemberCount + 5)] public readonly bool WrappingKeepStatementsOnSingleLine; + + [DataMember(Order = BaseMemberCount + 6)] public readonly bool WrappingPreserveSingleLine; public CSharpSyntaxFormattingOptions( @@ -218,5 +233,20 @@ public static CSharpSyntaxFormattingOptions Create(AnalyzerConfigOptions options (options.GetOption(CSharpFormattingOptions2.IndentSwitchSection) ? IndentationPlacement.SwitchSection : 0), wrappingKeepStatementsOnSingleLine: options.GetOption(CSharpFormattingOptions2.WrappingKeepStatementsOnSingleLine), wrappingPreserveSingleLine: options.GetOption(CSharpFormattingOptions2.WrappingPreserveSingleLine)); + + public override SyntaxFormattingOptions With(bool useTabs, int tabSize, int indentationSize) + => new CSharpSyntaxFormattingOptions( + useTabs: useTabs, + tabSize: tabSize, + indentationSize: indentationSize, + NewLine, + SeparateImportDirectiveGroups, + Spacing, + SpacingAroundBinaryOperator, + NewLines, + LabelPositioning, + Indentation, + WrappingKeepStatementsOnSingleLine, + WrappingPreserveSingleLine); } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/ISyntaxFormattingService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/ISyntaxFormattingService.cs index c83b980861bbe..ccf7dd4cf0fb5 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/ISyntaxFormattingService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/ISyntaxFormattingService.cs @@ -4,11 +4,11 @@ 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; +using System.Runtime.Serialization; +using System.Threading.Tasks; using Microsoft.CodeAnalysis.Options; #if !CODE_STYLE @@ -27,15 +27,26 @@ internal interface ISyntaxFormattingService IFormattingResult GetFormattingResult(SyntaxNode node, IEnumerable? spans, SyntaxFormattingOptions options, IEnumerable? rules, CancellationToken cancellationToken); } + [DataContract] internal abstract class SyntaxFormattingOptions { + [DataMember(Order = 0)] public readonly bool UseTabs; + + [DataMember(Order = 1)] public readonly int TabSize; + + [DataMember(Order = 2)] public readonly int IndentationSize; + + [DataMember(Order = 3)] public readonly string NewLine; + [DataMember(Order = 4)] public readonly bool SeparateImportDirectiveGroups; + protected const int BaseMemberCount = 5; + protected SyntaxFormattingOptions( bool useTabs, int tabSize, @@ -50,6 +61,8 @@ protected SyntaxFormattingOptions( SeparateImportDirectiveGroups = separateImportDirectiveGroups; } + public abstract SyntaxFormattingOptions With(bool useTabs, int tabSize, int indentationSize); + #if !CODE_STYLE public static SyntaxFormattingOptions Create(OptionSet options, HostWorkspaceServices services, string language) { diff --git a/src/Workspaces/VisualBasic/Portable/Formatting/VisualBasicSyntaxFormattingOptions.vb b/src/Workspaces/VisualBasic/Portable/Formatting/VisualBasicSyntaxFormattingOptions.vb index 8c7f1ee5c89f3..d1f004738a274 100644 --- a/src/Workspaces/VisualBasic/Portable/Formatting/VisualBasicSyntaxFormattingOptions.vb +++ b/src/Workspaces/VisualBasic/Portable/Formatting/VisualBasicSyntaxFormattingOptions.vb @@ -2,11 +2,13 @@ ' 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.Runtime.Serialization Imports Microsoft.CodeAnalysis.Diagnostics Imports Microsoft.CodeAnalysis.Editing Imports Microsoft.CodeAnalysis.Formatting Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting + Friend NotInheritable Class VisualBasicSyntaxFormattingOptions Inherits SyntaxFormattingOptions @@ -38,5 +40,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting newLine:=options.GetOption(FormattingOptions2.NewLine), separateImportDirectiveGroups:=options.GetOption(GenerationOptions.SeparateImportDirectiveGroups)) End Function + + Public Overrides Function [With](useTabs As Boolean, tabSize As Integer, indentationSize As Integer) As SyntaxFormattingOptions + Return New VisualBasicSyntaxFormattingOptions( + useTabs:=useTabs, + tabSize:=tabSize, + indentationSize:=indentationSize, + newLine:=NewLine, + separateImportDirectiveGroups:=SeparateImportDirectiveGroups) + End Function End Class End Namespace