Skip to content

Commit 776904b

Browse files
committed
First refactoring of Parse and ParseAll
1 parent 971c3d3 commit 776904b

File tree

3 files changed

+93
-150
lines changed

3 files changed

+93
-150
lines changed

Rubberduck.Parsing/VBA/EnumerableExtensions.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,10 @@ public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> source, Fun
2020
var hashSet = new HashSet<TKey>();
2121
return source.Where(item => hashSet.Add(keySelector(item)));
2222
}
23+
24+
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source)
25+
{
26+
return new HashSet<T>(source);
27+
}
2328
}
2429
}

Rubberduck.Parsing/VBA/ParseCoordinator.cs

Lines changed: 88 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ namespace Rubberduck.Parsing.VBA
2626
public class ParseCoordinator : IParseCoordinator
2727
{
2828
public RubberduckParserState State { get { return _state; } }
29-
29+
3030
private readonly ConcurrentDictionary<IVBComponent, Tuple<Task, CancellationTokenSource>> _currentTasks =
3131
new ConcurrentDictionary<IVBComponent, Tuple<Task, CancellationTokenSource>>();
3232

@@ -91,44 +91,69 @@ public void Parse(CancellationTokenSource token)
9191
{
9292
State.RefreshProjects(_vbe);
9393

94-
var components = new List<IVBComponent>();
95-
foreach (var project in State.Projects)
96-
{
97-
foreach (var component in project.VBComponents)
98-
{
99-
components.Add(component);
100-
}
101-
}
94+
var components = State.Projects.SelectMany(project => project.VBComponents).ToList();
10295

10396
// tests do not fire events when components are removed--clear components
104-
foreach (var tree in State.ParseTrees)
105-
{
106-
State.ClearStateCache(tree.Key); // handle potentially removed components without crashing
107-
}
97+
ClearComponentStateCacheForTests();
10898

10999
SyncComReferences(State.Projects);
110100
State.RefreshFinder(_hostApp);
111-
101+
112102
AddBuiltInDeclarations();
113103
State.RefreshFinder(_hostApp);
114104

105+
SetModuleStates(components, ParserState.Pending);
106+
107+
// invalidation cleanup should go into ParseAsync?
108+
CleanUpComponentAttributes(components);
109+
110+
_projectDeclarations.Clear();
111+
State.ClearBuiltInReferences();
112+
113+
ParseComponents(components, token);
114+
115+
if (token.IsCancellationRequested || State.Status >= ParserState.Error)
116+
{
117+
return;
118+
}
119+
120+
State.SetStatusAndFireStateChanged(this, ParserState.ResolvedDeclarations);
121+
122+
ResolveReferences(token.Token);
123+
124+
State.RebuildSelectionCache();
125+
126+
}
127+
128+
private void SetModuleStates(List<IVBComponent> components, ParserState parserState)
129+
{
115130
foreach (var component in components)
116131
{
117-
State.SetModuleState(component, ParserState.Pending);
132+
State.SetModuleState(component, parserState);
118133
}
134+
}
119135

120-
// invalidation cleanup should go into ParseAsync?
136+
private void CleanUpComponentAttributes(List<IVBComponent> components)
137+
{
121138
foreach (var key in _componentAttributes.Keys)
122139
{
123140
if (!components.Contains(key))
124141
{
125142
_componentAttributes.Remove(key);
126143
}
127144
}
145+
}
128146

129-
_projectDeclarations.Clear();
130-
State.ClearBuiltInReferences();
147+
private void ClearComponentStateCacheForTests()
148+
{
149+
foreach (var tree in State.ParseTrees)
150+
{
151+
State.ClearStateCache(tree.Key); // handle potentially removed components without crashing
152+
}
153+
}
131154

155+
private void ParseComponents(List<IVBComponent> components, CancellationTokenSource token)
156+
{
132157
var parseTasks = new Task[components.Count];
133158
for (var i = 0; i < components.Count; i++)
134159
{
@@ -147,74 +172,35 @@ public void Parse(CancellationTokenSource token)
147172
var qualifiedName = new QualifiedModuleName(components[index]);
148173

149174
State.SetModuleState(components[index], ParserState.ResolvingDeclarations);
175+
150176
ResolveDeclarations(qualifiedName.Component,
151177
State.ParseTrees.Find(s => s.Key == qualifiedName).Value);
152178
});
153179

154180
parseTasks[i].Start();
155181
}
156-
157182
Task.WaitAll(parseTasks);
183+
}
158184

159-
if (State.Status < ParserState.Error)
160-
{
161-
State.SetStatusAndFireStateChanged(this, ParserState.ResolvedDeclarations);
162-
Task.WaitAll(ResolveReferencesAsync(token.Token));
163-
State.RebuildSelectionCache();
164-
}
185+
private void ResolveReferences(CancellationToken token)
186+
{
187+
Task.WaitAll(ResolveReferencesAsync(token));
165188
}
166189

190+
167191
/// <summary>
168192
/// Starts parsing all components of all unprotected VBProjects associated with the VBE-Instance passed to the constructor of this parser instance.
169193
/// </summary>
170194
private void ParseAll(object requestor, CancellationTokenSource token)
171195
{
172196
State.RefreshProjects(_vbe);
173-
174-
var components = new List<IVBComponent>();
175-
foreach (var project in State.Projects)
176-
{
177-
foreach (IVBComponent component in project.VBComponents)
178-
{
179-
components.Add(component);
180-
}
181-
}
182-
183-
var componentsRemoved = false;
184-
foreach (var declaration in State.AllUserDeclarations)
185-
{
186-
if (!declaration.DeclarationType.HasFlag(DeclarationType.Module))
187-
{
188-
continue;
189-
}
190-
191-
var componentExists = false;
192-
foreach (var component in components)
193-
{
194-
if (component.Name == declaration.ComponentName &&
195-
component.Collection.Parent.HelpFile == declaration.ProjectId)
196-
{
197-
componentExists = true;
198-
break;
199-
}
200-
}
201-
202-
if (!componentExists)
203-
{
204-
componentsRemoved = true;
205-
State.ClearStateCache(declaration.QualifiedName.QualifiedModuleName);
206-
}
207-
}
208-
209-
var toParse = new List<IVBComponent>();
210-
foreach (var component in components)
211-
{
212-
if (State.IsNewOrModified(component))
213-
{
214-
toParse.Add(component);
215-
}
216-
}
217-
197+
198+
var components = State.Projects.SelectMany(project => project.VBComponents).ToList();
199+
200+
var componentsRemoved = ClearStateCashForRemovedComponents(components);
201+
202+
var toParse = components.Where(component => State.IsNewOrModified(component)).ToList();
203+
218204
if (toParse.Count == 0)
219205
{
220206
if (componentsRemoved) // trigger UI updates
@@ -225,11 +211,8 @@ private void ParseAll(object requestor, CancellationTokenSource token)
225211
State.SetStatusAndFireStateChanged(requestor, State.Status);
226212
//return; // returning here leaves state in 'ResolvedDeclarations' when a module is removed, which disables refresh
227213
}
228-
229-
foreach (var component in toParse)
230-
{
231-
State.SetModuleState(component, ParserState.Pending);
232-
}
214+
215+
SetModuleStates(toParse, ParserState.Pending);
233216

234217
SyncComReferences(State.Projects);
235218
State.RefreshFinder(_hostApp);
@@ -238,13 +221,7 @@ private void ParseAll(object requestor, CancellationTokenSource token)
238221
State.RefreshFinder(_hostApp);
239222

240223
// invalidation cleanup should go into ParseAsync?
241-
foreach (var key in _componentAttributes.Keys)
242-
{
243-
if (!components.Contains(key))
244-
{
245-
_componentAttributes.Remove(key);
246-
}
247-
}
224+
CleanUpComponentAttributes(components);
248225

249226
if (token.IsCancellationRequested)
250227
{
@@ -254,53 +231,46 @@ private void ParseAll(object requestor, CancellationTokenSource token)
254231
_projectDeclarations.Clear();
255232
State.ClearBuiltInReferences();
256233

257-
var parseTasks = new Task[toParse.Count];
258-
for (var i = 0; i < toParse.Count; i++)
259-
{
260-
var index = i;
261-
parseTasks[i] = new Task(() =>
262-
{
263-
ParseAsync(toParse[index], token).Wait(token.Token);
234+
ParseComponents(toParse, token);
264235

265-
if (token.IsCancellationRequested)
266-
{
267-
return;
268-
}
269-
270-
if (State.Status == ParserState.Error) { return; }
271-
272-
var qualifiedName = new QualifiedModuleName(toParse[index]);
273-
274-
State.SetModuleState(toParse[index], ParserState.ResolvingDeclarations);
275-
276-
ResolveDeclarations(qualifiedName.Component,
277-
State.ParseTrees.Find(s => s.Key == qualifiedName).Value);
278-
});
279-
280-
parseTasks[i].Start();
281-
}
282-
283-
if (token.IsCancellationRequested)
236+
if (token.IsCancellationRequested || State.Status >= ParserState.Error)
284237
{
285238
return;
286239
}
287240

288-
Task.WaitAll(parseTasks);
241+
Debug.Assert(State.ParseTrees.Count == components.Count, string.Format("ParserState has {0} parse trees for {1} components.", State.ParseTrees.Count, components.Count));
242+
243+
State.SetStatusAndFireStateChanged(requestor, ParserState.ResolvedDeclarations);
244+
245+
ResolveReferences(token.Token);
246+
247+
State.RebuildSelectionCache();
248+
}
289249

290-
if (token.IsCancellationRequested)
250+
/// <summary>
251+
/// Clears state cach for removed components.
252+
/// Returns whether components have been removed.
253+
/// </summary>
254+
private bool ClearStateCashForRemovedComponents(List<IVBComponent> components)
255+
{
256+
var removedModuledecalrations = RemovedModuleDeclarations(components);
257+
var componentRemoved = removedModuledecalrations.Any();
258+
foreach (var declaration in removedModuledecalrations)
291259
{
292-
return;
260+
State.ClearStateCache(declaration.QualifiedName.QualifiedModuleName);
293261
}
262+
return componentRemoved;
263+
}
294264

295-
if (State.Status < ParserState.Error)
296-
{
297-
Debug.Assert(State.ParseTrees.Count == components.Count, string.Format("ParserState has {0} parse trees for {1} components.", State.ParseTrees.Count, components.Count));
298-
State.SetStatusAndFireStateChanged(requestor, ParserState.ResolvedDeclarations);
299-
Task.WaitAll(ResolveReferencesAsync(token.Token));
300-
State.RebuildSelectionCache();
301-
}
265+
private IEnumerable<Declaration> RemovedModuleDeclarations(List<IVBComponent> components)
266+
{
267+
var moduleDeclarations = State.AllUserDeclarations.Where(declaration => declaration.DeclarationType.HasFlag(DeclarationType.Module));
268+
var componentKeys = components.Select(component => new { name = component.Name, projectId = component.Collection.Parent.HelpFile }).ToHashSet();
269+
var removedModuledecalrations = moduleDeclarations.Where(declaration => !componentKeys.Contains(new { name = declaration.ComponentName, projectId = declaration.ProjectId }));
270+
return removedModuledecalrations;
302271
}
303272

273+
304274
private Task[] ResolveReferencesAsync(CancellationToken token)
305275
{
306276
foreach (var kvp in State.ParseTrees)

RubberduckTests/ComponentTypeExtensionTests.cs

Lines changed: 0 additions & 32 deletions
This file was deleted.

0 commit comments

Comments
 (0)