Chapter 17

初期化と終了について追加

takl
takl
2020.12.22に更新

やらなくても動くのでこれまで手抜きをしていましたが、実は初期化と終了についてもう少し細かい取り決めがあります。

  • initialize request
    • initialize request より前に来た request は code: -32002 のエラーを返さなければならない
    • initialize request より前に来た notification は exit を除いて無視しなければならない
  • shutdown request
    • Language Client が終了する前に shutdown request が飛んでくる
    • shutdown request より後に何か request が来たら InvalidRequest を返さなければならない
    • (notification については書かれていないけど、きっと無視しなければならないんだろうな…)
  • exit notification
    • Language Client が Language Server を終了させたいときに exit notification が飛んでくる
    • このとき Language Server は process.exit() しなければならない
    • shutdown 後なら exit status は 0
    • shutdown 前なら exit status は 1

なので正しい dispatch の実装は恐らくこうです。

oreore.js
requestTable["shutdown"] = (msg) => {
    sendMessage({ jsonrpc: "2.0", id: msg.id, result: null });
}
let afterInitialize = false;
let afterShutdown = false;

function dispatch(msg) {
    if ("id" in msg && "method" in msg) { // request
        if (msg.method === "initialize") afterInitialize = true;
        if (afterShutdown) {
            sendInvalidRequestResponse(msg.id);
        } else if (!afterInitialize) {
            sendMessage({ jsonrpc: "2.0", id: msg.id, error: { code: -32002, message: "a request arrived before initialize" } });
        } else if (msg.method in requestTable) {
            requestTable[msg.method](msg);
        } else {
            sendMethodNotFoundResponse(msg.id, msg.method)
        }
    } else if ("id" in msg) { // response
        // Ignore.
        // This language server doesn't send any request.
        // If this language server receives a response, that is invalid.
    } else if ("method" in msg) { // notification
        if (msg.method === "exit") process.exit(afterShutdown ? 0 : 1);

        if (afterShutdown) return;

        if (afterInitialize && msg.method in notificationTable) {
            notificationTable[msg.method](msg);
        }
    } else { // error
        sendInvalidRequestResponse(null);
    }
}

ただ、 vscode-languageclient は initialize 前にメッセージを投げてこないし exit を投げる条件もわからないし、まだ見落としがあるかもしれないし…というわけで自信はありません。