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

Merge pull request #18564 from CyrusNajmabadi/symbolFindOOP4

Move graph query search OOP as well.
......@@ -7,9 +7,7 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.PatternMatching;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.VisualStudio.GraphModel;
using Roslyn.Utilities;
......@@ -108,36 +106,27 @@ private async Task<GraphNode> AddLinkedNodeForMemberAsync(Project project, ISymb
internal async Task<ImmutableArray<ISymbol>> FindNavigableSourceSymbolsAsync(
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
// 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
var results = ArrayBuilder<ISymbol>.GetInstance();
foreach (var symbol in symbols)
{
cancellationToken.ThrowIfCancellationRequested();
// 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));
// Ignore constructors and namespaces. We don't want to expose them through this API.
if (symbol.IsConstructor() ||
symbol.IsStaticConstructor() ||
symbol is INamespaceSymbol)
{
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;
}
......@@ -168,38 +157,5 @@ private async Task<GraphNode> AddLinkedNodeForMemberAsync(Project project, ISymb
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 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.PatternMatching;
using Microsoft.CodeAnalysis.Remote;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.FindSymbols
......@@ -82,6 +84,31 @@ internal static partial class DeclarationFinder
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
#region Remote Dispatch
......@@ -95,7 +122,7 @@ internal static partial class DeclarationFinder
if (session != null)
{
var result = await session.InvokeAsync<SerializableSymbolAndProjectId[]>(
nameof(IRemoteSymbolFinder.FindSolutionSourceDeclarationsWithNormalQuery),
nameof(IRemoteSymbolFinder.FindSolutionSourceDeclarationsWithNormalQueryAsync),
name, ignoreCase, criteria).ConfigureAwait(false);
var rehydrated = await RehydrateAsync(
......@@ -114,7 +141,7 @@ internal static partial class DeclarationFinder
if (session != null)
{
var result = await session.InvokeAsync<SerializableSymbolAndProjectId[]>(
nameof(IRemoteSymbolFinder.FindProjectSourceDeclarationsWithNormalQuery),
nameof(IRemoteSymbolFinder.FindProjectSourceDeclarationsWithNormalQueryAsync),
project.Id, name, ignoreCase, criteria).ConfigureAwait(false);
var rehydrated = await RehydrateAsync(
......@@ -126,6 +153,25 @@ internal static partial class DeclarationFinder
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
#region Local processing
......@@ -159,6 +205,81 @@ internal static partial class DeclarationFinder
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
}
}
\ No newline at end of file
......@@ -13,10 +13,13 @@ internal interface IRemoteSymbolFinder
Task<SerializableSymbolAndProjectId[]> FindAllDeclarationsWithNormalQueryAsync(
ProjectId projectId, string name, SearchKind searchKind, SymbolFilter criteria);
Task<SerializableSymbolAndProjectId[]> FindSolutionSourceDeclarationsWithNormalQuery(
Task<SerializableSymbolAndProjectId[]> FindSolutionSourceDeclarationsWithNormalQueryAsync(
string name, bool ignoreCase, SymbolFilter criteria);
Task<SerializableSymbolAndProjectId[]> FindProjectSourceDeclarationsWithNormalQuery(
Task<SerializableSymbolAndProjectId[]> FindProjectSourceDeclarationsWithNormalQueryAsync(
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
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)
{
if (project == null)
......
......@@ -46,7 +46,7 @@ public async Task FindLiteralReferencesAsync(object value)
return result.Select(SerializableSymbolAndProjectId.Dehydrate).ToArray();
}
public async Task<SerializableSymbolAndProjectId[]> FindSolutionSourceDeclarationsWithNormalQuery(
public async Task<SerializableSymbolAndProjectId[]> FindSolutionSourceDeclarationsWithNormalQueryAsync(
string name, bool ignoreCase, SymbolFilter criteria)
{
var solution = await GetSolutionAsync().ConfigureAwait(false);
......@@ -56,7 +56,7 @@ public async Task FindLiteralReferencesAsync(object value)
return result.Select(SerializableSymbolAndProjectId.Dehydrate).ToArray();
}
public async Task<SerializableSymbolAndProjectId[]> FindProjectSourceDeclarationsWithNormalQuery(
public async Task<SerializableSymbolAndProjectId[]> FindProjectSourceDeclarationsWithNormalQueryAsync(
ProjectId projectId, string name, bool ignoreCase, SymbolFilter criteria)
{
var solution = await GetSolutionAsync().ConfigureAwait(false);
......@@ -68,6 +68,18 @@ public async Task FindLiteralReferencesAsync(object value)
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 readonly CodeAnalysisService _service;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册