Skip to content

Commit 1067b92

Browse files
Yatao LiTylerLeonhardt
Yatao Li
authored andcommitted
EditorServiceHost: allow Tcp/NamedPipe/Stdio listeners (#629)
* EditorServiceHost: allow Tcp/NamedPipe/Stdio listeners * update ps scripts * fix script and test errors; * update startup script: there can be only one service on the stdio channel * 1. handle file refresh where range info is absent; 2. listen to the named pipe on start. * FileChange: Reloaded->IsReload * Start-EditorServices.ps1: $env:PSMODULEPATH -> $env:PsModulePath * Update Start-EditorServices.ps1 * update startup script. * update startup script, fix tests
1 parent e37f1f2 commit 1067b92

File tree

9 files changed

+212
-88
lines changed

9 files changed

+212
-88
lines changed

module/PowerShellEditorServices/PowerShellEditorServices.psm1

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,21 @@ function Start-EditorServicesHost {
3131
[string]
3232
$HostVersion,
3333

34-
[Parameter(Mandatory=$true)]
35-
[ValidateNotNullOrEmpty()]
3634
[int]
3735
$LanguageServicePort,
3836

39-
[Parameter(Mandatory=$true)]
40-
[ValidateNotNullOrEmpty()]
4137
[int]
4238
$DebugServicePort,
4339

40+
[switch]
41+
$Stdio,
42+
43+
[string]
44+
$LanguageServiceNamedPipe,
45+
46+
[string]
47+
$DebugServiceNamedPipe,
48+
4449
[ValidateNotNullOrEmpty()]
4550
[string]
4651
$BundledModulesPath,
@@ -89,12 +94,39 @@ function Start-EditorServicesHost {
8994

9095
$editorServicesHost.StartLogging($LogPath, $LogLevel);
9196

92-
if ($DebugServiceOnly.IsPresent) {
93-
$editorServicesHost.StartDebugService($DebugServicePort, $profilePaths, $false);
97+
$languageServiceConfig = New-Object Microsoft.PowerShell.EditorServices.Host.EditorServiceTransportConfig
98+
$debugServiceConfig = New-Object Microsoft.PowerShell.EditorServices.Host.EditorServiceTransportConfig
99+
100+
if ($Stdio.IsPresent) {
101+
$languageServiceConfig.TransportType = [Microsoft.PowerShell.EditorServices.Host.EditorServiceTransportType]::Stdio
102+
$debugServiceConfig.TransportType = [Microsoft.PowerShell.EditorServices.Host.EditorServiceTransportType]::Stdio
94103
}
95-
else {
96-
$editorServicesHost.StartLanguageService($LanguageServicePort, $profilePaths);
97-
$editorServicesHost.StartDebugService($DebugServicePort, $profilePaths, $true);
104+
105+
if ($LanguageServicePort) {
106+
$languageServiceConfig.TransportType = [Microsoft.PowerShell.EditorServices.Host.EditorServiceTransportType]::Tcp
107+
$languageServiceConfig.Endpoint = "$LanguageServicePort"
108+
}
109+
110+
if ($DebugServicePort) {
111+
$debugServiceConfig.TransportType = [Microsoft.PowerShell.EditorServices.Host.EditorServiceTransportType]::Tcp
112+
$debugServiceConfig.Endpoint = "$DebugServicePort"
113+
}
114+
115+
if ($LanguageServiceNamedPipe) {
116+
$languageServiceConfig.TransportType = [Microsoft.PowerShell.EditorServices.Host.EditorServiceTransportType]::NamedPipe
117+
$languageServiceConfig.Endpoint = "$LanguageServiceNamedPipe"
118+
}
119+
120+
if ($DebugServiceNamedPipe) {
121+
$debugServiceConfig.TransportType = [Microsoft.PowerShell.EditorServices.Host.EditorServiceTransportType]::NamedPipe
122+
$debugServiceConfig.Endpoint = "$DebugServiceNamedPipe"
123+
}
124+
125+
if ($DebugServiceOnly.IsPresent) {
126+
$editorServicesHost.StartDebugService($debugServiceConfig, $profilePaths, $false);
127+
} else {
128+
$editorServicesHost.StartLanguageService($languageServiceConfig, $profilePaths);
129+
$editorServicesHost.StartDebugService($debugServiceConfig, $profilePaths, $true);
98130
}
99131

100132
return $editorServicesHost

module/Start-EditorServices.ps1

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,16 @@ param(
6868
$WaitForDebugger,
6969

7070
[switch]
71-
$ConfirmInstall
71+
$ConfirmInstall,
72+
73+
[switch]
74+
$Stdio,
75+
76+
[string]
77+
$LanguageServicePipeName = $null,
78+
79+
[string]
80+
$DebugServicePipeName = $null
7281
)
7382

7483
$minPortNumber = 10000
@@ -271,16 +280,21 @@ try {
271280
Import-Module PowerShellEditorServices -Version $parsedVersion -ErrorAction Stop
272281
}
273282

274-
# Locate available port numbers for services
275-
Log "Searching for available socket port for the language service"
276-
$languageServicePort = Get-AvailablePort
283+
# Locate available port numbers for services
284+
# There could be only one service on Stdio channel
277285

278-
Log "Searching for available socket port for the debug service"
279-
$debugServicePort = Get-AvailablePort
286+
$languageServiceTransport = $null
287+
$debugServiceTransport = $null
280288

281-
if (!$languageServicePort -or !$debugServicePort) {
282-
ExitWithError "Failed to find an open socket port for either the language or debug service."
283-
}
289+
if ($Stdio.IsPresent -and -not $DebugServiceOnly.IsPresent) { $languageServiceTransport = "Stdio" }
290+
elseif ($LanguageServicePipeName) { $languageServiceTransport = "NamedPipe"; $languageServicePipeName = "$LanguageServicePipeName" }
291+
elseif ($languageServicePort = Get-AvailablePort) { $languageServiceTransport = "Tcp" }
292+
else { ExitWithError "Failed to find an open socket port for language service." }
293+
294+
if ($Stdio.IsPresent -and $DebugServiceOnly.IsPresent) { $debugServiceTransport = "Stdio" }
295+
elseif ($DebugServicePipeName) { $debugServiceTransport = "NamedPipe"; $debugServicePipeName = "$DebugServicePipeName" }
296+
elseif ($debugServicePort = Get-AvailablePort) { $debugServiceTransport = "Tcp" }
297+
else { ExitWithError "Failed to find an open socket port for debug service." }
284298

285299
if ($EnableConsoleRepl) {
286300
Write-Host "PowerShell Integrated Console`n"
@@ -298,6 +312,9 @@ try {
298312
-AdditionalModules $AdditionalModules `
299313
-LanguageServicePort $languageServicePort `
300314
-DebugServicePort $debugServicePort `
315+
-LanguageServiceNamedPipe $LanguageServicePipeName `
316+
-DebugServiceNamedPipe $DebugServicePipeName `
317+
-Stdio:$Stdio.IsPresent`
301318
-BundledModulesPath $BundledModulesPath `
302319
-EnableConsoleRepl:$EnableConsoleRepl.IsPresent `
303320
-DebugServiceOnly:$DebugServiceOnly.IsPresent `
@@ -308,10 +325,15 @@ try {
308325

309326
$resultDetails = @{
310327
"status" = "started";
311-
"channel" = "tcp";
312-
"languageServicePort" = $languageServicePort;
313-
"debugServicePort" = $debugServicePort;
314-
}
328+
"languageServiceTransport" = $languageServiceTransport;
329+
"debugServiceTransport" = $debugServiceTransport;
330+
};
331+
332+
if ($languageServicePipeName) { $resultDetails["languageServicePipeName"] = "$languageServicePipeName" }
333+
if ($debugServicePipeName) { $resultDetails["debugServicePipeName"] = "$debugServicePipeName" }
334+
335+
if ($languageServicePort) { $resultDetails["languageServicePort"] = $languageServicePort }
336+
if ($debugServicePort) { $resultDetails["debugServicePort"] = $debugServicePort }
315337

316338
# Notify the client that the services have started
317339
WriteSessionFile $resultDetails

src/PowerShellEditorServices.Host/EditorServicesHost.cs

Lines changed: 56 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,25 @@ public enum EditorServicesHostStatus
2929
Ended
3030
}
3131

32+
public enum EditorServiceTransportType
33+
{
34+
Tcp,
35+
NamedPipe,
36+
Stdio
37+
}
38+
39+
public class EditorServiceTransportConfig
40+
{
41+
public EditorServiceTransportType TransportType { get; set; }
42+
/// <summary>
43+
/// Configures the endpoint of the transport.
44+
/// For Tcp it's an integer specifying the port.
45+
/// For Stdio it's ignored.
46+
/// For NamedPipe it's the pipe name.
47+
/// </summary>
48+
public string Endpoint { get; set; }
49+
}
50+
3251
/// <summary>
3352
/// Provides a simplified interface for hosting the language and debug services
3453
/// over the named pipe server protocol.
@@ -48,8 +67,8 @@ public class EditorServicesHost
4867
private HashSet<string> featureFlags;
4968
private LanguageServer languageServer;
5069

51-
private TcpSocketServerListener languageServiceListener;
52-
private TcpSocketServerListener debugServiceListener;
70+
private IServerListener languageServiceListener;
71+
private IServerListener debugServiceListener;
5372

5473
private TaskCompletionSource<bool> serverCompletedTask;
5574

@@ -164,29 +183,25 @@ public void StartLogging(string logFilePath, LogLevel logLevel)
164183
/// </summary>
165184
/// <param name="languageServicePort">The port number for the language service.</param>
166185
/// <param name="profilePaths">The object containing the profile paths to load for this session.</param>
167-
public void StartLanguageService(int languageServicePort, ProfilePaths profilePaths)
186+
public void StartLanguageService(EditorServiceTransportConfig config, ProfilePaths profilePaths)
168187
{
169188
this.profilePaths = profilePaths;
170189

171-
this.languageServiceListener =
172-
new TcpSocketServerListener(
173-
MessageProtocolType.LanguageServer,
174-
languageServicePort,
175-
this.logger);
190+
this.languageServiceListener = CreateServiceListener(MessageProtocolType.LanguageServer, config);
176191

177192
this.languageServiceListener.ClientConnect += this.OnLanguageServiceClientConnect;
178193
this.languageServiceListener.Start();
179194

180195
this.logger.Write(
181196
LogLevel.Normal,
182197
string.Format(
183-
"Language service started, listening on port {0}",
184-
languageServicePort));
198+
"Language service started, type = {0}, endpoint = {1}",
199+
config.TransportType, config.Endpoint));
185200
}
186201

187202
private async void OnLanguageServiceClientConnect(
188203
object sender,
189-
TcpSocketServerChannel serverChannel)
204+
ChannelBase serverChannel)
190205
{
191206
MessageDispatcher messageDispatcher = new MessageDispatcher(this.logger);
192207

@@ -238,27 +253,22 @@ await this.editorSession.PowerShellContext.ImportCommandsModule(
238253
/// </summary>
239254
/// <param name="debugServicePort">The port number for the debug service.</param>
240255
public void StartDebugService(
241-
int debugServicePort,
256+
EditorServiceTransportConfig config,
242257
ProfilePaths profilePaths,
243258
bool useExistingSession)
244259
{
245-
this.debugServiceListener =
246-
new TcpSocketServerListener(
247-
MessageProtocolType.DebugAdapter,
248-
debugServicePort,
249-
this.logger);
250-
260+
this.debugServiceListener = CreateServiceListener(MessageProtocolType.DebugAdapter, config);
251261
this.debugServiceListener.ClientConnect += OnDebugServiceClientConnect;
252262
this.debugServiceListener.Start();
253263

254264
this.logger.Write(
255265
LogLevel.Normal,
256266
string.Format(
257-
"Debug service started, listening on port {0}",
258-
debugServicePort));
267+
"Debug service started, type = {0}, endpoint = {1}",
268+
config.TransportType, config.Endpoint));
259269
}
260270

261-
private void OnDebugServiceClientConnect(object sender, TcpSocketServerChannel serverChannel)
271+
private void OnDebugServiceClientConnect(object sender, ChannelBase serverChannel)
262272
{
263273
MessageDispatcher messageDispatcher = new MessageDispatcher(this.logger);
264274

@@ -441,6 +451,31 @@ private void CurrentDomain_UnhandledException(
441451
e.ExceptionObject.ToString()));
442452
}
443453
#endif
454+
private IServerListener CreateServiceListener(MessageProtocolType protocol, EditorServiceTransportConfig config)
455+
{
456+
switch (config.TransportType)
457+
{
458+
case EditorServiceTransportType.Tcp:
459+
{
460+
return new TcpSocketServerListener(protocol, int.Parse(config.Endpoint), this.logger);
461+
}
462+
463+
case EditorServiceTransportType.Stdio:
464+
{
465+
return new StdioServerListener(protocol, this.logger);
466+
}
467+
468+
case EditorServiceTransportType.NamedPipe:
469+
{
470+
return new NamedPipeServerListener(protocol, config.Endpoint, this.logger);
471+
}
472+
473+
default:
474+
{
475+
throw new NotSupportedException();
476+
}
477+
}
478+
}
444479

445480
#endregion
446481
}

src/PowerShellEditorServices.Protocol/MessageProtocol/Channel/NamedPipeServerListener.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public override void Start()
3838
1,
3939
PipeTransmissionMode.Byte,
4040
PipeOptions.Asynchronous);
41+
ListenForConnection();
4142
}
4243
catch (IOException e)
4344
{

src/PowerShellEditorServices.Protocol/MessageProtocol/Channel/ServerListenerBase.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
namespace Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol.Channel
1010
{
11-
public abstract class ServerListenerBase<TChannel>
11+
public abstract class ServerListenerBase<TChannel> : IServerListener
1212
where TChannel : ChannelBase
1313
{
1414
private MessageProtocolType messageProtocolType;
@@ -22,12 +22,21 @@ public ServerListenerBase(MessageProtocolType messageProtocolType)
2222

2323
public abstract void Stop();
2424

25-
public event EventHandler<TChannel> ClientConnect;
25+
public event EventHandler<ChannelBase> ClientConnect;
2626

2727
protected void OnClientConnect(TChannel channel)
2828
{
2929
channel.Start(this.messageProtocolType);
3030
this.ClientConnect?.Invoke(this, channel);
3131
}
3232
}
33+
34+
public interface IServerListener
35+
{
36+
void Start();
37+
38+
void Stop();
39+
40+
event EventHandler<ChannelBase> ClientConnect;
41+
}
3342
}

src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1362,13 +1362,16 @@ private static FileChange GetFileChangeDetails(Range changeRange, string insertS
13621362
{
13631363
// The protocol's positions are zero-based so add 1 to all offsets
13641364

1365+
if (changeRange == null) return new FileChange { InsertString = insertString, IsReload = true };
1366+
13651367
return new FileChange
13661368
{
13671369
InsertString = insertString,
13681370
Line = changeRange.Start.Line + 1,
13691371
Offset = changeRange.Start.Character + 1,
13701372
EndLine = changeRange.End.Line + 1,
1371-
EndOffset = changeRange.End.Character + 1
1373+
EndOffset = changeRange.End.Character + 1,
1374+
IsReload = false
13721375
};
13731376
}
13741377

src/PowerShellEditorServices/Workspace/FileChange.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,12 @@ public class FileChange
3434
/// The 1-based column offset where the change ends.
3535
/// </summary>
3636
public int EndOffset { get; set; }
37+
38+
/// <summary>
39+
/// Indicates that the InsertString is an overwrite
40+
/// of the content, and all stale content and metadata
41+
/// should be discarded.
42+
/// </summary>
43+
public bool IsReload { get; set; }
3744
}
3845
}

0 commit comments

Comments
 (0)