diff --git a/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.ProjectSymbolReference.cs b/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.ProjectSymbolReference.cs index 20d90a98cb5592a1fca8b1da935d776d42527ebf..3c02f29ed2c260b73c0ec1b4b667e31913113453 100644 --- a/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.ProjectSymbolReference.cs +++ b/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.ProjectSymbolReference.cs @@ -47,21 +47,11 @@ protected override CodeActionPriority GetPriority(Document document) if (document.Project.Id == _project.Id) { - if (!SearchResult.DesiredNameDiffersFromSourceName()) + if (SearchResult.DesiredNameMatchesSourceName(document)) { // The name doesn't change. This is a normal priority action. return CodeActionPriority.Medium; } - - // We need to update the source name. If all we're doing is changing the - // casing, and this is a case insensitive language, then this is just a - // normal priority action. Otherwise, this wil be low priority. - var syntaxFacts = document.GetLanguageService(); - if (!syntaxFacts.IsCaseSensitive && - SearchResult.DesiredNameDiffersFromSourceNameOnlyByCase()) - { - return CodeActionPriority.Medium; - } } // This is a weaker match. This should be lower than all other fixes. diff --git a/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.Reference.cs b/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.Reference.cs index e1099ef9ced7bdebd62f11731967e112a070e16d..dbfdcd787a0fb1253a42d738a403cb756761bad7 100644 --- a/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.Reference.cs +++ b/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.Reference.cs @@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.CodeFixes.AddImport { internal abstract partial class AbstractAddImportCodeFixProvider { - private abstract class Reference : IComparable, IEquatable + private abstract class Reference : IEquatable { protected readonly AbstractAddImportCodeFixProvider provider; public readonly SearchResult SearchResult; @@ -24,7 +24,7 @@ protected Reference(AbstractAddImportCodeFixProvider provider this.SearchResult = searchResult; } - public virtual int CompareTo(Reference other) + public int CompareTo(Document document, Reference other) { // If references have different weights, order by the ones with lower weight (i.e. // they are better matches). @@ -38,7 +38,36 @@ public virtual int CompareTo(Reference other) return 1; } - // If the weight are the same, just order them based on their names. + if (this.SearchResult.DesiredNameMatchesSourceName(document)) + { + if (!other.SearchResult.DesiredNameMatchesSourceName(document)) + { + // Prefer us as our name doesn't need to change. + return -1; + } + } + else + { + if (other.SearchResult.DesiredNameMatchesSourceName(document)) + { + // Prefer them as their name doesn't need to change. + return 1; + } + else + { + // Both our names need to change. Sort by the name we're + // changing to. + var diff = StringComparer.OrdinalIgnoreCase.Compare( + this.SearchResult.DesiredName, other.SearchResult.DesiredName); + if (diff != 0) + { + return diff; + } + } + } + + // If the weights are the same and no names changed, just order + // them based on the namespace we're adding an import for. return INamespaceOrTypeSymbolExtensions.CompareNameParts( this.SearchResult.NameParts, other.SearchResult.NameParts); } diff --git a/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.SymbolReference.cs b/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.SymbolReference.cs index 1db72b539b73caed1b93e54f18c701ed58e698df..daa4ecf13b6aa373658e51759d9453c0b65d0f36 100644 --- a/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.SymbolReference.cs +++ b/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.SymbolReference.cs @@ -26,19 +26,6 @@ public SymbolReference(AbstractAddImportCodeFixProvider provi protected abstract Glyph? GetGlyph(Document document); protected abstract bool CheckForExistingImport(Project project); - public override int CompareTo(Reference other) - { - var diff = base.CompareTo(other); - if (diff != 0) - { - return diff; - } - - var name1 = this.SymbolResult.DesiredName; - var name2 = (other as SymbolReference)?.SymbolResult.DesiredName; - return StringComparer.Ordinal.Compare(name1, name2); - } - public override bool Equals(object obj) { var equals = base.Equals(obj); @@ -88,6 +75,13 @@ public override int GetHashCode() return null; } + if (!this.SearchResult.DesiredNameMatchesSourceName(document)) + { + // The name is going to change. Make it clear in the description that + // this is going to happen. + description = $"{this.SearchResult.DesiredName} - {description}"; + } + return new OperationBasedCodeAction(description, GetGlyph(document), GetPriority(document), c => this.GetOperationsAsync(document, node, placeSystemNamespaceFirst, c), this.GetIsApplicableCheck(document.Project)); diff --git a/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.SymbolReferenceFinder.cs b/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.SymbolReferenceFinder.cs index 7b36d278a005c3f8cd437268883ce1e5eb8b73c4..e19030d5575ca0f2d0716af7b96b06f422db1bc6 100644 --- a/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.SymbolReferenceFinder.cs +++ b/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.SymbolReferenceFinder.cs @@ -74,10 +74,10 @@ private INamespaceSymbol MapToCompilationNamespaceIfPossible(INamespaceSymbol co return _semanticModel.Compilation.GetCompilationNamespace(containingNamespace) ?? containingNamespace; } - internal Task> FindInAllSymbolsInProjectAsync( - Project project, bool exact, CancellationToken cancellationToken) + internal Task> FindInAllSymbolsInStartingProjectAsync( + bool exact, CancellationToken cancellationToken) { - var searchScope = new AllSymbolsProjectSearchScope(_owner, project, exact, cancellationToken); + var searchScope = new AllSymbolsProjectSearchScope(_owner, _document.Project, exact, cancellationToken); return DoAsync(searchScope); } @@ -91,11 +91,11 @@ private INamespaceSymbol MapToCompilationNamespaceIfPossible(INamespaceSymbol co } internal Task> FindInMetadataSymbolsAsync( - Solution solution, IAssemblySymbol assembly, PortableExecutableReference metadataReference, + IAssemblySymbol assembly, PortableExecutableReference metadataReference, bool exact, CancellationToken cancellationToken) { var searchScope = new MetadataSymbolsSearchScope( - _owner, solution, assembly, metadataReference, exact, cancellationToken); + _owner, _document.Project.Solution, assembly, metadataReference, exact, cancellationToken); return DoAsync(searchScope); } @@ -153,7 +153,7 @@ private List DeDupeAndSortReferences(List allR .Distinct() .Where(NotNull) .Where(NotGlobalNamespace) - .Order() + .OrderBy((r1, r2) => r1.CompareTo(_document, r2)) .ToList(); } diff --git a/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.SymbolResult.cs b/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.SymbolResult.cs index 11e5aeaf2fdeb1be3cf0115e1aa534be01e6b7f9..5b6c869d31ab2b13eafb3b793ae50e87b6678413 100644 --- a/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.SymbolResult.cs +++ b/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.SymbolResult.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.CodeFixes.AddImport @@ -49,6 +50,27 @@ public bool DesiredNameDiffersFromSourceNameOnlyByCase() return StringComparer.OrdinalIgnoreCase.Equals( this.NameNode.GetFirstToken().ValueText, this.DesiredName); } + + public bool DesiredNameMatchesSourceName(Document document) + { + if (!this.DesiredNameDiffersFromSourceName()) + { + // Names match in any language. + return true; + } + + var syntaxFacts = document.GetLanguageService(); + + // Names differ. But in a case insensitive language they may match. + if (!syntaxFacts.IsCaseSensitive && + this.DesiredNameDiffersFromSourceNameOnlyByCase()) + { + return true; + } + + // Name are totally different in any language. + return false; + } } private struct SymbolResult where T : ISymbol diff --git a/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.cs b/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.cs index 09ac9f304b4fcec45ab2876b72caad91b51c4519..bc36903e9a2d1db369f1cce4a89fd19793727d00 100644 --- a/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.cs +++ b/src/Features/Core/Portable/CodeFixes/AddImport/AbstractAddImportCodeFixProvider.cs @@ -160,8 +160,8 @@ private static bool IsHostOrTestWorkspace(Project project) // First search the current project to see if any symbols (source or metadata) match the // search string. - await FindResultsInAllProjectSymbolsAsync( - project, allReferences, finder, exact, cancellationToken).ConfigureAwait(false); + await FindResultsInAllSymbolsInStartingProjectAsync( + allReferences, finder, exact, cancellationToken).ConfigureAwait(false); // Only bother doing this for host workspaces. We don't want this for // things like the Interactive workspace as we can't even add project @@ -186,11 +186,11 @@ private static bool IsHostOrTestWorkspace(Project project) return allReferences; } - private async Task FindResultsInAllProjectSymbolsAsync( - Project project, List allSymbolReferences, - SymbolReferenceFinder finder, bool exact, CancellationToken cancellationToken) + private async Task FindResultsInAllSymbolsInStartingProjectAsync( + List allSymbolReferences, SymbolReferenceFinder finder, + bool exact, CancellationToken cancellationToken) { - var references = await finder.FindInAllSymbolsInProjectAsync(project, exact, cancellationToken).ConfigureAwait(false); + var references = await finder.FindInAllSymbolsInStartingProjectAsync(exact, cancellationToken).ConfigureAwait(false); AddRange(allSymbolReferences, references); } @@ -272,7 +272,7 @@ private static bool IsHostOrTestWorkspace(Project project) if (assembly != null) { findTasks.Add(finder.FindInMetadataSymbolsAsync( - project.Solution, assembly, reference, exact, linkedTokenSource.Token)); + assembly, reference, exact, linkedTokenSource.Token)); } }