diff options
| -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`." } ] } |
