@@ -28,6 +28,7 @@ namespace Microsoft.PowerShell.EditorServices.Services
2828 /// </summary>
2929 internal class AnalysisService : IDisposable
3030 {
31+ private readonly SemaphoreSlim _initializationSemaphore = new ( 1 , 1 ) ;
3132 /// <summary>
3233 /// Reliably generate an ID for a diagnostic record to track it.
3334 /// </summary>
@@ -91,9 +92,6 @@ internal static string GetUniqueIdFromDiagnostic(Diagnostic diagnostic)
9192 private readonly int _analysisDelayMillis = 750 ;
9293
9394 private readonly ConcurrentDictionary < ScriptFile , CorrectionTableEntry > _mostRecentCorrectionsByFile = new ( ) ;
94-
95- private Lazy < PssaCmdletAnalysisEngine > _analysisEngineLazy ;
96-
9795 private CancellationTokenSource _diagnosticsCancellationTokenSource ;
9896
9997 private readonly string _pssaModulePath ;
@@ -112,14 +110,21 @@ public AnalysisService(
112110 _languageServer = languageServer ;
113111 _configurationService = configurationService ;
114112 _workspaceService = workspaceService ;
115- _analysisEngineLazy = new Lazy < PssaCmdletAnalysisEngine > ( InstantiateAnalysisEngine ) ;
116113 _pssaModulePath = Path . Combine ( hostInfo . BundledModulePath , "PSScriptAnalyzer" ) ;
117114 }
118-
115+ private PssaCmdletAnalysisEngine ? _analysisEngine ;
119116 /// <summary>
120117 /// The analysis engine to use for running script analysis.
121118 /// </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+ }
123128
124129 /// <summary>
125130 /// Sets up a script analysis run, eventually returning the result.
@@ -215,7 +220,8 @@ public async Task<string> GetCommentHelpText(string functionText, string helpLoc
215220 /// <returns>A thread-safe readonly dictionary of the code actions of the particular file.</returns>
216221 public async Task < IReadOnlyDictionary < string , IEnumerable < MarkerCorrection > > > GetMostRecentCodeActionsForFileAsync ( DocumentUri uri )
217222 {
218- if ( ! _workspaceService . TryGetFile ( uri , out ScriptFile file )
223+ ScriptFile ? file = await _workspaceService . TryGetFile ( uri ) . ConfigureAwait ( false ) ;
224+ if ( file is null
219225 || ! _mostRecentCorrectionsByFile . TryGetValue ( file , out CorrectionTableEntry corrections ) )
220226 {
221227 return null ;
@@ -239,7 +245,7 @@ public async Task<IReadOnlyDictionary<string, IEnumerable<MarkerCorrection>>> Ge
239245 /// </summary>
240246 /// <param name="_">The sender of the configuration update event.</param>
241247 /// <param name="settings">The new language server settings.</param>
242- public void OnConfigurationUpdated ( object _ , LanguageServerSettings settings )
248+ public async Task OnConfigurationUpdated ( LanguageServerSettings settings )
243249 {
244250 if ( settings . ScriptAnalysis . Enable )
245251 {
@@ -249,24 +255,25 @@ public void OnConfigurationUpdated(object _, LanguageServerSettings settings)
249255
250256 private void EnsureEngineSettingsCurrent ( )
251257 {
252- if ( _analysisEngineLazy is null
258+ if ( AnalysisEngine is null
253259 || ( _pssaSettingsFilePath is not null
254260 && ! File . Exists ( _pssaSettingsFilePath ) ) )
255261 {
256262 InitializeAnalysisEngineToCurrentSettings ( ) ;
257263 }
258264 }
259265
260- private void InitializeAnalysisEngineToCurrentSettings ( )
266+ private async Task InitializeAnalysisEngineToCurrentSettings ( )
261267 {
268+
262269 // We may be triggered after the lazy factory is set,
263270 // but before it's been able to instantiate
264- if ( _analysisEngineLazy is null )
271+ if ( AnalysisEngine is null )
265272 {
266- _analysisEngineLazy = new Lazy < PssaCmdletAnalysisEngine > ( InstantiateAnalysisEngine ) ;
273+ AnalysisEngine = await InstantiateAnalysisEngine ( ) . ConfigureAwait ( false ) ;
267274 return ;
268275 }
269- else if ( ! _analysisEngineLazy . IsValueCreated )
276+ else if ( IsValueCreated )
270277 {
271278 return ;
272279 }
@@ -276,15 +283,16 @@ private void InitializeAnalysisEngineToCurrentSettings()
276283
277284 // Clear the open file markers and set the new engine factory
278285 ClearOpenFileMarkers ( ) ;
279- _analysisEngineLazy = new Lazy < PssaCmdletAnalysisEngine > ( ( ) => RecreateAnalysisEngine ( currentAnalysisEngine ) ) ;
286+ AnalysisEngine = await RecreateAnalysisEngine ( currentAnalysisEngine ) . ConfigureAwait ( false ) ;
280287 }
281288
282- internal PssaCmdletAnalysisEngine InstantiateAnalysisEngine ( )
289+ internal async Task < PssaCmdletAnalysisEngine > InstantiateAnalysisEngine ( )
283290 {
284291 PssaCmdletAnalysisEngine . Builder pssaCmdletEngineBuilder = new ( _loggerFactory ) ;
285292
286293 // 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 ) )
288296 {
289297 _logger . LogInformation ( $ "Configuring PSScriptAnalyzer with rules at '{ settingsFilePath } '") ;
290298 _pssaSettingsFilePath = settingsFilePath ;
@@ -299,9 +307,10 @@ internal PssaCmdletAnalysisEngine InstantiateAnalysisEngine()
299307 return pssaCmdletEngineBuilder . Build ( _pssaModulePath ) ;
300308 }
301309
302- private PssaCmdletAnalysisEngine RecreateAnalysisEngine ( PssaCmdletAnalysisEngine oldAnalysisEngine )
310+ private async Task < PssaCmdletAnalysisEngine > RecreateAnalysisEngine ( PssaCmdletAnalysisEngine oldAnalysisEngine )
303311 {
304- if ( TryFindSettingsFile ( out string settingsFilePath ) )
312+ string ? settingsFilePath = await TryFindSettingsFile ( ) . ConfigureAwait ( false ) ;
313+ if ( ! string . IsNullOrEmpty ( settingsFilePath ) )
305314 {
306315 _logger . LogInformation ( $ "Recreating analysis engine with rules at '{ settingsFilePath } '") ;
307316 _pssaSettingsFilePath = settingsFilePath ;
@@ -312,27 +321,27 @@ private PssaCmdletAnalysisEngine RecreateAnalysisEngine(PssaCmdletAnalysisEngine
312321 return oldAnalysisEngine . RecreateWithRules ( s_defaultRules ) ;
313322 }
314323
315- private bool TryFindSettingsFile ( out string settingsFilePath )
324+ private async Task < string ? > TryFindSettingsFile ( )
316325 {
317326 string configuredPath = _configurationService ? . CurrentSettings . ScriptAnalysis . SettingsPath ;
318-
327+ string ? settingsFilePath ;
319328 if ( string . IsNullOrEmpty ( configuredPath ) )
320329 {
321330 settingsFilePath = null ;
322- return false ;
331+ return settingsFilePath ;
323332 }
324333
325- settingsFilePath = _workspaceService ? . ResolveWorkspacePath ( configuredPath ) ;
334+ settingsFilePath = await ( _workspaceService ? . ResolveWorkspacePath ( configuredPath ) ) . ConfigureAwait ( false ) ;
326335
327336 if ( settingsFilePath is null
328337 || ! File . Exists ( settingsFilePath ) )
329338 {
330339 _logger . LogInformation ( $ "Unable to find PSSA settings file at '{ configuredPath } '. Loading default rules.") ;
331340 settingsFilePath = null ;
332- return false ;
341+ return settingsFilePath ;
333342 }
334343
335- return true ;
344+ return settingsFilePath ;
336345 }
337346
338347 private void ClearOpenFileMarkers ( )
@@ -467,19 +476,22 @@ private static Hashtable GetCommentHelpRuleSettings(string helpLocation, bool fo
467476
468477 #region IDisposable Support
469478 private bool disposedValue ; // To detect redundant calls
479+ private bool IsValueCreated ;
470480
471481 protected virtual void Dispose ( bool disposing )
472482 {
473483 if ( ! disposedValue )
474484 {
475485 if ( disposing )
476486 {
477- if ( _analysisEngineLazy ? . IsValueCreated == true )
487+ if ( IsValueCreated )
478488 {
479- _analysisEngineLazy . Value . Dispose ( ) ;
489+ AnalysisEngine . Dispose ( ) ;
480490 }
491+ _initializationSemaphore . Dispose ( ) ;
481492
482493 _diagnosticsCancellationTokenSource ? . Dispose ( ) ;
494+ _analysisEngine ? . Dispose ( ) ;
483495 }
484496
485497 disposedValue = true ;
0 commit comments