-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for displaying reference kind in Find All References window. #30155
Changes from 5 commits
9ee0805
455ad76
4ed258d
e7f1980
286b8d2
f821e24
e0fbebf
041e939
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,8 @@ | ||
// 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.Concurrent; | ||
using Roslyn.Utilities; | ||
|
||
namespace Microsoft.CodeAnalysis.FindUsages | ||
{ | ||
|
@@ -10,6 +12,11 @@ namespace Microsoft.CodeAnalysis.FindUsages | |
/// </summary> | ||
internal sealed class SourceReferenceItem | ||
{ | ||
// We can have only a handful of different values for ValueUsageInfo flags enum, so the maximum size of this dictionary is capped. | ||
// So, we store this as a static dictionary which will be held in memory for the lifetime of the process. | ||
private static readonly ConcurrentDictionary<ValueUsageInfo, MultiDictionary<string, string>> s_valueUsageInfoToReferenceInfoMap | ||
= new ConcurrentDictionary<ValueUsageInfo, MultiDictionary<string, string>>(); | ||
|
||
/// <summary> | ||
/// The definition this reference corresponds to. | ||
/// </summary> | ||
|
@@ -25,11 +32,49 @@ internal sealed class SourceReferenceItem | |
/// </summary> | ||
public bool IsWrittenTo { get; } | ||
|
||
/// <summary> | ||
/// Additional information about the reference. | ||
/// Each entry represents a key-values pair of data. For example, consider the below entry: | ||
/// { "ValueUsageInfo" } = { "Read", "Write" } | ||
/// This entry indicates that the reference has additional value usage information which indicate | ||
/// it is a read/write reference, such as say 'a++'. | ||
/// </summary> | ||
public MultiDictionary<string, string> ReferenceInfo { get; } | ||
|
||
[Obsolete] | ||
public SourceReferenceItem(DefinitionItem definition, DocumentSpan sourceSpan, bool isWrittenTo) | ||
{ | ||
Definition = definition; | ||
SourceSpan = sourceSpan; | ||
IsWrittenTo = isWrittenTo; | ||
ReferenceInfo = GetOrCreateReferenceInfo(ValueUsageInfo.None); | ||
} | ||
|
||
public SourceReferenceItem(DefinitionItem definition, DocumentSpan sourceSpan, MultiDictionary<string, string> referenceInfo) | ||
{ | ||
Definition = definition; | ||
SourceSpan = sourceSpan; | ||
ReferenceInfo = referenceInfo ?? throw new ArgumentNullException(nameof(referenceInfo)); | ||
} | ||
|
||
internal SourceReferenceItem(DefinitionItem definition, DocumentSpan sourceSpan, ValueUsageInfo valueUsageInfo) | ||
: this(definition, sourceSpan, GetOrCreateReferenceInfo(valueUsageInfo)) | ||
{ | ||
IsWrittenTo = valueUsageInfo.IsWrittenTo(); | ||
} | ||
|
||
private static MultiDictionary<string, string> GetOrCreateReferenceInfo(ValueUsageInfo valueUsageInfo) | ||
=> s_valueUsageInfoToReferenceInfoMap.GetOrAdd(valueUsageInfo, CreateReferenceInfo); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i can't remember. will this cache the dleegate for CreateReferenceInfo? I don't think it will. So we can either fix this to explicitly cache that delegate, or switch to a simple lambda that calls the function (since hte compiler will cache htat). #Resolved |
||
|
||
private static MultiDictionary<string, string> CreateReferenceInfo(ValueUsageInfo valueUsageInfo) | ||
{ | ||
var referenceInfo = new MultiDictionary<string, string>(); | ||
foreach (var value in valueUsageInfo.ToLocalizableValues()) | ||
{ | ||
referenceInfo.Add(nameof(ValueUsageInfo), value); | ||
} | ||
|
||
return referenceInfo; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// 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.Collections.Generic; | ||
using System.Collections.Immutable; | ||
using System.Linq; | ||
using Microsoft.VisualStudio.Shell.TableControl; | ||
using Roslyn.Utilities; | ||
|
||
namespace Microsoft.VisualStudio.LanguageServices.FindUsages | ||
{ | ||
/// <summary> | ||
/// Implementation of a custom, dynamic column for the Find All References window. | ||
/// </summary> | ||
internal abstract class AbstractFindUsagesCustomColumnDefinition : TableColumnDefinitionBase | ||
{ | ||
protected AbstractFindUsagesCustomColumnDefinition() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. tagging @olegtk for eyes from editor team. I can't find github alias for david pugh. #Pending There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, yes confirmation from Oleg and David will help. By the way, I verified that just defining a new column with export attribute doesn't cause it to be automatically displayed in FAR window even for us. Platform code explicitly adds only the fixed column set and any variable column has to be explicitly added using the |
||
{ | ||
DefaultColumnState = new ColumnState2(Name, isVisible: false, DefaultWidth); | ||
} | ||
|
||
public ColumnState2 DefaultColumnState { get; } | ||
|
||
public override bool TryGetFilterItems(ITableEntryHandle entry, out IEnumerable<string> filterItems) | ||
{ | ||
// Determine the constituent strings for the display value in column, which should be used for applying the filter. | ||
// For example, if value "Read, Write" is displayed in column for an entry, we will return "Read" and "Write" here, | ||
// so filtering based on individual filter terms can be done. | ||
if (IsFilterable && | ||
entry.TryGetValue(Name, out var value) && | ||
value is string displayString && | ||
!string.IsNullOrEmpty(displayString)) | ||
{ | ||
filterItems = SplitColumnDisplayValue(displayString); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. how often is this called? once per item in the row? if so, we may want to cache the split values (and, if you've already done this, ignore this comment). #Resolved There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, |
||
return true; | ||
} | ||
|
||
return base.TryGetFilterItems(entry, out filterItems); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This basically determines the constituent strings for the display value in column, which should be used for applying the filter. For example, value "Read, Write" displayed in column for an entry, we will return "Read" and "Write" here, so filtering based on individual filter terms can be done. #Resolved There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
public abstract string GetDisplayStringForColumnValues(MultiDictionary<string, string>.ValueSet values); | ||
protected abstract IEnumerable<string> SplitColumnDisplayValue(string displayValue); | ||
|
||
protected static string JoinValues(MultiDictionary<string, string>.ValueSet values) => string.Join(", ", values.Order()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 'Order' seems like an odd name... i can guess what it likely means, but it's not super clear. That said, htis is exceptionally minor, so i don't really care if we can't make something better. #Resolved There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This will go away when I switch from |
||
protected static IEnumerable<string> SplitAndTrimValue(string displayValue) => displayValue.Split(',').Select(v => v.Trim()); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how about having ctro that contains both isWrittenTo and ReferenceInfo as parameter? #ByDesign
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have maked the the ctor with IsWrittenTo as obsolete. @CyrusNajmabadi told me that TS uses it, so I did not remove it. Hopefully they can move to new ctor and then we can completely remove IsWrittenTo and obsolete ctor. #ByDesign
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: i'm not certain if they use it. might as well check. if tehy don't, def just remove :) #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Verified that TypeScript does still use the constructor
public SourceReferenceItem(DefinitionItem definition, DocumentSpan sourceSpan, bool isWrittenTo)
. I am going to retain the existing approach.In reply to: 220759901 [](ancestors = 220759901)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
on the fence about ValueUsageInfo being baked into this API directly. I think i'd prefer if it just took a
MultiDictionary<string, string> ReferenceInfo
as an argument instead.Other minor note: Is MultiDictionary mutable? If so... i may have to retract my position about using it since we'd want immutable data here. If it is immutbale, great! #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it is. I will revert it back to
ImmutableDictionary<string, ImmutableArray<string>>
. #ResolvedThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Darn... sorry about that :(
#Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We invoke this from multiple places and we are caching the mapping from ValueUsageInfo to the reference info values map here, so for now I will retain this here.
In reply to: 222055535 [](ancestors = 222055535)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No issues :) moved back to ImmutableDictionary.
In reply to: 222063382 [](ancestors = 222063382)