Skip to content

feat(md,wit): format WIT and nested code inside markdown files - #379

Draft
mkatychev wants to merge 6 commits into
mainfrom
feat/format-markdown-wit
Draft

mkatychev wants to merge 6 commits into
mainfrom
feat/format-markdown-wit

Conversation

@mkatychev

Copy link
Copy Markdown
Member

!!WIP!!

WIT formatting:
topiary fmt ./**/*.wit

markdown formatting (only formats WIT, toml codeblocks):
topiary fmt ./**/*.md --skip-stage host --skip-language rust


world target-world {
include wasi:http/proxy@0.2.3;
include wasi:http/proxy@0.2.3;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

up to us what indentation we prefer, this can be set in .topiary/languages.ncl similar to the indent key here:
https://github.com/topiary/topiary/blob/c72efbfd164f98e5d2fab70a7c984280e0a90378/topiary-config/languages.ncl#L284-L294

(code_fence_content) @injection.content
; mdBook inline macro
(#not-match? @info ".*(nofmt|rust).*")
(#not-match? @injection.content "\\{\\{\\#include")

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

used to skip mdbook directives

A WASI 0.2 read call returned a single `input-stream` resource and surfaced terminal errors only as you consumed it. WASI 0.3 splits those concerns: the call returns a `stream<u8>` for the data and a `future<result<_, error-code>>` for the outcome, packed into a tuple.

```wit
```wit nofmt

@mkatychev mkatychev Oct 6, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nofmt is picked up by this query to skip WIT fragments:

(#not-match? @info ".*(nofmt|rust).*")

Comment on lines 8 to -11
include wasi:cli/imports@0.2.0;

export add;
} No newline at end of file

@mkatychev mkatychev Oct 6, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

up to us if we want to preserve double newlines between certain items

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah let's have newlines between this, but only for worlds? I like the idea of separating the imports, includes and exports in a world declaration

@mkatychev

Copy link
Copy Markdown
Member Author

Neat thing about diff grammar is that technically so adding a language hint can actually allow one to format it if the code fence start was changed to something like this by formatting new and old state separately:

```diff wit
world app { 
    import calculate; 
+   export wasi:cli/run@0.2.7; 
} 
```

```diff
world app {
import calculate;
+ export wasi:cli/run@0.2.7;
}
```

…ources/rust.md --skip-stage host --skip-language rust`
Comment thread .topiary/languages.ncl
},
queries.injections.source.path = "queries/injections.scm",
},
wit.indent = " ", # 4 spaces

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The repo has a mix of 2 and 4 space indentation for WIT and this helps align it.

@mkatychev

Copy link
Copy Markdown
Member Author

rustfmt canll be used for the rust formatting backend once topiary/bud#8 is implemented (as well as gofmt for go codeblocks and whatever else):

```rust
impl docs::rpn::types::HostEngine for MyHost {
fn new(

An `enum` type is a variant type where none of the cases have associated data:

```wit
```wit nofmt

@mkatychev mkatychev Oct 6, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nofmt will eventually be removed once hideline fragments are properly handled in topiary:

Suggested change
```wit nofmt
```wit,hidelines=!!!
!!! interface foo {

enum op {
add,
}
eval-expression: func(op: op, x: u32, y: u32) -> u32;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice if we defaulted to a space between items inside an interface.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this apply to worlds as well? We can do only a subset of each as well:
https://github.com/bytecodealliance/tree-sitter-wit/blob/f777cdbe11281ccc68ffa30bd7ea34cdf4ddbec6/grammar.js#L139-L146

    world_definition: ($) =>
      choice(
        $.export_item,
        $.import_item,
        $.use_item,
        $.typedef_item,
        $.include_item,
      ),
    // ...
    _interface_definition: ($) =>
      choice(
        $.use_item,
        seq(optional($.external_id), choice($.typedef_item, $.func_item)),
      ),

mkatychev added a commit to topiary/topiary that referenced this pull request Oct 10, 2026
# Fix WIT formatting errors encountered in markdown

References bytecodealliance/component-docs#379 bytecodealliance/tree-sitter-wit#33

## Description

Handle mdbook as well as newline separation between various WIT definitions


* added new WIT test case

* fix(wit): added new queries to handle edge case

* fix(wit): inline statement node

* revert(config): pin WIT gramar

* revert: errant changes

* Auto-publish releases to crates.io (#1344)

* Auto-publish releases to crates.io

* Update MAINTAINERS.md

* Update CHANGELOG.md

* log(config): add callout when failing to copy queries during fetch (#1343)

During `topiary prefetch`, nonexistent files are not called out when copying thus returning unclear errors:

```
 ● I/O Error
 ├ topiary-cli/src/config.rs:136
 │
 ● Error Fetching Language: We encountered an io error: No such file or directory (os error 2)
 ├ topiary-cli/src/config.rs:136
 ╰ topiary_config::error::TopiaryConfigError::Fetching(Io(Os { code: 2, kind: NotFound, message: "No such file or directory" }))
```

---------

Co-authored-by: Christopher Harrison <Xophmeister@users.noreply.github.com>

* fix(config): update WIT grammar sha

* fix(ci): prefetch markdown

---------

Co-authored-by: Christopher Harrison <Xophmeister@users.noreply.github.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants