提交 e4adebbe 编写于 作者: C CyrusNajmabadi 提交者: GitHub

Merge pull request #13059 from CyrusNajmabadi/clearerFuzzyMatches

Make fuzzy add-using matches clearer

Fixes #12985
...@@ -47,21 +47,11 @@ protected override CodeActionPriority GetPriority(Document document) ...@@ -47,21 +47,11 @@ protected override CodeActionPriority GetPriority(Document document)
if (document.Project.Id == _project.Id) if (document.Project.Id == _project.Id)
{ {
if (!SearchResult.DesiredNameDiffersFromSourceName()) if (SearchResult.DesiredNameMatchesSourceName(document))
{ {
// The name doesn't change. This is a normal priority action. // The name doesn't change. This is a normal priority action.
return CodeActionPriority.Medium; 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<ISyntaxFactsService>();
if (!syntaxFacts.IsCaseSensitive &&
SearchResult.DesiredNameDiffersFromSourceNameOnlyByCase())
{
return CodeActionPriority.Medium;
}
} }
// This is a weaker match. This should be lower than all other fixes. // This is a weaker match. This should be lower than all other fixes.
......
...@@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.CodeFixes.AddImport ...@@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.CodeFixes.AddImport
{ {
internal abstract partial class AbstractAddImportCodeFixProvider<TSimpleNameSyntax> internal abstract partial class AbstractAddImportCodeFixProvider<TSimpleNameSyntax>
{ {
private abstract class Reference : IComparable<Reference>, IEquatable<Reference> private abstract class Reference : IEquatable<Reference>
{ {
protected readonly AbstractAddImportCodeFixProvider<TSimpleNameSyntax> provider; protected readonly AbstractAddImportCodeFixProvider<TSimpleNameSyntax> provider;
public readonly SearchResult SearchResult; public readonly SearchResult SearchResult;
...@@ -24,7 +24,7 @@ protected Reference(AbstractAddImportCodeFixProvider<TSimpleNameSyntax> provider ...@@ -24,7 +24,7 @@ protected Reference(AbstractAddImportCodeFixProvider<TSimpleNameSyntax> provider
this.SearchResult = searchResult; 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. // If references have different weights, order by the ones with lower weight (i.e.
// they are better matches). // they are better matches).
...@@ -38,7 +38,36 @@ public virtual int CompareTo(Reference other) ...@@ -38,7 +38,36 @@ public virtual int CompareTo(Reference other)
return 1; 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( return INamespaceOrTypeSymbolExtensions.CompareNameParts(
this.SearchResult.NameParts, other.SearchResult.NameParts); this.SearchResult.NameParts, other.SearchResult.NameParts);
} }
......
...@@ -26,19 +26,6 @@ public SymbolReference(AbstractAddImportCodeFixProvider<TSimpleNameSyntax> provi ...@@ -26,19 +26,6 @@ public SymbolReference(AbstractAddImportCodeFixProvider<TSimpleNameSyntax> provi
protected abstract Glyph? GetGlyph(Document document); protected abstract Glyph? GetGlyph(Document document);
protected abstract bool CheckForExistingImport(Project project); 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) public override bool Equals(object obj)
{ {
var equals = base.Equals(obj); var equals = base.Equals(obj);
...@@ -88,6 +75,13 @@ public override int GetHashCode() ...@@ -88,6 +75,13 @@ public override int GetHashCode()
return null; 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), return new OperationBasedCodeAction(description, GetGlyph(document), GetPriority(document),
c => this.GetOperationsAsync(document, node, placeSystemNamespaceFirst, c), c => this.GetOperationsAsync(document, node, placeSystemNamespaceFirst, c),
this.GetIsApplicableCheck(document.Project)); this.GetIsApplicableCheck(document.Project));
......
...@@ -74,10 +74,10 @@ private INamespaceSymbol MapToCompilationNamespaceIfPossible(INamespaceSymbol co ...@@ -74,10 +74,10 @@ private INamespaceSymbol MapToCompilationNamespaceIfPossible(INamespaceSymbol co
return _semanticModel.Compilation.GetCompilationNamespace(containingNamespace) ?? containingNamespace; return _semanticModel.Compilation.GetCompilationNamespace(containingNamespace) ?? containingNamespace;
} }
internal Task<List<SymbolReference>> FindInAllSymbolsInProjectAsync( internal Task<List<SymbolReference>> FindInAllSymbolsInStartingProjectAsync(
Project project, bool exact, CancellationToken cancellationToken) bool exact, CancellationToken cancellationToken)
{ {
var searchScope = new AllSymbolsProjectSearchScope(_owner, project, exact, cancellationToken); var searchScope = new AllSymbolsProjectSearchScope(_owner, _document.Project, exact, cancellationToken);
return DoAsync(searchScope); return DoAsync(searchScope);
} }
...@@ -91,11 +91,11 @@ private INamespaceSymbol MapToCompilationNamespaceIfPossible(INamespaceSymbol co ...@@ -91,11 +91,11 @@ private INamespaceSymbol MapToCompilationNamespaceIfPossible(INamespaceSymbol co
} }
internal Task<List<SymbolReference>> FindInMetadataSymbolsAsync( internal Task<List<SymbolReference>> FindInMetadataSymbolsAsync(
Solution solution, IAssemblySymbol assembly, PortableExecutableReference metadataReference, IAssemblySymbol assembly, PortableExecutableReference metadataReference,
bool exact, CancellationToken cancellationToken) bool exact, CancellationToken cancellationToken)
{ {
var searchScope = new MetadataSymbolsSearchScope( var searchScope = new MetadataSymbolsSearchScope(
_owner, solution, assembly, metadataReference, exact, cancellationToken); _owner, _document.Project.Solution, assembly, metadataReference, exact, cancellationToken);
return DoAsync(searchScope); return DoAsync(searchScope);
} }
...@@ -153,7 +153,7 @@ private List<SymbolReference> DeDupeAndSortReferences(List<SymbolReference> allR ...@@ -153,7 +153,7 @@ private List<SymbolReference> DeDupeAndSortReferences(List<SymbolReference> allR
.Distinct() .Distinct()
.Where(NotNull) .Where(NotNull)
.Where(NotGlobalNamespace) .Where(NotGlobalNamespace)
.Order() .OrderBy((r1, r2) => r1.CompareTo(_document, r2))
.ToList(); .ToList();
} }
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions;
namespace Microsoft.CodeAnalysis.CodeFixes.AddImport namespace Microsoft.CodeAnalysis.CodeFixes.AddImport
...@@ -49,6 +50,27 @@ public bool DesiredNameDiffersFromSourceNameOnlyByCase() ...@@ -49,6 +50,27 @@ public bool DesiredNameDiffersFromSourceNameOnlyByCase()
return StringComparer.OrdinalIgnoreCase.Equals( return StringComparer.OrdinalIgnoreCase.Equals(
this.NameNode.GetFirstToken().ValueText, this.DesiredName); 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<ISyntaxFactsService>();
// 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<T> where T : ISymbol private struct SymbolResult<T> where T : ISymbol
......
...@@ -160,8 +160,8 @@ private static bool IsHostOrTestWorkspace(Project project) ...@@ -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 // First search the current project to see if any symbols (source or metadata) match the
// search string. // search string.
await FindResultsInAllProjectSymbolsAsync( await FindResultsInAllSymbolsInStartingProjectAsync(
project, allReferences, finder, exact, cancellationToken).ConfigureAwait(false); allReferences, finder, exact, cancellationToken).ConfigureAwait(false);
// Only bother doing this for host workspaces. We don't want this for // 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 // things like the Interactive workspace as we can't even add project
...@@ -186,11 +186,11 @@ private static bool IsHostOrTestWorkspace(Project project) ...@@ -186,11 +186,11 @@ private static bool IsHostOrTestWorkspace(Project project)
return allReferences; return allReferences;
} }
private async Task FindResultsInAllProjectSymbolsAsync( private async Task FindResultsInAllSymbolsInStartingProjectAsync(
Project project, List<Reference> allSymbolReferences, List<Reference> allSymbolReferences, SymbolReferenceFinder finder,
SymbolReferenceFinder finder, bool exact, CancellationToken cancellationToken) 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); AddRange(allSymbolReferences, references);
} }
...@@ -272,7 +272,7 @@ private static bool IsHostOrTestWorkspace(Project project) ...@@ -272,7 +272,7 @@ private static bool IsHostOrTestWorkspace(Project project)
if (assembly != null) if (assembly != null)
{ {
findTasks.Add(finder.FindInMetadataSymbolsAsync( findTasks.Add(finder.FindInMetadataSymbolsAsync(
project.Solution, assembly, reference, exact, linkedTokenSource.Token)); assembly, reference, exact, linkedTokenSource.Token));
} }
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册