use std::fmt::Write as _;
use std::path::PathBuf;
use crate::config::LintConfig;
use crate::linter::check::check_source_with_target;
use crate::linter::fix::apply_fixes;
use crate::linter::render::{OutputMode, render_findings};
use crate::linter::rules::Rule;
fn example_path() -> PathBuf {
PathBuf::from("example.jl")
}
pub fn render_rule_doc(rule: &dyn Rule) -> String {
let mut out = String::new();
let id = rule.id();
let _ = writeln!(out, "## `{id}`");
let description = rule.description().trim();
if !description.is_empty() {
let _ = writeln!(out);
let _ = writeln!(out, "{description}");
}
let config = LintConfig {
select: Some(vec![id.to_string()]),
..Default::default()
};
let path = example_path();
for example in rule.examples() {
let _ = writeln!(out);
if !example.caption.is_empty() {
let _ = writeln!(out, "{}", example.caption);
let _ = writeln!(out);
}
fenced(&mut out, "julia", example.source);
let report = check_source_with_target(
Some(&path),
example.source,
&config,
rule.example_julia_target(),
);
let source = example.source.to_string();
let rendered = render_findings(&report.diagnostics, OutputMode::Pretty, false, &|p| {
(p == Some(path.as_path())).then(|| source.clone())
});
let _ = writeln!(out);
fenced(&mut out, "text", &rendered);
let fixed = apply_fixes(example.source, &report.diagnostics, false);
if fixed.output != example.source {
let _ = writeln!(out);
let _ = writeln!(out, "After applying the fix:");
let _ = writeln!(out);
fenced(&mut out, "julia", &fixed.output);
}
}
out
}
fn fenced(out: &mut String, lang: &str, body: &str) {
let _ = writeln!(out, "```{lang}");
let _ = out.write_str(body);
if !body.ends_with('\n') {
let _ = out.write_str("\n");
}
let _ = writeln!(out, "```");
}
pub fn render_reference_page() -> String {
let mut out = String::from(PREAMBLE);
for rule in crate::linter::rules::all_rules() {
if rule.examples().is_empty() {
continue;
}
out.push('\n');
out.push_str(&render_rule_doc(rule.as_ref()));
}
out
}
const PREAMBLE: &str = "\
<!-- Generated by `cargo run --example docgen`. Do not edit by hand: edit each \
rule's `description()`/`examples()` in `src/linter/rules/` and regenerate. -->
# Lint Rules
`fatou lint` runs a set of built-in rules over each file and reports a
diagnostic for every finding. This page is the catalogue: one section per rule,
keyed by its stable **rule ID**. That ID is what appears in a diagnostic, what
`[lint]` `select`/`ignore` (and `--select`/`--ignore`) target, what
`[lint.severity]` re-grades, and what a `# fatou-ignore <id>` comment
suppresses.
Most rules are on by default. The few that are opt-in say so in their
description; name one in `select` to run it.
Where a rewrite is unambiguous a rule carries an **autofix**: a *safe* fix
(shown below as \"After applying the fix\") is applied by `fatou lint --fix`; an
*unsafe* fix, one that may change behavior, is applied only with
`--unsafe-fixes` or as an editor code action, so it has no \"after\" block here.
Each example below is linted live to produce its diagnostics and fixed output,
so this page never drifts from the rules' actual behavior.
";