Skip to content

Commit

Permalink
Handle folder component sync correctly. Closes #4720
Browse files Browse the repository at this point in the history
  • Loading branch information
comintern committed Jan 18, 2019
1 parent 8ab45d8 commit 446f1e1
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 8 deletions.
Expand Up @@ -44,7 +44,7 @@ public sealed class CodeExplorerCustomFolderViewModel : CodeExplorerItemViewMode

public string FullPath { get; }

public string FolderAttribute => $"@Folder(\"{FullPath.Replace("\"", string.Empty)}\")";
public string FolderAttribute => $"'@Folder(\"{FullPath.Replace("\"", string.Empty)}\")";

/// <summary>
/// One-based depth in the folder hierarchy.
Expand Down Expand Up @@ -76,7 +76,7 @@ protected override void AddNewChildren(List<Declaration> declarations)
}
}

foreach (var declaration in declarations.GroupBy(item => item.ComponentName))
foreach (var declaration in declarations.Where(child => child.IsInFolder(FullPath)).GroupBy(item => item.ComponentName))
{
var moduleName = declaration.Key;
var parent = declarations.SingleOrDefault(item =>
Expand All @@ -91,25 +91,56 @@ protected override void AddNewChildren(List<Declaration> declarations)
!ComponentTypes.Contains(item.DeclarationType) && item.ComponentName == moduleName);

AddChild(new CodeExplorerComponentViewModel(this, parent, members, _vbe));
declarations.Remove(parent);
}
}

public override void Synchronize(List<Declaration> updated)
{
var declarations = updated.Where(declaration => declaration.IsInFolderOrSubFolder(FullPath)).ToList();
SynchronizeChildren(updated);
}

protected override void SynchronizeChildren(List<Declaration> updated)
{
var declarations = updated.Where(declaration => declaration.IsInFolderOrSubFolder(FullPath)).ToList();

if (!declarations.Any())
{
Declaration = null;
return;
}

foreach (var declaration in declarations)
var synchronizing = declarations.ToList();

foreach (var subfolder in Children.OfType<CodeExplorerCustomFolderViewModel>().ToList())
{
subfolder.SynchronizeChildren(declarations);
if (subfolder.Declaration is null)
{
RemoveChild(subfolder);
continue;
}

var synchronized = synchronizing.Where(child => !declarations.Contains(child)).ToList();
foreach (var declaration in synchronized)
{
updated.Remove(declaration);
}
}

foreach (var child in Children.OfType<CodeExplorerComponentViewModel>().ToList())
{
updated.Remove(declaration);
child.Synchronize(updated);
if (child.Declaration is null)
{
RemoveChild(child);
continue;
}

updated.Remove(child.Declaration);
}

SynchronizeChildren(declarations);
AddNewChildren(updated);
}
}
}
Expand Up @@ -79,7 +79,7 @@ protected void AddComponent(CodeExplorerItemViewModel node)
? nodeProject.VBComponents
: ComponentsCollectionFromActiveProject())
{
var folderAnnotation = $"'@Folder(\"{GetFolder(node)}\")";
var folderAnnotation = (node is CodeExplorerCustomFolderViewModel folder) ? folder.FolderAttribute : $"'@Folder(\"{GetFolder(node)}\")";

using (var newComponent = components.Add(ComponentType))
{
Expand Down Expand Up @@ -110,7 +110,7 @@ protected void AddComponent(CodeExplorerItemViewModel node, string moduleText)
? nodeProject.VBComponents
: ComponentsCollectionFromActiveProject())
{
var folderAnnotation = $"'@Folder(\"{GetFolder(node)}\")";
var folderAnnotation = (node is CodeExplorerCustomFolderViewModel folder) ? folder.FolderAttribute : $"'@Folder(\"{GetFolder(node)}\")";
var fileName = CreateTempTextFile(moduleText);

using (var newComponent = components.Import(fileName))
Expand Down
136 changes: 136 additions & 0 deletions RubberduckTests/CodeExplorer/CodeExplorerFolderTests.cs
@@ -1,7 +1,14 @@
using NUnit.Framework;
using Rubberduck.Navigation.CodeExplorer;
using Rubberduck.Parsing.Annotations;
using Rubberduck.Parsing.Symbols;
using Rubberduck.VBEditor;
using Rubberduck.VBEditor.SafeComWrappers;
using Rubberduck.VBEditor.Utility;
using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;

namespace RubberduckTests.CodeExplorer
{
Expand Down Expand Up @@ -54,6 +61,123 @@ Sub Foo()
}
}

[Category("Code Explorer")]
[Test]
public void AddedModuleIsAtCorrectDepth_DefaultNode()
{
const string inputCode =
@"'@Folder(""First.Second.Third"")
Sub Foo()
Dim d As Boolean
d = True
End Sub";

using (var explorer = new MockedCodeExplorer(ProjectType.HostProject, new[] { ComponentType.StandardModule }, new[] { inputCode })
.SelectFirstProject())
{
var project = (CodeExplorerProjectViewModel)explorer.ViewModel.SelectedItem;
var folder = project.Children.OfType<CodeExplorerCustomFolderViewModel>().First(node => node.Name.Equals(project.Declaration.IdentifierName));
var declarations = project.State.AllUserDeclarations.ToList();
declarations.Add(GetNewClassDeclaration(project.Declaration, "Foo"));

project.Synchronize(declarations);
var added = folder.Children.OfType<CodeExplorerComponentViewModel>().Single();

Assert.AreEqual(DeclarationType.ClassModule, added.Declaration.DeclarationType);
Assert.AreEqual(project.Declaration.IdentifierName, added.Declaration.CustomFolder);
}
}

[Category("Code Explorer")]
[Test]
public void AddedModuleIsAtCorrectDepth_RootNode()
{
const string inputCode =
@"'@Folder(""First.Second.Third"")
Sub Foo()
Dim d As Boolean
d = True
End Sub";

using (var explorer = new MockedCodeExplorer(ProjectType.HostProject, new[] { ComponentType.StandardModule }, new[] { inputCode })
.SelectFirstCustomFolder())
{
var project = explorer.ViewModel.Projects.OfType<CodeExplorerProjectViewModel>().First();
var folder = (CodeExplorerCustomFolderViewModel)explorer.ViewModel.SelectedItem;
var declarations = project.State.AllUserDeclarations.ToList();
declarations.Add(GetNewClassDeclaration(project.Declaration, "Foo", "\"First\""));

project.Synchronize(declarations);
var added = folder.Children.OfType<CodeExplorerComponentViewModel>().Single();

Assert.AreEqual(DeclarationType.ClassModule, added.Declaration.DeclarationType);
Assert.AreEqual("\"First\"", added.Declaration.CustomFolder);
}
}

[Category("Code Explorer")]
[Test]
public void AddedModuleIsAtCorrectDepth_SubNode()
{
const string inputCode =
@"'@Folder(""First.Second.Third"")
Sub Foo()
Dim d As Boolean
d = True
End Sub";

using (var explorer = new MockedCodeExplorer(ProjectType.HostProject, new[] { ComponentType.StandardModule }, new[] { inputCode })
.SelectFirstCustomFolder())
{
var project = explorer.ViewModel.Projects.OfType<CodeExplorerProjectViewModel>().First();
var folder = (CodeExplorerCustomFolderViewModel)explorer.ViewModel.SelectedItem;
var subfolder = folder.Children.OfType<CodeExplorerCustomFolderViewModel>().Single();
var declarations = project.State.AllUserDeclarations.ToList();
declarations.Add(GetNewClassDeclaration(project.Declaration, "Foo", "\"First.Second\""));

project.Synchronize(declarations);
var added = subfolder.Children.OfType<CodeExplorerComponentViewModel>().Single();

Assert.AreEqual(DeclarationType.ClassModule, added.Declaration.DeclarationType);
Assert.AreEqual("\"First.Second\"", added.Declaration.CustomFolder);
}
}

[Category("Code Explorer")]
[Test]
public void AddedModuleIsAtCorrectDepth_TerminalNode()
{
const string inputCode =
@"'@Folder(""First.Second.Third"")
Sub Foo()
Dim d As Boolean
d = True
End Sub";

using (var explorer = new MockedCodeExplorer(ProjectType.HostProject, new[] { ComponentType.StandardModule }, new[] { inputCode })
.SelectFirstCustomFolder())
{
var project = explorer.ViewModel.Projects.OfType<CodeExplorerProjectViewModel>().First();
var folder = (CodeExplorerCustomFolderViewModel)explorer.ViewModel.SelectedItem;
var subfolder = folder.Children.OfType<CodeExplorerCustomFolderViewModel>().Single()
.Children.OfType<CodeExplorerCustomFolderViewModel>().Single();
var declarations = project.State.AllUserDeclarations.ToList();
declarations.Add(GetNewClassDeclaration(project.Declaration, "Foo", "\"First.Second.Third\""));

project.Synchronize(declarations);

var added = subfolder.Children.OfType<CodeExplorerComponentViewModel>()
.SingleOrDefault(node => node.Declaration.DeclarationType == DeclarationType.ClassModule);

Assert.IsNotNull(added);
Assert.AreEqual("\"First.Second.Third\"", added.Declaration.CustomFolder);
}
}

[Category("Code Explorer")]
[Test]
public void SubFolderModuleIsChildOfDeepestSubFolder()
Expand Down Expand Up @@ -167,5 +291,17 @@ public void FoldersNamesAreCaseSensitive()
Assert.AreEqual(3, project.Children.Count);
}
}

private static Declaration GetNewClassDeclaration(Declaration project, string name, string folder = "")
{
var annotations = string.IsNullOrEmpty(folder)
? Enumerable.Empty<IAnnotation>()
: new[] { new FolderAnnotation(new QualifiedSelection(project.QualifiedModuleName, new Selection(1, 1)), null, new[] { folder }) };

var declaration =
new ClassModuleDeclaration(new QualifiedMemberName(project.QualifiedModuleName, name), project, name, true, annotations, new Attributes());

return declaration;
}
}
}

0 comments on commit 446f1e1

Please sign in to comment.