diff --git a/src/PowerShellEditorServices/Services/TextDocument/Handlers/CompletionHandler.cs b/src/PowerShellEditorServices/Services/TextDocument/Handlers/CompletionHandler.cs index 284d11a96..a8144d360 100644 --- a/src/PowerShellEditorServices/Services/TextDocument/Handlers/CompletionHandler.cs +++ b/src/PowerShellEditorServices/Services/TextDocument/Handlers/CompletionHandler.cs @@ -69,16 +69,25 @@ public override async Task Handle(CompletionParams request, Canc int cursorColumn = request.Position.Character + 1; ScriptFile scriptFile = _workspaceService.GetFile(request.TextDocument.Uri); - (bool isIncomplete, IReadOnlyList completionResults) = await GetCompletionsInFileAsync( - scriptFile, - cursorLine, - cursorColumn, - cancellationToken).ConfigureAwait(false); - - // Treat completions trigged by space as incomplete so that `gci ` - // and then typing `-` doesn't just filter the list of parameter values - // (typically files) returned by the space completion - return new CompletionList(completionResults, isIncomplete || request?.Context?.TriggerCharacter is " "); + try + { + (bool isIncomplete, IReadOnlyList completionResults) = await GetCompletionsInFileAsync( + scriptFile, + cursorLine, + cursorColumn, + cancellationToken).ConfigureAwait(false); + + // Treat completions triggered by space as incomplete so that `gci ` + // and then typing `-` doesn't just filter the list of parameter values + // (typically files) returned by the space completion + return new CompletionList(completionResults, isIncomplete || request?.Context?.TriggerCharacter is " "); + } + // We can't do anything about completions failing. + catch (Exception e) + { + _logger.LogWarning(e, "Exception occurred while running handling completion request"); + return new CompletionList(isIncomplete: true); + } } // Handler for "completionItem/resolve". In VSCode this is fired when a completion item is highlighted in the completion list. diff --git a/test/PowerShellEditorServices.Test.E2E/LSPTestsFixures.cs b/test/PowerShellEditorServices.Test.E2E/LSPTestsFixures.cs index c31333d89..e58d1e550 100644 --- a/test/PowerShellEditorServices.Test.E2E/LSPTestsFixures.cs +++ b/test/PowerShellEditorServices.Test.E2E/LSPTestsFixures.cs @@ -33,8 +33,9 @@ public class LSPTestsFixture : IAsyncLifetime private const bool IsDebugAdapterTests = false; public ILanguageClient PsesLanguageClient { get; private set; } - public List Diagnostics { get; set; } - internal List TelemetryEvents { get; set; } + public List Messages = new(); + public List Diagnostics = new(); + internal List TelemetryEvents = new(); public ITestOutputHelper Output { get; set; } protected PsesStdioProcess _psesProcess; @@ -46,9 +47,7 @@ public async Task InitializeAsync() _psesProcess = new PsesStdioProcess(factory, IsDebugAdapterTests); await _psesProcess.Start().ConfigureAwait(false); - Diagnostics = new List(); - TelemetryEvents = new List(); - DirectoryInfo testdir = + DirectoryInfo testDir = Directory.CreateDirectory(Path.Combine(s_binDir, Path.GetRandomFileName())); PsesLanguageClient = LanguageClient.PreInit(options => @@ -56,10 +55,13 @@ public async Task InitializeAsync() options .WithInput(_psesProcess.OutputStream) .WithOutput(_psesProcess.InputStream) - .WithWorkspaceFolder(DocumentUri.FromFileSystemPath(testdir.FullName), "testdir") + .WithWorkspaceFolder(DocumentUri.FromFileSystemPath(testDir.FullName), "testdir") .WithInitializationOptions(new { EnableProfileLoading = false }) .OnPublishDiagnostics(diagnosticParams => Diagnostics.AddRange(diagnosticParams.Diagnostics.Where(d => d != null))) - .OnLogMessage(logMessageParams => Output?.WriteLine($"{logMessageParams.Type}: {logMessageParams.Message}")) + .OnLogMessage(logMessageParams => { + Output?.WriteLine($"{logMessageParams.Type}: {logMessageParams.Message}"); + Messages.Add(logMessageParams); + }) .OnTelemetryEvent(telemetryEventParams => TelemetryEvents.Add( new PsesTelemetryEvent { diff --git a/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs b/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs index df1a86912..0ca4bb2e3 100644 --- a/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs +++ b/test/PowerShellEditorServices.Test.E2E/LanguageServerProtocolMessageTests.cs @@ -38,6 +38,7 @@ public class LanguageServerProtocolMessageTests : IClassFixture Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); private readonly ILanguageClient PsesLanguageClient; + private readonly List Messages; private readonly List Diagnostics; private readonly List TelemetryEvents; private readonly string PwshExe; @@ -46,6 +47,8 @@ public LanguageServerProtocolMessageTests(ITestOutputHelper output, LSPTestsFixt { data.Output = output; PsesLanguageClient = data.PsesLanguageClient; + Messages = data.Messages; + Messages.Clear(); Diagnostics = data.Diagnostics; Diagnostics.Clear(); TelemetryEvents = data.TelemetryEvents; @@ -957,6 +960,36 @@ public async Task CanSendCompletionAndCompletionResolveRequestAsync() Assert.Contains("Writes customized output to a host", updatedCompletionItem.Documentation.String); } + // Regression test for https://github.com/PowerShell/PowerShellEditorServices/issues/1926 + [SkippableFact] + public async Task CanRequestCompletionsAndHandleExceptions() + { + Skip.If(PsesStdioProcess.IsWindowsPowerShell, "This is a temporary bug in PowerShell 7, the fix is making its way upstream."); + string filePath = NewTestFile(@" +@() | ForEach-Object { + if ($false) { + return + } + + @{key=$} + }"); + + Messages.Clear(); // On some systems there's a warning message about configuration items too. + CompletionList completionItems = await PsesLanguageClient.TextDocument.RequestCompletion( + new CompletionParams + { + TextDocument = new TextDocumentIdentifier + { + Uri = DocumentUri.FromFileSystemPath(filePath) + }, + Position = new Position(line: 6, character: 11) + }); + + Assert.Empty(completionItems); + LogMessageParams message = Assert.Single(Messages); + Assert.Contains("Exception occurred while running handling completion request", message.Message); + } + [SkippableFact(Skip = "Completion for Expand-SlowArchive is flaky.")] public async Task CanSendCompletionResolveWithModulePrefixRequestAsync() {