File: BraceCompletion\BracketBraceCompletionService.cs
Web Access
Project: src\src\Features\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.Features.csproj (Microsoft.CodeAnalysis.CSharp.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.
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
using System.Threading;
using Microsoft.CodeAnalysis.BraceCompletion;
using Microsoft.CodeAnalysis.CSharp.Formatting;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Formatting.Rules;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Indentation;
 
namespace Microsoft.CodeAnalysis.CSharp.BraceCompletion;
 
[ExportBraceCompletionService(LanguageNames.CSharp), Shared]
[method: ImportingConstructor]
[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
internal sealed class BracketBraceCompletionService() : AbstractCurlyBraceOrBracketCompletionService
{
    protected override char OpeningBrace => Bracket.OpenCharacter;
 
    protected override char ClosingBrace => Bracket.CloseCharacter;
 
    public override bool AllowOverType(BraceCompletionContext context, CancellationToken cancellationToken)
        => AllowOverTypeInUserCodeWithValidClosingToken(context, cancellationToken);
 
    protected override bool IsValidOpeningBraceToken(SyntaxToken token) => token.IsKind(SyntaxKind.OpenBracketToken);
 
    protected override bool IsValidClosingBraceToken(SyntaxToken token) => token.IsKind(SyntaxKind.CloseBracketToken);
 
    protected override int AdjustFormattingEndPoint(ParsedDocument document, int startPoint, int endPoint)
        => endPoint;
 
    protected override ImmutableArray<AbstractFormattingRule> GetBraceFormattingIndentationRulesAfterReturn(IndentationOptions options)
    {
        return [BracketCompletionFormattingRule.Instance];
    }
 
    private sealed class BracketCompletionFormattingRule : BaseFormattingRule
    {
        public static readonly AbstractFormattingRule Instance = new BracketCompletionFormattingRule();
 
        public override AdjustNewLinesOperation? GetAdjustNewLinesOperation(in SyntaxToken previousToken, in SyntaxToken currentToken, in NextGetAdjustNewLinesOperation nextOperation)
        {
            if (currentToken.IsKind(SyntaxKind.OpenBracketToken) && currentToken.Parent.IsKind(SyntaxKind.ListPattern))
            {
                // For list patterns we format brackets as though they are a block, so when formatting after Return
                // we add a newline
                return CreateAdjustNewLinesOperation(1, AdjustNewLinesOption.PreserveLines);
            }
 
            return base.GetAdjustNewLinesOperation(in previousToken, in currentToken, in nextOperation);
        }
 
        public override void AddAlignTokensOperations(List<AlignTokensOperation> list, SyntaxNode node, in NextAlignTokensOperationAction nextOperation)
        {
            base.AddAlignTokensOperations(list, node, in nextOperation);
 
            var bracketPair = node.GetBracketPair();
            if (bracketPair.IsValidBracketOrBracePair() && node is ListPatternSyntax or CollectionExpressionSyntax)
            {
                // For list patterns we format brackets as though they are a block, so ensure the close bracket
                // is aligned with the open bracket
                AddAlignIndentationOfTokensToBaseTokenOperation(list, node, bracketPair.openBracket,
                    [bracketPair.closeBracket], AlignTokensOption.AlignIndentationOfTokensToFirstTokenOfBaseTokenLine);
            }
        }
    }
}