aboutsummaryrefslogtreecommitdiff
path: root/Zig.novaextension/Scripts/main.js
diff options
context:
space:
mode:
authorDavid Czihak <git@dcz.at>2026-05-11 17:45:21 +0200
committerDavid Czihak <git@dcz.at>2026-05-11 17:45:21 +0200
commitbf0eacb506bc8125b0ab8d438913e54feba63391 (patch)
tree31b941c40ce3409022c68d0b413aa64dff905658 /Zig.novaextension/Scripts/main.js
parent7d5fbaa5cae6b08f24c04fe58fbd049be49d8934 (diff)
Fix: Cache compiler errors
Cache compiler errors instead of passing them directly to Nova. This removes the possibility for outdated old errors not going away.
Diffstat (limited to 'Zig.novaextension/Scripts/main.js')
-rw-r--r--Zig.novaextension/Scripts/main.js136
1 files changed, 130 insertions, 6 deletions
diff --git a/Zig.novaextension/Scripts/main.js b/Zig.novaextension/Scripts/main.js
index 6ef2241..0e66188 100644
--- a/Zig.novaextension/Scripts/main.js
+++ b/Zig.novaextension/Scripts/main.js
@@ -32,7 +32,112 @@ let commandRegistrations = [];
let lastZlsStopAt = 0;
const ZLS_RESTART_GRACE_MS = 500;
+// Extension-managed issue collection for compiler diagnostics. Using an
+// IssueCollection instead of task matchers lets us explicitly clear stale
+// issues before each build, regardless of which task type the user runs.
+let zigBuildIssues = null;
+let issueFileWatcher = null;
+
+// --- ISSUE COLLECTION --------------------------------------------------------
+
+function zigIssueFilePath() {
+ return nova.path.join(nova.extension.workspaceStoragePath, "build-issues.json");
+}
+
+function loadIssuesFromFile() {
+ if (!zigBuildIssues) return;
+ const filePath = zigIssueFilePath();
+ let content = "";
+ try {
+ const file = nova.fs.open(filePath, "r");
+ if (!file) return;
+ content = file.read() || "";
+ file.close();
+ } catch (e) {
+ return;
+ }
+
+ const trimmed = content.trim();
+ if (!trimmed || trimmed === "[]") return;
+
+ let data;
+ try {
+ data = JSON.parse(trimmed);
+ } catch (e) {
+ console.warn(`[${EXTENSION_ID}] Could not parse build-issues.json: ${e}`);
+ return;
+ }
+ if (!Array.isArray(data)) return;
+
+ const byFile = new Map();
+ for (const entry of data) {
+ if (!entry.file || !entry.severity) continue;
+ if (!byFile.has(entry.file)) byFile.set(entry.file, []);
+ const issue = new Issue();
+ issue.message = entry.message || "";
+ issue.severity =
+ entry.severity === "error" ? IssueSeverity.Error :
+ entry.severity === "warning" ? IssueSeverity.Warning :
+ IssueSeverity.Hint;
+ issue.line = entry.line || 1;
+ issue.column = entry.column || 1;
+ byFile.get(entry.file).push(issue);
+ }
+
+ zigBuildIssues.clear();
+ for (const [file, issues] of byFile) {
+ zigBuildIssues.set("file://" + file, issues);
+ }
+}
+
+function setupIssueCollection() {
+ if (issueFileWatcher) {
+ issueFileWatcher.dispose();
+ issueFileWatcher = null;
+ }
+ if (zigBuildIssues) {
+ zigBuildIssues.dispose();
+ zigBuildIssues = null;
+ }
+
+ zigBuildIssues = new IssueCollection();
+ nova.subscriptions.add(zigBuildIssues);
+
+ const filePath = zigIssueFilePath();
+ try {
+ const dir = nova.path.dirname(filePath);
+ if (!nova.fs.stat(dir)) nova.fs.mkdir(dir);
+ if (!nova.fs.stat(filePath)) {
+ const f = nova.fs.open(filePath, "w");
+ f.write("[]");
+ f.close();
+ }
+ issueFileWatcher = nova.fs.watch(filePath, loadIssuesFromFile);
+ } catch (e) {
+ console.warn(`[${EXTENSION_ID}] Could not set up issue file watcher: ${e}`);
+ }
+}
+
+// Clear the issue collection and reset the JSON file before a build starts.
+// Called from every build-type task resolver so stale issues from a previous
+// run of ANY task type are always removed before the new output arrives.
+function resetBuildIssues() {
+ if (zigBuildIssues) zigBuildIssues.clear();
+ const filePath = zigIssueFilePath();
+ try {
+ const dir = nova.path.dirname(filePath);
+ if (!nova.fs.stat(dir)) nova.fs.mkdir(dir);
+ const f = nova.fs.open(filePath, "w");
+ f.write("[]");
+ f.close();
+ } catch (e) {
+ console.warn(`[${EXTENSION_ID}] Could not reset issue file: ${e}`);
+ }
+}
+
exports.activate = function activate() {
+ setupIssueCollection();
+
// Nova may call activate() again without first calling deactivate() when a
// language server crashes and the extension is auto-restarted. Dispose any
// existing instances so their config observers and ZLS processes don't leak.
@@ -89,6 +194,15 @@ exports.deactivate = function deactivate() {
});
commandRegistrations = [];
+ if (issueFileWatcher) {
+ issueFileWatcher.dispose();
+ issueFileWatcher = null;
+ }
+ if (zigBuildIssues) {
+ zigBuildIssues.dispose();
+ zigBuildIssues = null;
+ }
+
console.log(`[${EXTENSION_ID}] deactivated`);
};
@@ -1340,14 +1454,16 @@ class ZigTaskAssistant {
}
}
- createAction(command, args, cwd) {
+ // useMatchers: true keeps Nova's built-in matcher (needed for watch mode
+ // where the process never exits and the normalizer never writes the JSON).
+ createAction(command, args, cwd, { useMatchers = false } = {}) {
+ const env = { NOVA_ZIG_TASK_CWD: cwd || "" };
+ if (!useMatchers) env.NOVA_ZIG_ISSUE_FILE = zigIssueFilePath();
return new TaskProcessAction("/bin/zsh", {
args: ["-lc", buildIssueNormalizedCommand(command, args)],
cwd,
- env: {
- NOVA_ZIG_TASK_CWD: cwd || "",
- },
- matchers: [ISSUE_MATCHER],
+ env,
+ ...(useMatchers ? { matchers: [ISSUE_MATCHER] } : {}),
});
}
@@ -1392,6 +1508,7 @@ class ZigTaskAssistant {
async resolveBuildAction(config, cwd) {
console.log(`[${TASK_ASSISTANT_ID}] resolveBuildAction: cwd=${cwd}`);
+ resetBuildIssues();
const zigPath = await resolveZigExecutable();
if (!zigPath) return null;
@@ -1400,6 +1517,7 @@ class ZigTaskAssistant {
async resolveDebugBuildAction(config, cwd) {
console.log(`[${TASK_ASSISTANT_ID}] resolveDebugBuildAction: cwd=${cwd}`);
+ resetBuildIssues();
const zigPath = await resolveZigExecutable();
if (!zigPath) return null;
@@ -1412,6 +1530,7 @@ class ZigTaskAssistant {
async resolveBuildRunAction(config, cwd, forceConsole) {
console.log(`[${TASK_ASSISTANT_ID}] resolveBuildRunAction: cwd=${cwd}`);
+ resetBuildIssues();
const zigPath = await resolveZigExecutable();
if (!zigPath) return null;
@@ -1442,6 +1561,7 @@ class ZigTaskAssistant {
async resolveBuildStepAction(step) {
console.log(`[${TASK_ASSISTANT_ID}] resolveBuildStepAction: step=${step}`);
+ resetBuildIssues();
if (!step || !/^[A-Za-z_][\w-]*$/.test(step)) return null;
const zigPath = await resolveZigExecutable();
@@ -1455,6 +1575,7 @@ class ZigTaskAssistant {
async resolveTestAction(config, cwd) {
console.log(`[${TASK_ASSISTANT_ID}] resolveTestAction: cwd=${cwd}`);
+ resetBuildIssues();
const zigPath = await resolveZigExecutable();
if (!zigPath) return null;
@@ -1492,11 +1613,14 @@ class ZigTaskAssistant {
if (incremental === "on") argv.push("-fincremental");
else if (incremental === "off") argv.push("-fno-incremental");
- return this.createAction(zigPath, argv, cwd);
+ // Watch mode never exits, so the normalizer never writes the JSON file.
+ // Keep the matcher-based approach for continuous issue updates.
+ return this.createAction(zigPath, argv, cwd, { useMatchers: true });
}
async resolveCurrentFileRunAction() {
console.log(`[${TASK_ASSISTANT_ID}] resolveCurrentFileRunAction`);
+ resetBuildIssues();
const zigPath = await resolveZigExecutable();
if (!zigPath) return null;