diff --git a/crates/ruff/src/lib.rs b/crates/ruff/src/lib.rs index 676e521dfeca99..ae14723abfef1a 100644 --- a/crates/ruff/src/lib.rs +++ b/crates/ruff/src/lib.rs @@ -5,6 +5,8 @@ //! //! [Ruff]: https://github.com/astral-sh/ruff +#[cfg(feature = "clap")] +pub use rule_selector::clap_completion::RuleSelectorParser; pub use rule_selector::RuleSelector; pub use rules::pycodestyle::rules::{IOError, SyntaxError}; diff --git a/crates/ruff/src/rule_selector.rs b/crates/ruff/src/rule_selector.rs index 667f6b430acf2a..5cef5b839fc6bc 100644 --- a/crates/ruff/src/rule_selector.rs +++ b/crates/ruff/src/rule_selector.rs @@ -336,13 +336,14 @@ pub enum Specificity { } #[cfg(feature = "clap")] -mod clap_completion { +pub mod clap_completion { use clap::builder::{PossibleValue, TypedValueParser, ValueParserFactory}; use strum::IntoEnumIterator; use crate::{ codes::RuleCodePrefix, registry::{Linter, RuleNamespace}, + rule_selector::is_single_rule_selector, RuleSelector, }; @@ -372,7 +373,7 @@ mod clap_completion { value .parse() - .map_err(|e| clap::Error::raw(clap::error::ErrorKind::InvalidValue, e)) + .map_err(|err| clap::Error::raw(clap::error::ErrorKind::InvalidValue, err)) } fn possible_values(&self) -> Option + '_>> { @@ -387,27 +388,34 @@ mod clap_completion { RuleCodePrefix::iter() // Filter out rule gated behind `#[cfg(feature = "unreachable-code")]`, which is // off-by-default - .filter(|p| { - format!("{}{}", p.linter().common_prefix(), p.short_code()) - != "RUF014" + .filter(|prefix| { + format!( + "{}{}", + prefix.linter().common_prefix(), + prefix.short_code() + ) != "RUF014" }) - .map(|p| { - let prefix = p.linter().common_prefix(); - let code = p.short_code(); - - let mut rules_iter = p.rules(); - let rule1 = rules_iter.next(); - let rule2 = rules_iter.next(); - - let value = PossibleValue::new(format!("{prefix}{code}")); - - if rule2.is_none() { - let rule1 = rule1.unwrap(); - let name: &'static str = rule1.into(); - value.help(name) - } else { - value + .filter_map(|prefix| { + // Ex) `UP` + if prefix.short_code().is_empty() { + let code = prefix.linter().common_prefix(); + let name = prefix.linter().name(); + return Some(PossibleValue::new(code).help(name)); } + + // Ex) `UP004` + if is_single_rule_selector(&prefix) { + let rule = prefix.rules().next()?; + let code = format!( + "{}{}", + prefix.linter().common_prefix(), + prefix.short_code() + ); + let name: &'static str = rule.into(); + return Some(PossibleValue::new(code).help(name)); + } + + None }), ), ), diff --git a/crates/ruff_cli/src/args.rs b/crates/ruff_cli/src/args.rs index 18ce203ec0f5b1..6ff4362eef5a27 100644 --- a/crates/ruff_cli/src/args.rs +++ b/crates/ruff_cli/src/args.rs @@ -1,17 +1,16 @@ use std::path::PathBuf; -use std::str::FromStr; use clap::{command, Parser}; use regex::Regex; -use ruff::line_width::LineLength; use rustc_hash::FxHashMap; +use ruff::line_width::LineLength; use ruff::logging::LogLevel; use ruff::registry::Rule; use ruff::settings::types::{ FilePattern, PatternPrefixPair, PerFileIgnore, PreviewMode, PythonVersion, SerializationFormat, }; -use ruff::RuleSelector; +use ruff::{RuleSelector, RuleSelectorParser}; use ruff_workspace::configuration::{Configuration, RuleSelection}; use ruff_workspace::resolver::ConfigProcessor; @@ -129,7 +128,7 @@ pub struct CheckCommand { long, value_delimiter = ',', value_name = "RULE_CODE", - value_parser = parse_rule_selector, + value_parser = RuleSelectorParser, help_heading = "Rule selection", hide_possible_values = true )] @@ -139,7 +138,7 @@ pub struct CheckCommand { long, value_delimiter = ',', value_name = "RULE_CODE", - value_parser = parse_rule_selector, + value_parser = RuleSelectorParser, help_heading = "Rule selection", hide_possible_values = true )] @@ -149,7 +148,7 @@ pub struct CheckCommand { long, value_delimiter = ',', value_name = "RULE_CODE", - value_parser = parse_rule_selector, + value_parser = RuleSelectorParser, help_heading = "Rule selection", hide_possible_values = true )] @@ -159,7 +158,7 @@ pub struct CheckCommand { long, value_delimiter = ',', value_name = "RULE_CODE", - value_parser = parse_rule_selector, + value_parser = RuleSelectorParser, help_heading = "Rule selection", hide = true )] @@ -191,7 +190,7 @@ pub struct CheckCommand { long, value_delimiter = ',', value_name = "RULE_CODE", - value_parser = parse_rule_selector, + value_parser = RuleSelectorParser, help_heading = "Rule selection", hide_possible_values = true )] @@ -201,7 +200,7 @@ pub struct CheckCommand { long, value_delimiter = ',', value_name = "RULE_CODE", - value_parser = parse_rule_selector, + value_parser = RuleSelectorParser, help_heading = "Rule selection", hide_possible_values = true )] @@ -211,7 +210,7 @@ pub struct CheckCommand { long, value_delimiter = ',', value_name = "RULE_CODE", - value_parser = parse_rule_selector, + value_parser = RuleSelectorParser, help_heading = "Rule selection", hide_possible_values = true )] @@ -221,7 +220,7 @@ pub struct CheckCommand { long, value_delimiter = ',', value_name = "RULE_CODE", - value_parser = parse_rule_selector, + value_parser = RuleSelectorParser, help_heading = "Rule selection", hide = true )] @@ -511,11 +510,6 @@ impl FormatCommand { } } -fn parse_rule_selector(env: &str) -> Result { - RuleSelector::from_str(env) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e)) -} - fn resolve_bool_arg(yes: bool, no: bool) -> Option { match (yes, no) { (true, false) => Some(true), diff --git a/docs/configuration.md b/docs/configuration.md index b210376bfbfa4e..fa546ce3b6046b 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -476,7 +476,7 @@ Ruff supports two command-line flags that alter its exit code behavior: `--exit-non-zero-on-fix` can result in a non-zero exit code even if no violations remain after autofixing. -## Autocompletion +## Shell autocompletion Ruff supports autocompletion for most shells. A shell-specific completion script can be generated by `ruff generate-shell-completion `, where `` is one of `bash`, `elvish`, `fig`, `fish`,