@@ -28,6 +28,7 @@ namespace Microsoft.PowerShell.EditorServices.Services
28
28
/// </summary>
29
29
internal class AnalysisService : IDisposable
30
30
{
31
+ private readonly SemaphoreSlim _initializationSemaphore = new ( 1 , 1 ) ;
31
32
/// <summary>
32
33
/// Reliably generate an ID for a diagnostic record to track it.
33
34
/// </summary>
@@ -91,9 +92,6 @@ internal static string GetUniqueIdFromDiagnostic(Diagnostic diagnostic)
91
92
private readonly int _analysisDelayMillis = 750 ;
92
93
93
94
private readonly ConcurrentDictionary < ScriptFile , CorrectionTableEntry > _mostRecentCorrectionsByFile = new ( ) ;
94
-
95
- private Lazy < PssaCmdletAnalysisEngine > _analysisEngineLazy ;
96
-
97
95
private CancellationTokenSource _diagnosticsCancellationTokenSource ;
98
96
99
97
private readonly string _pssaModulePath ;
@@ -112,14 +110,21 @@ public AnalysisService(
112
110
_languageServer = languageServer ;
113
111
_configurationService = configurationService ;
114
112
_workspaceService = workspaceService ;
115
- _analysisEngineLazy = new Lazy < PssaCmdletAnalysisEngine > ( InstantiateAnalysisEngine ) ;
116
113
_pssaModulePath = Path . Combine ( hostInfo . BundledModulePath , "PSScriptAnalyzer" ) ;
117
114
}
118
-
115
+ private PssaCmdletAnalysisEngine ? _analysisEngine ;
119
116
/// <summary>
120
117
/// The analysis engine to use for running script analysis.
121
118
/// </summary>
122
- internal PssaCmdletAnalysisEngine AnalysisEngine => _analysisEngineLazy ? . Value ;
119
+ internal PssaCmdletAnalysisEngine ? AnalysisEngine
120
+ {
121
+ get => _analysisEngine ;
122
+ set
123
+ {
124
+ IsValueCreated = true ;
125
+ _analysisEngine = value ;
126
+ }
127
+ }
123
128
124
129
/// <summary>
125
130
/// Sets up a script analysis run, eventually returning the result.
@@ -215,7 +220,8 @@ public async Task<string> GetCommentHelpText(string functionText, string helpLoc
215
220
/// <returns>A thread-safe readonly dictionary of the code actions of the particular file.</returns>
216
221
public async Task < IReadOnlyDictionary < string , IEnumerable < MarkerCorrection > > > GetMostRecentCodeActionsForFileAsync ( DocumentUri uri )
217
222
{
218
- if ( ! _workspaceService . TryGetFile ( uri , out ScriptFile file )
223
+ ScriptFile ? file = await _workspaceService . TryGetFile ( uri ) . ConfigureAwait ( false ) ;
224
+ if ( file is null
219
225
|| ! _mostRecentCorrectionsByFile . TryGetValue ( file , out CorrectionTableEntry corrections ) )
220
226
{
221
227
return null ;
@@ -239,7 +245,7 @@ public async Task<IReadOnlyDictionary<string, IEnumerable<MarkerCorrection>>> Ge
239
245
/// </summary>
240
246
/// <param name="_">The sender of the configuration update event.</param>
241
247
/// <param name="settings">The new language server settings.</param>
242
- public void OnConfigurationUpdated ( object _ , LanguageServerSettings settings )
248
+ public async Task OnConfigurationUpdated ( LanguageServerSettings settings )
243
249
{
244
250
if ( settings . ScriptAnalysis . Enable )
245
251
{
@@ -249,24 +255,25 @@ public void OnConfigurationUpdated(object _, LanguageServerSettings settings)
249
255
250
256
private void EnsureEngineSettingsCurrent ( )
251
257
{
252
- if ( _analysisEngineLazy is null
258
+ if ( AnalysisEngine is null
253
259
|| ( _pssaSettingsFilePath is not null
254
260
&& ! File . Exists ( _pssaSettingsFilePath ) ) )
255
261
{
256
262
InitializeAnalysisEngineToCurrentSettings ( ) ;
257
263
}
258
264
}
259
265
260
- private void InitializeAnalysisEngineToCurrentSettings ( )
266
+ private async Task InitializeAnalysisEngineToCurrentSettings ( )
261
267
{
268
+
262
269
// We may be triggered after the lazy factory is set,
263
270
// but before it's been able to instantiate
264
- if ( _analysisEngineLazy is null )
271
+ if ( AnalysisEngine is null )
265
272
{
266
- _analysisEngineLazy = new Lazy < PssaCmdletAnalysisEngine > ( InstantiateAnalysisEngine ) ;
273
+ AnalysisEngine = await InstantiateAnalysisEngine ( ) . ConfigureAwait ( false ) ;
267
274
return ;
268
275
}
269
- else if ( ! _analysisEngineLazy . IsValueCreated )
276
+ else if ( IsValueCreated )
270
277
{
271
278
return ;
272
279
}
@@ -276,15 +283,16 @@ private void InitializeAnalysisEngineToCurrentSettings()
276
283
277
284
// Clear the open file markers and set the new engine factory
278
285
ClearOpenFileMarkers ( ) ;
279
- _analysisEngineLazy = new Lazy < PssaCmdletAnalysisEngine > ( ( ) => RecreateAnalysisEngine ( currentAnalysisEngine ) ) ;
286
+ AnalysisEngine = await RecreateAnalysisEngine ( currentAnalysisEngine ) . ConfigureAwait ( false ) ;
280
287
}
281
288
282
- internal PssaCmdletAnalysisEngine InstantiateAnalysisEngine ( )
289
+ internal async Task < PssaCmdletAnalysisEngine > InstantiateAnalysisEngine ( )
283
290
{
284
291
PssaCmdletAnalysisEngine . Builder pssaCmdletEngineBuilder = new ( _loggerFactory ) ;
285
292
286
293
// If there's a settings file use that
287
- if ( TryFindSettingsFile ( out string settingsFilePath ) )
294
+ string ? settingsFilePath = await TryFindSettingsFile ( ) . ConfigureAwait ( false ) ;
295
+ if ( ! string . IsNullOrEmpty ( settingsFilePath ) )
288
296
{
289
297
_logger . LogInformation ( $ "Configuring PSScriptAnalyzer with rules at '{ settingsFilePath } '") ;
290
298
_pssaSettingsFilePath = settingsFilePath ;
@@ -299,9 +307,10 @@ internal PssaCmdletAnalysisEngine InstantiateAnalysisEngine()
299
307
return pssaCmdletEngineBuilder . Build ( _pssaModulePath ) ;
300
308
}
301
309
302
- private PssaCmdletAnalysisEngine RecreateAnalysisEngine ( PssaCmdletAnalysisEngine oldAnalysisEngine )
310
+ private async Task < PssaCmdletAnalysisEngine > RecreateAnalysisEngine ( PssaCmdletAnalysisEngine oldAnalysisEngine )
303
311
{
304
- if ( TryFindSettingsFile ( out string settingsFilePath ) )
312
+ string ? settingsFilePath = await TryFindSettingsFile ( ) . ConfigureAwait ( false ) ;
313
+ if ( ! string . IsNullOrEmpty ( settingsFilePath ) )
305
314
{
306
315
_logger . LogInformation ( $ "Recreating analysis engine with rules at '{ settingsFilePath } '") ;
307
316
_pssaSettingsFilePath = settingsFilePath ;
@@ -312,27 +321,27 @@ private PssaCmdletAnalysisEngine RecreateAnalysisEngine(PssaCmdletAnalysisEngine
312
321
return oldAnalysisEngine . RecreateWithRules ( s_defaultRules ) ;
313
322
}
314
323
315
- private bool TryFindSettingsFile ( out string settingsFilePath )
324
+ private async Task < string ? > TryFindSettingsFile ( )
316
325
{
317
326
string configuredPath = _configurationService ? . CurrentSettings . ScriptAnalysis . SettingsPath ;
318
-
327
+ string ? settingsFilePath ;
319
328
if ( string . IsNullOrEmpty ( configuredPath ) )
320
329
{
321
330
settingsFilePath = null ;
322
- return false ;
331
+ return settingsFilePath ;
323
332
}
324
333
325
- settingsFilePath = _workspaceService ? . ResolveWorkspacePath ( configuredPath ) ;
334
+ settingsFilePath = await ( _workspaceService ? . ResolveWorkspacePath ( configuredPath ) ) . ConfigureAwait ( false ) ;
326
335
327
336
if ( settingsFilePath is null
328
337
|| ! File . Exists ( settingsFilePath ) )
329
338
{
330
339
_logger . LogInformation ( $ "Unable to find PSSA settings file at '{ configuredPath } '. Loading default rules.") ;
331
340
settingsFilePath = null ;
332
- return false ;
341
+ return settingsFilePath ;
333
342
}
334
343
335
- return true ;
344
+ return settingsFilePath ;
336
345
}
337
346
338
347
private void ClearOpenFileMarkers ( )
@@ -467,19 +476,22 @@ private static Hashtable GetCommentHelpRuleSettings(string helpLocation, bool fo
467
476
468
477
#region IDisposable Support
469
478
private bool disposedValue ; // To detect redundant calls
479
+ private bool IsValueCreated ;
470
480
471
481
protected virtual void Dispose ( bool disposing )
472
482
{
473
483
if ( ! disposedValue )
474
484
{
475
485
if ( disposing )
476
486
{
477
- if ( _analysisEngineLazy ? . IsValueCreated == true )
487
+ if ( IsValueCreated )
478
488
{
479
- _analysisEngineLazy . Value . Dispose ( ) ;
489
+ AnalysisEngine . Dispose ( ) ;
480
490
}
491
+ _initializationSemaphore . Dispose ( ) ;
481
492
482
493
_diagnosticsCancellationTokenSource ? . Dispose ( ) ;
494
+ _analysisEngine ? . Dispose ( ) ;
483
495
}
484
496
485
497
disposedValue = true ;
0 commit comments