@@ -272,7 +272,7 @@ namespace ts.server {
272272 readonly typingSafeListLocation : string ,
273273 readonly typesMapLocation : string ,
274274 private readonly npmLocation : string | undefined ,
275- private eventSender : EventSender ) {
275+ private event : Event ) {
276276 }
277277
278278 isKnownTypesPackageName ( name : string ) : boolean {
@@ -304,8 +304,8 @@ namespace ts.server {
304304 if ( this . installerPidReported ) {
305305 return ;
306306 }
307- if ( this . eventSender && this . installer ) {
308- this . eventSender . event ( { pid : this . installer . pid } , "typingsInstallerPid" ) ;
307+ if ( this . event && this . installer ) {
308+ this . event ( { pid : this . installer . pid } , "typingsInstallerPid" ) ;
309309 this . installerPidReported = true ;
310310 }
311311 }
@@ -416,32 +416,32 @@ namespace ts.server {
416416 }
417417 case EventInitializationFailed :
418418 {
419- if ( ! this . eventSender ) {
419+ if ( ! this . event ) {
420420 break ;
421421 }
422422 const body : protocol . TypesInstallerInitializationFailedEventBody = {
423423 message : response . message
424424 } ;
425425 const eventName : protocol . TypesInstallerInitializationFailedEventName = "typesInstallerInitializationFailed" ;
426- this . eventSender . event ( body , eventName ) ;
426+ this . event ( body , eventName ) ;
427427 break ;
428428 }
429429 case EventBeginInstallTypes :
430430 {
431- if ( ! this . eventSender ) {
431+ if ( ! this . event ) {
432432 break ;
433433 }
434434 const body : protocol . BeginInstallTypesEventBody = {
435435 eventId : response . eventId ,
436436 packages : response . packagesToInstall ,
437437 } ;
438438 const eventName : protocol . BeginInstallTypesEventName = "beginInstallTypes" ;
439- this . eventSender . event ( body , eventName ) ;
439+ this . event ( body , eventName ) ;
440440 break ;
441441 }
442442 case EventEndInstallTypes :
443443 {
444- if ( ! this . eventSender ) {
444+ if ( ! this . event ) {
445445 break ;
446446 }
447447 if ( this . telemetryEnabled ) {
@@ -454,7 +454,7 @@ namespace ts.server {
454454 }
455455 } ;
456456 const eventName : protocol . TelemetryEventName = "telemetry" ;
457- this . eventSender . event ( body , eventName ) ;
457+ this . event ( body , eventName ) ;
458458 }
459459
460460 const body : protocol . EndInstallTypesEventBody = {
@@ -463,7 +463,7 @@ namespace ts.server {
463463 success : response . installSuccess ,
464464 } ;
465465 const eventName : protocol . EndInstallTypesEventName = "endInstallTypes" ;
466- this . eventSender . event ( body , eventName ) ;
466+ this . event ( body , eventName ) ;
467467 break ;
468468 }
469469 case ActionInvalidate :
@@ -495,8 +495,8 @@ namespace ts.server {
495495
496496 this . projectService . updateTypingsForProject ( response ) ;
497497
498- if ( this . eventSender ) {
499- this . eventSender . event ( response , "setTypings" ) ;
498+ if ( this . event ) {
499+ this . event ( response , "setTypings" ) ;
500500 }
501501
502502 break ;
@@ -515,72 +515,49 @@ namespace ts.server {
515515 }
516516 }
517517
518- export class DefaultEventSender implements EventSender {
519- constructor ( protected host : ServerHost ,
520- protected byteLength : ( buf : string , encoding ?: string ) => number ,
521- protected logger : Logger ,
522- protected canUseEvents : boolean ) { }
523-
524- public event = < T > ( body : T , eventName : string ) => {
525- const ev : protocol . Event = {
526- seq : 0 ,
527- type : "event" ,
528- event : eventName ,
529- body
530- } ;
531- defaultSend ( this . host , this . byteLength , this . logger , this . canUseEvents , ev ) ;
532- }
533- }
518+ // export class DefaultEventSender implements EventSender {
519+ // constructor(protected host: ServerHost,
520+ // protected byteLength: (buf: string, encoding?: string) => number,
521+ // protected logger: Logger,
522+ // protected canUseEvents: boolean) { }
523+
524+ // public event = <T>(body: T, eventName: string) => {
525+ // const ev: protocol.Event = {
526+ // seq: 0,
527+ // type: "event",
528+ // event: eventName,
529+ // body
530+ // };
531+ // defaultSend(this.host, this.byteLength, this.logger, this.canUseEvents, ev);
532+ // }
533+ // }
534534
535- class SocketEventSender implements EventSender {
535+ class IOSession extends Session {
536+ private eventPort : number ;
536537 private eventSocket : NodeSocket | undefined ;
537538 private socketEventQueue : { body : any , eventName : string } [ ] | undefined ;
539+ private constructed : boolean | undefined ;
538540
539- constructor ( private host : ServerHost ,
540- private byteLength : ( buf : string , encoding ?: string ) => number ,
541- private logger : Logger ,
542- private eventPort : number ) {
543-
544- const s = net . connect ( { port : this . eventPort } , ( ) => {
545- this . eventSocket = s ;
546- if ( this . socketEventQueue ) {
547- // flush queue.
548- for ( const event of this . socketEventQueue ) {
549- this . writeToEventSocket ( event . body , event . eventName ) ;
550- }
551- this . socketEventQueue = undefined ;
552- }
553- } ) ;
554- }
555-
556- public event = < T > ( body : T , eventName : string ) => {
557- if ( ! this . eventSocket ) {
558- if ( this . logger . hasLevel ( LogLevel . verbose ) ) {
559- this . logger . info ( `eventPort: event "${ eventName } " queued, but socket not yet initialized` ) ;
560- }
561- ( this . socketEventQueue || ( this . socketEventQueue = [ ] ) ) . push ( { body, eventName } ) ;
562- return ;
563- }
564- else {
565- Debug . assert ( this . socketEventQueue === undefined ) ;
566- this . writeToEventSocket ( body , eventName ) ;
567- }
568- } ;
569-
570- private writeToEventSocket ( body : any , eventName : string ) : void {
571- this . eventSocket . write ( formatMessage ( { seq : 0 , type : "event" , event : eventName , body } , this . logger , this . byteLength , this . host . newLine ) , "utf8" ) ;
572- }
573- }
574-
575- class IOSession extends Session {
576541 constructor ( options : IoSessionOptions ) {
577542 const { host, eventPort, globalTypingsCacheLocation, typingSafeListLocation, typesMapLocation, npmLocation, canUseEvents } = options ;
578543
579- const eventSender = eventPort && canUseEvents ? new SocketEventSender ( host , Buffer . byteLength , logger , eventPort ) : new DefaultEventSender ( host , Buffer . byteLength , logger , canUseEvents ) ;
544+ const event : Event | undefined = canUseEvents ?
545+ ( body : { } , eventName : string ) => {
546+ if ( this . constructed ) {
547+ this . event ( body , eventName ) ;
548+ }
549+ else {
550+ // It is unsafe to dereference `this` before initialization completes,
551+ // so we defer until the next tick.
552+ //
553+ // Construction should finish before the next tick fires, so we do not need to do this recursively.
554+ setImmediate ( ( ) => this . event ( body , eventName ) ) ;
555+ }
556+ } : undefined ;
580557
581558 const typingsInstaller = disableAutomaticTypingAcquisition
582559 ? undefined
583- : new NodeTypingsInstaller ( telemetryEnabled , logger , host , globalTypingsCacheLocation , typingSafeListLocation , typesMapLocation , npmLocation , eventSender ) ;
560+ : new NodeTypingsInstaller ( telemetryEnabled , logger , host , globalTypingsCacheLocation , typingSafeListLocation , typesMapLocation , npmLocation , event ) ;
584561
585562 super ( {
586563 host,
@@ -592,10 +569,51 @@ namespace ts.server {
592569 hrtime : process . hrtime ,
593570 logger,
594571 canUseEvents,
595- eventSender,
596572 globalPlugins : options . globalPlugins ,
597573 pluginProbeLocations : options . pluginProbeLocations ,
598- allowLocalPluginLoads : options . allowLocalPluginLoads } ) ;
574+ allowLocalPluginLoads : options . allowLocalPluginLoads
575+ } ) ;
576+
577+ this . eventPort = eventPort ;
578+ if ( this . canUseEvents && this . eventPort ) {
579+ const s = net . connect ( { port : this . eventPort } , ( ) => {
580+ this . eventSocket = s ;
581+ if ( this . socketEventQueue ) {
582+ // flush queue.
583+ for ( const event of this . socketEventQueue ) {
584+ this . writeToEventSocket ( event . body , event . eventName ) ;
585+ }
586+ this . socketEventQueue = undefined ;
587+ }
588+ } ) ;
589+ }
590+
591+ this . constructed = true ;
592+ }
593+
594+ event < T > ( body : T , eventName : string ) : void {
595+ Debug . assert ( this . constructed , "Should only call `IOSession.prototype.event` on an initialized IOSession" ) ;
596+
597+ if ( this . canUseEvents && this . eventPort ) {
598+ if ( ! this . eventSocket ) {
599+ if ( this . logger . hasLevel ( LogLevel . verbose ) ) {
600+ this . logger . info ( `eventPort: event "${ eventName } " queued, but socket not yet initialized` ) ;
601+ }
602+ ( this . socketEventQueue || ( this . socketEventQueue = [ ] ) ) . push ( { body, eventName } ) ;
603+ return ;
604+ }
605+ else {
606+ Debug . assert ( this . socketEventQueue === undefined ) ;
607+ this . writeToEventSocket ( body , eventName ) ;
608+ }
609+ }
610+ else {
611+ super . event ( body , eventName ) ;
612+ }
613+ }
614+
615+ private writeToEventSocket ( body : any , eventName : string ) : void {
616+ this . eventSocket . write ( formatMessage ( { seq : 0 , type : "event" , event : eventName , body } , this . logger , this . byteLength , this . host . newLine ) , "utf8" ) ;
599617 }
600618
601619 exit ( ) {
0 commit comments