| File: BraceMatching\AbstractBraceMatcher.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.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; namespace Microsoft.CodeAnalysis.BraceMatching; internal abstract class AbstractBraceMatcher : IBraceMatcher { private readonly BraceCharacterAndKind _openBrace; private readonly BraceCharacterAndKind _closeBrace; protected AbstractBraceMatcher( BraceCharacterAndKind openBrace, BraceCharacterAndKind closeBrace) { _openBrace = openBrace; _closeBrace = closeBrace; } private bool TryFindMatchingToken(SyntaxToken token, out SyntaxToken match) { var parent = token.Parent; var braceTokens = (from child in parent.ChildNodesAndTokens() where child.IsToken let tok = child.AsToken() where tok.RawKind == _openBrace.Kind || tok.RawKind == _closeBrace.Kind where tok.Span.Length > 0 select tok).ToList(); if (braceTokens.Count == 2 && braceTokens[0].RawKind == _openBrace.Kind && braceTokens[1].RawKind == _closeBrace.Kind) { if (braceTokens[0] == token) { match = braceTokens[1]; return true; } else if (braceTokens[1] == token) { match = braceTokens[0]; return true; } } match = default; return false; } public async Task<BraceMatchingResult?> FindBracesAsync( Document document, int position, BraceMatchingOptions options, CancellationToken cancellationToken) { var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var token = root.FindToken(position); var text = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); if (position < text.Length && this.IsBrace(text[position])) { if (token.RawKind == _openBrace.Kind && AllowedForToken(token)) { var leftToken = token; if (TryFindMatchingToken(leftToken, out var rightToken)) { return new BraceMatchingResult(leftToken.Span, rightToken.Span); } } else if (token.RawKind == _closeBrace.Kind && AllowedForToken(token)) { var rightToken = token; if (TryFindMatchingToken(rightToken, out var leftToken)) { return new BraceMatchingResult(leftToken.Span, rightToken.Span); } } } return null; } protected virtual bool AllowedForToken(SyntaxToken token) => true; private bool IsBrace(char c) => _openBrace.Character == c || _closeBrace.Character == c; }