| File: Wrapping\BinaryExpression\BinaryExpressionCodeActionComputer.cs | Web Access |
| Project: src\roslyn\src\Features\Core\Portable\Microsoft.CodeAnalysis.Features.csproj (Microsoft.CodeAnalysis.Features) |
// 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. #nullable disable using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Wrapping.BinaryExpression; internal abstract partial class AbstractBinaryExpressionWrapper<TBinaryExpressionSyntax> { private sealed class BinaryExpressionCodeActionComputer : AbstractCodeActionComputer<AbstractBinaryExpressionWrapper<TBinaryExpressionSyntax>> { private readonly ImmutableArray<SyntaxNodeOrToken> _exprsAndOperators; /// <summary> /// trivia to place at the end of a node prior to a chunk that is wrapped. /// For C# this will just be a newline. For VB this will include a line- /// continuation character. /// </summary> private readonly SyntaxTriviaList _newlineBeforeOperatorTrivia; /// <summary> /// The indent trivia to insert if we are trying to align wrapped code with the /// start of the original expression. /// </summary> private readonly SyntaxTriviaList _indentAndAlignTrivia; /// <summary> /// The indent trivia to insert if we are trying to simply smart-indent all wrapped /// parts of the expression. /// </summary> private readonly AsyncLazy<SyntaxTriviaList> _smartIndentTrivia; public BinaryExpressionCodeActionComputer( AbstractBinaryExpressionWrapper<TBinaryExpressionSyntax> service, Document document, SourceText originalSourceText, SyntaxWrappingOptions options, TBinaryExpressionSyntax binaryExpression, ImmutableArray<SyntaxNodeOrToken> exprsAndOperators) : base(service, document, originalSourceText, options) { _exprsAndOperators = exprsAndOperators; var generator = SyntaxGenerator.GetGenerator(document); _newlineBeforeOperatorTrivia = service.GetNewLineBeforeOperatorTrivia(NewLineTrivia); _indentAndAlignTrivia = new SyntaxTriviaList(generator.Whitespace( OriginalSourceText.GetOffset(binaryExpression.Span.Start) .CreateIndentationString(options.FormattingOptions.UseTabs, options.FormattingOptions.TabSize))); _smartIndentTrivia = AsyncLazy.Create(async cancellationToken => new SyntaxTriviaList(generator.Whitespace( await GetSmartIndentationAfterAsync(_exprsAndOperators[1], cancellationToken).ConfigureAwait(false)))); } protected override async Task<ImmutableArray<WrappingGroup>> ComputeWrappingGroupsAsync(CancellationToken cancellationToken) => [new WrappingGroup( isInlinable: true, [ await GetWrapCodeActionAsync(align: false, cancellationToken).ConfigureAwait(false), await GetWrapCodeActionAsync(align: true, cancellationToken).ConfigureAwait(false), await GetUnwrapCodeActionAsync(cancellationToken).ConfigureAwait(false), ])]; private async Task<WrapItemsAction> 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<WrapItemsAction> GetUnwrapCodeActionAsync(CancellationToken cancellationToken) => TryCreateCodeActionAsync(GetUnwrapEdits(), FeaturesResources.Wrapping, FeaturesResources.Unwrap_expression, cancellationToken); private async Task<ImmutableArray<Edit>> GetWrapEditsAsync(bool align, CancellationToken cancellationToken) { using var _ = ArrayBuilder<Edit>.GetInstance(out var result); var indentationTrivia = align ? _indentAndAlignTrivia : await _smartIndentTrivia.GetValueAsync(cancellationToken).ConfigureAwait(false); for (var i = 1; i < _exprsAndOperators.Length; i += 2) { var left = _exprsAndOperators[i - 1].AsNode(); var opToken = _exprsAndOperators[i].AsToken(); var right = _exprsAndOperators[i + 1].AsNode(); if (Options.OperatorPlacement == OperatorPlacementWhenWrappingPreference.BeginningOfLine) { // convert: // (a == b) && (c == d) to // // (a == b) // && (c == d) result.Add(Edit.UpdateBetween(left, _newlineBeforeOperatorTrivia, indentationTrivia, opToken)); result.Add(Edit.UpdateBetween(opToken, SingleWhitespaceTrivia, NoTrivia, right)); } else { // convert: // (a == b) && (c == d) to // // (a == b) && // (c == d) result.Add(Edit.UpdateBetween(left, SingleWhitespaceTrivia, NoTrivia, opToken)); result.Add(Edit.UpdateBetween(opToken, NewLineTrivia, indentationTrivia, right)); } } return result.ToImmutableAndClear(); } private ImmutableArray<Edit> GetUnwrapEdits() { var count = _exprsAndOperators.Length - 1; var result = new FixedSizeArrayBuilder<Edit>(count); for (var i = 0; i < count; i++) { result.Add(Edit.UpdateBetween( _exprsAndOperators[i], SingleWhitespaceTrivia, NoTrivia, _exprsAndOperators[i + 1])); } return result.MoveToImmutable(); } } }