Skip to content

Commit b6d3c9a

Browse files
powerShell/getPSHostProcesses and powerShell/getRunspace (PowerShell#1002)
1 parent 43128e6 commit b6d3c9a

File tree

7 files changed

+192
-1
lines changed

7 files changed

+192
-1
lines changed

src/PowerShellEditorServices.Engine/LanguageServer/OmnisharpLanguageServer.cs

+1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ public async Task StartAsync()
9393
.WithHandler<ReferencesHandler>()
9494
.WithHandler<DocumentSymbolHandler>()
9595
.WithHandler<DocumentHighlightHandler>()
96+
.WithHandler<PSHostProcessAndRunspaceHandlers>()
9697
.WithHandler<CodeLensHandlers>();
9798

9899
logger.LogInformation("Handlers added");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using OmniSharp.Extensions.Embedded.MediatR;
2+
using OmniSharp.Extensions.JsonRpc;
3+
4+
namespace PowerShellEditorServices.Engine.Services.Handlers
5+
{
6+
[Serial, Method("powerShell/getPSHostProcesses")]
7+
public interface IGetPSHostProcessesHandler : IJsonRpcRequestHandler<GetPSHostProcesssesParams, PSHostProcessResponse[]> { }
8+
9+
public class GetPSHostProcesssesParams : IRequest<PSHostProcessResponse[]> { }
10+
11+
public class PSHostProcessResponse
12+
{
13+
public string ProcessName { get; set; }
14+
15+
public int ProcessId { get; set; }
16+
17+
public string AppDomainName { get; set; }
18+
19+
public string MainWindowTitle { get; set; }
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using OmniSharp.Extensions.Embedded.MediatR;
2+
using OmniSharp.Extensions.JsonRpc;
3+
4+
namespace PowerShellEditorServices.Engine.Services.Handlers
5+
{
6+
[Serial, Method("powerShell/getRunspace")]
7+
public interface IGetRunspaceHandler : IJsonRpcRequestHandler<GetRunspaceParams, RunspaceResponse[]> { }
8+
9+
public class GetRunspaceParams : IRequest<RunspaceResponse[]>
10+
{
11+
public string ProcessId {get; set; }
12+
}
13+
14+
public class RunspaceResponse
15+
{
16+
public int Id { get; set; }
17+
18+
public string Name { get; set; }
19+
20+
public string Availability { get; set; }
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
using System.Collections.Generic;
2+
using System.Management.Automation;
3+
using System.Management.Automation.Runspaces;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
using Microsoft.Extensions.Logging;
7+
8+
namespace PowerShellEditorServices.Engine.Services.Handlers
9+
{
10+
public class PSHostProcessAndRunspaceHandlers : IGetPSHostProcessesHandler, IGetRunspaceHandler
11+
{
12+
private readonly ILogger<GetVersionHandler> _logger;
13+
14+
public PSHostProcessAndRunspaceHandlers(ILoggerFactory factory)
15+
{
16+
_logger = factory.CreateLogger<GetVersionHandler>();
17+
}
18+
19+
public Task<PSHostProcessResponse[]> Handle(GetPSHostProcesssesParams request, CancellationToken cancellationToken)
20+
{
21+
var psHostProcesses = new List<PSHostProcessResponse>();
22+
23+
int processId = System.Diagnostics.Process.GetCurrentProcess().Id;
24+
25+
using (var pwsh = PowerShell.Create())
26+
{
27+
pwsh.AddCommand("Get-PSHostProcessInfo")
28+
.AddCommand("Where-Object")
29+
.AddParameter("Property", "ProcessId")
30+
.AddParameter("NE")
31+
.AddParameter("Value", processId.ToString());
32+
33+
var processes = pwsh.Invoke<PSObject>();
34+
35+
if (processes != null)
36+
{
37+
foreach (dynamic p in processes)
38+
{
39+
psHostProcesses.Add(
40+
new PSHostProcessResponse
41+
{
42+
ProcessName = p.ProcessName,
43+
ProcessId = p.ProcessId,
44+
AppDomainName = p.AppDomainName,
45+
MainWindowTitle = p.MainWindowTitle
46+
});
47+
}
48+
}
49+
}
50+
51+
return Task.FromResult(psHostProcesses.ToArray());
52+
}
53+
54+
public Task<RunspaceResponse[]> Handle(GetRunspaceParams request, CancellationToken cancellationToken)
55+
{
56+
IEnumerable<PSObject> runspaces = null;
57+
58+
if (request.ProcessId == null) {
59+
request.ProcessId = "current";
60+
}
61+
62+
// If the processId is a valid int, we need to run Get-Runspace within that process
63+
// otherwise just use the current runspace.
64+
if (int.TryParse(request.ProcessId, out int pid))
65+
{
66+
// Create a remote runspace that we will invoke Get-Runspace in.
67+
using(var rs = RunspaceFactory.CreateRunspace(new NamedPipeConnectionInfo(pid)))
68+
using(var ps = PowerShell.Create())
69+
{
70+
rs.Open();
71+
ps.Runspace = rs;
72+
// Returns deserialized Runspaces. For simpler code, we use PSObject and rely on dynamic later.
73+
runspaces = ps.AddCommand("Microsoft.PowerShell.Utility\\Get-Runspace").Invoke<PSObject>();
74+
}
75+
}
76+
else
77+
{
78+
// TODO: Bring back
79+
// var psCommand = new PSCommand().AddCommand("Microsoft.PowerShell.Utility\\Get-Runspace");
80+
// var sb = new StringBuilder();
81+
// // returns (not deserialized) Runspaces. For simpler code, we use PSObject and rely on dynamic later.
82+
// runspaces = await editorSession.PowerShellContext.ExecuteCommandAsync<PSObject>(psCommand, sb);
83+
}
84+
85+
var runspaceResponses = new List<RunspaceResponse>();
86+
87+
if (runspaces != null)
88+
{
89+
foreach (dynamic runspace in runspaces)
90+
{
91+
runspaceResponses.Add(
92+
new RunspaceResponse
93+
{
94+
Id = runspace.Id,
95+
Name = runspace.Name,
96+
Availability = runspace.RunspaceAvailability.ToString()
97+
});
98+
}
99+
}
100+
101+
return Task.FromResult(runspaceResponses.ToArray());
102+
}
103+
}
104+
}

test/Pester/EditorServices.Integration.Tests.ps1

+24
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,30 @@ Write-Host 'Goodbye'
331331
$response.Result[1].Range.End.Character | Should -BeExactly 10
332332
}
333333

334+
It "Can handle a powerShell/getPSHostProcesses request" {
335+
$request = Send-LspRequest -Client $client -Method "powerShell/getPSHostProcesses"
336+
$response = Get-LspResponse -Client $client -Id $request.Id
337+
$response.Result | Should -Not -BeNullOrEmpty
338+
339+
$processInfos = @(Get-PSHostProcessInfo)
340+
341+
# We need to subtract one because this message fiilters out the "current" process.
342+
$processInfos.Count - 1 | Should -BeExactly $response.Result.Count
343+
344+
$response.Result[0].processName |
345+
Should -MatchExactly -RegularExpression "((pwsh)|(powershell))(.exe)*"
346+
}
347+
348+
It "Can handle a powerShell/getRunspace request" {
349+
$processInfos = Get-PSHostProcessInfo
350+
351+
$request = Send-LspGetRunspaceRequest -Client $client -ProcessId $processInfos[0].ProcessId
352+
$response = Get-LspResponse -Client $client -Id $request.Id
353+
354+
$response.Result | Should -Not -BeNullOrEmpty
355+
$response.Result.Count | Should -BeGreaterThan 0
356+
}
357+
334358
It "Can handle a textDocument/codeLens Pester request" {
335359
$filePath = New-TestFile -FileName ("$([System.IO.Path]::GetRandomFileName()).Tests.ps1") -Script '
336360
Describe "DescribeName" {

tools/PsesPsClient/PsesPsClient.psd1

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ FunctionsToExport = @(
8181
'Send-LspDocumentSymbolRequest',
8282
'Send-LspDocumentHighlightRequest',
8383
'Send-LspReferencesRequest',
84+
'Send-LspGetRunspaceRequest',
8485
'Send-LspCodeLensRequest',
8586
'Send-LspCodeLensResolveRequest',
8687
'Send-LspShutdownRequest',

tools/PsesPsClient/PsesPsClient.psm1

+19-1
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,7 @@ function Send-LspDocumentHighlightRequest
487487
return Send-LspRequest -Client $Client -Method 'textDocument/documentHighlight' -Parameters $documentHighlightParams
488488
}
489489

490-
function Send-LspCodeLensRequest
490+
function Send-LspGetRunspaceRequest
491491
{
492492
[OutputType([PsesPsClient.LspRequest])]
493493
param(
@@ -496,6 +496,24 @@ function Send-LspCodeLensRequest
496496
$Client,
497497

498498
[Parameter(Mandatory)]
499+
[int]
500+
$ProcessId
501+
)
502+
503+
$params = [PowerShellEditorServices.Engine.Services.Handlers.GetRunspaceParams]@{
504+
ProcessId = $ProcessId
505+
}
506+
return Send-LspRequest -Client $Client -Method 'powerShell/getRunspace' -Parameters $params
507+
}
508+
509+
function Send-LspCodeLensRequest
510+
{
511+
[OutputType([PsesPsClient.LspRequest])]
512+
param(
513+
[Parameter(Position = 0, Mandatory)]
514+
[PsesPsClient.PsesLspClient]
515+
$Client,
516+
499517
[string]
500518
$Uri
501519
)

0 commit comments

Comments
 (0)