# Zig for Nova — User Manual
This manual covers the task system. For installation, language-server, and
debugging prerequisites see [README.md](README.md).
---
## Table of Contents
- [Overview](#overview)
- [Creating a task](#creating-a-task)
- [Common options](#common-options)
- [Working Directory](#working-directory)
- [Optimize](#optimize)
- [Target](#target)
- [User Options](#user-options)
- [Build Arguments](#build-arguments)
- [Program Arguments](#program-arguments)
- [Task templates](#task-templates)
- [Zig Package](#zig-package)
- [Zig Debug](#zig-debug)
- [Zig Test](#zig-test)
- [Zig Watch](#zig-watch)
- [Automatic tasks](#automatic-tasks)
- [Current Zig File](#current-zig-file)
- [Discovered build steps](#discovered-build-steps)
- [Recipes](#recipes)
- [Run a single executable](#recipe-run-executable)
- [Run with a custom step name](#recipe-custom-step)
- [Cross-compile to a different target](#recipe-cross-compile)
- [Pass custom -D flags](#recipe-d-flags)
- [Run specific tests](#recipe-tests)
- [Ziglings-style projects](#recipe-ziglings)
- [Debug a binary that isn't in zig-out/bin](#recipe-debug)
- [Clean action](#clean-action)
- [Inline issue reporting](#inline-issues)
- [Settings reference](#settings)
---
## Overview
The extension integrates with `zig build` at three levels:
| Kind | How it appears | Configurable | Persistent |
|---|---|---|---|
| **Task template** | Added by you in Project Settings | Yes — full config UI | Yes — saved in `.nova/` |
| **Current Zig File** | Always present automatically | No | No |
| **Discovered step** | One per step in `zig build --list-steps` | No | No |
Task templates are the right tool when you need to control optimize mode,
target, test filters, or program arguments. Automatic tasks are zero-config
quick-launchers.
---
## Creating a task
1. Open **Project Settings** (`⌘,` with a project open, or **Project →
Project Settings…**).
2. Select the **Tasks** tab.
3. Click **+** and choose a template from the Zig section.
4. Fill in the options and close the panel — Nova saves the task automatically.
Each task exposes up to three action slots that map to Nova's toolbar buttons:
| Slot | Toolbar button | What it does |
|---|---|---|
| **Build** | ⌘B | Compiles without running |
| **Run** | ▶ | Builds (if needed) and runs |
| **Clean** | Broom icon | Removes build artifacts |
---
## Common options
Several options appear in more than one template. They are documented once
here and referenced from each template section.
### Working Directory
| Field | Default |
|---|---|
| Working Directory | Workspace root |
The directory from which `zig build` is invoked. Relative paths in other
options are resolved against this directory. Leave blank to use the workspace
root.
Change this for monorepos or sub-package layouts where `build.zig` lives
below the workspace root.
### Optimize
| Value | Flag emitted | When to use |
|---|---|---|
| **Project Default** | *(nothing)* | Let `build.zig` decide (most projects default to `Debug`) |
| **Debug** | `-Doptimize=Debug` | Development, debugging |
| **ReleaseSafe** | `-Doptimize=ReleaseSafe` | Production with safety checks |
| **ReleaseFast** | `-Doptimize=ReleaseFast` | Maximum performance |
| **ReleaseSmall** | `-Doptimize=ReleaseSmall` | Smallest binary size |
The flag is inserted immediately after `zig build`:
```
zig build -Doptimize=ReleaseFast …
```
### Target
| Field | Default |
|---|---|
| Target | *(host)* |
A Zig target triple such as `aarch64-macos`, `x86_64-linux-gnu`, or
`wasm32-wasi`. Passed as `-Dtarget=`. Leave blank to compile for the
machine you are running on.
```
zig build -Dtarget=aarch64-macos …
```
See `zig targets` for the full list of supported triples.
### User Options
A list of custom `-D` flags defined by the project's `build.zig`. Each entry
can be either:
- `key` — emits `-Dkey` (boolean `true`)
- `key=value` — emits `-Dkey=value`
Example entries and the flags they produce:
| Entry | Flag |
|---|---|
| `verbose` | `-Dverbose` |
| `n=42` | `-Dn=42` |
| `healed-path=patches/healed` | `-Dhealed-path=patches/healed` |
Entries that do not match the pattern `[A-Za-z_][A-Za-z0-9_-]*(=.*)?` are
silently skipped with a warning in the Extension Console.
### Build Arguments
Free-form arguments appended after the `-D` flags but before the step name.
Use this for `zig build` flags that are not exposed as dedicated fields, such
as `--summary all`, `--verbose`, or `-j4`.
```
zig build [-Doptimize=…] [-Dtarget=…] [-D…] [step] …
```
### Program Arguments
Arguments passed to the compiled program after `--`:
```
zig build … run --
```
For the **Zig Debug** template these are forwarded to the debugger's launch
request instead.
---
## Task templates
### Zig Package
> **Build → Run → Clean** a Zig package from a single task configuration.
**When to use:** everyday development — building, running, and cleaning a
package whose output is an executable.
#### Actions
| Action | Command |
|---|---|
| Build | `zig build [flags…]` |
| Run | `zig build [flags…] [step] [-- program-args]` |
| Clean | removes `.zig-cache`, `zig-cache`, `zig-out` |
The Build action compiles without running (equivalent to `zig build` with no
run step). The Run action builds *and* runs in a single `zig build` invocation
— Nova does not chain them; `zig build` handles both internally.
#### Options
| Option | Type | Default | Description |
|---|---|---|---|
| Working Directory | path | workspace root | See [Working Directory](#working-directory) |
| Run Step | string | *(blank)* | Step name for the Run action. See below. |
| Optimize | enum | Project Default | See [Optimize](#optimize) |
| Target | string | host | See [Target](#target) |
| User Options | string list | — | See [User Options](#user-options) |
| Build Arguments | string list | — | See [Build Arguments](#build-arguments) |
| Program Arguments | string list | — | See [Program Arguments](#program-arguments) |
| Console | enum | Internal Console | Where to run the program |
#### Run Step
The step name appended to `zig build` for the Run action. Common values:
| Value | Command produced | When |
|---|---|---|
| *(blank)* | `zig build` | Default install step — artifacts land in `zig-out/` |
| `run` | `zig build run` | Project exposes a `run` step |
| `serve` | `zig build serve` | Any custom step name |
Leave blank if the project uses `-D` flags instead of a step name (Ziglings
style — see [Recipes](#recipes)).
#### Console
| Value | Behaviour |
|---|---|
| **Internal Console** | Output appears in Nova's built-in console panel |
| **External Terminal** | Opens macOS Terminal.app in a new window or tab |
Use External Terminal for interactive programs that read from stdin.
---
### Zig Debug
> **Build → Debug** a Zig executable under lldb-dap.
**When to use:** stepping through code, setting breakpoints, inspecting
variables.
The Run action always triggers a Build first (`buildBeforeRunning`), so the
debugger always launches the freshest binary.
Requires `lldb-dap`, discovered automatically via `xcrun` or `PATH`. Install
the Xcode Command Line Tools if it is missing: `xcode-select --install`.
#### Actions
| Action | Command |
|---|---|
| Build | `zig build -Doptimize= [flags…]` |
| Run | launches lldb-dap (build runs first automatically) |
| Clean | removes `.zig-cache`, `zig-cache`, `zig-out` |
#### Options
| Option | Type | Default | Description |
|---|---|---|---|
| Working Directory | path | workspace root | See [Working Directory](#working-directory) |
| Program | path | *(auto-detected)* | Path to the executable to debug. See below. |
| Optimize | enum | **Debug** | See [Optimize](#optimize) |
| Target | string | host | See [Target](#target) |
| User Options | string list | — | See [User Options](#user-options) |
| Build Arguments | string list | — | See [Build Arguments](#build-arguments) |
| Program Arguments | string list | — | Forwarded to the debugged process |
| Console | enum | Internal Console | Where the debugged process runs |
| Stop On Entry | boolean | off | Pause at the very first instruction |
#### Program path auto-detection
When the **Program** field is blank, the extension reads `build.zig.zon` and
extracts the package `.name`. It then probes `zig-out/bin/` relative to
the working directory. If the file exists it is used automatically; otherwise
the task shows a warning asking you to fill in the field manually.
This works for the common case of a single executable whose name matches the
package name. For other layouts — multiple executables, custom install
prefixes, library packages — set the path explicitly.
#### Console (debug)
| Value | Where the debugged process runs |
|---|---|
| **Internal Console** | Nova's built-in console (default) |
| **Integrated Terminal** | Nova's integrated terminal |
| **External Terminal** | macOS Terminal.app |
---
### Zig Test
> **Build → Run → Clean** tests via `zig build test`.
**When to use:** running the project's test suite, optionally filtered to a
subset of tests.
#### Actions
| Action | Command |
|---|---|
| Build | `zig build test [flags…]` |
| Run | `zig build test [flags…] [--summary=…] [--test-filter …] [-- runner-args]` |
| Clean | removes `.zig-cache`, `zig-cache`, `zig-out` |
Both Build and Run invoke `zig build test`. The distinction is that the Run
action additionally applies the Test Filter and Summary fields.
Test failures surface as inline issues in the editor via the same
`file:line:col: error:` pattern used for compiler errors.
#### Options
| Option | Type | Default | Description |
|---|---|---|---|
| Working Directory | path | workspace root | See [Working Directory](#working-directory) |
| Test Filter | string | — | Substring filter on test names. See below. |
| Summary | enum | Default | Verbosity of `--summary`. See below. |
| Optimize | enum | Project Default | See [Optimize](#optimize) |
| Target | string | host | See [Target](#target) |
| User Options | string list | — | See [User Options](#user-options) |
| Build Arguments | string list | — | Appended after `zig build test` |
| Program Arguments | string list | — | Passed after `--` to the test runner |
#### Test Filter
A substring of the test name. Only tests whose names contain this string are
run. Maps directly to `zig build`'s `--test-filter` flag.
```
zig build test --test-filter "parser"
```
Leave blank to run all tests.
#### Summary
Controls how much output `zig build` prints about the test run.
| Value | Flag | Output |
|---|---|---|
| **Default** | *(nothing)* | Zig's built-in default |
| **All** | `--summary=all` | Every step result |
| **Failures only** | `--summary=failures` | Only failed steps |
| **None** | `--summary=none` | Silent on success |
---
### Zig Watch
> **Build** with `zig build --watch`, rebuilding automatically on file changes.
**When to use:** tight edit–compile loops where you want continuous feedback
without manually re-triggering the Build action.
> **⚠ Known limitation:** Nova's issue matchers fire only on the first build
> cycle. Errors found in subsequent automatic rebuilds will not appear as
> inline annotations in the editor. Re-run the task (stop and start again) to
> refresh the issue overlay.
#### Actions
| Action | Command |
|---|---|
| Build | `zig build [step] --watch [--debounce N] [-fincremental] [flags…]` |
| Clean | removes `.zig-cache`, `zig-cache`, `zig-out` |
There is no Run action — `zig build --watch` manages the build loop itself.
#### Options
| Option | Type | Default | Description |
|---|---|---|---|
| Working Directory | path | workspace root | See [Working Directory](#working-directory) |
| Step | string | *(blank)* | Step to watch. Leave blank for Zig's default install step. |
| Debounce (ms) | number | *(Zig default)* | Milliseconds to wait after a change before rebuilding. |
| Incremental | enum | Default | Force incremental compilation on or off. |
| Optimize | enum | Project Default | See [Optimize](#optimize) |
| Target | string | host | See [Target](#target) |
| User Options | string list | — | See [User Options](#user-options) |
| Build Arguments | string list | — | See [Build Arguments](#build-arguments) |
#### Debounce
Passed as `--debounce ` to `zig build`. Controls how long Zig waits after
detecting a file change before starting a rebuild. Useful on projects with
slow file-system events or many simultaneous saves. Leave blank to use Zig's
built-in default.
#### Incremental
| Value | Flag | Effect |
|---|---|---|
| **Default** | *(nothing)* | Zig decides |
| **On** | `-fincremental` | Force incremental compilation on |
| **Off** | `-fno-incremental` | Force incremental compilation off |
---
## Automatic tasks
Automatic tasks appear in the task list without any configuration. They cannot
be edited and are not saved in project settings.
### Current Zig File
Always present. Targets whichever `.zig` file is currently focused in the
editor.
| Action | Command |
|---|---|
| Run | `zig run ` (in the file's directory) |
| Clean | removes `.zig-cache`, `zig-cache`, `zig-out` from the nearest ancestor directory that contains a `build.zig` |
The Clean action walks up the directory tree from the active file until it
finds a `build.zig`, then cleans that project root. If no `build.zig` is
found above the file, it falls back to the workspace root.
**Note:** there is no Build action — `zig run` compiles and runs in one step.
---
### Discovered build steps
When the workspace contains a `build.zig`, the extension runs
`zig build --list-steps` in the background and creates one task per step:
```
Zig Build: install
Zig Build: run
Zig Build: test
Zig Build: bench
…
```
Each task has a single **Run** action that invokes `zig build ` in the
workspace root with no extra flags.
#### How discovery works
1. On workspace open (or task-list refresh), the extension stats `build.zig`
and `build.zig.zon` to capture their modification times.
2. It spawns `zig build --list-steps` asynchronously — the workspace is usable
immediately; discovered tasks appear once the command finishes.
3. The result is cached. The cache is considered fresh if:
- Both `build.zig` and `build.zig.zon` have the same mtimes as when last
fetched, **and**
- The cache is less than 5 minutes old.
4. When `build.zig` or `build.zig.zon` changes on disk, the cache is
invalidated and `--list-steps` is re-run automatically.
#### What steps appear
`zig build --list-steps` executes `build.zig` with no `-D` options, so only
the steps registered unconditionally by the build script are visible. Steps
that are registered only under a `-D` branch (such as Ziglings' `zigling` and
`random` steps) do not appear unless that branch is taken — and it won't be,
because the discovery command passes no flags.
Steps whose names are not valid identifiers (`[A-Za-z_][A-Za-z0-9_-]*`) are
silently filtered out.
#### Disabling discovery
Some projects run expensive logic at the top of `build()` that you may not
want triggered on every workspace open. Discovery can be turned off:
- **Globally:** Nova Preferences → Extensions → Zig → Tasks → uncheck
**Discover Build Steps**.
- **Per workspace:** Project Settings → Zig → Tasks → uncheck **Discover Build
Steps** (overrides the global setting for this project only).
When disabled, the `Zig Build: ` tasks disappear; the
[Current Zig File](#current-zig-file) task and all configured templates are
unaffected.
---
## Recipes
### Run a single executable
Create a **Zig Package** task.
- **Run Step:** `run` (or the name your `build.zig` uses for the run step)
- Leave everything else at defaults.
The Run (▶) button compiles and launches the program in Nova's console.
### Run with a custom step name
Same as above but change **Run Step** to whatever `b.step(…)` name your
`build.zig` declares:
```zig
const serve_step = b.step("serve", "Start the HTTP server");
```
→ set **Run Step** to `serve`.
### Cross-compile to a different target
Create a **Zig Package** task and set:
- **Target:** `aarch64-linux-musl`
- **Optimize:** `ReleaseSmall`
The command produced will be:
```
zig build -Doptimize=ReleaseSmall -Dtarget=aarch64-linux-musl run
```
### Pass custom -D flags
Your `build.zig` declares:
```zig
const verbose = b.option(bool, "verbose", "Enable verbose output") orelse false;
const port = b.option(u16, "port", "Listening port") orelse 8080;
```
Add two entries to **User Options**:
| Entry | Flag produced |
|---|---|
| `verbose` | `-Dverbose` |
| `port=9000` | `-Dport=9000` |
### Run specific tests
Create a **Zig Test** task and set:
- **Test Filter:** `parser` (runs any test whose name contains "parser")
- **Summary:** Failures only (quieter output when most tests pass)
### Ziglings-style projects
Ziglings selects exercises with `-Dn=` rather than a named step.
For "run all exercises" use a **Zig Package** task with:
- **Run Step:** *(blank)* — runs `zig build`, which hits the default `ziglings`
step
For a single exercise use:
- **Run Step:** *(blank)*
- **User Options:** `n=42`
This produces `zig build -Dn=42`.
Alternatively, if you have step discovery enabled, **Zig Build: ziglings**
appears automatically and covers the "run all" case with one click.
### Debug a binary that isn't in zig-out/bin
Create a **Zig Debug** task and set **Program** explicitly:
- `zig-out/bin/my-tool` (relative to Working Directory)
- or an absolute path
When the field is left blank the extension checks `build.zig.zon` for the
package name and probes `zig-out/bin/`. If your binary name doesn't
match the package name, set the path manually.
---
## Clean action
Every template task includes a Clean action. It:
1. Checks that the working directory is a real absolute path inside the current
workspace. Clean refuses to run if the path resolves to `/`, `$HOME`, or
anywhere outside the workspace root, and shows a warning instead.
2. If the project's `build.zig` exposes an `uninstall` step (already in the
step discovery cache), runs `zig build uninstall` first.
3. Removes `.zig-cache`, `zig-cache`, and `zig-out` with `rm -rf`.
The [Current Zig File](#current-zig-file) automatic task's Clean action
applies the same logic but targets the nearest ancestor directory that contains
a `build.zig`, rather than a configured working directory.
---
## Inline issue reporting
Compiler and test errors are shown as inline annotations in the editor using
the pattern:
```
::: error:
::: warning:
```
This covers output from `zig build`, `zig build test`, `zig run`, `zig test`,
and `zig fmt` (non-`--check` mode).
`zig fmt --check` only prints filenames, not line numbers, so it does not
produce inline annotations.
For the **Zig Watch** template, annotations are populated from the first build
cycle only. Subsequent automatic rebuilds do not update them — stop and restart
the task to refresh.
---
## Settings reference
Settings live in two places:
- **Nova Preferences → Extensions → Zig** — global defaults for all projects.
- **Project Settings → Zig** — workspace-specific overrides. These take
precedence over the global settings for the open project.
### Tooling
| Setting | Description |
|---|---|
| Zig Executable | Absolute path to `zig`. Leave blank to discover from `PATH`. |
| ZLS Executable | Absolute path to `zls`. Leave blank to discover from `PATH`. |
| LLDB DAP Executable | Absolute path to `lldb-dap`. Leave blank to discover via `xcrun` or `PATH`. |
### Language Server
| Setting | Default | Description |
|---|---|---|
| Enable ZLS | on | Enable diagnostics, completion, hover, go-to-definition, and formatting via ZLS. |
| Build On Save | off | Allow ZLS to run its build/check runner when you save a file. |
| Debug Server Messages | off | Log ZLS protocol traffic to the Extension Console (for troubleshooting). |
### Debug Adapter
| Setting | Default | Description |
|---|---|---|
| Enable Proxy Log | off | Write lldb-dap proxy traffic to a log file. For extension development only. |
### Tasks
| Setting | Default | Description |
|---|---|---|
| Discover Build Steps | on | Run `zig build --list-steps` and surface each step as a task. Disable for projects where executing `build.zig` on activation is undesirable. |