Skip to content

Commit f62c7a0

Browse files
authored
Merge pull request #3117 from MDoerner/Fix3115
Cache Invalidation for Projects Outside of the Sinks
2 parents 8a33837 + 0342e5c commit f62c7a0

File tree

4 files changed

+61
-48
lines changed

4 files changed

+61
-48
lines changed

RetailCoder.VBE/Navigation/CodeExplorer/CodeExplorerViewModel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ private void HandleStateChanged(object sender, ParserStateEventArgs e)
254254
return;
255255
}
256256

257-
var userDeclarations = _state.AllUserDeclarations
257+
var userDeclarations = _state.DeclarationFinder.AllUserDeclarations
258258
.GroupBy(declaration => declaration.ProjectId)
259259
.ToList();
260260

Rubberduck.Parsing/Symbols/DeclarationFinder.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,8 @@ public IEnumerable<Declaration> UserDeclarations(DeclarationType type)
376376
.SelectMany(item => item.Value);
377377
}
378378

379+
public IEnumerable<Declaration> AllUserDeclarations => _userDeclarationsByType.AllValues();
380+
379381
public IEnumerable<Declaration> BuiltInDeclarations(DeclarationType type)
380382
{
381383
List<Declaration> result;
@@ -386,6 +388,8 @@ public IEnumerable<Declaration> BuiltInDeclarations(DeclarationType type)
386388
.SelectMany(item => item.Value);
387389
}
388390

391+
public IEnumerable<Declaration> AllBuiltInDeclarations => _builtInDeclarationsByType.Value.AllValues();
392+
389393
public IEnumerable<Declaration> DeclarationsWithType(DeclarationType type)
390394
{
391395
return BuiltInDeclarations(type).Concat(UserDeclarations(type));

Rubberduck.Parsing/VBA/ParseCoordinator.cs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Diagnostics;
88
using System.Linq;
99
using NLog;
10+
using Rubberduck.VBEditor.SafeComWrappers.Abstract;
1011

1112
namespace Rubberduck.Parsing.VBA
1213
{
@@ -241,14 +242,19 @@ private void ExecuteCommonParseActivities(IReadOnlyCollection<QualifiedModuleNam
241242
token.ThrowIfCancellationRequested();
242243

243244
_parsingStageService.ResolveDeclarations(toParse, token);
244-
RefreshDeclarationFinder();
245245
}
246246

247247
if (token.IsCancellationRequested || State.Status >= ParserState.Error)
248248
{
249249
throw new OperationCanceledException(token);
250250
}
251251

252+
//We need to refresh the DeclarationFinder before the handlers for ResolvedDeclarations run no matter
253+
//whether we parsed or resolved something because modules not referenced by any remeining module might
254+
//have been removed. E.g. the CodeExplorer needs this update.
255+
RefreshDeclarationFinder();
256+
token.ThrowIfCancellationRequested();
257+
252258
//Explicitly setting the overall state here guarantees that the handlers attached
253259
//to the state change to ResolvedDeclarations always run, provided there is no error.
254260
State.SetStatusAndFireStateChanged(this, ParserState.ResolvedDeclarations);
@@ -361,12 +367,19 @@ private void ParseAllInternal(object requestor, CancellationToken token)
361367
var removedModules = RemovedModules(modules);
362368
token.ThrowIfCancellationRequested();
363369

370+
var removedProjects = RemovedProjects(_projectManager.Projects);
371+
token.ThrowIfCancellationRequested();
372+
364373
var toReResolveReferences = _parsingCacheService.ModulesReferencingAny(removedModules);
365374
token.ThrowIfCancellationRequested();
366375

367376
CleanUpRemovedComponents(removedModules, token);
368377
token.ThrowIfCancellationRequested();
369378

379+
//This must come after the component cleanup because of cache invalidation.
380+
CleanUpRemovedProjects(removedProjects);
381+
token.ThrowIfCancellationRequested();
382+
370383
var toParse = modules.Where(module => State.IsNewOrModified(module)).ToHashSet();
371384
token.ThrowIfCancellationRequested();
372385

@@ -395,6 +408,20 @@ private void ClearStateCache(IEnumerable<QualifiedModuleName> modules)
395408
}
396409
}
397410

411+
private void CleanUpRemovedProjects(IReadOnlyCollection<Tuple<string,string>> removedProjects)
412+
{
413+
var removedProjectIds = removedProjects.Select(removedProject => removedProject.Item1);
414+
ClearStateCache(removedProjectIds);
415+
}
416+
417+
private void ClearStateCache(IEnumerable<string> projectIds)
418+
{
419+
foreach (var projectId in projectIds)
420+
{
421+
State.ClearStateCache(projectId);
422+
}
423+
}
424+
398425
private IReadOnlyCollection<QualifiedModuleName> RemovedModules(IReadOnlyCollection<QualifiedModuleName> modules)
399426
{
400427
var modulesWithModuleDeclarations = State.DeclarationFinder.UserDeclarations(DeclarationType.Module).Select(declaration => declaration.QualifiedName.QualifiedModuleName);
@@ -403,6 +430,14 @@ private IReadOnlyCollection<QualifiedModuleName> RemovedModules(IReadOnlyCollect
403430
return removedModuledecalrations.ToHashSet().AsReadOnly();
404431
}
405432

433+
private IReadOnlyCollection<Tuple<string,string>> RemovedProjects(IReadOnlyCollection<IVBProject> projects)
434+
{
435+
var projectsWithProjectDeclarations = State.DeclarationFinder.UserDeclarations(DeclarationType.Project).Select(declaration => new Tuple<string,string>(declaration.ProjectId, declaration.ProjectName));
436+
var currentlyExistingProjects = projects.Select(project => new Tuple<string, string>(project.ProjectId, project.Name)).ToHashSet();
437+
var removedProjects = projectsWithProjectDeclarations.Where(project => !currentlyExistingProjects.Contains(project));
438+
return removedProjects.ToHashSet().AsReadOnly();
439+
}
440+
406441

407442
public void Dispose()
408443
{

Rubberduck.Parsing/VBA/RubberduckParserState.cs

Lines changed: 20 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,6 @@ private void Sinks_ProjectAdded(object sender, ProjectEventArgs e)
140140
if (!e.Project.VBE.IsInDesignMode) { return; }
141141

142142
Logger.Debug("Project '{0}' was added.", e.ProjectId);
143-
RefreshProjects(_vbe); // note side-effect: assigns ProjectId/HelpFile
144143
OnParseRequested(sender);
145144
}
146145

@@ -149,7 +148,6 @@ private void Sinks_ProjectRemoved(object sender, ProjectEventArgs e)
149148
if (!e.Project.VBE.IsInDesignMode) { return; }
150149

151150
Debug.Assert(e.ProjectId != null);
152-
RemoveProject(e.ProjectId, true);
153151
OnParseRequested(sender);
154152
}
155153

@@ -164,9 +162,6 @@ private void Sinks_ProjectRenamed(object sender, ProjectRenamedEventArgs e)
164162

165163
Logger.Debug("Project {0} was renamed.", e.ProjectId);
166164

167-
RemoveProject(e.ProjectId);
168-
RefreshProjects(e.Project.VBE);
169-
170165
OnParseRequested(sender);
171166
}
172167

@@ -207,14 +202,26 @@ private void Sinks_ComponentRenamed(object sender, ComponentRenamedEventArgs e)
207202

208203
Logger.Debug("Component '{0}' was renamed to '{1}'.", e.OldName, e.Component.Name);
209204

205+
//todo: Find out for which situation this drastic (and problematic) cache invalidation has been introduced.
206+
if (ComponentIsWorksheet(e))
207+
{
208+
RemoveProject(e.ProjectId);
209+
Logger.Debug("Project '{0}' was removed.", e.Component.Name);
210+
}
211+
212+
OnParseRequested(sender);
213+
}
214+
215+
private bool ComponentIsWorksheet(ComponentRenamedEventArgs e)
216+
{
210217
var componentIsWorksheet = false;
211218
foreach (var declaration in AllUserDeclarations)
212219
{
213220
if (declaration.ProjectId == e.ProjectId &&
214221
declaration.DeclarationType == DeclarationType.ClassModule &&
215222
declaration.IdentifierName == e.OldName)
216223
{
217-
foreach (var superType in ((ClassModuleDeclaration) declaration).Supertypes)
224+
foreach (var superType in ((ClassModuleDeclaration)declaration).Supertypes)
218225
{
219226
if (superType.IdentifierName == "Worksheet")
220227
{
@@ -227,19 +234,7 @@ private void Sinks_ComponentRenamed(object sender, ComponentRenamedEventArgs e)
227234
}
228235
}
229236

230-
if (componentIsWorksheet)
231-
{
232-
RemoveProject(e.ProjectId);
233-
Logger.Debug("Project '{0}' was removed.", e.Component.Name);
234-
235-
RefreshProjects(e.Project.VBE);
236-
}
237-
else
238-
{
239-
RemoveRenamedComponent(e.ProjectId, e.OldName);
240-
}
241-
242-
OnParseRequested(sender);
237+
return componentIsWorksheet;
243238
}
244239

245240
public void OnStatusMessageUpdate(string message)
@@ -765,20 +760,20 @@ public void AddUnresolvedMemberDeclaration(UnboundMemberDeclaration declaration)
765760
}
766761
}
767762

768-
private void ClearStateCache(string projectId, bool notifyStateChanged = false)
763+
public void ClearStateCache(string projectId, bool notifyStateChanged = false)
769764
{
770765
try
771766
{
772-
foreach (var moduleState in _moduleStates)
767+
foreach (var moduleState in _moduleStates.Where(moduleState => moduleState.Key.ProjectId == projectId))
773768
{
774-
if (moduleState.Key.ProjectId == projectId && moduleState.Key.Component != null)
769+
if (moduleState.Key.Component != null)
775770
{
776771
while (!ClearStateCache(moduleState.Key.Component))
777772
{
778773
// until Hell freezes over?
779774
}
780775
}
781-
else if (moduleState.Key.ProjectId == projectId && moduleState.Key.Component == null)
776+
else if (moduleState.Key.Component == null)
782777
{
783778
// store project module name
784779
var qualifiedModuleName = moduleState.Key;
@@ -790,8 +785,9 @@ private void ClearStateCache(string projectId, bool notifyStateChanged = false)
790785
}
791786
}
792787
}
793-
catch (COMException)
788+
catch (COMException exception)
794789
{
790+
Logger.Error(exception, $"Unexpected COMException while clearing the project with projectId {projectId}. Clearing all modules.");
795791
_moduleStates.Clear();
796792
}
797793

@@ -849,28 +845,6 @@ public bool ClearStateCache(QualifiedModuleName module, bool notifyStateChanged
849845
return success;
850846
}
851847

852-
private bool RemoveRenamedComponent(string projectId, string oldComponentName)
853-
{
854-
var keys = new List<QualifiedModuleName>();
855-
foreach (var key in _moduleStates.Keys)
856-
{
857-
if (key.ComponentName == oldComponentName && key.ProjectId == projectId)
858-
{
859-
keys.Add(key);
860-
}
861-
}
862-
863-
var success = keys.Count != 0 && RemoveKeysFromCollections(keys);
864-
865-
if (success)
866-
{
867-
OnStateChanged(this, ParserState.ResolvedDeclarations); // trigger test explorer and code explorer updates
868-
OnStateChanged(this, ParserState.Ready); // trigger find all references &c. updates
869-
}
870-
871-
return success;
872-
}
873-
874848
private bool RemoveKeysFromCollections(IEnumerable<QualifiedModuleName> keys)
875849
{
876850
var success = true;

0 commit comments

Comments
 (0)