From 5588806259d6bb9e154ac64c743f970fb2fbbc66 Mon Sep 17 00:00:00 2001 From: Matt Ward Date: Sun, 8 Jul 2012 12:40:49 +0100 Subject: [PATCH] Update the projects window without refreshing the entire tree as MvcScaffolding adds new files and folders to the project. --- .../Project/PackageManagement.csproj | 4 + .../FakePackageManagementProjectService.cs | 7 + .../Project/Src/EnvDTE/Project.cs | 5 + .../Project/Src/EnvDTE/ProjectItems.cs | 18 +- .../Src/IPackageManagementProjectService.cs | 4 +- .../Project/Src/IProjectBrowserUpdater.cs | 11 + .../Src/PackageManagementProjectService.cs | 5 + .../Project/Src/ProjectBrowserUpdater.cs | 44 ++ .../Src/ThreadSafeProjectBrowserUpdater.cs | 31 ++ .../UpdateProjectBrowserFileNodesVisitor.cs | 141 ++++++ .../Test/PackageManagement.Tests.csproj | 5 + .../Test/Src/EnvDTE/ProjectItemsTests.cs | 37 ++ .../Test/Src/Helpers/ProjectHelper.cs | 1 + .../Test/Src/ProjectBrowserUpdaterTests.cs | 435 ++++++++++++++++++ .../Gui/Components/ExtTreeView/ExtTreeNode.cs | 2 +- 15 files changed, 741 insertions(+), 9 deletions(-) create mode 100644 src/AddIns/Misc/PackageManagement/Project/Src/IProjectBrowserUpdater.cs create mode 100644 src/AddIns/Misc/PackageManagement/Project/Src/ProjectBrowserUpdater.cs create mode 100644 src/AddIns/Misc/PackageManagement/Project/Src/ThreadSafeProjectBrowserUpdater.cs create mode 100644 src/AddIns/Misc/PackageManagement/Project/Src/UpdateProjectBrowserFileNodesVisitor.cs create mode 100644 src/AddIns/Misc/PackageManagement/Test/Src/ProjectBrowserUpdaterTests.cs diff --git a/src/AddIns/Misc/PackageManagement/Project/PackageManagement.csproj b/src/AddIns/Misc/PackageManagement/Project/PackageManagement.csproj index 39507f9ed5..532257cbd9 100644 --- a/src/AddIns/Misc/PackageManagement/Project/PackageManagement.csproj +++ b/src/AddIns/Misc/PackageManagement/Project/PackageManagement.csproj @@ -130,6 +130,7 @@ + @@ -225,6 +226,7 @@ + @@ -381,6 +383,7 @@ + @@ -427,6 +430,7 @@ + diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/Design/FakePackageManagementProjectService.cs b/src/AddIns/Misc/PackageManagement/Project/Src/Design/FakePackageManagementProjectService.cs index 5bbb903496..c41a7742fd 100644 --- a/src/AddIns/Misc/PackageManagement/Project/Src/Design/FakePackageManagementProjectService.cs +++ b/src/AddIns/Misc/PackageManagement/Project/Src/Design/FakePackageManagementProjectService.cs @@ -85,5 +85,12 @@ namespace ICSharpCode.PackageManagement.Design { return new DefaultProjectContent(); } + + public IProjectBrowserUpdater ProjectBrowserUpdater; + + public IProjectBrowserUpdater CreateProjectBrowserUpdater() + { + return ProjectBrowserUpdater; + } } } diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/Project.cs b/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/Project.cs index 7da23c5ee2..dff3cde73b 100644 --- a/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/Project.cs +++ b/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/Project.cs @@ -256,5 +256,10 @@ namespace ICSharpCode.PackageManagement.EnvDTE { return FileUtility.GetRelativePath(MSBuildProject.Directory, path); } + + internal IProjectBrowserUpdater CreateProjectBrowserUpdater() + { + return projectService.CreateProjectBrowserUpdater(); + } } } diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/ProjectItems.cs b/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/ProjectItems.cs index eef0ab2cc3..5474a667e3 100644 --- a/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/ProjectItems.cs +++ b/src/AddIns/Misc/PackageManagement/Project/Src/EnvDTE/ProjectItems.cs @@ -89,17 +89,21 @@ namespace ICSharpCode.PackageManagement.EnvDTE public virtual ProjectItem AddFromDirectory(string directory) { - ProjectItem directoryItem = project.AddDirectoryProjectItemUsingFullPath(directory); - project.Save(); - return directoryItem; + using (IProjectBrowserUpdater updater = project.CreateProjectBrowserUpdater()) { + ProjectItem directoryItem = project.AddDirectoryProjectItemUsingFullPath(directory); + project.Save(); + return directoryItem; + } } public virtual ProjectItem AddFromFile(string fileName) { - ProjectItem projectItem = project.AddFileProjectItemUsingFullPath(fileName); - project.Save(); - fileService.ParseFile(fileName); - return projectItem; + using (IProjectBrowserUpdater updater = project.CreateProjectBrowserUpdater()) { + ProjectItem projectItem = project.AddFileProjectItemUsingFullPath(fileName); + project.Save(); + fileService.ParseFile(fileName); + return projectItem; + } } public virtual int Count { diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/IPackageManagementProjectService.cs b/src/AddIns/Misc/PackageManagement/Project/Src/IPackageManagementProjectService.cs index a692a977d1..1a85ff0827 100644 --- a/src/AddIns/Misc/PackageManagement/Project/Src/IPackageManagementProjectService.cs +++ b/src/AddIns/Misc/PackageManagement/Project/Src/IPackageManagementProjectService.cs @@ -25,6 +25,8 @@ namespace ICSharpCode.PackageManagement IEnumerable GetOpenProjects(); - IProjectContent GetProjectContent(IProject project); + IProjectContent GetProjectContent(IProject project); + + IProjectBrowserUpdater CreateProjectBrowserUpdater(); } } diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/IProjectBrowserUpdater.cs b/src/AddIns/Misc/PackageManagement/Project/Src/IProjectBrowserUpdater.cs new file mode 100644 index 0000000000..f855cdb80a --- /dev/null +++ b/src/AddIns/Misc/PackageManagement/Project/Src/IProjectBrowserUpdater.cs @@ -0,0 +1,11 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; + +namespace ICSharpCode.PackageManagement +{ + public interface IProjectBrowserUpdater : IDisposable + { + } +} diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/PackageManagementProjectService.cs b/src/AddIns/Misc/PackageManagement/Project/Src/PackageManagementProjectService.cs index 63ad28d556..3bc8a20ff0 100644 --- a/src/AddIns/Misc/PackageManagement/Project/Src/PackageManagementProjectService.cs +++ b/src/AddIns/Misc/PackageManagement/Project/Src/PackageManagementProjectService.cs @@ -94,5 +94,10 @@ namespace ICSharpCode.PackageManagement add { ProjectService.SolutionFolderRemoved += value; } remove { ProjectService.SolutionFolderRemoved -= value; } } + + public IProjectBrowserUpdater CreateProjectBrowserUpdater() + { + return new ThreadSafeProjectBrowserUpdater(); + } } } diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/ProjectBrowserUpdater.cs b/src/AddIns/Misc/PackageManagement/Project/Src/ProjectBrowserUpdater.cs new file mode 100644 index 0000000000..c28730f8d9 --- /dev/null +++ b/src/AddIns/Misc/PackageManagement/Project/Src/ProjectBrowserUpdater.cs @@ -0,0 +1,44 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using ICSharpCode.SharpDevelop.Project; + +namespace ICSharpCode.PackageManagement +{ + public class ProjectBrowserUpdater : IProjectBrowserUpdater + { + ProjectBrowserControl projectBrowser; + + public ProjectBrowserUpdater() + : this(ProjectBrowserPad.Instance.ProjectBrowserControl) + { + } + + public ProjectBrowserUpdater(ProjectBrowserControl projectBrowser) + { + this.projectBrowser = projectBrowser; + ProjectService.ProjectItemAdded += ProjectItemAdded; + } + + protected virtual void ProjectItemAdded(object sender, ProjectItemEventArgs e) + { + if (e.ProjectItem is FileProjectItem) { + AddFileProjectItemToProjectBrowser(e); + } + } + + void AddFileProjectItemToProjectBrowser(ProjectItemEventArgs e) + { + var visitor = new UpdateProjectBrowserFileNodesVisitor(e); + foreach (AbstractProjectBrowserTreeNode node in projectBrowser.TreeView.Nodes) { + node.AcceptVisitor(visitor, null); + } + } + + public void Dispose() + { + ProjectService.ProjectItemAdded -= ProjectItemAdded; + } + } +} diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/ThreadSafeProjectBrowserUpdater.cs b/src/AddIns/Misc/PackageManagement/Project/Src/ThreadSafeProjectBrowserUpdater.cs new file mode 100644 index 0000000000..e551f89aee --- /dev/null +++ b/src/AddIns/Misc/PackageManagement/Project/Src/ThreadSafeProjectBrowserUpdater.cs @@ -0,0 +1,31 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using ICSharpCode.SharpDevelop.Gui; +using ICSharpCode.SharpDevelop.Project; + +namespace ICSharpCode.PackageManagement +{ + public class ThreadSafeProjectBrowserUpdater : ProjectBrowserUpdater + { + public ThreadSafeProjectBrowserUpdater() + : base(GetProjectBrowserControl()) + { + } + + static ProjectBrowserControl GetProjectBrowserControl() + { + if (WorkbenchSingleton.InvokeRequired) { + return WorkbenchSingleton.SafeThreadFunction(() => GetProjectBrowserControl()); + } else { + return ProjectBrowserPad.Instance.ProjectBrowserControl; + } + } + + protected override void ProjectItemAdded(object sender, ProjectItemEventArgs e) + { + WorkbenchSingleton.SafeThreadAsyncCall(() => base.ProjectItemAdded(sender, e)); + } + } +} diff --git a/src/AddIns/Misc/PackageManagement/Project/Src/UpdateProjectBrowserFileNodesVisitor.cs b/src/AddIns/Misc/PackageManagement/Project/Src/UpdateProjectBrowserFileNodesVisitor.cs new file mode 100644 index 0000000000..30778b8566 --- /dev/null +++ b/src/AddIns/Misc/PackageManagement/Project/Src/UpdateProjectBrowserFileNodesVisitor.cs @@ -0,0 +1,141 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Windows.Forms; + +using ICSharpCode.Core; +using ICSharpCode.SharpDevelop.Gui; +using ICSharpCode.SharpDevelop.Project; + +namespace ICSharpCode.PackageManagement +{ + public class UpdateProjectBrowserFileNodesVisitor : ProjectBrowserTreeNodeVisitor + { + ProjectItemEventArgs projectItemEventArgs; + FileProjectItem newFileAddedToProject; + string directoryForNewFileAddedToProject; + + public UpdateProjectBrowserFileNodesVisitor(ProjectItemEventArgs projectItemEventArgs) + { + this.projectItemEventArgs = projectItemEventArgs; + this.newFileAddedToProject = projectItemEventArgs.ProjectItem as FileProjectItem; + } + + string DirectoryForNewFileAddedToProject { + get { + if (directoryForNewFileAddedToProject == null) { + directoryForNewFileAddedToProject = Path.GetDirectoryName(newFileAddedToProject.FileName); + } + return directoryForNewFileAddedToProject; + } + } + + public override object Visit(ProjectNode projectNode, object data) + { + if (IsFileAddedInProject(projectNode)) { + return Visit((DirectoryNode)projectNode, data); + } + return null; + } + + public override object Visit(DirectoryNode directoryNode, object data) + { + if (!ShouldVisitDirectoryNode(directoryNode)) + return null; + + if (IsImmediateParentForNewFile(directoryNode)) { + AddFileNodeTo(directoryNode); + } else if (IsChildDirectoryNodeMissingForNewFile(directoryNode)) { + AddChildDirectoryNodeForNewFileTo(directoryNode); + } else { + return base.Visit(directoryNode, data); + } + return null; + } + + bool ShouldVisitDirectoryNode(DirectoryNode directoryNode) + { + return directoryNode.IsInitialized && IsNewFileInsideDirectory(directoryNode); + } + + bool IsNewFileInsideDirectory(DirectoryNode directoryNode) + { + return FileUtility.IsBaseDirectory(directoryNode.Directory, DirectoryForNewFileAddedToProject); + } + + bool IsFileAddedInProject(ProjectNode projectNode) + { + return projectNode.Project == newFileAddedToProject.Project; + } + + bool IsImmediateParentForNewFile(DirectoryNode directoryNode) + { + return FileUtility.IsBaseDirectory(DirectoryForNewFileAddedToProject, directoryNode.Directory); + } + + string GetDirectoryForFileAddedToProject() + { + return Path.GetDirectoryName(newFileAddedToProject.FileName); + } + + void AddChildDirectoryNodeForNewFileTo(DirectoryNode parentNode) + { + string childDirectory = GetMissingChildDirectory(parentNode.Directory); + AddDirectoryNodeTo(parentNode, childDirectory); + } + + string GetMissingChildDirectory(string parentDirectory) + { + string relativeDirectoryForNewFile = GetRelativeDirectoryForNewFile(parentDirectory); + string childDirectoryName = GetFirstChildDirectoryName(relativeDirectoryForNewFile); + return Path.Combine(parentDirectory, childDirectoryName); + } + + string GetRelativeDirectoryForNewFile(string baseDirectory) + { + return FileUtility.GetRelativePath(baseDirectory, DirectoryForNewFileAddedToProject); + } + + string GetFirstChildDirectoryName(string fullSubFolderPath) + { + return fullSubFolderPath.Split('\\').First(); + } + + void AddDirectoryNodeTo(TreeNode parentNode, string directory) + { + var directoryNode = new DirectoryNode(directory, FileNodeStatus.InProject); + directoryNode.InsertSorted(parentNode); + } + + void AddFileNodeTo(TreeNode node) + { + var fileNode = new FileNode(newFileAddedToProject.FileName, FileNodeStatus.InProject); + fileNode.InsertSorted(node); + } + + bool IsChildDirectoryNodeMissingForNewFile(DirectoryNode parentDirectoryNode) + { + return !IsChildDirectoryNodeAlreadyAddedForNewFile(parentDirectoryNode); + } + + bool IsChildDirectoryNodeAlreadyAddedForNewFile(DirectoryNode parentDirectoryNode) + { + return GetChildDirectoryNodes(parentDirectoryNode) + .Any(childDirectoryNode => DirectoryOfNewFileStartsWith(childDirectoryNode)); + } + + bool DirectoryOfNewFileStartsWith(DirectoryNode directoryNode) + { + return FileUtility.IsBaseDirectory(directoryNode.Directory, DirectoryForNewFileAddedToProject); + } + + IEnumerable GetChildDirectoryNodes(ExtTreeNode parentNode) + { + return parentNode.AllNodes.OfType(); + } + } +} diff --git a/src/AddIns/Misc/PackageManagement/Test/PackageManagement.Tests.csproj b/src/AddIns/Misc/PackageManagement/Test/PackageManagement.Tests.csproj index 316a22e976..9a9ac5b54e 100644 --- a/src/AddIns/Misc/PackageManagement/Test/PackageManagement.Tests.csproj +++ b/src/AddIns/Misc/PackageManagement/Test/PackageManagement.Tests.csproj @@ -185,6 +185,7 @@ + @@ -361,6 +362,10 @@ + + SharpDevelop.exe + Always + diff --git a/src/AddIns/Misc/PackageManagement/Test/Src/EnvDTE/ProjectItemsTests.cs b/src/AddIns/Misc/PackageManagement/Test/Src/EnvDTE/ProjectItemsTests.cs index 65d735e9c5..711716a17e 100644 --- a/src/AddIns/Misc/PackageManagement/Test/Src/EnvDTE/ProjectItemsTests.cs +++ b/src/AddIns/Misc/PackageManagement/Test/Src/EnvDTE/ProjectItemsTests.cs @@ -10,6 +10,7 @@ using ICSharpCode.PackageManagement.EnvDTE; using ICSharpCode.SharpDevelop.Project; using NUnit.Framework; using PackageManagement.Tests.Helpers; +using Rhino.Mocks; using DTE = ICSharpCode.PackageManagement.EnvDTE; namespace PackageManagement.Tests.EnvDTE @@ -40,6 +41,13 @@ namespace PackageManagement.Tests.EnvDTE fakeFileService.AddDirectoryToFakeFileSystem(parentDirectory, childDirectory); } + IProjectBrowserUpdater CreateProjectBrowserUpdater() + { + IProjectBrowserUpdater projectBrowserUpdater = MockRepository.GenerateStub(); + project.FakeProjectService.ProjectBrowserUpdater = projectBrowserUpdater; + return projectBrowserUpdater; + } + [Test] public void AddFromFileCopy_AddFileNameOutsideProjectFolder_FileIsIncludedInProjectInProjectFolder() { @@ -604,5 +612,34 @@ namespace PackageManagement.Tests.EnvDTE Assert.AreEqual(@"d:\projects\myproject\tools\packages\a.txt", item.FileName); Assert.AreEqual(1, msbuildProject.Items.Count); } + + [Test] + public void AddFromFile_FullFileNameIsInsideProject_ProjectBrowserUpdaterIsDisposed() + { + CreateProjectItems(); + IProjectBrowserUpdater projectBrowserUpdater = CreateProjectBrowserUpdater(); + msbuildProject.FileName = @"d:\projects\myproject\myproject.csproj"; + string fileName = @"d:\projects\myproject\tools\test.cs"; + + msbuildProject.ItemTypeToReturnFromGetDefaultItemType = ItemType.Page; + projectItems.AddFromFile(fileName); + + projectBrowserUpdater.AssertWasCalled(updater => updater.Dispose()); + } + + [Test] + public void AddFromDirectory_EmptyDirectoryInsideProject_ProjectBrowserUpdaterIsDisposed() + { + CreateProjectItems(); + IProjectBrowserUpdater projectBrowserUpdater = CreateProjectBrowserUpdater(); + msbuildProject.FileName = @"d:\projects\myproject\myproject.csproj"; + string directory = @"d:\projects\myproject\tools"; + + projectItems.AddFromDirectory(directory); + + bool saved = msbuildProject.IsSaved; + + projectBrowserUpdater.AssertWasCalled(updater => updater.Dispose()); + } } } diff --git a/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/ProjectHelper.cs b/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/ProjectHelper.cs index 924a5dc878..ad6ad3c20d 100644 --- a/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/ProjectHelper.cs +++ b/src/AddIns/Misc/PackageManagement/Test/Src/Helpers/ProjectHelper.cs @@ -28,6 +28,7 @@ namespace PackageManagement.Tests.Helpers var project = new TestableProject(createInfo); project.Parent = solution; + solution.AddFolder(project); return project; } diff --git a/src/AddIns/Misc/PackageManagement/Test/Src/ProjectBrowserUpdaterTests.cs b/src/AddIns/Misc/PackageManagement/Test/Src/ProjectBrowserUpdaterTests.cs new file mode 100644 index 0000000000..6dc80c8aa7 --- /dev/null +++ b/src/AddIns/Misc/PackageManagement/Test/Src/ProjectBrowserUpdaterTests.cs @@ -0,0 +1,435 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Resources; +using ICSharpCode.Core; +using ICSharpCode.PackageManagement; +using ICSharpCode.SharpDevelop.Gui; +using ICSharpCode.SharpDevelop.Project; +using NUnit.Framework; +using PackageManagement.Tests.Helpers; + +namespace PackageManagement.Tests +{ + [TestFixture] + public class ProjectBrowserUpdaterTests + { + ProjectBrowserControl projectBrowser; + ProjectBrowserUpdater projectBrowserUpdater; + TestableProject project; + + [TestFixtureSetUp] + public void InitFixture() + { + PropertyService.InitializeServiceForUnitTests(); + + Assembly assembly = Assembly.Load("SharpDevelop"); + ResourceService.RegisterNeutralImages(new ResourceManager("Resources.BitmapResources", assembly)); + ResourceService.RegisterNeutralStrings(new ResourceManager("Resources.StringResources", assembly)); + + AddDefaultDotNetNodeBuilderToAddinTree(); + } + + void AddDefaultDotNetNodeBuilderToAddinTree() + { + string xml = + "\r\n" + + " \r\n" + + " \r\n" + + " \r\n" + + " \r\n" + + " \r\n" + + " \r\n" + + ""; + + var addin = AddIn.Load(new StringReader(xml), String.Empty, null); + addin.Enabled = true; + AddInTree.InsertAddIn(addin); + } + + [TearDown] + public void TearDown() + { + projectBrowser.Dispose(); + } + + FileNode GetFirstFileChildNode(ExtTreeNode node) + { + return GetFirstChildNode(node, childNode => childNode is FileNode) as FileNode; + } + + ExtTreeNode GetFirstChildNode(ExtTreeNode node, Func predicate) + { + return node.AllNodes.FirstOrDefault(predicate); + } + + DirectoryNode GetFirstDirectoryChildNode(ExtTreeNode node) + { + return GetFirstChildNode(node, childNode => childNode is DirectoryNode) as DirectoryNode; + } + + void AddFileToUnknownProject(string fileName) + { + TestableProject unknownProject = ProjectHelper.CreateTestProject(); + var fileProjectItem = new FileProjectItem(unknownProject, ItemType.Compile); + fileProjectItem.FileName = fileName; + ProjectService.AddProjectItem(unknownProject, fileProjectItem); + } + + void CreateProjectBrowserUpdater() + { + CreateProjectBrowserControl(); + CreateProjectBrowserUpdater(projectBrowser); + } + + void CreateProjectBrowserControl() + { + projectBrowser = new ProjectBrowserControl(); + } + + void CreateProjectBrowserUpdater(ProjectBrowserControl control) + { + projectBrowserUpdater = new ProjectBrowserUpdater(control); + } + + ProjectNode AddProjectToProjectBrowser(string projectFileName) + { + project = ProjectHelper.CreateTestProject(); + project.FileName = projectFileName; + + projectBrowser.ViewSolution(project.ParentSolution); + var solutionNode = projectBrowser.RootNode as SolutionNode; + return solutionNode.FirstNode as ProjectNode; + } + + void AddFileToProject(string fileName) + { + var fileProjectItem = new FileProjectItem(project, ItemType.Compile) { + FileName = fileName + }; + AddProjectItemToProject(fileProjectItem); + } + + void AddReferenceToProject(string name) + { + var reference = new ReferenceProjectItem(project, name); + AddProjectItemToProject(reference); + } + + void AddProjectItemToProject(ProjectItem item) + { + ProjectService.AddProjectItem(project, item); + } + + FileNode GetFileChildNodeAtIndex(ExtTreeNode node, int index) + { + return GetChildNodesOfType(node).ElementAt(index); + } + + IEnumerable GetChildNodesOfType(ExtTreeNode parentNode) + { + return parentNode.AllNodes.OfType(); + } + + DirectoryNode GetDirectoryChildNodeAtIndex(ProjectNode projectNode, int index) + { + return GetChildNodesOfType(projectNode).ElementAt(index); + } + + [Test] + public void Constructor_ProjectWithNoFilesAndFileAddedToProjectRootDirectory_FileNodeAddedToProjectBrowser() + { + CreateProjectBrowserUpdater(); + ProjectNode projectNode = AddProjectToProjectBrowser(@"d:\projects\MyProject\MyProject.csproj"); + projectNode.Expanding(); + + AddFileToProject(@"d:\projects\MyProject\test.cs"); + + FileNode fileNode = GetFirstFileChildNode(projectNode); + Assert.AreEqual(@"d:\projects\MyProject\test.cs", fileNode.FileName); + Assert.AreEqual(FileNodeStatus.InProject, fileNode.FileNodeStatus); + } + + [Test] + public void Constructor_ProjectWithNoFilesAndReferenceAddedToProject_ReferenceIgnoredByProjectBrowserUpdater() + { + CreateProjectBrowserUpdater(); + ProjectNode projectNode = AddProjectToProjectBrowser(@"d:\projects\MyProject\MyProject.csproj"); + projectNode.Expanding(); + + AddReferenceToProject("System.Xml"); + + FileNode fileNode = GetFirstFileChildNode(projectNode); + Assert.IsNull(fileNode); + } + + [Test] + public void Constructor_ProjectWithNoFilesAndFileAddedToUnknownProject_FileProjectItemAddedIsIgnored() + { + CreateProjectBrowserUpdater(); + ProjectNode projectNode = AddProjectToProjectBrowser(@"d:\projects\MyProject\MyProject.csproj"); + projectNode.Expanding(); + + AddFileToUnknownProject(@"d:\projects\AnotherProject\test.cs"); + + FileNode fileNode = GetFirstFileChildNode(projectNode); + Assert.IsNull(fileNode); + } + + [Test] + public void Constructor_ProjectWithNoFilesAndFileAddedInSubDirectory_DirectoryNodeAddedToProjectNode() + { + CreateProjectBrowserUpdater(); + ProjectNode projectNode = AddProjectToProjectBrowser(@"d:\projects\MyProject\MyProject.csproj"); + projectNode.Expanding(); + + AddFileToProject(@"d:\projects\MyProject\Subfolder\test.cs"); + + DirectoryNode directoryNode = GetFirstDirectoryChildNode(projectNode); + Assert.AreEqual(@"d:\projects\MyProject\Subfolder", directoryNode.Directory); + Assert.AreEqual("Subfolder", directoryNode.Text); + Assert.AreEqual(FileNodeStatus.InProject, directoryNode.FileNodeStatus); + } + + [Test] + public void Constructor_ProjectWithNoFilesAndFileAddedTwoSubFoldersBelowProjectRootDirectory_DirectoryNodeForFirstSubFolderAddedToProjectNode() + { + CreateProjectBrowserUpdater(); + ProjectNode projectNode = AddProjectToProjectBrowser(@"d:\projects\MyProject\MyProject.csproj"); + projectNode.Expanding(); + + AddFileToProject(@"d:\projects\MyProject\Subfolder1\Subfolder2\test.cs"); + + DirectoryNode directoryNode = GetFirstDirectoryChildNode(projectNode); + Assert.AreEqual(@"d:\projects\MyProject\Subfolder1", directoryNode.Directory); + Assert.AreEqual(FileNodeStatus.InProject, directoryNode.FileNodeStatus); + } + + [Test] + public void Constructor_ProjectWithNoFilesAndFileAddedInSubdirectory_NoFileNodeAddedToProjectNode() + { + CreateProjectBrowserUpdater(); + ProjectNode projectNode = AddProjectToProjectBrowser(@"d:\projects\MyProject\MyProject.csproj"); + projectNode.Expanding(); + + AddFileToProject(@"d:\projects\MyProject\Subfolder\test.cs"); + + FileNode fileNode = GetFirstFileChildNode(projectNode); + Assert.IsNull(fileNode); + } + + [Test] + public void Constructor_ProjectNodeHasNeverBeenExpandedAndFileAddedToProject_FileNodeNotAdded() + { + CreateProjectBrowserUpdater(); + ProjectNode projectNode = AddProjectToProjectBrowser(@"d:\projects\MyProject\MyProject.csproj"); + + AddFileToProject(@"d:\projects\MyProject\test.cs"); + + FileNode fileNode = GetFirstFileChildNode(projectNode); + Assert.IsNull(fileNode); + } + + [Test] + public void Dispose_ProjectWithNoFilesAndFileAddedToProjectRootDirectoryAfterUpdaterDisposed_NoFileNodeAdded() + { + CreateProjectBrowserUpdater(); + ProjectNode projectNode = AddProjectToProjectBrowser(@"d:\projects\MyProject\MyProject.csproj"); + projectNode.Expanding(); + + projectBrowserUpdater.Dispose(); + + AddFileToProject(@"d:\projects\MyProject\test.cs"); + FileNode fileNode = GetFirstFileChildNode(projectNode); + Assert.IsNull(fileNode); + } + + [Test] + public void Constructor_ProjectWithTwoFilesAndFileAddedToProjectRootDirectory_FileNodeAddedToProjectBrowserInAlphabeticalOrder() + { + CreateProjectBrowserControl(); + ProjectNode projectNode = AddProjectToProjectBrowser(@"d:\projects\MyProject\MyProject.csproj"); + AddFileToProject(@"d:\projects\MyProject\a.cs"); + AddFileToProject(@"d:\projects\MyProject\c.cs"); + CreateProjectBrowserUpdater(projectBrowser); + projectNode.Expanding(); + + AddFileToProject(@"d:\projects\MyProject\b.cs"); + + FileNode fileNode = GetFileChildNodeAtIndex(projectNode, 1); + Assert.AreEqual(@"d:\projects\MyProject\b.cs", fileNode.FileName); + } + + [Test] + public void Constructor_ProjectWithTwoFoldersAndFileInSubFolderAddedToProject_FileDirectoryNodeAddedToProjectBrowserInAlphabeticalOrder() + { + CreateProjectBrowserControl(); + ProjectNode projectNode = AddProjectToProjectBrowser(@"d:\projects\MyProject\MyProject.csproj"); + AddFileToProject(@"d:\projects\MyProject\a\a.cs"); + AddFileToProject(@"d:\projects\MyProject\c\a.cs"); + CreateProjectBrowserUpdater(projectBrowser); + projectNode.Expanding(); + + AddFileToProject(@"d:\projects\MyProject\b\test.cs"); + + DirectoryNode directoryNode = GetDirectoryChildNodeAtIndex(projectNode, 1); + Assert.AreEqual(@"d:\projects\MyProject\b", directoryNode.Directory); + } + + [Test] + public void Constructor_ProjectWithOneFileInSubFolderAndNewFileAddedToSubFolder_DirectoryNodeNotAddedToProjectSinceItAlreadyExists() + { + CreateProjectBrowserControl(); + ProjectNode projectNode = AddProjectToProjectBrowser(@"d:\projects\MyProject\MyProject.csproj"); + AddFileToProject(@"d:\projects\MyProject\a\a.cs"); + CreateProjectBrowserUpdater(projectBrowser); + projectNode.Expanding(); + + AddFileToProject(@"d:\projects\MyProject\a\test.cs"); + + int count = GetChildNodesOfType(projectNode).Count(); + DirectoryNode directoryNode = GetFirstDirectoryChildNode(projectNode); + FileNode fileNode = GetFirstFileChildNode(directoryNode); + Assert.AreEqual(1, count); + Assert.IsNull(fileNode); + } + + [Test] + public void Constructor_ProjectWithDirectoryNodeExpandedAndNewFileAddedToDirectory_FileAddedToDirectory() + { + CreateProjectBrowserControl(); + ProjectNode projectNode = AddProjectToProjectBrowser(@"d:\projects\MyProject\MyProject.csproj"); + AddFileToProject(@"d:\projects\MyProject\a\a.cs"); + CreateProjectBrowserUpdater(projectBrowser); + projectNode.Expanding(); + DirectoryNode directoryNode = GetFirstDirectoryChildNode(projectNode); + directoryNode.Expanding(); + + AddFileToProject(@"d:\projects\MyProject\a\test.cs"); + + FileNode fileNode = GetFileChildNodeAtIndex(directoryNode, 1); + Assert.AreEqual(@"d:\projects\MyProject\a\test.cs", fileNode.FileName); + } + + [Test] + public void Constructor_ProjectWithDirectoryNodeExpandedAndNewFileAddedToSubFolderOfExpandedDirectory_NoNewFileNodedAdded() + { + CreateProjectBrowserControl(); + ProjectNode projectNode = AddProjectToProjectBrowser(@"d:\projects\MyProject\MyProject.csproj"); + AddFileToProject(@"d:\projects\MyProject\a\a.cs"); + CreateProjectBrowserUpdater(projectBrowser); + projectNode.Expanding(); + DirectoryNode directoryNode = GetFirstDirectoryChildNode(projectNode); + directoryNode.Expanding(); + + AddFileToProject(@"d:\projects\MyProject\a\b\test.cs"); + + int childFileNodesCount = GetChildNodesOfType(directoryNode).Count(); + FileNode fileNode = GetFirstFileChildNode(directoryNode); + Assert.AreEqual(1, childFileNodesCount); + Assert.AreEqual(@"d:\projects\MyProject\a\a.cs", fileNode.FileName); + } + + [Test] + public void Constructor_ProjectWithDirectoryNodeExpandedAndNewFileAddedToSubFolderOfExpandedDirectory_DirectoryNodeAddedToDirectoryNode() + { + CreateProjectBrowserControl(); + ProjectNode projectNode = AddProjectToProjectBrowser(@"d:\projects\MyProject\MyProject.csproj"); + AddFileToProject(@"d:\projects\MyProject\a\a.cs"); + CreateProjectBrowserUpdater(projectBrowser); + projectNode.Expanding(); + DirectoryNode directoryNode = GetFirstDirectoryChildNode(projectNode); + directoryNode.Expanding(); + + AddFileToProject(@"d:\projects\MyProject\a\b\test.cs"); + + DirectoryNode childDirectoryNode = GetFirstDirectoryChildNode(directoryNode); + Assert.AreEqual(@"d:\projects\MyProject\a\b", childDirectoryNode.Directory); + Assert.AreEqual(FileNodeStatus.InProject, childDirectoryNode.FileNodeStatus); + } + + [Test] + public void Constructor_ProjectWithDirectoryNodeExpandedAndNewFileAddedToSubFolderTwoLevelsBelowExpandedDirectory_DirectoryNodeAddedForChildSubFolder() + { + CreateProjectBrowserControl(); + ProjectNode projectNode = AddProjectToProjectBrowser(@"d:\projects\MyProject\MyProject.csproj"); + AddFileToProject(@"d:\projects\MyProject\a\a.cs"); + CreateProjectBrowserUpdater(projectBrowser); + projectNode.Expanding(); + DirectoryNode directoryNode = GetFirstDirectoryChildNode(projectNode); + directoryNode.Expanding(); + + AddFileToProject(@"d:\projects\MyProject\a\b\c\test.cs"); + + DirectoryNode childDirectoryNode = GetFirstDirectoryChildNode(directoryNode); + Assert.AreEqual(@"d:\projects\MyProject\a\b", childDirectoryNode.Directory); + } + + [Test] + public void Constructor_ProjectWithDirectoryNodeExpandedAndNewFileAddedToSubFolderWhichAlreadyExistsInExpandedDirectory_NewDirectoryNodeNotAdded() + { + CreateProjectBrowserControl(); + ProjectNode projectNode = AddProjectToProjectBrowser(@"d:\projects\MyProject\MyProject.csproj"); + AddFileToProject(@"d:\projects\MyProject\a\a.cs"); + AddFileToProject(@"d:\projects\MyProject\a\b\b.cs"); + CreateProjectBrowserUpdater(projectBrowser); + projectNode.Expanding(); + DirectoryNode directoryNode = GetFirstDirectoryChildNode(projectNode); + directoryNode.Expanding(); + + AddFileToProject(@"d:\projects\MyProject\a\b\test.cs"); + + int directoryNodeCount = GetChildNodesOfType(directoryNode).Count(); + DirectoryNode childDirectoryNode = GetFirstDirectoryChildNode(directoryNode); + Assert.AreEqual(1, directoryNodeCount); + Assert.AreEqual(@"d:\projects\MyProject\a\b", childDirectoryNode.Directory); + } + + [Test] + public void Constructor_ProjectWithDirectoryNodeTwoLevelsDeepExpandedAndNewFileAddedToSubFolderOfExpandedDirectory_DirectoryNodeAddedToDirectoryNode() + { + CreateProjectBrowserControl(); + ProjectNode projectNode = AddProjectToProjectBrowser(@"d:\projects\MyProject\MyProject.csproj"); + AddFileToProject(@"d:\projects\MyProject\a\b\a.cs"); + CreateProjectBrowserUpdater(projectBrowser); + projectNode.Expanding(); + DirectoryNode topLevelDirectoryNode = GetFirstDirectoryChildNode(projectNode); + topLevelDirectoryNode.Expanding(); + DirectoryNode directoryNode = GetFirstDirectoryChildNode(topLevelDirectoryNode); + directoryNode.Expanding(); + + AddFileToProject(@"d:\projects\MyProject\a\b\test.cs"); + + FileNode fileNode = GetFileChildNodeAtIndex(directoryNode, 1); + Assert.AreEqual(@"d:\projects\MyProject\a\b\test.cs", fileNode.FileName); + Assert.AreEqual(FileNodeStatus.InProject, fileNode.FileNodeStatus); + } + + [Test] + public void Constructor_ProjectWithTwoDirectoryNodesExpandedAndNewFileAddedToFirstExpandedDirectory_SecondDirectoryNodeIsNotAffected() + { + CreateProjectBrowserControl(); + ProjectNode projectNode = AddProjectToProjectBrowser(@"d:\projects\MyProject\MyProject.csproj"); + AddFileToProject(@"d:\projects\MyProject\a\a.cs"); + AddFileToProject(@"d:\projects\MyProject\b\b.cs"); + CreateProjectBrowserUpdater(projectBrowser); + projectNode.Expanding(); + DirectoryNode directoryNode = GetFirstDirectoryChildNode(projectNode); + directoryNode.Expanding(); + DirectoryNode secondDirectoryNode = GetDirectoryChildNodeAtIndex(projectNode, 1); + secondDirectoryNode.Expanding(); + + AddFileToProject(@"d:\projects\MyProject\a\test.cs"); + + FileNode fileNode = GetFirstFileChildNode(secondDirectoryNode); + Assert.AreEqual(1, secondDirectoryNode.Nodes.Count); + Assert.AreEqual(@"d:\projects\MyProject\b\b.cs", fileNode.FileName); + } + } +} diff --git a/src/Main/Base/Project/Src/Gui/Components/ExtTreeView/ExtTreeNode.cs b/src/Main/Base/Project/Src/Gui/Components/ExtTreeView/ExtTreeNode.cs index 7046216122..6487f566d0 100644 --- a/src/Main/Base/Project/Src/Gui/Components/ExtTreeView/ExtTreeNode.cs +++ b/src/Main/Base/Project/Src/Gui/Components/ExtTreeView/ExtTreeNode.cs @@ -16,7 +16,7 @@ namespace ICSharpCode.SharpDevelop.Gui protected bool isInitialized = false; string image = null; - internal bool IsInitialized { + public bool IsInitialized { get { return isInitialized; }