Skip to content
Browse files

No longer references dlls in bin folders

ReSharper tracks the file location of each usage of each assembly. The
list of file locations is in an undefined order, and the reference
quick-fix uses the first. If the first is in a bin folder, we don't find
it in NuGet. We now check all file locations for a NuGet package. Fixes
#2

Also tested in ReSharper 7.1, and bumped version to 1.0. Fixes #5
  • Loading branch information...
1 parent c81f69b commit 5151f593216ce5226786a3db81790326df2a74fc @citizenmatt citizenmatt committed Nov 20, 2012
View
48 src/resharper-nuget/NuGetApi.cs
@@ -15,6 +15,7 @@
*/
using System;
+using System.Collections.Generic;
using EnvDTE;
using JetBrains.Application;
using JetBrains.ProjectModel;
@@ -57,60 +58,59 @@ private bool IsNuGetAvailable
get { return vsPackageInstallerServices != null && vsPackageInstaller != null; }
}
- public bool IsNuGetPackageAssembly(FileSystemPath location)
+ public bool AreAnyAssemblyFilesNuGetPackages(IEnumerable<FileSystemPath> fileLocations)
{
- return IsNuGetAvailable && IsPackageAssembly(location);
- }
+ if (!IsNuGetAvailable)
+ return false;
- private bool IsPackageAssembly(FileSystemPath location)
- {
// We're talking to NuGet via COM. Make sure we're on the UI thread
- var isPackageAssembly = false;
+ var hasPackageAssembly = false;
threading.Dispatcher.Invoke("NuGet", () =>
{
- isPackageAssembly = GetPackageFromAssemblyLocation(location) != null;
+ hasPackageAssembly = GetPackageFromAssemblyLocations(fileLocations) != null;
});
- return isPackageAssembly;
+ return hasPackageAssembly;
}
- public bool InstallAssemblyAsNuGetPackage(FileSystemPath assemblyLocation, IProject project)
+ public string InstallNuGetPackageFromAssemblyFiles(IEnumerable<FileSystemPath> assemblyLocations, IProject project)
{
if (!IsNuGetAvailable)
- return false;
+ return null;
- var installed = false;
+ string installedAssembly = null;
- // We talking to NuGet via COM. Make sure we're on the UI thread
+ // We're talking to NuGet via COM. Make sure we're on the UI thread
threading.Dispatcher.Invoke("NuGet", () =>
{
- installed = DoInstallAssemblyAsNuGetPackage(assemblyLocation, project);
+ var vsProject = GetVsProject(project);
+ if (vsProject != null)
+ installedAssembly = DoInstallAssemblyAsNuGetPackage(assemblyLocations, vsProject);
});
- return installed;
+ return installedAssembly;
}
- private bool DoInstallAssemblyAsNuGetPackage(FileSystemPath assemblyLocation, IProject project)
+ private string DoInstallAssemblyAsNuGetPackage(IEnumerable<FileSystemPath> assemblyLocations, Project vsProject)
{
- var metadata = GetPackageFromAssemblyLocation(assemblyLocation);
+ var metadata = GetPackageFromAssemblyLocations(assemblyLocations);
if (metadata == null)
- return false;
-
- var vsProject = GetVsProject(project);
- if (vsProject == null)
- return false;
+ return null;
// Passing in the package's install path and a null version is enough for NuGet to install the existing
// package into the current project
// TODO: Need to add some error handling here. What can go wrong?
vsPackageInstaller.InstallPackage(metadata.InstallPath, vsProject, metadata.Id, (Version) null, false);
- return true;
+ return metadata.InstallPath;
}
- private IVsPackageMetadata GetPackageFromAssemblyLocation(FileSystemPath assemblyLocation)
+ private IVsPackageMetadata GetPackageFromAssemblyLocations(IEnumerable<FileSystemPath> assemblyLocations)
{
- return vsPackageInstallerServices.GetInstalledPackages().FirstOrDefault(p => assemblyLocation.FullPath.StartsWith(p.InstallPath, StringComparison.InvariantCultureIgnoreCase));
+ return (from p in vsPackageInstallerServices.GetInstalledPackages()
+ from l in assemblyLocations
+ where l.FullPath.StartsWith(p.InstallPath, StringComparison.InvariantCultureIgnoreCase)
+ select p).FirstOrDefault();
}
private Project GetVsProject(IProject project)
View
83 src/resharper-nuget/NuGetModuleReferencer.cs
@@ -14,11 +14,15 @@
* limitations under the License.
*/
+using System;
+using System.Collections.Generic;
using JetBrains.Application.Progress;
using JetBrains.ProjectModel;
+using JetBrains.ProjectModel.Model2.Assemblies.Interfaces;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.Module;
using JetBrains.Util;
+using System.Linq;
namespace JetBrains.ReSharper.Plugins.NuGet
{
@@ -37,44 +41,79 @@ public NuGetModuleReferencer(NuGetApi nuget)
public bool CanReferenceModule(IPsiModule module, IPsiModule moduleToReference)
{
- return IsNuGetAssembly(moduleToReference);
+ if (!IsProjectModule(module) || !IsAssemblyModule(moduleToReference))
+ return false;
+
+ var assemblyLocations = GetAllAssemblyLocations(module);
+ return nuget.AreAnyAssemblyFilesNuGetPackages(assemblyLocations);
}
// ReSharper 7.1
- public virtual bool ReferenceModule(IPsiModule module, IPsiModule moduleToReference)
+ public bool ReferenceModule(IPsiModule module, IPsiModule moduleToReference)
{
- var assemblyModule = moduleToReference as IAssemblyPsiModule;
- var projectModule = module as IProjectPsiModule;
- if (assemblyModule == null || projectModule == null)
+ if (!IsProjectModule(module) || !IsAssemblyModule(moduleToReference))
return false;
- if (nuget.InstallAssemblyAsNuGetPackage(assemblyModule.Assembly.Location, projectModule.Project))
- {
- // TODO: I wish we didn't have to do this
- // When NuGet references the assemblies, they are queued up to be processed, but after
- // this method completes. Which means the import type part of the process fails to find
- // the type to import. We force an update which works through the system early. It would
- // be nice to find out if we can process the proper import notifications instead
- using (var cookie = module.GetSolution().CreateTransactionCookie(DefaultAction.Commit, "ReferenceModuleWithType", NullProgressIndicator.Instance))
- cookie.AddModuleReference(projectModule.Project, assemblyModule.ContainingProjectModule);
- return true;
- }
+ var assemblyLocations = GetAllAssemblyLocations(moduleToReference);
+ var projectModule = (IProjectPsiModule)module;
+ var packageLocation = nuget.InstallNuGetPackageFromAssemblyFiles(assemblyLocations, projectModule.Project);
+
+ PokeReSharpersAssemblyReferences(module, assemblyLocations, packageLocation, projectModule);
- return false;
+ return !string.IsNullOrEmpty(packageLocation);
}
public bool ReferenceModuleWithType(IPsiModule module, ITypeElement typeToReference)
{
return ReferenceModule(module, typeToReference.Module);
}
- private bool IsNuGetAssembly(IPsiModule module)
+ private static bool IsProjectModule(IPsiModule module)
{
- var assemblyModule = module as IAssemblyPsiModule;
- if (assemblyModule == null)
- return false;
+ return module is IProjectPsiModule;
+ }
+
+ private static bool IsAssemblyModule(IPsiModule module)
+ {
+ return module is IAssemblyPsiModule;
+ }
+
+ private static IList<FileSystemPath> GetAllAssemblyLocations(IPsiModule psiModule)
+ {
+ var projectModelAssembly = psiModule.ContainingProjectModule as IAssembly;
+ if (projectModelAssembly == null)
+ return null;
+
+ // ReSharper maintains a list of unique assemblies, and each assembly keeps a track of
+ // all of the file copies of itself that the solution knows about. This list of file
+ // locations includes sources for references (including NuGet packages), but can also
+ // include outputs, e.g. if CopyLocal is set to True. The IAssemblyPsiModule.Assembly.Location
+ // returns back the file location of the first copy of the assembly, but the order of
+ // the list is undefined - it is entirely possible to get back a file location in a bin\Debug
+ // folder. This doesn't help us when trying to add NuGet references - we need to look
+ // at all of the locations to try and find the NuGet package location. So we use the
+ // ProjectModel instead of the PSI, and get all file locations of the IAssembly
+ return (from f in projectModelAssembly.GetFiles()
+ select f.Location).ToList();
+ }
- return nuget.IsNuGetPackageAssembly(assemblyModule.Assembly.Location);
+ private static void PokeReSharpersAssemblyReferences(IPsiModule module, IEnumerable<FileSystemPath> assemblyLocations, string packageLocation,
+ IProjectPsiModule projectModule)
+ {
+ if (string.IsNullOrEmpty(packageLocation))
+ return;
+
+ // TODO: I wish we didn't have to do this
+ // When NuGet references the assemblies, they are queued up to be processed, but after
+ // this method completes. Which means the import type part of the process fails to find
+ // the type to import. We force an update which works through the system early. It would
+ // be nice to find out if we can process the proper import notifications instead
+ using (var cookie = module.GetSolution().CreateTransactionCookie(DefaultAction.Commit, "ReferenceModuleWithType", NullProgressIndicator.Instance))
+ {
+ var assemblyLocation = assemblyLocations.FirstOrDefault(l => l.FullPath.StartsWith(packageLocation, StringComparison.InvariantCultureIgnoreCase));
+ if (!assemblyLocation.IsNullOrEmpty())
+ cookie.AddAssemblyReference(projectModule.Project, assemblyLocation);
+ }
}
}
}
View
4 src/resharper-nuget/Properties/AssemblyInfo.cs
@@ -13,8 +13,8 @@
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
-[assembly: AssemblyVersion("0.3.0.0")]
-[assembly: AssemblyFileVersion("0.3.0.0")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
// The following information is displayed by ReSharper in the Plugins dialog
[assembly: PluginTitle("NuGet support for ReSharper")]

0 comments on commit 5151f59

Please sign in to comment.
Something went wrong with that request. Please try again.