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

@ -24,10 +24,9 @@ define_violation!(
/// `__init__.py` file is typically meant to be a regular package, and /// `__init__.py` file is typically meant to be a regular package, and
/// the absence of the `__init__.py` file is probably an oversight. /// the absence of the `__init__.py` file is probably an oversight.
/// ///
/// Note that namespace packages can be specified via the /// ## Options
/// [`namespace-packages`](https://github.com/charliermarsh/ruff#namespace-packages) ///
/// configuration option. Adding a namespace package to the configuration /// * `namespace-packages`
/// will suppress this violation for a given package.
pub struct ImplicitNamespacePackage { pub struct ImplicitNamespacePackage {
pub filename: String, pub filename: String,
} }

View file

@ -14,13 +14,16 @@ use crate::violation::AlwaysAutofixableViolation;
define_violation!( define_violation!(
/// ## What it does /// ## What it does
/// Checks for inline strings that use single quotes or double quotes, /// Checks for inline strings that use single quotes or double quotes,
/// depending on the value of the [`inline-quotes`](https://github.com/charliermarsh/ruff#inline-quotes) /// depending on the value of the [`flake8-quotes.inline-quotes`] option.
/// setting.
/// ///
/// ## Why is this bad? /// ## Why is this bad?
/// Consistency is good. Use either single or double quotes for inline /// Consistency is good. Use either single or double quotes for inline
/// strings, but be consistent. /// strings, but be consistent.
/// ///
/// ## Options
///
/// * `flake8-quotes.inline-quotes`
///
/// ## Example /// ## Example
/// ```python /// ```python
/// foo = 'bar' /// foo = 'bar'
@ -56,13 +59,17 @@ impl AlwaysAutofixableViolation for BadQuotesInlineString {
define_violation!( define_violation!(
/// ## What it does /// ## What it does
/// Checks for multiline strings that use single quotes or double quotes, /// Checks for multiline strings that use single quotes or double quotes,
/// depending on the value of the [`multiline-quotes`](https://github.com/charliermarsh/ruff#multiline-quotes) /// depending on the value of the [`flake8-quotes.multiline-quotes`]
/// setting. /// setting.
/// ///
/// ## Why is this bad? /// ## Why is this bad?
/// Consistency is good. Use either single or double quotes for multiline /// Consistency is good. Use either single or double quotes for multiline
/// strings, but be consistent. /// strings, but be consistent.
/// ///
/// ## Options
///
/// * `flake8-quotes.multiline-quotes`
///
/// ## Example /// ## Example
/// ```python /// ```python
/// foo = ''' /// foo = '''
@ -101,13 +108,17 @@ impl AlwaysAutofixableViolation for BadQuotesMultilineString {
define_violation!( define_violation!(
/// ## What it does /// ## What it does
/// Checks for docstrings that use single quotes or double quotes, depending on the value of the [`docstring-quotes`](https://github.com/charliermarsh/ruff#docstring-quotes) /// Checks for docstrings that use single quotes or double quotes, depending
/// setting. /// on the value of the [`flake8-quotes.docstring-quotes`] setting.
/// ///
/// ## Why is this bad? /// ## Why is this bad?
/// Consistency is good. Use either single or double quotes for docstring /// Consistency is good. Use either single or double quotes for docstring
/// strings, but be consistent. /// strings, but be consistent.
/// ///
/// ## Options
///
/// * `flake8-quotes.docstring-quotes`
///
/// ## Example /// ## Example
/// ```python /// ```python
/// ''' /// '''

View file

@ -48,11 +48,9 @@ define_violation!(
/// > from .sibling import example /// > from .sibling import example
/// > ``` /// > ```
/// ///
/// Note that degree of strictness packages can be specified via the /// ## Options
/// [`ban-relative-imports`](https://github.com/charliermarsh/ruff#ban-relative-imports) ///
/// configuration option, which allows banning all relative imports /// * `flake8-tidy-imports.ban-relative-imports`
/// (`ban-relative-imports = "all"`) or only those that extend into the parent module or beyond
/// (`ban-relative-imports = "parents"`, the default).
/// ///
/// ## Example /// ## Example
/// ```python /// ```python

View file

@ -18,6 +18,10 @@ define_violation!(
/// ## Why is this bad? /// ## Why is this bad?
/// Functions with a high complexity are hard to understand and maintain. /// Functions with a high complexity are hard to understand and maintain.
/// ///
/// ## Options
///
/// * `mccabe.max-complexity`
///
/// ## Example /// ## Example
/// ```python /// ```python
/// def foo(a, b, c): /// def foo(a, b, c):

View file

@ -23,9 +23,13 @@ define_violation!(
/// A variable that is defined but not used is likely a mistake, and should be /// A variable that is defined but not used is likely a mistake, and should be
/// removed to avoid confusion. /// removed to avoid confusion.
/// ///
/// If a variable is intentionally defined-but-not-used, it should be prefixed /// If a variable is intentionally defined-but-not-used, it should be
/// with an underscore, or some other value that adheres to the /// prefixed with an underscore, or some other value that adheres to the
/// [`dummy-variable-rgx`](https://github.com/charliermarsh/ruff#dummy-variable-rgx) pattern. /// [`dummy-variable-rgx`] pattern.
///
/// ## Options
///
/// * `dummy-variable-rgx`
/// ///
/// ## Example /// ## Example
/// ```python /// ```python

View file

@ -5,6 +5,8 @@ use std::fs;
use anyhow::Result; use anyhow::Result;
use ruff::registry::{Linter, Rule, RuleNamespace}; use ruff::registry::{Linter, Rule, RuleNamespace};
use ruff::settings::options::Options;
use ruff::settings::options_base::ConfigurationOptions;
use ruff::AutofixAvailability; use ruff::AutofixAvailability;
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
@ -37,7 +39,7 @@ pub fn main(args: &Args) -> Result<()> {
output.push('\n'); output.push('\n');
} }
output.push_str(explanation.trim()); process_documentation(explanation.trim(), &mut output);
if args.dry_run { if args.dry_run {
println!("{output}"); println!("{output}");
@ -49,3 +51,35 @@ pub fn main(args: &Args) -> Result<()> {
} }
Ok(()) 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);
}
}

View file

@ -5,13 +5,17 @@ Derived from the **flake8-quotes** linter.
Autofix is always available. Autofix is always available.
## What it does ## What it does
Checks for docstrings that use single quotes or double quotes, depending on the value of the [`docstring-quotes`](https://github.com/charliermarsh/ruff#docstring-quotes) Checks for docstrings that use single quotes or double quotes, depending
setting. on the value of the [`flake8-quotes.docstring-quotes`] setting.
## Why is this bad? ## Why is this bad?
Consistency is good. Use either single or double quotes for docstring Consistency is good. Use either single or double quotes for docstring
strings, but be consistent. strings, but be consistent.
## Options
* [`flake8-quotes.docstring-quotes`]
## Example ## Example
```python ```python
''' '''
@ -25,3 +29,5 @@ Assuming `docstring-quotes` is set to `double`, use instead:
bar bar
""" """
``` ```
[`flake8-quotes.docstring-quotes`]: ../../settings#docstring-quotes

View file

@ -6,13 +6,16 @@ Autofix is always available.
## What it does ## What it does
Checks for inline strings that use single quotes or double quotes, Checks for inline strings that use single quotes or double quotes,
depending on the value of the [`inline-quotes`](https://github.com/charliermarsh/ruff#inline-quotes) depending on the value of the [`flake8-quotes.inline-quotes`] option.
setting.
## Why is this bad? ## Why is this bad?
Consistency is good. Use either single or double quotes for inline Consistency is good. Use either single or double quotes for inline
strings, but be consistent. strings, but be consistent.
## Options
* [`flake8-quotes.inline-quotes`]
## Example ## Example
```python ```python
foo = 'bar' foo = 'bar'
@ -22,3 +25,5 @@ Assuming `inline-quotes` is set to `double`, use instead:
```python ```python
foo = "bar" foo = "bar"
``` ```
[`flake8-quotes.inline-quotes`]: ../../settings#inline-quotes

View file

@ -6,13 +6,17 @@ Autofix is always available.
## What it does ## What it does
Checks for multiline strings that use single quotes or double quotes, Checks for multiline strings that use single quotes or double quotes,
depending on the value of the [`multiline-quotes`](https://github.com/charliermarsh/ruff#multiline-quotes) depending on the value of the [`flake8-quotes.multiline-quotes`]
setting. setting.
## Why is this bad? ## Why is this bad?
Consistency is good. Use either single or double quotes for multiline Consistency is good. Use either single or double quotes for multiline
strings, but be consistent. strings, but be consistent.
## Options
* [`flake8-quotes.multiline-quotes`]
## Example ## Example
```python ```python
foo = ''' foo = '''
@ -26,3 +30,5 @@ foo = """
bar bar
""" """
``` ```
[`flake8-quotes.multiline-quotes`]: ../../settings#multiline-quotes

View file

@ -13,6 +13,10 @@ the code where the program has a choice of two or more paths to follow.
## Why is this bad? ## Why is this bad?
Functions with a high complexity are hard to understand and maintain. Functions with a high complexity are hard to understand and maintain.
## Options
* [`mccabe.max-complexity`]
## Example ## Example
```python ```python
def foo(a, b, c): def foo(a, b, c):
@ -39,3 +43,5 @@ def foo(a, b, c):
return 2 return 2
return 1 return 1
``` ```
[`mccabe.max-complexity`]: ../../settings#max-complexity

View file

@ -18,7 +18,9 @@ Namespace packages are less widely used, so a package that lacks an
`__init__.py` file is typically meant to be a regular package, and `__init__.py` file is typically meant to be a regular package, and
the absence of the `__init__.py` file is probably an oversight. the absence of the `__init__.py` file is probably an oversight.
Note that namespace packages can be specified via the ## Options
[`namespace-packages`](https://github.com/charliermarsh/ruff#namespace-packages)
configuration option. Adding a namespace package to the configuration * [`namespace-packages`]
will suppress this violation for a given package.
[`namespace-packages`]: ../../settings#namespace-packages

View file

@ -24,11 +24,9 @@ Absolute imports, or relative imports from siblings, are recommended by [PEP 8](
> from .sibling import example > from .sibling import example
> ``` > ```
Note that degree of strictness packages can be specified via the ## Options
[`ban-relative-imports`](https://github.com/charliermarsh/ruff#ban-relative-imports)
configuration option, which allows banning all relative imports * [`flake8-tidy-imports.ban-relative-imports`]
(`ban-relative-imports = "all"`) or only those that extend into the parent module or beyond
(`ban-relative-imports = "parents"`, the default).
## Example ## Example
```python ```python
@ -39,3 +37,5 @@ Use instead:
```python ```python
from mypkg import foo from mypkg import foo
``` ```
[`flake8-tidy-imports.ban-relative-imports`]: ../../settings#ban-relative-imports

View file

@ -11,9 +11,13 @@ Checks for the presence of unused variables in function scopes.
A variable that is defined but not used is likely a mistake, and should be A variable that is defined but not used is likely a mistake, and should be
removed to avoid confusion. removed to avoid confusion.
If a variable is intentionally defined-but-not-used, it should be prefixed If a variable is intentionally defined-but-not-used, it should be
with an underscore, or some other value that adheres to the prefixed with an underscore, or some other value that adheres to the
[`dummy-variable-rgx`](https://github.com/charliermarsh/ruff#dummy-variable-rgx) pattern. [`dummy-variable-rgx`] pattern.
## Options
* [`dummy-variable-rgx`]
## Example ## Example
```python ```python
@ -29,3 +33,5 @@ def foo():
x = 1 x = 1
return x return x
``` ```
[`dummy-variable-rgx`]: ../../settings#dummy-variable-rgx