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)),
      ),

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