Skip to content

Commit ab53c2f

Browse files
committed
Converted WorkspaceService.GetFile, TryGetFile, and EnumeratePSFiles to async
1 parent 483dd6f commit ab53c2f

File tree

14 files changed

+163
-131
lines changed

14 files changed

+163
-131
lines changed

src/PowerShellEditorServices/Extensions/Api/WorkspaceService.cs

+12-13
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System;
66
using System.Collections.Generic;
77
using System.Management.Automation.Language;
8+
using System.Threading.Tasks;
89

910
namespace Microsoft.PowerShell.EditorServices.Extensions.Services
1011
{
@@ -64,22 +65,22 @@ public interface IWorkspaceService
6465
/// </summary>
6566
/// <param name="fileUri">The absolute URI of the file to get.</param>
6667
/// <returns>A representation of the file.</returns>
67-
IEditorScriptFile GetFile(Uri fileUri);
68+
Task<IEditorScriptFile> GetFile(Uri fileUri);
6869

6970
/// <summary>
7071
/// Attempt to get a file within the workspace.
7172
/// </summary>
7273
/// <param name="fileUri">The absolute URI of the file to get.</param>
7374
/// <param name="file">The file, if it was found.</param>
7475
/// <returns>True if the file was found, false otherwise.</returns>
75-
bool TryGetFile(Uri fileUri, out IEditorScriptFile file);
76+
Task<IEditorScriptFile?> TryGetFile(Uri fileUri);
7677

7778
/// <summary>
7879
/// Get all the open files in the editor workspace.
7980
/// The result is not kept up to date as files are opened or closed.
8081
/// </summary>
8182
/// <returns>All open files in the editor workspace.</returns>
82-
IReadOnlyList<IEditorScriptFile> GetOpenedFiles();
83+
Task<IReadOnlyList<IEditorScriptFile>> GetOpenedFiles();
8384
}
8485

8586
internal class EditorScriptFile : IEditorScriptFile
@@ -124,30 +125,28 @@ internal WorkspaceService(
124125

125126
public IReadOnlyList<string> ExcludedFileGlobs { get; }
126127

127-
public IEditorScriptFile GetFile(Uri fileUri) => GetEditorFileFromScriptFile(_workspaceService.GetFile(fileUri));
128+
public async Task<IEditorScriptFile> GetFile(Uri fileUri) => await GetEditorFileFromScriptFile(await _workspaceService.GetFile(fileUri).ConfigureAwait(false)).ConfigureAwait(false);
128129

129-
public bool TryGetFile(Uri fileUri, out IEditorScriptFile file)
130+
public async Task<IEditorScriptFile?> TryGetFile(Uri fileUri)
130131
{
131-
if (!_workspaceService.TryGetFile(fileUri.LocalPath, out ScriptFile scriptFile))
132+
if (await _workspaceService.TryGetFile(fileUri.LocalPath).ConfigureAwait(false) is not ScriptFile scriptFile)
132133
{
133-
file = null;
134-
return false;
134+
return null;
135135
}
136136

137-
file = GetEditorFileFromScriptFile(scriptFile);
138-
return true;
137+
return await GetEditorFileFromScriptFile(scriptFile).ConfigureAwait(false);
139138
}
140139

141-
public IReadOnlyList<IEditorScriptFile> GetOpenedFiles()
140+
public async Task<IReadOnlyList<IEditorScriptFile>> GetOpenedFiles()
142141
{
143142
List<IEditorScriptFile> files = new();
144143
foreach (ScriptFile openedFile in _workspaceService.GetOpenedFiles())
145144
{
146-
files.Add(GetEditorFileFromScriptFile(openedFile));
145+
files.Add(await GetEditorFileFromScriptFile(openedFile).ConfigureAwait(false));
147146
}
148147
return files.AsReadOnly();
149148
}
150149

151-
private static IEditorScriptFile GetEditorFileFromScriptFile(ScriptFile file) => new EditorScriptFile(file);
150+
private static async Task<IEditorScriptFile> GetEditorFileFromScriptFile(ScriptFile file) => new EditorScriptFile(file);
152151
}
153152
}

src/PowerShellEditorServices/Services/Analysis/AnalysisService.cs

+38-26
Original file line numberDiff line numberDiff line change
@@ -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;

src/PowerShellEditorServices/Services/DebugAdapter/Handlers/BreakpointHandlers.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ public BreakpointHandlers(
4949

5050
public async Task<SetBreakpointsResponse> Handle(SetBreakpointsArguments request, CancellationToken cancellationToken)
5151
{
52-
if (!_workspaceService.TryGetFile(request.Source.Path, out ScriptFile scriptFile))
52+
ScriptFile scriptFile = await _workspaceService.TryGetFile(request.Source.Path).ConfigureAwait(false);
53+
if (scriptFile is null)
5354
{
5455
string message = _debugStateService.NoDebug ? string.Empty : "Source file could not be accessed, breakpoint not set.";
5556
IEnumerable<Breakpoint> srcBreakpoints = request.Breakpoints

src/PowerShellEditorServices/Services/DebugAdapter/Handlers/ConfigurationDoneHandler.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,8 @@ internal async Task LaunchScriptAsync(string scriptToLaunch)
116116
}
117117
else // It's a URI to an untitled script, or a raw script.
118118
{
119-
bool isScriptFile = _workspaceService.TryGetFile(scriptToLaunch, out ScriptFile untitledScript);
120-
if (isScriptFile && BreakpointApiUtils.SupportsBreakpointApis(_runspaceContext.CurrentRunspace))
119+
ScriptFile untitledScript = await _workspaceService.TryGetFile(scriptToLaunch).ConfigureAwait(false);
120+
if (untitledScript is ScriptFile && BreakpointApiUtils.SupportsBreakpointApis(_runspaceContext.CurrentRunspace))
121121
{
122122
// Parse untitled files with their `Untitled:` URI as the filename which will
123123
// cache the URI and contents within the PowerShell parser. By doing this, we
@@ -149,7 +149,7 @@ internal async Task LaunchScriptAsync(string scriptToLaunch)
149149
command = PSCommandHelpers.BuildDotSourceCommandWithArguments(
150150
string.Concat(
151151
"{" + System.Environment.NewLine,
152-
isScriptFile ? untitledScript.Contents : scriptToLaunch,
152+
untitledScript is ScriptFile ? untitledScript.Contents : scriptToLaunch,
153153
System.Environment.NewLine + "}"),
154154
_debugStateService?.Arguments);
155155
}

src/PowerShellEditorServices/Services/PowerShell/Handlers/GetCommentHelpHandler.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ public GetCommentHelpHandler(
2727
public async Task<CommentHelpRequestResult> Handle(CommentHelpRequestParams request, CancellationToken cancellationToken)
2828
{
2929
CommentHelpRequestResult result = new();
30-
31-
if (!_workspaceService.TryGetFile(request.DocumentUri, out ScriptFile scriptFile))
30+
ScriptFile? scriptFile = await _workspaceService.TryGetFile(request.DocumentUri).ConfigureAwait(false);
31+
if (scriptFile is null)
3232
{
3333
return result;
3434
}

src/PowerShellEditorServices/Services/Symbols/SymbolsService.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ internal async Task ScanWorkspacePSFiles(CancellationToken cancellationToken = d
457457
return functionDefinitionAst as FunctionDefinitionAst;
458458
}
459459

460-
internal void OnConfigurationUpdated(object _, LanguageServerSettings e)
460+
internal async Task OnConfigurationUpdated(LanguageServerSettings e)
461461
{
462462
if (e.AnalyzeOpenDocumentsOnly)
463463
{

src/PowerShellEditorServices/Services/Workspace/Handlers/ConfigurationHandler.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public override async Task<Unit> Handle(DidChangeConfigurationParams request, Ca
5454

5555
// Run any events subscribed to configuration updates
5656
_logger.LogTrace("Running configuration update event handlers");
57-
ConfigurationUpdated?.Invoke(this, _configurationService.CurrentSettings);
57+
await (ConfigurationUpdated?.Invoke(_configurationService.CurrentSettings)).ConfigureAwait(false);
5858

5959
// Convert the editor file glob patterns into an array for the Workspace
6060
// Both the files.exclude and search.exclude hash tables look like (glob-text, is-enabled):
@@ -101,6 +101,6 @@ public override async Task<Unit> Handle(DidChangeConfigurationParams request, Ca
101101
}
102102

103103

104-
public event EventHandler<LanguageServerSettings> ConfigurationUpdated;
104+
public Func<LanguageServerSettings, Task> ConfigurationUpdated;
105105
}
106106
}

src/PowerShellEditorServices/Services/Workspace/WorkspaceService.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ internal class WorkspaceService
5959
private readonly Version powerShellVersion;
6060
private readonly ConcurrentDictionary<string, ScriptFile> workspaceFiles = new();
6161
private readonly IInternalPowerShellExecutionService executionService;
62-
private bool oldLogic;
62+
private readonly bool oldLogic = true;
6363

6464
#endregion
6565

0 commit comments

Comments
 (0)