Skip to content

TSServer doesn't send projectLoadingStart until project loading is complete.  #47465

Open
@icholy

Description

@icholy

Bug Report

If a configure request is sent prior to an open request, the projectLoadingStart event doesn't arrive until the project is finished loading.

🕗 Version & Regression Information

This behaviour exists in 4.5.4 and HEAD (at the time of writing).

💻 Code

Here's a minimal tsserver client which demonstrates the issue. If you tail the tsserver.log you'll see that the projectLoadingStart is being sent at the correct time. If I remove the configure request, the projectLoadingStart arrives right away.

import { fork } from "child_process";

const tsserver = "/home/icholy/src/github.com/microsoft/TypeScript/bin/tsserver";
const args = [
    "--logFile", "tsserver.log",
    "--logVerbosity", "verbose",
];
const proc = fork(tsserver, args, { silent: true });

proc.once("exit", () => {
    console.error("tsserver exited");
    process.exit();
});

const { stdin, stdout } = proc;
if (!stdin || !stdout) {
    throw new Error("failed to get stdin/stdout");
}

stdout.on("data", (data) => {
    console.log(data.toString());
});

const configure = {
    command: "configure",
    seq: 1,
    type: "request",
    arguments: {
        preferences: {}
    }
};

const open = {
    command: "open",
    seq: 2,
    type: "request",
    arguments: {
        file: "/home/icholy/src/example/main.ts",
    },
};

const msg = JSON.stringify(configure) + "\n" + JSON.stringify(open) + "\n";
stdin.write(msg);

🙁 Actual behavior

The projectLoadingStart event arrives at the same time as projectLoadingFinish.

🙂 Expected behavior

The projectLoadingStart arrives immediately after the open request is sent.

Details

The configure response is sent first and sets canWrite = false. The projectLoadingStart event is then placed into the pending array. I'm assuming the project loading/analysis is starving the event loop which prevents setCanWriteFlagAndWriteMessageIfNecessary from being called.

function writeMessage(buf: Buffer) {
if (!canWrite) {
pending.push(buf);
}
else {
canWrite = false;
process.stdout.write(buf, setCanWriteFlagAndWriteMessageIfNecessary);
}
}
function setCanWriteFlagAndWriteMessageIfNecessary() {
canWrite = true;
if (pending.length) {
writeMessage(pending.shift()!);
}
}

It seems like the analysis/loading would need some preemption points to allow for the event loop to process pending tasks.

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugA bug in TypeScriptDomain: TSServerIssues related to the TSServerHelp WantedYou can do this

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions