提交 633c2b09 编写于 作者: C Cyrus Najmabadi

Move regex highlighting impl over from bracematcher code to highlighter code.

上级 c8056896
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.RegularExpressions;
using Microsoft.CodeAnalysis.Editor.BraceMatching;
namespace Microsoft.CodeAnalysis.Editor.CSharp.BraceMatching
{
[ExportBraceMatcher(LanguageNames.CSharp)]
internal class RegexBraceMatcher : AbstractRegexBraceMatcher
{
public RegexBraceMatcher()
: base((int)SyntaxKind.StringLiteralToken,
CSharpSyntaxFactsService.Instance,
CSharpSemanticFactsService.Instance,
CSharpVirtualCharService.Instance)
{
}
}
}
......@@ -256,37 +256,4 @@ public async Task TestTuplesWithGenerics()
var x = [|(|]new Dictionary<int, string>(), new List<int>()[|)$$|];
}", TestOptions.Regular);
}
[WpfFact, Trait(Traits.Feature, Traits.Features.BraceHighlighting)]
public async Task TestRegexBraces1()
{
await TestBraceHighlightingAsync(
@"
using System.Text.RegularExpressions;
class C
{
void Goo()
{
var r = new Regex(@""[|(|]a[|)|]$$"");
}
}", TestOptions.Regular);
}
[WpfFact, Trait(Traits.Feature, Traits.Features.BraceHighlighting)]
public async Task TestRegexReference1()
{
await TestBraceHighlightingAsync(
@"
using System.Text.RegularExpressions;
class C
{
void Goo()
{
var r = new Regex(@""[|(a)|]\0[|\$$1|]"");
}
}", TestOptions.Regular);
}
}
}
......@@ -76,12 +76,13 @@ protected override async Task ProduceTagsAsync(TaggerContext<KeywordHighlightTag
var position = caretPosition.Value;
var snapshot = snapshotSpan.Snapshot;
var existingTags = context.GetExistingTags(new SnapshotSpan(snapshot, position, 0));
// See if the user is just moving their caret around in an existing tag. If so, we don't
// want to actually go recompute things. Note: this only works for containment. If the
// user moves their caret to the end of a highlighted reference, we do want to recompute
// as they may now be at the start of some other reference that should be highlighted instead.
var existingTags = context.GetExistingContainingTags(new SnapshotPoint(snapshot, position));
if (!existingTags.IsEmpty())
{
// We already have a tag at this position. So the user is moving from one highlight
// tag to another. In this case we don't want to recompute anything. Let our caller
// know that we should preserve all tags.
context.SetSpansTagged(SpecializedCollections.EmptyEnumerable<DocumentSnapshotSpan>());
return;
}
......
......@@ -106,12 +106,13 @@ protected override Task ProduceTagsAsync(TaggerContext<NavigableHighlightTag> co
return SpecializedTasks.EmptyTask;
}
var existingTags = context.GetExistingTags(new SnapshotSpan(caretPosition, 0));
// See if the user is just moving their caret around in an existing tag. If so, we don't
// want to actually go recompute things. Note: this only works for containment. If the
// user moves their caret to the end of a highlighted reference, we do want to recompute
// as they may now be at the start of some other reference that should be highlighted instead.
var existingTags = context.GetExistingContainingTags(caretPosition);
if (!existingTags.IsEmpty())
{
// We already have a tag at this position. So the user is moving from one highlight
// tag to another. In this case we don't want to recompute anything. Let our caller
// know that we should preserve all tags.
context.SetSpansTagged(SpecializedCollections.EmptyEnumerable<DocumentSnapshotSpan>());
return SpecializedTasks.EmptyTask;
}
......
......@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.Editor.Shared.Tagging;
using Microsoft.CodeAnalysis.Text;
......@@ -86,11 +87,15 @@ public void SetSpansTagged(IEnumerable<DocumentSnapshotSpan> spansTagged)
this._spansTagged = spansTagged ?? throw new ArgumentNullException(nameof(spansTagged));
}
public IEnumerable<ITagSpan<TTag>> GetExistingTags(SnapshotSpan span)
public IEnumerable<ITagSpan<TTag>> GetExistingContainingTags(SnapshotPoint point)
{
return _existingTags != null && _existingTags.TryGetValue(span.Snapshot.TextBuffer, out var tree)
? tree.GetIntersectingSpans(span)
: SpecializedCollections.EmptyEnumerable<ITagSpan<TTag>>();
if (_existingTags != null && _existingTags.TryGetValue(point.Snapshot.TextBuffer, out var tree))
{
return tree.GetIntersectingSpans(new SnapshotSpan(point.Snapshot, new Span(point, 0)))
.Where(s => s.Span.Contains(point));
}
return SpecializedCollections.EmptyEnumerable<ITagSpan<TTag>>();
}
}
}
......@@ -672,5 +672,49 @@ class C
Await VerifyHighlightsAsync(input)
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.ReferenceHighlighting)>
Public Async Function TestRegexBracket1() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
using System.Text.RegularExpressions;
class C
{
void Goo()
{
var r = new Regex(@"{|Reference:(|}a{|Reference:)|}$$");
}
}
</Document>
</Project>
</Workspace>
Await VerifyHighlightsAsync(input)
End Function
<WpfFact, Trait(Traits.Feature, Traits.Features.ReferenceHighlighting)>
Public Async Function TestRegexReference1() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
using System.Text.RegularExpressions;
class C
{
void Goo()
{
var r = new Regex(@"{|Reference:(a)|}\0{|Reference:\$$1|}");
}
}
</Document>
</Project>
</Workspace>
Await VerifyHighlightsAsync(input)
End Function
End Class
End Namespace
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports Microsoft.CodeAnalysis.Editor.BraceMatching
Imports Microsoft.CodeAnalysis.VisualBasic.RegularExpressions
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.BraceMatching
<ExportBraceMatcher(LanguageNames.VisualBasic)>
Friend Class RegexBraceMatcher
Inherits AbstractRegexBraceMatcher
Public Sub New()
MyBase.New(
SyntaxKind.StringLiteralToken,
VisualBasicSyntaxFactsService.Instance,
VisualBasicSemanticFactsService.Instance,
VisualBasicVirtualCharService.Instance)
End Sub
End Class
End Namespace
......@@ -8,7 +8,6 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Experiments;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.PooledObjects;
......@@ -61,6 +60,12 @@ internal abstract partial class AbstractDocumentHighlightsService : IDocumentHig
private async Task<ImmutableArray<DocumentHighlights>> GetDocumentHighlightsInCurrentProcessAsync(
Document document, int position, IImmutableSet<Document> documentsToSearch, CancellationToken cancellationToken)
{
var result = await TryGetRegexPatternHighlightsAsync(document, position, cancellationToken).ConfigureAwait(false);
if (!result.IsDefaultOrEmpty)
{
return result;
}
// use speculative semantic model to see whether we are on a symbol we can do HR
var span = new TextSpan(position, 0);
var solution = document.Project.Solution;
......@@ -315,7 +320,7 @@ private static async Task AddLocationSpan(Location location, Solution solution,
var tree = location.SourceTree;
var document = solution.GetDocument(tree);
var syntaxFacts = document.Project.LanguageServices.GetService<ISyntaxFactsService>();
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
if (syntaxFacts != null)
{
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.RegularExpressions;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor.BraceMatching
namespace Microsoft.CodeAnalysis.DocumentHighlighting
{
internal class AbstractRegexBraceMatcher : IBraceMatcher
internal abstract partial class AbstractDocumentHighlightsService : IDocumentHighlightsService
{
private readonly int _stringLiteralKind;
private readonly ISyntaxFactsService _syntaxFacts;
private readonly ISemanticFactsService _semanticFacts;
private readonly IVirtualCharService _virtualCharService;
protected AbstractRegexBraceMatcher(
int stringLiteralKind,
ISyntaxFactsService syntaxFacts,
ISemanticFactsService semanticFacts,
IVirtualCharService virtualCharService)
private async Task<ImmutableArray<DocumentHighlights>> TryGetRegexPatternHighlightsAsync(
Document document, int position, CancellationToken cancellationToken)
{
_stringLiteralKind = stringLiteralKind;
_syntaxFacts = syntaxFacts;
_semanticFacts = semanticFacts;
_virtualCharService = virtualCharService;
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var token = root.FindToken(position);
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
if (RegexPatternDetector.IsDefinitelyNotPattern(token, syntaxFacts))
{
return default;
}
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var detector = RegexPatternDetector.TryGetOrCreate(semanticModel, syntaxFacts, document.GetLanguageService<ISemanticFactsService>());
var tree = detector?.TryParseRegexPattern(token, document.GetLanguageService<IVirtualCharService>(), cancellationToken);
if (tree == null)
{
return default;
}
return GetHighlights(document, tree, position);
}
public async Task<BraceMatchingResult?> FindBracesAsync(Document document, int position, CancellationToken cancellationToken = default)
private ImmutableArray<DocumentHighlights> GetHighlights(
Document document, RegexTree tree, int position)
{
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var token = root.FindToken(position);
var highlightsOnTheRight = GetHighlights(document, tree, position, caretOnLeft: true);
var highlightsOnTheLeft = GetHighlights(document, tree, position - 1, caretOnLeft: false);
if (token.RawKind == _stringLiteralKind &&
!RegexPatternDetector.IsDefinitelyNotPattern(token, _syntaxFacts))
if (!highlightsOnTheRight.references.IsEmpty)
{
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var detector = RegexPatternDetector.TryGetOrCreate(semanticModel, _syntaxFacts, _semanticFacts);
var tree = detector?.TryParseRegexPattern(token, _virtualCharService, cancellationToken);
if (tree != null)
{
return FindBraces(tree, position);
}
// We were on the left of a reference. Just return only these highlights.
return highlightsOnTheRight.references;
}
return null;
if (!highlightsOnTheRight.braces.IsEmpty)
{
// We were on the left of an open open. Return these highlights, and any
// highlights if we were on the right of a close paren. Don't return
// references to the left of the caret.
return highlightsOnTheRight.braces.Concat(highlightsOnTheLeft.braces);
}
// Nothing was on the right of the caret. Return anything we were able to find on
// the left of the caret
return highlightsOnTheLeft.references.Concat(highlightsOnTheLeft.braces);
}
private BraceMatchingResult? FindBraces(RegexTree tree, int position)
private (ImmutableArray<DocumentHighlights> references, ImmutableArray<DocumentHighlights> braces) GetHighlights(
Document document, RegexTree tree, int position, bool caretOnLeft)
{
var virtualChar = tree.Text.FirstOrNullable(vc => vc.Span.Contains(position));
if (virtualChar == null)
{
return null;
return (ImmutableArray<DocumentHighlights>.Empty, ImmutableArray<DocumentHighlights>.Empty);
}
var ch = virtualChar.Value;
return FindReferenceMatch(tree, ch) ?? FindBraceMatch(tree, ch);
var referenceHighlights = FindReferenceHighlights(document, tree, ch);
if (caretOnLeft)
{
var braceHighlights = ch == '('
? FindBraceHighlights(document, tree, ch)
: ImmutableArray<DocumentHighlights>.Empty;
return (referenceHighlights, braceHighlights);
}
else
{
var braceHighlights = ch == ')'
? FindBraceHighlights(document, tree, ch)
: ImmutableArray<DocumentHighlights>.Empty;
return (referenceHighlights, braceHighlights);
}
}
private BraceMatchingResult? FindBraceMatch(RegexTree tree, VirtualChar ch)
private ImmutableArray<DocumentHighlights> FindBraceHighlights(
Document document, RegexTree tree, VirtualChar ch)
{
var node = FindGroupingNode(tree.Root, ch);
if (node == null)
{
return null;
return ImmutableArray<DocumentHighlights>.Empty;
}
if (node.OpenParenToken.IsMissing || node.CloseParenToken.IsMissing)
{
return null;
return ImmutableArray<DocumentHighlights>.Empty;
}
return new BraceMatchingResult(
node.OpenParenToken.VirtualChars[0].Span,
node.CloseParenToken.VirtualChars[0].Span);
return ImmutableArray.Create(new DocumentHighlights(
document, ImmutableArray.Create(
new HighlightSpan(node.OpenParenToken.VirtualChars[0].Span, HighlightSpanKind.None),
new HighlightSpan(node.CloseParenToken.VirtualChars[0].Span, HighlightSpanKind.None))));
}
private RegexGroupingNode FindGroupingNode(RegexNode node, VirtualChar ch)
......@@ -101,12 +137,13 @@ private RegexGroupingNode FindGroupingNode(RegexNode node, VirtualChar ch)
return null;
}
private BraceMatchingResult? FindReferenceMatch(RegexTree tree, VirtualChar ch)
private ImmutableArray<DocumentHighlights> FindReferenceHighlights(
Document document, RegexTree tree, VirtualChar ch)
{
var node = FindReferenceNode(tree.Root, ch);
if (node == null)
{
return null;
return ImmutableArray<DocumentHighlights>.Empty;
}
var captureToken = GetCaptureToken(node);
......@@ -115,7 +152,7 @@ private RegexGroupingNode FindGroupingNode(RegexNode node, VirtualChar ch)
var val = (int)captureToken.Value;
if (tree.CaptureNumbersToSpan.TryGetValue(val, out var captureSpan))
{
return new BraceMatchingResult(RegexHelpers.GetSpan(node), captureSpan);
return CreateHighlights(document, node, captureSpan);
}
}
else
......@@ -123,11 +160,20 @@ private RegexGroupingNode FindGroupingNode(RegexNode node, VirtualChar ch)
var val = (string)captureToken.Value;
if (tree.CaptureNamesToSpan.TryGetValue(val, out var captureSpan))
{
return new BraceMatchingResult(RegexHelpers.GetSpan(node), captureSpan);
return CreateHighlights(document, node, captureSpan);
}
}
return null;
return ImmutableArray<DocumentHighlights>.Empty;
}
private ImmutableArray<DocumentHighlights> CreateHighlights(
Document document, RegexEscapeNode node, TextSpan captureSpan)
{
return ImmutableArray.Create(new DocumentHighlights(document,
ImmutableArray.Create(
new HighlightSpan(RegexHelpers.GetSpan(node), HighlightSpanKind.None),
new HighlightSpan(captureSpan, HighlightSpanKind.None))));
}
private RegexToken GetCaptureToken(RegexEscapeNode node)
......@@ -171,5 +217,6 @@ private RegexEscapeNode FindReferenceNode(RegexNode node, VirtualChar virtualCha
return null;
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册