diff options
author | Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> | 2024-05-09 13:49:10 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-09 13:49:10 -0700 |
commit | 19c0633a947f2ee28cc07b59c32322151779101d (patch) | |
tree | ab7983bf954cd109517f80b42bd3199eb27efaa4 /cli/tsc | |
parent | 439b3b8db97106be44bb5fde9573632c20ed4e95 (diff) |
refactor(lsp): Have JS drive TSC event loop in LSP (#23565)
Diffstat (limited to 'cli/tsc')
-rw-r--r-- | cli/tsc/99_main_compiler.js | 83 |
1 files changed, 56 insertions, 27 deletions
diff --git a/cli/tsc/99_main_compiler.js b/cli/tsc/99_main_compiler.js index ddbd77ae0..16e8f1ee9 100644 --- a/cli/tsc/99_main_compiler.js +++ b/cli/tsc/99_main_compiler.js @@ -1079,19 +1079,69 @@ delete Object.prototype.__proto__; /** * @param {number} _id * @param {any} data + * @param {any | null} error */ // TODO(bartlomieju): this feels needlessly generic, both type chcking // and language server use it with inefficient serialization. Id is not used // anyway... - function respond(_id, data = null) { - ops.op_respond(JSON.stringify(data)); + function respond(_id, data = null, error = null) { + if (error) { + ops.op_respond( + "error", + "stack" in error ? error.stack.toString() : error.toString(), + ); + } else { + ops.op_respond(JSON.stringify(data), ""); + } + } + + /** @typedef {[[string, number][], number, boolean] } PendingChange */ + /** + * @template T + * @typedef {T | null} Option<T> */ + + /** @returns {Promise<[number, string, any[], Option<PendingChange>] | null>} */ + async function pollRequests() { + return await ops.op_poll_requests(); + } + + let hasStarted = false; + + /** @param {boolean} enableDebugLogging */ + async function serverMainLoop(enableDebugLogging) { + if (hasStarted) { + throw new Error("The language server has already been initialized."); + } + hasStarted = true; + languageService = ts.createLanguageService(host, documentRegistry); + setLogDebug(enableDebugLogging, "TSLS"); + debug("serverInit()"); + + while (true) { + const request = await pollRequests(); + if (request === null) { + break; + } + try { + serverRequest(request[0], request[1], request[2], request[3]); + } catch (err) { + const reqString = "[" + request.map((v) => + JSON.stringify(v) + ).join(", ") + "]"; + error( + `Error occurred processing request ${reqString} : ${ + "stack" in err ? err.stack : err + }`, + ); + } + } } /** * @param {number} id * @param {string} method * @param {any[]} args - * @param {[[string, number][], number, boolean] | null} maybeChange + * @param {PendingChange | null} maybeChange */ function serverRequest(id, method, args, maybeChange) { if (logDebug) { @@ -1160,11 +1210,7 @@ delete Object.prototype.__proto__; if ( !isCancellationError(e) ) { - if ("stack" in e) { - error(e.stack); - } else { - error(e); - } + respond(id, {}, e); throw e; } return respond(id, {}); @@ -1181,11 +1227,7 @@ delete Object.prototype.__proto__; return respond(id, languageService[method](...args)); } catch (e) { if (!isCancellationError(e)) { - if ("stack" in e) { - error(e.stack); - } else { - error(e); - } + respond(id, null, e); throw e; } return respond(id); @@ -1198,18 +1240,6 @@ delete Object.prototype.__proto__; } } - let hasStarted = false; - /** @param {{ debug: boolean; }} init */ - function serverInit({ debug: debugFlag }) { - if (hasStarted) { - throw new Error("The language server has already been initialized."); - } - hasStarted = true; - languageService = ts.createLanguageService(host, documentRegistry); - setLogDebug(debugFlag, "TSLS"); - debug("serverInit()"); - } - // A build time only op that provides some setup information that is used to // ensure the snapshot is setup properly. /** @type {{ buildSpecifier: string; libs: string[]; nodeBuiltInModuleNames: string[] }} */ @@ -1300,6 +1330,5 @@ delete Object.prototype.__proto__; // exposes the functions that are called when the compiler is used as a // language service. - global.serverInit = serverInit; - global.serverRequest = serverRequest; + global.serverMainLoop = serverMainLoop; })(this); |