Skip to content

Commit 81a2626

Browse files
authored
Merge pull request #4721 from comintern/next
Code Explorer folder hotfix 2
2 parents 64ae145 + 2073155 commit 81a2626

File tree

3 files changed

+245
-8
lines changed

3 files changed

+245
-8
lines changed

Rubberduck.Core/Navigation/CodeExplorer/CodeExplorerCustomFolderViewModel.cs

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public CodeExplorerCustomFolderViewModel(
4444

4545
public string FullPath { get; }
4646

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

4949
/// <summary>
5050
/// One-based depth in the folder hierarchy.
@@ -76,7 +76,7 @@ protected override void AddNewChildren(List<Declaration> declarations)
7676
}
7777
}
7878

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

9393
AddChild(new CodeExplorerComponentViewModel(this, parent, members, _vbe));
94+
declarations.Remove(parent);
9495
}
9596
}
9697

9798
public override void Synchronize(List<Declaration> updated)
9899
{
99-
var declarations = updated.Where(declaration => declaration.IsInFolderOrSubFolder(FullPath)).ToList();
100+
SynchronizeChildren(updated);
101+
}
100102

103+
protected override void SynchronizeChildren(List<Declaration> updated)
104+
{
105+
var declarations = updated.Where(declaration => declaration.IsInFolderOrSubFolder(FullPath)).ToList();
106+
101107
if (!declarations.Any())
102108
{
103109
Declaration = null;
104110
return;
105111
}
106112

107-
foreach (var declaration in declarations)
113+
var synchronizing = declarations.ToList();
114+
115+
foreach (var subfolder in Children.OfType<CodeExplorerCustomFolderViewModel>().ToList())
116+
{
117+
subfolder.SynchronizeChildren(declarations);
118+
if (subfolder.Declaration is null)
119+
{
120+
RemoveChild(subfolder);
121+
continue;
122+
}
123+
124+
var synchronized = synchronizing.Where(child => !declarations.Contains(child)).ToList();
125+
foreach (var declaration in synchronized)
126+
{
127+
updated.Remove(declaration);
128+
}
129+
}
130+
131+
foreach (var child in Children.OfType<CodeExplorerComponentViewModel>().ToList())
108132
{
109-
updated.Remove(declaration);
133+
child.Synchronize(updated);
134+
if (child.Declaration is null)
135+
{
136+
RemoveChild(child);
137+
continue;
138+
}
139+
140+
updated.Remove(child.Declaration);
110141
}
111142

112-
SynchronizeChildren(declarations);
143+
AddNewChildren(updated);
113144
}
114145
}
115146
}

Rubberduck.Core/UI/CodeExplorer/Commands/AddComponentCommand.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ protected void AddComponent(CodeExplorerItemViewModel node)
7979
? nodeProject.VBComponents
8080
: ComponentsCollectionFromActiveProject())
8181
{
82-
var folderAnnotation = $"'@Folder(\"{GetFolder(node)}\")";
82+
var folderAnnotation = (node is CodeExplorerCustomFolderViewModel folder) ? folder.FolderAttribute : $"'@Folder(\"{GetFolder(node)}\")";
8383

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

116116
using (var newComponent = components.Import(fileName))

RubberduckTests/CodeExplorer/CodeExplorerFolderTests.cs

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
using NUnit.Framework;
22
using Rubberduck.Navigation.CodeExplorer;
3+
using Rubberduck.Parsing.Annotations;
4+
using Rubberduck.Parsing.Symbols;
5+
using Rubberduck.VBEditor;
36
using Rubberduck.VBEditor.SafeComWrappers;
7+
using Rubberduck.VBEditor.Utility;
8+
using System;
9+
using System.Collections.Generic;
410
using System.Linq;
11+
using System.Runtime.InteropServices;
12+
using System.Runtime.InteropServices.ComTypes;
513

614
namespace RubberduckTests.CodeExplorer
715
{
@@ -54,6 +62,187 @@ Dim d As Boolean
5462
}
5563
}
5664

65+
[Category("Code Explorer")]
66+
[Test]
67+
public void AddedModuleIsAtCorrectDepth_DefaultNode()
68+
{
69+
const string inputCode =
70+
@"'@Folder(""First.Second.Third"")
71+
72+
Sub Foo()
73+
Dim d As Boolean
74+
d = True
75+
End Sub";
76+
77+
using (var explorer = new MockedCodeExplorer(ProjectType.HostProject, new[] { ComponentType.StandardModule }, new[] { inputCode })
78+
.SelectFirstProject())
79+
{
80+
var project = (CodeExplorerProjectViewModel)explorer.ViewModel.SelectedItem;
81+
var folder = project.Children.OfType<CodeExplorerCustomFolderViewModel>().First(node => node.Name.Equals(project.Declaration.IdentifierName));
82+
var declarations = project.State.AllUserDeclarations.ToList();
83+
declarations.Add(GetNewClassDeclaration(project.Declaration, "Foo"));
84+
85+
project.Synchronize(declarations);
86+
var added = folder.Children.OfType<CodeExplorerComponentViewModel>().Single();
87+
88+
Assert.AreEqual(DeclarationType.ClassModule, added.Declaration.DeclarationType);
89+
Assert.AreEqual(project.Declaration.IdentifierName, added.Declaration.CustomFolder);
90+
}
91+
}
92+
93+
[Category("Code Explorer")]
94+
[Test]
95+
public void AddedModuleIsAtCorrectDepth_FirstAnnotation()
96+
{
97+
const string inputCode =
98+
@"'@Folder(""First.Second.Third"")
99+
100+
Sub Foo()
101+
Dim d As Boolean
102+
d = True
103+
End Sub";
104+
105+
using (var explorer = new MockedCodeExplorer(ProjectType.HostProject, new[] { ComponentType.StandardModule }, new[] { inputCode })
106+
.SelectFirstCustomFolder())
107+
{
108+
var project = explorer.ViewModel.Projects.OfType<CodeExplorerProjectViewModel>().First();
109+
var folder = (CodeExplorerCustomFolderViewModel)explorer.ViewModel.SelectedItem;
110+
var declarations = project.State.AllUserDeclarations.ToList();
111+
112+
var annotation = new FolderAnnotation(new QualifiedSelection(project.Declaration.QualifiedModuleName, new Selection(1, 1)), null, new[] { "\"First\"" });
113+
var predeclared = new PredeclaredIdAnnotation(new QualifiedSelection(project.Declaration.QualifiedModuleName, new Selection(2, 1)), null, Enumerable.Empty<string>());
114+
115+
declarations.Add(GetNewClassDeclaration(project.Declaration, "Foo", new IAnnotation [] { annotation, predeclared }));
116+
117+
project.Synchronize(declarations);
118+
var added = folder.Children.OfType<CodeExplorerComponentViewModel>().Single();
119+
120+
Assert.AreEqual(DeclarationType.ClassModule, added.Declaration.DeclarationType);
121+
Assert.AreEqual("\"First\"", added.Declaration.CustomFolder);
122+
}
123+
}
124+
125+
[Category("Code Explorer")]
126+
[Test]
127+
public void AddedModuleIsAtCorrectDepth_NotFirstAnnotation()
128+
{
129+
const string inputCode =
130+
@"'@Folder(""First.Second.Third"")
131+
132+
Sub Foo()
133+
Dim d As Boolean
134+
d = True
135+
End Sub";
136+
137+
using (var explorer = new MockedCodeExplorer(ProjectType.HostProject, new[] { ComponentType.StandardModule }, new[] { inputCode })
138+
.SelectFirstCustomFolder())
139+
{
140+
var project = explorer.ViewModel.Projects.OfType<CodeExplorerProjectViewModel>().First();
141+
var folder = (CodeExplorerCustomFolderViewModel)explorer.ViewModel.SelectedItem;
142+
var declarations = project.State.AllUserDeclarations.ToList();
143+
144+
var annotation = new FolderAnnotation(new QualifiedSelection(project.Declaration.QualifiedModuleName, new Selection(2, 1)), null, new[] { "\"First\"" });
145+
var predeclared = new PredeclaredIdAnnotation(new QualifiedSelection(project.Declaration.QualifiedModuleName, new Selection(1, 1)), null, Enumerable.Empty<string>());
146+
147+
declarations.Add(GetNewClassDeclaration(project.Declaration, "Foo", new IAnnotation[] { predeclared, annotation }));
148+
149+
project.Synchronize(declarations);
150+
var added = folder.Children.OfType<CodeExplorerComponentViewModel>().Single();
151+
152+
Assert.AreEqual(DeclarationType.ClassModule, added.Declaration.DeclarationType);
153+
Assert.AreEqual("\"First\"", added.Declaration.CustomFolder);
154+
}
155+
}
156+
157+
[Category("Code Explorer")]
158+
[Test]
159+
public void AddedModuleIsAtCorrectDepth_RootNode()
160+
{
161+
const string inputCode =
162+
@"'@Folder(""First.Second.Third"")
163+
164+
Sub Foo()
165+
Dim d As Boolean
166+
d = True
167+
End Sub";
168+
169+
using (var explorer = new MockedCodeExplorer(ProjectType.HostProject, new[] { ComponentType.StandardModule }, new[] { inputCode })
170+
.SelectFirstCustomFolder())
171+
{
172+
var project = explorer.ViewModel.Projects.OfType<CodeExplorerProjectViewModel>().First();
173+
var folder = (CodeExplorerCustomFolderViewModel)explorer.ViewModel.SelectedItem;
174+
var declarations = project.State.AllUserDeclarations.ToList();
175+
declarations.Add(GetNewClassDeclaration(project.Declaration, "Foo", "\"First\""));
176+
177+
project.Synchronize(declarations);
178+
var added = folder.Children.OfType<CodeExplorerComponentViewModel>().Single();
179+
180+
Assert.AreEqual(DeclarationType.ClassModule, added.Declaration.DeclarationType);
181+
Assert.AreEqual("\"First\"", added.Declaration.CustomFolder);
182+
}
183+
}
184+
185+
[Category("Code Explorer")]
186+
[Test]
187+
public void AddedModuleIsAtCorrectDepth_SubNode()
188+
{
189+
const string inputCode =
190+
@"'@Folder(""First.Second.Third"")
191+
192+
Sub Foo()
193+
Dim d As Boolean
194+
d = True
195+
End Sub";
196+
197+
using (var explorer = new MockedCodeExplorer(ProjectType.HostProject, new[] { ComponentType.StandardModule }, new[] { inputCode })
198+
.SelectFirstCustomFolder())
199+
{
200+
var project = explorer.ViewModel.Projects.OfType<CodeExplorerProjectViewModel>().First();
201+
var folder = (CodeExplorerCustomFolderViewModel)explorer.ViewModel.SelectedItem;
202+
var subfolder = folder.Children.OfType<CodeExplorerCustomFolderViewModel>().Single();
203+
var declarations = project.State.AllUserDeclarations.ToList();
204+
declarations.Add(GetNewClassDeclaration(project.Declaration, "Foo", "\"First.Second\""));
205+
206+
project.Synchronize(declarations);
207+
var added = subfolder.Children.OfType<CodeExplorerComponentViewModel>().Single();
208+
209+
Assert.AreEqual(DeclarationType.ClassModule, added.Declaration.DeclarationType);
210+
Assert.AreEqual("\"First.Second\"", added.Declaration.CustomFolder);
211+
}
212+
}
213+
214+
[Category("Code Explorer")]
215+
[Test]
216+
public void AddedModuleIsAtCorrectDepth_TerminalNode()
217+
{
218+
const string inputCode =
219+
@"'@Folder(""First.Second.Third"")
220+
221+
Sub Foo()
222+
Dim d As Boolean
223+
d = True
224+
End Sub";
225+
226+
using (var explorer = new MockedCodeExplorer(ProjectType.HostProject, new[] { ComponentType.StandardModule }, new[] { inputCode })
227+
.SelectFirstCustomFolder())
228+
{
229+
var project = explorer.ViewModel.Projects.OfType<CodeExplorerProjectViewModel>().First();
230+
var folder = (CodeExplorerCustomFolderViewModel)explorer.ViewModel.SelectedItem;
231+
var subfolder = folder.Children.OfType<CodeExplorerCustomFolderViewModel>().Single()
232+
.Children.OfType<CodeExplorerCustomFolderViewModel>().Single();
233+
var declarations = project.State.AllUserDeclarations.ToList();
234+
declarations.Add(GetNewClassDeclaration(project.Declaration, "Foo", "\"First.Second.Third\""));
235+
236+
project.Synchronize(declarations);
237+
238+
var added = subfolder.Children.OfType<CodeExplorerComponentViewModel>()
239+
.SingleOrDefault(node => node.Declaration.DeclarationType == DeclarationType.ClassModule);
240+
241+
Assert.IsNotNull(added);
242+
Assert.AreEqual("\"First.Second.Third\"", added.Declaration.CustomFolder);
243+
}
244+
}
245+
57246
[Category("Code Explorer")]
58247
[Test]
59248
public void SubFolderModuleIsChildOfDeepestSubFolder()
@@ -167,5 +356,22 @@ public void FoldersNamesAreCaseSensitive()
167356
Assert.AreEqual(3, project.Children.Count);
168357
}
169358
}
359+
360+
private static Declaration GetNewClassDeclaration(Declaration project, string name, string folder = "")
361+
{
362+
var annotations = string.IsNullOrEmpty(folder)
363+
? Enumerable.Empty<IAnnotation>()
364+
: new[] { new FolderAnnotation(new QualifiedSelection(project.QualifiedModuleName, new Selection(1, 1)), null, new[] { folder }) };
365+
366+
return GetNewClassDeclaration(project, name, annotations);
367+
}
368+
369+
private static Declaration GetNewClassDeclaration(Declaration project, string name, IEnumerable<IAnnotation> annotations)
370+
{
371+
var declaration =
372+
new ClassModuleDeclaration(new QualifiedMemberName(project.QualifiedModuleName, name), project, name, true, annotations, new Attributes());
373+
374+
return declaration;
375+
}
170376
}
171377
}

0 commit comments

Comments
 (0)