提交 0b9d0b43 编写于 作者: C CyrusNajmabadi 提交者: GitHub

Merge pull request #18564 from CyrusNajmabadi/symbolFindOOP4

Move graph query search OOP as well.
...@@ -7,9 +7,7 @@ ...@@ -7,9 +7,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.PatternMatching;
using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.VisualStudio.GraphModel; using Microsoft.VisualStudio.GraphModel;
using Roslyn.Utilities; using Roslyn.Utilities;
...@@ -108,36 +106,27 @@ private async Task<GraphNode> AddLinkedNodeForMemberAsync(Project project, ISymb ...@@ -108,36 +106,27 @@ private async Task<GraphNode> AddLinkedNodeForMemberAsync(Project project, ISymb
internal async Task<ImmutableArray<ISymbol>> FindNavigableSourceSymbolsAsync( internal async Task<ImmutableArray<ISymbol>> FindNavigableSourceSymbolsAsync(
Project project, CancellationToken cancellationToken) Project project, CancellationToken cancellationToken)
{ {
var results = ArrayBuilder<ISymbol>.GetInstance(); var declarations = await DeclarationFinder.FindSourceDeclarationsWithPatternAsync(
project, _searchPattern, SymbolFilter.TypeAndMember, cancellationToken).ConfigureAwait(false);
var symbols = declarations.SelectAsArray(d => d.Symbol);
// The compiler API only supports a predicate which is given a symbol's name. Because var results = ArrayBuilder<ISymbol>.GetInstance();
// we only have the name, and nothing else, we need to check it against the last segment
// of the pattern. i.e. if the pattern is 'Console.WL' and we are given 'WriteLine', then
// we don't want to check the whole pattern against it (as it will clearly fail), instead
// we only want to check the 'WL' portion. Then, after we get all the candidate symbols
// we'll check if the full name matches the full pattern.
var patternMatcher = new PatternMatcher(_searchPattern);
var symbols = await SymbolFinder.FindSourceDeclarationsAsync(
project, k => !patternMatcher.GetMatchesForLastSegmentOfPattern(k).IsDefaultOrEmpty, SymbolFilter.TypeAndMember, cancellationToken).ConfigureAwait(false);
symbols = symbols.Where(s =>
!s.IsConstructor()
&& !s.IsStaticConstructor() // not constructors, they get matched on type name
&& !(s is INamespaceSymbol) // not namespaces
&& s.Locations.Any(loc => loc.IsInSource)); // only source symbols
foreach (var symbol in symbols) foreach (var symbol in symbols)
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
// As an optimization, don't bother getting the container for this symbol if this // Ignore constructors and namespaces. We don't want to expose them through this API.
// isn't a dotted pattern. Getting the container could cause lots of string if (symbol.IsConstructor() ||
// allocations that we don't if we're never going to check it. symbol.IsStaticConstructor() ||
var matches = !patternMatcher.IsDottedPattern symbol is INamespaceSymbol)
? new PatternMatches(patternMatcher.GetMatches(GetSearchName(symbol))) {
: patternMatcher.GetMatches(GetSearchName(symbol), GetContainer(symbol)); continue;
}
if (matches.IsEmpty) // Ignore symbols that have no source location. We don't want to expose them through this API.
if (!symbol.Locations.Any(loc => loc.IsInSource))
{ {
continue; continue;
} }
...@@ -168,38 +157,5 @@ private async Task<GraphNode> AddLinkedNodeForMemberAsync(Project project, ISymb ...@@ -168,38 +157,5 @@ private async Task<GraphNode> AddLinkedNodeForMemberAsync(Project project, ISymb
return results.ToImmutableAndFree(); return results.ToImmutableAndFree();
} }
public static readonly SymbolDisplayFormat DottedNameFormat =
new SymbolDisplayFormat(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
delegateStyle: SymbolDisplayDelegateStyle.NameOnly,
extensionMethodStyle: SymbolDisplayExtensionMethodStyle.StaticMethod,
propertyStyle: SymbolDisplayPropertyStyle.NameOnly);
private static string GetContainer(ISymbol symbol)
{
var container = symbol.ContainingSymbol;
if (container == null)
{
return null;
}
return container.ToDisplayString(DottedNameFormat);
}
private static string GetSearchName(ISymbol symbol)
{
if (symbol.IsConstructor() || symbol.IsStaticConstructor())
{
return symbol.ContainingType.Name;
}
else if (symbol.IsIndexer() && symbol.Name == WellKnownMemberNames.Indexer)
{
return "this";
}
return symbol.Name;
}
} }
} }
\ No newline at end of file
...@@ -7,7 +7,9 @@ ...@@ -7,7 +7,9 @@
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.PatternMatching;
using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Remote;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities; using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.FindSymbols namespace Microsoft.CodeAnalysis.FindSymbols
...@@ -82,6 +84,31 @@ internal static partial class DeclarationFinder ...@@ -82,6 +84,31 @@ internal static partial class DeclarationFinder
project, name, ignoreCase, criteria, cancellationToken).ConfigureAwait(false); project, name, ignoreCase, criteria, cancellationToken).ConfigureAwait(false);
} }
public static async Task<ImmutableArray<SymbolAndProjectId>> FindSourceDeclarationsWithPatternAsync(
Project project, string pattern, SymbolFilter criteria, CancellationToken cancellationToken)
{
if (project == null)
{
throw new ArgumentNullException(nameof(project));
}
if (pattern == null)
{
throw new ArgumentNullException(nameof(pattern));
}
var (succeded, results) = await TryFindSourceDeclarationsWithPatternInRemoteProcessAsync(
project, pattern, criteria, cancellationToken).ConfigureAwait(false);
if (succeded)
{
return results;
}
return await FindSourceDeclarationsWithPatternInCurrentProcessAsync(
project, pattern, criteria, cancellationToken).ConfigureAwait(false);
}
#endregion #endregion
#region Remote Dispatch #region Remote Dispatch
...@@ -95,7 +122,7 @@ internal static partial class DeclarationFinder ...@@ -95,7 +122,7 @@ internal static partial class DeclarationFinder
if (session != null) if (session != null)
{ {
var result = await session.InvokeAsync<SerializableSymbolAndProjectId[]>( var result = await session.InvokeAsync<SerializableSymbolAndProjectId[]>(
nameof(IRemoteSymbolFinder.FindSolutionSourceDeclarationsWithNormalQuery), nameof(IRemoteSymbolFinder.FindSolutionSourceDeclarationsWithNormalQueryAsync),
name, ignoreCase, criteria).ConfigureAwait(false); name, ignoreCase, criteria).ConfigureAwait(false);
var rehydrated = await RehydrateAsync( var rehydrated = await RehydrateAsync(
...@@ -114,7 +141,7 @@ internal static partial class DeclarationFinder ...@@ -114,7 +141,7 @@ internal static partial class DeclarationFinder
if (session != null) if (session != null)
{ {
var result = await session.InvokeAsync<SerializableSymbolAndProjectId[]>( var result = await session.InvokeAsync<SerializableSymbolAndProjectId[]>(
nameof(IRemoteSymbolFinder.FindProjectSourceDeclarationsWithNormalQuery), nameof(IRemoteSymbolFinder.FindProjectSourceDeclarationsWithNormalQueryAsync),
project.Id, name, ignoreCase, criteria).ConfigureAwait(false); project.Id, name, ignoreCase, criteria).ConfigureAwait(false);
var rehydrated = await RehydrateAsync( var rehydrated = await RehydrateAsync(
...@@ -126,6 +153,25 @@ internal static partial class DeclarationFinder ...@@ -126,6 +153,25 @@ internal static partial class DeclarationFinder
return (false, ImmutableArray<SymbolAndProjectId>.Empty); return (false, ImmutableArray<SymbolAndProjectId>.Empty);
} }
private static async Task<(bool, ImmutableArray<SymbolAndProjectId>)> TryFindSourceDeclarationsWithPatternInRemoteProcessAsync(
Project project, string pattern, SymbolFilter criteria, CancellationToken cancellationToken)
{
var session = await SymbolFinder.TryGetRemoteSessionAsync(project.Solution, cancellationToken).ConfigureAwait(false);
if (session != null)
{
var result = await session.InvokeAsync<SerializableSymbolAndProjectId[]>(
nameof(IRemoteSymbolFinder.FindProjectSourceDeclarationsWithPatternAsync),
project.Id, pattern, criteria).ConfigureAwait(false);
var rehydrated = await RehydrateAsync(
project.Solution, result, cancellationToken).ConfigureAwait(false);
return (true, rehydrated);
}
return (false, ImmutableArray<SymbolAndProjectId>.Empty);
}
#endregion #endregion
#region Local processing #region Local processing
...@@ -159,6 +205,81 @@ internal static partial class DeclarationFinder ...@@ -159,6 +205,81 @@ internal static partial class DeclarationFinder
return list.ToImmutableAndFree(); return list.ToImmutableAndFree();
} }
internal static async Task<ImmutableArray<SymbolAndProjectId>> FindSourceDeclarationsWithPatternInCurrentProcessAsync(
Project project, string pattern, SymbolFilter criteria, CancellationToken cancellationToken)
{
// The compiler API only supports a predicate which is given a symbol's name. Because
// we only have the name, and nothing else, we need to check it against the last segment
// of the pattern. i.e. if the pattern is 'Console.WL' and we are given 'WriteLine', then
// we don't want to check the whole pattern against it (as it will clearly fail), instead
// we only want to check the 'WL' portion. Then, after we get all the candidate symbols
// we'll check if the full name matches the full pattern.
var patternMatcher = new PatternMatcher(pattern);
var query = SearchQuery.CreateCustom(
k => !patternMatcher.GetMatchesForLastSegmentOfPattern(k).IsDefaultOrEmpty);
var symbolAndProjectIds = await SymbolFinder.FindSourceDeclarationsWithCustomQueryAsync(
project, query, criteria, cancellationToken).ConfigureAwait(false);
var result = ArrayBuilder<SymbolAndProjectId>.GetInstance();
// Now see if the symbols the compiler returned actually match the full pattern.
foreach (var symbolAndProjectId in symbolAndProjectIds)
{
var symbol = symbolAndProjectId.Symbol;
// As an optimization, don't bother getting the container for this symbol if this
// isn't a dotted pattern. Getting the container could cause lots of string
// allocations that we don't if we're never going to check it.
var matches = !patternMatcher.IsDottedPattern
? new PatternMatches(patternMatcher.GetMatches(GetSearchName(symbol)))
: patternMatcher.GetMatches(GetSearchName(symbol), GetContainer(symbol));
if (matches.IsEmpty)
{
// Didn't actually match the full pattern, ignore it.
continue;
}
result.Add(symbolAndProjectId);
}
return result.ToImmutableAndFree();
}
private static string GetSearchName(ISymbol symbol)
{
if (symbol.IsConstructor() || symbol.IsStaticConstructor())
{
return symbol.ContainingType.Name;
}
else if (symbol.IsIndexer() && symbol.Name == WellKnownMemberNames.Indexer)
{
return "this";
}
return symbol.Name;
}
private static string GetContainer(ISymbol symbol)
{
var container = symbol.ContainingSymbol;
if (container == null)
{
return null;
}
return container.ToDisplayString(DottedNameFormat);
}
private static readonly SymbolDisplayFormat DottedNameFormat =
new SymbolDisplayFormat(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
delegateStyle: SymbolDisplayDelegateStyle.NameOnly,
extensionMethodStyle: SymbolDisplayExtensionMethodStyle.StaticMethod,
propertyStyle: SymbolDisplayPropertyStyle.NameOnly);
#endregion #endregion
} }
} }
\ No newline at end of file
...@@ -13,10 +13,13 @@ internal interface IRemoteSymbolFinder ...@@ -13,10 +13,13 @@ internal interface IRemoteSymbolFinder
Task<SerializableSymbolAndProjectId[]> FindAllDeclarationsWithNormalQueryAsync( Task<SerializableSymbolAndProjectId[]> FindAllDeclarationsWithNormalQueryAsync(
ProjectId projectId, string name, SearchKind searchKind, SymbolFilter criteria); ProjectId projectId, string name, SearchKind searchKind, SymbolFilter criteria);
Task<SerializableSymbolAndProjectId[]> FindSolutionSourceDeclarationsWithNormalQuery( Task<SerializableSymbolAndProjectId[]> FindSolutionSourceDeclarationsWithNormalQueryAsync(
string name, bool ignoreCase, SymbolFilter criteria); string name, bool ignoreCase, SymbolFilter criteria);
Task<SerializableSymbolAndProjectId[]> FindProjectSourceDeclarationsWithNormalQuery( Task<SerializableSymbolAndProjectId[]> FindProjectSourceDeclarationsWithNormalQueryAsync(
ProjectId projectId, string name, bool ignoreCase, SymbolFilter criteria); ProjectId projectId, string name, bool ignoreCase, SymbolFilter criteria);
Task<SerializableSymbolAndProjectId[]> FindProjectSourceDeclarationsWithPatternAsync(
ProjectId projectId, string pattern, SymbolFilter criteria);
} }
} }
\ No newline at end of file
...@@ -80,7 +80,7 @@ public static async Task<IEnumerable<ISymbol>> FindSourceDeclarationsAsync(Proje ...@@ -80,7 +80,7 @@ public static async Task<IEnumerable<ISymbol>> FindSourceDeclarationsAsync(Proje
return declarations.SelectAsArray(d => d.Symbol); return declarations.SelectAsArray(d => d.Symbol);
} }
private static async Task<ImmutableArray<SymbolAndProjectId>> FindSourceDeclarationsWithCustomQueryAsync( internal static async Task<ImmutableArray<SymbolAndProjectId>> FindSourceDeclarationsWithCustomQueryAsync(
Project project, SearchQuery query, SymbolFilter filter, CancellationToken cancellationToken) Project project, SearchQuery query, SymbolFilter filter, CancellationToken cancellationToken)
{ {
if (project == null) if (project == null)
......
...@@ -46,7 +46,7 @@ public async Task FindLiteralReferencesAsync(object value) ...@@ -46,7 +46,7 @@ public async Task FindLiteralReferencesAsync(object value)
return result.Select(SerializableSymbolAndProjectId.Dehydrate).ToArray(); return result.Select(SerializableSymbolAndProjectId.Dehydrate).ToArray();
} }
public async Task<SerializableSymbolAndProjectId[]> FindSolutionSourceDeclarationsWithNormalQuery( public async Task<SerializableSymbolAndProjectId[]> FindSolutionSourceDeclarationsWithNormalQueryAsync(
string name, bool ignoreCase, SymbolFilter criteria) string name, bool ignoreCase, SymbolFilter criteria)
{ {
var solution = await GetSolutionAsync().ConfigureAwait(false); var solution = await GetSolutionAsync().ConfigureAwait(false);
...@@ -56,7 +56,7 @@ public async Task FindLiteralReferencesAsync(object value) ...@@ -56,7 +56,7 @@ public async Task FindLiteralReferencesAsync(object value)
return result.Select(SerializableSymbolAndProjectId.Dehydrate).ToArray(); return result.Select(SerializableSymbolAndProjectId.Dehydrate).ToArray();
} }
public async Task<SerializableSymbolAndProjectId[]> FindProjectSourceDeclarationsWithNormalQuery( public async Task<SerializableSymbolAndProjectId[]> FindProjectSourceDeclarationsWithNormalQueryAsync(
ProjectId projectId, string name, bool ignoreCase, SymbolFilter criteria) ProjectId projectId, string name, bool ignoreCase, SymbolFilter criteria)
{ {
var solution = await GetSolutionAsync().ConfigureAwait(false); var solution = await GetSolutionAsync().ConfigureAwait(false);
...@@ -68,6 +68,18 @@ public async Task FindLiteralReferencesAsync(object value) ...@@ -68,6 +68,18 @@ public async Task FindLiteralReferencesAsync(object value)
return result.Select(SerializableSymbolAndProjectId.Dehydrate).ToArray(); return result.Select(SerializableSymbolAndProjectId.Dehydrate).ToArray();
} }
public async Task<SerializableSymbolAndProjectId[]> FindProjectSourceDeclarationsWithPatternAsync(
ProjectId projectId, string pattern, SymbolFilter criteria)
{
var solution = await GetSolutionAsync().ConfigureAwait(false);
var project = solution.GetProject(projectId);
var result = await DeclarationFinder.FindSourceDeclarationsWithPatternInCurrentProcessAsync(
project, pattern, criteria, CancellationToken).ConfigureAwait(false);
return result.Select(SerializableSymbolAndProjectId.Dehydrate).ToArray();
}
private class FindLiteralReferencesProgressCallback : IStreamingFindLiteralReferencesProgress private class FindLiteralReferencesProgressCallback : IStreamingFindLiteralReferencesProgress
{ {
private readonly CodeAnalysisService _service; private readonly CodeAnalysisService _service;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册