8
8
using NLog ;
9
9
using Rubberduck . Parsing . Common ;
10
10
using Rubberduck . Parsing . ComReflection ;
11
+ using Rubberduck . Parsing . Symbols ;
11
12
using Rubberduck . Parsing . VBA . Extensions ;
12
13
using Rubberduck . VBEditor ;
13
14
using Rubberduck . VBEditor . ComManagement ;
@@ -23,7 +24,8 @@ public abstract class COMReferenceSynchronizerBase : ICOMReferenceSynchronizer,
23
24
private readonly IProjectsProvider _projectsProvider ;
24
25
private readonly IReferencedDeclarationsCollector _referencedDeclarationsCollector ;
25
26
26
- private readonly List < QualifiedModuleName > _unloadedCOMReferences ;
27
+ private readonly List < string > _unloadedCOMReferences = new List < string > ( ) ;
28
+ private readonly List < ( string projectId , string referencedProjectId ) > _referencesAffectedByPriorityChanges = new List < ( string projectId , string referencedProjectId ) > ( ) ;
27
29
28
30
private readonly Dictionary < ( string identifierName , string fullPath ) , string > _projectIdsByFilePathAndProjectName = new Dictionary < ( string identifierName , string fullPath ) , string > ( ) ;
29
31
@@ -53,15 +55,15 @@ protected COMReferenceSynchronizerBase(RubberduckParserState state, IParserState
53
55
_parserStateManager = parserStateManager ;
54
56
_projectsProvider = projectsProvider ;
55
57
_referencedDeclarationsCollector = referencedDeclarationsCollector ;
56
- _unloadedCOMReferences = new List < QualifiedModuleName > ( ) ;
57
58
}
58
59
59
60
60
61
public bool LastSyncOfCOMReferencesLoadedReferences { get ; private set ; }
61
- public IEnumerable < QualifiedModuleName > COMReferencesUnloadedUnloadedInLastSync => _unloadedCOMReferences ;
62
+ public IEnumerable < string > COMReferencesUnloadedInLastSync => _unloadedCOMReferences ;
63
+ public IEnumerable < ( string projectId , string referencedProjectId ) > COMReferencesAffectedByPriorityChangesInLastSync => _referencesAffectedByPriorityChanges ;
62
64
63
- private readonly HashSet < ReferencePriorityMap > _projectReferences = new HashSet < ReferencePriorityMap > ( ) ;
64
- public IReadOnlyCollection < ReferencePriorityMap > ProjectReferences => _projectReferences . AsReadOnly ( ) ;
65
+ private readonly IDictionary < string , ReferencePriorityMap > _projectReferences = new Dictionary < string , ReferencePriorityMap > ( ) ;
66
+ public IReadOnlyCollection < ReferencePriorityMap > ProjectReferences => _projectReferences . Values . AsReadOnly ( ) ;
65
67
66
68
67
69
protected abstract void LoadReferences ( IEnumerable < ReferenceInfo > referencesToLoad , ConcurrentBag < ReferenceInfo > unmapped , CancellationToken token ) ;
@@ -71,35 +73,133 @@ public void SyncComReferences(CancellationToken token)
71
73
{
72
74
var parsingStageTimer = ParsingStageTimer . StartNew ( ) ;
73
75
76
+ var oldProjectReferences = _projectReferences . ToDictionary ( ) ;
77
+ _projectReferences . Clear ( ) ;
78
+
74
79
LastSyncOfCOMReferencesLoadedReferences = false ;
75
80
_unloadedCOMReferences . Clear ( ) ;
81
+ _referencesAffectedByPriorityChanges . Clear ( ) ;
76
82
RefreshReferencedByProjectId ( ) ;
77
83
78
84
var unmapped = new ConcurrentBag < ReferenceInfo > ( ) ;
79
85
80
86
var referencesByProjectId = ReferencedByProjectId ( ) ;
81
- var referencesToLoad = GetReferencesToLoadAndSaveReferencePriority ( referencesByProjectId ) ;
87
+ var referencesToLoad = GetReferencesToLoadAndSaveReferencePriority ( referencesByProjectId , oldProjectReferences ) ;
82
88
83
89
if ( referencesToLoad . Any ( ) )
84
90
{
85
91
LastSyncOfCOMReferencesLoadedReferences = true ;
86
92
LoadReferences ( referencesToLoad , unmapped , token ) ;
87
93
}
88
94
89
- var allReferences = referencesByProjectId . Values . SelectMany ( references => references ) . ToHashSet ( ) ;
90
- var notMappedReferences = NonMappedReferences ( allReferences ) ;
91
- foreach ( var item in notMappedReferences )
95
+ DetermineReferencesAffectedByPriorityChanges ( _projectReferences , oldProjectReferences ) ;
96
+ UnloadNoLongerExistingReferences ( _projectReferences , oldProjectReferences ) ;
97
+
98
+ parsingStageTimer . Stop ( ) ;
99
+ parsingStageTimer . Log ( "Loaded and unloaded referenced libraries in {0}ms." ) ;
100
+ }
101
+
102
+ private void UnloadNoLongerExistingReferences ( IDictionary < string , ReferencePriorityMap > newProjectReferences , IDictionary < string , ReferencePriorityMap > oldProjectReferences )
103
+ {
104
+ var noLongerReferencedProjectIds = oldProjectReferences . Keys
105
+ . Where ( projectId => ! newProjectReferences . ContainsKey ( projectId ) )
106
+ . ToList ( ) ;
107
+
108
+ foreach ( var referencedProjectId in noLongerReferencedProjectIds )
92
109
{
93
- unmapped . Add ( item ) ;
110
+ UnloadComReference ( referencedProjectId ) ;
94
111
}
112
+ }
95
113
96
- foreach ( var reference in unmapped )
114
+ private void DetermineReferencesAffectedByPriorityChanges ( IDictionary < string , ReferencePriorityMap > newProjectReferences , IDictionary < string , ReferencePriorityMap > oldProjectReferences )
115
+ {
116
+ var referencePriorityChanges = ReferencePriorityChanges ( newProjectReferences , oldProjectReferences ) ;
117
+
118
+ foreach ( var oldMap in oldProjectReferences . Values )
97
119
{
98
- UnloadComReference ( reference ) ;
120
+ foreach ( var projectId in referencePriorityChanges . Keys )
121
+ {
122
+ if ( oldMap . TryGetValue ( projectId , out var priority )
123
+ && referencePriorityChanges [ projectId ]
124
+ . Any ( tpl => ( tpl . oldPriority <= priority && tpl . newPriority >= priority )
125
+ || ( tpl . oldPriority >= priority && tpl . newPriority <= priority ) ) )
126
+ {
127
+ _referencesAffectedByPriorityChanges . Add ( ( projectId , oldMap . ReferencedProjectId ) ) ;
128
+ }
129
+ }
99
130
}
131
+ }
100
132
101
- parsingStageTimer . Stop ( ) ;
102
- parsingStageTimer . Log ( "Loaded and unloaded referenced libraries in {0}ms." ) ;
133
+ private static IDictionary < string , List < ( int newPriority , int oldPriority ) > > ReferencePriorityChanges ( IDictionary < string , ReferencePriorityMap > newProjectReferences , IDictionary < string , ReferencePriorityMap > oldProjectReferences )
134
+ {
135
+ var referencePriorityChanges = new Dictionary < string , List < ( int newPriority , int oldPriority ) > > ( ) ;
136
+ foreach ( var referencedProjectId in oldProjectReferences . Keys )
137
+ {
138
+ var oldMap = oldProjectReferences [ referencedProjectId ] ;
139
+ if ( ! newProjectReferences . TryGetValue ( referencedProjectId , out var newMap ) )
140
+ {
141
+ foreach ( var projectId in oldMap . Keys )
142
+ {
143
+ AddPriorityChangeToDictionary ( projectId , int . MaxValue , oldMap [ projectId ] , referencePriorityChanges ) ;
144
+ }
145
+ }
146
+ else
147
+ {
148
+ foreach ( var projectId in oldMap . Keys )
149
+ {
150
+ var oldPriority = oldMap [ projectId ] ;
151
+ if ( ! newMap . TryGetValue ( referencedProjectId , out var newPriority ) )
152
+ {
153
+ newPriority = int . MaxValue ;
154
+ }
155
+
156
+ if ( newPriority != oldPriority )
157
+ {
158
+ AddPriorityChangeToDictionary ( projectId , newPriority , oldMap [ projectId ] , referencePriorityChanges ) ;
159
+ }
160
+ }
161
+
162
+ foreach ( var projectId in newMap . Keys )
163
+ {
164
+ if ( ! oldMap . ContainsKey ( projectId ) )
165
+ {
166
+ AddPriorityChangeToDictionary ( projectId , newMap [ projectId ] , int . MaxValue , referencePriorityChanges ) ;
167
+ }
168
+ }
169
+ }
170
+ }
171
+
172
+ foreach ( var referencedProjectId in newProjectReferences . Keys )
173
+ {
174
+ if ( oldProjectReferences . ContainsKey ( referencedProjectId ) )
175
+ {
176
+ continue ;
177
+ }
178
+
179
+ var newMap = newProjectReferences [ referencedProjectId ] ;
180
+ foreach ( var projectId in newMap . Keys )
181
+ {
182
+ AddPriorityChangeToDictionary ( projectId , newMap [ projectId ] , int . MaxValue , referencePriorityChanges ) ;
183
+ }
184
+ }
185
+
186
+ return referencePriorityChanges ;
187
+ }
188
+
189
+ private static void AddPriorityChangeToDictionary (
190
+ string projectId ,
191
+ int newPriority ,
192
+ int oldPriority ,
193
+ IDictionary < string , List < ( int newPriority , int oldPriority ) > > dict )
194
+ {
195
+ if ( dict . TryGetValue ( projectId , out var changeList ) )
196
+ {
197
+ changeList . Add ( ( newPriority , oldPriority ) ) ;
198
+ }
199
+ else
200
+ {
201
+ dict . Add ( projectId , new List < ( int newPriority , int oldPriority ) > { ( newPriority , oldPriority ) } ) ;
202
+ }
103
203
}
104
204
105
205
private Dictionary < string , List < ReferenceInfo > > ReferencedByProjectId ( )
@@ -173,7 +273,7 @@ private static bool TryGetFullPath(IVBProject project, out string fullPath)
173
273
return true ;
174
274
}
175
275
176
- private ICollection < ReferenceInfo > GetReferencesToLoadAndSaveReferencePriority ( Dictionary < string , List < ReferenceInfo > > referencedByProjectId )
276
+ private ICollection < ReferenceInfo > GetReferencesToLoadAndSaveReferencePriority ( Dictionary < string , List < ReferenceInfo > > referencedByProjectId , IDictionary < string , ReferencePriorityMap > oldProjectReferences )
177
277
{
178
278
var referencesToLoad = new List < ReferenceInfo > ( ) ;
179
279
@@ -188,13 +288,15 @@ private ICollection<ReferenceInfo> GetReferencesToLoadAndSaveReferencePriority(D
188
288
// todo: figure out why Rubberduck.tlb *sometimes* throws
189
289
190
290
var referencedProjectId = GetReferenceProjectId ( reference ) ;
191
- var map = _projectReferences . FirstOrDefault ( item =>
192
- item . ReferencedProjectId == referencedProjectId ) ;
193
-
194
- if ( map == null )
291
+ if ( ! _projectReferences . TryGetValue ( referencedProjectId , out var map ) )
195
292
{
196
293
map = new ReferencePriorityMap ( referencedProjectId ) { { projectId , priority } } ;
197
- _projectReferences . Add ( map ) ;
294
+ _projectReferences . Add ( referencedProjectId , map ) ;
295
+
296
+ if ( oldProjectReferences . TryGetValue ( referencedProjectId , out var oldMap ) )
297
+ {
298
+ map . IsLoaded = oldMap . IsLoaded ;
299
+ }
198
300
}
199
301
else
200
302
{
@@ -252,43 +354,18 @@ protected void LoadReference(ReferenceInfo reference, ConcurrentBag<ReferenceInf
252
354
253
355
private IEnumerable < ReferenceInfo > NonMappedReferences ( ICollection < ReferenceInfo > references )
254
356
{
255
- var mappedIds = _projectReferences . Select ( item => item . ReferencedProjectId ) . ToHashSet ( ) ;
256
- return references . Where ( item => ! mappedIds . Contains ( GetReferenceProjectId ( item ) ) ) . ToList ( ) ;
357
+ return references . Where ( item => ! _projectReferences . ContainsKey ( GetReferenceProjectId ( item ) ) ) . ToList ( ) ;
257
358
}
258
359
259
- private void UnloadComReference ( ReferenceInfo reference )
360
+ private void UnloadComReference ( string referencedProjectId )
260
361
{
261
- var referencedProjectId = GetReferenceProjectId ( reference ) ;
262
-
263
- ReferencePriorityMap map = null ;
264
- try
265
- {
266
- map = _projectReferences . SingleOrDefault ( item => item . ReferencedProjectId == referencedProjectId ) ;
267
- }
268
- catch ( InvalidOperationException exception )
362
+ //There is nothing to unload for a user project.
363
+ if ( ! IsUserProjectProjectId ( referencedProjectId ) )
269
364
{
270
- //There are multiple maps with the same referencedProjectId. That should not happen. (ghost?).
271
- Logger . Error ( exception , "Failed To unload com reference with referencedProjectID {0} because RD stores multiple instances of it." , referencedProjectId ) ;
272
- return ;
273
- }
365
+ _unloadedCOMReferences . Add ( referencedProjectId ) ;
274
366
275
- if ( map == null || ! map . IsLoaded )
276
- {
277
- Logger . Warn ( "Tried to unload untracked project reference." ) ; //This shouldn't happen.
278
- return ;
279
- }
280
-
281
- map . Remove ( referencedProjectId ) ;
282
- if ( map . Count == 0 )
283
- {
284
- _projectReferences . Remove ( map ) ;
285
-
286
- //There is nothing to unload for a user project.
287
- if ( ! IsUserProjectProjectId ( referencedProjectId ) )
288
- {
289
- AddUnloadedReferenceToUnloadedReferences ( reference ) ;
290
- _state . RemoveBuiltInDeclarations ( reference ) ;
291
- }
367
+ var projectQMN = ProjectQMNFromBuildInProjectId ( referencedProjectId ) ;
368
+ _state . RemoveBuiltInDeclarations ( projectQMN ) ;
292
369
}
293
370
}
294
371
@@ -297,10 +374,13 @@ private bool IsUserProjectProjectId(string projectId)
297
374
return _projectIdsByFilePathAndProjectName . Values . Contains ( projectId ) ;
298
375
}
299
376
300
- private void AddUnloadedReferenceToUnloadedReferences ( ReferenceInfo reference )
377
+ private QualifiedModuleName ProjectQMNFromBuildInProjectId ( string projectId )
301
378
{
302
- var projectQMN = new QualifiedModuleName ( reference ) ;
303
- _unloadedCOMReferences . Add ( projectQMN ) ;
379
+ return _state . DeclarationFinder
380
+ . BuiltInDeclarations ( DeclarationType . Project )
381
+ . First ( declaration => declaration . ProjectId == projectId )
382
+ . QualifiedModuleName ;
383
+
304
384
}
305
385
}
306
386
}
0 commit comments