Skip to content

Commit af68499

Browse files
committed
Improve SC-style import commands around types for which we use workarounds for import
1 parent 6113d06 commit af68499

File tree

8 files changed

+346
-64
lines changed

8 files changed

+346
-64
lines changed

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

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using Rubberduck.Parsing.VBA;
88
using Rubberduck.Resources;
99
using Rubberduck.VBEditor.Events;
10+
using Rubberduck.VBEditor.Extensions;
1011
using Rubberduck.VBEditor.SafeComWrappers;
1112
using Rubberduck.VBEditor.SafeComWrappers.Abstract;
1213

@@ -41,9 +42,7 @@ public ImportCommand(
4142

4243
AddToCanExecuteEvaluation(SpecialEvaluateCanExecute);
4344

44-
ComponentTypeForExtension = vbe.Kind == VBEKind.Hosted
45-
? VBAComponentTypeForExtension
46-
: VB6ComponentTypeForExtension;
45+
ComponentTypeForExtension = ComponentTypeExtensions.ComponentTypeForExtension(_vbe.Kind);
4746

4847
_importableExtensions = ComponentTypeForExtension.Keys.ToList();
4948
_filterExtensions = string.Join("; ", _importableExtensions.Select(ext => $"*{ext}"));
@@ -150,8 +149,19 @@ protected virtual void ImportFiles(ICollection<string> filesToImport, IVBProject
150149
{
151150
foreach (var filename in filesToImport)
152151
{
153-
//We have to dispose the return value.
154-
using (components.Import(filename)) {}
152+
var fileExtension = Path.GetExtension(filename);
153+
if (fileExtension != null
154+
&& ComponentTypeForExtension.TryGetValue(fileExtension, out var componentType)
155+
&& componentType == ComponentType.Document)
156+
{
157+
//We have to dispose the return value.
158+
using (components.ImportSourceFile(filename)) { }
159+
}
160+
else
161+
{
162+
//We have to dispose the return value.
163+
using (components.Import(filename)) { }
164+
}
155165
}
156166
}
157167
}
@@ -181,25 +191,5 @@ protected override void OnExecute(object parameter)
181191
}
182192

183193
protected IDictionary<string, ComponentType> ComponentTypeForExtension { get; }
184-
185-
private static IDictionary<string, ComponentType> VBAComponentTypeForExtension = new Dictionary<string, ComponentType>
186-
{
187-
[".bas"] = ComponentType.StandardModule,
188-
[".cls"] = ComponentType.ClassModule,
189-
[".frm"] = ComponentType.UserForm
190-
//TODO: find out what ".doccls" corresponds to.
191-
//[".doccls"] = ???
192-
};
193-
194-
private static IDictionary<string, ComponentType> VB6ComponentTypeForExtension = new Dictionary<string, ComponentType>
195-
{
196-
[".bas"] = ComponentType.StandardModule,
197-
[".cls"] = ComponentType.ClassModule,
198-
[".frm"] = ComponentType.VBForm,
199-
//TODO: double check whether the guesses below are correct.
200-
[".ctl"] = ComponentType.UserControl,
201-
[".pag"] = ComponentType.PropPage,
202-
[".dob"] = ComponentType.DocObject,
203-
};
204194
}
205195
}

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System.Collections.Generic;
2+
using System.Linq;
23
using Rubberduck.Parsing.VBA;
34
using Rubberduck.VBEditor.Events;
5+
using Rubberduck.VBEditor.SafeComWrappers;
46
using Rubberduck.VBEditor.SafeComWrappers.Abstract;
57

68
namespace Rubberduck.UI.CodeExplorer.Commands
@@ -25,14 +27,15 @@ protected override void ImportFiles(ICollection<string> filesToImport, IVBProjec
2527

2628
private void RemoveReimportableComponents(IVBProject project)
2729
{
28-
var importableComponentTypes = ComponentTypeForExtension.Values;
30+
var reimportableComponentTypes = ComponentTypeForExtension.Values
31+
.Where(componentType => componentType != ComponentType.Document);
2932
using(var components = project.VBComponents)
3033
{
3134
foreach(var component in components)
3235
{
3336
using (component)
3437
{
35-
if (importableComponentTypes.Contains(component.Type))
38+
if (reimportableComponentTypes.Contains(component.Type))
3639
{
3740
components.Remove(component);
3841
}

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

Lines changed: 73 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
using System.Collections.Generic;
22
using System.IO;
33
using System.Linq;
4-
using System.Text;
54
using Rubberduck.Parsing.VBA;
65
using Rubberduck.Parsing.VBA.DeclarationCaching;
6+
using Rubberduck.Parsing.VBA.Extensions;
77
using Rubberduck.VBEditor;
88
using Rubberduck.VBEditor.Events;
99
using Rubberduck.VBEditor.ComManagement;
1010
using Rubberduck.VBEditor.Extensions;
11+
using Rubberduck.VBEditor.SafeComWrappers;
1112
using Rubberduck.VBEditor.SafeComWrappers.Abstract;
13+
using Rubberduck.VBEditor.Utility;
1214

1315
namespace Rubberduck.UI.CodeExplorer.Commands
1416
{
@@ -39,6 +41,19 @@ protected override void ImportFiles(ICollection<string> filesToImport, IVBProjec
3941

4042
var moduleNames = ModuleNames(filesToImport);
4143

44+
var formBinaryModuleNames = moduleNames
45+
.Where(kvp => ComponentTypeExtensions.FormBinaryExtension.Equals(Path.GetExtension(kvp.Key)))
46+
.Select(kvp => kvp.Value)
47+
.ToHashSet();
48+
49+
var formFilesWithoutBinaries = FormFilesWithoutBinaries(moduleNames, formBinaryModuleNames);
50+
51+
//We cannot import the the binary separately.
52+
foreach (var formBinaryModuleName in formBinaryModuleNames)
53+
{
54+
moduleNames.Remove(formBinaryModuleName);
55+
}
56+
4257
if (!ValuesAreUnique(moduleNames))
4358
{
4459
//TODO: report this to the user.
@@ -53,6 +68,39 @@ protected override void ImportFiles(ICollection<string> filesToImport, IVBProjec
5368
return;
5469
}
5570

71+
var documentFiles = moduleNames
72+
.Select(kvp => kvp.Key)
73+
.Where(filename => Path.GetExtension(filename) != null
74+
&& ComponentTypeForExtension.TryGetValue(Path.GetExtension(filename), out var componentType)
75+
&& componentType == ComponentType.Document)
76+
.ToHashSet();
77+
78+
//We can only insert inte existing documents.
79+
if (!documentFiles.All(filename => modules.ContainsKey(filename)))
80+
{
81+
//TODO: report this to the user.
82+
return;
83+
}
84+
85+
//We must not remove document modules.
86+
foreach (var filename in documentFiles)
87+
{
88+
modules.Remove(filename);
89+
}
90+
91+
//We import the standalone code behind by replacing the code in an existing form.
92+
//So, the form has to exist already.
93+
if (!formFilesWithoutBinaries.All(filename => modules.ContainsKey(filename)))
94+
{
95+
//TODO: report this to the user.
96+
return;
97+
}
98+
99+
foreach (var filename in formFilesWithoutBinaries)
100+
{
101+
modules.Remove(filename);
102+
}
103+
56104
using (var components = targetProject.VBComponents)
57105
{
58106
foreach (var filename in filesToImport)
@@ -63,8 +111,16 @@ protected override void ImportFiles(ICollection<string> filesToImport, IVBProjec
63111
components.Remove(component);
64112
}
65113

66-
//We have to dispose the return value.
67-
using (components.Import(filename)) { }
114+
if(documentFiles.Contains(filename) || formBinaryModuleNames.Contains(filename))
115+
{
116+
//We have to dispose the return value.
117+
using (components.ImportSourceFile(filename)) { }
118+
}
119+
else
120+
{
121+
//We have to dispose the return value.
122+
using (components.Import(filename)) { }
123+
}
68124
}
69125
}
70126
}
@@ -116,6 +172,17 @@ private bool ValuesAreUnique(Dictionary<string, string> moduleNames)
116172
.All(moduleNameGroup => moduleNameGroup.Count() == 1);
117173
}
118174

175+
private ICollection<string> FormFilesWithoutBinaries(IDictionary<string, string> moduleNames, ICollection<string> formBinaryModuleNames)
176+
{
177+
return moduleNames
178+
.Where(kvp => Path.GetExtension(kvp.Key) != null
179+
&& ComponentTypeForExtension.TryGetValue(Path.GetExtension(kvp.Key), out var componentType)
180+
&& componentType == ComponentType.UserForm
181+
&& !formBinaryModuleNames.Contains(kvp.Value))
182+
.Select(kvp => kvp.Key)
183+
.ToHashSet();
184+
}
185+
119186
private QualifiedModuleName? Module(string moduleName, string projectId, DeclarationFinder finder)
120187
{
121188
foreach(var module in finder.AllModules)
@@ -133,35 +200,9 @@ private bool ValuesAreUnique(Dictionary<string, string> moduleNames)
133200
private bool HasMatchingFileExtension(string filename, QualifiedModuleName module)
134201
{
135202
var fileExtension = Path.GetExtension(filename);
136-
return ComponentTypeForExtension.TryGetValue(fileExtension, out var componentType)
137-
? module.ComponentType.Equals(componentType)
138-
: false;
139-
}
140-
}
141-
142-
public interface IModuleNameFromFileExtractor
143-
{
144-
string ModuleName(string filename);
145-
}
146-
147-
public class ModuleNameFromFileExtractor : IModuleNameFromFileExtractor
148-
{
149-
public string ModuleName(string filename)
150-
{
151-
if (!File.Exists(filename))
152-
{
153-
return null;
154-
}
155-
156-
var contents = File.ReadLines(filename, Encoding.Default);
157-
var nameLine = contents.FirstOrDefault(line => line.StartsWith("Attribute VB_Name = "));
158-
if (nameLine == null)
159-
{
160-
return Path.GetFileNameWithoutExtension(filename);
161-
}
162-
163-
//The format is Attribute VB_Name = "ModuleName"
164-
return nameLine.Substring("Attribute VB_Name = ".Length + 1, nameLine.Length - "Attribute VB_Name = ".Length - 2);
203+
return fileExtension != null
204+
&& ComponentTypeForExtension.TryGetValue(fileExtension, out var componentType)
205+
&& module.ComponentType.Equals(componentType);
165206
}
166207
}
167208
}

Rubberduck.VBEEditor/Extensions/ComponentTypeExtensions.cs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Collections.Generic;
12
using Rubberduck.VBEditor.SafeComWrappers;
23

34
namespace Rubberduck.VBEditor.Extensions
@@ -10,12 +11,18 @@ public static class ComponentTypeExtensions
1011
public const string FormBinaryExtension = ".frx";
1112
public const string DocClassExtension = ".doccls";
1213

14+
//TODO: double check whether the guesses below are correct.
15+
public const string UserControlExtension = ".ctl";
16+
public const string PropertyPageExtension = ".pag";
17+
public const string DocObjectExtension = ".dob";
18+
1319
/// <summary>
1420
/// Returns the proper file extension for the Component Type.
1521
/// </summary>
1622
/// <remarks>Document classes should properly have a ".cls" file extension.
1723
/// However, because they cannot be removed and imported like other component types, we need to make a distinction.</remarks>
1824
/// <param name="componentType"></param>
25+
/// <param name="vbeKind"></param>
1926
/// <returns>File extension that includes a preceeding "dot" (.) </returns>
2027
public static string FileExtension(this ComponentType componentType)
2128
{
@@ -30,10 +37,40 @@ public static string FileExtension(this ComponentType componentType)
3037
case ComponentType.Document:
3138
// documents should technically be a ".cls", but we need to be able to tell them apart.
3239
return DocClassExtension;
33-
case ComponentType.ActiveXDesigner:
40+
case ComponentType.PropPage:
41+
return PropertyPageExtension;
42+
case ComponentType.UserControl:
43+
return UserControlExtension;
44+
case ComponentType.DocObject:
45+
return DocObjectExtension;
3446
default:
3547
return string.Empty;
3648
}
3749
}
50+
51+
public static IDictionary<string, ComponentType> ComponentTypeForExtension(VBEKind vbeKind)
52+
{
53+
return vbeKind == VBEKind.Hosted
54+
? VBAComponentTypeForExtension
55+
: VB6ComponentTypeForExtension;
56+
}
57+
58+
private static readonly IDictionary<string, ComponentType> VBAComponentTypeForExtension = new Dictionary<string, ComponentType>
59+
{
60+
[StandardExtension] = ComponentType.StandardModule,
61+
[ClassExtension] = ComponentType.ClassModule,
62+
[FormExtension] = ComponentType.UserForm,
63+
[DocClassExtension] = ComponentType.Document
64+
};
65+
66+
private static readonly IDictionary<string, ComponentType> VB6ComponentTypeForExtension = new Dictionary<string, ComponentType>
67+
{
68+
[StandardExtension] = ComponentType.StandardModule,
69+
[ClassExtension] = ComponentType.ClassModule,
70+
[FormExtension] = ComponentType.VBForm,
71+
[UserControlExtension] = ComponentType.UserControl,
72+
[PropertyPageExtension] = ComponentType.PropPage,
73+
[DocObjectExtension] = ComponentType.DocObject,
74+
};
3875
}
3976
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using System.IO;
2+
using System.Linq;
3+
using System.Text;
4+
using Rubberduck.VBEditor.Extensions;
5+
6+
namespace Rubberduck.VBEditor.Utility
7+
{
8+
public interface IModuleNameFromFileExtractor
9+
{
10+
string ModuleName(string filename);
11+
}
12+
13+
public class ModuleNameFromFileExtractor : IModuleNameFromFileExtractor
14+
{
15+
public string ModuleName(string filename)
16+
{
17+
if (!File.Exists(filename))
18+
{
19+
return null;
20+
}
21+
22+
//We cannot read binary files.
23+
if(ComponentTypeExtensions.FormBinaryExtension.Equals(Path.GetExtension(filename)))
24+
{
25+
return Path.GetFileNameWithoutExtension(filename);
26+
}
27+
28+
var contents = File.ReadLines(filename, Encoding.Default);
29+
var nameLine = contents.FirstOrDefault(line => line.StartsWith("Attribute VB_Name = "));
30+
if (nameLine == null)
31+
{
32+
return Path.GetFileNameWithoutExtension(filename);
33+
}
34+
35+
//The format is Attribute VB_Name = "ModuleName"
36+
return nameLine.Substring("Attribute VB_Name = ".Length + 1, nameLine.Length - "Attribute VB_Name = ".Length - 2);
37+
}
38+
}
39+
}

Rubberduck.VBEditor.VB6/SafeComWrappers/VB/VBComponents.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ public override int GetHashCode()
7272

7373
public IVBComponent ImportSourceFile(string path)
7474
{
75-
throw new NotSupportedException("ImportSourceFile not supported in VB6");
75+
//Since we have no special handling as in VBA, we just forward to Import.
76+
return Import(path);
7677
}
7778

7879
public void RemoveSafely(IVBComponent component)

0 commit comments

Comments
 (0)