From fda36cf99bea2de3d475836282c2bdf175ace81f Mon Sep 17 00:00:00 2001 From: Matt Ward Date: Sat, 19 May 2012 12:15:47 +0100 Subject: [PATCH] Implement EnvDTE.ProjectItems.AddFromDirectory. --- .../Src/EnvDTE/DirectoryProjectItem.cs | 12 +- .../Project/Src/EnvDTE/Project.cs | 71 ++++++-- .../Project/Src/EnvDTE/ProjectItem.cs | 10 +- .../Project/Src/EnvDTE/ProjectItems.cs | 8 +- .../Src/IPackageManagementFileService.cs | 2 + .../Src/PackageManagementFileService.cs | 24 ++- .../Project/Src/VisualStudio/Package.cs | 1 - .../Test/Src/EnvDTE/ProjectItemsTests.cs | 168 +++++++++++++++++- .../Test/Src/Helpers/FakeFileService.cs | 44 +++++ 9 files changed, 319 insertions(+), 21 deletions(-) diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/DirectoryProjectItem.cs b/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/DirectoryProjectItem.cs index 10e9d1ed20..3355b060a2 100644 --- a/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/DirectoryProjectItem.cs +++ b/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/DirectoryProjectItem.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using ICSharpCode.Core; using ICSharpCode.SharpDevelop.Project; using SD = ICSharpCode.SharpDevelop.Project; @@ -29,7 +30,10 @@ namespace ICSharpCode.PackageManagement.EnvDTE static string GetLastDirectoryName(string relativePath) { string[] directoryNames = relativePath.Split('\\'); - return directoryNames[1]; + if (directoryNames.Length > 1) { + return directoryNames[1]; + } + return relativePath; } public DirectoryProjectItem(Project project, FileProjectItem projectItem) @@ -45,5 +49,11 @@ namespace ICSharpCode.PackageManagement.EnvDTE } return false; } + + public static DirectoryProjectItem CreateDirectoryProjectItemFromFullPath(Project project, string directory) + { + string relativePath = project.GetRelativePath(directory); + return new DirectoryProjectItem(project, relativePath); + } } } diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/Project.cs b/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/Project.cs index bf0cea65b8..3e2959c651 100644 --- a/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/Project.cs +++ b/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/Project.cs @@ -4,7 +4,9 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; +using ICSharpCode.Core; using ICSharpCode.SharpDevelop.Project; using Microsoft.Build.Construction; using SD = ICSharpCode.SharpDevelop.Project; @@ -107,7 +109,7 @@ namespace ICSharpCode.PackageManagement.EnvDTE { if (!HasReference(path)) { var referenceItem = new ReferenceProjectItem(MSBuildProject, path); - projectService.AddProjectItem(MSBuildProject, referenceItem); + AddProjectItemToMSBuildProject(referenceItem); } } @@ -121,6 +123,11 @@ namespace ICSharpCode.PackageManagement.EnvDTE return false; } + void AddProjectItemToMSBuildProject(SD.ProjectItem projectItem) + { + projectService.AddProjectItem(MSBuildProject, projectItem); + } + internal IEnumerable GetReferences() { return MSBuildProject.GetItemsOfType(ItemType.Reference); @@ -136,17 +143,17 @@ namespace ICSharpCode.PackageManagement.EnvDTE projectService.RemoveProjectItem(MSBuildProject, referenceItem); } - internal void AddFileUsingPathRelativeToProject(string include) + internal void AddFileProjectItemUsingPathRelativeToProject(string include) { - var fileProjectItem = CreateFileProjectItemUsingPathRelativeToProject(include); - projectService.AddProjectItem(MSBuildProject, fileProjectItem); + FileProjectItem fileProjectItem = CreateFileProjectItemUsingPathRelativeToProject(include); + AddProjectItemToMSBuildProject(fileProjectItem); } - internal ProjectItem AddFileUsingFullPath(string path) + internal ProjectItem AddFileProjectItemUsingFullPath(string path) { FileProjectItem fileProjectItem = CreateFileProjectItemUsingFullPath(path); fileProjectItem.FileName = path; - projectService.AddProjectItem(MSBuildProject, fileProjectItem); + AddProjectItemToMSBuildProject(fileProjectItem); return new ProjectItem(this, fileProjectItem); } @@ -158,19 +165,20 @@ namespace ICSharpCode.PackageManagement.EnvDTE ItemType GetDefaultItemType(string include) { - return MSBuildProject.GetDefaultItemType(include); + return MSBuildProject.GetDefaultItemType(Path.GetFileName(include)); } FileProjectItem CreateFileProjectItemUsingPathRelativeToProject(ItemType itemType, string include) { - var fileProjectItem = new FileProjectItem(MSBuildProject, itemType); - fileProjectItem.Include = include; - return fileProjectItem; + return new FileProjectItem(MSBuildProject, itemType) { + Include = include + }; } FileProjectItem CreateFileProjectItemUsingFullPath(string path) { - return CreateFileProjectItemUsingPathRelativeToProject(Path.GetFileName(path)); + string relativePath = GetRelativePath(path); + return CreateFileProjectItemUsingPathRelativeToProject(relativePath); } internal IList GetAllPropertyNames() @@ -202,5 +210,46 @@ namespace ICSharpCode.PackageManagement.EnvDTE { fileService.RemoveFile(fileName); } + + public ProjectItem AddDirectoryProjectItemUsingFullPath(string directory) + { + AddDirectoryProjectItemsRecursively(directory); + return DirectoryProjectItem.CreateDirectoryProjectItemFromFullPath(this, directory); + } + + void AddDirectoryProjectItemsRecursively(string directory) + { + string[] files = fileService.GetFiles(directory); + string[] childDirectories = fileService.GetDirectories(directory); + if (files.Any()) { + foreach (string file in files) { + AddFileProjectItemUsingFullPath(file); + } + } else if (!childDirectories.Any()) { + AddDirectoryProjectItemToMSBuildProject(directory); + } + + foreach (string childDirectory in childDirectories) { + AddDirectoryProjectItemsRecursively(childDirectory); + } + } + + void AddDirectoryProjectItemToMSBuildProject(string directory) + { + FileProjectItem projectItem = CreateMSBuildProjectItemForDirectory(directory); + AddProjectItemToMSBuildProject(projectItem); + } + + FileProjectItem CreateMSBuildProjectItemForDirectory(string directory) + { + return new FileProjectItem(MSBuildProject, ItemType.Folder) { + FileName = directory + }; + } + + internal string GetRelativePath(string path) + { + return FileUtility.GetRelativePath(MSBuildProject.Directory, path); + } } } diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/ProjectItem.cs b/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/ProjectItem.cs index 0152ec73c2..d9d625e092 100644 --- a/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/ProjectItem.cs +++ b/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/ProjectItem.cs @@ -24,7 +24,15 @@ namespace ICSharpCode.PackageManagement.EnvDTE this.ContainingProject = project; this.ProjectItems = new DirectoryProjectItems(this); CreateProperties(); - Kind = Constants.VsProjectItemKindPhysicalFile; + Kind = GetKindFromFileProjectItemType(); + } + + string GetKindFromFileProjectItemType() + { + if (projectItem.ItemType == ItemType.Folder) { + return Constants.VsProjectItemKindPhysicalFolder; + } + return Constants.VsProjectItemKindPhysicalFile; } public ProjectItem() diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/ProjectItems.cs b/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/ProjectItems.cs index 10fdfef809..5e64ea1c04 100644 --- a/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/ProjectItems.cs +++ b/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/ProjectItems.cs @@ -34,7 +34,7 @@ namespace ICSharpCode.PackageManagement.EnvDTE { string include = Path.GetFileName(filePath); CopyFileIntoProject(filePath, include); - project.AddFileUsingPathRelativeToProject(include); + project.AddFileProjectItemUsingPathRelativeToProject(include); project.Save(); } @@ -89,12 +89,14 @@ namespace ICSharpCode.PackageManagement.EnvDTE public virtual ProjectItem AddFromDirectory(string directory) { - throw new NotImplementedException(); + ProjectItem directoryItem = project.AddDirectoryProjectItemUsingFullPath(directory); + project.Save(); + return directoryItem; } public virtual ProjectItem AddFromFile(string fileName) { - ProjectItem projectItem = project.AddFileUsingFullPath(fileName); + ProjectItem projectItem = project.AddFileProjectItemUsingFullPath(fileName); project.Save(); return projectItem; } diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/IPackageManagementFileService.cs b/src/AddIns/Misc/PackageManagement/Project/Src/IPackageManagementFileService.cs index 08293aebe1..f95e39a837 100644 --- a/src/AddIns/Misc/PackageManagement/Project/Src/IPackageManagementFileService.cs +++ b/src/AddIns/Misc/PackageManagement/Project/Src/IPackageManagementFileService.cs @@ -12,5 +12,7 @@ namespace ICSharpCode.PackageManagement void OpenFile(string fileName); void CopyFile(string oldFileName, string newFileName); bool FileExists(string fileName); + string[] GetFiles(string path); + string[] GetDirectories(string path); } } diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/PackageManagementFileService.cs b/src/AddIns/Misc/PackageManagement/Project/Src/PackageManagementFileService.cs index 1b7916e633..ef644105f5 100644 --- a/src/AddIns/Misc/PackageManagement/Project/Src/PackageManagementFileService.cs +++ b/src/AddIns/Misc/PackageManagement/Project/Src/PackageManagementFileService.cs @@ -52,7 +52,29 @@ namespace ICSharpCode.PackageManagement public bool FileExists(string fileName) { - return File.Exists(fileName); + if (WorkbenchSingleton.InvokeRequired) { + return WorkbenchSingleton.SafeThreadFunction(() => FileExists(fileName)); + } else { + return File.Exists(fileName); + } + } + + public string[] GetFiles(string path) + { + if (WorkbenchSingleton.InvokeRequired) { + return WorkbenchSingleton.SafeThreadFunction(() => GetFiles(path)); + } else { + return Directory.GetFiles(path); + } + } + + public string[] GetDirectories(string path) + { + if (WorkbenchSingleton.InvokeRequired) { + return WorkbenchSingleton.SafeThreadFunction(() => GetDirectories(path)); + } else { + return Directory.GetDirectories(path); + } } } } diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/VisualStudio/Package.cs b/src/AddIns/Misc/PackageManagement/Project/Src/VisualStudio/Package.cs index 05215141ba..496a25d96b 100644 --- a/src/AddIns/Misc/PackageManagement/Project/Src/VisualStudio/Package.cs +++ b/src/AddIns/Misc/PackageManagement/Project/Src/VisualStudio/Package.cs @@ -13,7 +13,6 @@ namespace Microsoft.VisualStudio.Shell { //typeof(IVsSolution) //typeof(SComponentModel) --> not used - console initializer. - //typeof(SVsExtensionManager) if (serviceType == typeof(DTE)) { return new DTE(); } else if (serviceType == typeof(SVsExtensionManager)) { diff --git a/src/AddIns/Misc/PackageManagement/Test/Src/EnvDTE/ProjectItemsTests.cs b/src/AddIns/Misc/PackageManagement/Test/Src/EnvDTE/ProjectItemsTests.cs index f451841996..9b258e6daf 100644 --- a/src/AddIns/Misc/PackageManagement/Test/Src/EnvDTE/ProjectItemsTests.cs +++ b/src/AddIns/Misc/PackageManagement/Test/Src/EnvDTE/ProjectItemsTests.cs @@ -30,6 +30,16 @@ namespace PackageManagement.Tests.EnvDTE fakeFileService = project.FakeFileService; } + void AddFileToFakeFileSystem(string directory, string relativeFileName) + { + fakeFileService.AddFilesToFakeFileSystem(directory, relativeFileName); + } + + void AddDirectoryToFakeFileSystem(string parentDirectory, string childDirectory) + { + fakeFileService.AddDirectoryToFakeFileSystem(parentDirectory, childDirectory); + } + [Test] public void AddFromFileCopy_AddFileNameOutsideProjectFolder_FileIsIncludedInProjectInProjectFolder() { @@ -390,7 +400,7 @@ namespace PackageManagement.Tests.EnvDTE public void AddFromFile_FullFileNameIsInsideProject_ProjectIsSaved() { CreateProjectItems(); - msbuildProject.FileName = @"d:\projects\myproject\myproject\myproject.csproj"; + msbuildProject.FileName = @"d:\projects\myproject\myproject.csproj"; string fileName = @"d:\projects\myproject\packages\tools\test.cs"; projectItems.AddFromFile(fileName); @@ -404,7 +414,7 @@ namespace PackageManagement.Tests.EnvDTE public void AddFromFile_FullFileNameIsInsideProject_ProjectItemReturned() { CreateProjectItems(); - msbuildProject.FileName = @"d:\projects\myproject\myproject\myproject.csproj"; + msbuildProject.FileName = @"d:\projects\myproject\myproject.csproj"; string fileName = @"d:\projects\myproject\tools\test.cs"; msbuildProject.ItemTypeToReturnFromGetDefaultItemType = ItemType.Page; @@ -420,7 +430,7 @@ namespace PackageManagement.Tests.EnvDTE public void AddFromFile_FullFileNameIsInsideProject_FileNameUsedToDetermineProjectItemType() { CreateProjectItems(); - msbuildProject.FileName = @"d:\projects\myproject\myproject\myproject.csproj"; + msbuildProject.FileName = @"d:\projects\myproject\myproject.csproj"; string fileName = @"d:\projects\myproject\tools\test.cs"; projectItems.AddFromFile(fileName); @@ -428,5 +438,157 @@ namespace PackageManagement.Tests.EnvDTE Assert.AreEqual("test.cs", msbuildProject.FileNamePassedToGetDefaultItemType); } + [Test] + public void AddFromDirectory_EmptyDirectoryInsideProject_ProjectIsSaved() + { + CreateProjectItems(); + msbuildProject.FileName = @"d:\projects\myproject\myproject.csproj"; + string directory = @"d:\projects\myproject\tools"; + + projectItems.AddFromDirectory(directory); + + bool saved = msbuildProject.IsSaved; + + Assert.IsTrue(saved); + } + + [Test] + public void AddFromDirectory_EmptyDirectoryInsideProject_ProjectItemIsReturnedForNewDirectory() + { + CreateProjectItems(); + msbuildProject.FileName = @"d:\projects\myproject\myproject.csproj"; + string directory = @"d:\projects\myproject\tools"; + + DTE.ProjectItem item = projectItems.AddFromDirectory(directory); + string name = item.Name; + + Assert.AreEqual("tools", name); + Assert.AreEqual(project, item.ContainingProject); + Assert.AreEqual(Constants.VsProjectItemKindPhysicalFolder, item.Kind); + } + + [Test] + public void AddFromDirectory_EmptyDirectoryInsideProject_FolderProjectItemAddedToProject() + { + CreateProjectItems(); + msbuildProject.FileName = @"d:\projects\myproject\myproject.csproj"; + string directory = @"d:\projects\myproject\tools"; + + projectItems.AddFromDirectory(directory); + + var item = msbuildProject.Items[0] as FileProjectItem; + + Assert.AreEqual("tools", item.Include); + Assert.AreEqual(ItemType.Folder, item.ItemType); + } + + [Test] + public void AddFromDirectory_EmptyDirectoryIsTwoLevelsInsideProject_FolderProjectItemAddedToProject() + { + CreateProjectItems(); + msbuildProject.FileName = @"d:\projects\myproject\myproject.csproj"; + string directory = @"d:\projects\myproject\tools\packages"; + + projectItems.AddFromDirectory(directory); + + var item = msbuildProject.Items[0] as FileProjectItem; + + Assert.AreEqual(@"tools\packages", item.Include); + Assert.AreEqual(ItemType.Folder, item.ItemType); + } + + [Test] + public void AddFromDirectory_DirectoryContainsOneFile_FileAddedToMSBuildProjectButNoFolder() + { + CreateProjectItems(); + msbuildProject.FileName = @"d:\projects\myproject\myproject.csproj"; + string directory = @"d:\projects\myproject\tools"; + AddFileToFakeFileSystem(directory, "a.txt"); + project.TestableProject.ItemTypeToReturnFromGetDefaultItemType = ItemType.None; + + projectItems.AddFromDirectory(directory); + + var item = msbuildProject.Items[0] as FileProjectItem; + + Assert.AreEqual(@"tools\a.txt", item.Include); + Assert.AreEqual(ItemType.None, item.ItemType); + Assert.AreEqual(@"d:\projects\myproject\tools\a.txt", item.FileName); + Assert.AreEqual(1, msbuildProject.Items.Count); + } + + [Test] + public void AddFromDirectory_DirectoryContainsOneFile_ProjectItemReturnedHasDirectoryName() + { + CreateProjectItems(); + msbuildProject.FileName = @"d:\projects\myproject\myproject.csproj"; + string directory = @"d:\projects\myproject\tools"; + AddFileToFakeFileSystem(directory, "a.txt"); + + DTE.ProjectItem item = projectItems.AddFromDirectory(directory); + string name = item.Name; + + Assert.AreEqual("tools", name); + Assert.AreEqual(project, item.ContainingProject); + Assert.AreEqual(Constants.VsProjectItemKindPhysicalFolder, item.Kind); + } + + [Test] + public void AddFromDirectory_DirectoryContainsChildDirectoryWithNoFiles_DirectoryProjectItemReturnedForParentDirectory() + { + CreateProjectItems(); + msbuildProject.FileName = @"d:\projects\myproject\myproject.csproj"; + string parentDirectory = @"d:\projects\myproject\tools"; + AddDirectoryToFakeFileSystem(parentDirectory, "packages"); + + DTE.ProjectItem item = projectItems.AddFromDirectory(parentDirectory); + string name = item.Name; + + DTE.ProjectItem childItem = item.ProjectItems.Item("packages"); + + Assert.AreEqual("tools", name); + Assert.AreEqual(project, item.ContainingProject); + Assert.AreEqual(Constants.VsProjectItemKindPhysicalFolder, item.Kind); + Assert.AreEqual(1, item.ProjectItems.Count); + Assert.AreEqual(Constants.VsProjectItemKindPhysicalFolder, childItem.Kind); + } + + [Test] + public void AddFromDirectory_DirectoryContainsChildDirectoryWithNoFiles_MSBuildProjectItemAddedForChildDirectoryButNotParent() + { + CreateProjectItems(); + msbuildProject.FileName = @"d:\projects\myproject\myproject.csproj"; + string parentDirectory = @"d:\projects\myproject\tools"; + AddDirectoryToFakeFileSystem(parentDirectory, "packages"); + + projectItems.AddFromDirectory(parentDirectory); + + var item = msbuildProject.Items[0] as FileProjectItem; + + Assert.AreEqual(@"tools\packages", item.Include); + Assert.AreEqual(ItemType.Folder, item.ItemType); + Assert.AreEqual(@"d:\projects\myproject\tools\packages", item.FileName); + Assert.AreEqual(1, msbuildProject.Items.Count); + } + + [Test] + public void AddFromDirectory_DirectoryContainsChildDirectoryWithOneFile_MSBuildProjectItemAddedForFileButNotForParentNorChildDirectory() + { + CreateProjectItems(); + msbuildProject.FileName = @"d:\projects\myproject\myproject.csproj"; + string parentDirectory = @"d:\projects\myproject\tools"; + string childDirectory = @"d:\projects\myproject\tools\packages"; + AddDirectoryToFakeFileSystem(parentDirectory, "packages"); + AddFileToFakeFileSystem(childDirectory, "a.txt"); + project.TestableProject.ItemTypeToReturnFromGetDefaultItemType = ItemType.None; + + projectItems.AddFromDirectory(parentDirectory); + + var item = msbuildProject.Items[0] as FileProjectItem; + + Assert.AreEqual(@"tools\packages\a.txt", item.Include); + Assert.AreEqual(ItemType.None, item.ItemType); + Assert.AreEqual(@"d:\projects\myproject\tools\packages\a.txt", item.FileName); + Assert.AreEqual(1, msbuildProject.Items.Count); + } } } diff --git a/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/FakeFileService.cs b/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/FakeFileService.cs index e44d745171..9d2f058728 100644 --- a/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/FakeFileService.cs +++ b/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/FakeFileService.cs @@ -3,6 +3,9 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Linq; + using ICSharpCode.PackageManagement; using ICSharpCode.SharpDevelop.Project; @@ -62,5 +65,46 @@ namespace PackageManagement.Tests.Helpers { return ExistingFileNames.Contains(fileName); } + + Dictionary directoryFiles = new Dictionary(); + + public void AddFilesToFakeFileSystem(string directory, params string[] filePathsRelativeToDirectory) + { + string[] fullPathFiles = ConvertToFullPaths(directory, filePathsRelativeToDirectory); + directoryFiles.Add(directory, fullPathFiles); + } + + string[] ConvertToFullPaths(string directory, string[] pathsRelativeToDirectory) + { + return pathsRelativeToDirectory + .Select(relativePath => Path.Combine(directory, relativePath)) + .ToArray(); + } + + public string[] GetFiles(string path) + { + string[] files; + if (directoryFiles.TryGetValue(path, out files)) { + return files; + } + return new string[0]; + } + + Dictionary directories = new Dictionary(); + + public void AddDirectoryToFakeFileSystem(string parentDirectory, params string[] childDirectoryPathsRelativeToParent) + { + string[] fullPathChildDirectories = ConvertToFullPaths(parentDirectory, childDirectoryPathsRelativeToParent); + directories.Add(parentDirectory, fullPathChildDirectories); + } + + public string[] GetDirectories(string path) + { + string[] childDirectories; + if (directories.TryGetValue(path, out childDirectories)) { + return childDirectories; + } + return new string[0]; + } } }