Skip to content

Commit cb9898a

Browse files
authored
Fix PSSA settings discovery (#1206)
1 parent 00a4d2a commit cb9898a

File tree

2 files changed

+101
-20
lines changed

2 files changed

+101
-20
lines changed

src/PowerShellEditorServices/Services/Analysis/AnalysisService.cs

Lines changed: 86 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
using System.Collections;
88
using System.Collections.Concurrent;
99
using System.Collections.Generic;
10+
using System.IO;
1011
using System.Linq;
12+
using System.Runtime.InteropServices;
1113
using System.Text;
1214
using System.Threading;
1315
using System.Threading.Tasks;
@@ -91,10 +93,12 @@ internal static string GetUniqueIdFromDiagnostic(Diagnostic diagnostic)
9193

9294
private readonly ConcurrentDictionary<ScriptFile, CorrectionTableEntry> _mostRecentCorrectionsByFile;
9395

94-
private Lazy<PssaCmdletAnalysisEngine> _analysisEngine;
96+
private Lazy<PssaCmdletAnalysisEngine> _analysisEngineLazy;
9597

9698
private CancellationTokenSource _diagnosticsCancellationTokenSource;
9799

100+
private string _pssaSettingsFilePath;
101+
98102
/// <summary>
99103
/// Construct a new AnalysisService.
100104
/// </summary>
@@ -115,13 +119,14 @@ public AnalysisService(
115119
_workplaceService = workspaceService;
116120
_analysisDelayMillis = 750;
117121
_mostRecentCorrectionsByFile = new ConcurrentDictionary<ScriptFile, CorrectionTableEntry>();
118-
_analysisEngine = new Lazy<PssaCmdletAnalysisEngine>(InstantiateAnalysisEngine);
122+
_analysisEngineLazy = new Lazy<PssaCmdletAnalysisEngine>(InstantiateAnalysisEngine);
123+
_pssaSettingsFilePath = null;
119124
}
120125

121126
/// <summary>
122127
/// The analysis engine to use for running script analysis.
123128
/// </summary>
124-
private PssaCmdletAnalysisEngine AnalysisEngine => _analysisEngine.Value;
129+
private PssaCmdletAnalysisEngine AnalysisEngine => _analysisEngineLazy?.Value;
125130

126131
/// <summary>
127132
/// Sets up a script analysis run, eventually returning the result.
@@ -133,6 +138,8 @@ public void RunScriptDiagnostics(
133138
ScriptFile[] filesToAnalyze,
134139
CancellationToken cancellationToken)
135140
{
141+
EnsureEngineSettingsCurrent();
142+
136143
if (AnalysisEngine == null)
137144
{
138145
return;
@@ -183,6 +190,8 @@ public void RunScriptDiagnostics(
183190
/// <returns>The text of the formatted PowerShell script.</returns>
184191
public Task<string> FormatAsync(string scriptFileContents, Hashtable formatSettings, int[] formatRange = null)
185192
{
193+
EnsureEngineSettingsCurrent();
194+
186195
if (AnalysisEngine == null)
187196
{
188197
return null;
@@ -254,23 +263,61 @@ public void ClearMarkers(ScriptFile file)
254263
/// <param name="settings">The new language server settings.</param>
255264
public void OnConfigurationUpdated(object sender, LanguageServerSettings settings)
256265
{
257-
ClearOpenFileMarkers();
258-
_analysisEngine = new Lazy<PssaCmdletAnalysisEngine>(InstantiateAnalysisEngine);
266+
InitializeAnalysisEngineToCurrentSettings();
259267
}
260268

261-
private PssaCmdletAnalysisEngine InstantiateAnalysisEngine()
269+
private void EnsureEngineSettingsCurrent()
262270
{
263-
if (!(_configurationService.CurrentSettings.ScriptAnalysis.Enable ?? false))
271+
if (_pssaSettingsFilePath != null
272+
&& !File.Exists(_pssaSettingsFilePath))
264273
{
265-
return null;
274+
InitializeAnalysisEngineToCurrentSettings();
275+
}
276+
}
277+
278+
private void InitializeAnalysisEngineToCurrentSettings()
279+
{
280+
// If script analysis has been disabled, just return null
281+
if (_configurationService.CurrentSettings.ScriptAnalysis.Enable != true)
282+
{
283+
if (_analysisEngineLazy != null && _analysisEngineLazy.IsValueCreated)
284+
{
285+
_analysisEngineLazy.Value.Dispose();
286+
}
287+
288+
_analysisEngineLazy = null;
289+
return;
290+
}
291+
292+
// We may be triggered after the lazy factory is set,
293+
// but before it's been able to instantiate
294+
if (_analysisEngineLazy == null)
295+
{
296+
_analysisEngineLazy = new Lazy<PssaCmdletAnalysisEngine>(InstantiateAnalysisEngine);
297+
return;
298+
}
299+
else if (!_analysisEngineLazy.IsValueCreated)
300+
{
301+
return;
266302
}
267303

304+
// Retrieve the current script analysis engine so we can recreate it after we've overridden it
305+
PssaCmdletAnalysisEngine currentAnalysisEngine = AnalysisEngine;
306+
307+
// Clear the open file markers and set the new engine factory
308+
ClearOpenFileMarkers();
309+
_analysisEngineLazy = new Lazy<PssaCmdletAnalysisEngine>(() => RecreateAnalysisEngine(currentAnalysisEngine));
310+
}
311+
312+
private PssaCmdletAnalysisEngine InstantiateAnalysisEngine()
313+
{
268314
var pssaCmdletEngineBuilder = new PssaCmdletAnalysisEngine.Builder(_loggerFactory);
269315

270316
// If there's a settings file use that
271317
if (TryFindSettingsFile(out string settingsFilePath))
272318
{
273319
_logger.LogInformation($"Configuring PSScriptAnalyzer with rules at '{settingsFilePath}'");
320+
_pssaSettingsFilePath = settingsFilePath;
274321
pssaCmdletEngineBuilder.WithSettingsFile(settingsFilePath);
275322
}
276323
else
@@ -282,26 +329,40 @@ private PssaCmdletAnalysisEngine InstantiateAnalysisEngine()
282329
return pssaCmdletEngineBuilder.Build();
283330
}
284331

332+
private PssaCmdletAnalysisEngine RecreateAnalysisEngine(PssaCmdletAnalysisEngine oldAnalysisEngine)
333+
{
334+
if (TryFindSettingsFile(out string settingsFilePath))
335+
{
336+
_logger.LogInformation($"Recreating analysis engine with rules at '{settingsFilePath}'");
337+
_pssaSettingsFilePath = settingsFilePath;
338+
return oldAnalysisEngine.RecreateWithNewSettings(settingsFilePath);
339+
}
340+
341+
_logger.LogInformation("PSScriptAnalyzer settings file not found. Falling back to default rules");
342+
return oldAnalysisEngine.RecreateWithRules(s_defaultRules);
343+
}
344+
285345
private bool TryFindSettingsFile(out string settingsFilePath)
286346
{
287347
string configuredPath = _configurationService.CurrentSettings.ScriptAnalysis.SettingsPath;
288348

289-
if (!string.IsNullOrEmpty(configuredPath))
349+
if (string.IsNullOrEmpty(configuredPath))
290350
{
291-
settingsFilePath = _workplaceService.ResolveWorkspacePath(configuredPath);
351+
settingsFilePath = null;
352+
return false;
353+
}
292354

293-
if (settingsFilePath == null)
294-
{
295-
_logger.LogError($"Unable to find PSSA settings file at '{configuredPath}'. Loading default rules.");
296-
}
355+
settingsFilePath = _workplaceService.ResolveWorkspacePath(configuredPath);
297356

298-
return settingsFilePath != null;
357+
if (settingsFilePath == null
358+
|| !File.Exists(settingsFilePath))
359+
{
360+
_logger.LogWarning($"Unable to find PSSA settings file at '{configuredPath}'. Loading default rules.");
361+
settingsFilePath = null;
362+
return false;
299363
}
300364

301-
// TODO: Could search for a default here
302-
303-
settingsFilePath = null;
304-
return false;
365+
return true;
305366
}
306367

307368
private void ClearOpenFileMarkers()
@@ -442,7 +503,12 @@ protected virtual void Dispose(bool disposing)
442503
{
443504
if (disposing)
444505
{
445-
if (_analysisEngine.IsValueCreated) { _analysisEngine.Value.Dispose(); }
506+
if (_analysisEngineLazy != null
507+
&& _analysisEngineLazy.IsValueCreated)
508+
{
509+
_analysisEngineLazy.Value.Dispose();
510+
}
511+
446512
_diagnosticsCancellationTokenSource?.Dispose();
447513
}
448514

src/PowerShellEditorServices/Services/Analysis/PssaCmdletAnalysisEngine.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,21 @@ public Task<ScriptFileMarker[]> AnalyzeScriptAsync(string scriptContent, Hashtab
261261
return GetSemanticMarkersFromCommandAsync(command);
262262
}
263263

264+
public PssaCmdletAnalysisEngine RecreateWithNewSettings(string settingsPath)
265+
{
266+
return new PssaCmdletAnalysisEngine(_logger, _analysisRunspacePool, _pssaModuleInfo, settingsPath);
267+
}
268+
269+
public PssaCmdletAnalysisEngine RecreateWithNewSettings(Hashtable settingsHashtable)
270+
{
271+
return new PssaCmdletAnalysisEngine(_logger, _analysisRunspacePool, _pssaModuleInfo, settingsHashtable);
272+
}
273+
274+
public PssaCmdletAnalysisEngine RecreateWithRules(string[] rules)
275+
{
276+
return new PssaCmdletAnalysisEngine(_logger, _analysisRunspacePool, _pssaModuleInfo, rules);
277+
}
278+
264279
#region IDisposable Support
265280
private bool disposedValue = false; // To detect redundant calls
266281

0 commit comments

Comments
 (0)