Automatically linkify option references in rule documentation

Previously the rule documentation referenced configuration options
via full https:// URLs, which was bad for several reasons:

* changing the website would mean you'd have to change all URLs
* the links didn't work when building mkdocs locally
* the URLs showed up in the `ruff rule` output
* broken references weren't detected by our CI

This commit solves all of these problems by post-processing the
Markdown, recognizing sections such as:

    ## Options

    * `flake8-tidy-imports.ban-relative-imports`

`cargo dev generate-all` will automatically linkify such references
and panic if the referenced option doesn't exist.
Note that the option can also be linked in the other Markdown sections
via e.g. [`flake8-tidy-imports.ban-relative-imports`] since
the post-processing code generates a CommonMark link definition.

Resolves #2766.
This commit is contained in:
Martin Fischer 2023-02-11 19:57:40 +01:00 committed by Charlie Marsh
parent fc4c927788
commit 28c9263722
13 changed files with 122 additions and 41 deletions

View file

@ -5,6 +5,8 @@ use std::fs;
use anyhow::Result;
use ruff::registry::{Linter, Rule, RuleNamespace};
use ruff::settings::options::Options;
use ruff::settings::options_base::ConfigurationOptions;
use ruff::AutofixAvailability;
use strum::IntoEnumIterator;
@ -37,7 +39,7 @@ pub fn main(args: &Args) -> Result<()> {
output.push('\n');
}
output.push_str(explanation.trim());
process_documentation(explanation.trim(), &mut output);
if args.dry_run {
println!("{output}");
@ -49,3 +51,35 @@ pub fn main(args: &Args) -> Result<()> {
}
Ok(())
}
fn process_documentation(documentation: &str, out: &mut String) {
let mut in_options = false;
let mut after = String::new();
for line in documentation.split_inclusive('\n') {
if line.starts_with("## ") {
in_options = line == "## Options\n";
} else if in_options {
if let Some(rest) = line.strip_prefix("* `") {
let option = rest.trim_end().trim_end_matches('`');
assert!(
Options::get(Some(option)).is_some(),
"unknown option {option}"
);
let anchor = option.rsplit('.').next().unwrap();
out.push_str(&format!("* [`{option}`]\n"));
after.push_str(&format!("[`{option}`]: ../../settings#{anchor}"));
continue;
}
}
out.push_str(line);
}
if !after.is_empty() {
out.push_str("\n\n");
out.push_str(&after);
}
}