From 7d771b329ca29a7a9f7e7dd02a7c96d80e5eff4f Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 4 Apr 2017 16:19:17 -0700 Subject: [PATCH] Break types into their own files. --- .../Core/Portable/AddImport/SearchScope.cs | 207 ------------------ .../AllSymbolsProjectSearchScope.cs | 35 +++ .../MetadataSymbolsSearchScope.cs | 55 +++++ .../SearchScopes/ProjectSearchScope.cs | 30 +++ .../AddImport/SearchScopes/SearchScope.cs | 72 ++++++ .../SourceSymbolsProjectSearchScope.cs | 62 ++++++ src/Features/Core/Portable/Features.csproj | 6 +- 7 files changed, 259 insertions(+), 208 deletions(-) delete mode 100644 src/Features/Core/Portable/AddImport/SearchScope.cs create mode 100644 src/Features/Core/Portable/AddImport/SearchScopes/AllSymbolsProjectSearchScope.cs create mode 100644 src/Features/Core/Portable/AddImport/SearchScopes/MetadataSymbolsSearchScope.cs create mode 100644 src/Features/Core/Portable/AddImport/SearchScopes/ProjectSearchScope.cs create mode 100644 src/Features/Core/Portable/AddImport/SearchScopes/SearchScope.cs create mode 100644 src/Features/Core/Portable/AddImport/SearchScopes/SourceSymbolsProjectSearchScope.cs diff --git a/src/Features/Core/Portable/AddImport/SearchScope.cs b/src/Features/Core/Portable/AddImport/SearchScope.cs deleted file mode 100644 index ec1819a3b7d8f..0000000000000 --- a/src/Features/Core/Portable/AddImport/SearchScope.cs +++ /dev/null @@ -1,207 +0,0 @@ -// 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.Concurrent; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.FindSymbols; -using Microsoft.CodeAnalysis.FindSymbols.SymbolTree; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.CodeFixes.AddImport -{ - internal abstract partial class AbstractAddImportCodeFixProvider - { - /// - /// SearchScope is used to control where the - /// 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. - /// - private abstract class SearchScope - { - public readonly bool Exact; - protected readonly AbstractAddImportCodeFixProvider provider; - public readonly CancellationToken CancellationToken; - - protected SearchScope(AbstractAddImportCodeFixProvider provider, bool exact, CancellationToken cancellationToken) - { - this.provider = provider; - Exact = exact; - CancellationToken = cancellationToken; - } - - protected abstract Task> FindDeclarationsAsync(string name, SymbolFilter filter, SearchQuery query); - public abstract SymbolReference CreateReference(SymbolResult symbol) where T : INamespaceOrTypeSymbol; - - public async Task>> FindDeclarationsAsync( - string name, TSimpleNameSyntax nameNode, SymbolFilter filter) - { - if (name != null && string.IsNullOrWhiteSpace(name)) - { - return ImmutableArray>.Empty; - } - - var query = this.Exact ? SearchQuery.Create(name, ignoreCase: true) : SearchQuery.CreateFuzzy(name); - var symbols = await FindDeclarationsAsync(name, filter, query).ConfigureAwait(false); - - if (Exact) - { - // We did an exact, case insensitive, search. Case sensitive matches should - // be preferred though over insensitive ones. - return symbols.SelectAsArray(s => - SymbolResult.Create(s.Name, nameNode, s, weight: s.Name == name ? 0 : 1)); - } - - // 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)) - { - return symbols.SelectAsArray(s => - { - var areSimilar = similarityChecker.AreSimilar(s.Name, out var matchCost); - - Debug.Assert(areSimilar); - return SymbolResult.Create(s.Name, nameNode, s, matchCost); - }); - } - } - } - - private abstract class ProjectSearchScope : SearchScope - { - protected readonly Project _project; - - public ProjectSearchScope( - AbstractAddImportCodeFixProvider provider, - Project project, - bool exact, - CancellationToken cancellationToken) - : base(provider, exact, cancellationToken) - { - _project = project; - } - - public override SymbolReference CreateReference(SymbolResult symbol) - { - return new ProjectSymbolReference( - provider, symbol.WithSymbol(symbol.Symbol), _project); - } - } - - /// - /// SearchScope used for searching *all* the symbols contained within a project/compilation. - /// i.e. the symbols created from source *and* symbols from references (both project and - /// metadata). - /// - private class AllSymbolsProjectSearchScope : ProjectSearchScope - { - public AllSymbolsProjectSearchScope( - AbstractAddImportCodeFixProvider provider, - Project project, - bool exact, - CancellationToken cancellationToken) - : base(provider, project, exact, cancellationToken) - { - } - - protected override Task> FindDeclarationsAsync( - string name, SymbolFilter filter, SearchQuery searchQuery) - { - return SymbolFinder.FindDeclarationsAsync(_project, searchQuery, filter, CancellationToken); - } - } - - /// - /// SearchScope used for searching *only* the source symbols contained within a project/compilation. - /// i.e. symbols from metadata will not be searched. - /// - private class SourceSymbolsProjectSearchScope : ProjectSearchScope - { - private readonly ConcurrentDictionary> _projectToAssembly; - - public SourceSymbolsProjectSearchScope( - AbstractAddImportCodeFixProvider provider, - ConcurrentDictionary> projectToAssembly, - Project project, bool ignoreCase, CancellationToken cancellationToken) - : base(provider, project, ignoreCase, cancellationToken) - { - _projectToAssembly = projectToAssembly; - } - - protected override async Task> FindDeclarationsAsync( - string name, SymbolFilter filter, SearchQuery searchQuery) - { - var service = _project.Solution.Workspace.Services.GetService(); - var info = await service.TryGetSourceSymbolTreeInfoAsync(_project, CancellationToken).ConfigureAwait(false); - if (info == null) - { - // Looks like there was nothing in the cache. Return no results for now. - return ImmutableArray.Empty; - } - - // Don't create the assembly until it is actually needed by the SymbolTreeInfo.FindAsync - // code. Creating the assembly can be costly and we want to avoid it until it is actually - // needed. - var lazyAssembly = _projectToAssembly.GetOrAdd(_project, CreateLazyAssembly); - - return await info.FindAsync(searchQuery, lazyAssembly, filter, CancellationToken).ConfigureAwait(false); - } - - private static AsyncLazy CreateLazyAssembly(Project project) - { - return new AsyncLazy( - async c => - { - var compilation = await project.GetCompilationAsync(c).ConfigureAwait(false); - return compilation.Assembly; - }, cacheResult: true); - } - } - - private class MetadataSymbolsSearchScope : SearchScope - { - private readonly IAssemblySymbol _assembly; - private readonly PortableExecutableReference _metadataReference; - private readonly Solution _solution; - - public MetadataSymbolsSearchScope( - AbstractAddImportCodeFixProvider provider, - Solution solution, - IAssemblySymbol assembly, - PortableExecutableReference metadataReference, - bool exact, - CancellationToken cancellationToken) - : base(provider, exact, cancellationToken) - { - _solution = solution; - _assembly = assembly; - _metadataReference = metadataReference; - } - - public override SymbolReference CreateReference(SymbolResult searchResult) - { - return new MetadataSymbolReference( - provider, - searchResult.WithSymbol(searchResult.Symbol), - _metadataReference); - } - - protected override async Task> FindDeclarationsAsync( - string name, SymbolFilter filter, SearchQuery searchQuery) - { - var service = _solution.Workspace.Services.GetService(); - var info = await service.TryGetMetadataSymbolTreeInfoAsync(_solution, _metadataReference, CancellationToken).ConfigureAwait(false); - if (info == null) - { - return ImmutableArray.Empty; - } - - return await info.FindAsync(searchQuery, _assembly, filter, CancellationToken).ConfigureAwait(false); - } - } - } -} \ No newline at end of file diff --git a/src/Features/Core/Portable/AddImport/SearchScopes/AllSymbolsProjectSearchScope.cs b/src/Features/Core/Portable/AddImport/SearchScopes/AllSymbolsProjectSearchScope.cs new file mode 100644 index 0000000000000..930ac9477f22c --- /dev/null +++ b/src/Features/Core/Portable/AddImport/SearchScopes/AllSymbolsProjectSearchScope.cs @@ -0,0 +1,35 @@ +// 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.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.FindSymbols; + +namespace Microsoft.CodeAnalysis.CodeFixes.AddImport +{ + internal abstract partial class AbstractAddImportCodeFixProvider + { + /// + /// SearchScope used for searching *all* the symbols contained within a project/compilation. + /// i.e. the symbols created from source *and* symbols from references (both project and + /// metadata). + /// + private class AllSymbolsProjectSearchScope : ProjectSearchScope + { + public AllSymbolsProjectSearchScope( + AbstractAddImportCodeFixProvider provider, + Project project, + bool exact, + CancellationToken cancellationToken) + : base(provider, project, exact, cancellationToken) + { + } + + protected override Task> FindDeclarationsAsync( + string name, SymbolFilter filter, SearchQuery searchQuery) + { + return SymbolFinder.FindDeclarationsAsync(_project, searchQuery, filter, CancellationToken); + } + } + } +} \ No newline at end of file diff --git a/src/Features/Core/Portable/AddImport/SearchScopes/MetadataSymbolsSearchScope.cs b/src/Features/Core/Portable/AddImport/SearchScopes/MetadataSymbolsSearchScope.cs new file mode 100644 index 0000000000000..87f1a2cccd577 --- /dev/null +++ b/src/Features/Core/Portable/AddImport/SearchScopes/MetadataSymbolsSearchScope.cs @@ -0,0 +1,55 @@ +// 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.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.FindSymbols; +using Microsoft.CodeAnalysis.FindSymbols.SymbolTree; + +namespace Microsoft.CodeAnalysis.CodeFixes.AddImport +{ + internal abstract partial class AbstractAddImportCodeFixProvider + { + private class MetadataSymbolsSearchScope : SearchScope + { + private readonly IAssemblySymbol _assembly; + private readonly PortableExecutableReference _metadataReference; + private readonly Solution _solution; + + public MetadataSymbolsSearchScope( + AbstractAddImportCodeFixProvider provider, + Solution solution, + IAssemblySymbol assembly, + PortableExecutableReference metadataReference, + bool exact, + CancellationToken cancellationToken) + : base(provider, exact, cancellationToken) + { + _solution = solution; + _assembly = assembly; + _metadataReference = metadataReference; + } + + public override SymbolReference CreateReference(SymbolResult searchResult) + { + return new MetadataSymbolReference( + provider, + searchResult.WithSymbol(searchResult.Symbol), + _metadataReference); + } + + protected override async Task> FindDeclarationsAsync( + string name, SymbolFilter filter, SearchQuery searchQuery) + { + var service = _solution.Workspace.Services.GetService(); + var info = await service.TryGetMetadataSymbolTreeInfoAsync(_solution, _metadataReference, CancellationToken).ConfigureAwait(false); + if (info == null) + { + return ImmutableArray.Empty; + } + + return await info.FindAsync(searchQuery, _assembly, filter, CancellationToken).ConfigureAwait(false); + } + } + } +} \ No newline at end of file diff --git a/src/Features/Core/Portable/AddImport/SearchScopes/ProjectSearchScope.cs b/src/Features/Core/Portable/AddImport/SearchScopes/ProjectSearchScope.cs new file mode 100644 index 0000000000000..624ca43097c3c --- /dev/null +++ b/src/Features/Core/Portable/AddImport/SearchScopes/ProjectSearchScope.cs @@ -0,0 +1,30 @@ +// 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.Threading; + +namespace Microsoft.CodeAnalysis.CodeFixes.AddImport +{ + internal abstract partial class AbstractAddImportCodeFixProvider + { + private abstract class ProjectSearchScope : SearchScope + { + protected readonly Project _project; + + public ProjectSearchScope( + AbstractAddImportCodeFixProvider provider, + Project project, + bool exact, + CancellationToken cancellationToken) + : base(provider, exact, cancellationToken) + { + _project = project; + } + + public override SymbolReference CreateReference(SymbolResult symbol) + { + return new ProjectSymbolReference( + provider, symbol.WithSymbol(symbol.Symbol), _project); + } + } + } +} \ No newline at end of file diff --git a/src/Features/Core/Portable/AddImport/SearchScopes/SearchScope.cs b/src/Features/Core/Portable/AddImport/SearchScopes/SearchScope.cs new file mode 100644 index 0000000000000..51f86f6665cfa --- /dev/null +++ b/src/Features/Core/Portable/AddImport/SearchScopes/SearchScope.cs @@ -0,0 +1,72 @@ +// 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.Immutable; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.FindSymbols; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CodeFixes.AddImport +{ + internal abstract partial class AbstractAddImportCodeFixProvider + { + /// + /// SearchScope is used to control where the + /// 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. + /// + private abstract class SearchScope + { + public readonly bool Exact; + protected readonly AbstractAddImportCodeFixProvider provider; + public readonly CancellationToken CancellationToken; + + protected SearchScope(AbstractAddImportCodeFixProvider provider, bool exact, CancellationToken cancellationToken) + { + this.provider = provider; + Exact = exact; + CancellationToken = cancellationToken; + } + + protected abstract Task> FindDeclarationsAsync(string name, SymbolFilter filter, SearchQuery query); + public abstract SymbolReference CreateReference(SymbolResult symbol) where T : INamespaceOrTypeSymbol; + + public async Task>> FindDeclarationsAsync( + string name, TSimpleNameSyntax nameNode, SymbolFilter filter) + { + if (name != null && string.IsNullOrWhiteSpace(name)) + { + return ImmutableArray>.Empty; + } + + var query = this.Exact ? SearchQuery.Create(name, ignoreCase: true) : SearchQuery.CreateFuzzy(name); + var symbols = await FindDeclarationsAsync(name, filter, query).ConfigureAwait(false); + + if (Exact) + { + // We did an exact, case insensitive, search. Case sensitive matches should + // be preferred though over insensitive ones. + return symbols.SelectAsArray(s => + SymbolResult.Create(s.Name, nameNode, s, weight: s.Name == name ? 0 : 1)); + } + + // 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)) + { + return symbols.SelectAsArray(s => + { + var areSimilar = similarityChecker.AreSimilar(s.Name, out var matchCost); + + Debug.Assert(areSimilar); + return SymbolResult.Create(s.Name, nameNode, s, matchCost); + }); + } + } + } + } +} \ No newline at end of file diff --git a/src/Features/Core/Portable/AddImport/SearchScopes/SourceSymbolsProjectSearchScope.cs b/src/Features/Core/Portable/AddImport/SearchScopes/SourceSymbolsProjectSearchScope.cs new file mode 100644 index 0000000000000..53b08863b77bf --- /dev/null +++ b/src/Features/Core/Portable/AddImport/SearchScopes/SourceSymbolsProjectSearchScope.cs @@ -0,0 +1,62 @@ +// 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.Concurrent; +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.FindSymbols; +using Microsoft.CodeAnalysis.FindSymbols.SymbolTree; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CodeFixes.AddImport +{ + internal abstract partial class AbstractAddImportCodeFixProvider + { + /// + /// SearchScope used for searching *only* the source symbols contained within a project/compilation. + /// i.e. symbols from metadata will not be searched. + /// + private class SourceSymbolsProjectSearchScope : ProjectSearchScope + { + private readonly ConcurrentDictionary> _projectToAssembly; + + public SourceSymbolsProjectSearchScope( + AbstractAddImportCodeFixProvider provider, + ConcurrentDictionary> projectToAssembly, + Project project, bool ignoreCase, CancellationToken cancellationToken) + : base(provider, project, ignoreCase, cancellationToken) + { + _projectToAssembly = projectToAssembly; + } + + protected override async Task> FindDeclarationsAsync( + string name, SymbolFilter filter, SearchQuery searchQuery) + { + var service = _project.Solution.Workspace.Services.GetService(); + var info = await service.TryGetSourceSymbolTreeInfoAsync(_project, CancellationToken).ConfigureAwait(false); + if (info == null) + { + // Looks like there was nothing in the cache. Return no results for now. + return ImmutableArray.Empty; + } + + // Don't create the assembly until it is actually needed by the SymbolTreeInfo.FindAsync + // code. Creating the assembly can be costly and we want to avoid it until it is actually + // needed. + var lazyAssembly = _projectToAssembly.GetOrAdd(_project, CreateLazyAssembly); + + return await info.FindAsync(searchQuery, lazyAssembly, filter, CancellationToken).ConfigureAwait(false); + } + + private static AsyncLazy CreateLazyAssembly(Project project) + { + return new AsyncLazy( + async c => + { + var compilation = await project.GetCompilationAsync(c).ConfigureAwait(false); + return compilation.Assembly; + }, cacheResult: true); + } + } + } +} \ No newline at end of file diff --git a/src/Features/Core/Portable/Features.csproj b/src/Features/Core/Portable/Features.csproj index 0066eddf0b6ac..8bfa7d0ca6f60 100644 --- a/src/Features/Core/Portable/Features.csproj +++ b/src/Features/Core/Portable/Features.csproj @@ -100,6 +100,10 @@ Shared\Utilities\DesktopShim.cs + + + + @@ -216,7 +220,7 @@ - +