diff --git a/src/Features/CSharp/Portable/AddImport/CSharpAddImportCodeFixProvider.cs b/src/Features/CSharp/Portable/AddImport/CSharpAddImportCodeFixProvider.cs index e391297bc804bbf0407812aa929cd1aa6d22f4e8..e361b756fa4a7ed2f9c3f91ef9f8ed45dde058c4 100644 --- a/src/Features/CSharp/Portable/AddImport/CSharpAddImportCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/AddImport/CSharpAddImportCodeFixProvider.cs @@ -174,11 +174,11 @@ protected override bool CanAddImport(SyntaxNode node, CancellationToken cancella } protected override bool CanAddImportForMethod( - Diagnostic diagnostic, ISyntaxFactsService syntaxFacts, SyntaxNode node, out SimpleNameSyntax nameNode) + string diagnosticId, ISyntaxFactsService syntaxFacts, SyntaxNode node, out SimpleNameSyntax nameNode) { nameNode = null; - switch (diagnostic.Id) + switch (diagnosticId) { case CS7036: case CS0428: @@ -264,18 +264,18 @@ protected override bool CanAddImport(SyntaxNode node, CancellationToken cancella return true; } - protected override bool CanAddImportForDeconstruct(Diagnostic diagnostic, SyntaxNode node) - => diagnostic.Id == CS8129; + protected override bool CanAddImportForDeconstruct(string diagnosticId, SyntaxNode node) + => diagnosticId == CS8129; - protected override bool CanAddImportForNamespace(Diagnostic diagnostic, SyntaxNode node, out SimpleNameSyntax nameNode) + protected override bool CanAddImportForNamespace(string diagnosticId, SyntaxNode node, out SimpleNameSyntax nameNode) { nameNode = null; return false; } - protected override bool CanAddImportForQuery(Diagnostic diagnostic, SyntaxNode node) + protected override bool CanAddImportForQuery(string diagnosticId, SyntaxNode node) { - if (diagnostic.Id != CS1935) + if (diagnosticId != CS1935) { return false; } @@ -283,10 +283,10 @@ protected override bool CanAddImportForQuery(Diagnostic diagnostic, SyntaxNode n return node.AncestorsAndSelf().Any(n => n is QueryExpressionSyntax && !(n.Parent is QueryContinuationSyntax)); } - protected override bool CanAddImportForType(Diagnostic diagnostic, SyntaxNode node, out SimpleNameSyntax nameNode) + protected override bool CanAddImportForType(string diagnosticId, SyntaxNode node, out SimpleNameSyntax nameNode) { nameNode = null; - switch (diagnostic.Id) + switch (diagnosticId) { case CS0103: case CS0246: diff --git a/src/Features/Core/Portable/AddImport/AbstractAddImportCodeFixProvider.cs b/src/Features/Core/Portable/AddImport/AbstractAddImportCodeFixProvider.cs index bf28a2a12dff898452215db8ecdd96724ae0f1f5..c5c8a1b96d75e55cd064d350dd5916d117f5b23a 100644 --- a/src/Features/Core/Portable/AddImport/AbstractAddImportCodeFixProvider.cs +++ b/src/Features/Core/Portable/AddImport/AbstractAddImportCodeFixProvider.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Host; @@ -40,11 +41,11 @@ internal abstract partial class AbstractAddImportCodeFixProvider GetImportNamespacesInScope(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken); protected abstract ITypeSymbol GetDeconstructInfo(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken); @@ -98,16 +99,17 @@ private async Task HandleDiagnosticAsync(CodeFixContext context, Diagnostic if (this.CanAddImport(node, cancellationToken)) { var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var allSymbolReferences = await FindResultsAsync(document, semanticModel, diagnostic, node, cancellationToken).ConfigureAwait(false); + var allSymbolReferences = await FindResultsAsync(document, semanticModel, diagnostic.Id, node, cancellationToken).ConfigureAwait(false); // Nothing found at all. No need to proceed. foreach (var reference in allSymbolReferences) { cancellationToken.ThrowIfCancellationRequested(); - var codeAction = await reference.CreateCodeActionAsync(document, node, placeSystemNamespaceFirst, cancellationToken).ConfigureAwait(false); - if (codeAction != null) + var fixData = await reference.GetFixDataAsync(document, node, placeSystemNamespaceFirst, cancellationToken).ConfigureAwait(false); + if (fixData != null) { + var codeAction = CreateCodeAction(document, fixData); context.RegisterCodeFix(codeAction, diagnostic); count++; } @@ -120,8 +122,35 @@ private async Task HandleDiagnosticAsync(CodeFixContext context, Diagnostic return count; } + private CodeAction CreateCodeAction( + Document document, AddImportFixData fixData) + { + switch (fixData.Kind) + { + case AddImportFixKind.ProjectSymbol: + return new ProjectSymbolReferenceCodeAction(document, fixData); + + case AddImportFixKind.MetadataSymbol: + return new MetadataSymbolReferenceCodeAction(document, fixData); + + case AddImportFixKind.PackageSymbol: + return new ParentInstallPackageCodeAction(document, fixData, GetPackageInstallerService(document)); + + case AddImportFixKind.ReferenceAssemblySymbol: + return new AssemblyReferenceCodeAction(document, fixData); + } + + throw ExceptionUtilities.Unreachable; + } + + private IPackageInstallerService GetPackageInstallerService(Document document) + { + var workspaceServices = document.Project.Solution.Workspace.Services; + return _packageInstallerService ?? workspaceServices.GetService(); + } + private async Task> FindResultsAsync( - Document document, SemanticModel semanticModel, Diagnostic diagnostic, SyntaxNode node, CancellationToken cancellationToken) + Document document, SemanticModel semanticModel, string diagnosticId, SyntaxNode node, CancellationToken cancellationToken) { // Caches so we don't produce the same data multiple times while searching // all over the solution. @@ -129,7 +158,7 @@ private async Task HandleDiagnosticAsync(CodeFixContext context, Diagnostic var projectToAssembly = new ConcurrentDictionary>(concurrencyLevel: 2, capacity: project.Solution.ProjectIds.Count); var referenceToCompilation = new ConcurrentDictionary(concurrencyLevel: 2, capacity: project.Solution.Projects.Sum(p => p.MetadataReferences.Count)); - var finder = new SymbolReferenceFinder(this, document, semanticModel, diagnostic, node, cancellationToken); + var finder = new SymbolReferenceFinder(this, document, semanticModel, diagnosticId, node, cancellationToken); // Look for exact matches first: var exactReferences = await FindResultsAsync(projectToAssembly, referenceToCompilation, project, finder, exact: true, cancellationToken: cancellationToken).ConfigureAwait(false); diff --git a/src/Features/Core/Portable/AddImport/AddImportFixData.cs b/src/Features/Core/Portable/AddImport/AddImportFixData.cs new file mode 100644 index 0000000000000000000000000000000000000000..93835273e65e6f72c2d8b079a4c0215353a4279e --- /dev/null +++ b/src/Features/Core/Portable/AddImport/AddImportFixData.cs @@ -0,0 +1,133 @@ +// 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 Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.Tags; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.AddImport +{ + internal class AddImportFixData + { + public AddImportFixKind Kind { get; } + + /// + /// Text changes to make to the document. Usually just the import to add. May also + /// include a change to the name node the feature was invoked on to fix the casing of it. + /// May be empty for fixes that don't need to add an import and only do something like + /// add a project/metadata reference. + /// + public ImmutableArray TextChanges { get; } + + /// + /// String to display in the lightbulb menu. + /// + public string Title { get; private set; } + + /// + /// Tags that control what glyph is displayed in the lightbulb menu. + /// + public ImmutableArray Tags { get; private set; } + + /// + /// The priority this item should have in the lightbulb list. + /// + public CodeActionPriority Priority { get; private set; } + + #region When adding P2P refrences. + + /// + /// The optional id for a we'd like to add a reference to. + /// + public ProjectId ProjectReferenceToAdd { get; private set; } + + #endregion + + #region When adding a metadata reference + + /// + /// If we're adding then this + /// is the id for the we can find that + /// referenced from. + /// + public ProjectId PortableExecutableReferenceProjectId { get; private set; } + + /// + /// If we want to add a metadata reference, this + /// is the for it. + /// + public string PortableExecutableReferenceFilePathToAdd { get; private set; } + + #endregion + + #region When adding an assembly reference + + public string AssemblyReferenceAssemblyName { get; private set; } + public string AssemblyReferenceFullyQualifiedTypeName { get; private set; } + + #endregion + + #region When adding a package reference + + public string PackageSource { get; private set; } + public string PackageName { get; private set; } + public string PackageVersionOpt { get; private set; } + + #endregion + + private AddImportFixData( + AddImportFixKind kind, + ImmutableArray textChanges) + { + Kind = kind; + TextChanges = textChanges; + Tags = ImmutableArray.Empty; + } + + public static AddImportFixData CreateForProjectSymbol(ImmutableArray textChanges, string title, ImmutableArray tags, CodeActionPriority priority, ProjectId projectReferenceToAdd) + { + return new AddImportFixData(AddImportFixKind.ProjectSymbol, textChanges) + { + Title = title, + Tags = tags, + Priority = priority, + ProjectReferenceToAdd = projectReferenceToAdd + }; + } + + public static AddImportFixData CreateForMetadataSymbol(ImmutableArray textChanges, string title, ImmutableArray tags, CodeActionPriority priority, ProjectId portableExecutableReferenceProjectId, string portableExecutableReferenceFilePathToAdd) + { + return new AddImportFixData(AddImportFixKind.MetadataSymbol, textChanges) + { + Title = title, + Tags = tags, + Priority = priority, + PortableExecutableReferenceProjectId = portableExecutableReferenceProjectId, + PortableExecutableReferenceFilePathToAdd = portableExecutableReferenceFilePathToAdd + }; + } + + public static AddImportFixData CreateForReferenceAssemblySymbol(ImmutableArray textChanges, string title, string assemblyReferenceAssemblyName, string assemblyReferenceFullyQualifiedTypeName) + { + return new AddImportFixData(AddImportFixKind.ReferenceAssemblySymbol, textChanges) + { + Title = title, + Tags = WellKnownTagArrays.AddReference, + Priority = CodeActionPriority.Low, + AssemblyReferenceAssemblyName = assemblyReferenceAssemblyName, + AssemblyReferenceFullyQualifiedTypeName = assemblyReferenceFullyQualifiedTypeName + }; + } + + public static AddImportFixData CreateForPackageSymbol(ImmutableArray textChanges, string packageSource, string packageName, string packageVersionOpt) + { + return new AddImportFixData(AddImportFixKind.PackageSymbol, textChanges) + { + PackageSource = packageSource, + Priority = CodeActionPriority.Low, + PackageName = packageName, + PackageVersionOpt = packageVersionOpt, + }; + } + } +} \ No newline at end of file diff --git a/src/Features/Core/Portable/AddImport/AddImportFixKind.cs b/src/Features/Core/Portable/AddImport/AddImportFixKind.cs new file mode 100644 index 0000000000000000000000000000000000000000..7632bbc5d6127580ac579ad78752d1a48d344fc8 --- /dev/null +++ b/src/Features/Core/Portable/AddImport/AddImportFixKind.cs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + + +namespace Microsoft.CodeAnalysis.AddImport +{ + internal enum AddImportFixKind + { + ProjectSymbol, + MetadataSymbol, + PackageSymbol, + ReferenceAssemblySymbol, + } +} \ No newline at end of file diff --git a/src/Features/Core/Portable/AddImport/CodeActions/AddImportCodeAction.cs b/src/Features/Core/Portable/AddImport/CodeActions/AddImportCodeAction.cs index 0a0370352ebcd6bac204c6f4e1f67ad270dfdacd..33addab955f3803cad13eb08d717f59609e6847a 100644 --- a/src/Features/Core/Portable/AddImport/CodeActions/AddImportCodeAction.cs +++ b/src/Features/Core/Portable/AddImport/CodeActions/AddImportCodeAction.cs @@ -18,7 +18,9 @@ internal abstract partial class AbstractAddImportCodeFixProvider private abstract class AddImportCodeAction : CodeAction { - public sealed override string Title { get; } + protected readonly AddImportFixData FixData; + + public override string Title { get; } public sealed override ImmutableArray Tags { get; } internal sealed override CodeActionPriority Priority { get; } @@ -36,15 +38,15 @@ private abstract class AddImportCodeAction : CodeAction protected AddImportCodeAction( Document originalDocument, - ImmutableArray textChanges, - string title, ImmutableArray tags, - CodeActionPriority priority) + AddImportFixData fixData) { OriginalDocument = originalDocument; - Title = title; - Tags = tags; - Priority = priority; - _textChanges = textChanges; + FixData = fixData; + + Title = fixData.Title; + Tags = fixData.Tags; + Priority = fixData.Priority; + _textChanges = fixData.TextChanges; } protected async Task GetUpdatedDocumentAsync(CancellationToken cancellationToken) diff --git a/src/Features/Core/Portable/AddImport/CodeActions/AssemblyReferenceCodeAction.cs b/src/Features/Core/Portable/AddImport/CodeActions/AssemblyReferenceCodeAction.cs index 47e8d572fe01b96135336fac4ecaacf12b338a12..8959bea4a1ad0ae24cda9f950f451f122efa454b 100644 --- a/src/Features/Core/Portable/AddImport/CodeActions/AssemblyReferenceCodeAction.cs +++ b/src/Features/Core/Portable/AddImport/CodeActions/AssemblyReferenceCodeAction.cs @@ -2,13 +2,10 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Tags; -using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.AddImport @@ -17,22 +14,14 @@ internal abstract partial class AbstractAddImportCodeFixProvider _lazyResolvedPath; public AssemblyReferenceCodeAction( Document originalDocument, - ImmutableArray textChanges, - string title, - string assemblyName, - string fullyQualifiedTypeName) - : base(originalDocument, textChanges, title, WellKnownTagArrays.AddReference, CodeActionPriority.Low) + AddImportFixData fixData) + : base(originalDocument, fixData) { - _assemblyName = assemblyName; - _fullyQualifiedTypeName = fullyQualifiedTypeName; - + Contract.ThrowIfFalse(fixData.Kind == AddImportFixKind.ReferenceAssemblySymbol); _lazyResolvedPath = new Lazy(ResolvePath); } @@ -41,7 +30,9 @@ private string ResolvePath() var assemblyResolverService = OriginalDocument.Project.Solution.Workspace.Services.GetService(); var assemblyPath = assemblyResolverService?.ResolveAssemblyPath( - OriginalDocument.Project.Id, _assemblyName, _fullyQualifiedTypeName); + OriginalDocument.Project.Id, + FixData.AssemblyReferenceAssemblyName, + FixData.AssemblyReferenceFullyQualifiedTypeName); return assemblyPath; } diff --git a/src/Features/Core/Portable/AddImport/CodeActions/InstallPackageAndAddImportCodeAction.cs b/src/Features/Core/Portable/AddImport/CodeActions/InstallPackageAndAddImportCodeAction.cs index 666244f15f664995221f4f5cfd96e72e4bfdc92c..d767c2f7ec6f9183d5a6310ce9f1c6265999889f 100644 --- a/src/Features/Core/Portable/AddImport/CodeActions/InstallPackageAndAddImportCodeAction.cs +++ b/src/Features/Core/Portable/AddImport/CodeActions/InstallPackageAndAddImportCodeAction.cs @@ -1,6 +1,5 @@ // 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 System.Collections.Generic; using System.Collections.Immutable; using System.Threading; @@ -9,6 +8,7 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.AddImport { @@ -16,6 +16,8 @@ internal abstract partial class AbstractAddImportCodeFixProvider /// The operation that will actually install the nuget package. /// @@ -23,12 +25,13 @@ private class InstallPackageAndAddImportCodeAction : AddImportCodeAction public InstallPackageAndAddImportCodeAction( Document originalDocument, - ImmutableArray textChanges, + AddImportFixData fixData, string title, - CodeActionPriority priority, InstallPackageDirectlyCodeActionOperation installOperation) - : base(originalDocument, textChanges, title, ImmutableArray.Empty, priority) + : base(originalDocument, fixData) { + Contract.ThrowIfFalse(fixData.Kind == AddImportFixKind.PackageSymbol); + Title = title; _installOperation = installOperation; } diff --git a/src/Features/Core/Portable/AddImport/CodeActions/MetadataSymbolReferenceCodeAction.cs b/src/Features/Core/Portable/AddImport/CodeActions/MetadataSymbolReferenceCodeAction.cs index 13f315b3b87b33eb641465cc9c00c79ec3490859..137faa0a165d56f92f68c4ffa387691899f25ccf 100644 --- a/src/Features/Core/Portable/AddImport/CodeActions/MetadataSymbolReferenceCodeAction.cs +++ b/src/Features/Core/Portable/AddImport/CodeActions/MetadataSymbolReferenceCodeAction.cs @@ -1,9 +1,6 @@ // 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.Linq; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.AddImport @@ -12,38 +9,18 @@ internal abstract partial class AbstractAddImportCodeFixProvider - /// If we're adding then this - /// is the id for the we can find that - /// referenced from. - /// - private readonly ProjectId _portableExecutableReferenceProjectId; - - /// - /// If we want to add a metadata reference, this - /// is the for it. - /// - private readonly string _portableExecutableReferenceFilePathToAdd; - - public MetadataSymbolReferenceCodeAction( - Document originalDocument, - ImmutableArray textChanges, - string title, ImmutableArray tags, - CodeActionPriority priority, - ProjectId portableExecutableReferenceProjectId, - string portableExecutableReferenceFilePathToAdd) - : base(originalDocument, textChanges, title, tags, priority) + public MetadataSymbolReferenceCodeAction(Document originalDocument, AddImportFixData fixData) + : base(originalDocument, fixData) { - _portableExecutableReferenceProjectId = portableExecutableReferenceProjectId; - _portableExecutableReferenceFilePathToAdd = portableExecutableReferenceFilePathToAdd; + Contract.ThrowIfFalse(fixData.Kind == AddImportFixKind.MetadataSymbol); } protected override Project UpdateProject(Project project) { - var projectWithReference = project.Solution.GetProject(_portableExecutableReferenceProjectId); + var projectWithReference = project.Solution.GetProject(FixData.PortableExecutableReferenceProjectId); var reference = projectWithReference.MetadataReferences .OfType() - .First(pe => pe.FilePath == _portableExecutableReferenceFilePathToAdd); + .First(pe => pe.FilePath == FixData.PortableExecutableReferenceFilePathToAdd); return project.AddMetadataReference(reference); } diff --git a/src/Features/Core/Portable/AddImport/CodeActions/ParentInstallPackageCodeAction.cs b/src/Features/Core/Portable/AddImport/CodeActions/ParentInstallPackageCodeAction.cs index c93c3fe87e8df58b52c3dc011c24b279a1609d47..edb9836e7a4a2ea13b5a75f6735ff408a9bac452 100644 --- a/src/Features/Core/Portable/AddImport/CodeActions/ParentInstallPackageCodeAction.cs +++ b/src/Features/Core/Portable/AddImport/CodeActions/ParentInstallPackageCodeAction.cs @@ -2,13 +2,10 @@ using System.Collections.Immutable; using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.AddPackage; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Packaging; using Microsoft.CodeAnalysis.Tags; -using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.AddImport @@ -35,57 +32,48 @@ private class ParentInstallPackageCodeAction : CodeAction.CodeActionWithNestedAc /// public ParentInstallPackageCodeAction( Document document, - ImmutableArray textChanges, - IPackageInstallerService installerService, - string source, - string packageName, - string versionOpt) - : base(string.Format(FeaturesResources.Install_package_0, packageName), - CreateNestedActions(document, textChanges, installerService, source, packageName, versionOpt), + AddImportFixData fixData, + IPackageInstallerService installerService) + : base(string.Format(FeaturesResources.Install_package_0, fixData.PackageName), + CreateNestedActions(document, fixData, installerService), isInlinable: false) { + Contract.ThrowIfFalse(fixData.Kind == AddImportFixKind.PackageSymbol); } private static ImmutableArray CreateNestedActions( Document document, - ImmutableArray textChanges, - IPackageInstallerService installerService, - string source, - string packageName, - string versionOpt) + AddImportFixData fixData, + IPackageInstallerService installerService) { // Determine what versions of this package are already installed in some project // in this solution. We'll offer to add those specific versions to this project, // followed by an option to "Find and install latest version." - var installedVersions = installerService.GetInstalledVersions(packageName).NullToEmpty(); + var installedVersions = installerService.GetInstalledVersions(fixData.PackageName).NullToEmpty(); var codeActions = ArrayBuilder.GetInstance(); // First add the actions to install a specific version. codeActions.AddRange(installedVersions.Select( v => CreateCodeAction( - document, textChanges, installerService, - source, packageName, versionOpt: v, isLocal: true))); + document, fixData, installerService, versionOpt: v, isLocal: true))); // Now add the action to install the specific version. - var preferredVersion = versionOpt; + var preferredVersion = fixData.PackageVersionOpt; if (preferredVersion == null || !installedVersions.Contains(preferredVersion)) { codeActions.Add(CreateCodeAction( - document, textChanges, installerService, - source, packageName, versionOpt, isLocal: false)); + document, fixData, installerService, preferredVersion, isLocal: false)); } // And finally the action to show the package manager dialog. - codeActions.Add(new InstallWithPackageManagerCodeAction(installerService, packageName)); + codeActions.Add(new InstallWithPackageManagerCodeAction(installerService, fixData.PackageName)); return codeActions.ToImmutableAndFree(); } private static CodeAction CreateCodeAction( Document document, - ImmutableArray textChanges, + AddImportFixData fixData, IPackageInstallerService installerService, - string source, - string packageName, string versionOpt, bool isLocal) { @@ -96,12 +84,12 @@ private class ParentInstallPackageCodeAction : CodeAction.CodeActionWithNestedAc : string.Format(FeaturesResources.Install_version_0, versionOpt); var installOperation = new InstallPackageDirectlyCodeActionOperation( - installerService, document, source, packageName, versionOpt, + installerService, document, fixData.PackageSource, fixData.PackageName, versionOpt, includePrerelease: false, isLocal: isLocal); // Nuget hits should always come after other results. return new InstallPackageAndAddImportCodeAction( - document, textChanges, title, CodeActionPriority.Low, installOperation); + document, fixData, title, installOperation); } } } diff --git a/src/Features/Core/Portable/AddImport/CodeActions/ProjectSymbolReferenceCodeAction.cs b/src/Features/Core/Portable/AddImport/CodeActions/ProjectSymbolReferenceCodeAction.cs index 07710eeae3326409c02f8657172a4c818bf1f6af..6f278c70b45a2907dcfd952f583e0bbbced316c2 100644 --- a/src/Features/Core/Portable/AddImport/CodeActions/ProjectSymbolReferenceCodeAction.cs +++ b/src/Features/Core/Portable/AddImport/CodeActions/ProjectSymbolReferenceCodeAction.cs @@ -1,8 +1,6 @@ // 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 Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.AddImport { @@ -16,38 +14,30 @@ internal abstract partial class AbstractAddImportCodeFixProvider private class ProjectSymbolReferenceCodeAction : SymbolReferenceCodeAction { - /// - /// The optional id for a we'd like to add a reference to. - /// - private readonly ProjectId _projectReferenceToAdd; - public ProjectSymbolReferenceCodeAction( Document originalDocument, - ImmutableArray textChanges, - string title, ImmutableArray tags, - CodeActionPriority priority, - ProjectId projectReferenceToAdd) - : base(originalDocument, textChanges, title, tags, priority) + AddImportFixData fixData) + : base(originalDocument, fixData) { - // We only want to add a project reference if the project the import references - // is different from the project we started from. - if (projectReferenceToAdd != originalDocument.Project.Id) - { - _projectReferenceToAdd = projectReferenceToAdd; - } + Contract.ThrowIfFalse(fixData.Kind == AddImportFixKind.ProjectSymbol); } + private bool ShouldAddProjectReference() + => FixData.ProjectReferenceToAdd != null && FixData.ProjectReferenceToAdd != OriginalDocument.Project.Id; + internal override bool PerformFinalApplicabilityCheck - => _projectReferenceToAdd != null; + => ShouldAddProjectReference(); internal override bool IsApplicable(Workspace workspace) - => _projectReferenceToAdd != null && workspace.CanAddProjectReference(OriginalDocument.Project.Id, _projectReferenceToAdd); + => ShouldAddProjectReference() && + workspace.CanAddProjectReference( + OriginalDocument.Project.Id, FixData.ProjectReferenceToAdd); protected override Project UpdateProject(Project project) { - return _projectReferenceToAdd == null - ? project - : project.AddProjectReference(new ProjectReference(_projectReferenceToAdd)); + return ShouldAddProjectReference() + ? project.AddProjectReference(new ProjectReference(FixData.ProjectReferenceToAdd)) + : project; } } } diff --git a/src/Features/Core/Portable/AddImport/CodeActions/SymbolReference.SymbolReferenceCodeAction.cs b/src/Features/Core/Portable/AddImport/CodeActions/SymbolReference.SymbolReferenceCodeAction.cs index 107dba615a9a2a58dd05aae471e3033ba268de65..adc1fcdb387b3027ca82ddd9fd95d0cbfd38fd11 100644 --- a/src/Features/Core/Portable/AddImport/CodeActions/SymbolReference.SymbolReferenceCodeAction.cs +++ b/src/Features/Core/Portable/AddImport/CodeActions/SymbolReference.SymbolReferenceCodeAction.cs @@ -1,10 +1,7 @@ // 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.CodeActions; -using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.AddImport { @@ -20,10 +17,8 @@ private abstract class SymbolReferenceCodeAction : AddImportCodeAction { protected SymbolReferenceCodeAction( Document originalDocument, - ImmutableArray textChanges, - string title, ImmutableArray tags, - CodeActionPriority priority) - : base(originalDocument, textChanges, title, tags, priority) + AddImportFixData fixData) + : base(originalDocument, fixData) { } diff --git a/src/Features/Core/Portable/AddImport/References/AssemblyReference.cs b/src/Features/Core/Portable/AddImport/References/AssemblyReference.cs index 70d7d01d84794329fb34fdc5834bfc14920f0aff..5da3826e69732b8f6e17507631e357a52b5031e9 100644 --- a/src/Features/Core/Portable/AddImport/References/AssemblyReference.cs +++ b/src/Features/Core/Portable/AddImport/References/AssemblyReference.cs @@ -2,7 +2,6 @@ using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.SymbolSearch; using Roslyn.Utilities; @@ -23,7 +22,7 @@ private partial class AssemblyReference : Reference _referenceAssemblyWithType = referenceAssemblyWithType; } - public override async Task CreateCodeActionAsync( + public override async Task GetFixDataAsync( Document document, SyntaxNode node, bool placeSystemNamespaceFirst, CancellationToken cancellationToken) { var textChanges = await GetTextChangesAsync( @@ -33,8 +32,8 @@ private partial class AssemblyReference : Reference var fullyQualifiedTypeName = string.Join( ".", _referenceAssemblyWithType.ContainingNamespaceNames.Concat(_referenceAssemblyWithType.TypeName)); - return new AssemblyReferenceCodeAction( - document, textChanges, title, _referenceAssemblyWithType.AssemblyName, fullyQualifiedTypeName); + return AddImportFixData.CreateForReferenceAssemblySymbol( + textChanges, title, _referenceAssemblyWithType.AssemblyName, fullyQualifiedTypeName); } public override bool Equals(object obj) diff --git a/src/Features/Core/Portable/AddImport/References/MetadataSymbolReference.cs b/src/Features/Core/Portable/AddImport/References/MetadataSymbolReference.cs index 70ad538955ad53ea6148a3dc283959690ed1382b..16541f892b3880e4c1ebc6c511c0f80f157809c2 100644 --- a/src/Features/Core/Portable/AddImport/References/MetadataSymbolReference.cs +++ b/src/Features/Core/Portable/AddImport/References/MetadataSymbolReference.cs @@ -49,12 +49,12 @@ private partial class MetadataSymbolReference : SymbolReference hasExistingImport); } - protected override CodeAction CreateCodeAction( + protected override AddImportFixData GetFixData( Document document, ImmutableArray textChanges, string description, ImmutableArray tags, CodeActionPriority priority) { - return new MetadataSymbolReferenceCodeAction( - document, textChanges, description, tags, priority, + return AddImportFixData.CreateForMetadataSymbol( + textChanges, description, tags, priority, _referenceProjectId, _reference.FilePath); } diff --git a/src/Features/Core/Portable/AddImport/References/PackageReference.cs b/src/Features/Core/Portable/AddImport/References/PackageReference.cs index c623238a4c55d3aae92f757a0c8e5bee5f278317..2816e73e7aab28d9834906ee94a0d4a021c25787 100644 --- a/src/Features/Core/Portable/AddImport/References/PackageReference.cs +++ b/src/Features/Core/Portable/AddImport/References/PackageReference.cs @@ -1,9 +1,7 @@ // 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.CodeActions; using Microsoft.CodeAnalysis.Packaging; using Roslyn.Utilities; @@ -33,14 +31,14 @@ private partial class PackageReference : Reference _versionOpt = versionOpt; } - public override async Task CreateCodeActionAsync( + public override async Task GetFixDataAsync( Document document, SyntaxNode node, bool placeSystemNamespaceFirst, CancellationToken cancellationToken) { var textChanges = await GetTextChangesAsync( document, node, placeSystemNamespaceFirst, cancellationToken).ConfigureAwait(false); - return new ParentInstallPackageCodeAction( - document, textChanges, _installerService, _source, _packageName, _versionOpt); + return AddImportFixData.CreateForPackageSymbol( + textChanges, _source, _packageName, _versionOpt); } public override bool Equals(object obj) diff --git a/src/Features/Core/Portable/AddImport/References/ProjectSymbolReference.cs b/src/Features/Core/Portable/AddImport/References/ProjectSymbolReference.cs index fbba64475468aad66bf95ad720d7c038952ac952..b7e72224c5781607215293f0c1482c5d0f43e169 100644 --- a/src/Features/Core/Portable/AddImport/References/ProjectSymbolReference.cs +++ b/src/Features/Core/Portable/AddImport/References/ProjectSymbolReference.cs @@ -67,12 +67,12 @@ protected override CodeActionPriority GetPriority(Document document) return CodeActionPriority.Low; } - protected override CodeAction CreateCodeAction( + protected override AddImportFixData GetFixData( Document document, ImmutableArray textChanges, string description, ImmutableArray tags, CodeActionPriority priority) { - return new ProjectSymbolReferenceCodeAction( - document, textChanges, description, tags, priority, _project.Id); + return AddImportFixData.CreateForProjectSymbol( + textChanges, description, tags, priority, _project.Id); } protected override (string description, bool hasExistingImport) GetDescription( diff --git a/src/Features/Core/Portable/AddImport/References/Reference.cs b/src/Features/Core/Portable/AddImport/References/Reference.cs index 1356e8f96872a9695c26499db235e1dc085f9bb1..0bfe7cb63451c8af69d742c6035a0d0a52d83432 100644 --- a/src/Features/Core/Portable/AddImport/References/Reference.cs +++ b/src/Features/Core/Portable/AddImport/References/Reference.cs @@ -117,7 +117,7 @@ public override int GetHashCode() return (newContextNode, newDocument); } - public abstract Task CreateCodeActionAsync( + public abstract Task GetFixDataAsync( Document document, SyntaxNode node, bool placeSystemNamespaceFirst, CancellationToken cancellationToken); protected async Task> GetTextChangesAsync( diff --git a/src/Features/Core/Portable/AddImport/References/SymbolReference.cs b/src/Features/Core/Portable/AddImport/References/SymbolReference.cs index 10058caef259fb7d9c6de52732e1a47c770c3b19..a5696166a4a8db38793173e327c910ed27739a89 100644 --- a/src/Features/Core/Portable/AddImport/References/SymbolReference.cs +++ b/src/Features/Core/Portable/AddImport/References/SymbolReference.cs @@ -71,7 +71,7 @@ public override int GetHashCode() return textChanges.ToImmutableArray(); } - public sealed override async Task CreateCodeActionAsync( + public sealed override async Task GetFixDataAsync( Document document, SyntaxNode node, bool placeSystemNamespaceFirst, CancellationToken cancellationToken) { @@ -97,12 +97,12 @@ public override int GetHashCode() var textChanges = await GetTextChangesAsync( document, node, placeSystemNamespaceFirst, hasExistingImport, cancellationToken).ConfigureAwait(false); - return CreateCodeAction( + return GetFixData( document, textChanges.ToImmutableArray(), description, GetTags(document), GetPriority(document)); } - protected abstract CodeAction CreateCodeAction( + protected abstract AddImportFixData GetFixData( Document document, ImmutableArray textChanges, string description, ImmutableArray tags, CodeActionPriority priority); diff --git a/src/Features/Core/Portable/AddImport/SymbolReferenceFinder.cs b/src/Features/Core/Portable/AddImport/SymbolReferenceFinder.cs index 7bf9dc6d7f4d09f99b619b7bbb58158ff68f0381..ba9146059ef6f4f6d209b7c1f5fbf222547b5ba9 100644 --- a/src/Features/Core/Portable/AddImport/SymbolReferenceFinder.cs +++ b/src/Features/Core/Portable/AddImport/SymbolReferenceFinder.cs @@ -20,7 +20,7 @@ private partial class SymbolReferenceFinder { private const string AttributeSuffix = nameof(Attribute); - private readonly Diagnostic _diagnostic; + private readonly string _diagnosticId; private readonly Document _document; private readonly SemanticModel _semanticModel; @@ -35,13 +35,13 @@ private partial class SymbolReferenceFinder public SymbolReferenceFinder( AbstractAddImportCodeFixProvider owner, Document document, SemanticModel semanticModel, - Diagnostic diagnostic, SyntaxNode node, + string diagnosticId, SyntaxNode node, CancellationToken cancellationToken) { _owner = owner; _document = document; _semanticModel = semanticModel; - _diagnostic = diagnostic; + _diagnosticId = diagnosticId; _node = node; _containingType = semanticModel.GetEnclosingNamedType(node.SpanStart, cancellationToken); @@ -155,7 +155,7 @@ private ImmutableArray DeDupeAndSortReferences(ImmutableArray> GetReferencesForMatchingTypesAsync(SearchScope searchScope) { searchScope.CancellationToken.ThrowIfCancellationRequested(); - if (!_owner.CanAddImportForType(_diagnostic, _node, out var nameNode)) + if (!_owner.CanAddImportForType(_diagnosticId, _node, out var nameNode)) { return ImmutableArray.Empty; } @@ -223,7 +223,7 @@ private async Task> GetReferencesForMatchingType SearchScope searchScope) { searchScope.CancellationToken.ThrowIfCancellationRequested(); - if (_owner.CanAddImportForNamespace(_diagnostic, _node, out var nameNode)) + if (_owner.CanAddImportForNamespace(_diagnosticId, _node, out var nameNode)) { _syntaxFacts.GetNameAndArityOfSimpleName(nameNode, out var name, out var arity); @@ -250,7 +250,7 @@ private async Task> GetReferencesForMatchingType SearchScope searchScope) { searchScope.CancellationToken.ThrowIfCancellationRequested(); - if (_owner.CanAddImportForMethod(_diagnostic, _syntaxFacts, _node, out var nameNode) && + if (_owner.CanAddImportForMethod(_diagnosticId, _syntaxFacts, _node, out var nameNode) && nameNode != null) { // We have code like "Color.Black". "Color" bound to a 'Color Color' property, and @@ -310,7 +310,7 @@ private bool HasAccessibleStaticFieldOrProperty(INamedTypeSymbol namedType, stri private async Task> GetReferencesForMatchingExtensionMethodsAsync(SearchScope searchScope) { searchScope.CancellationToken.ThrowIfCancellationRequested(); - if (_owner.CanAddImportForMethod(_diagnostic, _syntaxFacts, _node, out var nameNode) && + if (_owner.CanAddImportForMethod(_diagnosticId, _syntaxFacts, _node, out var nameNode) && nameNode != null) { searchScope.CancellationToken.ThrowIfCancellationRequested(); @@ -368,7 +368,7 @@ private async Task> GetReferencesForMatchingExte private async Task> GetReferencesForCollectionInitializerMethodsAsync(SearchScope searchScope) { searchScope.CancellationToken.ThrowIfCancellationRequested(); - if (!_owner.CanAddImportForMethod(_diagnostic, _syntaxFacts, _node, out var nameNode)) + if (!_owner.CanAddImportForMethod(_diagnosticId, _syntaxFacts, _node, out var nameNode)) { return ImmutableArray.Empty; } @@ -403,7 +403,7 @@ private async Task> GetReferencesForQueryPattern { searchScope.CancellationToken.ThrowIfCancellationRequested(); - if (_owner.CanAddImportForQuery(_diagnostic, _node)) + if (_owner.CanAddImportForQuery(_diagnosticId, _node)) { var type = _owner.GetQueryClauseInfo(_semanticModel, _node, searchScope.CancellationToken); if (type != null) @@ -426,7 +426,7 @@ private async Task> GetReferencesForDeconstructA { searchScope.CancellationToken.ThrowIfCancellationRequested(); - if (_owner.CanAddImportForDeconstruct(_diagnostic, _node)) + if (_owner.CanAddImportForDeconstruct(_diagnosticId, _node)) { var type = _owner.GetDeconstructInfo(_semanticModel, _node, searchScope.CancellationToken); if (type != null) diff --git a/src/Features/Core/Portable/AddImport/SymbolReferenceFinder_PackageAssemblySearch.cs b/src/Features/Core/Portable/AddImport/SymbolReferenceFinder_PackageAssemblySearch.cs index 6229433e47f85aaa999f30fc8e0f23e2b37d33ec..db4fe3fa0e9bbff60d391e905160205bde87068d 100644 --- a/src/Features/Core/Portable/AddImport/SymbolReferenceFinder_PackageAssemblySearch.cs +++ b/src/Features/Core/Portable/AddImport/SymbolReferenceFinder_PackageAssemblySearch.cs @@ -23,7 +23,7 @@ private partial class SymbolReferenceFinder return; } - if (!_owner.CanAddImportForType(_diagnostic, _node, out var nameNode)) + if (!_owner.CanAddImportForType(_diagnosticId, _node, out var nameNode)) { return; } @@ -63,7 +63,7 @@ private partial class SymbolReferenceFinder var workspaceServices = _document.Project.Solution.Workspace.Services; var symbolSearchService = _owner._symbolSearchService ?? workspaceServices.GetService(); - var installerService = _owner._packageInstallerService ?? workspaceServices.GetService(); + var installerService = _owner.GetPackageInstallerService(_document); var language = _document.Project.Language; diff --git a/src/Features/Core/Portable/Features.csproj b/src/Features/Core/Portable/Features.csproj index 9687335fb166c308e433c4bbd242dc71b70b3212..43346381e0cca092ce2c65edae67203808dfeeec 100644 --- a/src/Features/Core/Portable/Features.csproj +++ b/src/Features/Core/Portable/Features.csproj @@ -99,6 +99,8 @@ Shared\Utilities\DesktopShim.cs + + diff --git a/src/Features/VisualBasic/Portable/AddImport/VisualBasicAddImportCodeFixProvider.vb b/src/Features/VisualBasic/Portable/AddImport/VisualBasicAddImportCodeFixProvider.vb index c4b239807f4b17ee76a3c0d6d4a8c92bc1fdeb07..4473adb6309aaa1e94c4c1143c35f8d41004e488 100644 --- a/src/Features/VisualBasic/Portable/AddImport/VisualBasicAddImportCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/AddImport/VisualBasicAddImportCodeFixProvider.vb @@ -120,11 +120,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.AddImport End Function Protected Overrides Function CanAddImportForMethod( - diagnostic As Diagnostic, + diagnosticId As String, syntaxFacts As ISyntaxFactsService, node As SyntaxNode, ByRef nameNode As SimpleNameSyntax) As Boolean - Select Case diagnostic.Id + Select Case diagnosticId Case BC30456, BC30390, BC42309, BC30451 Exit Select Case BC30512 @@ -177,8 +177,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.AddImport Return True End Function - Protected Overrides Function CanAddImportForNamespace(diagnostic As Diagnostic, node As SyntaxNode, ByRef nameNode As SimpleNameSyntax) As Boolean - Select Case diagnostic.Id + Protected Overrides Function CanAddImportForNamespace(diagnosticId As String, node As SyntaxNode, ByRef nameNode As SimpleNameSyntax) As Boolean + Select Case diagnosticId Case BC30002, BC30451 Exit Select Case Else @@ -188,13 +188,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.AddImport Return CanAddImportForTypeOrNamespaceCore(node, nameNode) End Function - Protected Overrides Function CanAddImportForDeconstruct(diagnostic As Diagnostic, node As SyntaxNode) As Boolean + Protected Overrides Function CanAddImportForDeconstruct(diagnosticId As String, node As SyntaxNode) As Boolean ' Not supported yet. Return False End Function - Protected Overrides Function CanAddImportForQuery(diagnostic As Diagnostic, node As SyntaxNode) As Boolean - If diagnostic.Id <> BC36593 Then + Protected Overrides Function CanAddImportForQuery(diagnosticId As String, node As SyntaxNode) As Boolean + If diagnosticId <> BC36593 Then Return False End If @@ -208,8 +208,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.AddImport End Function Protected Overrides Function CanAddImportForType( - diagnostic As Diagnostic, node As SyntaxNode, ByRef nameNode As SimpleNameSyntax) As Boolean - Select Case diagnostic.Id + diagnosticId As String, node As SyntaxNode, ByRef nameNode As SimpleNameSyntax) As Boolean + Select Case diagnosticId Case BC30002, BC30451, BC32042, BC32045, BC30389, BC31504, BC36610, BC30182 Exit Select Case BC42309 diff --git a/src/VisualStudio/Core/Def/Packaging/PackageInstallerServiceFactory.cs b/src/VisualStudio/Core/Def/Packaging/PackageInstallerServiceFactory.cs index c405d483ff01c84b5709312e43a1d8db4e443585..6b015fc907b44685758324b1374baa6a86aeda02 100644 --- a/src/VisualStudio/Core/Def/Packaging/PackageInstallerServiceFactory.cs +++ b/src/VisualStudio/Core/Def/Packaging/PackageInstallerServiceFactory.cs @@ -540,7 +540,7 @@ public IEnumerable GetProjectsWithInstalledPackage(Solution solution, s { var state = kvp.Value; var versionSet = state.InstalledPackageToVersion[packageName]; - if (versionSet.Contains(packageName)) + if (versionSet.Contains(version)) { var project = solution.GetProject(kvp.Key); if (project != null)