@@ -2,6 +2,7 @@ import process from "process";
22import * as p from "vscode-languageserver-protocol" ;
33import * as m from "vscode-jsonrpc/lib/messages" ;
44import * as v from "vscode-languageserver" ;
5+ import * as rpc from "vscode-jsonrpc" ;
56import * as path from "path" ;
67import fs from "fs" ;
78// TODO: check DidChangeWatchedFilesNotification.
@@ -39,6 +40,9 @@ let projectsFiles: Map<
3940> = new Map ( ) ;
4041// ^ caching AND states AND distributed system. Why does LSP has to be stupid like this
4142
43+ // will be properly defined later depending on the mode (stdio/node-rpc)
44+ let send : ( msg : m . Message ) => void = ( _ ) => { } ;
45+
4246let sendUpdatedDiagnostics = ( ) => {
4347 projectsFiles . forEach ( ( { filesWithDiagnostics } , projectRootPath ) => {
4448 let content = fs . readFileSync (
@@ -60,7 +64,7 @@ let sendUpdatedDiagnostics = () => {
6064 method : "textDocument/publishDiagnostics" ,
6165 params : params ,
6266 } ;
63- process . send ! ( notification ) ;
67+ send ( notification ) ;
6468
6569 filesWithDiagnostics . add ( file ) ;
6670 } ) ;
@@ -78,7 +82,7 @@ let sendUpdatedDiagnostics = () => {
7882 method : "textDocument/publishDiagnostics" ,
7983 params : params ,
8084 } ;
81- process . send ! ( notification ) ;
85+ send ( notification ) ;
8286 filesWithDiagnostics . delete ( file ) ;
8387 }
8488 } ) ;
@@ -98,7 +102,7 @@ let deleteProjectDiagnostics = (projectRootPath: string) => {
98102 method : "textDocument/publishDiagnostics" ,
99103 params : params ,
100104 } ;
101- process . send ! ( notification ) ;
105+ send ( notification ) ;
102106 } ) ;
103107
104108 projectsFiles . delete ( projectRootPath ) ;
@@ -167,7 +171,7 @@ let openedFile = (fileUri: string, fileContent: string) => {
167171 method : "window/showMessageRequest" ,
168172 params : params ,
169173 } ;
170- process . send ! ( request ) ;
174+ send ( request ) ;
171175 // the client might send us back the "start build" action, which we'll
172176 // handle in the isResponseMessage check in the message handling way
173177 // below
@@ -216,7 +220,22 @@ let getOpenedFileContent = (fileUri: string) => {
216220 return content ;
217221} ;
218222
219- process . on ( "message" , ( msg : m . Message ) => {
223+ // Start listening now!
224+ // We support two modes: the regular node RPC mode for VSCode, and the --stdio
225+ // mode for other editors The latter is _technically unsupported_. It's an
226+ // implementation detail that might change at any time
227+ if ( process . argv . includes ( "--stdio" ) ) {
228+ let writer = new rpc . StreamMessageWriter ( process . stdout ) ;
229+ let reader = new rpc . StreamMessageReader ( process . stdin ) ;
230+ // proper `this` scope for writer
231+ send = ( msg : m . Message ) => writer . write ( msg ) ;
232+ reader . listen ( onMessage ) ;
233+ } else {
234+ // proper `this` scope for process
235+ send = ( msg : m . Message ) => process . send ! ( msg ) ;
236+ process . on ( "message" , onMessage ) ;
237+ }
238+ function onMessage ( msg : m . Message ) {
220239 if ( m . isNotificationMessage ( msg ) ) {
221240 // notification message, aka the client ends it and doesn't want a reply
222241 if ( ! initialized && msg . method !== "exit" ) {
@@ -266,7 +285,7 @@ process.on("message", (msg: m.Message) => {
266285 message : "Server not initialized." ,
267286 } ,
268287 } ;
269- process . send ! ( response ) ;
288+ send ( response ) ;
270289 } else if ( msg . method === "initialize" ) {
271290 // send the list of features we support
272291 let result : p . InitializeResult = {
@@ -290,15 +309,15 @@ process.on("message", (msg: m.Message) => {
290309 result : result ,
291310 } ;
292311 initialized = true ;
293- process . send ! ( response ) ;
312+ send ( response ) ;
294313 } else if ( msg . method === "initialized" ) {
295314 // sent from client after initialize. Nothing to do for now
296315 let response : m . ResponseMessage = {
297316 jsonrpc : c . jsonrpcVersion ,
298317 id : msg . id ,
299318 result : null ,
300319 } ;
301- process . send ! ( response ) ;
320+ send ( response ) ;
302321 } else if ( msg . method === "shutdown" ) {
303322 // https://microsoft.github.io/language-server-protocol/specification#shutdown
304323 if ( shutdownRequestAlreadyReceived ) {
@@ -310,7 +329,7 @@ process.on("message", (msg: m.Message) => {
310329 message : `Language server already received the shutdown request` ,
311330 } ,
312331 } ;
313- process . send ! ( response ) ;
332+ send ( response ) ;
314333 } else {
315334 shutdownRequestAlreadyReceived = true ;
316335 // TODO: recheck logic around init/shutdown...
@@ -322,7 +341,7 @@ process.on("message", (msg: m.Message) => {
322341 id : msg . id ,
323342 result : null ,
324343 } ;
325- process . send ! ( response ) ;
344+ send ( response ) ;
326345 }
327346 } else if ( msg . method === p . HoverRequest . method ) {
328347 let emptyHoverResponse : m . ResponseMessage = {
@@ -338,9 +357,9 @@ process.on("message", (msg: m.Message) => {
338357 ...emptyHoverResponse ,
339358 result : { contents : result . hover } ,
340359 } ;
341- process . send ! ( hoverResponse ) ;
360+ send ( hoverResponse ) ;
342361 } else {
343- process . send ! ( emptyHoverResponse ) ;
362+ send ( emptyHoverResponse ) ;
344363 }
345364 } else if ( msg . method === p . DefinitionRequest . method ) {
346365 // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_definition
@@ -361,9 +380,9 @@ process.on("message", (msg: m.Message) => {
361380 range : result . definition . range ,
362381 } ,
363382 } ;
364- process . send ! ( definitionResponse ) ;
383+ send ( definitionResponse ) ;
365384 } else {
366- process . send ! ( emptyDefinitionResponse ) ;
385+ send ( emptyDefinitionResponse ) ;
367386 }
368387 } else if ( msg . method === p . CompletionRequest . method ) {
369388 let emptyCompletionResponse : m . ResponseMessage = {
@@ -374,13 +393,13 @@ process.on("message", (msg: m.Message) => {
374393 let code = getOpenedFileContent ( msg . params . textDocument . uri ) ;
375394 let result = runCompletionCommand ( msg , code ) ;
376395 if ( result === null ) {
377- process . send ! ( emptyCompletionResponse ) ;
396+ send ( emptyCompletionResponse ) ;
378397 } else {
379398 let definitionResponse : m . ResponseMessage = {
380399 ...emptyCompletionResponse ,
381400 result : result ,
382401 } ;
383- process . send ! ( definitionResponse ) ;
402+ send ( definitionResponse ) ;
384403 }
385404 } else if ( msg . method === p . DocumentFormattingRequest . method ) {
386405 // technically, a formatting failure should reply with the error. Sadly
@@ -409,8 +428,8 @@ process.on("message", (msg: m.Message) => {
409428 method : "window/showMessage" ,
410429 params : params ,
411430 } ;
412- process . send ! ( fakeSuccessResponse ) ;
413- process . send ! ( response ) ;
431+ send ( fakeSuccessResponse ) ;
432+ send ( response ) ;
414433 } else {
415434 // See comment on findBscExeDirOfFile for why we need
416435 // to recursively search for bsc.exe upward
@@ -425,8 +444,8 @@ process.on("message", (msg: m.Message) => {
425444 method : "window/showMessage" ,
426445 params : params ,
427446 } ;
428- process . send ! ( fakeSuccessResponse ) ;
429- process . send ! ( response ) ;
447+ send ( fakeSuccessResponse ) ;
448+ send ( response ) ;
430449 } else {
431450 let resolvedBscExePath = path . join ( bscExeDir , c . bscExePartialPath ) ;
432451 // code will always be defined here, even though technically it can be undefined
@@ -454,13 +473,13 @@ process.on("message", (msg: m.Message) => {
454473 id : msg . id ,
455474 result : result ,
456475 } ;
457- process . send ! ( response ) ;
476+ send ( response ) ;
458477 } else {
459478 // let the diagnostics logic display the updated syntax errors,
460479 // from the build.
461480 // Again, not sending the actual errors. See fakeSuccessResponse
462481 // above for explanation
463- process . send ! ( fakeSuccessResponse ) ;
482+ send ( fakeSuccessResponse ) ;
464483 }
465484 }
466485 }
@@ -473,7 +492,7 @@ process.on("message", (msg: m.Message) => {
473492 message : "Unrecognized editor request." ,
474493 } ,
475494 } ;
476- process . send ! ( response ) ;
495+ send ( response ) ;
477496 }
478497 } else if ( m . isResponseMessage ( msg ) ) {
479498 // response message. Currently the client should have only sent a response
@@ -505,4 +524,4 @@ process.on("message", (msg: m.Message) => {
505524 }
506525 }
507526 }
508- } ) ;
527+ }
0 commit comments