Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enforce formatting of code examples in Bevy book #279

Closed
alice-i-cecile opened this issue Feb 12, 2022 · 11 comments · Fixed by #1578
Closed

Enforce formatting of code examples in Bevy book #279

alice-i-cecile opened this issue Feb 12, 2022 · 11 comments · Fixed by #1578
Labels
A-Book A-Build-System S-Ready-For-Implementation The core questions are answered: just add code

Comments

@alice-i-cecile
Copy link
Member

It can be quite challenging to maintain an appropriately consistent style when writing code snippets for examples. Indentation errors, offset parentheses and so on reduce the perception of quality to new readers, and distract from the learning experience.

Fortunately, cargo fmt is very good. We should ensure that submitted code passes cargo fmt. This is very closely related to #83, and we should ensure that the solution we select for that can be extended to fit this.

Ideally, this process is also easy for authors to use. We probably won't be able to use cargo fmt directly, as the Rust files are embedded inside markdown, but a script that applies the formatting to all code blocks and can be run on command would be massively appreciated.

@BD103
Copy link
Member

BD103 commented Mar 1, 2024

This can be accomplished with the unstable format_code_in_doc_comments rustfmt option, but there are a few open issues related to it.

Perhaps this could be linted in CI, but not required to merge?

@alice-i-cecile
Copy link
Member Author

Yeah, I'd be happy to try that out. We'd want to add advice on how to run that command locally too.

@BD103 BD103 added the S-Ready-For-Implementation The core questions are answered: just add code label Mar 1, 2024
@TrialDragon
Copy link
Member

TrialDragon commented Mar 6, 2024

This can be accomplished with the unstable format_code_in_doc_comments rustfmt option, but there are a few open issues related to it.

Perhaps this could be linted in CI, but not required to merge?

That was the initial solution proposed in #281 but was proven not viable (at least at the time, maybe stuff has changed). It only worked if the code was already inside a Rust file and not brought in via stuff like #[doc = include_str!("_index.md")]. The only way this could work is if these were all rustdoc comments in a Rust file that are then brought into the Zola pages afaik. (Perhaps using our file code block shortcode?)

@TrialDragon
Copy link
Member

TrialDragon commented Mar 6, 2024

Other potential solutions I've considered, and are still viable in my mind, are:

  • Take all the code examples out of the markdown files and put them into individual Rust files as regular Rust code which can be validated and formatted with cargo check and cargo fmt and are then brought into the markdown files via shortcode or something.
    (This is my current preferred solution since it's clean, removes the frail code validator, is stable, and most certainly proven possible by the existence of the file code block shortcode. Would just need to either modify it, or make a derivative, that hides lines somehow which is one part of the design I still don't know. Maybe comments that say stuff like // hide which hide the line directly below when parsed? Main issue with that is the lines that should be hidden changing after the formatter gets it's hands on it).
  • Write a tool to parse the Rust code blocks, removing lines marked with the pound sign, and passing that to rustfmt via stdin then either checking or applying those changes to the markdown files. Main con is that this wouldn't work well with keeping / remembering the hidden lines after formatting and could play badly with the current code validator.

I think that was all of them, tho there may have been more I've forgotten. Overall has been a little complex issue to design a fix for I've been mulling over for a while.

@alice-i-cecile
Copy link
Member Author

Take all the code examples out of the markdown files and put them into individual Rust files as regular Rust code which can be validated and formatted with cargo check and cargo fmt and are then brought into the markdown files via shortcode or something.

This is the sort of direction I'm leaning towards as well. It's not perfect, but it seems a lot less fragile.

@BD103
Copy link
Member

BD103 commented Mar 6, 2024

I know write-rustdoc-hide-lines scans for all code blocks within Markdown files, so that could be a point of inspiration for writing this tool.

@TrialDragon
Copy link
Member

Hmm, would single example per file, where we explicitly hide lines, be desirable, or would allowing multiple examples per file, and explicitly defining what code is used per an example, be better. I'm leaning more towards the define example over what to hide since it seems less likely to break if we format code.


Single Example per File
// HIDE
use bevy::prelude::*;
  
fn main() {
    App::new().run();
}

And used like:

{% example(file="book/app/basic_app.rs") %}

Multiple Examples per File
use bevy::prelude::*;
  
// EXAMPLE: basic_app
fn main() {
    App::new().run();
}
// END_EXAMPLE

And used like:

{% example(file="book/app/basic_app.rs", example="basic_app") %}

or instead

{% example(file="book/app/basic_app.rs:basic_app") %}

(This whole format, and the last usage API, are heavily inspired by how ratatui.rs does it for their own site).


Note

Working on a PR for this issue.

@TrialDragon
Copy link
Member

Also another design question / consideration; how should "versioned" examples be handled? For example in the ECS section of Quick Start's Getting Started where throughout the chapter it changes a singular function to show progress. Like should we have example_file_v1.rs, example_file_v2.rs, example_file_vX.rs et cetera? Or should we just try to avoid this sort of evolution of code in examples?

@BD103
Copy link
Member

BD103 commented Jul 28, 2024

I like the multiple examples per file approach, since it reduces the total amount of files required. (I think it would be nice to also have the ability to hide lines of code, so that we could also run cargo check on it.)

For versioned, you could do:

use bevy::prelude::*;

mod v1 {
// EXAMPLE: basic_app_v1
fn main() {
    App::new().run();
}
// END_EXAMPLE
}

mod v2 {
// EXAMPLE: basic_app_v2
fn main() -> AppExit {
    App::new().run()
}
// END_EXAMPLE
}

Or some other form.

@TrialDragon
Copy link
Member

[…] (I think it would be nice to also have the ability to hide lines of code, so that we could also run cargo check on it.) […]

What do you mean? We can still run cargo check on the example rust files without hiding lines of code.

@BD103
Copy link
Member

BD103 commented Jul 28, 2024

What I meant is that sometimes code examples look like this:

commands.spawn(Planet::new());

But that won't compile by itself, so you need to add some extra hidden code:

# use bevy::prelude::*;
# use project::Planet;
#
# fn spawn_planet(mut commands: Commands) {
    commands.spawn(Planet::new());
# }

I want to make sure that you can still have this hidden code so that we can verify it using cargo check. In most scenarios, you can probably just put this outside of the // EXAMPLE and // END_EXAMPLE comments, but that make not always work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Book A-Build-System S-Ready-For-Implementation The core questions are answered: just add code
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants