aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Czihak <git@dcz.at>2026-05-09 01:30:32 +0200
committerDavid Czihak <git@dcz.at>2026-05-09 01:30:32 +0200
commit4c3e7ea293b2e5a4929781e1b97a2f6affff6576 (patch)
tree7799581d02da28b0aac88261f402ab4efa160edb
parentd6dc191a9237b1be62d4fe729750a5af1ddd470e (diff)
Fix: Prevent hangs and failures in task system
A few async functions were swallowing errors or could leave commands stuck waiting forever: - The »Open in Terminal« command could hang if osascript failed to start. It now reports the failure instead. - ZLS config sync ran without error handling. Errors now show up in the log. - Task discovery would block indefinitely if `zig build --list-steps` got stuck. It now gives up after a minute and moves on.
-rw-r--r--Scripts/main.js63
1 files changed, 56 insertions, 7 deletions
diff --git a/Scripts/main.js b/Scripts/main.js
index 652e4cc..24f8cde 100644
--- a/Scripts/main.js
+++ b/Scripts/main.js
@@ -446,6 +446,7 @@ const stepCache = {
const result = await runProcess(zigPath, {
args: ["build", "--list-steps"],
cwd,
+ timeoutMs: 60000,
});
if (result.status !== 0) return null;
@@ -503,19 +504,30 @@ function localizeText(key, fallback, variables) {
* Wraps Nova's Process API in a Promise, resolving with stdout, stderr, and exit status.
*
* @param {string} command - Absolute path to the executable
- * @param {Object} options - Options passed to Nova's Process constructor (args, cwd, env, …)
+ * @param {Object} options - Options passed to Nova's Process constructor (args, cwd, env, …);
+ * the optional `timeoutMs` is consumed here and removed before construction
* @returns {Promise<{status: number, stdout: string, stderr: string}>}
*/
function runProcess(command, options) {
return new Promise((resolve, reject) => {
const stdout = [];
const stderr = [];
- const process = new Process(command, options || {});
+ const { timeoutMs, ...processOptions } = options || {};
+ const process = new Process(command, processOptions);
+
+ let settled = false;
+ let timer = null;
+ const settle = (result) => {
+ if (settled) return;
+ settled = true;
+ if (timer) clearTimeout(timer);
+ resolve(result);
+ };
process.onStdout((line) => stdout.push(line));
process.onStderr((line) => stderr.push(line));
process.onDidExit((status) => {
- resolve({
+ settle({
status,
stdout: stdout.join(""),
stderr: stderr.join(""),
@@ -525,7 +537,24 @@ function runProcess(command, options) {
try {
process.start();
} catch (error) {
+ if (timer) clearTimeout(timer);
reject(error);
+ return;
+ }
+
+ if (typeof timeoutMs === "number" && timeoutMs > 0) {
+ timer = setTimeout(() => {
+ try {
+ if (typeof process.signal === "function") {
+ process.signal("SIGTERM");
+ }
+ } catch (_) {}
+ settle({
+ status: -1,
+ stdout: stdout.join(""),
+ stderr: `${stderr.join("")}\n[timeout after ${timeoutMs}ms]`,
+ });
+ }, timeoutMs);
}
});
}
@@ -756,7 +785,12 @@ end tell`;
process.onDidExit((status) => {
resolve({ status, stderr: stderr.trim() });
});
- process.start();
+
+ try {
+ process.start();
+ } catch (error) {
+ resolve({ status: -1, stderr: String(error) });
+ }
});
}
@@ -843,7 +877,12 @@ class ZigLanguageServer {
if (restart) {
this.start();
} else {
- void this.pushConfiguration();
+ this.pushConfiguration().catch((error) => {
+ console.error(
+ `[${LANGUAGE_CLIENT_ID}] pushConfiguration failed`,
+ error,
+ );
+ });
}
}),
);
@@ -852,7 +891,12 @@ class ZigLanguageServer {
if (restart) {
this.start();
} else {
- void this.pushConfiguration();
+ this.pushConfiguration().catch((error) => {
+ console.error(
+ `[${LANGUAGE_CLIENT_ID}] pushConfiguration failed`,
+ error,
+ );
+ });
}
}),
);
@@ -955,7 +999,12 @@ class ZigLanguageServer {
client.start();
this.client = client;
nova.subscriptions.add(client);
- void this.pushConfiguration();
+ this.pushConfiguration().catch((error) => {
+ console.error(
+ `[${LANGUAGE_CLIENT_ID}] pushConfiguration failed`,
+ error,
+ );
+ });
this.warnedMissing.delete("zls");
if (zigPath) {
this.warnedMissing.delete("zig");