Skip to content

Commit

Permalink
Implement shell autocompletion for rule codes
Browse files Browse the repository at this point in the history
For example:

    $ ruff check --select=EM<Tab>
    EM          -- flake8-errmsg
    EM10   EM1  --
    EM101       -- raw-string-in-exception
    EM102       -- f-string-in-exception
    EM103       -- dot-format-in-exception

(You will need to enable autocompletion as described
 in the Autocompletion section in the README.)

Fixes #2808.

(The --help help change in the README is due to a clap bug,
 for which I already submitted a fix:
 clap-rs/clap#4710.)
  • Loading branch information
not-my-profile authored and charliermarsh committed Feb 15, 2023
1 parent ca49b00 commit 70e378b
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 7 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -468,7 +468,7 @@ Options:
--show-settings
See the settings Ruff will use to lint a given Python file
-h, --help
Print help
Print help (see more with '--help')
Rule selection:
--select <RULE_CODE>
Expand Down
2 changes: 1 addition & 1 deletion crates/ruff/Cargo.toml
Expand Up @@ -21,7 +21,7 @@ bisection = { version = "0.1.0" }
bitflags = { version = "1.3.2" }
cfg-if = { version = "1.0.0" }
chrono = { version = "0.4.21", default-features = false, features = ["clock"] }
clap = { workspace = true, features = ["derive", "env"] }
clap = { workspace = true, features = ["derive", "env", "string"] }
colored = { version = "2.0.0" }
dirs = { version = "4.0.0" }
fern = { version = "0.6.1" }
Expand Down
73 changes: 73 additions & 0 deletions crates/ruff/src/rule_selector.rs
Expand Up @@ -233,3 +233,76 @@ pub(crate) enum Specificity {
Code4Chars,
Code5Chars,
}

mod clap_completion {
use clap::builder::{PossibleValue, TypedValueParser, ValueParserFactory};
use strum::IntoEnumIterator;

use crate::{
codes::RuleCodePrefix,
registry::{Linter, RuleNamespace},
RuleSelector,
};

#[derive(Clone)]
pub struct RuleSelectorParser;

impl ValueParserFactory for RuleSelector {
type Parser = RuleSelectorParser;

fn value_parser() -> Self::Parser {
RuleSelectorParser
}
}

impl TypedValueParser for RuleSelectorParser {
type Value = RuleSelector;

fn parse_ref(
&self,
_cmd: &clap::Command,
_arg: Option<&clap::Arg>,
value: &std::ffi::OsStr,
) -> Result<Self::Value, clap::Error> {
let value = value
.to_str()
.ok_or_else(|| clap::Error::new(clap::error::ErrorKind::InvalidUtf8))?;

value
.parse()
.map_err(|e| clap::Error::raw(clap::error::ErrorKind::InvalidValue, e))
}

fn possible_values(
&self,
) -> Option<Box<dyn Iterator<Item = clap::builder::PossibleValue> + '_>> {
Some(Box::new(
std::iter::once(PossibleValue::new("ALL").help("all rules")).chain(
Linter::iter()
.filter_map(|l| {
let prefix = l.common_prefix();
(!prefix.is_empty()).then(|| PossibleValue::new(prefix).help(l.name()))
})
.chain(RuleCodePrefix::iter().map(|p| {
let prefix = p.linter().common_prefix();
let code = p.short_code();

let mut rules_iter = p.into_iter();
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
}
})),
),
))
}
}
}
15 changes: 10 additions & 5 deletions crates/ruff_cli/src/args.rs
Expand Up @@ -109,15 +109,17 @@ pub struct CheckArgs {
long,
value_delimiter = ',',
value_name = "RULE_CODE",
help_heading = "Rule selection"
help_heading = "Rule selection",
hide_possible_values = true
)]
pub select: Option<Vec<RuleSelector>>,
/// Comma-separated list of rule codes to disable.
#[arg(
long,
value_delimiter = ',',
value_name = "RULE_CODE",
help_heading = "Rule selection"
help_heading = "Rule selection",
hide_possible_values = true
)]
pub ignore: Option<Vec<RuleSelector>>,
/// Like --select, but adds additional rule codes on top of the selected
Expand All @@ -126,7 +128,8 @@ pub struct CheckArgs {
long,
value_delimiter = ',',
value_name = "RULE_CODE",
help_heading = "Rule selection"
help_heading = "Rule selection",
hide_possible_values = true
)]
pub extend_select: Option<Vec<RuleSelector>>,
/// Like --ignore. (Deprecated: You can just use --ignore instead.)
Expand Down Expand Up @@ -164,7 +167,8 @@ pub struct CheckArgs {
long,
value_delimiter = ',',
value_name = "RULE_CODE",
help_heading = "Rule selection"
help_heading = "Rule selection",
hide_possible_values = true
)]
pub fixable: Option<Vec<RuleSelector>>,
/// List of rule codes to treat as ineligible for autofix. Only applicable
Expand All @@ -173,7 +177,8 @@ pub struct CheckArgs {
long,
value_delimiter = ',',
value_name = "RULE_CODE",
help_heading = "Rule selection"
help_heading = "Rule selection",
hide_possible_values = true
)]
pub unfixable: Option<Vec<RuleSelector>>,
/// Respect file exclusions via `.gitignore` and other standard ignore
Expand Down
1 change: 1 addition & 0 deletions crates/ruff_macros/src/register_rules.rs
Expand Up @@ -57,6 +57,7 @@ pub fn register_rules(input: &Input) -> proc_macro2::TokenStream {
PartialOrd,
Ord,
AsRefStr,
::strum_macros::IntoStaticStr,
)]
#[strum(serialize_all = "kebab-case")]
pub enum Rule { #rule_variants }
Expand Down

0 comments on commit 70e378b

Please sign in to comment.