Skip to content

Commit 7733e38

Browse files
authored
Merge pull request #4601 from comintern/refs
💯
2 parents c5be87b + 3943988 commit 7733e38

File tree

88 files changed

+6899
-2314
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

88 files changed

+6899
-2314
lines changed
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
using System;
2+
using System.ComponentModel;
3+
using System.IO;
4+
using System.Runtime.CompilerServices;
5+
using System.Runtime.InteropServices;
6+
using System.Runtime.InteropServices.ComTypes;
7+
using Rubberduck.Parsing.ComReflection;
8+
using Rubberduck.VBEditor;
9+
using Rubberduck.VBEditor.SafeComWrappers;
10+
using Rubberduck.VBEditor.SafeComWrappers.Abstract;
11+
using Rubberduck.VBEditor.Utility;
12+
13+
namespace Rubberduck.AddRemoveReferences
14+
{
15+
public class ReferenceModel : INotifyPropertyChanged
16+
{
17+
public event PropertyChangedEventHandler PropertyChanged;
18+
19+
private ReferenceModel()
20+
{
21+
_info = new Lazy<ReferenceInfo>(GenerateInfo);
22+
}
23+
24+
public ReferenceModel(ReferenceInfo info, ReferenceKind type, bool recent = false, bool pinned = false) : this()
25+
{
26+
Guid = info.Guid;
27+
Name = info.Name;
28+
Description = Name;
29+
FullPath = info.FullPath;
30+
Major = info.Major;
31+
Minor = info.Minor;
32+
IsRecent = recent;
33+
IsPinned = pinned;
34+
Type = type;
35+
}
36+
37+
public ReferenceModel(IVBProject project, int priority) : this()
38+
{
39+
Name = project.Name ?? string.Empty;
40+
Priority = priority;
41+
Guid = Guid.Empty;
42+
Description = project.Description ?? project.Name;
43+
FullPath = project.FileName ?? string.Empty;
44+
IsBuiltIn = false;
45+
Type = ReferenceKind.Project;
46+
}
47+
48+
public ReferenceModel(RegisteredLibraryInfo info) : this()
49+
{
50+
Name = info.Name ?? string.Empty;
51+
Guid = info.Guid;
52+
Description = string.IsNullOrEmpty(info.Description) ? Path.GetFileNameWithoutExtension(info.FullPath) : info.Description;
53+
Major = info.Major;
54+
Minor = info.Minor;
55+
FullPath = info.FullPath;
56+
LocaleName = info.LocaleName;
57+
IsBuiltIn = false;
58+
Type = ReferenceKind.TypeLibrary;
59+
Flags = (TypeLibTypeFlags)info.Flags;
60+
IsRegistered = true;
61+
}
62+
63+
public ReferenceModel(RegisteredLibraryInfo info, IReference reference, int priority) : this(info)
64+
{
65+
Priority = priority;
66+
IsBuiltIn = reference.IsBuiltIn;
67+
IsBroken = reference.IsBroken;
68+
IsReferenced = true;
69+
}
70+
71+
public ReferenceModel(IReference reference, int priority) : this()
72+
{
73+
Priority = priority;
74+
Name = reference.Name;
75+
Guid = Guid.TryParse(reference.Guid, out var guid) ? guid : Guid.Empty;
76+
Description = string.IsNullOrEmpty(reference.Description) ? Path.GetFileNameWithoutExtension(reference.FullPath) : reference.Description;
77+
Major = reference.Major;
78+
Minor = reference.Minor;
79+
FullPath = reference.FullPath;
80+
IsBuiltIn = reference.IsBuiltIn;
81+
IsBroken = reference.IsBroken;
82+
IsReferenced = true;
83+
Type = reference.Type;
84+
}
85+
86+
public ReferenceModel(string path, ITypeLib reference, IComLibraryProvider provider) : this()
87+
{
88+
FullPath = path;
89+
90+
var documentation = provider.GetComDocumentation(reference);
91+
Name = documentation.Name;
92+
Description = documentation.DocString;
93+
94+
var info = provider.GetReferenceInfo(reference, Name, path);
95+
Guid = info.Guid;
96+
Major = info.Major;
97+
Minor = info.Minor;
98+
}
99+
100+
public ReferenceModel(string path, bool broken = false) : this()
101+
{
102+
FullPath = path;
103+
try
104+
{
105+
Name = Path.GetFileName(path) ?? path;
106+
Description = Name;
107+
}
108+
catch
109+
{
110+
// Yeah, that's probably busted.
111+
IsBroken = true;
112+
return;
113+
}
114+
115+
IsBroken = broken;
116+
}
117+
118+
private bool _pinned;
119+
public bool IsPinned
120+
{
121+
get => _pinned;
122+
set
123+
{
124+
_pinned = value;
125+
NotifyPropertyChanged();
126+
}
127+
}
128+
129+
public bool IsRecent { get; set; }
130+
public bool IsRegistered { get; set; }
131+
public bool IsReferenced { get; set; }
132+
133+
public int? Priority { get; set; }
134+
135+
public string Name { get; } = string.Empty;
136+
public Guid Guid { get; }
137+
public string Description { get; } = string.Empty;
138+
public string FullPath { get; }
139+
public string LocaleName { get; } = string.Empty;
140+
141+
public bool IsBuiltIn { get; set; }
142+
public bool IsBroken { get; }
143+
public TypeLibTypeFlags Flags { get; set; }
144+
public ReferenceKind Type { get; }
145+
146+
private string FullPath32 { get; } = string.Empty;
147+
private string FullPath64 { get; } = string.Empty;
148+
public int Major { get; set; }
149+
public int Minor { get; set; }
150+
public string Version => $"{Major}.{Minor}";
151+
152+
public ReferenceStatus Status
153+
{
154+
get
155+
{
156+
var status = IsPinned ? ReferenceStatus.Pinned : ReferenceStatus.None;
157+
if (!Priority.HasValue)
158+
{
159+
return IsRecent ? status | ReferenceStatus.Recent : status;
160+
}
161+
162+
if (IsBroken)
163+
{
164+
return status | ReferenceStatus.Broken;
165+
}
166+
167+
if (IsBuiltIn)
168+
{
169+
return status | ReferenceStatus.BuiltIn;
170+
}
171+
172+
return status | (IsReferenced ? ReferenceStatus.Loaded : ReferenceStatus.Added);
173+
}
174+
}
175+
176+
private readonly Lazy<ReferenceInfo> _info;
177+
private ReferenceInfo GenerateInfo() => new ReferenceInfo(Guid, Name, FullPath, Major, Minor);
178+
public ReferenceInfo ToReferenceInfo() => _info.Value;
179+
180+
public bool Matches(ReferenceInfo info)
181+
{
182+
return Major == info.Major && Minor == info.Minor &&
183+
FullPath.Equals(info.FullPath, StringComparison.OrdinalIgnoreCase) ||
184+
FullPath32.Equals(info.FullPath, StringComparison.OrdinalIgnoreCase) ||
185+
FullPath64.Equals(info.FullPath, StringComparison.OrdinalIgnoreCase) ||
186+
Guid.Equals(info.Guid);
187+
}
188+
189+
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
190+
{
191+
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
192+
}
193+
}
194+
}
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
using System.Collections.Generic;
2+
using System.IO;
3+
using System.Linq;
4+
using System.Runtime.InteropServices;
5+
using Rubberduck.Interaction;
6+
using Rubberduck.Parsing.ComReflection;
7+
using Rubberduck.Resources;
8+
using Rubberduck.Settings;
9+
using Rubberduck.SettingsProvider;
10+
using Rubberduck.UI.AddRemoveReferences;
11+
using Rubberduck.VBEditor.SafeComWrappers.Abstract;
12+
13+
namespace Rubberduck.AddRemoveReferences
14+
{
15+
public interface IReferenceReconciler
16+
{
17+
List<ReferenceModel> ReconcileReferences(IAddRemoveReferencesModel model);
18+
List<ReferenceModel> ReconcileReferences(IAddRemoveReferencesModel model, List<ReferenceModel> allReferences);
19+
ReferenceModel TryAddReference(IVBProject project, string path);
20+
ReferenceModel TryAddReference(IVBProject project, ReferenceModel reference);
21+
ReferenceModel GetLibraryInfoFromPath(string path);
22+
void UpdateSettings(IAddRemoveReferencesModel model, bool recent = false);
23+
}
24+
25+
public class ReferenceReconciler : IReferenceReconciler
26+
{
27+
public static readonly List<string> TypeLibraryExtensions = new List<string> { ".olb", ".tlb", ".dll", ".ocx", ".exe" };
28+
29+
private readonly IMessageBox _messageBox;
30+
private readonly IConfigProvider<ReferenceSettings> _settings;
31+
private readonly IComLibraryProvider _libraryProvider;
32+
33+
public ReferenceReconciler(
34+
IMessageBox messageBox,
35+
IConfigProvider<ReferenceSettings> settings,
36+
IComLibraryProvider libraryProvider)
37+
{
38+
_messageBox = messageBox;
39+
_settings = settings;
40+
_libraryProvider = libraryProvider;
41+
}
42+
43+
public List<ReferenceModel> ReconcileReferences(IAddRemoveReferencesModel model)
44+
{
45+
if (model?.NewReferences is null || !model.NewReferences.Any())
46+
{
47+
return new List<ReferenceModel>();
48+
}
49+
50+
return ReconcileReferences(model, model.NewReferences.ToList());
51+
}
52+
53+
//TODO test for simple adds.
54+
public List<ReferenceModel> ReconcileReferences(IAddRemoveReferencesModel model, List<ReferenceModel> allReferences)
55+
{
56+
if (model is null || allReferences is null || !allReferences.Any())
57+
{
58+
return new List<ReferenceModel>();
59+
}
60+
61+
var selected = allReferences.Where(reference => !reference.IsBuiltIn && reference.Priority.HasValue)
62+
.ToDictionary(reference => reference.FullPath);
63+
64+
var output = selected.Values.Where(reference => reference.IsBuiltIn).ToList();
65+
66+
var project = model.Project.Project;
67+
using (var references = project.References)
68+
{
69+
foreach (var reference in references)
70+
{
71+
try
72+
{
73+
if (!reference.IsBuiltIn)
74+
{
75+
references.Remove(reference);
76+
}
77+
}
78+
finally
79+
{
80+
reference.Dispose();
81+
}
82+
}
83+
output.AddRange(selected.Values.OrderBy(selection => selection.Priority)
84+
.Select(reference => TryAddReference(project, reference)).Where(added => added != null));
85+
}
86+
87+
UpdateSettings(model, true);
88+
return output;
89+
}
90+
91+
public ReferenceModel GetLibraryInfoFromPath(string path)
92+
{
93+
try
94+
{
95+
var extension = Path.GetExtension(path)?.ToLowerInvariant() ?? string.Empty;
96+
if (string.IsNullOrEmpty(extension))
97+
{
98+
return null;
99+
}
100+
101+
// LoadTypeLibrary will attempt to open files in the host, so only attempt on possible COM servers.
102+
if (TypeLibraryExtensions.Contains(extension))
103+
{
104+
var type = _libraryProvider.LoadTypeLibrary(path);
105+
return new ReferenceModel(path, type, _libraryProvider);
106+
}
107+
return new ReferenceModel(path);
108+
}
109+
catch
110+
{
111+
// Most likely this is unloadable. If not, it we can't fail here because it could have come from the Apply
112+
// button in the AddRemoveReferencesDialog. Wait for it... :-P
113+
return new ReferenceModel(path, true);
114+
}
115+
}
116+
117+
public ReferenceModel TryAddReference(IVBProject project, string path)
118+
{
119+
using (var references = project.References)
120+
{
121+
try
122+
{
123+
using (var reference = references.AddFromFile(path))
124+
{
125+
return reference is null ? null : new ReferenceModel(reference, references.Count) { IsRecent = true };
126+
}
127+
}
128+
catch (COMException ex)
129+
{
130+
_messageBox.NotifyWarn(ex.Message, RubberduckUI.References_AddFailedCaption);
131+
}
132+
return null;
133+
}
134+
}
135+
136+
public ReferenceModel TryAddReference(IVBProject project, ReferenceModel reference)
137+
{
138+
using (var references = project.References)
139+
{
140+
try
141+
{
142+
using (references.AddFromFile(reference.FullPath))
143+
{
144+
reference.Priority = references.Count;
145+
reference.IsRecent = true;
146+
return reference;
147+
}
148+
}
149+
catch (COMException ex)
150+
{
151+
_messageBox.NotifyWarn(ex.Message, RubberduckUI.References_AddFailedCaption);
152+
}
153+
return null;
154+
}
155+
}
156+
157+
public void UpdateSettings(IAddRemoveReferencesModel model, bool recent = false)
158+
{
159+
if (model?.Settings is null || model.References is null)
160+
{
161+
return;
162+
}
163+
164+
if (recent)
165+
{
166+
model.Settings.UpdateRecentReferencesForHost(model.HostApplication,
167+
model.References.Where(reference => reference.IsReferenced && !reference.IsBuiltIn)
168+
.Select(reference => reference.ToReferenceInfo()).ToList());
169+
170+
}
171+
172+
model.Settings.UpdatePinnedReferencesForHost(model.HostApplication,
173+
model.References.Where(reference => reference.IsPinned).Select(reference => reference.ToReferenceInfo())
174+
.ToList());
175+
176+
_settings.Save(model.Settings);
177+
}
178+
}
179+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System;
2+
3+
namespace Rubberduck.AddRemoveReferences
4+
{
5+
[Flags]
6+
public enum ReferenceStatus
7+
{
8+
None = 0,
9+
BuiltIn = 1 << 1,
10+
Loaded = 1 << 2,
11+
Broken = 1 << 3,
12+
Pinned = 1 << 4,
13+
Recent = 1 << 5,
14+
Added = 1 << 6
15+
}
16+
}

0 commit comments

Comments
 (0)