Skip to content

Commit 2ecf597

Browse files
authored
Refactor Options representation (#7591)
1 parent f137819 commit 2ecf597

File tree

7 files changed

+336
-170
lines changed

7 files changed

+336
-170
lines changed

crates/ruff_cli/src/commands/config.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
use anyhow::{anyhow, Result};
22

33
use ruff_workspace::options::Options;
4+
use ruff_workspace::options_base::OptionsMetadata;
45

56
#[allow(clippy::print_stdout)]
67
pub(crate) fn config(key: Option<&str>) -> Result<()> {
78
match key {
89
None => print!("{}", Options::metadata()),
9-
Some(key) => match Options::metadata().get(key) {
10+
Some(key) => match Options::metadata().find(key) {
1011
None => {
1112
return Err(anyhow!("Unknown option: {key}"));
1213
}

crates/ruff_dev/src/generate_docs.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use strum::IntoEnumIterator;
1111
use ruff_diagnostics::AutofixKind;
1212
use ruff_linter::registry::{Linter, Rule, RuleNamespace};
1313
use ruff_workspace::options::Options;
14+
use ruff_workspace::options_base::OptionsMetadata;
1415

1516
use crate::ROOT_DIR;
1617

@@ -96,10 +97,7 @@ fn process_documentation(documentation: &str, out: &mut String) {
9697
if let Some(rest) = line.strip_prefix("- `") {
9798
let option = rest.trim_end().trim_end_matches('`');
9899

99-
assert!(
100-
Options::metadata().get(option).is_some(),
101-
"unknown option {option}"
102-
);
100+
assert!(Options::metadata().has(option), "unknown option {option}");
103101

104102
let anchor = option.replace('.', "-");
105103
out.push_str(&format!("- [`{option}`][{option}]\n"));
Lines changed: 72 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,68 @@
11
//! Generate a Markdown-compatible listing of configuration options for `pyproject.toml`.
22
//!
33
//! Used for <https://docs.astral.sh/ruff/settings/>.
4-
use itertools::Itertools;
4+
use std::fmt::Write;
5+
56
use ruff_workspace::options::Options;
6-
use ruff_workspace::options_base::{OptionEntry, OptionField};
7+
use ruff_workspace::options_base::{OptionField, OptionSet, OptionsMetadata, Visit};
8+
9+
pub(crate) fn generate() -> String {
10+
let mut output = String::new();
11+
generate_set(&mut output, &Set::Toplevel(Options::metadata()));
12+
13+
output
14+
}
15+
16+
fn generate_set(output: &mut String, set: &Set) {
17+
writeln!(output, "### {title}\n", title = set.title()).unwrap();
18+
19+
let mut visitor = CollectOptionsVisitor::default();
20+
set.metadata().record(&mut visitor);
21+
22+
let (mut fields, mut sets) = (visitor.fields, visitor.groups);
23+
24+
fields.sort_unstable_by(|(name, _), (name2, _)| name.cmp(name2));
25+
sets.sort_unstable_by(|(name, _), (name2, _)| name.cmp(name2));
26+
27+
// Generate the fields.
28+
for (name, field) in &fields {
29+
emit_field(output, name, field, set.name());
30+
output.push_str("---\n\n");
31+
}
32+
33+
// Generate all the sub-sets.
34+
for (set_name, sub_set) in &sets {
35+
generate_set(output, &Set::Named(set_name, *sub_set));
36+
}
37+
}
38+
39+
enum Set<'a> {
40+
Toplevel(OptionSet),
41+
Named(&'a str, OptionSet),
42+
}
43+
44+
impl<'a> Set<'a> {
45+
fn name(&self) -> Option<&'a str> {
46+
match self {
47+
Set::Toplevel(_) => None,
48+
Set::Named(name, _) => Some(name),
49+
}
50+
}
51+
52+
fn title(&self) -> &'a str {
53+
match self {
54+
Set::Toplevel(_) => "Top-level",
55+
Set::Named(name, _) => name,
56+
}
57+
}
58+
59+
fn metadata(&self) -> &OptionSet {
60+
match self {
61+
Set::Toplevel(set) => set,
62+
Set::Named(_, set) => set,
63+
}
64+
}
65+
}
766

867
fn emit_field(output: &mut String, name: &str, field: &OptionField, group_name: Option<&str>) {
968
// if there's a group name, we need to add it to the anchor
@@ -37,38 +96,18 @@ fn emit_field(output: &mut String, name: &str, field: &OptionField, group_name:
3796
output.push('\n');
3897
}
3998

40-
pub(crate) fn generate() -> String {
41-
let mut output: String = "### Top-level\n\n".into();
42-
43-
let sorted_options: Vec<_> = Options::metadata()
44-
.into_iter()
45-
.sorted_by_key(|(name, _)| *name)
46-
.collect();
47-
48-
// Generate all the top-level fields.
49-
for (name, entry) in &sorted_options {
50-
let OptionEntry::Field(field) = entry else {
51-
continue;
52-
};
53-
emit_field(&mut output, name, field, None);
54-
output.push_str("---\n\n");
55-
}
99+
#[derive(Default)]
100+
struct CollectOptionsVisitor {
101+
groups: Vec<(String, OptionSet)>,
102+
fields: Vec<(String, OptionField)>,
103+
}
56104

57-
// Generate all the sub-groups.
58-
for (group_name, entry) in &sorted_options {
59-
let OptionEntry::Group(fields) = entry else {
60-
continue;
61-
};
62-
output.push_str(&format!("### {group_name}\n"));
63-
output.push('\n');
64-
for (name, entry) in fields.iter().sorted_by_key(|(name, _)| name) {
65-
let OptionEntry::Field(field) = entry else {
66-
continue;
67-
};
68-
emit_field(&mut output, name, field, Some(group_name));
69-
output.push_str("---\n\n");
70-
}
105+
impl Visit for CollectOptionsVisitor {
106+
fn record_set(&mut self, name: &str, group: OptionSet) {
107+
self.groups.push((name.to_owned(), group));
71108
}
72109

73-
output
110+
fn record_field(&mut self, name: &str, field: OptionField) {
111+
self.fields.push((name.to_owned(), field));
112+
}
74113
}

crates/ruff_dev/src/generate_rules_table.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use ruff_diagnostics::AutofixKind;
99
use ruff_linter::registry::{Linter, Rule, RuleNamespace};
1010
use ruff_linter::upstream_categories::UpstreamCategoryAndPrefix;
1111
use ruff_workspace::options::Options;
12+
use ruff_workspace::options_base::OptionsMetadata;
1213

1314
const FIX_SYMBOL: &str = "🛠️";
1415
const PREVIEW_SYMBOL: &str = "🧪";
@@ -104,10 +105,7 @@ pub(crate) fn generate() -> String {
104105
table_out.push('\n');
105106
}
106107

107-
if Options::metadata()
108-
.iter()
109-
.any(|(name, _)| name == &linter.name())
110-
{
108+
if Options::metadata().has(linter.name()) {
111109
table_out.push_str(&format!(
112110
"For related settings, see [{}](settings.md#{}).",
113111
linter.name(),

crates/ruff_macros/src/config.rs

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,11 @@ pub(crate) fn derive_impl(input: DeriveInput) -> syn::Result<proc_macro2::TokenS
5050
};
5151
}
5252

53-
let options_len = output.len();
54-
5553
Ok(quote! {
5654

57-
impl #ident {
58-
pub const fn metadata() -> crate::options_base::OptionGroup {
59-
const OPTIONS: [(&'static str, crate::options_base::OptionEntry); #options_len] = [#(#output),*];
60-
crate::options_base::OptionGroup::new(&OPTIONS)
55+
impl crate::options_base::OptionsMetadata for #ident {
56+
fn record(visit: &mut dyn crate::options_base::Visit) {
57+
#(#output);*
6158
}
6259
}
6360
})
@@ -92,7 +89,7 @@ fn handle_option_group(field: &Field) -> syn::Result<proc_macro2::TokenStream> {
9289
let kebab_name = LitStr::new(&ident.to_string().replace('_', "-"), ident.span());
9390

9491
Ok(quote_spanned!(
95-
ident.span() => (#kebab_name, crate::options_base::OptionEntry::Group(#path::metadata()))
92+
ident.span() => (visit.record_set(#kebab_name, crate::options_base::OptionSet::of::<#path>()))
9693
))
9794
}
9895
_ => Err(syn::Error::new(
@@ -150,12 +147,14 @@ fn handle_option(
150147
let kebab_name = LitStr::new(&ident.to_string().replace('_', "-"), ident.span());
151148

152149
Ok(quote_spanned!(
153-
ident.span() => (#kebab_name, crate::options_base::OptionEntry::Field(crate::options_base::OptionField {
154-
doc: &#doc,
155-
default: &#default,
156-
value_type: &#value_type,
157-
example: &#example,
158-
}))
150+
ident.span() => {
151+
visit.record_field(#kebab_name, crate::options_base::OptionField{
152+
doc: &#doc,
153+
default: &#default,
154+
value_type: &#value_type,
155+
example: &#example,
156+
})
157+
}
159158
))
160159
}
161160

crates/ruff_workspace/src/options.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
77
use serde::{Deserialize, Serialize};
88
use strum::IntoEnumIterator;
99

10+
use crate::options_base::{OptionsMetadata, Visit};
1011
use ruff_linter::line_width::{LineLength, TabSize};
1112
use ruff_linter::rules::flake8_pytest_style::settings::SettingsError;
1213
use ruff_linter::rules::flake8_pytest_style::types;
@@ -2399,10 +2400,6 @@ pub enum FormatOrOutputFormat {
23992400
}
24002401

24012402
impl FormatOrOutputFormat {
2402-
pub const fn metadata() -> crate::options_base::OptionGroup {
2403-
FormatOptions::metadata()
2404-
}
2405-
24062403
pub const fn as_output_format(&self) -> Option<SerializationFormat> {
24072404
match self {
24082405
FormatOrOutputFormat::Format(_) => None,
@@ -2411,6 +2408,12 @@ impl FormatOrOutputFormat {
24112408
}
24122409
}
24132410

2411+
impl OptionsMetadata for FormatOrOutputFormat {
2412+
fn record(visit: &mut dyn Visit) {
2413+
FormatOptions::record(visit);
2414+
}
2415+
}
2416+
24142417
#[derive(
24152418
Debug, PartialEq, Eq, Default, Serialize, Deserialize, ConfigurationOptions, CombineOptions,
24162419
)]

0 commit comments

Comments
 (0)