Implement config subcommand

The synopsis is as follows.

List all top-level config keys:

    $ ruff config
    allowed-confusables
    builtins
    cache-dir
    ... etc.

List all config keys in a specific section:

    $ ruff config mccabe
    max-complexity

Describe a specific config option:

    $ ruff config mccabe.max-complexity
    The maximum McCabe complexity to allow before triggering `C901` errors.

    Default value: 10
    Type: int
    Example usage:
    ```toml
    # Flag errors (`C901`) whenever the complexity level exceeds 5.
    max-complexity = 5
    ```
This commit is contained in:
Martin Fischer 2023-02-11 18:37:11 +01:00 committed by Charlie Marsh
parent bbe44360e8
commit 0e4d5eeea7
5 changed files with 53 additions and 0 deletions

View file

@ -393,6 +393,7 @@ Usage: ruff [OPTIONS] <COMMAND>
Commands:
check Run Ruff on the given files or directories (default)
rule Explain a rule
config List or describe the available configuration options
linter List all supported upstream linters
clean Clear any caches in the current directory and any subdirectories
help Print this message or the help of the given subcommand(s)

View file

@ -44,6 +44,8 @@ pub enum Command {
#[arg(long, value_enum, default_value = "pretty")]
format: HelpFormat,
},
/// List or describe the available configuration options
Config { option: Option<String> },
/// List all supported upstream linters
Linter {
/// Output format

View file

@ -0,0 +1,48 @@
use ruff::settings::{
options::Options,
options_base::{ConfigurationOptions, OptionEntry, OptionField},
};
use crate::ExitStatus;
#[allow(clippy::print_stdout)]
pub(crate) fn config(option: Option<&str>) -> ExitStatus {
let entries = Options::get_available_options();
let mut entries = &entries;
let mut parts_iter = option.iter().flat_map(|s| s.split('.'));
while let Some(part) = parts_iter.next() {
let Some((_, field)) = entries.iter().find(|(name, _)| *name == part) else {
println!("Unknown option");
return ExitStatus::Error;
};
match field {
OptionEntry::Field(OptionField {
doc,
default,
value_type,
example,
}) => {
if parts_iter.next().is_some() {
println!("Unknown option");
return ExitStatus::Error;
}
println!("{doc}");
println!();
println!("Default value: {default}");
println!("Type: {value_type}");
println!("Example usage:\n```toml\n{example}\n```");
return ExitStatus::Success;
}
OptionEntry::Group(fields) => {
entries = fields;
}
}
}
for (name, _) in entries {
println!("{name}");
}
ExitStatus::Success
}

View file

@ -9,6 +9,7 @@ pub use show_settings::show_settings;
mod add_noqa;
mod clean;
pub mod config;
mod linter;
mod rule;
mod run;

View file

@ -101,6 +101,7 @@ quoting the executed command, along with the relevant file contents and `pyproje
match command {
Command::Rule { rule, format } => commands::rule(&rule, format)?,
Command::Config { option } => return Ok(commands::config::config(option.as_deref())),
Command::Linter { format } => commands::linter(format)?,
Command::Clean => commands::clean(log_level)?,
Command::GenerateShellCompletion { shell } => {