-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Send events through a single stream #19864
Conversation
installerEventPort: eventPort, | ||
canUseEvents: eventPort === undefined, | ||
eventPort, | ||
canUseEvents: true, | ||
useSingleInferredProject, |
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.
Whats the purpose of setting canUseEvents as true all the time?
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 this change, we can send events regardless of whether eventPort is set or not. If eventPort === undefined
(ie: sending events through stdin/stdout), the behavior is unchanged. But now tsserver can send events through the event port, so if eventPort !== undefined
, we can send events as well.
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 kept the canUseEvents
option available to avoid breaking the public API. As far as I am aware, @anstarovoyt is the only direct consumer of the IoSession API. Perhaps he can comment on whether canUseEvents
is a useful option for him. And if not, we can consider deprecating and eventually removing canUseEvents
.
But we should do this in a separate issue.
src/server/session.ts
Outdated
@@ -1,9 +1,19 @@ | |||
/// <reference types="node" /> |
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.
What types do you need for this? I see you are defining the 'net' module types inline below.
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.
The reference is needed for require
and Buffer
to be defined, used to import "net"
and write events to the socket, respectively.
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.
my 2 cents: session.ts
used to be a abstracted from any host specific bits so it can be used in any environment + it was also very convenient for testing since anything can be mocked. All host specific bits were injected from the outside in iosession.ts
.
I don't see any changes that are sending 'projectInfo' events. Is that not part of this pull request? Also, as discussed, I'm not sure this is the right approach. Why isn't the projectInfo just sent back as part of the response to a request to open or update a project, and the editor can handle it then? This isn't really an 'event'. projectInfo doesn't just happen out of band or complete at some later time. |
I'll answer these in reverse order (since the first question is moot if we'd rather take another approach). Since the event is already used as a mechanism for reporting telemetry in vscode, replacing it with a property on a response would introduce two mechanisms we need to maintain: the (vscode) client needs to be able to process events to get Here are the reasons why
The logic for sending the Note that in VS, In |
@aozgaa All points taken but why is it not that IOSession open the socket and give the information to typing installer and also set the eventHandler for ProjectServiceOptions that sends that event through the socket? |
I tried exposing the This is to say, at the initialization of the |
* need to retain eventPort and canUseEvents arguments and functionality for backwards compatibility.
src/server/session.ts
Outdated
this.event = opts.event; | ||
} | ||
else if (this.eventPort && this.canUseEvents) { | ||
const eventSender = new SocketEventSender(this.host, this.logger, this.eventPort); |
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've retained the initialization of the socket here for backwards compatibility with users initializing Session
with the eventPort
argument. This way, the typings installer will be able to send events through the socket by calling event
on the Session.
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.
Oops, realized that I actually added the eventPort
argument in an earlier commit of this PR, so this is unnecessary. Will fix.
src/server/server.ts
Outdated
protected canUseEvents: boolean) { } | ||
|
||
public event = <T>(body: T, eventName: string) => { | ||
const ev: protocol.Event = { |
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.
It is a bit awkward that this sender duplicates the implementation of Session.prototype.event
.
If someone subclasses Session
and overwrites send
, the event
implementation in Session
will call the overridden send
. This "default" sender will not.
Overall this feels like an imperfect way of factoring out the default behavior. Any suggestions for how to do this better?
Per review with @mhegazy , the problem that led to the extra complexity with the factored-out event-senders is that events are being sent before the constructor finishes. So, I'll instead defer any events that get fired while we are still in the (IO)Session constructor (ie: before the object owning The simplest approach we came up with is to register a callback that fires on the next tick for issuing the offending events (namely, TypingsInstallerPID), and document this requirement. |
Good way to add test would be to create a derived class of Sessions in the test (in src/harness/unittests/tsserverProjectSystem.ts) such that it has this.event overriden and verify that you receive events? |
EDIT: also fixes #19665
We would like to send
projectInfo
events to VS. The stdin/stdout streams for communication between VS and tsserver are necessarily serialized as a parsing optimization in the client (VS). So, we cannot send events to VS through stdin/stdout.*A natural alternative (implemented here) is to send the
projectInfo
event over the event port, which is currently used for typings installer events. To do this, theProjectService
instance needs to be able to issue events through the event port.ProjectService
sends events through aneventHandler
argument which is owned by theSession
instance, so I modified theSession
constructor to pick whether the instance will send events through stdin/stdout (as some editors, eg vscode, take events over stdout) or through the event port.Unless a user of the
Session
API overwrites theeventHandler
argument, all events will now be treated consistently.These changes are backwards-compatible, and require no changes from clients. When running against a tsserver with these changes, I've validated that vscode and VS continue to receive and parse all the events they previously received. In vscode, all messages continue to be sent through stdin/stdout. In VS, I've checked that typingsInstaller events continue to be sent through the event port.**
* Note that vscode does not use this mechanism -- all messages, including events, are sent through stdin/stdout.
** More precisely, typingsInstaller events are sent via stdin/stdout from the typingsInstaller process to tsserver, where they are packaged into tsserver-protocol-compatible events and then sent to the client. The same mechanism is used in the status quo.