-
Notifications
You must be signed in to change notification settings - Fork 235
EditorServiceHost: allow Tcp/NamedPipe/Stdio listeners #629
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
oops, looks like some tests are failing? looking into it. |
AppVeyor timeout? |
Thanks for this! Would you mind supplying the steps you used to verify/test this? I'm interested in the "Stdio server from outside" part. |
@tylerl0706 check this out: https://github.com/v-yadli/vimrc/blob/nvim/init.vim here are the steps with my vimrc noise cancelled:
"LanguageServerProtocol setup
"required for operations modifying multiple buffers like rename.
set hidden
"setup startup sequences...
let g:LanguageClient_serverCommands = {
\ 'haskell': ['hie', '--lsp'],
\ 'python': ['pyls'],
\ 'cs': ['~\.omnisharp\OmniSharp.exe'],
\ 'ps1': ['powershell', '~\git\PowerShellEditorServices\module\Start-EditorServices.ps1', '-HostName', 'nvim', '-HostProfileId', '0', '-HostVersion', '1.0.0', '-EditorServicesVersion', '1.6.0', '-LogPath', 'pses.log.txt', '-LogLevel', 'Normal', '-BundledModulesPath', '~\git\PowerShellEditorServices\module', '-Stdio'],
\ }
|
This is only partially working at the moment, from pses log I see some null reference exceptions while trying to do completion, and handling document change events. Still looking into it but I think that's another issue. |
…amed pipe on start.
Fixed it. Not a lot of changes so I guess we can merge it altogether? |
@daviwil can you look at this when you get a chance? I remember you saying that you had comments. |
Oh and FWIW, I was able to get NeoVim working with PSES using this PR. 🎉 🎉 |
@yatli @tylerl0706 Just wanted to say that this PR makes PSES work like a charm with Sublime Text 3 LSP plugin https://github.com/tomv564/LSP 🎉 😄 I have used following {
"clients":
{
"poshls":
{
"command": [
"powershell",
"~\\Git\\PowerShellEditorServices\\module\\Start-EditorServices.ps1",
"-HostName", "nvim",
"-HostProfileId", "0",
"-HostVersion", "1.0.0",
"-EditorServicesVersion", "1.6.0",
"-LogPath", "~\\Git\\PowerShellEditorServices\\pses.log.txt",
"-LogLevel", "Normal",
"-BundledModulesPath", "~\\Git\\PowerShellEditorServices\\module",
"-Stdio"
],
"scopes": ["source.powershell"],
"syntaxes": ["Packages/PowerShell/Support/PowershellSyntax.tmLanguage"],
"languageId": "powershell"
}
}
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really awesome. Thank you so much for working on this! This is a big step towards getting a whole lot more PSES implementations :)
Just a couple of nit picks, mostly on the PowerShell side.
[int] | ||
$DebugServicePort, | ||
|
||
[bool] | ||
$Stdio, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make this parameter a [switch]
parameter instead of [bool]
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, might be better to change name to UseStdio
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@SeeminglyScience I tried to make it a switch as the outer starting script does, but I don't know how to relay the switch in without splitting the command into two, one with the switch and one without. Sorry I'm still learning my way on powershell 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typically it is -InnerSwitch:$OuterSwitch
$LanguageServiceNamedPipe, | ||
|
||
[string] | ||
$DebugServiceNamedPipe, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could the different connection types be separate parameter sets? For example
[Parameter(Mandatory, ParameterSetName='ByTcpPort')]
[ValidateNotNull()]
[int]
$LanguageServicePort,
[Parameter(Mandatory, ParameterSetName='ByTcpPort')]
[ValidateNotNull()]
[int]
$DebugServicePort,
[Parameter(Mandatory, ParameterSetName='ByNamedPipe')]
[ValidateNotNullOrEmpty()]
[string]
$LanguageServiceNamedPipe,
[Parameter(Mandatory, ParameterSetName='ByNamedPipe')]
[ValidateNotNullOrEmpty()]
[string]
$DebugServiceNamePipe,
# etc...
if ($DebugServiceNamedPipe) { | ||
$debugServiceConfig.TransportType = [Microsoft.PowerShell.EditorServices.Host.EditorServiceTransportType]::NamedPipe | ||
$debugServiceConfig.Endpoint = "$DebugServiceNamedPipe" | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With parameter sets this would work nicely as a switch statement
switch ($PSCmdlet.ParameterSetName) {
ByTcp {
$languageServiceConfig.TransportType = $debugServiceConfig.TransportType =
Microsoft.PowerShell.EditorServices.Host.EditorServiceTransportType]::Tcp
$languageServiceConfig.Endpoint = $LanguageServicePort
$debugServiceConfig.Endpoint = $DebugServicePort
}
ByNamedPipe {
$languageServiceConfig.TransportType = $debugServiceConfig.TransportType =
[Microsoft.PowerShell.EditorServices.Host.EditorServiceTransportType]::NamedPipe
$languageServiceConfig.Endpoint = $LanguageServiceNamedPipe
$debugServiceConfig.Endpoint = $DebugServiceNamedPipe
}
# etc...
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ooo that's nice 😍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree with @SeeminglyScience but if you'd prefer we can add the parameter sets after this gets merged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah this is totally cool!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just thought about this for a while.. A few days ago I was debugging a problem when both language service and debug service are talking to stdio and the channel gets too crowded. In fact these two should never sit on the same stdio train. :)
module/Start-EditorServices.ps1
Outdated
-BundledModulesPath $BundledModulesPath ` | ||
-DebugServiceOnly:$DebugServiceOnly.IsPresent` | ||
-WaitForDebugger:$WaitForDebugger.IsPresent |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assuming the switch to parameter sets, this would need to be switched to a splat (probably should anyway to fit common community style guides)
$splat = @{
AdditionalModules = @()
WaitForDebugger = $WaitForDebugger.IsPresent
# etc...
}
switch ($PSCmdlet.ParameterSetName) {
ByTcp {
$splat.LanguageServicePort = $languageServicePort
# etc...
}
}
$editorServicesHost = Start-EditorServicesHost @splat
/// of the content, and all stale content and metadata | ||
/// should be discarded. | ||
/// </summary> | ||
public bool Reloaded { get; set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one is really nit picky, but could we change this member to IsReload
Everything looks great! I'll give it a really good look later today but wanted to ask if you could fix this PSModulePath casing as a part of your PR: Totally optional but figured I'd ask since you're already touching the file. |
@yatli just curious, did you have an example that uses named pipes? 😄 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree with all of @SeeminglyScience's comments :) other than those, LGTM!
Super excited to get this in 😄
|
||
if ($DebugServiceOnly.IsPresent) { | ||
$editorServicesHost.StartDebugService($debugServiceConfig, $profilePaths, $false); | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a huge thing but the script style for this file doesn't use cuddled else. Again, something we can tweak after merging.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. There are some tweaks that could be done but like I mention in my comments, we could do those after merging.
@tylerl0706 nak, I just tested it working with a dummy client, with captured payload from another run :D |
Digging gitter chat history, I remember we discussed something about it before |
ok, got it, |
that said, we should still fix the test failures before merging. We also should verify that this PSES still works in VSCode (it should, but just in case), but I can test that for you 🙂 |
understood ^ ^ |
I think I broke this one: JObject result = JObject.Parse(sessionDetailsText);
if (result["status"].Value<string>() == "started")
{
return new Tuple<int, int>(
result["languageServicePort"].Value<int>(),
result["debugServicePort"].Value<int>());
} but now, the details carry my modified info like this: {"status":"started","debugServiceTransport":"Tcp port 28157","languageServiceTransport":"Tcp port 14630"} |
Ah I see... would a structure like this make sense: {
"status":"started",
"debugServiceTransport":"Tcp",
"languageServiceTransport":"Tcp",
"languageServicePort":1234,
"debugServicePort":1234
} where the ports are "optional" basically That way it's also back-compat and clients that use TCP don't have to do string manipulation to get the port values. |
I'm on this. A few seconds... |
oh... my... |
so I think this might be something that needs to be handled client side... because it's really the client that's spinning up the instance of PowerShell that runs in the case of NeoVim, adding something like this on exit?
which I got from this. |
I still wish we could make PSES smart enough to realize the client has disconnected and stop itself but I can't think of a good way to do that at the moment. |
@tylerl0706 it passes the tests locally now. let's wait and see. :) |
No worries, @yatli! That's awesome 😄 Do you have any thoughts on how to address the extra processes? |
@tylerl0706 I think this is mostly a problem of the language client plugin and it should be addressed over there. :) isn't there a shutdown command in the LSP? |
@yatli sorry to differ, but current behavior is simply far what UNIX pipe programs are. I know I know "posh" comes from Windows world, but imagine you would need to send When client dies or closes the pipe, or whatever happens with the channel, there is NO way to send shutdown command anymore, since it is single pipe, once closed it is gone forever. It is not a TCP you can reconnect etc. etc. So regardless if this will be addressed in VIM, expect stray processed anyway. I think this should be addressed either as a part of this patch or soon after as it is strongly related. |
@nanoant I completely agree with you on the part that Also, I'm very curious about how this thing works in VSCode -- because it looks like VSCode is connecting to PSES by two tcp ports, and how is PSES shut down when VSCode decides to do so? -- that's how I come to the conclusion that, So, like I said earlier in this thread, I do think we need to think about the termination problem -- it's not just about stdio, and all clients should expect a common practice for doing so. btw in my current setting, my vim lsp client totally ignores PSES debug port. So there'd be a thread waiting for a connection in their house. |
Ok everyone, I did some research and here's what I've found: Right now, the client kills the server process. The proof can be found here in the VSCode client: https://github.com/PowerShell/vscode-powershell/blob/master/src/process.ts#L145-L161 That said, this is not a good thing. You might be wondering... The LSP has both a The answer is yes.......... but it's functionally a no-op 😢 You can see the which is referring to this TaskCompletionSource that is causing the process to stay open: PowerShellEditorServices/src/PowerShellEditorServices.Host/EditorServicesHost.cs Line 341 in 4716b7f
So what's the plan? Since we currently rely on the client to kill the server, we should call that "out of scope" of this PR. I'll open up a new issue with priority (that I will address ASAP) that actually implements these messages correctly. P.s. big thanks to @SeeminglyScience for helping me investigate this! 😄 |
P.S. I verified that the latest changes work with VSCode 🎉 |
I've had a look through everything and this all looks really cool. Haven't had a chance to run the code yet, but will try to this weekend. On the PSES shutdown thing: is there a way for us to keep track of how many clients we are serving and then shutdown when it goes to zero (like a Windows named pipe)? |
Just for information's sake, this should only be 1 or 0. 2 clients cannot share an instance of PSES because a single runspace is being managed in each PSES session.
I believe that's only possible by using a heartbeat 😟 I'm not a huge fan of that idea, but even if we wanted to do that, I'd still call that out of scope of this PR IMO. |
Definitely out of scope for this PR, yes.
So does that mean that for a single PSES process there can only be one client? So the problem is that a client connects to PSES over multiple streams and we don't have a good answer for what to do when those streams break? That's how I'm interpreting @yatli's points about multiplexed input. My question is, do all our proposed streams guarantee connection? I know other protocols have heartbeat messages, but the concept of a connection-oriented transport layer in my mind means we get told by the transport layer below us when a connection is broken or closed. When all connections to PSES are closed, we should just wrap up. Generally the client should give that command, but they can do it with a |
@tylerl0706 Thanks. I am okay to continue the shutdown discussion at #655 and have this merged. |
PSES 1.7.0 is released with this change! https://github.com/PowerShell/PowerShellEditorServices/releases/tag/v1.7.0 You should be able to download this zip and get it to work with your LSP client of choice |
Refactored listeners and channels to be a bit more generic. The auxiliary scripts are also updated to support for example, Stdio server from outside.