Skip to content

Commit 3c45bc0

Browse files
Return help text from the powerShell/showHelp request
Convert `powerShell/showHelp` from a fire-and-forget notification into a request that returns `ShowHelpResult { HelpText }`, so the client can render help in a read-only editor pane (and the language model `get_help` tool can reuse the same path) instead of printing into the integrated console. The handler captures `Get-Help -Full | Out-String` and `.Trim()`s both ends — `Out-String` pads the output with a leading and trailing blank line, which looked wrong in the help pane and in tool output. Drafted by Copilot (Claude Opus 4.8). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent d2112c2 commit 3c45bc0

1 file changed

Lines changed: 35 additions & 41 deletions

File tree

Lines changed: 35 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4+
using System.Collections.Generic;
45
using System.Management.Automation;
56
using System.Threading;
67
using System.Threading.Tasks;
@@ -11,74 +12,67 @@
1112

1213
namespace Microsoft.PowerShell.EditorServices.Handlers
1314
{
14-
[Serial, Method("powerShell/showHelp")]
15-
internal interface IShowHelpHandler : IJsonRpcNotificationHandler<ShowHelpParams> { }
15+
[Serial, Method("powerShell/showHelp", Direction.ClientToServer)]
16+
internal interface IShowHelpHandler : IJsonRpcRequestHandler<ShowHelpParams, ShowHelpResult> { }
1617

17-
internal class ShowHelpParams : IRequest
18+
internal class ShowHelpParams : IRequest<ShowHelpResult>
1819
{
1920
public string Text { get; set; }
2021
}
2122

23+
internal class ShowHelpResult
24+
{
25+
public string HelpText { get; set; }
26+
}
27+
2228
internal class ShowHelpHandler : IShowHelpHandler
2329
{
2430
private readonly IInternalPowerShellExecutionService _executionService;
2531

2632
public ShowHelpHandler(IInternalPowerShellExecutionService executionService) => _executionService = executionService;
2733

28-
public async Task<Unit> Handle(ShowHelpParams request, CancellationToken cancellationToken)
34+
public async Task<ShowHelpResult> Handle(ShowHelpParams request, CancellationToken cancellationToken)
2935
{
30-
// TODO: Refactor to not rerun the function definition every time.
31-
const string CheckHelpScript = @"
36+
// Resolves the command and returns its full help as a string so the
37+
// client can display it (e.g. in an editor pane) or pass it to a
38+
// language model tool. Returns a friendly message if the command
39+
// cannot be found rather than throwing.
40+
const string GetHelpScript = @"
3241
[System.Diagnostics.DebuggerHidden()]
3342
[CmdletBinding()]
3443
param (
3544
[String]$CommandName
3645
)
37-
try {
38-
$command = Microsoft.PowerShell.Core\Get-Command $CommandName -ErrorAction Stop
39-
} catch [System.Management.Automation.CommandNotFoundException] {
40-
$PSCmdlet.ThrowTerminatingError($PSItem)
46+
$command = Microsoft.PowerShell.Core\Get-Command $CommandName -ErrorAction Ignore
47+
if ($null -eq $command) {
48+
return ""No command named '$CommandName' was found.""
4149
}
42-
try {
43-
$helpUri = [Microsoft.PowerShell.Commands.GetHelpCodeMethods]::GetHelpUri($command)
44-
45-
$oldSslVersion = [System.Net.ServicePointManager]::SecurityProtocol
46-
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12
47-
48-
# HEAD means we don't need the content itself back, just the response header
49-
$status = (Microsoft.PowerShell.Utility\Invoke-WebRequest -Method Head -Uri $helpUri -TimeoutSec 5 -ErrorAction Stop).StatusCode
50-
if ($status -lt 400) {
51-
$null = Microsoft.PowerShell.Core\Get-Help $CommandName -Online
52-
return
53-
}
54-
} catch {
55-
# Ignore - we want to drop out to Get-Help -Full
56-
} finally {
57-
[System.Net.ServicePointManager]::SecurityProtocol = $oldSslVersion
58-
}
59-
60-
return Microsoft.PowerShell.Core\Get-Help $CommandName -Full
50+
return (Microsoft.PowerShell.Core\Get-Help $CommandName -Full | Microsoft.PowerShell.Utility\Out-String)
6151
";
6252

63-
string helpParams = request.Text;
64-
if (string.IsNullOrEmpty(helpParams)) { helpParams = "Get-Help"; }
53+
string commandName = request.Text;
54+
if (string.IsNullOrEmpty(commandName)) { commandName = "Get-Help"; }
6555

66-
PSCommand checkHelpPSCommand = new PSCommand()
67-
.AddScript(CheckHelpScript, useLocalScope: true)
68-
.AddArgument(helpParams);
56+
PSCommand getHelpCommand = new PSCommand()
57+
.AddScript(GetHelpScript, useLocalScope: true)
58+
.AddArgument(commandName);
6959

70-
// TODO: Rather than print the help in the console, we should send the string back
71-
// to VSCode to display in a help pop-up (or similar)
72-
await _executionService.ExecutePSCommandAsync<PSObject>(
73-
checkHelpPSCommand,
60+
IReadOnlyList<string> results = await _executionService.ExecutePSCommandAsync<string>(
61+
getHelpCommand,
7462
cancellationToken,
7563
new PowerShellExecutionOptions
7664
{
77-
RequiresForeground = true,
78-
WriteOutputToHost = true,
7965
ThrowOnError = false
8066
}).ConfigureAwait(false);
81-
return Unit.Value;
67+
68+
// Get-Help piped through Out-String is padded with a leading blank
69+
// line and trailing blank lines (a console-formatter artifact); trim
70+
// both so the help pane and the language model tool get clean output.
71+
string helpText = results is { Count: > 0 }
72+
? string.Concat(results).Trim()
73+
: string.Empty;
74+
75+
return new ShowHelpResult { HelpText = helpText };
8276
}
8377
}
8478
}

0 commit comments

Comments
 (0)