| File: AddImport\SearchScopes\SearchScope.cs | Web Access |
| Project: src\roslyn\src\Features\Core\Portable\Microsoft.CodeAnalysis.Features.csproj (Microsoft.CodeAnalysis.Features) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable disable using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.AddImport; internal abstract partial class AbstractAddImportFeatureService<TSimpleNameSyntax> { /// <summary> /// SearchScope is used to control where the <see cref="AbstractAddImportFeatureService{TSimpleNameSyntax}"/> /// searches. We search different scopes in different ways. For example we use /// SymbolTreeInfos to search unreferenced projects and metadata dlls. However, /// for the current project we're editing we defer to the compiler to do the /// search. /// </summary> private abstract class SearchScope( AbstractAddImportFeatureService<TSimpleNameSyntax> provider, bool exact) { public readonly bool Exact = exact; protected readonly AbstractAddImportFeatureService<TSimpleNameSyntax> Provider = provider; protected abstract Task<ImmutableArray<ISymbol>> FindDeclarationsAsync(SymbolFilter filter, SearchQuery query, CancellationToken cancellationToken); public abstract SymbolReference CreateReference<T>(SymbolResult<T> symbol) where T : INamespaceOrTypeSymbol; public async Task<ImmutableArray<SymbolResult<ISymbol>>> FindDeclarationsAsync( string name, TSimpleNameSyntax nameNode, SymbolFilter filter, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); if (name != null && string.IsNullOrWhiteSpace(name)) return []; if (Exact) { // Try finding exact matches first. This provides better results for the common case of // people typing the right name, and it also allows for searching specialized indices that // contain those names quickly. { using var query = SearchQuery.Create(name, ignoreCase: false); var symbols = await FindDeclarationsAsync(filter, query, cancellationToken).ConfigureAwait(false); if (symbols.Length > 0) return symbols.SelectAsArray(static (s, nameNode) => SymbolResult.Create(s.Name, nameNode, s, weight: 0), nameNode); } // If no exact matches were found, fallback to a the weaker case insensitive search. This // uses heuristics that can find some additional results, but with less accuracy (so not // everything will necessarily be found). { using var query = SearchQuery.Create(name, ignoreCase: true); var symbols = await FindDeclarationsAsync(filter, query, cancellationToken).ConfigureAwait(false); // Use a weight of '1' to indicate that these were case insensitive matches, and any other // results from other search scoped should beat it. return symbols.SelectAsArray(static (s, nameNode) => SymbolResult.Create(s.Name, nameNode, s, weight: 1), nameNode); } } else { using var query = SearchQuery.CreateFuzzy(name); var symbols = await FindDeclarationsAsync(filter, query, cancellationToken).ConfigureAwait(false); // TODO(cyrusn): It's a shame we have to compute this twice. However, there's no // great way to store the original value we compute because it happens deep in the // compiler bowels when we call FindDeclarations. using var similarityChecker = new WordSimilarityChecker(name, substringsAreSimilar: false); var results = new FixedSizeArrayBuilder<SymbolResult<ISymbol>>(symbols.Length); foreach (var symbol in symbols) { var areSimilar = similarityChecker.AreSimilar(symbol.Name, out var matchCost); Debug.Assert(areSimilar); results.Add(SymbolResult.Create(symbol.Name, nameNode, symbol, matchCost)); } return results.MoveToImmutable(); } } } }