Skip to content
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

More work organizing code related to finding symbols. #18445

Merged
merged 2 commits into from
Apr 5, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions src/Workspaces/Core/Portable/FindSymbols/SearchKind.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// 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 Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.FindSymbols
{
internal enum SearchKind
{
/// <summary>
/// Use an case-sensitive comparison when searching for matching items.
/// </summary>
Exact,

/// <summary>
/// Use a case-insensitive comparison when searching for matching items.
/// </summary>
ExactIgnoreCase,

/// <summary>
/// Use a fuzzy comparison when searching for matching items. Fuzzy matching allows for
/// a certain amount of misspellings, missing words, etc. See <see cref="SpellChecker"/> for
/// more details.
/// </summary>
Fuzzy,

/// <summary>
/// Search term is matched in a custom manner (i.e. with a user provided predicate).
/// </summary>
Custom
}
}
71 changes: 71 additions & 0 deletions src/Workspaces/Core/Portable/FindSymbols/SearchQuery.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// 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 Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.FindSymbols
{
internal class SearchQuery
{
/// <summary>The name being searched for. Is null in the case of custom predicate searching.. But
/// can be used for faster index based searching when it is available.</summary>
public readonly string Name;

///<summary>The kind of search this is. Faster index-based searching can be used if the
/// SearchKind is not <see cref="SearchKind.Custom"/>.</summary>
public readonly SearchKind Kind;

///<summary>The predicate to fall back on if faster index searching is not possible.</summary>
private readonly Func<string, bool> _predicate;

private SearchQuery(string name, SearchKind kind)
{
Name = name ?? throw new ArgumentNullException(nameof(name));
Kind = kind;

switch (kind)
{
case SearchKind.Exact:
_predicate = s => StringComparer.Ordinal.Equals(name, s);
break;
case SearchKind.ExactIgnoreCase:
_predicate = s => CaseInsensitiveComparison.Comparer.Equals(name, s);
break;
case SearchKind.Fuzzy:
// Create a single WordSimilarityChecker and capture a delegate reference to
// its 'AreSimilar' method. That way we only create the WordSimilarityChecker
// once and it can cache all the information it needs while it does the AreSimilar
// check against all the possible candidates.
var editDistance = new WordSimilarityChecker(name, substringsAreSimilar: false);
_predicate = editDistance.AreSimilar;
break;
}
}

private SearchQuery(Func<string, bool> predicate)
{
Kind = SearchKind.Custom;
_predicate = predicate ?? throw new ArgumentNullException(nameof(predicate));
}

public static SearchQuery Create(string name, bool ignoreCase)
{
return new SearchQuery(name, ignoreCase ? SearchKind.ExactIgnoreCase : SearchKind.Exact);
}

public static SearchQuery CreateFuzzy(string name)
{
return new SearchQuery(name, SearchKind.Fuzzy);
}

public static SearchQuery CreateCustom(Func<string, bool> predicate)
{
return new SearchQuery(predicate);
}

public Func<string, bool> GetPredicate()
{
return _predicate;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,95 +14,6 @@

namespace Microsoft.CodeAnalysis.FindSymbols
{
internal enum SearchKind
{
/// <summary>
/// Use an case-sensitive comparison when searching for matching items.
/// </summary>
Exact,

/// <summary>
/// Use a case-insensitive comparison when searching for matching items.
/// </summary>
ExactIgnoreCase,

/// <summary>
/// Use a fuzzy comparison when searching for matching items. Fuzzy matching allows for
/// a certain amount of misspellings, missing words, etc. See <see cref="SpellChecker"/> for
/// more details.
/// </summary>
Fuzzy,

/// <summary>
/// Search term is matched in a custom manner (i.e. with a user provided predicate).
/// </summary>
Custom
}

internal class SearchQuery
{
/// <summary>The name being searched for. Is null in the case of custom predicate searching.. But
/// can be used for faster index based searching when it is available.</summary>
public readonly string Name;

///<summary>The kind of search this is. Faster index-based searching can be used if the
/// SearchKind is not <see cref="SearchKind.Custom"/>.</summary>
public readonly SearchKind Kind;

///<summary>The predicate to fall back on if faster index searching is not possible.</summary>
private readonly Func<string, bool> _predicate;

private SearchQuery(string name, SearchKind kind)
{
Name = name ?? throw new ArgumentNullException(nameof(name));
Kind = kind;

switch (kind)
{
case SearchKind.Exact:
_predicate = s => StringComparer.Ordinal.Equals(name, s);
break;
case SearchKind.ExactIgnoreCase:
_predicate = s => CaseInsensitiveComparison.Comparer.Equals(name, s);
break;
case SearchKind.Fuzzy:
// Create a single WordSimilarityChecker and capture a delegate reference to
// its 'AreSimilar' method. That way we only create the WordSimilarityChecker
// once and it can cache all the information it needs while it does the AreSimilar
// check against all the possible candidates.
var editDistance = new WordSimilarityChecker(name, substringsAreSimilar: false);
_predicate = editDistance.AreSimilar;
break;
}
}

private SearchQuery(Func<string, bool> predicate)
{
Kind = SearchKind.Custom;
_predicate = predicate ?? throw new ArgumentNullException(nameof(predicate));
}

public static SearchQuery Create(string name, bool ignoreCase)
{
return new SearchQuery(name, ignoreCase ? SearchKind.ExactIgnoreCase : SearchKind.Exact);
}

public static SearchQuery CreateFuzzy(string name)
{
return new SearchQuery(name, SearchKind.Fuzzy);
}

public static SearchQuery CreateCustom(Func<string, bool> predicate)
{
return new SearchQuery(predicate);
}

public Func<string, bool> GetPredicate()
{
return _predicate;
}
}

public static partial class SymbolFinder
{
/// <summary>
Expand Down Expand Up @@ -432,7 +343,7 @@ public static async Task<IEnumerable<ISymbol>> FindSourceDeclarationsAsync(
solution, SearchQuery.CreateCustom(predicate), filter, cancellationToken).ConfigureAwait(false);
}

internal static async Task<ImmutableArray<ISymbol>> FindSourceDeclarationsAsync(
private static async Task<ImmutableArray<ISymbol>> FindSourceDeclarationsAsync(
Solution solution, SearchQuery query, SymbolFilter filter, CancellationToken cancellationToken)
{
if (solution == null)
Expand Down Expand Up @@ -477,7 +388,7 @@ public static async Task<IEnumerable<ISymbol>> FindSourceDeclarationsAsync(
project, SearchQuery.CreateCustom(predicate), filter, cancellationToken).ConfigureAwait(false);
}

internal static async Task<ImmutableArray<ISymbol>> FindSourceDeclarationsAsync(
private static async Task<ImmutableArray<ISymbol>> FindSourceDeclarationsAsync(
Project project, SearchQuery query, SymbolFilter filter, CancellationToken cancellationToken)
{
if (project == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,6 @@ namespace Microsoft.CodeAnalysis.FindSymbols
{
public static partial class SymbolFinder
{
internal static async Task FindReferencesAsync(
SymbolAndProjectId symbolAndProjectId,
Solution solution,
IStreamingFindReferencesProgress progress,
IImmutableSet<Document> documents,
CancellationToken cancellationToken)
{
using (Logger.LogBlock(FunctionId.FindReference, cancellationToken))
{
var outOfProcessAllowed = solution.Workspace.Options.GetOption(SymbolFinderOptions.OutOfProcessAllowed);
if (symbolAndProjectId.ProjectId == null || !outOfProcessAllowed)
{
// This is a call through our old public API. We don't have the necessary
// data to effectively run the call out of proc.
await FindReferencesInCurrentProcessAsync(
symbolAndProjectId, solution, progress, documents, cancellationToken).ConfigureAwait(false);
}
else
{
await FindReferencesInServiceProcessAsync(
symbolAndProjectId, solution, progress, documents, cancellationToken).ConfigureAwait(false);
}
}
}

internal static async Task FindLiteralReferencesAsync(
object value,
Solution solution,
Expand All @@ -61,45 +36,6 @@ await FindLiteralReferencesInServiceProcessOrFallBackToLocalProcessAsync(
}
}

internal static Task FindReferencesInCurrentProcessAsync(
SymbolAndProjectId symbolAndProjectId, Solution solution,
IStreamingFindReferencesProgress progress, IImmutableSet<Document> documents,
CancellationToken cancellationToken)
{
var finders = ReferenceFinders.DefaultReferenceFinders;
progress = progress ?? StreamingFindReferencesProgress.Instance;
var engine = new FindReferencesSearchEngine(
solution, documents, finders, progress, cancellationToken);
return engine.FindReferencesAsync(symbolAndProjectId);
}

private static async Task FindReferencesInServiceProcessAsync(
SymbolAndProjectId symbolAndProjectId,
Solution solution,
IStreamingFindReferencesProgress progress,
IImmutableSet<Document> documents,
CancellationToken cancellationToken)
{
var client = await solution.Workspace.TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false);
if (client == null)
{
await FindReferencesInCurrentProcessAsync(
symbolAndProjectId, solution, progress, documents, cancellationToken).ConfigureAwait(false);
return;
}

// Create a callback that we can pass to the server process to hear about the
// results as it finds them. When we hear about results we'll forward them to
// the 'progress' parameter which will then update the UI.
var serverCallback = new FindReferencesServerCallback(solution, progress, cancellationToken);

await client.RunCodeAnalysisServiceOnRemoteHostAsync(
solution, serverCallback,
nameof(IRemoteSymbolFinder.FindReferencesAsync),
new object[] { SerializableSymbolAndProjectId.Dehydrate(symbolAndProjectId), documents?.Select(d => d.Id).ToArray() },
cancellationToken).ConfigureAwait(false);
}

internal static Task FindLiteralReferencesInCurrentProcessAsync(
object value, Solution solution,
IStreamingFindLiteralReferencesProgress progress,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// 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 System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.FindSymbols.Finders;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Remote;

namespace Microsoft.CodeAnalysis.FindSymbols
{
// This file contains the current FindReferences APIs. The current APIs allow for OOP
// implementation and will defer to the oop server if it is available. If not, it will
// compute the results in process.

public static partial class SymbolFinder
{
internal static async Task FindReferencesAsync(
SymbolAndProjectId symbolAndProjectId,
Solution solution,
IStreamingFindReferencesProgress progress,
IImmutableSet<Document> documents,
CancellationToken cancellationToken)
{
using (Logger.LogBlock(FunctionId.FindReference, cancellationToken))
{
var outOfProcessAllowed = solution.Workspace.Options.GetOption(SymbolFinderOptions.OutOfProcessAllowed);
if (symbolAndProjectId.ProjectId == null || !outOfProcessAllowed)
{
// This is a call through our old public API. We don't have the necessary
// data to effectively run the call out of proc.
await FindReferencesInCurrentProcessAsync(
symbolAndProjectId, solution, progress, documents, cancellationToken).ConfigureAwait(false);
}
else
{
await FindReferencesInServiceProcessAsync(
symbolAndProjectId, solution, progress, documents, cancellationToken).ConfigureAwait(false);
}
}
}

internal static Task FindReferencesInCurrentProcessAsync(
SymbolAndProjectId symbolAndProjectId, Solution solution,
IStreamingFindReferencesProgress progress, IImmutableSet<Document> documents,
CancellationToken cancellationToken)
{
var finders = ReferenceFinders.DefaultReferenceFinders;
progress = progress ?? StreamingFindReferencesProgress.Instance;
var engine = new FindReferencesSearchEngine(
solution, documents, finders, progress, cancellationToken);
return engine.FindReferencesAsync(symbolAndProjectId);
}

private static async Task FindReferencesInServiceProcessAsync(
SymbolAndProjectId symbolAndProjectId,
Solution solution,
IStreamingFindReferencesProgress progress,
IImmutableSet<Document> documents,
CancellationToken cancellationToken)
{
var client = await solution.Workspace.TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false);
if (client == null)
{
await FindReferencesInCurrentProcessAsync(
symbolAndProjectId, solution, progress, documents, cancellationToken).ConfigureAwait(false);
return;
}

// Create a callback that we can pass to the server process to hear about the
// results as it finds them. When we hear about results we'll forward them to
// the 'progress' parameter which will then update the UI.
var serverCallback = new FindReferencesServerCallback(solution, progress, cancellationToken);

await client.RunCodeAnalysisServiceOnRemoteHostAsync(
solution, serverCallback,
nameof(IRemoteSymbolFinder.FindReferencesAsync),
new object[] { SerializableSymbolAndProjectId.Dehydrate(symbolAndProjectId), documents?.Select(d => d.Id).ToArray() },
cancellationToken).ConfigureAwait(false);
}
}
}
Loading