Adding a Chunk Option

Chunk options are the #| key: value lines at the top of a code chunk. They control evaluation, display, figure dimensions, and more. This page shows how to add a new one end-to-end.

We will add a hypothetical center-output: true option as a worked example.


Step 1 — Declare the option in ChunkOptions

crates/knot-core/src/parser/options.rs contains the ChunkOptions struct (the raw parsed options) and ResolvedChunkOptions (after merging defaults).

Add your field to both:

#![allow(unused)]
fn main() {
// In ChunkOptions (raw, all Option<T>)
pub center_output: Option<bool>,

// In ResolvedChunkOptions (resolved, concrete type with a default)
pub center_output: bool,
}

Then update ResolvedChunkOptions::resolve() to merge from the option chain (global defaults → language defaults → per-chunk):

#![allow(unused)]
fn main() {
center_output: per_chunk.center_output
    .or(lang_default.center_output)
    .or(global_default.center_output)
    .unwrap_or(false),
}

Step 2 — Parse the YAML key

The parser in parser/winnow_parser.rs reads #| lines as YAML strings. Option names use kebab-case in the source and are mapped to the ChunkOptions fields in the apply_option function:

#![allow(unused)]
fn main() {
"center-output" => {
    options.center_output = Some(
        value.parse::<bool>().map_err(|_| ParseError::InvalidOptionValue {
            key: "center-output",
            value: value.to_string(),
        })?
    );
}
}

Step 3 — Register metadata (drives completion + hover)

OptionMetadata in crates/knot-core/src/defaults.rs drives both the LSP completion list and hover documentation. Add an entry:

#![allow(unused)]
fn main() {
OptionMetadata {
    name: "center-output",
    kind: OptionKind::Bool,
    default: "false",
    description: "Center the output block horizontally in the document.",
},
}

Step 4 — Use the option in the backend

backend.rs renders each chunk node to Typst. In format_chunk(), pass the new option as a parameter to the #code-chunk(...) call:

#![allow(unused)]
fn main() {
if options.center_output {
    lines.push("  center-output: true,".to_string());
}
}

Step 5 — Handle it in lib/knot.typ

If the option affects rendering, add the corresponding logic to the code-chunk function in lib/knot.typ. For center-output:

#let code-chunk(
  // … existing params …
  center-output: false,
  body,
) = {
  // …
  if center-output {
    align(center, output-block)
  } else {
    output-block
  }
}

Step 6 — Add a snapshot test

Add a test case in crates/knot-core/src/backend.rs:

#![allow(unused)]
fn main() {
#[test]
fn test_format_chunk_center_output() {
    let backend = Backend::new(BackendOptions::default());
    let result = backend.format_chunk(&Node::CodeChunk {
        // … with center_output: true …
    });
    assert_snapshot!(result);
}
}

Run INSTA_UPDATE=always cargo test -p knot-core to generate the snapshot.


Step 7 — Expose in knot.toml

If the option should be configurable globally or per-language, add it to config.rs in the ChunkDefaults struct and handle it in load_config(). Document it in docs/book/src/chunk-options.md and docs/book/src/configuration.md.