Skip to content

Commit 3bd33cd

Browse files
jjleechenglou
authored andcommitted
Add --stdio switch
motivated by LSP clients such as emacs lsp-mode.el that don't support communication over node IPC
1 parent 8113017 commit 3bd33cd

File tree

1 file changed

+50
-23
lines changed

1 file changed

+50
-23
lines changed

server/src/server.ts

Lines changed: 50 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import process from "process";
22
import * as p from "vscode-languageserver-protocol";
33
import * as m from "vscode-jsonrpc/lib/messages";
44
import * as v from "vscode-languageserver";
5+
import * as rpc from "vscode-jsonrpc";
56
import * as path from "path";
67
import fs from "fs";
78
// TODO: check DidChangeWatchedFilesNotification.
@@ -39,6 +40,8 @@ let projectsFiles: Map<
3940
> = new Map();
4041
// ^ caching AND states AND distributed system. Why does LSP has to be stupid like this
4142

43+
let messageSender: any = {send: (msg: m.Message) => process.send!(msg)};
44+
4245
let sendUpdatedDiagnostics = () => {
4346
projectsFiles.forEach(({ filesWithDiagnostics }, projectRootPath) => {
4447
let content = fs.readFileSync(
@@ -60,7 +63,7 @@ let sendUpdatedDiagnostics = () => {
6063
method: "textDocument/publishDiagnostics",
6164
params: params,
6265
};
63-
process.send!(notification);
66+
messageSender.send(notification);
6467

6568
filesWithDiagnostics.add(file);
6669
});
@@ -78,7 +81,7 @@ let sendUpdatedDiagnostics = () => {
7881
method: "textDocument/publishDiagnostics",
7982
params: params,
8083
};
81-
process.send!(notification);
84+
messageSender.send(notification);
8285
filesWithDiagnostics.delete(file);
8386
}
8487
});
@@ -98,7 +101,7 @@ let deleteProjectDiagnostics = (projectRootPath: string) => {
98101
method: "textDocument/publishDiagnostics",
99102
params: params,
100103
};
101-
process.send!(notification);
104+
messageSender.send(notification);
102105
});
103106

104107
projectsFiles.delete(projectRootPath);
@@ -167,7 +170,7 @@ let openedFile = (fileUri: string, fileContent: string) => {
167170
method: "window/showMessageRequest",
168171
params: params,
169172
};
170-
process.send!(request);
173+
messageSender.send(request);
171174
// the client might send us back the "start build" action, which we'll
172175
// handle in the isResponseMessage check in the message handling way
173176
// below
@@ -216,7 +219,31 @@ let getOpenedFileContent = (fileUri: string) => {
216219
return content;
217220
};
218221

219-
process.on("message", (msg: m.Message) => {
222+
let onMessage = (func: any) => {
223+
process.on("message", (msg: m.Message) => {
224+
func(messageSender.send, msg);
225+
})
226+
};
227+
228+
let argv = process.argv.slice(2);
229+
for (let i = 0; i < argv.length; i++) {
230+
let arg = argv[i];
231+
if (arg === "--stdio") {
232+
let writer = new rpc.StreamMessageWriter(process.stdout);
233+
let reader = new rpc.StreamMessageReader(process.stdin);
234+
messageSender.send = (msg: m.Message) => writer.write(msg);
235+
onMessage = ((func: any) => {
236+
let callback = (message: m.Message) => {
237+
func(messageSender.send, message);
238+
}
239+
reader.listen(callback);
240+
});
241+
242+
break;
243+
};
244+
}
245+
246+
onMessage((send: any, msg: m.Message) => {
220247
if (m.isNotificationMessage(msg)) {
221248
// notification message, aka the client ends it and doesn't want a reply
222249
if (!initialized && msg.method !== "exit") {
@@ -266,7 +293,7 @@ process.on("message", (msg: m.Message) => {
266293
message: "Server not initialized.",
267294
},
268295
};
269-
process.send!(response);
296+
send(response);
270297
} else if (msg.method === "initialize") {
271298
// send the list of features we support
272299
let result: p.InitializeResult = {
@@ -290,15 +317,15 @@ process.on("message", (msg: m.Message) => {
290317
result: result,
291318
};
292319
initialized = true;
293-
process.send!(response);
320+
send(response);
294321
} else if (msg.method === "initialized") {
295322
// sent from client after initialize. Nothing to do for now
296323
let response: m.ResponseMessage = {
297324
jsonrpc: c.jsonrpcVersion,
298325
id: msg.id,
299326
result: null,
300327
};
301-
process.send!(response);
328+
send(response);
302329
} else if (msg.method === "shutdown") {
303330
// https://microsoft.github.io/language-server-protocol/specification#shutdown
304331
if (shutdownRequestAlreadyReceived) {
@@ -310,7 +337,7 @@ process.on("message", (msg: m.Message) => {
310337
message: `Language server already received the shutdown request`,
311338
},
312339
};
313-
process.send!(response);
340+
send(response);
314341
} else {
315342
shutdownRequestAlreadyReceived = true;
316343
// TODO: recheck logic around init/shutdown...
@@ -322,7 +349,7 @@ process.on("message", (msg: m.Message) => {
322349
id: msg.id,
323350
result: null,
324351
};
325-
process.send!(response);
352+
send(response);
326353
}
327354
} else if (msg.method === p.HoverRequest.method) {
328355
let emptyHoverResponse: m.ResponseMessage = {
@@ -338,9 +365,9 @@ process.on("message", (msg: m.Message) => {
338365
...emptyHoverResponse,
339366
result: { contents: result.hover },
340367
};
341-
process.send!(hoverResponse);
368+
send(hoverResponse);
342369
} else {
343-
process.send!(emptyHoverResponse);
370+
send(emptyHoverResponse);
344371
}
345372
} else if (msg.method === p.DefinitionRequest.method) {
346373
// https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_definition
@@ -361,9 +388,9 @@ process.on("message", (msg: m.Message) => {
361388
range: result.definition.range,
362389
},
363390
};
364-
process.send!(definitionResponse);
391+
send(definitionResponse);
365392
} else {
366-
process.send!(emptyDefinitionResponse);
393+
send(emptyDefinitionResponse);
367394
}
368395
} else if (msg.method === p.CompletionRequest.method) {
369396
let emptyCompletionResponse: m.ResponseMessage = {
@@ -374,13 +401,13 @@ process.on("message", (msg: m.Message) => {
374401
let code = getOpenedFileContent(msg.params.textDocument.uri);
375402
let result = runCompletionCommand(msg, code);
376403
if (result === null) {
377-
process.send!(emptyCompletionResponse);
404+
send(emptyCompletionResponse);
378405
} else {
379406
let definitionResponse: m.ResponseMessage = {
380407
...emptyCompletionResponse,
381408
result: result,
382409
};
383-
process.send!(definitionResponse);
410+
send(definitionResponse);
384411
}
385412
} else if (msg.method === p.DocumentFormattingRequest.method) {
386413
// technically, a formatting failure should reply with the error. Sadly
@@ -409,8 +436,8 @@ process.on("message", (msg: m.Message) => {
409436
method: "window/showMessage",
410437
params: params,
411438
};
412-
process.send!(fakeSuccessResponse);
413-
process.send!(response);
439+
send(fakeSuccessResponse);
440+
send(response);
414441
} else {
415442
// See comment on findBscExeDirOfFile for why we need
416443
// to recursively search for bsc.exe upward
@@ -425,8 +452,8 @@ process.on("message", (msg: m.Message) => {
425452
method: "window/showMessage",
426453
params: params,
427454
};
428-
process.send!(fakeSuccessResponse);
429-
process.send!(response);
455+
send(fakeSuccessResponse);
456+
send(response);
430457
} else {
431458
let resolvedBscExePath = path.join(bscExeDir, c.bscExePartialPath);
432459
// code will always be defined here, even though technically it can be undefined
@@ -454,13 +481,13 @@ process.on("message", (msg: m.Message) => {
454481
id: msg.id,
455482
result: result,
456483
};
457-
process.send!(response);
484+
send(response);
458485
} else {
459486
// let the diagnostics logic display the updated syntax errors,
460487
// from the build.
461488
// Again, not sending the actual errors. See fakeSuccessResponse
462489
// above for explanation
463-
process.send!(fakeSuccessResponse);
490+
send(fakeSuccessResponse);
464491
}
465492
}
466493
}
@@ -473,7 +500,7 @@ process.on("message", (msg: m.Message) => {
473500
message: "Unrecognized editor request.",
474501
},
475502
};
476-
process.send!(response);
503+
send(response);
477504
}
478505
} else if (m.isResponseMessage(msg)) {
479506
// response message. Currently the client should have only sent a response

0 commit comments

Comments
 (0)