diff --git a/src/cli.rs b/src/cli.rs index 3cb333e39f..4fa010f575 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -38,8 +38,10 @@ pub struct Cli { #[arg(short, long)] pub watch: bool, /// Attempt to automatically fix lint errors. - #[arg(short, long)] - pub fix: bool, + #[arg(long, overrides_with("no_fix"))] + fix: bool, + #[clap(long, overrides_with("fix"), hide = true)] + no_fix: bool, /// Disable cache reads. #[arg(short, long)] pub no_cache: bool, @@ -94,6 +96,22 @@ pub struct Cli { pub stdin_filename: Option, } +impl Cli { + // See: https://github.com/clap-rs/clap/issues/3146 + pub fn fix(&self) -> Option { + resolve_bool_arg(self.fix, self.no_fix) + } +} + +fn resolve_bool_arg(yes: bool, no: bool) -> Option { + match (yes, no) { + (true, false) => Some(true), + (false, true) => Some(false), + (false, false) => None, + (..) => unreachable!("Clap should make this impossible"), + } +} + /// Map the CLI settings to a `LogLevel`. pub fn extract_log_level(cli: &Cli) -> LogLevel { if cli.silent { @@ -165,7 +183,7 @@ pub fn warn_on( } } -/// Collect a list of `PatternPrefixPair` structs as a `BTreeMap`. +/// Convert a list of `PatternPrefixPair` structs to `PerFileIgnore`. pub fn collect_per_file_ignores( pairs: Vec, project_root: &Option, diff --git a/src/main.rs b/src/main.rs index 80747aa1f3..160edb3653 100644 --- a/src/main.rs +++ b/src/main.rs @@ -220,7 +220,9 @@ fn autoformat(files: &[PathBuf], settings: &Settings) -> Result { } fn inner_main() -> Result { + // Extract command-line arguments. let cli = Cli::parse(); + let fix = cli.fix(); let log_level = extract_log_level(&cli); set_up_logging(&log_level)?; @@ -239,7 +241,7 @@ fn inner_main() -> Result { None => debug!("Unable to find pyproject.toml; using default settings..."), }; - // Parse the settings from the pyproject.toml and command-line arguments. + // Reconcile configuration from pyproject.toml and command-line arguments. let exclude: Vec = cli .exclude .iter() @@ -296,6 +298,9 @@ fn inner_main() -> Result { if let Some(dummy_variable_rgx) = cli.dummy_variable_rgx { configuration.dummy_variable_rgx = dummy_variable_rgx; } + if let Some(fix) = fix { + configuration.fix = fix; + } if cli.show_settings && cli.show_files { eprintln!("Error: specify --show-settings or show-files (not both)."); @@ -306,6 +311,8 @@ fn inner_main() -> Result { return Ok(ExitCode::SUCCESS); } + // Extract settings for internal use. + let autofix = configuration.fix; let settings = Settings::from_configuration(configuration); if cli.show_files { @@ -318,7 +325,7 @@ fn inner_main() -> Result { let printer = Printer::new(&cli.format, &log_level); if cli.watch { - if cli.fix { + if autofix { eprintln!("Warning: --fix is not enabled in watch mode."); } @@ -381,15 +388,15 @@ fn inner_main() -> Result { let messages = if is_stdin { let filename = cli.stdin_filename.unwrap_or_else(|| "-".to_string()); let path = Path::new(&filename); - run_once_stdin(&settings, path, cli.fix)? + run_once_stdin(&settings, path, autofix)? } else { - run_once(&cli.files, &settings, !cli.no_cache, cli.fix)? + run_once(&cli.files, &settings, !cli.no_cache, autofix)? }; // Always try to print violations (the printer itself may suppress output), // unless we're writing fixes via stdin (in which case, the transformed // source code goes to stdout). - if !(is_stdin && cli.fix) { + if !(is_stdin && autofix) { printer.write_once(&messages)?; } diff --git a/src/settings/configuration.rs b/src/settings/configuration.rs index 4a15f5e8bb..61e763a6d5 100644 --- a/src/settings/configuration.rs +++ b/src/settings/configuration.rs @@ -20,6 +20,7 @@ pub struct Configuration { pub extend_exclude: Vec, pub extend_ignore: Vec, pub extend_select: Vec, + pub fix: bool, pub ignore: Vec, pub line_length: usize, pub per_file_ignores: Vec, @@ -91,6 +92,7 @@ impl Configuration { .select .unwrap_or_else(|| vec![CheckCodePrefix::E, CheckCodePrefix::F]), extend_select: options.extend_select.unwrap_or_default(), + fix: options.fix.unwrap_or_default(), ignore: options.ignore.unwrap_or_default(), line_length: options.line_length.unwrap_or(88), per_file_ignores: options diff --git a/src/settings/options.rs b/src/settings/options.rs index 53747dc435..d87ae776a9 100644 --- a/src/settings/options.rs +++ b/src/settings/options.rs @@ -11,15 +11,16 @@ use crate::{flake8_annotations, flake8_quotes, pep8_naming}; #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default)] #[serde(deny_unknown_fields, rename_all = "kebab-case")] pub struct Options { - pub line_length: Option, + pub dummy_variable_rgx: Option, pub exclude: Option>, pub extend_exclude: Option>, - pub select: Option>, - pub extend_select: Option>, - pub ignore: Option>, pub extend_ignore: Option>, + pub extend_select: Option>, + pub fix: Option, + pub ignore: Option>, + pub line_length: Option, pub per_file_ignores: Option>>, - pub dummy_variable_rgx: Option, + pub select: Option>, pub target_version: Option, // Plugins pub flake8_annotations: Option, diff --git a/src/settings/pyproject.rs b/src/settings/pyproject.rs index 67de700c65..e81129e2de 100644 --- a/src/settings/pyproject.rs +++ b/src/settings/pyproject.rs @@ -134,6 +134,7 @@ mod tests { Some(Tools { ruff: Some(Options { line_length: None, + fix: None, exclude: None, extend_exclude: None, select: None, @@ -162,6 +163,7 @@ line-length = 79 Some(Tools { ruff: Some(Options { line_length: Some(79), + fix: None, exclude: None, extend_exclude: None, select: None, @@ -190,6 +192,7 @@ exclude = ["foo.py"] Some(Tools { ruff: Some(Options { line_length: None, + fix: None, exclude: Some(vec!["foo.py".to_string()]), extend_exclude: None, select: None, @@ -218,6 +221,7 @@ select = ["E501"] Some(Tools { ruff: Some(Options { line_length: None, + fix: None, exclude: None, extend_exclude: None, select: Some(vec![CheckCodePrefix::E501]), @@ -247,6 +251,7 @@ ignore = ["E501"] Some(Tools { ruff: Some(Options { line_length: None, + fix: None, exclude: None, extend_exclude: None, select: None, @@ -315,6 +320,7 @@ other-attribute = 1 config, Options { line_length: Some(88), + fix: None, exclude: None, extend_exclude: Some(vec![ "excluded.py".to_string(), diff --git a/src/settings/user.rs b/src/settings/user.rs index 654c781178..c86ec4d0cf 100644 --- a/src/settings/user.rs +++ b/src/settings/user.rs @@ -40,6 +40,7 @@ pub struct UserConfiguration { pub extend_exclude: Vec, pub extend_ignore: Vec, pub extend_select: Vec, + pub fix: bool, pub ignore: Vec, pub line_length: usize, pub per_file_ignores: Vec<(Exclusion, Vec)>, @@ -74,6 +75,7 @@ impl UserConfiguration { .collect(), extend_ignore: configuration.extend_ignore, extend_select: configuration.extend_select, + fix: configuration.fix, ignore: configuration.ignore, line_length: configuration.line_length, per_file_ignores: configuration