@@ -13,6 +13,7 @@ export class VscodeProvider {
13
13
public readonly serverRootPath : string
14
14
public readonly vsRootPath : string
15
15
private _vscode ?: Promise < cp . ChildProcess >
16
+ private timeoutInterval = 10000 // 10s, matches VS Code's timeouts.
16
17
17
18
public constructor ( ) {
18
19
this . vsRootPath = path . resolve ( rootPath , "lib/vscode" )
@@ -52,33 +53,25 @@ export class VscodeProvider {
52
53
const vscode = await this . fork ( )
53
54
54
55
logger . debug ( "setting up vs code..." )
55
- return new Promise < ipc . WorkbenchOptions > ( ( resolve , reject ) => {
56
- const onExit = ( code : number | null ) => reject ( new Error ( `VS Code exited unexpectedly with code ${ code } ` ) )
57
-
58
- vscode . once ( "message" , ( message : ipc . VscodeMessage ) => {
59
- logger . debug ( "got message from vs code" , field ( "message" , message ) )
60
- vscode . off ( "error" , reject )
61
- vscode . off ( "exit" , onExit )
62
- return message . type === "options" && message . id === id
63
- ? resolve ( message . options )
64
- : reject ( new Error ( "Unexpected response during initialization" ) )
65
- } )
66
56
67
- vscode . once ( "error" , reject )
68
- vscode . once ( "exit" , onExit )
69
-
70
- this . send (
71
- {
72
- type : "init" ,
73
- id,
74
- options : {
75
- ...options ,
76
- startPath,
77
- } ,
57
+ this . send (
58
+ {
59
+ type : "init" ,
60
+ id,
61
+ options : {
62
+ ...options ,
63
+ startPath,
78
64
} ,
79
- vscode ,
80
- )
65
+ } ,
66
+ vscode ,
67
+ )
68
+
69
+ const message = await this . onMessage < ipc . OptionsMessage > ( vscode , ( message ) : message is ipc . OptionsMessage => {
70
+ // There can be parallel initializations so wait for the right ID.
71
+ return message . type === "options" && message . id === id
81
72
} )
73
+
74
+ return message . options
82
75
}
83
76
84
77
private fork ( ) : Promise < cp . ChildProcess > {
@@ -88,32 +81,71 @@ export class VscodeProvider {
88
81
89
82
logger . debug ( "forking vs code..." )
90
83
const vscode = cp . fork ( path . join ( this . serverRootPath , "fork" ) )
91
- vscode . on ( "error" , ( error ) => {
92
- logger . error ( error . message )
84
+
85
+ const dispose = ( ) => {
86
+ vscode . removeAllListeners ( )
87
+ vscode . kill ( )
93
88
this . _vscode = undefined
89
+ }
90
+
91
+ vscode . on ( "error" , ( error : Error ) => {
92
+ logger . error ( error . message )
93
+ if ( error . stack ) {
94
+ logger . debug ( error . stack )
95
+ }
96
+ dispose ( )
94
97
} )
98
+
95
99
vscode . on ( "exit" , ( code ) => {
96
100
logger . error ( `VS Code exited unexpectedly with code ${ code } ` )
97
- this . _vscode = undefined
101
+ dispose ( )
98
102
} )
99
103
100
- this . _vscode = new Promise ( ( resolve , reject ) => {
101
- const onExit = ( code : number | null ) => reject ( new Error ( `VS Code exited unexpectedly with code ${ code } ` ) )
104
+ this . _vscode = this . onMessage < ipc . ReadyMessage > ( vscode , ( message ) : message is ipc . ReadyMessage => {
105
+ return message . type === "ready"
106
+ } ) . then ( ( ) => vscode )
102
107
103
- vscode . once ( "message" , ( message : ipc . VscodeMessage ) => {
104
- logger . debug ( "got message from vs code" , field ( "message" , message ) )
105
- vscode . off ( "error" , reject )
106
- vscode . off ( "exit" , onExit )
107
- return message . type === "ready"
108
- ? resolve ( vscode )
109
- : reject ( new Error ( "Unexpected response waiting for ready response" ) )
108
+ return this . _vscode
109
+ }
110
+
111
+ /**
112
+ * Listen to a single message from a process. Reject if the process errors,
113
+ * exits, or times out.
114
+ *
115
+ * `fn` is a function that determines whether the message is the one we're
116
+ * waiting for.
117
+ */
118
+ private onMessage < T extends ipc . VscodeMessage > (
119
+ proc : cp . ChildProcess ,
120
+ fn : ( message : ipc . VscodeMessage ) => message is T ,
121
+ ) : Promise < T > {
122
+ return new Promise ( ( resolve , _reject ) => {
123
+ const reject = ( error : Error ) => {
124
+ clearTimeout ( timeout )
125
+ _reject ( error )
126
+ }
127
+
128
+ const onExit = ( code : number | null ) => {
129
+ reject ( new Error ( `VS Code exited unexpectedly with code ${ code } ` ) )
130
+ }
131
+
132
+ const timeout = setTimeout ( ( ) => {
133
+ reject ( new Error ( "timed out" ) )
134
+ } , this . timeoutInterval )
135
+
136
+ proc . on ( "message" , ( message : ipc . VscodeMessage ) => {
137
+ logger . debug ( "got message from vscode" , field ( "message" , message ) )
138
+ proc . off ( "error" , reject )
139
+ proc . off ( "exit" , onExit )
140
+ if ( fn ( message ) ) {
141
+ clearTimeout ( timeout )
142
+ resolve ( message )
143
+ }
110
144
} )
111
145
112
- vscode . once ( "error" , reject )
113
- vscode . once ( "exit" , onExit )
146
+ proc . once ( "error" , reject )
147
+ proc . once ( "exit" , onExit )
114
148
} )
115
-
116
- return this . _vscode
117
149
}
118
150
119
151
/**
0 commit comments