提交 c193f37a 编写于 作者: C Cyrus Najmabadi

Add support for highlighting written references differently than read references.

上级 126ceaed
......@@ -578,6 +578,8 @@
<Compile Include="Implementation\Peek\PeekableItemSource.cs" />
<Compile Include="Implementation\Peek\PeekableItemSourceProvider.cs" />
<Compile Include="Implementation\Peek\PeekHelpers.cs" />
<Compile Include="Implementation\ReferenceHighlighting\WrittenReferenceHighlightTag.cs" />
<Compile Include="Implementation\ReferenceHighlighting\WrittenReferenceHighlightTagDefinition.cs" />
<Compile Include="Implementation\RenameTracking\RenameTrackingCancellationCommandHandler.cs" />
<Compile Include="Implementation\RenameTracking\RenameTrackingTagDefinition.cs" />
<Compile Include="Implementation\RenameTracking\RenameTrackingTag.cs" />
......
......@@ -1069,6 +1069,15 @@ internal class EditorFeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to Highlighted Written Reference.
/// </summary>
internal static string HighlightedWrittenReference {
get {
return ResourceManager.GetString("HighlightedWrittenReference", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Implemented By.
/// </summary>
......
......@@ -730,4 +730,7 @@ Do you want to proceed?</value>
<data name="TheRenameTrackingSessionWasCancelledAndIsNoLongerAvailable" xml:space="preserve">
<value>The rename tracking session was cancelled and is no longer available.</value>
</data>
<data name="HighlightedWrittenReference" xml:space="preserve">
<value>Highlighted Written Reference</value>
</data>
</root>
\ No newline at end of file
......@@ -134,7 +134,7 @@ private bool ShouldConsiderSymbol(ISymbol symbol)
symbol.Locations.Length > 0)
{
// For alias symbol we want to get the tag only for the alias definition, not the target symbol's definition.
await AddLocationSpan(symbol.Locations.First(), solution, spanSet, tagMap, true, cancellationToken).ConfigureAwait(false);
await AddLocationSpan(symbol.Locations.First(), solution, spanSet, tagMap, HighlightSpanKind.Definition, cancellationToken).ConfigureAwait(false);
addAllDefinitions = false;
}
......@@ -147,21 +147,22 @@ private bool ShouldConsiderSymbol(ISymbol symbol)
{
if (location.IsInSource && documentToSearch.Contains(solution.GetDocument(location.SourceTree)))
{
await AddLocationSpan(location, solution, spanSet, tagMap, true, cancellationToken).ConfigureAwait(false);
await AddLocationSpan(location, solution, spanSet, tagMap, HighlightSpanKind.Definition, cancellationToken).ConfigureAwait(false);
}
}
}
foreach (var referenceLocation in reference.Locations)
{
await AddLocationSpan(referenceLocation.Location, solution, spanSet, tagMap, false, cancellationToken).ConfigureAwait(false);
var referenceKind = referenceLocation.IsWrittenTo ? HighlightSpanKind.WrittenReference : HighlightSpanKind.ReadReference;
await AddLocationSpan(referenceLocation.Location, solution, spanSet, tagMap, referenceKind, cancellationToken).ConfigureAwait(false);
}
}
// Add additional references
foreach (var location in additionalReferences)
{
await AddLocationSpan(location, solution, spanSet, tagMap, false, cancellationToken).ConfigureAwait(false);
await AddLocationSpan(location, solution, spanSet, tagMap, HighlightSpanKind.ReadReference, cancellationToken).ConfigureAwait(false);
}
var list = new List<DocumentHighlights>(tagMap.Count);
......@@ -205,13 +206,13 @@ private static bool ShouldIncludeDefinition(ISymbol symbol)
return true;
}
private async Task AddLocationSpan(Location location, Solution solution, HashSet<ValueTuple<Document, TextSpan>> spanSet, MultiDictionary<Document, HighlightSpan> tagList, bool isDefinition, CancellationToken cancellationToken)
private async Task AddLocationSpan(Location location, Solution solution, HashSet<ValueTuple<Document, TextSpan>> spanSet, MultiDictionary<Document, HighlightSpan> tagList, HighlightSpanKind kind, CancellationToken cancellationToken)
{
var span = await GetLocationSpanAsync(solution, location, cancellationToken).ConfigureAwait(false);
if (span != null && !spanSet.Contains(span.Value))
{
spanSet.Add(span.Value);
tagList.Add(span.Value.Item1, new HighlightSpan(span.Value.Item2, isDefinition));
tagList.Add(span.Value.Item1, new HighlightSpan(span.Value.Item2, kind));
}
}
......
......@@ -11,15 +11,22 @@
namespace Microsoft.CodeAnalysis.Editor
{
internal enum HighlightSpanKind
{
Definition,
ReadReference,
WrittenReference,
}
internal struct HighlightSpan
{
public TextSpan TextSpan { get; }
public bool IsDefinition { get; }
public HighlightSpanKind Kind { get; }
public HighlightSpan(TextSpan textSpan, bool isDefinition) : this()
public HighlightSpan(TextSpan textSpan, HighlightSpanKind kind) : this()
{
this.TextSpan = textSpan;
this.IsDefinition = isDefinition;
this.Kind = kind;
}
}
......
......@@ -122,11 +122,25 @@ public void Dispose()
foreach (var span in documentHighlights.HighlightSpans)
{
var tag = span.IsDefinition ? (AbstractNavigatableReferenceHighlightingTag)DefinitionHighlightTag.Instance : ReferenceHighlightTag.Instance;
var tag = GetTag(span);
tags.Add(new TagSpan<AbstractNavigatableReferenceHighlightingTag>(
textSnapshot.GetSpan(Span.FromBounds(span.TextSpan.Start, span.TextSpan.End)), tag));
}
}
private static AbstractNavigatableReferenceHighlightingTag GetTag(HighlightSpan span)
{
switch (span.Kind)
{
case HighlightSpanKind.WrittenReference:
return WrittenReferenceHighlightTag.Instance;
case HighlightSpanKind.Definition:
return DefinitionHighlightTag.Instance;
case HighlightSpanKind.ReadReference:
default:
return ReferenceHighlightTag.Instance;
}
}
}
}
}
// 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.Editor.Shared.Tagging;
namespace Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting
{
internal class WrittenReferenceHighlightTag : AbstractNavigatableReferenceHighlightingTag
{
internal const string TagId = "MarkerFormatDefinition/HighlightedWrittenReference";
public static readonly WrittenReferenceHighlightTag Instance = new WrittenReferenceHighlightTag();
private WrittenReferenceHighlightTag()
: base(TagId)
{
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
using Microsoft.VisualStudio.Text.Classification;
using Microsoft.VisualStudio.Utilities;
namespace Microsoft.CodeAnalysis.Editor.Implementation.ReferenceHighlighting
{
[Export(typeof(EditorFormatDefinition))]
[Name(WrittenReferenceHighlightTag.TagId)]
[UserVisible(true)]
internal class WrittenReferenceHighlightTagDefinition : MarkerFormatDefinition
{
public WrittenReferenceHighlightTagDefinition()
{
// NOTE: This is the same color used by the editor for reference highlighting
this.BackgroundColor = Color.FromRgb(219, 224, 204);
this.DisplayName = EditorFeaturesResources.HighlightedWrittenReference;
}
}
}
......@@ -31,29 +31,22 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.ReferenceHighlighting
workspace, document,
cancellationToken:=Nothing).Result
Order By tag.Span.Start
Let spanType = If(tag.Tag.Type = DefinitionHighlightTag.TagId, "Definition", "Reference")
Select spanType + ": " + tag.Span.Span.ToTextSpan().ToString()
Let spanType = If(tag.Tag.Type = DefinitionHighlightTag.TagId, "Definition",
If(tag.Tag.Type = WrittenReferenceHighlightTag.TagId, "WrittenReference", "Reference"))
Select spanType + ":" + tag.Span.Span.ToTextSpan().ToString()
Dim expectedDefinitionSpans As New List(Of Tuple(Of String, TextSpan))
Dim expectedTags As New List(Of String)
For Each hostDocument In workspace.Documents
If hostDocument.AnnotatedSpans.ContainsKey("Definition") Then
For Each definitionSpan In hostDocument.AnnotatedSpans("Definition")
expectedDefinitionSpans.Add(Tuple.Create("Definition", definitionSpan))
For Each nameAndSpans In hostDocument.AnnotatedSpans
For Each span In nameAndSpans.Value
expectedTags.Add(nameAndSpans.Key + ":" + span.ToString())
Next
End If
Next
Next
Dim expectedReferenceSpans = workspace.Documents.SelectMany(Function(d) d.SelectedSpans).Select(Function(s) Tuple.Create("Reference", s))
Dim expectedTags = From span In expectedDefinitionSpans.Concat(expectedReferenceSpans)
Order By span.Item2.Start
Select span.Item1 + ": " + span.Item2.ToString()
AssertEx.Equal(expectedTags, producedTags)
End Using
End Sub
End Class
End Namespace
......@@ -48,7 +48,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.ReferenceHighlighting
{
}
[|$$Script|].M();
{|Reference:$$Script|}.M();
</Document>
</Project>
</Workspace>)
......@@ -64,7 +64,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.ReferenceHighlighting
{
{|Definition:Foo|}()
{
[|var|] x = new [|Foo|]();
{|Reference:var|} x = new {|Reference:Foo|}();
}
}
</Document>
......@@ -83,7 +83,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.ReferenceHighlighting
{
void Blah()
{
var x = new [|$$Foo|]();
var x = new {|Reference:$$Foo|}();
}
}
</Document>
......@@ -106,7 +106,7 @@ class {|Definition:Program|}
{
static void Main(string[] args)
{
new List<[|Program$$|]>();
new List<{|Reference:Program$$|}>();
}
}]]>
</Document>
......@@ -153,7 +153,7 @@ namespace X
{
public void M()
{
$$[|Q|].Directory.Exists("");
$${|Reference:Q|}.Directory.Exists("");
}
}
}
......@@ -178,7 +178,7 @@ namespace X
{
public void M()
{
[|Q|].Directory.Exists("");
{|Reference:Q|}.Directory.Exists("");
}
}
}
......@@ -198,12 +198,12 @@ namespace X
<Document>
namespace X
{
using Q = System.$$[|IO|];
using Q = System.$${|Reference:IO|};
Class B
{
public void M()
{
[|Q|].Directory.Exists("");
{|Reference:Q|}.Directory.Exists("");
}
}
}
......@@ -227,7 +227,7 @@ namespace N
{
using $${|Definition:C|} = A<C>; // select C
class A<T> { }
class B : [|C|] { }
class B : {|Reference:C|} { }
}]]>
</Document>
</Project>
......@@ -247,10 +247,10 @@ class C
{
void F()
{
$$[|var|] i = 1;
[|int|] j = 0;
$${|Reference:var|} i = 1;
{|Reference:int|} j = 0;
double d;
[|int|] k = 1;
{|Reference:int|} k = 1;
}
}
</Document>
......@@ -271,10 +271,10 @@ class C
{
void F()
{
[|var|] i = 1;
$$[|int|] j = 0;
{|Reference:var|} i = 1;
$${|Reference:int|} j = 0;
double d;
[|int|] k = 1;
{|Reference:int|} k = 1;
}
}
</Document>
......@@ -297,10 +297,10 @@ class C
{
void F()
{
$$[|var|] i = new [|List|]<string>();
$${|Reference:var|} i = new {|Reference:List|}<string>();
int j = 0;
double d;
[|var|] k = new [|List|]<int>();
{|Reference:var|} k = new {|Reference:List|}<int>();
}
}
]]></Document>
......@@ -438,8 +438,8 @@ class A
{
B b = new B();
dynamic d = 1.5f;
b.[|Boo|](1); //Line 4
b.$$[|Boo|](d); //Line 5
b.{|Reference:Boo|}(1); //Line 4
b.$${|Reference:Boo|}(d); //Line 5
b.Boo("d"); //Line 6
}
}
......@@ -463,7 +463,7 @@ class C
{
get
{
return this[[|i|]];
return this[{|Reference:i|}];
}
}
}
......@@ -486,7 +486,7 @@ class C
{
var $${|Definition:a|} = "Hello";
var b = "World";
var c = $"{[|a|]}, {b}!";
var c = $"{ {|Reference:a|} }, {b}!";
}
}
</Document>
......@@ -508,7 +508,28 @@ class C
{
var a = "Hello";
var $${|Definition:b|} = "World";
var c = $"{a}, {[|b|]}!";
var c = $"{a}, { {|Reference:b|} }!";
}
}
</Document>
</Project>
</Workspace>
VerifyHighlights(input)
End Sub
<Fact, Trait(Traits.Feature, Traits.Features.ReferenceHighlighting)>
Public Sub TestWrittenReference()
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
class C
{
void M()
{
var $${|Definition:b|} = "Hello";
{|WrittenReference:b|} = "World";
}
}
</Document>
......
......@@ -15,7 +15,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.ReferenceHighlighting
{
void Blah()
{
[|$$Console|].WriteLine();
{|Reference:$$Console|}.WriteLine();
}
}
</Document>
......
......@@ -28,8 +28,8 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.ReferenceHighlighting
<Document>
Class {|Definition:$$Foo|}
Public Sub {|Definition:New|}()
Dim x = New [|Foo|]()
Dim y As New [|Foo|]()
Dim x = New {|Reference:Foo|}()
Dim y As New {|Reference:Foo|}()
End Sub
End Class
</Document>
......@@ -47,8 +47,8 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.ReferenceHighlighting
<Document>
Class {|Definition:Foo|}
Public Sub Blah()
Dim x = New [|$$Foo|]()
Dim y As New [|Foo|]()
Dim x = New {|Reference:$$Foo|}()
Dim y As New {|Reference:Foo|}()
End Sub
End Class
</Document>
......@@ -71,7 +71,7 @@ End Interface
Class C
Implements I
Public Sub Bar() Implements I.[|Foo|]
Public Sub Bar() Implements I.{|Reference:Foo|}
End Sub
End Class
</Document>
......@@ -94,7 +94,7 @@ End Interface
Class C
Implements I
Public Sub Bar() Implements I.[|$$Foo|]
Public Sub Bar() Implements I.{|Reference:$$Foo|}
End Sub
End Class
</Document>
......@@ -117,7 +117,7 @@ End Interface
Class C
Implements I
Public Sub {|Definition:Foo|}() Implements I.[|$$Foo|]
Public Sub {|Definition:Foo|}() Implements I.{|Reference:$$Foo|}
End Sub
End Class
</Document>
......@@ -151,8 +151,8 @@ End Class
<Document>
Module M
Sub Main
[|$$Global|].M.Main()
[|Global|].M.Main()
{|Reference:$$Global|}.M.Main()
{|Reference:Global|}.M.Main()
End Sub
End Module
</Document>
......@@ -218,7 +218,7 @@ End Class
Class C
Default Public Property Foo($${|Definition:x|} As Integer) As Integer
Get
Return [|x|]
Return {|Reference:x|}
End Get
Set(value As Integer)
......@@ -231,5 +231,24 @@ End Class
VerifyHighlights(input)
End Sub
<Fact, Trait(Traits.Feature, Traits.Features.ReferenceHighlighting)>
Public Sub TestWrittenReference()
Dim input =
<Workspace>
<Project Language="Visual Basic" CommonReferences="true">
<Document>
Class Foo
Public Sub New()
Dim {|Definition:$$x|} As Integer
{|WrittenReference:x|} = 0
End Sub
End Class
</Document>
</Project>
</Workspace>
VerifyHighlights(input)
End Sub
End Class
End Namespace
......@@ -98,7 +98,8 @@ protected override bool CanFind(IMethodSymbol symbol)
var invocations = nodes.Where(n => syntaxFactsService.IsInvocationExpression(n))
.Where(e => semanticModel.GetSymbolInfo(e, cancellationToken).Symbol.OriginalDefinition == methodSymbol);
return invocations.Concat(convertedAnonymousFunctions).Select(e => new ReferenceLocation(document, null, e.GetLocation(), isImplicit: false, candidateReason: CandidateReason.None));
return invocations.Concat(convertedAnonymousFunctions).Select(
e => new ReferenceLocation(document, null, e.GetLocation(), isImplicit: false, isWrittenTo: false, candidateReason: CandidateReason.None));
}
}
}
......@@ -515,7 +515,8 @@ protected static bool IdentifiersMatch(ISyntaxFactsService syntaxFacts, string n
var alias = FindReferenceCache.GetAliasInfo(semanticFacts, semanticModel, token, cancellationToken);
var location = token.GetLocation();
locations.Add(new ReferenceLocation(document, alias, location, isImplicit: false, candidateReason: match.Item2));
var isWrittemTo = semanticFacts.IsWrittenTo(semanticModel, token.Parent, cancellationToken);
locations.Add(new ReferenceLocation(document, alias, location, isImplicit: false, isWrittenTo: isWrittemTo, candidateReason: match.Item2));
}
}
}
......@@ -731,7 +732,7 @@ protected Task<IEnumerable<Document>> FindDocumentsWithForEachStatementsAsync(Pr
{
var location = node.GetFirstToken().GetLocation();
locations.Add(new ReferenceLocation(
document, alias: null, location: location, isImplicit: true, candidateReason: CandidateReason.None));
document, alias: null, location: location, isImplicit: true, isWrittenTo: false, candidateReason: CandidateReason.None));
}
}
......
......@@ -156,7 +156,7 @@ private static bool IsForEachProperty(IPropertySymbol symbol)
if (nodeToBeReferenced != null)
{
var location = nodeToBeReferenced.SyntaxTree.GetLocation(new TextSpan(nodeToBeReferenced.SpanStart, 0));
locations.Add(new ReferenceLocation(document, null, location, isImplicit: false, candidateReason: match.Item2));
locations.Add(new ReferenceLocation(document, null, location, isImplicit: false, isWrittenTo: false, candidateReason: match.Item2));
}
}
}
......
......@@ -36,15 +36,21 @@ public struct ReferenceLocation : IComparable<ReferenceLocation>, IEquatable<Ref
/// </summary>
public bool IsImplicit { get; }
/// <summary>
/// Inidicates if this is a location where the reference is written to.
/// </summary>
internal bool IsWrittenTo { get; }
public CandidateReason CandidateReason { get; }
internal ReferenceLocation(Document document, IAliasSymbol alias, Location location, bool isImplicit, CandidateReason candidateReason)
internal ReferenceLocation(Document document, IAliasSymbol alias, Location location, bool isImplicit, bool isWrittenTo, CandidateReason candidateReason)
: this()
{
this.Document = document;
this.Alias = alias;
this.Location = location;
this.IsImplicit = isImplicit;
this.IsWrittenTo = isWrittenTo;
this.CandidateReason = candidateReason;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册