|
// 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;
}
|