From c7668f17389a0cf300d19c0ca0e7de9b752a35db Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 22 Oct 2024 13:55:33 -0700 Subject: [PATCH] Update wrapping refactoring provider to be more async --- .../Wrapping/AbstractCodeActionComputer.cs | 47 +++--- ...AbstractWrappingCodeRefactoringProvider.cs | 2 +- .../AbstractBinaryExpressionWrapper.cs | 3 +- .../BinaryExpressionCodeActionComputer.cs | 37 ++--- .../AbstractChainedExpressionWrapper.cs | 2 +- .../ChainedExpressionCodeActionComputer.cs | 47 +++--- .../Portable/Wrapping/ICodeActionComputer.cs | 3 +- .../AbstractSeparatedSyntaxListWrapper.cs | 2 +- .../SeparatedSyntaxListCodeActionComputer.cs | 136 ++++++++++-------- 9 files changed, 156 insertions(+), 123 deletions(-) diff --git a/src/Features/Core/Portable/Wrapping/AbstractCodeActionComputer.cs b/src/Features/Core/Portable/Wrapping/AbstractCodeActionComputer.cs index 095fdc38843b3..0d3f57f717f90 100644 --- a/src/Features/Core/Portable/Wrapping/AbstractCodeActionComputer.cs +++ b/src/Features/Core/Portable/Wrapping/AbstractCodeActionComputer.cs @@ -48,7 +48,6 @@ protected abstract class AbstractCodeActionComputer : ICodeActionCompu protected readonly Document OriginalDocument; protected readonly SourceText OriginalSourceText; - protected readonly CancellationToken CancellationToken; protected readonly SyntaxWrappingOptions Options; protected readonly SyntaxTriviaList NewLineTrivia; @@ -65,13 +64,11 @@ public AbstractCodeActionComputer( TWrapper service, Document document, SourceText originalSourceText, - SyntaxWrappingOptions options, - CancellationToken cancellationToken) + SyntaxWrappingOptions options) { Wrapper = service; OriginalDocument = document; OriginalSourceText = originalSourceText; - CancellationToken = cancellationToken; Options = options; var generator = SyntaxGenerator.GetGenerator(document); @@ -80,12 +77,13 @@ public AbstractCodeActionComputer( SingleWhitespaceTrivia = new SyntaxTriviaList(generator.Whitespace(" ")); } - protected abstract Task> ComputeWrappingGroupsAsync(); + protected abstract Task> ComputeWrappingGroupsAsync(CancellationToken cancellationToken); - protected string GetSmartIndentationAfter(SyntaxNodeOrToken nodeOrToken) - => GetIndentationAfter(nodeOrToken, FormattingOptions2.IndentStyle.Smart); + protected Task GetSmartIndentationAfterAsync(SyntaxNodeOrToken nodeOrToken, CancellationToken cancellationToken) + => GetIndentationAfterAsync(nodeOrToken, FormattingOptions2.IndentStyle.Smart, cancellationToken); - protected string GetIndentationAfter(SyntaxNodeOrToken nodeOrToken, FormattingOptions2.IndentStyle indentStyle) + protected async Task GetIndentationAfterAsync( + SyntaxNodeOrToken nodeOrToken, FormattingOptions2.IndentStyle indentStyle, CancellationToken cancellationToken) { var newLine = Options.FormattingOptions.NewLine; var newSourceText = OriginalSourceText.WithChanges( @@ -100,12 +98,12 @@ protected string GetIndentationAfter(SyntaxNodeOrToken nodeOrToken, FormattingOp var originalLineNumber = newSourceText.Lines.GetLineFromPosition(nodeOrToken.Span.End).LineNumber; // TODO: should be async https://github.com/dotnet/roslyn/issues/61998 - var newParsedDocument = ParsedDocument.CreateSynchronously(newDocument, CancellationToken); + var newParsedDocument = await ParsedDocument.CreateAsync(newDocument, cancellationToken).ConfigureAwait(false); var desiredIndentation = indentationService.GetIndentation( newParsedDocument, originalLineNumber + 1, indentationOptions, - CancellationToken); + cancellationToken); return desiredIndentation.GetIndentationString(newSourceText, Options.FormattingOptions.UseTabs, Options.FormattingOptions.TabSize); } @@ -119,10 +117,10 @@ protected string GetIndentationAfter(SyntaxNodeOrToken nodeOrToken, FormattingOp /// 3. A previous code action was created that already had the same effect. /// protected async Task TryCreateCodeActionAsync( - ImmutableArray edits, string parentTitle, string title) + ImmutableArray edits, string parentTitle, string title, CancellationToken cancellationToken) { // First, rewrite the tree with the edits provided. - var (root, rewrittenRoot, spanToFormat) = await RewriteTreeAsync(edits).ConfigureAwait(false); + var (root, rewrittenRoot, spanToFormat) = await RewriteTreeAsync(edits, cancellationToken).ConfigureAwait(false); if (rewrittenRoot == null) { // Couldn't rewrite for some reason. No code action to create. @@ -131,8 +129,8 @@ protected string GetIndentationAfter(SyntaxNodeOrToken nodeOrToken, FormattingOp // Now, format the part of the tree that we edited. This will ensure we properly // respect the user preferences around things like comma/operator spacing. - var formattedDocument = await FormatDocumentAsync(rewrittenRoot, spanToFormat).ConfigureAwait(false); - var formattedRoot = await formattedDocument.GetRequiredSyntaxRootAsync(CancellationToken).ConfigureAwait(false); + var formattedDocument = await FormatDocumentAsync(rewrittenRoot, spanToFormat, cancellationToken).ConfigureAwait(false); + var formattedRoot = await formattedDocument.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); // Now, check if this new formatted tree matches our starting tree, or any of the // trees we've already created for our other code actions. If so, we don't want to @@ -161,15 +159,17 @@ protected string GetIndentationAfter(SyntaxNodeOrToken nodeOrToken, FormattingOp return new WrapItemsAction(title, parentTitle, (_, _) => Task.FromResult(formattedDocument)); } - private async Task FormatDocumentAsync(SyntaxNode rewrittenRoot, TextSpan spanToFormat) + private async Task FormatDocumentAsync( + SyntaxNode rewrittenRoot, TextSpan spanToFormat, CancellationToken cancellationToken) { var newDocument = OriginalDocument.WithSyntaxRoot(rewrittenRoot); var formattedDocument = await Formatter.FormatAsync( - newDocument, spanToFormat, Options.FormattingOptions, CancellationToken).ConfigureAwait(false); + newDocument, spanToFormat, Options.FormattingOptions, cancellationToken).ConfigureAwait(false); return formattedDocument; } - private async Task<(SyntaxNode root, SyntaxNode rewrittenRoot, TextSpan spanToFormat)> RewriteTreeAsync(ImmutableArray edits) + private async Task<(SyntaxNode root, SyntaxNode rewrittenRoot, TextSpan spanToFormat)> RewriteTreeAsync( + ImmutableArray edits, CancellationToken cancellationToken) { using var _1 = PooledDictionary.GetInstance(out var leftTokenToTrailingTrivia); using var _2 = PooledDictionary.GetInstance(out var rightTokenToLeadingTrivia); @@ -200,7 +200,7 @@ private async Task FormatDocumentAsync(SyntaxNode rewrittenRoot, TextS } return await RewriteTreeAsync( - leftTokenToTrailingTrivia, rightTokenToLeadingTrivia).ConfigureAwait(false); + leftTokenToTrailingTrivia, rightTokenToLeadingTrivia, cancellationToken).ConfigureAwait(false); } private static bool IsSafeToRemove(string text) @@ -219,9 +219,10 @@ private static bool IsSafeToRemove(string text) private async Task<(SyntaxNode root, SyntaxNode rewrittenRoot, TextSpan spanToFormat)> RewriteTreeAsync( Dictionary leftTokenToTrailingTrivia, - Dictionary rightTokenToLeadingTrivia) + Dictionary rightTokenToLeadingTrivia, + CancellationToken cancellationToken) { - var root = await OriginalDocument.GetRequiredSyntaxRootAsync(CancellationToken).ConfigureAwait(false); + var root = await OriginalDocument.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var tokens = leftTokenToTrailingTrivia.Keys.Concat(rightTokenToLeadingTrivia.Keys).Distinct().ToImmutableArray(); // Find the closest node that contains all the tokens we're editing. That's the @@ -266,12 +267,12 @@ private static bool IsSafeToRemove(string text) return (root, rewrittenRoot, trackedNode.Span); } - public async Task> GetTopLevelCodeActionsAsync() + public async Task> GetTopLevelCodeActionsAsync(CancellationToken cancellationToken) { try { // Ask subclass to produce whole nested list of wrapping code actions - var wrappingGroups = await ComputeWrappingGroupsAsync().ConfigureAwait(false); + var wrappingGroups = await ComputeWrappingGroupsAsync(cancellationToken).ConfigureAwait(false); using var result = TemporaryArray.Empty; foreach (var group in wrappingGroups) @@ -306,7 +307,7 @@ public async Task> GetTopLevelCodeActionsAsync() // both the top level items and the nested items are ordered appropriate. return WrapItemsAction.SortActionsByMostRecentlyUsed(result.ToImmutableAndClear()); } - catch (Exception ex) when (FatalError.ReportAndCatchUnlessCanceled(ex, CancellationToken, ErrorSeverity.Diagnostic)) + catch (Exception ex) when (FatalError.ReportAndCatchUnlessCanceled(ex, cancellationToken, ErrorSeverity.Diagnostic)) { throw; } diff --git a/src/Features/Core/Portable/Wrapping/AbstractWrappingCodeRefactoringProvider.cs b/src/Features/Core/Portable/Wrapping/AbstractWrappingCodeRefactoringProvider.cs index eb3f4395eb587..8c397a29b66de 100644 --- a/src/Features/Core/Portable/Wrapping/AbstractWrappingCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/Wrapping/AbstractWrappingCodeRefactoringProvider.cs @@ -62,7 +62,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte if (computer == null) continue; - var actions = await computer.GetTopLevelCodeActionsAsync().ConfigureAwait(false); + var actions = await computer.GetTopLevelCodeActionsAsync(cancellationToken).ConfigureAwait(false); if (actions.IsDefaultOrEmpty) continue; diff --git a/src/Features/Core/Portable/Wrapping/BinaryExpression/AbstractBinaryExpressionWrapper.cs b/src/Features/Core/Portable/Wrapping/BinaryExpression/AbstractBinaryExpressionWrapper.cs index 7fa52f7c9a031..daa468be79408 100644 --- a/src/Features/Core/Portable/Wrapping/BinaryExpression/AbstractBinaryExpressionWrapper.cs +++ b/src/Features/Core/Portable/Wrapping/BinaryExpression/AbstractBinaryExpressionWrapper.cs @@ -89,8 +89,7 @@ protected AbstractBinaryExpressionWrapper( var sourceText = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); return new BinaryExpressionCodeActionComputer( - this, document, sourceText, options, binaryExpr, - exprsAndOperators, cancellationToken); + this, document, sourceText, options, binaryExpr, exprsAndOperators); } private ImmutableArray GetExpressionsAndOperators( diff --git a/src/Features/Core/Portable/Wrapping/BinaryExpression/BinaryExpressionCodeActionComputer.cs b/src/Features/Core/Portable/Wrapping/BinaryExpression/BinaryExpressionCodeActionComputer.cs index c489f4e9e46e7..3ca3307b28abc 100644 --- a/src/Features/Core/Portable/Wrapping/BinaryExpression/BinaryExpressionCodeActionComputer.cs +++ b/src/Features/Core/Portable/Wrapping/BinaryExpression/BinaryExpressionCodeActionComputer.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Wrapping.BinaryExpression; @@ -40,7 +41,7 @@ private sealed class BinaryExpressionCodeActionComputer : /// The indent trivia to insert if we are trying to simply smart-indent all wrapped /// parts of the expression. /// - private readonly SyntaxTriviaList _smartIndentTrivia; + private readonly AsyncLazy _smartIndentTrivia; public BinaryExpressionCodeActionComputer( AbstractBinaryExpressionWrapper service, @@ -48,9 +49,8 @@ public BinaryExpressionCodeActionComputer( SourceText originalSourceText, SyntaxWrappingOptions options, TBinaryExpressionSyntax binaryExpression, - ImmutableArray exprsAndOperators, - CancellationToken cancellationToken) - : base(service, document, originalSourceText, options, cancellationToken) + ImmutableArray exprsAndOperators) + : base(service, document, originalSourceText, options) { _exprsAndOperators = exprsAndOperators; @@ -62,30 +62,33 @@ public BinaryExpressionCodeActionComputer( OriginalSourceText.GetOffset(binaryExpression.Span.Start) .CreateIndentationString(options.FormattingOptions.UseTabs, options.FormattingOptions.TabSize))); - _smartIndentTrivia = new SyntaxTriviaList(generator.Whitespace( - GetSmartIndentationAfter(_exprsAndOperators[1]))); + _smartIndentTrivia = AsyncLazy.Create(async cancellationToken => new SyntaxTriviaList(generator.Whitespace( + await GetSmartIndentationAfterAsync(_exprsAndOperators[1], cancellationToken).ConfigureAwait(false)))); } - protected override async Task> ComputeWrappingGroupsAsync() + protected override async Task> ComputeWrappingGroupsAsync(CancellationToken cancellationToken) => [new WrappingGroup( isInlinable: true, [ - await GetWrapCodeActionAsync(align: false).ConfigureAwait(false), - await GetWrapCodeActionAsync(align: true).ConfigureAwait(false), - await GetUnwrapCodeActionAsync().ConfigureAwait(false), + await GetWrapCodeActionAsync(align: false, cancellationToken).ConfigureAwait(false), + await GetWrapCodeActionAsync(align: true, cancellationToken).ConfigureAwait(false), + await GetUnwrapCodeActionAsync(cancellationToken).ConfigureAwait(false), ])]; - private Task GetWrapCodeActionAsync(bool align) - => TryCreateCodeActionAsync(GetWrapEdits(align), FeaturesResources.Wrapping, - align ? FeaturesResources.Wrap_and_align_expression : FeaturesResources.Wrap_expression); + private async Task GetWrapCodeActionAsync(bool align, CancellationToken cancellationToken) + => await TryCreateCodeActionAsync(await GetWrapEditsAsync(align, cancellationToken).ConfigureAwait(false), FeaturesResources.Wrapping, + align ? FeaturesResources.Wrap_and_align_expression : FeaturesResources.Wrap_expression, + cancellationToken).ConfigureAwait(false); - private Task GetUnwrapCodeActionAsync() - => TryCreateCodeActionAsync(GetUnwrapEdits(), FeaturesResources.Wrapping, FeaturesResources.Unwrap_expression); + private Task GetUnwrapCodeActionAsync(CancellationToken cancellationToken) + => TryCreateCodeActionAsync(GetUnwrapEdits(), FeaturesResources.Wrapping, FeaturesResources.Unwrap_expression, cancellationToken); - private ImmutableArray GetWrapEdits(bool align) + private async Task> GetWrapEditsAsync(bool align, CancellationToken cancellationToken) { using var _ = ArrayBuilder.GetInstance(out var result); - var indentationTrivia = align ? _indentAndAlignTrivia : _smartIndentTrivia; + var indentationTrivia = align + ? _indentAndAlignTrivia + : await _smartIndentTrivia.GetValueAsync(cancellationToken).ConfigureAwait(false); for (var i = 1; i < _exprsAndOperators.Length; i += 2) { diff --git a/src/Features/Core/Portable/Wrapping/ChainedExpression/AbstractChainedExpressionWrapper.cs b/src/Features/Core/Portable/Wrapping/ChainedExpression/AbstractChainedExpressionWrapper.cs index a3204b82fab58..c3bda732fd699 100644 --- a/src/Features/Core/Portable/Wrapping/ChainedExpression/AbstractChainedExpressionWrapper.cs +++ b/src/Features/Core/Portable/Wrapping/ChainedExpression/AbstractChainedExpressionWrapper.cs @@ -110,7 +110,7 @@ protected AbstractChainedExpressionWrapper( // the set of wrapping options to provide. var sourceText = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); return new CallExpressionCodeActionComputer( - this, document, sourceText, options, chunks, cancellationToken); + this, document, sourceText, options, chunks); } private ImmutableArray> GetChainChunks(SyntaxNode node) diff --git a/src/Features/Core/Portable/Wrapping/ChainedExpression/ChainedExpressionCodeActionComputer.cs b/src/Features/Core/Portable/Wrapping/ChainedExpression/ChainedExpressionCodeActionComputer.cs index 69e99583a0b64..d6f1ea70295a2 100644 --- a/src/Features/Core/Portable/Wrapping/ChainedExpression/ChainedExpressionCodeActionComputer.cs +++ b/src/Features/Core/Portable/Wrapping/ChainedExpression/ChainedExpressionCodeActionComputer.cs @@ -62,16 +62,15 @@ private sealed class CallExpressionCodeActionComputer : /// The indent trivia to insert if we are trying to simply smart-indent all wrapped /// chunks. /// - private readonly SyntaxTriviaList _smartIndentTrivia; + private readonly AsyncLazy _smartIndentTrivia; public CallExpressionCodeActionComputer( AbstractChainedExpressionWrapper service, Document document, SourceText originalSourceText, SyntaxWrappingOptions options, - ImmutableArray> chunks, - CancellationToken cancellationToken) - : base(service, document, originalSourceText, options, cancellationToken) + ImmutableArray> chunks) + : base(service, document, originalSourceText, options) { _chunks = chunks; @@ -85,41 +84,49 @@ public CallExpressionCodeActionComputer( _firstPeriodIndentationTrivia = new SyntaxTriviaList(generator.Whitespace( OriginalSourceText.GetOffset(firstPeriod.SpanStart).CreateIndentationString(options.FormattingOptions.UseTabs, options.FormattingOptions.TabSize))); - _smartIndentTrivia = new SyntaxTriviaList(generator.Whitespace( - GetSmartIndentationAfter(firstPeriod))); + _smartIndentTrivia = AsyncLazy.Create(async cancellationToken => new SyntaxTriviaList(generator.Whitespace( + await GetSmartIndentationAfterAsync(firstPeriod, cancellationToken).ConfigureAwait(false)))); _newlineBeforeOperatorTrivia = service.GetNewLineBeforeOperatorTrivia(NewLineTrivia); } - protected override async Task> ComputeWrappingGroupsAsync() + protected override async Task> ComputeWrappingGroupsAsync(CancellationToken cancellationToken) { using var _ = ArrayBuilder.GetInstance(out var actions); - await AddWrapCodeActionAsync(actions).ConfigureAwait(false); - await AddUnwrapCodeActionAsync(actions).ConfigureAwait(false); - await AddWrapLongCodeActionAsync(actions).ConfigureAwait(false); + await AddWrapCodeActionAsync(actions, cancellationToken).ConfigureAwait(false); + await AddUnwrapCodeActionAsync(actions, cancellationToken).ConfigureAwait(false); + await AddWrapLongCodeActionAsync(actions, cancellationToken).ConfigureAwait(false); return [new WrappingGroup(isInlinable: true, actions.ToImmutable())]; } // Pass 0 as the wrapping column as we effectively always want to wrap each chunk // Not just when the chunk would go past the wrapping column. - private async Task AddWrapCodeActionAsync(ArrayBuilder actions) + private async Task AddWrapCodeActionAsync(ArrayBuilder actions, CancellationToken cancellationToken) { - actions.Add(await TryCreateCodeActionAsync(GetWrapEdits(wrappingColumn: 0, align: false), FeaturesResources.Wrapping, FeaturesResources.Wrap_call_chain).ConfigureAwait(false)); - actions.Add(await TryCreateCodeActionAsync(GetWrapEdits(wrappingColumn: 0, align: true), FeaturesResources.Wrapping, FeaturesResources.Wrap_and_align_call_chain).ConfigureAwait(false)); + actions.Add(await TryCreateCodeActionAsync( + await GetWrapEditsAsync(wrappingColumn: 0, align: false, cancellationToken).ConfigureAwait(false), + FeaturesResources.Wrapping, FeaturesResources.Wrap_call_chain, cancellationToken).ConfigureAwait(false)); + actions.Add(await TryCreateCodeActionAsync( + await GetWrapEditsAsync(wrappingColumn: 0, align: true, cancellationToken).ConfigureAwait(false), + FeaturesResources.Wrapping, FeaturesResources.Wrap_and_align_call_chain, cancellationToken).ConfigureAwait(false)); } - private async Task AddUnwrapCodeActionAsync(ArrayBuilder actions) - => actions.Add(await TryCreateCodeActionAsync(GetUnwrapEdits(), FeaturesResources.Wrapping, FeaturesResources.Unwrap_call_chain).ConfigureAwait(false)); + private async Task AddUnwrapCodeActionAsync(ArrayBuilder actions, CancellationToken cancellationToken) + => actions.Add(await TryCreateCodeActionAsync(GetUnwrapEdits(), FeaturesResources.Wrapping, FeaturesResources.Unwrap_call_chain, cancellationToken).ConfigureAwait(false)); - private async Task AddWrapLongCodeActionAsync(ArrayBuilder actions) + private async Task AddWrapLongCodeActionAsync(ArrayBuilder actions, CancellationToken cancellationToken) { - actions.Add(await TryCreateCodeActionAsync(GetWrapEdits(Options.WrappingColumn, align: false), FeaturesResources.Wrapping, FeaturesResources.Wrap_long_call_chain).ConfigureAwait(false)); - actions.Add(await TryCreateCodeActionAsync(GetWrapEdits(Options.WrappingColumn, align: true), FeaturesResources.Wrapping, FeaturesResources.Wrap_and_align_long_call_chain).ConfigureAwait(false)); + actions.Add(await TryCreateCodeActionAsync( + await GetWrapEditsAsync(Options.WrappingColumn, align: false, cancellationToken).ConfigureAwait(false), + FeaturesResources.Wrapping, FeaturesResources.Wrap_long_call_chain, cancellationToken).ConfigureAwait(false)); + actions.Add(await TryCreateCodeActionAsync( + await GetWrapEditsAsync(Options.WrappingColumn, align: true, cancellationToken).ConfigureAwait(false), + FeaturesResources.Wrapping, FeaturesResources.Wrap_and_align_long_call_chain, cancellationToken).ConfigureAwait(false)); } - private ImmutableArray GetWrapEdits(int wrappingColumn, bool align) + private async Task> GetWrapEditsAsync(int wrappingColumn, bool align, CancellationToken cancellationToken) { using var _ = ArrayBuilder.GetInstance(out var result); @@ -127,7 +134,7 @@ private ImmutableArray GetWrapEdits(int wrappingColumn, bool align) var firstChunk = _chunks[0]; DeleteAllSpacesInChunk(result, firstChunk); - var indentationTrivia = align ? _firstPeriodIndentationTrivia : _smartIndentTrivia; + var indentationTrivia = align ? _firstPeriodIndentationTrivia : await _smartIndentTrivia.GetValueAsync(cancellationToken).ConfigureAwait(false); // Our starting position is at the end of the first chunk. That position // is effectively the start of the first period, plus the length of the diff --git a/src/Features/Core/Portable/Wrapping/ICodeActionComputer.cs b/src/Features/Core/Portable/Wrapping/ICodeActionComputer.cs index 8afbf75a6175c..60c6360d531c6 100644 --- a/src/Features/Core/Portable/Wrapping/ICodeActionComputer.cs +++ b/src/Features/Core/Portable/Wrapping/ICodeActionComputer.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; +using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; @@ -13,5 +14,5 @@ internal interface ICodeActionComputer /// /// Produces the actual top-level code wrapping actions for the original node provided. /// - Task> GetTopLevelCodeActionsAsync(); + Task> GetTopLevelCodeActionsAsync(CancellationToken cancellationToken); } diff --git a/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/AbstractSeparatedSyntaxListWrapper.cs b/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/AbstractSeparatedSyntaxListWrapper.cs index 465b37dc2ab19..4c98bafb725e2 100644 --- a/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/AbstractSeparatedSyntaxListWrapper.cs +++ b/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/AbstractSeparatedSyntaxListWrapper.cs @@ -83,6 +83,6 @@ protected abstract bool PositionIsApplicable( var sourceText = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); return new SeparatedSyntaxListCodeActionComputer( - this, document, sourceText, options, listSyntax, listItems, cancellationToken); + this, document, sourceText, options, listSyntax, listItems); } } diff --git a/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/SeparatedSyntaxListCodeActionComputer.cs b/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/SeparatedSyntaxListCodeActionComputer.cs index 0c2ffd4329486..2c6276e32b4e4 100644 --- a/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/SeparatedSyntaxListCodeActionComputer.cs +++ b/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/SeparatedSyntaxListCodeActionComputer.cs @@ -46,7 +46,7 @@ private sealed class SeparatedSyntaxListCodeActionComputer : AbstractCodeActionC /// ^ /// | /// - private readonly Lazy _singleIndentationTrivia; + private readonly AsyncLazy _singleIndentationTrivia; /// /// Indentation to use when placing brace. e.g.: @@ -55,7 +55,7 @@ private sealed class SeparatedSyntaxListCodeActionComputer : AbstractCodeActionC /// ^ /// | /// - private readonly Lazy _braceIndentationTrivia; + private readonly AsyncLazy _braceIndentationTrivia; /// /// Whether or not we should move the open brace of this separated list to a new line. Many separated lists @@ -77,9 +77,8 @@ public SeparatedSyntaxListCodeActionComputer( SourceText sourceText, SyntaxWrappingOptions options, TListSyntax listSyntax, - SeparatedSyntaxList listItems, - CancellationToken cancellationToken) - : base(service, document, sourceText, options, cancellationToken) + SeparatedSyntaxList listItems) + : base(service, document, sourceText, options) { _listSyntax = listSyntax; _listItems = listItems; @@ -90,15 +89,15 @@ public SeparatedSyntaxListCodeActionComputer( var generator = SyntaxGenerator.GetGenerator(OriginalDocument); _afterOpenTokenIndentationTrivia = generator.Whitespace(GetAfterOpenTokenIndentation()); - _singleIndentationTrivia = new Lazy(() => generator.Whitespace(GetSingleIndentation())); - _braceIndentationTrivia = new Lazy(() => generator.Whitespace(GetBraceTokenIndentation())); + _singleIndentationTrivia = AsyncLazy.Create(async cancellationToken => generator.Whitespace(await GetSingleIndentationAsync(cancellationToken).ConfigureAwait(false))); + _braceIndentationTrivia = AsyncLazy.Create(async cancellationToken => generator.Whitespace(await GetBraceTokenIndentationAsync(cancellationToken).ConfigureAwait(false))); } - private void AddTextChangeBetweenOpenAndFirstItem( - WrappingStyle wrappingStyle, ArrayBuilder result) + private async Task AddTextChangeBetweenOpenAndFirstItemAsync( + WrappingStyle wrappingStyle, ArrayBuilder result, CancellationToken cancellationToken) { result.Add(wrappingStyle == WrappingStyle.WrapFirst_IndentRest - ? Edit.UpdateBetween(_listSyntax.GetFirstToken(), NewLineTrivia, _singleIndentationTrivia.Value, _listItems[0]) + ? Edit.UpdateBetween(_listSyntax.GetFirstToken(), NewLineTrivia, await _singleIndentationTrivia.GetValueAsync(cancellationToken).ConfigureAwait(false), _listItems[0]) : Edit.DeleteBetween(_listSyntax.GetFirstToken(), _listItems[0])); } @@ -111,48 +110,49 @@ private string GetAfterOpenTokenIndentation() return indentString; } - private string GetSingleIndentation() + private Task GetSingleIndentationAsync(CancellationToken cancellationToken) { // Insert a newline after the open token of the list. Then ask the // ISynchronousIndentationService where it thinks that the next line should be // indented. var openToken = _listSyntax.GetFirstToken(); - return GetSmartIndentationAfter(openToken); + return GetSmartIndentationAfterAsync(openToken, cancellationToken); } - private SyntaxTrivia GetIndentationTrivia(WrappingStyle wrappingStyle) + private async Task GetIndentationTriviaAsync(WrappingStyle wrappingStyle, CancellationToken cancellationToken) { return wrappingStyle == WrappingStyle.UnwrapFirst_AlignRest ? _afterOpenTokenIndentationTrivia - : _singleIndentationTrivia.Value; + : await _singleIndentationTrivia.GetValueAsync(cancellationToken).ConfigureAwait(false); } - private string GetBraceTokenIndentation() + private Task GetBraceTokenIndentationAsync(CancellationToken cancellationToken) { var previousToken = _listSyntax.GetFirstToken().GetPreviousToken(); // Block indentation is the only style that correctly indents across all initializer expressions - return GetIndentationAfter(previousToken, FormattingOptions2.IndentStyle.Block); + return GetIndentationAfterAsync(previousToken, FormattingOptions2.IndentStyle.Block, cancellationToken); } - protected override async Task> ComputeWrappingGroupsAsync() + protected override async Task> ComputeWrappingGroupsAsync(CancellationToken cancellationToken) { using var _ = ArrayBuilder.GetInstance(out var result); - await AddWrappingGroupsAsync(result).ConfigureAwait(false); + await AddWrappingGroupsAsync(result, cancellationToken).ConfigureAwait(false); return result.ToImmutableAndClear(); } - private async Task AddWrappingGroupsAsync(ArrayBuilder result) + private async Task AddWrappingGroupsAsync( + ArrayBuilder result, CancellationToken cancellationToken) { - result.Add(await GetWrapEveryGroupAsync().ConfigureAwait(false)); - result.Add(await GetUnwrapGroupAsync().ConfigureAwait(false)); - result.Add(await GetWrapLongGroupAsync().ConfigureAwait(false)); + result.Add(await GetWrapEveryGroupAsync(cancellationToken).ConfigureAwait(false)); + result.Add(await GetUnwrapGroupAsync(cancellationToken).ConfigureAwait(false)); + result.Add(await GetWrapLongGroupAsync(cancellationToken).ConfigureAwait(false)); } #region unwrap group - private async Task GetUnwrapGroupAsync() + private async Task GetUnwrapGroupAsync(CancellationToken cancellationToken) { using var _ = ArrayBuilder.GetInstance(out var unwrapActions); @@ -160,7 +160,8 @@ private async Task GetUnwrapGroupAsync() // Unwrap entirely. // MethodName(int a, int b, int c, int d, int e, int f, int g, int h, int i, int j) - unwrapActions.AddIfNotNull(await GetUnwrapAllCodeActionAsync(parentTitle, WrappingStyle.UnwrapFirst_IndentRest).ConfigureAwait(false)); + unwrapActions.AddIfNotNull(await GetUnwrapAllCodeActionAsync( + parentTitle, WrappingStyle.UnwrapFirst_IndentRest, cancellationToken).ConfigureAwait(false)); if (this.Wrapper.Supports_UnwrapGroup_WrapFirst_IndentRest) { @@ -168,7 +169,8 @@ private async Task GetUnwrapGroupAsync() // int a, int b, int c, int d, int e, int f, int g, int h, int i, int j) // // Unwrap the items, adjusting the braces as well. - unwrapActions.AddIfNotNull(await GetUnwrapAllCodeActionAsync(parentTitle, WrappingStyle.WrapFirst_IndentRest).ConfigureAwait(false)); + unwrapActions.AddIfNotNull(await GetUnwrapAllCodeActionAsync( + parentTitle, WrappingStyle.WrapFirst_IndentRest, cancellationToken).ConfigureAwait(false)); } // { @@ -177,7 +179,7 @@ private async Task GetUnwrapGroupAsync() // // without adjusting the braces. var unwrapWithoutBraces = await GetWrapLongLineCodeActionAsync( - parentTitle, WrappingStyle.WrapFirst_IndentRest, wrappingColumn: int.MaxValue).ConfigureAwait(false); + parentTitle, WrappingStyle.WrapFirst_IndentRest, wrappingColumn: int.MaxValue, cancellationToken).ConfigureAwait(false); unwrapActions.AddIfNotNull(unwrapWithoutBraces); // The first two unwrap options share no title with anything else (so they can be inlined). However, @@ -187,24 +189,26 @@ private async Task GetUnwrapGroupAsync() return new WrappingGroup(isInlinable, unwrapActions.ToImmutableAndClear()); } - private async Task GetUnwrapAllCodeActionAsync(string parentTitle, WrappingStyle wrappingStyle) + private async Task GetUnwrapAllCodeActionAsync( + string parentTitle, WrappingStyle wrappingStyle, CancellationToken cancellationToken) { - var edits = GetUnwrapAllEdits(wrappingStyle); + var edits = await GetUnwrapAllEditsAsync(wrappingStyle, cancellationToken).ConfigureAwait(false); var title = wrappingStyle == WrappingStyle.WrapFirst_IndentRest ? Wrapper.Unwrap_and_indent_all_items : Wrapper.Unwrap_all_items; - return await TryCreateCodeActionAsync(edits, parentTitle, title).ConfigureAwait(false); + return await TryCreateCodeActionAsync(edits, parentTitle, title, cancellationToken).ConfigureAwait(false); } - private ImmutableArray GetUnwrapAllEdits(WrappingStyle wrappingStyle) + private async Task> GetUnwrapAllEditsAsync(WrappingStyle wrappingStyle, CancellationToken cancellationToken) { using var _ = ArrayBuilder.GetInstance(out var result); if (_shouldMoveOpenBraceToNewLine) result.Add(Edit.DeleteBetween(_listSyntax.GetFirstToken().GetPreviousToken(), _listSyntax.GetFirstToken())); - AddTextChangeBetweenOpenAndFirstItem(wrappingStyle, result); + await AddTextChangeBetweenOpenAndFirstItemAsync( + wrappingStyle, result, cancellationToken).ConfigureAwait(false); foreach (var comma in _listItems.GetSeparators()) { @@ -223,7 +227,7 @@ private ImmutableArray GetUnwrapAllEdits(WrappingStyle wrappingStyle) #region wrap long line - private async Task GetWrapLongGroupAsync() + private async Task GetWrapLongGroupAsync(CancellationToken cancellationToken) { var parentTitle = Wrapper.Wrap_long_list; using var _ = ArrayBuilder.GetInstance(out var codeActions); @@ -237,14 +241,14 @@ private async Task GetWrapLongGroupAsync() // int g, int h, int i, // int j) codeActions.AddIfNotNull(await GetWrapLongLineCodeActionAsync( - parentTitle, WrappingStyle.UnwrapFirst_AlignRest, wrappingColumn).ConfigureAwait(false)); + parentTitle, WrappingStyle.UnwrapFirst_AlignRest, wrappingColumn, cancellationToken).ConfigureAwait(false)); } // MethodName( // int a, int b, int c, int d, int e, // int f, int g, int h, int i, int j) codeActions.AddIfNotNull(await GetWrapLongLineCodeActionAsync( - parentTitle, WrappingStyle.WrapFirst_IndentRest, wrappingColumn).ConfigureAwait(false)); + parentTitle, WrappingStyle.WrapFirst_IndentRest, wrappingColumn, cancellationToken).ConfigureAwait(false)); if (this.Wrapper.Supports_WrapLongGroup_UnwrapFirst) { @@ -252,7 +256,7 @@ private async Task GetWrapLongGroupAsync() // int d, int e, int f, int g, // int h, int i, int j) codeActions.AddIfNotNull(await GetWrapLongLineCodeActionAsync( - parentTitle, WrappingStyle.UnwrapFirst_IndentRest, wrappingColumn).ConfigureAwait(false)); + parentTitle, WrappingStyle.UnwrapFirst_IndentRest, wrappingColumn, cancellationToken).ConfigureAwait(false)); } // The wrap-all and wrap-long code action titles are not unique. i.e. we show them @@ -271,25 +275,32 @@ private async Task GetWrapLongGroupAsync() } private async Task GetWrapLongLineCodeActionAsync( - string parentTitle, WrappingStyle wrappingStyle, int wrappingColumn) + string parentTitle, WrappingStyle wrappingStyle, int wrappingColumn, CancellationToken cancellationToken) { - var indentationTrivia = GetIndentationTrivia(wrappingStyle); + var indentationTrivia = await GetIndentationTriviaAsync(wrappingStyle, cancellationToken).ConfigureAwait(false); - var edits = GetWrapLongLinesEdits(wrappingStyle, indentationTrivia, wrappingColumn); + var edits = await GetWrapLongLinesEditsAsync( + wrappingStyle, indentationTrivia, wrappingColumn, cancellationToken).ConfigureAwait(false); var title = GetNestedCodeActionTitle(wrappingStyle); - return await TryCreateCodeActionAsync(edits, parentTitle, title).ConfigureAwait(false); + return await TryCreateCodeActionAsync(edits, parentTitle, title, cancellationToken).ConfigureAwait(false); } - private ImmutableArray GetWrapLongLinesEdits( - WrappingStyle wrappingStyle, SyntaxTrivia indentationTrivia, int wrappingColumn) + private async Task> GetWrapLongLinesEditsAsync( + WrappingStyle wrappingStyle, SyntaxTrivia indentationTrivia, int wrappingColumn, CancellationToken cancellationToken) { using var _ = ArrayBuilder.GetInstance(out var result); if (_shouldMoveOpenBraceToNewLine) - result.Add(Edit.UpdateBetween(_listSyntax.GetFirstToken().GetPreviousToken(), NewLineTrivia, _braceIndentationTrivia.Value, _listSyntax.GetFirstToken())); + { + result.Add(Edit.UpdateBetween( + _listSyntax.GetFirstToken().GetPreviousToken(), NewLineTrivia, + await _braceIndentationTrivia.GetValueAsync(cancellationToken).ConfigureAwait(false), + _listSyntax.GetFirstToken())); + } - AddTextChangeBetweenOpenAndFirstItem(wrappingStyle, result); + await AddTextChangeBetweenOpenAndFirstItemAsync( + wrappingStyle, result, cancellationToken).ConfigureAwait(false); var currentOffset = wrappingStyle == WrappingStyle.WrapFirst_IndentRest ? indentationTrivia.FullWidth() @@ -334,7 +345,10 @@ private ImmutableArray GetWrapLongLinesEdits( if (this.Wrapper.ShouldMoveCloseBraceToNewLine) { - result.Add(Edit.UpdateBetween(itemsAndSeparators.Last(), NewLineTrivia, _braceIndentationTrivia.Value, _listSyntax.GetLastToken())); + result.Add(Edit.UpdateBetween( + itemsAndSeparators.Last(), NewLineTrivia, + await _braceIndentationTrivia.GetValueAsync(cancellationToken).ConfigureAwait(false), + _listSyntax.GetLastToken())); } else { @@ -348,7 +362,7 @@ private ImmutableArray GetWrapLongLinesEdits( #region wrap every - private async Task GetWrapEveryGroupAsync() + private async Task GetWrapEveryGroupAsync(CancellationToken cancellationToken) { var parentTitle = Wrapper.Wrap_every_item; @@ -361,7 +375,7 @@ private async Task GetWrapEveryGroupAsync() // ... // int j); codeActions.AddIfNotNull(await GetWrapEveryNestedCodeActionAsync( - parentTitle, WrappingStyle.UnwrapFirst_AlignRest).ConfigureAwait(false)); + parentTitle, WrappingStyle.UnwrapFirst_AlignRest, cancellationToken).ConfigureAwait(false)); } // MethodName( @@ -370,7 +384,7 @@ private async Task GetWrapEveryGroupAsync() // ... // int j) codeActions.AddIfNotNull(await GetWrapEveryNestedCodeActionAsync( - parentTitle, WrappingStyle.WrapFirst_IndentRest).ConfigureAwait(false)); + parentTitle, WrappingStyle.WrapFirst_IndentRest, cancellationToken).ConfigureAwait(false)); if (this.Wrapper.Supports_WrapEveryGroup_UnwrapFirst) { @@ -379,7 +393,7 @@ private async Task GetWrapEveryGroupAsync() // ... // int j) codeActions.AddIfNotNull(await GetWrapEveryNestedCodeActionAsync( - parentTitle, WrappingStyle.UnwrapFirst_IndentRest).ConfigureAwait(false)); + parentTitle, WrappingStyle.UnwrapFirst_IndentRest, cancellationToken).ConfigureAwait(false)); } // See comment in GetWrapLongTopLevelCodeActionAsync for explanation of why we're @@ -388,14 +402,14 @@ private async Task GetWrapEveryGroupAsync() } private async Task GetWrapEveryNestedCodeActionAsync( - string parentTitle, WrappingStyle wrappingStyle) + string parentTitle, WrappingStyle wrappingStyle, CancellationToken cancellationToken) { - var indentationTrivia = GetIndentationTrivia(wrappingStyle); + var indentationTrivia = await GetIndentationTriviaAsync(wrappingStyle, cancellationToken).ConfigureAwait(false); - var edits = GetWrapEachEdits(wrappingStyle, indentationTrivia); + var edits = await GetWrapEachEditsAsync(wrappingStyle, indentationTrivia, cancellationToken).ConfigureAwait(false); var title = GetNestedCodeActionTitle(wrappingStyle); - return await TryCreateCodeActionAsync(edits, parentTitle, title).ConfigureAwait(false); + return await TryCreateCodeActionAsync(edits, parentTitle, title, cancellationToken).ConfigureAwait(false); } private string GetNestedCodeActionTitle(WrappingStyle wrappingStyle) @@ -407,15 +421,20 @@ private string GetNestedCodeActionTitle(WrappingStyle wrappingStyle) _ => throw ExceptionUtilities.UnexpectedValue(wrappingStyle), }; - private ImmutableArray GetWrapEachEdits( - WrappingStyle wrappingStyle, SyntaxTrivia indentationTrivia) + private async Task> GetWrapEachEditsAsync( + WrappingStyle wrappingStyle, SyntaxTrivia indentationTrivia, CancellationToken cancellationToken) { using var _ = ArrayBuilder.GetInstance(out var result); if (_shouldMoveOpenBraceToNewLine) - result.Add(Edit.UpdateBetween(_listSyntax.GetFirstToken().GetPreviousToken(), NewLineTrivia, _braceIndentationTrivia.Value, _listSyntax.GetFirstToken())); + { + result.Add(Edit.UpdateBetween( + _listSyntax.GetFirstToken().GetPreviousToken(), NewLineTrivia, + await _braceIndentationTrivia.GetValueAsync(cancellationToken).ConfigureAwait(false), + _listSyntax.GetFirstToken())); + } - AddTextChangeBetweenOpenAndFirstItem(wrappingStyle, result); + await AddTextChangeBetweenOpenAndFirstItemAsync(wrappingStyle, result, cancellationToken).ConfigureAwait(false); var itemsAndSeparators = _listItems.GetWithSeparators(); @@ -436,7 +455,10 @@ private ImmutableArray GetWrapEachEdits( if (_shouldMoveCloseBraceToNewLine) { - result.Add(Edit.UpdateBetween(itemsAndSeparators.Last(), NewLineTrivia, _braceIndentationTrivia.Value, _listSyntax.GetLastToken())); + result.Add(Edit.UpdateBetween( + itemsAndSeparators.Last(), NewLineTrivia, + await _braceIndentationTrivia.GetValueAsync(cancellationToken).ConfigureAwait(false), + _listSyntax.GetLastToken())); } else {