diff --git a/README.md b/README.md index 547ffc88f5..df8cbc9fb7 100644 --- a/README.md +++ b/README.md @@ -300,13 +300,14 @@ ruff path/to/code/ --select F401 --select F403 See `ruff --help` for more: + ```shell Ruff: An extremely fast Python linter. Usage: ruff [OPTIONS] [FILES]... Arguments: - [FILES]... + [FILES]... Options: --config @@ -381,7 +382,9 @@ Options: Print help information -V, --version Print version information + ``` + ### `pyproject.toml` discovery @@ -1669,7 +1672,6 @@ Summary - #### [`allowed-confusables`](#allowed-confusables) A list of allowed "confusable" Unicode characters to ignore when diff --git a/ruff_dev/src/generate_all.rs b/ruff_dev/src/generate_all.rs index 7f5dfa127f..97cf3a82d6 100644 --- a/ruff_dev/src/generate_all.rs +++ b/ruff_dev/src/generate_all.rs @@ -4,7 +4,8 @@ use anyhow::Result; use clap::Args; use crate::{ - generate_check_code_prefix, generate_json_schema, generate_options, generate_rules_table, + generate_check_code_prefix, generate_cli_help, generate_json_schema, generate_options, + generate_rules_table, }; #[derive(Args)] @@ -27,5 +28,8 @@ pub fn main(cli: &Cli) -> Result<()> { generate_options::main(&generate_options::Cli { dry_run: cli.dry_run, })?; + generate_cli_help::main(&generate_cli_help::Cli { + dry_run: cli.dry_run, + })?; Ok(()) } diff --git a/ruff_dev/src/generate_cli_help.rs b/ruff_dev/src/generate_cli_help.rs new file mode 100644 index 0000000000..8f00e909d8 --- /dev/null +++ b/ruff_dev/src/generate_cli_help.rs @@ -0,0 +1,34 @@ +//! Generate CLI help. + +use anyhow::Result; +use clap::{Args, CommandFactory}; +use ruff::cli::Cli as MainCli; + +use crate::utils::replace_readme_section; + +const HELP_BEGIN_PRAGMA: &str = ""; +const HELP_END_PRAGMA: &str = ""; + +#[derive(Args)] +pub struct Cli { + /// Write the generated help to stdout (rather than to `README.md`). + #[arg(long)] + pub(crate) dry_run: bool, +} + +pub fn main(cli: &Cli) -> Result<()> { + let mut cmd = MainCli::command(); + let output = cmd.render_help().to_string(); + + if cli.dry_run { + print!("{output}"); + } else { + replace_readme_section( + &format!("```shell\n{output}\n```\n"), + HELP_BEGIN_PRAGMA, + HELP_END_PRAGMA, + )?; + } + + Ok(()) +} diff --git a/ruff_dev/src/generate_options.rs b/ruff_dev/src/generate_options.rs index 781cd22199..689a72f2c8 100644 --- a/ruff_dev/src/generate_options.rs +++ b/ruff_dev/src/generate_options.rs @@ -1,16 +1,13 @@ //! Generate a Markdown-compatible listing of configuration options. -use std::fs; -use std::fs::OpenOptions; -use std::io::Write; -use std::path::PathBuf; - use anyhow::Result; use clap::Args; use itertools::Itertools; use ruff::settings::options::Options; use ruff::settings::options_base::{ConfigurationOptions, OptionEntry, OptionField}; +use crate::utils::replace_readme_section; + const BEGIN_PRAGMA: &str = ""; const END_PRAGMA: &str = ""; @@ -95,30 +92,7 @@ pub fn main(cli: &Cli) -> Result<()> { if cli.dry_run { print!("{output}"); } else { - // Read the existing file. - let file = PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .parent() - .expect("Failed to find root directory") - .join("README.md"); - let existing = fs::read_to_string(&file)?; - - // Extract the prefix. - let index = existing - .find(BEGIN_PRAGMA) - .expect("Unable to find begin pragma"); - let prefix = &existing[..index + BEGIN_PRAGMA.len()]; - - // Extract the suffix. - let index = existing - .find(END_PRAGMA) - .expect("Unable to find end pragma"); - let suffix = &existing[index..]; - - // Write the prefix, new contents, and suffix. - let mut f = OpenOptions::new().write(true).truncate(true).open(&file)?; - write!(f, "{prefix}\n\n")?; - write!(f, "{output}")?; - write!(f, "{suffix}")?; + replace_readme_section(&output, BEGIN_PRAGMA, END_PRAGMA)?; } Ok(()) diff --git a/ruff_dev/src/generate_rules_table.rs b/ruff_dev/src/generate_rules_table.rs index a6436f9f6b..26523d79c9 100644 --- a/ruff_dev/src/generate_rules_table.rs +++ b/ruff_dev/src/generate_rules_table.rs @@ -1,16 +1,13 @@ //! Generate a Markdown-compatible table of supported lint rules. -use std::fs; -use std::fs::OpenOptions; -use std::io::Write; -use std::path::PathBuf; - use anyhow::Result; use clap::Args; use itertools::Itertools; use ruff::checks::{CheckCategory, CheckCode}; use strum::IntoEnumIterator; +use crate::utils::replace_readme_section; + const TABLE_BEGIN_PRAGMA: &str = ""; const TABLE_END_PRAGMA: &str = ""; @@ -85,32 +82,3 @@ pub fn main(cli: &Cli) -> Result<()> { Ok(()) } - -fn replace_readme_section(content: &str, begin_pragma: &str, end_pragma: &str) -> Result<()> { - // Read the existing file. - let file = PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .parent() - .expect("Failed to find root directory") - .join("README.md"); - let existing = fs::read_to_string(&file)?; - - // Extract the prefix. - let index = existing - .find(begin_pragma) - .expect("Unable to find begin pragma"); - let prefix = &existing[..index + begin_pragma.len()]; - - // Extract the suffix. - let index = existing - .find(end_pragma) - .expect("Unable to find end pragma"); - let suffix = &existing[index..]; - - // Write the prefix, new contents, and suffix. - let mut f = OpenOptions::new().write(true).truncate(true).open(&file)?; - writeln!(f, "{prefix}")?; - write!(f, "{content}")?; - write!(f, "{suffix}")?; - - Ok(()) -} diff --git a/ruff_dev/src/lib.rs b/ruff_dev/src/lib.rs index 0be6f5d9e8..819c90885d 100644 --- a/ruff_dev/src/lib.rs +++ b/ruff_dev/src/lib.rs @@ -13,6 +13,7 @@ pub mod generate_all; pub mod generate_check_code_prefix; +pub mod generate_cli_help; pub mod generate_json_schema; pub mod generate_options; pub mod generate_rules_table; @@ -20,3 +21,4 @@ pub mod print_ast; pub mod print_cst; pub mod print_tokens; pub mod round_trip; +mod utils; diff --git a/ruff_dev/src/main.rs b/ruff_dev/src/main.rs index 255a3065f5..83f4eac643 100644 --- a/ruff_dev/src/main.rs +++ b/ruff_dev/src/main.rs @@ -14,8 +14,8 @@ use anyhow::Result; use clap::{Parser, Subcommand}; use ruff_dev::{ - generate_all, generate_check_code_prefix, generate_json_schema, generate_options, - generate_rules_table, print_ast, print_cst, print_tokens, round_trip, + generate_all, generate_check_code_prefix, generate_cli_help, generate_json_schema, + generate_options, generate_rules_table, print_ast, print_cst, print_tokens, round_trip, }; #[derive(Parser)] @@ -38,6 +38,8 @@ enum Commands { GenerateRulesTable(generate_rules_table::Cli), /// Generate a Markdown-compatible listing of configuration options. GenerateOptions(generate_options::Cli), + /// Generate CLI help. + GenerateCliHelp(generate_cli_help::Cli), /// Print the AST for a given Python file. PrintAST(print_ast::Cli), /// Print the LibCST CST for a given Python file. @@ -56,6 +58,7 @@ fn main() -> Result<()> { Commands::GenerateJSONSchema(args) => generate_json_schema::main(args)?, Commands::GenerateRulesTable(args) => generate_rules_table::main(args)?, Commands::GenerateOptions(args) => generate_options::main(args)?, + Commands::GenerateCliHelp(args) => generate_cli_help::main(args)?, Commands::PrintAST(args) => print_ast::main(args)?, Commands::PrintCST(args) => print_cst::main(args)?, Commands::PrintTokens(args) => print_tokens::main(args)?, diff --git a/ruff_dev/src/utils.rs b/ruff_dev/src/utils.rs new file mode 100644 index 0000000000..72477b6d3c --- /dev/null +++ b/ruff_dev/src/utils.rs @@ -0,0 +1,35 @@ +use std::fs; +use std::fs::OpenOptions; +use std::io::Write; +use std::path::PathBuf; + +use anyhow::Result; + +pub fn replace_readme_section(content: &str, begin_pragma: &str, end_pragma: &str) -> Result<()> { + // Read the existing file. + let file = PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .parent() + .expect("Failed to find root directory") + .join("README.md"); + let existing = fs::read_to_string(&file)?; + + // Extract the prefix. + let index = existing + .find(begin_pragma) + .expect("Unable to find begin pragma"); + let prefix = &existing[..index + begin_pragma.len()]; + + // Extract the suffix. + let index = existing + .find(end_pragma) + .expect("Unable to find end pragma"); + let suffix = &existing[index..]; + + // Write the prefix, new contents, and suffix. + let mut f = OpenOptions::new().write(true).truncate(true).open(&file)?; + writeln!(f, "{prefix}")?; + write!(f, "{content}")?; + write!(f, "{suffix}")?; + + Ok(()) +}