Skip to content

Commit e22921d

Browse files
committed
Consume Editor Services host as PowerShell module
This change refactors the existing PowerShell Editor Services host management and editor client code to use the PSES host as a PowerShell module rather than its previous executable form. This involves launching the powershell.exe process and loading up the PSES host as a module in that process. It also involves communicating with the PSES host via named pipes instead of standard in/out as we did previously. This change can be understood further by looking at the following pull request at the PowerShellEditorServices repo: PowerShell/PowerShellEditorServices#256
1 parent 2e9b339 commit e22921d

File tree

9 files changed

+338
-162
lines changed

9 files changed

+338
-162
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ examples/Release/
33
examples/Tests/foo*.txt
44
out/
55
node_modules/
6+
logs/
7+
modules/*
8+
!modules/README.md
69
vscode-powershell.zip
710
vscps-preview.zip
811
*.vsix

.vscodeignore

+2
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@ build/**
77
bin/EditorServices.log
88
bin/DebugAdapter.log
99
bin/*.vshost.*
10+
logs/
11+

modules/README.md

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
## `modules` folder README
2+
3+
This folder contains modules that are bundled with the vscode-powershell extension.
4+
All subfolders are not included in our GitHub repository, they are added here just
5+
before the module is published to the Visual Studio Marketplace.
6+
7+
This file serves as a placeholder so that the `modules` folder will be included
8+
in our Git repository.

package.json

+5-78
Original file line numberDiff line numberDiff line change
@@ -118,19 +118,8 @@
118118
"powershell"
119119
]
120120
},
121-
"windows": {
122-
"program": "bin/Microsoft.PowerShell.EditorServices.Host.exe"
123-
},
124-
"winx86": {
125-
"program": "bin/Microsoft.PowerShell.EditorServices.Host.x86.exe"
126-
},
127-
"args": [
128-
"/debugAdapter",
129-
"/logLevel:Verbose",
130-
"/hostName:\"Visual Studio Code Host\"",
131-
"/hostProfileId:Microsoft.VSCode",
132-
"/hostVersion:0.6.1"
133-
],
121+
"program": "./out/debugAdapter.js",
122+
"runtime": "node",
134123
"configurationAttributes": {
135124
"launch": {
136125
"required": [
@@ -165,68 +154,6 @@
165154
"program": "${file}",
166155
"args": [],
167156
"cwd": "${file}"
168-
},
169-
{
170-
"name": "PowerShell x86",
171-
"type": "PowerShell x86",
172-
"request": "launch",
173-
"program": "${file}",
174-
"args": [],
175-
"cwd": "${file}"
176-
}
177-
]
178-
},
179-
{
180-
"type": "PowerShell x86",
181-
"enableBreakpointsFor": {
182-
"languageIds": [
183-
"powershell"
184-
]
185-
},
186-
"windows": {
187-
"program": "bin/Microsoft.PowerShell.EditorServices.Host.x86.exe"
188-
},
189-
"args": [
190-
"/debugAdapter",
191-
"/logLevel:Verbose",
192-
"/hostName:\"Visual Studio Code Host\"",
193-
"/hostProfileId:Microsoft.VSCode",
194-
"/hostVersion:0.6.1"
195-
],
196-
"configurationAttributes": {
197-
"launch": {
198-
"required": [
199-
"program"
200-
],
201-
"properties": {
202-
"program": {
203-
"type": "string",
204-
"description": "Absolute path to the PowerShell script to launch under the debugger."
205-
},
206-
"args": {
207-
"type": "array",
208-
"description": "Command line arguments to pass to the PowerShell script.",
209-
"items": {
210-
"type": "string"
211-
},
212-
"default": []
213-
},
214-
"cwd": {
215-
"type": "string",
216-
"description": "Absolute path to the working directory. Default is the current workspace.",
217-
"default": "."
218-
}
219-
}
220-
}
221-
},
222-
"initialConfigurations": [
223-
{
224-
"name": "PowerShell x86",
225-
"type": "PowerShell x86",
226-
"request": "launch",
227-
"program": "${file}",
228-
"args": [],
229-
"cwd": "${file}"
230157
}
231158
]
232159
}
@@ -255,10 +182,10 @@
255182
"default": "",
256183
"description": "Specifies the path to a PowerShell Script Analyzer settings file. Use either an absolute path (to override the default settings for all projects) or use a path relative to your workspace."
257184
},
258-
"powershell.developer.editorServicesHostPath": {
185+
"powershell.developer.bundledModulesPath": {
259186
"type": "string",
260-
"default": "../bin/",
261-
"description": "Specifies the path to the folder containing the PowerShell Editor Services host executables."
187+
"default": "../modules/",
188+
"description": "Specifies the path to the folder containing modules that are bundled with the PowerShell extension (i.e. PowerShell Editor Services, PowerShell Script Analyzer, Plaster)"
262189
},
263190
"powershell.developer.editorServicesLogLevel": {
264191
"type": "string",

scripts/Start-EditorServices.ps1

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
param(
2+
[Parameter(Mandatory=$true)]
3+
[ValidateNotNullOrEmpty()]
4+
[string]
5+
$EditorServicesVersion,
6+
7+
[Parameter(Mandatory=$true)]
8+
[ValidateNotNullOrEmpty()]
9+
[string]
10+
$HostName,
11+
12+
[Parameter(Mandatory=$true)]
13+
[ValidateNotNullOrEmpty()]
14+
[string]
15+
$HostProfileId,
16+
17+
[Parameter(Mandatory=$true)]
18+
[ValidateNotNullOrEmpty()]
19+
[string]
20+
$HostVersion,
21+
22+
[Parameter(Mandatory=$true)]
23+
[ValidateNotNullOrEmpty()]
24+
[string]
25+
$LanguageServicePipeName,
26+
27+
[Parameter(Mandatory=$true)]
28+
[ValidateNotNullOrEmpty()]
29+
[string]
30+
$DebugServicePipeName,
31+
32+
[ValidateNotNullOrEmpty()]
33+
[string]
34+
$BundledModulesPath,
35+
36+
[ValidateNotNullOrEmpty()]
37+
$LogPath,
38+
39+
[ValidateSet("Normal", "Verbose", "Error")]
40+
$LogLevel,
41+
42+
[switch]
43+
$WaitForCompletion,
44+
45+
[switch]
46+
$WaitForDebugger
47+
)
48+
49+
# Add BundledModulesPath to $env:PSModulePath
50+
if ($BundledModulesPath) {
51+
$env:PSModulePath = $BundledModulesPath + ";" + $env:PSModulePath
52+
}
53+
54+
$parsedVersion = [System.Version]::new($EditorServicesVersion)
55+
Import-Module PowerShellEditorServices -RequiredVersion $parsedVersion -ErrorAction Stop
56+
57+
Start-EditorServicesHost `
58+
-HostName $HostName `
59+
-HostProfileId $HostProfileId `
60+
-HostVersion $HostVersion `
61+
-LogPath $LogPath `
62+
-LogLevel $LogLevel `
63+
-LanguageServicePipeName $LanguageServicePipeName `
64+
-DebugServicePipeName $DebugServicePipeName `
65+
-BundledModulesPath $BundledModulesPath `
66+
-WaitForCompletion:$WaitForCompletion.IsPresent `
67+
-WaitForDebugger:$WaitForDebugger.IsPresent

src/debugAdapter.ts

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import fs = require('fs');
2+
import path = require('path');
3+
import net = require('net');
4+
import logging = require('./logging');
5+
6+
// NOTE: The purpose of this file is to serve as a bridge between
7+
// VS Code's debug adapter client (which communicates via stdio) and
8+
// PowerShell Editor Services' debug service (which communicates via
9+
// named pipes or a network protocol). It is purely a naive data
10+
// relay between the two transports.
11+
12+
var logBasePath = path.resolve(__dirname, "../logs");
13+
logging.ensurePathExists(logBasePath);
14+
15+
var debugAdapterLogWriter =
16+
fs.createWriteStream(
17+
path.resolve(
18+
logBasePath,
19+
logging.getLogName("DebugAdapterClient")));
20+
21+
// Pause the stdin buffer until we're connected to the
22+
// debug server
23+
process.stdin.pause();
24+
25+
// Establish connection before setting up the session
26+
let pipeName = "\\\\.\\pipe\\PSES-VSCode-DebugService-" + process.env.VSCODE_PID;
27+
debugAdapterLogWriter.write("Connecting to named pipe: " + pipeName + "\r\n");
28+
let debugServiceSocket = net.connect(pipeName);
29+
30+
// Write any errors to the log file
31+
debugServiceSocket.on(
32+
'error',
33+
(e) => debugAdapterLogWriter.write("Named pipe ERROR: " + e + "\r\n"));
34+
35+
// Route any output from the socket through stdout
36+
debugServiceSocket.on(
37+
'data',
38+
(data: Buffer) => process.stdout.write(data));
39+
40+
// Wait for the connection to complete
41+
debugServiceSocket.on(
42+
'connect',
43+
() => {
44+
debugAdapterLogWriter.write("Connected to named pipe: " + pipeName + "\r\n");
45+
46+
// When data comes on stdin, route it through the socket
47+
process.stdin.on(
48+
'data',
49+
(data: Buffer) => debugServiceSocket.write(data));
50+
51+
// Resume the stdin stream
52+
process.stdin.resume();
53+
});

src/logging.ts

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import fs = require('fs');
2+
3+
export function ensurePathExists(targetPath: string) {
4+
// Ensure that the path exists
5+
try {
6+
fs.mkdirSync(targetPath);
7+
}
8+
catch (e) {
9+
// If the exception isn't to indicate that the folder
10+
// exists already, rethrow it.
11+
if (e.code != 'EEXIST') {
12+
throw e;
13+
}
14+
}
15+
}
16+
17+
export function getLogName(baseName: string): string {
18+
return Math.floor(Date.now() / 1000) + '-' + baseName + '.log';
19+
}

0 commit comments

Comments
 (0)