@@ -26,7 +26,7 @@ namespace Rubberduck.Parsing.VBA
26
26
public class ParseCoordinator : IParseCoordinator
27
27
{
28
28
public RubberduckParserState State { get { return _state ; } }
29
-
29
+
30
30
private readonly ConcurrentDictionary < IVBComponent , Tuple < Task , CancellationTokenSource > > _currentTasks =
31
31
new ConcurrentDictionary < IVBComponent , Tuple < Task , CancellationTokenSource > > ( ) ;
32
32
@@ -91,44 +91,69 @@ public void Parse(CancellationTokenSource token)
91
91
{
92
92
State . RefreshProjects ( _vbe ) ;
93
93
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 ( ) ;
102
95
103
96
// 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 ( ) ;
108
98
109
99
SyncComReferences ( State . Projects ) ;
110
100
State . RefreshFinder ( _hostApp ) ;
111
-
101
+
112
102
AddBuiltInDeclarations ( ) ;
113
103
State . RefreshFinder ( _hostApp ) ;
114
104
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
+ {
115
130
foreach ( var component in components )
116
131
{
117
- State . SetModuleState ( component , ParserState . Pending ) ;
132
+ State . SetModuleState ( component , parserState ) ;
118
133
}
134
+ }
119
135
120
- // invalidation cleanup should go into ParseAsync?
136
+ private void CleanUpComponentAttributes ( List < IVBComponent > components )
137
+ {
121
138
foreach ( var key in _componentAttributes . Keys )
122
139
{
123
140
if ( ! components . Contains ( key ) )
124
141
{
125
142
_componentAttributes . Remove ( key ) ;
126
143
}
127
144
}
145
+ }
128
146
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
+ }
131
154
155
+ private void ParseComponents ( List < IVBComponent > components , CancellationTokenSource token )
156
+ {
132
157
var parseTasks = new Task [ components . Count ] ;
133
158
for ( var i = 0 ; i < components . Count ; i ++ )
134
159
{
@@ -147,74 +172,35 @@ public void Parse(CancellationTokenSource token)
147
172
var qualifiedName = new QualifiedModuleName ( components [ index ] ) ;
148
173
149
174
State . SetModuleState ( components [ index ] , ParserState . ResolvingDeclarations ) ;
175
+
150
176
ResolveDeclarations ( qualifiedName . Component ,
151
177
State . ParseTrees . Find ( s => s . Key == qualifiedName ) . Value ) ;
152
178
} ) ;
153
179
154
180
parseTasks [ i ] . Start ( ) ;
155
181
}
156
-
157
182
Task . WaitAll ( parseTasks ) ;
183
+ }
158
184
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 ) ) ;
165
188
}
166
189
190
+
167
191
/// <summary>
168
192
/// Starts parsing all components of all unprotected VBProjects associated with the VBE-Instance passed to the constructor of this parser instance.
169
193
/// </summary>
170
194
private void ParseAll ( object requestor , CancellationTokenSource token )
171
195
{
172
196
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
+
218
204
if ( toParse . Count == 0 )
219
205
{
220
206
if ( componentsRemoved ) // trigger UI updates
@@ -225,11 +211,8 @@ private void ParseAll(object requestor, CancellationTokenSource token)
225
211
State . SetStatusAndFireStateChanged ( requestor , State . Status ) ;
226
212
//return; // returning here leaves state in 'ResolvedDeclarations' when a module is removed, which disables refresh
227
213
}
228
-
229
- foreach ( var component in toParse )
230
- {
231
- State . SetModuleState ( component , ParserState . Pending ) ;
232
- }
214
+
215
+ SetModuleStates ( toParse , ParserState . Pending ) ;
233
216
234
217
SyncComReferences ( State . Projects ) ;
235
218
State . RefreshFinder ( _hostApp ) ;
@@ -238,13 +221,7 @@ private void ParseAll(object requestor, CancellationTokenSource token)
238
221
State . RefreshFinder ( _hostApp ) ;
239
222
240
223
// 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 ) ;
248
225
249
226
if ( token . IsCancellationRequested )
250
227
{
@@ -254,53 +231,46 @@ private void ParseAll(object requestor, CancellationTokenSource token)
254
231
_projectDeclarations . Clear ( ) ;
255
232
State . ClearBuiltInReferences ( ) ;
256
233
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 ) ;
264
235
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 )
284
237
{
285
238
return ;
286
239
}
287
240
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
+ }
289
249
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 )
291
259
{
292
- return ;
260
+ State . ClearStateCache ( declaration . QualifiedName . QualifiedModuleName ) ;
293
261
}
262
+ return componentRemoved ;
263
+ }
294
264
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 ;
302
271
}
303
272
273
+
304
274
private Task [ ] ResolveReferencesAsync ( CancellationToken token )
305
275
{
306
276
foreach ( var kvp in State . ParseTrees )
0 commit comments