From 9b3c73253809f10f6f6522a487eb393fc6477d9c Mon Sep 17 00:00:00 2001 From: Auguste Lalande Date: Thu, 21 Mar 2024 12:33:58 -0400 Subject: [PATCH] Docs: Link inline settings when not part of options section (#10499) ## Summary Some contributors have referenced settings in their documentation without adding the settings to an options section, this has lead to some rendering issues (#10427). This PR addresses this looking for potential inline links to settings, cross-checking them with the options sections, and then linking them anyway if they are not found. Resolves #10427. ## Test Plan Manually verified that the correct modifications were made and no docs were broken. --- crates/ruff_dev/src/generate_docs.rs | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/crates/ruff_dev/src/generate_docs.rs b/crates/ruff_dev/src/generate_docs.rs index 2c3f24975a2a8..987b485db94cc 100644 --- a/crates/ruff_dev/src/generate_docs.rs +++ b/crates/ruff_dev/src/generate_docs.rs @@ -1,6 +1,7 @@ //! Generate Markdown documentation for applicable rules. #![allow(clippy::print_stdout, clippy::print_stderr)] +use std::collections::HashSet; use std::fs; use std::path::PathBuf; @@ -97,20 +98,22 @@ pub(crate) fn main(args: &Args) -> Result<()> { fn process_documentation(documentation: &str, out: &mut String, rule_name: &str) { let mut in_options = false; let mut after = String::new(); + let mut referenced_options = HashSet::new(); // HACK: This is an ugly regex hack that's necessary because mkdocs uses // a non-CommonMark-compliant Markdown parser, which doesn't support code // tags in link definitions // (see https://github.com/Python-Markdown/markdown/issues/280). - let documentation = Regex::new(r"\[`([^`]*?)`]($|[^\[\(])") - .unwrap() - .replace_all(documentation, |caps: &Captures| { + let documentation = Regex::new(r"\[`([^`]*?)`]($|[^\[(])").unwrap().replace_all( + documentation, + |caps: &Captures| { format!( "[`{option}`][{option}]{sep}", option = &caps[1], sep = &caps[2] ) - }); + }, + ); for line in documentation.split_inclusive('\n') { if line.starts_with("## ") { @@ -134,6 +137,7 @@ fn process_documentation(documentation: &str, out: &mut String, rule_name: &str) let anchor = option.replace('.', "_"); out.push_str(&format!("- [`{option}`][{option}]\n")); after.push_str(&format!("[{option}]: ../settings.md#{anchor}\n")); + referenced_options.insert(option); continue; } @@ -141,6 +145,20 @@ fn process_documentation(documentation: &str, out: &mut String, rule_name: &str) out.push_str(line); } + + let re = Regex::new(r"\[`([^`]*?)`]\[(.*?)]").unwrap(); + for (_, [option, _]) in re.captures_iter(&documentation).map(|c| c.extract()) { + if let Some(OptionEntry::Field(field)) = Options::metadata().find(option) { + if referenced_options.insert(option) { + let anchor = option.replace('.', "_"); + after.push_str(&format!("[{option}]: ../settings.md#{anchor}\n")); + } + if field.deprecated.is_some() { + eprintln!("Rule {rule_name} references deprecated option {option}."); + } + } + } + if !after.is_empty() { out.push('\n'); out.push('\n');