diff options
| author | David Czihak <git@dcz.at> | 2026-05-11 17:45:21 +0200 |
|---|---|---|
| committer | David Czihak <git@dcz.at> | 2026-05-11 17:45:21 +0200 |
| commit | bf0eacb506bc8125b0ab8d438913e54feba63391 (patch) | |
| tree | 31b941c40ce3409022c68d0b413aa64dff905658 /Zig.novaextension/Scripts | |
| parent | 7d5fbaa5cae6b08f24c04fe58fbd049be49d8934 (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')
| -rw-r--r-- | Zig.novaextension/Scripts/main.js | 136 | ||||
| -rw-r--r-- | Zig.novaextension/Scripts/normalize-zig-issues.pl | 40 |
2 files changed, 170 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; diff --git a/Zig.novaextension/Scripts/normalize-zig-issues.pl b/Zig.novaextension/Scripts/normalize-zig-issues.pl index b6ff328..17620a0 100644 --- a/Zig.novaextension/Scripts/normalize-zig-issues.pl +++ b/Zig.novaextension/Scripts/normalize-zig-issues.pl @@ -4,6 +4,7 @@ $| = 1; my $base = $ENV{NOVA_ZIG_TASK_CWD} || $ENV{PWD} || ""; my %file_cache = (); +my @collected_issues = (); sub normalize_path { my ($path) = @_; @@ -83,6 +84,16 @@ while (my $line = <STDIN>) { } $line = "$normalized:$line_number:$column: $severity: $message"; + + if ($severity eq "error" || $severity eq "warning" || $severity eq "note") { + push @collected_issues, { + file => $normalized, + line => int($line_number), + column => int($column), + severity => $severity, + message => $message, + }; + } } elsif ($line =~ m{^([^:\n]+\.zig):(\d+):(\d+):}) { my ($path, $line_number, $column) = ($1, $2, $3); my $normalized = normalize_path($path); @@ -91,3 +102,32 @@ while (my $line = <STDIN>) { print $line, $newline; } + +END { + my $issue_file = $ENV{NOVA_ZIG_ISSUE_FILE} or return; + sub _json_str { + my ($s) = @_; + $s =~ s/\\/\\\\/g; + $s =~ s/"/\\"/g; + $s =~ s/\n/\\n/g; + $s =~ s/\r/\\r/g; + $s =~ s/\t/\\t/g; + return qq("$s"); + } + if (open my $fh, ">", $issue_file) { + print $fh "["; + my $first = 1; + for my $issue (@collected_issues) { + print $fh "," unless $first; + $first = 0; + printf $fh '{%s,%s,%s,%s,%s}', + '"file":' . _json_str($issue->{file}), + '"line":' . $issue->{line}, + '"column":' . $issue->{column}, + '"severity":' . _json_str($issue->{severity}), + '"message":' . _json_str($issue->{message}); + } + print $fh "]"; + close $fh; + } +} |
