diff options
| author | David Czihak <git@dcz.at> | 2026-05-08 03:00:15 +0200 |
|---|---|---|
| committer | David Czihak <git@dcz.at> | 2026-05-08 03:00:15 +0200 |
| commit | b9d713336bd4fdc5e40899257b1fe7a356ca8dcf (patch) | |
| tree | e1d2860ddf998a9507db45f04a67670c34c336aa | |
| parent | fa99e9c7564bafef500ec3b2218859319098ee74 (diff) | |
Feat: New task system
This commit brings a complete rewrite of the task system to align the Zig build system and Nova task system as closely as possible. Lots of new options and settings have been added to the tasks, and the Zig extension will auto-detect tasks from the `zig build --list-steps` command. Should you prefer to add tasks manually, or need a more fine-grained control, the auto-detection can be disabled.
This rewrite has not yet been thoroughly tested and will likely be a little rough around the edges.
| -rw-r--r-- | CHANGELOG.md | 12 | ||||
| -rw-r--r-- | README.md | 23 | ||||
| -rw-r--r-- | Scripts/main.js | 480 | ||||
| -rw-r--r-- | de.lproj/strings.json | 57 | ||||
| -rw-r--r-- | extension.json | 304 |
5 files changed, 736 insertions, 140 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index ef55853..3786150 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## Unreleased + +- Tasks now expose first-class config for `-Doptimize`, `-Dtarget`, and custom `-D<key>=<value>` user options on every template that takes them. +- Added **Zig Test** template (`zig build test` with `--test-filter` and `--summary`). +- Added **Zig Watch** template (`zig build --watch`, with `--debounce` and `-fincremental` controls). Note: Nova issue matchers only fire on the first build cycle. +- **Zig Package** gained a Console setting (Internal / External Terminal). The standalone *Zig Package (macOS Terminal)* template has been removed. +- Dynamic per-step tasks: each step from `zig build --list-steps` appears as `Zig Build: <step>` in the task list. Cached and refreshed on `build.zig` / `build.zig.zon` changes. +- **Zig Debug** auto-detects `programPath` from `build.zig.zon`'s `.name` and the `zig-out/bin/<name>` location when the field is left blank. +- The Run Step default flips from `run` to empty — `zig build` then runs its own default install step. Clear and re-set the field on existing tasks to use the new behavior. +- Clean now refuses to run when the working directory is `/`, `$HOME`, or outside the workspace, and runs `zig build uninstall` first when the project exposes that step. +- "Current Zig File" Clean walks up to the nearest `build.zig` instead of cleaning the file's directory. +- Issue assistant no longer registers for the non-existent `zig-package` syntax. ## 0.1.8 — 2026-05-07 - Added a setting to write an LLDB proxy log file for debugging the debug adapter. @@ -28,10 +28,25 @@ This extension is not endorsed by, affiliated with, or associated with the [Zig ### Tasks -- **Zig Package**: build and run a Zig package in the Nova console -- **Zig Package (macOS Terminal)**: build and run in the external Terminal app for interactive CLIs -- **Zig Debug**: build in Debug mode and launch under the debugger, with configurable console and stop-on-entry -- A clean action on every runnable template that removes `.zig-cache`, `zig-cache`, and `zig-out` +Four templates expose the `zig build` surface — pick one in **Project Settings > Tasks > New Task**: + +- **Zig Package**: build and run a Zig package. Configurable optimize mode, target triple, custom `-D` user options, run step, build/program arguments, and console (Internal Console or External Terminal). +- **Zig Debug**: build and launch under lldb-dap. Same flag surface as Zig Package, plus program path (auto-detected from `build.zig.zon` when blank), console, and stop-on-entry. +- **Zig Test**: run `zig build test` with optional `--test-filter`, `--summary`, plus optimize/target/user-options. +- **Zig Watch**: run `zig build --watch` with configurable step, debounce, and incremental flag. + +Every runnable template also exposes a clean action that removes `.zig-cache`, `zig-cache`, and `zig-out`. Clean refuses to run when the working directory resolves to `/`, `$HOME`, or anywhere outside the workspace; when the project's `build.zig` exposes an `uninstall` step, it runs first. + +Two ad-hoc tasks appear automatically: + +- **Current Zig File**: run via `zig run <file>` and clean the nearest `build.zig` ancestor. +- **Zig Build: \<step\>**: one task per step discovered from `zig build --list-steps`. The list is cached and refreshed when `build.zig` or `build.zig.zon` changes. + +The legacy **Zig Package (macOS Terminal)** template is preserved for backward compatibility. New configurations should use **Zig Package** with Console set to External Terminal. + +#### Watch-mode caveat + +Nova issue matchers fire only on the first build cycle of a long-running task. With `zig build --watch`, errors from later cycles will not appear inline — re-run the task to refresh the issue overlay. ### Debugging diff --git a/Scripts/main.js b/Scripts/main.js index 6f0a54e..70af784 100644 --- a/Scripts/main.js +++ b/Scripts/main.js @@ -4,6 +4,8 @@ const EXTENSION_ID = "at.dcz.nova-zig"; const TASK_ASSISTANT_ID = `${EXTENSION_ID}.tasks`; const LANGUAGE_CLIENT_ID = `${EXTENSION_ID}.zls`; const ISSUE_MATCHER = "zig.compiler"; +const USER_OPTION_REGEX = /^[A-Za-z_][A-Za-z0-9_-]*(=.*)?$/; +const STEP_CACHE_TTL_MS = 5 * 60 * 1000; const CONFIG_KEYS = { zigPath: `${EXTENSION_ID}.toolchain.zig-path`, @@ -13,6 +15,7 @@ const CONFIG_KEYS = { zlsBuildOnSave: `${EXTENSION_ID}.zls.build-on-save`, zlsDebug: `${EXTENSION_ID}.zls.debug`, lldbDapDebug: `${EXTENSION_ID}.debug-adapter.debug`, + discoverSteps: `${EXTENSION_ID}.tasks.discover-steps`, }; let languageServer = null; @@ -143,6 +146,229 @@ function getTaskCwd(config) { return nova.workspace.path || null; } +// Returns the configured step string, or null if the step should be omitted. +// An explicit empty string means "no step argument" — `zig build` then runs the +// default install step (Ziglings-style projects rely on this). Missing/null +// returns the caller-supplied fallback. +function resolveStep(config, key, fallback) { + const raw = config ? config.get(key) : undefined; + if (raw === undefined || raw === null) { + return fallback === undefined ? null : fallback; + } + const trimmed = String(raw).trim(); + return trimmed.length > 0 ? trimmed : null; +} + +// Backwards-compatible wrapper: zigBuildRun's `runStep` field. The legacy +// `step` key is still honored for users on pre-0.1 task configs. +function resolveRunStep(config) { + const raw = config ? config.get("runStep") : undefined; + if (raw === undefined || raw === null) { + const legacy = getTaskConfigValue(config, "step"); + return legacy ? legacy : null; + } + const trimmed = String(raw).trim(); + return trimmed.length > 0 ? trimmed : null; +} + +// Builds the `zig build` argv prefix shared by Build/Run/Test/Watch resolvers. +// Order: ["build", -Doptimize, -Dtarget, -D<userOptions>, ...buildArgs, step?]. +// `runArgs` are the caller's responsibility — they go after `--`. +function buildZigArgv(config, options) { + const opts = options || {}; + const argv = ["build"]; + + const optimize = getTaskConfigValue(config, "optimize") || opts.defaultOptimize || null; + if (optimize) argv.push(`-Doptimize=${optimize}`); + + const target = getTaskConfigValue(config, "target"); + if (target) argv.push(`-Dtarget=${target}`); + + for (const entry of getTaskArgs(config, "userOptions")) { + if (USER_OPTION_REGEX.test(entry)) { + argv.push(`-D${entry}`); + } else { + console.warn(`[zig-task] Skipping invalid userOptions entry: ${entry}`); + } + } + + argv.push(...getTaskArgs(config, "buildArgs")); + + if (opts.step) argv.push(opts.step); + return argv; +} + +// Refuses to clean unless `cwd` is non-root, not $HOME, and inside the workspace. +// Returns the array of cache directories to remove, or null after warning the user. +function safeCleanPaths(cwd) { + const showWarning = () => { + nova.workspace.showWarningMessage( + localizedText( + "warning.clean.unsafe_cwd", + "Refusing to clean: the working directory must be inside this workspace." + ) + ); + }; + + if (!cwd || typeof cwd !== "string" || !cwd.startsWith("/")) { + showWarning(); + return null; + } + + const normalized = nova.path.normalize(cwd); + if (normalized === "/" || normalized === "") { + showWarning(); + return null; + } + + const home = nova.environment ? nova.environment.HOME : null; + if (home && (normalized === home || normalized === nova.path.normalize(home))) { + showWarning(); + return null; + } + + const workspacePath = nova.workspace.path; + if (!workspacePath) { + showWarning(); + return null; + } + + const workspaceNormalized = nova.path.normalize(workspacePath); + if (normalized !== workspaceNormalized && !normalized.startsWith(workspaceNormalized + "/")) { + showWarning(); + return null; + } + + return [".zig-cache", "zig-cache", "zig-out"]; +} + +// Walks up from `startDir` to the nearest ancestor containing `build.zig`, +// stopping at the workspace root. Returns the workspace root if no build.zig +// is found above startDir, or null if no workspace is open. +function nearestBuildZigDir(startDir) { + const workspacePath = nova.workspace.path + ? nova.path.normalize(nova.workspace.path) + : null; + + if (!startDir) return workspacePath || null; + + let current = nova.path.normalize(startDir); + for (let i = 0; i < 64; i++) { + if (nova.fs.stat(nova.path.join(current, "build.zig"))) { + return current; + } + if (current === "/" || (workspacePath && current === workspacePath)) { + return workspacePath || null; + } + const parent = nova.path.dirname(current); + if (!parent || parent === current) break; + current = parent; + } + return workspacePath || null; +} + +// Best-effort regex extraction of the package name from build.zig.zon. Returns +// null when the file is missing, unreadable, or doesn't match the expected +// `\.name = "<name>"` / `\.name = .<ident>` shapes. Used to default +// `programPath` for the Debug template. +function parseProjectName(cwd) { + if (!cwd) return null; + const zonPath = nova.path.join(cwd, "build.zig.zon"); + if (!nova.fs.stat(zonPath)) return null; + + let content = ""; + try { + const file = nova.fs.open(zonPath, "r"); + content = file.read() || ""; + file.close(); + } catch (_) { + return null; + } + if (typeof content !== "string" || content.length === 0) return null; + + const match = content.match(/\.name\s*=\s*(?:"([^"]+)"|\.([A-Za-z_][\w]*))/); + if (!match) return null; + return match[1] || match[2] || null; +} + +// Per-cwd cache for `zig build --list-steps`. Invalidated by mtime changes on +// build.zig / build.zig.zon and a soft 5-minute TTL. `getOrFetch` returns +// cached steps synchronously and kicks off a background refresh when stale, +// nudging Nova to reload tasks once the refresh lands. +const stepCache = { + entries: new Map(), + pending: new Map(), + + getOrFetch(cwd) { + if (!cwd) return null; + + const buildZigPath = nova.path.join(cwd, "build.zig"); + const buildZigStat = nova.fs.stat(buildZigPath); + if (!buildZigStat) return null; + + const buildZonStat = nova.fs.stat(nova.path.join(cwd, "build.zig.zon")); + const buildZigMtimeMs = buildZigStat.mtime ? buildZigStat.mtime.getTime() : 0; + const buildZonMtimeMs = buildZonStat && buildZonStat.mtime ? buildZonStat.mtime.getTime() : 0; + + const cached = this.entries.get(cwd); + const fresh = + cached && + cached.buildZigMtimeMs === buildZigMtimeMs && + cached.buildZonMtimeMs === buildZonMtimeMs && + Date.now() - cached.fetchedAt < STEP_CACHE_TTL_MS; + + if (fresh) return cached.steps; + + if (!this.pending.has(cwd)) { + const promise = this.fetch(cwd, buildZigMtimeMs, buildZonMtimeMs).finally(() => { + this.pending.delete(cwd); + if (typeof nova.workspace.reloadTasks === "function") { + try { + nova.workspace.reloadTasks(TASK_ASSISTANT_ID); + } catch (_) {} + } + }); + this.pending.set(cwd, promise); + } + + return cached ? cached.steps : null; + }, + + async fetch(cwd, buildZigMtimeMs, buildZonMtimeMs) { + const zigPath = await resolveExecutable(CONFIG_KEYS.zigPath, "zig"); + if (!zigPath) return null; + + const result = await runProcess(zigPath, { + args: ["build", "--list-steps"], + cwd, + }); + if (result.status !== 0) return null; + + const steps = result.stdout + .split("\n") + .map((line) => line.trim()) + .filter((line) => line.length > 0) + .map((line) => line.split(/\s+/, 1)[0]) + .filter((name) => /^[A-Za-z_][\w-]*$/.test(name)); + + this.entries.set(cwd, { + steps, + buildZigMtimeMs, + buildZonMtimeMs, + fetchedAt: Date.now(), + }); + return steps; + }, + + invalidate(cwd) { + if (cwd) { + this.entries.delete(cwd); + } else { + this.entries.clear(); + } + }, +}; + function activeZigFilePath() { const editor = nova.workspace.activeTextEditor; if (!editor || !editor.document || !editor.document.path) { @@ -221,6 +447,22 @@ async function resolveExecutable(configKey, defaultCommand) { return findExecutableOnPath(defaultCommand); } +// Resolves the Zig executable, surfacing a single localized warning when it's +// missing. Returns null when no path is found — callers must short-circuit. +async function requireZig() { + const zigPath = await resolveExecutable(CONFIG_KEYS.zigPath, "zig"); + if (!zigPath) { + nova.workspace.showWarningMessage( + localizedText( + "warning.zig.not_found", + "Zig was not found. Install it or set a Zig executable path in Zig extension settings." + ) + ); + return null; + } + return zigPath; +} + async function findExecutableWithXcode(commandName) { const result = await runProcess("/usr/bin/xcrun", { args: ["--find", commandName], @@ -593,18 +835,41 @@ class ZigTaskAssistant { } provideTasks() { - const task = new Task(localizedText("task.current_file.name", "Current Zig File")); - task.setAction(Task.Run, new TaskResolvableAction({ + const tasks = []; + + const currentFile = new Task(localizedText("task.current_file.name", "Current Zig File")); + currentFile.setAction(Task.Run, new TaskResolvableAction({ data: { type: "current-file-run", }, })); - task.setAction(Task.Clean, new TaskResolvableAction({ + currentFile.setAction(Task.Clean, new TaskResolvableAction({ data: { type: "current-file-clean", }, })); - return [task]; + tasks.push(currentFile); + + const workspacePath = nova.workspace.path; + if (workspacePath && getBooleanConfigValue(CONFIG_KEYS.discoverSteps, true)) { + const steps = stepCache.getOrFetch(workspacePath); + if (steps && steps.length > 0) { + for (const step of steps) { + const task = new Task( + localizedText("task.build_step.name", "Zig Build: {step}", { step }) + ); + task.setAction(Task.Run, new TaskResolvableAction({ + data: { + type: "build-step", + step, + }, + })); + tasks.push(task); + } + } + } + + return tasks; } async resolveTaskAction(context) { @@ -623,14 +888,19 @@ class ZigTaskAssistant { return this.resolveDebugBuildAction(config, cwd); case "build-run": return this.resolveBuildRunAction(config, cwd); - case "build-run-terminal": - return this.resolveBuildRunTerminalAction(config, cwd); + case "build-step": + return this.resolveBuildStepAction(context.data && context.data.step); case "current-file-run": return this.resolveCurrentFileRunAction(); case "current-file-clean": return this.resolveCurrentFileCleanAction(); case "debug": return this.resolveDebugAction(config, cwd); + case "test-build": + case "test-run": + return this.resolveTestAction(config, cwd); + case "watch": + return this.resolveWatchAction(config, cwd); default: return null; } @@ -647,7 +917,7 @@ class ZigTaskAssistant { }); } - resolveCleanAction(cwd) { + async resolveCleanAction(cwd) { if (!cwd) { nova.workspace.showWarningMessage( localizedText( @@ -658,109 +928,129 @@ class ZigTaskAssistant { return null; } - return new TaskProcessAction("/bin/rm", { - args: ["-rf", ".zig-cache", "zig-cache", "zig-out"], - cwd, - }); + const paths = safeCleanPaths(cwd); + if (!paths) return null; + + // If the project exposes an `uninstall` step, prefer running it before + // wiping the cache directories. Use only what's already cached — don't + // block clean on a fresh `--list-steps` invocation. + const cached = stepCache.entries.get(cwd); + const args = ["-rf", ...paths]; + + if (cached && Array.isArray(cached.steps) && cached.steps.includes("uninstall")) { + const zigPath = await resolveExecutable(CONFIG_KEYS.zigPath, "zig"); + if (zigPath) { + const command = `${quoteShellArgument(zigPath)} build uninstall; /bin/rm ${args + .map(quoteShellArgument) + .join(" ")}`; + return new TaskProcessAction("/bin/zsh", { + args: ["-lc", command], + cwd, + matchers: [ISSUE_MATCHER], + }); + } + } + + return new TaskProcessAction("/bin/rm", { args, cwd }); } async resolveBuildAction(config, cwd) { - const zigPath = await resolveExecutable(CONFIG_KEYS.zigPath, "zig"); - if (!zigPath) { - nova.workspace.showWarningMessage( - localizedText( - "warning.zig.not_found", - "Zig was not found. Install it or set a Zig executable path in Zig extension settings." - ) - ); - return null; - } + const zigPath = await requireZig(); + if (!zigPath) return null; - return this.createAction(zigPath, ["build", ...getTaskArgs(config, "buildArgs")], cwd); + return this.createAction(zigPath, buildZigArgv(config), cwd); } async resolveDebugBuildAction(config, cwd) { - const zigPath = await resolveExecutable(CONFIG_KEYS.zigPath, "zig"); - if (!zigPath) { - nova.workspace.showWarningMessage( - localizedText( - "warning.zig.not_found", - "Zig was not found. Install it or set a Zig executable path in Zig extension settings." - ) - ); - return null; - } + const zigPath = await requireZig(); + if (!zigPath) return null; - return this.createAction(zigPath, ["build", "-Doptimize=Debug", ...getTaskArgs(config, "buildArgs")], cwd); + return this.createAction( + zigPath, + buildZigArgv(config, { defaultOptimize: "Debug" }), + cwd + ); } - async resolveBuildRunAction(config, cwd) { - const zigPath = await resolveExecutable(CONFIG_KEYS.zigPath, "zig"); - if (!zigPath) { - nova.workspace.showWarningMessage( - localizedText( - "warning.zig.not_found", - "Zig was not found. Install it or set a Zig executable path in Zig extension settings." - ) - ); - return null; - } + async resolveBuildRunAction(config, cwd, forceConsole) { + const zigPath = await requireZig(); + if (!zigPath) return null; - const step = - getTaskConfigValue(config, "runStep") || getTaskConfigValue(config, "step") || "run"; - const args = ["build", ...getTaskArgs(config, "buildArgs"), step]; + const step = resolveRunStep(config); + const argv = buildZigArgv(config, { step }); const runArgs = getTaskArgs(config, "runArgs"); + if (runArgs.length > 0) argv.push("--", ...runArgs); + + const consoleMode = + forceConsole || getTaskConfigValue(config, "console") || "internalConsole"; - if (runArgs.length > 0) { - args.push("--", ...runArgs); + if (consoleMode === "externalTerminal") { + return new TaskCommandAction(`${EXTENSION_ID}.runInTerminal`, { + args: [ + { + command: zigPath, + args: argv, + cwd, + }, + ], + }); } - return this.createAction(zigPath, args, cwd); + return this.createAction(zigPath, argv, cwd); } - async resolveBuildRunTerminalAction(config, cwd) { - const zigPath = await resolveExecutable(CONFIG_KEYS.zigPath, "zig"); - if (!zigPath) { - nova.workspace.showWarningMessage( - localizedText( - "warning.zig.not_found", - "Zig was not found. Install it or set a Zig executable path in Zig extension settings." - ) - ); - return null; - } + async resolveBuildStepAction(step) { + const zigPath = await requireZig(); + if (!zigPath) return null; + + const cwd = nova.workspace.path || null; + if (!cwd || !step || !/^[A-Za-z_][\w-]*$/.test(step)) return null; + + return this.createAction(zigPath, ["build", step], cwd); + } + + async resolveTestAction(config, cwd) { + const zigPath = await requireZig(); + if (!zigPath) return null; + + const argv = buildZigArgv(config, { step: "test" }); + + const summary = getTaskConfigValue(config, "summary"); + if (summary) argv.push(`--summary=${summary}`); + + const testFilter = getTaskConfigValue(config, "testFilter"); + if (testFilter) argv.push("--test-filter", testFilter); - const step = - getTaskConfigValue(config, "runStep") || getTaskConfigValue(config, "step") || "run"; - const args = ["build", ...getTaskArgs(config, "buildArgs"), step]; const runArgs = getTaskArgs(config, "runArgs"); + if (runArgs.length > 0) argv.push("--", ...runArgs); - if (runArgs.length > 0) { - args.push("--", ...runArgs); + return this.createAction(zigPath, argv, cwd); + } + + async resolveWatchAction(config, cwd) { + const zigPath = await requireZig(); + if (!zigPath) return null; + + const step = resolveStep(config, "step", null); + const argv = buildZigArgv(config, { step }); + argv.push("--watch"); + + const debounce = getTaskConfigValue(config, "debounceMs"); + if (debounce !== null && debounce !== undefined && debounce !== "") { + const n = Number(debounce); + if (Number.isFinite(n) && n >= 0) argv.push("--debounce", String(Math.floor(n))); } - return new TaskCommandAction(`${EXTENSION_ID}.runInTerminal`, { - args: [ - { - command: zigPath, - args, - cwd, - }, - ], - }); + const incremental = getTaskConfigValue(config, "incremental"); + if (incremental === "on") argv.push("-fincremental"); + else if (incremental === "off") argv.push("-fno-incremental"); + + return this.createAction(zigPath, argv, cwd); } async resolveCurrentFileRunAction() { - const zigPath = await resolveExecutable(CONFIG_KEYS.zigPath, "zig"); - if (!zigPath) { - nova.workspace.showWarningMessage( - localizedText( - "warning.zig.not_found", - "Zig was not found. Install it or set a Zig executable path in Zig extension settings." - ) - ); - return null; - } + const zigPath = await requireZig(); + if (!zigPath) return null; const filePath = activeZigFilePath(); const cwd = filePath ? nova.path.dirname(filePath) : null; @@ -777,9 +1067,9 @@ class ZigTaskAssistant { return this.createAction(zigPath, ["run", filePath], cwd); } - resolveCurrentFileCleanAction() { - const cwd = activeZigFileDirectory(); - if (!cwd) { + async resolveCurrentFileCleanAction() { + const startDir = activeZigFileDirectory(); + if (!startDir) { nova.workspace.showWarningMessage( localizedText( "warning.current_file.focus_editor_for_clean", @@ -789,6 +1079,7 @@ class ZigTaskAssistant { return null; } + const cwd = nearestBuildZigDir(startDir); return this.resolveCleanAction(cwd); } @@ -805,7 +1096,16 @@ class ZigTaskAssistant { } const configuredProgramPath = getTaskConfigValue(config, "programPath"); - const programPath = resolvePathAgainstBase(configuredProgramPath, cwd); + let programPath = resolvePathAgainstBase(configuredProgramPath, cwd); + if (!programPath) { + const projectName = parseProjectName(cwd); + if (projectName) { + const candidate = nova.path.join(cwd, "zig-out", "bin", projectName); + if (nova.fs.stat(candidate)) { + programPath = candidate; + } + } + } if (!programPath) { nova.workspace.showWarningMessage( localizedText( @@ -859,7 +1159,7 @@ class ZigTaskAssistant { class ZigIssueAssistant { constructor() { this.disposable = nova.assistants.registerIssueAssistant( - [{ syntax: "zig" }, { syntax: "zig-package" }], + [{ syntax: "zig" }], this, { event: "onChange" } ); diff --git a/de.lproj/strings.json b/de.lproj/strings.json index 1834995..217617a 100644 --- a/de.lproj/strings.json +++ b/de.lproj/strings.json @@ -29,18 +29,33 @@ "Workspace Root": "Workspace-Wurzel", "Run Step": "Ausführungsschritt", "run": "run", + "install": "install", + "host": "host", "The `zig build` step to execute for the Run action.": "Der `zig build`-Schritt, der für die Ausführen-Aktion ausgeführt wird.", + "The `zig build` step to execute for the Run action. Leave blank to run `zig build` with no step (Zig's default install step).": "Der `zig build`-Schritt für die Ausführen-Aktion. Leer lassen, um `zig build` ohne Schritt auszuführen (Zigs Standardschritt `install`).", "Build Arguments": "Build-Argumente", "Additional arguments appended after `zig build` for both Build and Run.": "Zusätzliche Argumente, die nach `zig build` sowohl für Bauen als auch Ausführen angehängt werden.", + "Additional arguments appended after `zig build`.": "Zusätzliche Argumente, die nach `zig build` angehängt werden.", + "Additional arguments appended after `zig build test`.": "Zusätzliche Argumente, die nach `zig build test` angehängt werden.", "Program Arguments": "Programmargumente", "Arguments passed after `--` to the built program.": "Argumente, die nach `--` an das gebaute Programm übergeben werden.", - "Zig Package (macOS Terminal)": "Zig-Paket (macOS-Terminal)", - "Build a Zig package and run it in the external macOS Terminal app.": "Ein Zig-Paket bauen und in der externen macOS-Terminal-App ausführen.", + "Arguments passed after `--` to the test runner.": "Argumente, die nach `--` an den Test-Runner übergeben werden.", + "Optimize": "Optimierung", + "Project Default": "Projektstandard", + "Passed as `-Doptimize=<mode>`. Leave on Project Default to let `build.zig` decide.": "Wird als `-Doptimize=<mode>` übergeben. Bei „Projektstandard“ entscheidet `build.zig`.", + "Passed as `-Doptimize=<mode>`. Debug is the recommended default for stepping through code.": "Wird als `-Doptimize=<mode>` übergeben. „Debug“ ist der empfohlene Standard zum Durchlaufen des Codes.", + "Passed as `-Doptimize=<mode>`.": "Wird als `-Doptimize=<mode>` übergeben.", + "Target": "Ziel", + "Cross-compile target triple, passed as `-Dtarget=`. Leave blank for the host.": "Cross-Compile-Ziel-Triplet, wird als `-Dtarget=` übergeben. Leer lassen für den Host.", + "User Options": "Benutzeroptionen", + "Custom `-D` flags. Each entry is `key` (boolean flag) or `key=value`.": "Eigene `-D`-Flags. Jeder Eintrag ist `key` (boolesches Flag) oder `key=value`.", + "Where to run the program. External Terminal launches the macOS Terminal app.": "Wo das Programm ausgeführt werden soll. „Externes Terminal“ startet die macOS-Terminal-App.", "Zig Debug": "Zig-Debug", "Build a Zig package in Debug mode and launch it under lldb-dap.": "Ein Zig-Paket im Debug-Modus bauen und unter lldb-dap starten.", "Program": "Programm", "zig-out/bin/app": "zig-out/bin/app", "Path to the executable to debug. Relative paths are resolved against the working directory.": "Pfad zur zu debuggenden ausführbaren Datei. Relative Pfade werden gegen das Arbeitsverzeichnis aufgelöst.", + "Path to the executable to debug. Relative paths are resolved against the working directory. Leave blank to auto-detect from `build.zig.zon`.": "Pfad zur zu debuggenden ausführbaren Datei. Relative Pfade werden gegen das Arbeitsverzeichnis aufgelöst. Leer lassen, um aus `build.zig.zon` automatisch zu erkennen.", "Additional arguments appended after `zig build -Doptimize=Debug`.": "Zusätzliche Argumente, die nach `zig build -Doptimize=Debug` angehängt werden.", "Arguments passed to the debugged program.": "Argumente, die an das zu debuggende Programm übergeben werden.", "Console": "Konsole", @@ -64,5 +79,41 @@ "warning.current_file.focus_editor_for_clean": "Fokussiere einen Zig-Editor, bevor du Artefakte der „Aktuelle Zig-Datei“ bereinigst.", "warning.lldb_dap.not_found": "lldb-dap wurde nicht gefunden. Installiere die Xcode Command Line Tools oder setze einen LLDB-DAP-Ausführungspfad in den Zig-Erweiterungseinstellungen.", "warning.debug.choose_program": "Wähle vor dem Ausführen von Zig Debug einen Programmpfad aus.", - "warning.node.not_found": "Node.js wurde nicht gefunden. Installiere Node.js, damit der Zig-Debug-Adapter-Proxy laufen kann." + "warning.node.not_found": "Node.js wurde nicht gefunden. Installiere Node.js, damit der Zig-Debug-Adapter-Proxy laufen kann.", + "Debug Adapter": "Debug-Adapter", + "Controls for the lldb-dap integration.": "Steuerelemente für die lldb-dap-Integration.", + "Enable Proxy Log": "Proxy-Protokoll aktivieren", + "Write lldb-dap-proxy traffic to a log file in the extension's storage directory. For extension development only.": "lldb-dap-Proxy-Datenverkehr in eine Protokolldatei im Speicherverzeichnis der Erweiterung schreiben. Nur für die Erweiterungsentwicklung.", + "Zig Test": "Zig-Test", + "Run `zig build test` with optional --test-filter and --summary controls.": "Führt `zig build test` mit optionalen --test-filter- und --summary-Steuerungen aus.", + "Test Filter": "Testfilter", + "substring of test name": "Teil eines Testnamens", + "Substring of the test name. Passed as `--test-filter`.": "Teil des Testnamens. Wird als `--test-filter` übergeben.", + "Summary": "Zusammenfassung", + "Default": "Standard", + "All": "Alle", + "Failures only": "Nur Fehler", + "None": "Keine", + "Controls `zig build`'s --summary verbosity.": "Steuert die Ausführlichkeit von `zig build --summary`.", + "Zig Watch": "Zig-Watch", + "Run `zig build --watch` and rebuild on file changes. Note: inline issues only update on the first build cycle — Nova's matchers do not re-arm for streaming output.": "Führt `zig build --watch` aus und baut bei Dateiänderungen neu. Hinweis: Inline-Probleme werden nur im ersten Build-Zyklus aktualisiert — Novas Matcher werden für Streaming-Ausgaben nicht erneut aktiviert.", + "Step": "Schritt", + "Build step to watch. Leave blank for `zig build`'s default install step.": "Build-Schritt, der überwacht werden soll. Leer lassen für Zigs Standardschritt `install`.", + "Debounce (ms)": "Entprellung (ms)", + "Passed as `--debounce <N>`. Leave blank for Zig's default.": "Wird als `--debounce <N>` übergeben. Leer lassen für Zigs Standard.", + "Incremental": "Inkrementell", + "On": "Ein", + "Off": "Aus", + "Toggle `-fincremental` / `-fno-incremental`.": "Schaltet `-fincremental` / `-fno-incremental` um.", + "task.build_step.name": "Zig-Build: {step}", + "warning.clean.unsafe_cwd": "Reinigung verweigert: Das Arbeitsverzeichnis muss innerhalb dieses Workspaces liegen.", + "warning.fmt.no_file": "Öffne eine gespeicherte Zig-Datei, bevor du „Aktuelle Zig-Datei formatieren“ ausführst.", + "warning.fmt.not_zig": "„Aktuelle Zig-Datei formatieren“ funktioniert nur mit Zig-Dateien.", + "warning.fmt.no_workspace": "Öffne einen Workspace, bevor du „Workspace formatieren“ ausführst.", + "warning.fmt.failed": "zig fmt ist fehlgeschlagen.", + "Tasks": "Aufgaben", + "Controls for the task system.": "Steuerelemente für das Aufgabensystem.", + "Workspace-specific task settings.": "Workspace-spezifische Aufgabeneinstellungen.", + "Discover Build Steps": "Build-Schritte erkennen", + "Run `zig build --list-steps` and surface each step as a task in the task list. Disable for projects where running build.zig on activation is undesirable.": "Führt `zig build --list-steps` aus und zeigt jeden Schritt als Aufgabe in der Aufgabenliste an. Für Projekte deaktivieren, bei denen das Ausführen von build.zig beim Aktivieren unerwünscht ist." } diff --git a/extension.json b/extension.json index 8f455cd..83e5934 100644 --- a/extension.json +++ b/extension.json @@ -102,6 +102,20 @@ "description": "Write lldb-dap-proxy traffic to a log file in the extension's storage directory. For extension development only." } ] + }, + { + "type": "section", + "title": "Tasks", + "description": "Controls for the task system.", + "children": [ + { + "key": "at.dcz.nova-zig.tasks.discover-steps", + "title": "Discover Build Steps", + "type": "boolean", + "default": true, + "description": "Run `zig build --list-steps` and surface each step as a task in the task list. Disable for projects where running build.zig on activation is undesirable." + } + ] } ], "configWorkspace": [ @@ -170,6 +184,19 @@ "description": "Write lldb-dap-proxy traffic to a log file in the extension's storage directory. For extension development only." } ] + }, + { + "type": "section", + "title": "Tasks", + "description": "Workspace-specific task settings.", + "children": [ + { + "key": "at.dcz.nova-zig.tasks.discover-steps", + "title": "Discover Build Steps", + "type": "boolean", + "description": "Run `zig build --list-steps` and surface each step as a task in the task list. Disable for projects where running build.zig on activation is undesirable." + } + ] } ], "issueMatchers": { @@ -224,9 +251,36 @@ "key": "runStep", "title": "Run Step", "type": "string", - "default": "run", - "placeholder": "run", - "description": "The `zig build` step to execute for the Run action." + "default": "", + "placeholder": "install", + "description": "The `zig build` step to execute for the Run action. Leave blank to run `zig build` with no step (Zig's default install step)." + }, + { + "key": "optimize", + "title": "Optimize", + "type": "enum", + "default": "", + "values": [ + ["", "Project Default"], + ["Debug", "Debug"], + ["ReleaseSafe", "ReleaseSafe"], + ["ReleaseFast", "ReleaseFast"], + ["ReleaseSmall", "ReleaseSmall"] + ], + "description": "Passed as `-Doptimize=<mode>`. Leave on Project Default to let `build.zig` decide." + }, + { + "key": "target", + "title": "Target", + "type": "string", + "placeholder": "host", + "description": "Cross-compile target triple, passed as `-Dtarget=`. Leave blank for the host." + }, + { + "key": "userOptions", + "title": "User Options", + "type": "stringArray", + "description": "Custom `-D` flags. Each entry is `key` (boolean flag) or `key=value`." }, { "key": "buildArgs", @@ -239,23 +293,35 @@ "title": "Program Arguments", "type": "stringArray", "description": "Arguments passed after `--` to the built program." + }, + { + "key": "console", + "title": "Console", + "type": "enum", + "default": "internalConsole", + "values": [ + ["internalConsole", "Internal Console"], + ["externalTerminal", "External Terminal"] + ], + "description": "Where to run the program. External Terminal launches the macOS Terminal app." } ] }, - "zigBuildRunTerminal": { - "name": "Zig Package (macOS Terminal)", - "description": "Build a Zig package and run it in the external macOS Terminal app.", + "zigDebug": { + "name": "Zig Debug", + "description": "Build a Zig package in Debug mode and launch it under lldb-dap.", "tasks": { "build": { "resolve": "at.dcz.nova-zig.tasks", "data": { - "type": "build" + "type": "build-debug" } }, "run": { "resolve": "at.dcz.nova-zig.tasks", + "buildBeforeRunning": true, "data": { - "type": "build-run-terminal" + "type": "debug" } }, "clean": { @@ -276,42 +342,88 @@ "placeholder": "Workspace Root" }, { - "key": "runStep", - "title": "Run Step", + "key": "programPath", + "title": "Program", + "type": "path", + "allowFiles": true, + "allowFolders": false, + "relative": true, + "placeholder": "zig-out/bin/app", + "description": "Path to the executable to debug. Relative paths are resolved against the working directory. Leave blank to auto-detect from `build.zig.zon`." + }, + { + "key": "optimize", + "title": "Optimize", + "type": "enum", + "default": "Debug", + "values": [ + ["Debug", "Debug"], + ["ReleaseSafe", "ReleaseSafe"], + ["ReleaseFast", "ReleaseFast"], + ["ReleaseSmall", "ReleaseSmall"] + ], + "description": "Passed as `-Doptimize=<mode>`. Debug is the recommended default for stepping through code." + }, + { + "key": "target", + "title": "Target", "type": "string", - "default": "run", - "placeholder": "run", - "description": "The `zig build` step to execute for the Run action." + "placeholder": "host", + "description": "Cross-compile target triple, passed as `-Dtarget=`. Leave blank for the host." + }, + { + "key": "userOptions", + "title": "User Options", + "type": "stringArray", + "description": "Custom `-D` flags. Each entry is `key` (boolean flag) or `key=value`." }, { "key": "buildArgs", "title": "Build Arguments", "type": "stringArray", - "description": "Additional arguments appended after `zig build` for both Build and Run." + "description": "Additional arguments appended after `zig build`." }, { "key": "runArgs", "title": "Program Arguments", "type": "stringArray", - "description": "Arguments passed after `--` to the built program." + "description": "Arguments passed to the debugged program." + }, + { + "key": "console", + "title": "Console", + "type": "enum", + "default": "internalConsole", + "values": [ + ["internalConsole", "Internal Console"], + ["integratedTerminal", "Integrated Terminal"], + ["externalTerminal", "External Terminal"] + ], + "description": "Where the debugged program should run." + }, + { + "key": "stopOnEntry", + "title": "Stop On Entry", + "type": "boolean", + "default": false, + "description": "Pause immediately when the program starts." } ] }, - "zigDebug": { - "name": "Zig Debug", - "description": "Build a Zig package in Debug mode and launch it under lldb-dap.", + "zigTest": { + "name": "Zig Test", + "description": "Run `zig build test` with optional --test-filter and --summary controls.", "tasks": { "build": { "resolve": "at.dcz.nova-zig.tasks", "data": { - "type": "build-debug" + "type": "test-build" } }, "run": { "resolve": "at.dcz.nova-zig.tasks", - "buildBeforeRunning": true, "data": { - "type": "debug" + "type": "test-run" } }, "clean": { @@ -332,45 +444,151 @@ "placeholder": "Workspace Root" }, { - "key": "programPath", - "title": "Program", - "type": "path", - "allowFiles": true, - "allowFolders": false, - "relative": true, - "placeholder": "zig-out/bin/app", - "description": "Path to the executable to debug. Relative paths are resolved against the working directory." + "key": "testFilter", + "title": "Test Filter", + "type": "string", + "placeholder": "substring of test name", + "description": "Substring of the test name. Passed as `--test-filter`." + }, + { + "key": "summary", + "title": "Summary", + "type": "enum", + "default": "", + "values": [ + ["", "Default"], + ["all", "All"], + ["failures", "Failures only"], + ["none", "None"] + ], + "description": "Controls `zig build`'s --summary verbosity." + }, + { + "key": "optimize", + "title": "Optimize", + "type": "enum", + "default": "", + "values": [ + ["", "Project Default"], + ["Debug", "Debug"], + ["ReleaseSafe", "ReleaseSafe"], + ["ReleaseFast", "ReleaseFast"], + ["ReleaseSmall", "ReleaseSmall"] + ], + "description": "Passed as `-Doptimize=<mode>`." + }, + { + "key": "target", + "title": "Target", + "type": "string", + "placeholder": "host", + "description": "Cross-compile target triple, passed as `-Dtarget=`. Leave blank for the host." + }, + { + "key": "userOptions", + "title": "User Options", + "type": "stringArray", + "description": "Custom `-D` flags. Each entry is `key` (boolean flag) or `key=value`." }, { "key": "buildArgs", "title": "Build Arguments", "type": "stringArray", - "description": "Additional arguments appended after `zig build -Doptimize=Debug`." + "description": "Additional arguments appended after `zig build test`." }, { "key": "runArgs", "title": "Program Arguments", "type": "stringArray", - "description": "Arguments passed to the debugged program." + "description": "Arguments passed after `--` to the test runner." + } + ] + }, + "zigWatch": { + "name": "Zig Watch", + "description": "Run `zig build --watch` and rebuild on file changes. Note: inline issues only update on the first build cycle — Nova's matchers do not re-arm for streaming output.", + "tasks": { + "build": { + "resolve": "at.dcz.nova-zig.tasks", + "data": { + "type": "watch" + } + }, + "clean": { + "resolve": "at.dcz.nova-zig.tasks", + "data": { + "type": "clean" + } + } + }, + "config": [ + { + "key": "cwd", + "title": "Working Directory", + "type": "path", + "allowFiles": false, + "allowFolders": true, + "relative": true, + "placeholder": "Workspace Root" }, { - "key": "console", - "title": "Console", + "key": "step", + "title": "Step", + "type": "string", + "default": "", + "placeholder": "install", + "description": "Build step to watch. Leave blank for `zig build`'s default install step." + }, + { + "key": "debounceMs", + "title": "Debounce (ms)", + "type": "number", + "description": "Passed as `--debounce <N>`. Leave blank for Zig's default." + }, + { + "key": "incremental", + "title": "Incremental", "type": "enum", - "default": "internalConsole", + "default": "", "values": [ - ["internalConsole", "Internal Console"], - ["integratedTerminal", "Integrated Terminal"], - ["externalTerminal", "External Terminal"] + ["", "Default"], + ["on", "On"], + ["off", "Off"] ], - "description": "Where the debugged program should run." + "description": "Toggle `-fincremental` / `-fno-incremental`." }, { - "key": "stopOnEntry", - "title": "Stop On Entry", - "type": "boolean", - "default": false, - "description": "Pause immediately when the program starts." + "key": "optimize", + "title": "Optimize", + "type": "enum", + "default": "", + "values": [ + ["", "Project Default"], + ["Debug", "Debug"], + ["ReleaseSafe", "ReleaseSafe"], + ["ReleaseFast", "ReleaseFast"], + ["ReleaseSmall", "ReleaseSmall"] + ], + "description": "Passed as `-Doptimize=<mode>`." + }, + { + "key": "target", + "title": "Target", + "type": "string", + "placeholder": "host", + "description": "Cross-compile target triple, passed as `-Dtarget=`. Leave blank for the host." + }, + { + "key": "userOptions", + "title": "User Options", + "type": "stringArray", + "description": "Custom `-D` flags. Each entry is `key` (boolean flag) or `key=value`." + }, + { + "key": "buildArgs", + "title": "Build Arguments", + "type": "stringArray", + "description": "Additional arguments appended after `zig build`." } ] } |
