diff --git a/Configurations.md b/Configurations.md index cb9d1c8a356..f035de6b7ab 100644 --- a/Configurations.md +++ b/Configurations.md @@ -17,6 +17,29 @@ To enable unstable options, set `unstable_features = true` in `rustfmt.toml` or Below you find a detailed visual guide on all the supported configuration options of rustfmt: +## `array_width` + +Maximum width of an array literal before falling back to vertical formatting. + +- **Default value**: `60` +- **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width) +- **Stable**: Yes + +By default this option is set as a percentage of [`max_width`](#max_width) provided by [`width_heuristics`](#width_heuristics), but a value set directly for `array_width` will take precedence. + +See also [`max_width`](#max_width) and [`width_heuristics`](#width_heuristics) + +## `attr_fn_like_width` + +Maximum width of the args of a function-like attributes before falling back to vertical formatting. + +- **Default value**: `70` +- **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width) +- **Stable**: Yes + +By default this option is set as a percentage of [`max_width`](#max_width) provided by [`width_heuristics`](#width_heuristics), but a value set directly for `attr_fn_like_width` will take precedence. + +See also [`max_width`](#max_width) and [`width_heuristics`](#width_heuristics) ## `binop_separator` @@ -272,6 +295,17 @@ where } ``` +## `chain_width` + +Maximum width of a chain to fit on one line. + +- **Default value**: `60` +- **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width) +- **Stable**: Yes + +By default this option is set as a percentage of [`max_width`](#max_width) provided by [`width_heuristics`](#width_heuristics), but a value set directly for `chain_width` will take precedence. + +See also [`max_width`](#max_width) and [`width_heuristics`](#width_heuristics) ## `color` @@ -717,6 +751,17 @@ trait Lorem { } ``` +## `fn_call_width` + +Maximum width of the args of a function call before falling back to vertical formatting. + +- **Default value**: `60` +- **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width) +- **Stable**: Yes + +By default this option is set as a percentage of [`max_width`](#max_width) provided by [`width_heuristics`](#width_heuristics), but a value set directly for `fn_call_width` will take precedence. + +See also [`max_width`](#max_width) and [`width_heuristics`](#width_heuristics) ## `fn_single_line` @@ -1881,6 +1926,18 @@ specific version of rustfmt is used in your CI, use this option. - **Possible values**: any published version (e.g. `"0.3.8"`) - **Stable**: No (tracking issue: [#3386](https://github.com/rust-lang/rustfmt/issues/3386)) +## `single_line_if_else_max_width` + +Maximum line length for single line if-else expressions. A value of `0` (zero) results in if-else expressions always being broken into multiple lines. Note this occurs when `width_heuristics` is set to `Off`. + +- **Default value**: `50` +- **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width) +- **Stable**: Yes + +By default this option is set as a percentage of [`max_width`](#max_width) provided by [`width_heuristics`](#width_heuristics), but a value set directly for `single_line_if_else_max_width` will take precedence. + +See also [`max_width`](#max_width) and [`width_heuristics`](#width_heuristics) + ## `space_after_colon` Leave a space after the colon. @@ -2080,6 +2137,29 @@ fn main() { See also: [`indent_style`](#indent_style). +## `struct_lit_width` + +Maximum width in the body of a struct literal before falling back to vertical formatting. A value of `0` (zero) results in struct literals always being broken into multiple lines. Note this occurs when `width_heuristics` is set to `Off`. + +- **Default value**: `18` +- **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width) +- **Stable**: Yes + +By default this option is set as a percentage of [`max_width`](#max_width) provided by [`width_heuristics`](#width_heuristics), but a value set directly for `struct_lit_width` will take precedence. + +See also [`max_width`](#max_width), [`width_heuristics`](#width_heuristics), and [`struct_lit_single_line`](#struct_lit_single_line) + +## `struct_variant_width` + +Maximum width in the body of a struct variant before falling back to vertical formatting. A value of `0` (zero) results in struct literals always being broken into multiple lines. Note this occurs when `width_heuristics` is set to `Off`. + +- **Default value**: `35` +- **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width) +- **Stable**: Yes + +By default this option is set as a percentage of [`max_width`](#max_width) provided by [`width_heuristics`](#width_heuristics), but a value set directly for `struct_variant_width` will take precedence. + +See also [`max_width`](#max_width) and [`width_heuristics`](#width_heuristics) ## `tab_spaces` @@ -2270,15 +2350,45 @@ fn main() { } ``` -## `use_small_heuristics` +## `width_heuristics` + +This option can be used to simplify the management and bulk updates of the granular width configuration settings ([`fn_call_width`](#fn_call_width), [`attr_fn_like_width`](#attr_fn_like_width), [`struct_lit_width`](#struct_lit_width), [`struct_variant_width`](#struct_variant_width), [`array_width`](#array_width), [`chain_width`](#chain_width), [`single_line_if_else_max_width`](#single_line_if_else_max_width)), that respectively control when formatted constructs are multi-lined/vertical based on width. -Whether to use different formatting for items and expressions if they satisfy a heuristic notion of 'small'. +Note that explicitly provided values for the width configuration settings take precedence and override the calculated values determined by `width_heuristics`. -- **Default value**: `"Default"` -- **Possible values**: `"Default"`, `"Off"`, `"Max"` +- **Default value**: `"Scaled"` +- **Possible values**: `"Scaled"`, `"Off"`, `"Max"` - **Stable**: Yes -#### `Default` (default): +#### `Scaled` (default): +When `width_heuristics` is set to `Scaled`, the values for the granular width settings are calculated as a ratio of the value for `max_width`. + +The ratios are: +* [`fn_call_width`](#fn_call_width) - `60%` +* [`attr_fn_like_width`](#attr_fn_like_width) - `70%` +* [`struct_lit_width`](#struct_lit_width) - `18%` +* [`struct_variant_width`](#struct_variant_width) - `35%` +* [`array_width`](#array_width) - `60%` +* [`chain_width`](#chain_width) - `60%` +* [`single_line_if_else_max_width`](#single_line_if_else_max_width) - `50%` + +For example when `max_width` is set to `100`, the width settings are: +* `fn_call_width=60` +* `attr_fn_like_width=70` +* `struct_lit_width=18` +* `struct_variant_width=35` +* `array_width=60` +* `chain_width=60` +* `single_line_if_else_max_width=50` + +and when `max_width` is set to `200`: +* `fn_call_width=120` +* `attr_fn_like_width=140` +* `struct_lit_width=36` +* `struct_variant_width=70` +* `array_width=120` +* `chain_width=120` +* `single_line_if_else_max_width=100` ```rust enum Lorem { @@ -2309,6 +2419,7 @@ fn main() { ``` #### `Off`: +When `width_heuristics` is set to `Off`, the granular width settings are functionally disabled and ignored. See the documentation for the respective width config options for specifics. ```rust enum Lorem { @@ -2337,6 +2448,16 @@ fn main() { ``` #### `Max`: +When `width_heuristics` is set to `Max`, then each granular width setting is set to the same value as `max_width`. + +So if `max_width` is set to `200`, then all the width settings are also set to `200`. +* `fn_call_width=200` +* `attr_fn_like_width=200` +* `struct_lit_width=200` +* `struct_variant_width=200` +* `array_width=200` +* `chain_width=200` +* `single_line_if_else_max_width=200` ```rust enum Lorem { @@ -2354,6 +2475,17 @@ fn main() { } ``` + +See also: +* [`max_width`](#max_width) +* [`fn_call_width`](#fn_call_width) +* [`attr_fn_like_width`](#attr_fn_like_width) +* [`struct_lit_width`](#struct_lit_width) +* [`struct_variant_width`](#struct_variant_width) +* [`array_width`](#array_width) +* [`chain_width`](#chain_width) +* [`single_line_if_else_max_width`](#single_line_if_else_max_width) + ## `use_try_shorthand` Replace uses of the try! macro by the ? shorthand diff --git a/rustfmt-core/rustfmt-config/src/config_type.rs b/rustfmt-core/rustfmt-config/src/config_type.rs index 25b0f38d70c..83b0ccd78a5 100644 --- a/rustfmt-core/rustfmt-config/src/config_type.rs +++ b/rustfmt-core/rustfmt-config/src/config_type.rs @@ -96,7 +96,15 @@ macro_rules! create_config { (self.0).$i.1 = true; (self.0).$i.2 = value; match stringify!($i) { - "max_width" | "use_small_heuristics" => self.0.set_heuristics(), + "max_width" + | "width_heuristics" + | "fn_call_width" + | "single_line_if_else_max_width" + | "attr_fn_like_width" + | "struct_lit_width" + | "struct_variant_width" + | "array_width" + | "chain_width" => self.0.set_heuristics(), "license_template_path" => self.0.set_license_template(), &_ => (), } @@ -228,7 +236,15 @@ macro_rules! create_config { } match key { - "max_width" | "use_small_heuristics" => self.set_heuristics(), + "max_width" + | "width_heuristics" + | "fn_call_width" + | "single_line_if_else_max_width" + | "attr_fn_like_width" + | "struct_lit_width" + | "struct_variant_width" + | "array_width" + | "chain_width" => self.set_heuristics(), "license_template_path" => self.set_license_template(), &_ => (), } @@ -236,8 +252,8 @@ macro_rules! create_config { #[allow(unreachable_pub)] pub fn is_hidden_option(name: &str) -> bool { - const HIDE_OPTIONS: [&str; 6] = [ - "verbose", "verbose_diff", "file_lines", "width_heuristics", + const HIDE_OPTIONS: [&str; 5] = [ + "verbose", "verbose_diff", "file_lines", "recursive", "print_misformatted_file_names", ]; HIDE_OPTIONS.contains(&name) @@ -280,16 +296,93 @@ macro_rules! create_config { )+ } + fn set_width_heuristics(&mut self, heuristics: WidthHeuristics) { + let max_width = self.max_width.2; + let get_width_value = | + was_set: bool, + override_value: usize, + heuristic_value: usize, + config_key: &str, + | -> usize { + if !was_set { + return heuristic_value; + } + if override_value > max_width { + eprintln!( + "`{0}` cannot have a value that exceeds `max_width`. \ + `{0}` will be set to the same value as `max_width`", + config_key, + ); + return max_width; + } + override_value + }; + + let fn_call_width = get_width_value( + self.was_set().fn_call_width(), + self.fn_call_width.2, + heuristics.fn_call_width, + "fn_call_width", + ); + self.fn_call_width.2 = fn_call_width; + + let attr_fn_like_width = get_width_value( + self.was_set().attr_fn_like_width(), + self.attr_fn_like_width.2, + heuristics.attr_fn_like_width, + "attr_fn_like_width", + ); + self.attr_fn_like_width.2 = attr_fn_like_width; + + let struct_lit_width = get_width_value( + self.was_set().struct_lit_width(), + self.struct_lit_width.2, + heuristics.struct_lit_width, + "struct_lit_width", + ); + self.struct_lit_width.2 = struct_lit_width; + + let struct_variant_width = get_width_value( + self.was_set().struct_variant_width(), + self.struct_variant_width.2, + heuristics.struct_variant_width, + "struct_variant_width", + ); + self.struct_variant_width.2 = struct_variant_width; + + let array_width = get_width_value( + self.was_set().array_width(), + self.array_width.2, + heuristics.array_width, + "array_width", + ); + self.array_width.2 = array_width; + + let chain_width = get_width_value( + self.was_set().chain_width(), + self.chain_width.2, + heuristics.chain_width, + "chain_width", + ); + self.chain_width.2 = chain_width; + + let single_line_if_else_max_width = get_width_value( + self.was_set().single_line_if_else_max_width(), + self.single_line_if_else_max_width.2, + heuristics.single_line_if_else_max_width, + "single_line_if_else_max_width", + ); + self.single_line_if_else_max_width.2 = single_line_if_else_max_width; + } + fn set_heuristics(&mut self) { - if self.use_small_heuristics.2 == Heuristics::Default { - let max_width = self.max_width.2; - self.set().width_heuristics(WidthHeuristics::scaled(max_width)); - } else if self.use_small_heuristics.2 == Heuristics::Max { - let max_width = self.max_width.2; - self.set().width_heuristics(WidthHeuristics::set(max_width)); - } else { - self.set().width_heuristics(WidthHeuristics::null()); - } + let max_width = self.max_width.2; + match self.width_heuristics.2 { + Heuristics::Scaled => + self.set_width_heuristics(WidthHeuristics::scaled(max_width)), + Heuristics::Max => self.set_width_heuristics(WidthHeuristics::set(max_width)), + Heuristics::Off => self.set_width_heuristics(WidthHeuristics::null()), + }; } fn set_license_template(&mut self) { diff --git a/rustfmt-core/rustfmt-config/src/lib.rs b/rustfmt-core/rustfmt-config/src/lib.rs index bf3a7d2dbbc..8a63e86c8e3 100644 --- a/rustfmt-core/rustfmt-config/src/lib.rs +++ b/rustfmt-core/rustfmt-config/src/lib.rs @@ -32,9 +32,24 @@ create_config! { hard_tabs: bool, false, true, "Use tab characters for indentation, spaces for alignment"; tab_spaces: usize, 4, true, "Number of spaces per tab"; newline_style: NewlineStyle, NewlineStyle::Auto, true, "Unix or Windows line endings"; - use_small_heuristics: Heuristics, Heuristics::Default, true, "Whether to use different \ - formatting for items and expressions if they satisfy a heuristic notion of 'small'"; indent_style: IndentStyle, IndentStyle::Block, false, "How do we indent expressions or items"; + width_heuristics: Heuristics, Heuristics::Scaled, true, "Controls width heuristics \ + by setting the values for the individual width heuristic options"; + + // Width Heuristics + fn_call_width: usize, 60, true, "Maximum width of the args of a function call before \ + falling back to vertical formatting."; + attr_fn_like_width: usize, 70, true, "Maximum width of the args of a function-like \ + attributes before falling back to vertical formatting."; + struct_lit_width: usize, 18, true, "Maximum width in the body of a struct lit before \ + falling back to vertical formatting."; + struct_variant_width: usize, 35, true, "Maximum width in the body of a struct variant before \ + falling back to vertical formatting."; + array_width: usize, 60, true, "Maximum width of an array literal before falling \ + back to vertical formatting."; + chain_width: usize, 60, true, "Maximum length of a chain to fit on a single line."; + single_line_if_else_max_width: usize, 50, true, "Maximum line length for single line if-else \ + expressions. A value of zero means always break if-else expressions."; // Comments. macros, and strings wrap_comments: bool, false, false, "Break comments to fit on the line"; @@ -146,8 +161,6 @@ create_config! { file_lines: FileLines, FileLines::all(), false, "Lines to format; this is not supported in rustfmt.toml, and can only be specified \ via the --file-lines option"; - width_heuristics: WidthHeuristics, WidthHeuristics::scaled(100), false, - "'small' heuristic values"; emit_mode: EmitMode, EmitMode::Files, false, "What emit Mode to use when none is supplied"; print_misformatted_file_names: bool, false, true, @@ -167,7 +180,6 @@ impl PartialConfig { let mut cloned = self.clone(); cloned.file_lines = None; cloned.verbose = None; - cloned.width_heuristics = None; cloned.print_misformatted_file_names = None; cloned.recursive = None; cloned.emit_mode = None; @@ -389,9 +401,6 @@ mod test { create_config! { // Options that are used by the generated functions max_width: usize, 100, true, "Maximum width of each line"; - use_small_heuristics: Heuristics, Heuristics::Default, true, - "Whether to use different formatting for items and \ - expressions if they satisfy a heuristic notion of 'small'."; license_template_path: String, String::default(), false, "Beginning of file must match license template"; required_version: String, env!("CARGO_PKG_VERSION").to_owned(), false, @@ -403,8 +412,22 @@ mod test { file_lines: FileLines, FileLines::all(), false, "Lines to format; this is not supported in rustfmt.toml, and can only be specified \ via the --file-lines option"; - width_heuristics: WidthHeuristics, WidthHeuristics::scaled(100), false, - "'small' heuristic values"; + width_heuristics: Heuristics, Heuristics::Scaled, true, "Controls width heuristics \ + by setting the values for the individual width heuristic options"; + // Width Heuristics + fn_call_width: usize, 60, true, "Maximum width of the args of a function call before \ + falling back to vertical formatting."; + attr_fn_like_width: usize, 70, true, "Maximum width of the args of a function-like \ + attributes before falling back to vertical formatting."; + struct_lit_width: usize, 18, true, "Maximum width in the body of a struct lit before \ + falling back to vertical formatting."; + struct_variant_width: usize, 35, true, "Maximum width in the body of a struct \ + variant before falling back to vertical formatting."; + array_width: usize, 60, true, "Maximum width of an array literal before falling \ + back to vertical formatting."; + chain_width: usize, 60, true, "Maximum length of a chain to fit on a single line."; + single_line_if_else_max_width: usize, 50, true, "Maximum line length for single \ + line if-else expressions. A value of zero means always break if-else expressions."; // Options that are used by the tests stable_option: bool, false, true, "A stable option"; @@ -492,8 +515,15 @@ mod test { hard_tabs = false tab_spaces = 4 newline_style = "Auto" -use_small_heuristics = "Default" indent_style = "Block" +width_heuristics = "Scaled" +fn_call_width = 60 +attr_fn_like_width = 70 +struct_lit_width = 18 +struct_variant_width = 35 +array_width = 60 +chain_width = 60 +single_line_if_else_max_width = 50 wrap_comments = false format_code_in_doc_comments = false comment_width = 80 @@ -651,4 +681,242 @@ ignore = [] assert_eq!(config.unstable_features(), true); } } + + #[cfg(test)] + mod width_heuristics { + use super::*; + + #[test] + fn test_scaled_sets_correct_widths() { + let toml = r#" + width_heuristics = "Scaled" + max_width = 200 + "#; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + assert_eq!(config.array_width(), 120); + assert_eq!(config.attr_fn_like_width(), 140); + assert_eq!(config.chain_width(), 120); + assert_eq!(config.fn_call_width(), 120); + assert_eq!(config.single_line_if_else_max_width(), 100); + assert_eq!(config.struct_lit_width(), 36); + assert_eq!(config.struct_variant_width(), 70); + } + + #[test] + fn test_max_sets_correct_widths() { + let toml = r#" + width_heuristics = "Max" + max_width = 120 + "#; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + assert_eq!(config.array_width(), 120); + assert_eq!(config.attr_fn_like_width(), 120); + assert_eq!(config.chain_width(), 120); + assert_eq!(config.fn_call_width(), 120); + assert_eq!(config.single_line_if_else_max_width(), 120); + assert_eq!(config.struct_lit_width(), 120); + assert_eq!(config.struct_variant_width(), 120); + } + + #[test] + fn test_off_sets_correct_widths() { + let toml = r#" + width_heuristics = "Off" + max_width = 100 + "#; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + assert_eq!(config.array_width(), usize::max_value()); + assert_eq!(config.attr_fn_like_width(), usize::max_value()); + assert_eq!(config.chain_width(), usize::max_value()); + assert_eq!(config.fn_call_width(), usize::max_value()); + assert_eq!(config.single_line_if_else_max_width(), 0); + assert_eq!(config.struct_lit_width(), 0); + assert_eq!(config.struct_variant_width(), 0); + } + + #[test] + fn test_override_works_with_scaled() { + let toml = r#" + width_heuristics = "Scaled" + array_width = 20 + attr_fn_like_width = 40 + chain_width = 20 + fn_call_width = 90 + single_line_if_else_max_width = 40 + struct_lit_width = 30 + struct_variant_width = 34 + "#; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + assert_eq!(config.array_width(), 20); + assert_eq!(config.attr_fn_like_width(), 40); + assert_eq!(config.chain_width(), 20); + assert_eq!(config.fn_call_width(), 90); + assert_eq!(config.single_line_if_else_max_width(), 40); + assert_eq!(config.struct_lit_width(), 30); + assert_eq!(config.struct_variant_width(), 34); + } + + #[test] + fn test_override_with_max() { + let toml = r#" + width_heuristics = "Max" + array_width = 20 + attr_fn_like_width = 40 + chain_width = 20 + fn_call_width = 90 + single_line_if_else_max_width = 40 + struct_lit_width = 30 + struct_variant_width = 34 + "#; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + assert_eq!(config.array_width(), 20); + assert_eq!(config.attr_fn_like_width(), 40); + assert_eq!(config.chain_width(), 20); + assert_eq!(config.fn_call_width(), 90); + assert_eq!(config.single_line_if_else_max_width(), 40); + assert_eq!(config.struct_lit_width(), 30); + assert_eq!(config.struct_variant_width(), 34); + } + + #[test] + fn test_override_with_off() { + let toml = r#" + width_heuristics = "Off" + array_width = 20 + attr_fn_like_width = 40 + chain_width = 20 + fn_call_width = 90 + single_line_if_else_max_width = 40 + struct_lit_width = 30 + struct_variant_width = 34 + "#; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + assert_eq!(config.array_width(), 20); + assert_eq!(config.attr_fn_like_width(), 40); + assert_eq!(config.chain_width(), 20); + assert_eq!(config.fn_call_width(), 90); + assert_eq!(config.single_line_if_else_max_width(), 40); + assert_eq!(config.struct_lit_width(), 30); + assert_eq!(config.struct_variant_width(), 34); + } + + #[test] + fn test_fn_call_width_config_exceeds_max_width() { + let toml = r#" + max_width = 90 + fn_call_width = 95 + "#; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + assert_eq!(config.fn_call_width(), 90); + } + + #[test] + fn test_attr_fn_like_width_config_exceeds_max_width() { + let toml = r#" + max_width = 80 + attr_fn_like_width = 90 + "#; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + assert_eq!(config.attr_fn_like_width(), 80); + } + + #[test] + fn test_struct_lit_config_exceeds_max_width() { + let toml = r#" + max_width = 78 + struct_lit_width = 90 + "#; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + assert_eq!(config.struct_lit_width(), 78); + } + + #[test] + fn test_struct_variant_width_config_exceeds_max_width() { + let toml = r#" + max_width = 80 + struct_variant_width = 90 + "#; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + assert_eq!(config.struct_variant_width(), 80); + } + + #[test] + fn test_array_width_config_exceeds_max_width() { + let toml = r#" + max_width = 60 + array_width = 80 + "#; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + assert_eq!(config.array_width(), 60); + } + + #[test] + fn test_chain_width_config_exceeds_max_width() { + let toml = r#" + max_width = 80 + chain_width = 90 + "#; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + assert_eq!(config.chain_width(), 80); + } + + #[test] + fn test_single_line_if_else_max_width_config_exceeds_max_width() { + let toml = r#" + max_width = 70 + single_line_if_else_max_width = 90 + "#; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + assert_eq!(config.single_line_if_else_max_width(), 70); + } + + #[test] + fn test_override_fn_call_width_exceeds_max_width() { + let mut config = Config::default(); + config.override_value("fn_call_width", "101"); + assert_eq!(config.fn_call_width(), 100); + } + + #[test] + fn test_override_attr_fn_like_width_exceeds_max_width() { + let mut config = Config::default(); + config.override_value("attr_fn_like_width", "101"); + assert_eq!(config.attr_fn_like_width(), 100); + } + + #[test] + fn test_override_struct_lit_exceeds_max_width() { + let mut config = Config::default(); + config.override_value("struct_lit_width", "101"); + assert_eq!(config.struct_lit_width(), 100); + } + + #[test] + fn test_override_struct_variant_width_exceeds_max_width() { + let mut config = Config::default(); + config.override_value("struct_variant_width", "101"); + assert_eq!(config.struct_variant_width(), 100); + } + + #[test] + fn test_override_array_width_exceeds_max_width() { + let mut config = Config::default(); + config.override_value("array_width", "101"); + assert_eq!(config.array_width(), 100); + } + + #[test] + fn test_override_chain_width_exceeds_max_width() { + let mut config = Config::default(); + config.override_value("chain_width", "101"); + assert_eq!(config.chain_width(), 100); + } + + #[test] + fn test_override_single_line_if_else_max_width_exceeds_max_width() { + let mut config = Config::default(); + config.override_value("single_line_if_else_max_width", "101"); + assert_eq!(config.single_line_if_else_max_width(), 100); + } + } } diff --git a/rustfmt-core/rustfmt-config/src/options.rs b/rustfmt-core/rustfmt-config/src/options.rs index 5d8319ef5cf..b34af248da8 100644 --- a/rustfmt-core/rustfmt-config/src/options.rs +++ b/rustfmt-core/rustfmt-config/src/options.rs @@ -77,14 +77,16 @@ pub enum TypeDensity { } #[config_type] -/// To what extent does rustfmt pursue its heuristics? +/// Heuristic settings that can be used to simply +/// the configuration of the granular width configurations +/// like `struct_lit_width`, `array_width`, etc. pub enum Heuristics { /// Turn off any heuristics Off, /// Turn on max heuristics Max, - /// Use Rustfmt's defaults - Default, + /// Use scaled values based on the value of `max_width` + Scaled, } impl Density { @@ -248,14 +250,6 @@ impl WidthHeuristics { } } -impl std::str::FromStr for WidthHeuristics { - type Err = &'static str; - - fn from_str(_: &str) -> Result { - Err("WidthHeuristics is not parsable") - } -} - impl Default for EmitMode { fn default() -> EmitMode { EmitMode::Files diff --git a/rustfmt-core/rustfmt-lib/src/attr.rs b/rustfmt-core/rustfmt-lib/src/attr.rs index e868efe4d6e..1b83a564a33 100644 --- a/rustfmt-core/rustfmt-lib/src/attr.rs +++ b/rustfmt-core/rustfmt-lib/src/attr.rs @@ -228,7 +228,7 @@ impl Rewrite for ast::MetaItem { // 1 = "]" shape.sub_width(1)?, self.span, - context.config.width_heuristics().attr_fn_like_width, + context.config.attr_fn_like_width(), Some(if has_trailing_comma { SeparatorTactic::Always } else { diff --git a/rustfmt-core/rustfmt-lib/src/chains.rs b/rustfmt-core/rustfmt-lib/src/chains.rs index ae7d04f7710..16a570945c5 100644 --- a/rustfmt-core/rustfmt-lib/src/chains.rs +++ b/rustfmt-core/rustfmt-lib/src/chains.rs @@ -566,7 +566,7 @@ impl<'a> ChainFormatterShared<'a> { let one_line_budget = if self.child_count == 1 { shape.width } else { - min(shape.width, context.config.width_heuristics().chain_width) + min(shape.width, context.config.chain_width()) } .saturating_sub(almost_total); diff --git a/rustfmt-core/rustfmt-lib/src/expr.rs b/rustfmt-core/rustfmt-lib/src/expr.rs index afdf4b0a5e3..1d7c07b6eb3 100644 --- a/rustfmt-core/rustfmt-lib/src/expr.rs +++ b/rustfmt-core/rustfmt-lib/src/expr.rs @@ -943,22 +943,11 @@ impl<'a> ControlFlow<'a> { || last_line_offsetted(shape.used_width(), &pat_expr_string)); // Try to format if-else on single line. - if self.allow_single_line - && context - .config - .width_heuristics() - .single_line_if_else_max_width - > 0 - { + if self.allow_single_line && context.config.single_line_if_else_max_width() > 0 { let trial = self.rewrite_single_line(&pat_expr_string, context, shape.width); if let Some(cond_str) = trial { - if cond_str.len() - <= context - .config - .width_heuristics() - .single_line_if_else_max_width - { + if cond_str.len() <= context.config.single_line_if_else_max_width() { return Some((cond_str, 0)); } } @@ -1297,7 +1286,7 @@ pub(crate) fn rewrite_call( args.iter(), shape, span, - context.config.width_heuristics().fn_call_width, + context.config.fn_call_width(), choose_separator_tactic(context, span), ) } @@ -1833,7 +1822,7 @@ pub(crate) fn rewrite_tuple<'a, T: 'a + IntoOverflowableItem<'a>>( items, shape, span, - context.config.width_heuristics().fn_call_width, + context.config.fn_call_width(), force_tactic, ) } else { diff --git a/rustfmt-core/rustfmt-lib/src/items.rs b/rustfmt-core/rustfmt-lib/src/items.rs index 98554f15deb..4a507cf386b 100644 --- a/rustfmt-core/rustfmt-lib/src/items.rs +++ b/rustfmt-core/rustfmt-lib/src/items.rs @@ -572,8 +572,8 @@ impl<'a> FmtVisitor<'a> { ) .collect() }; - let mut items: Vec<_> = - itemize_list_with(self.config.width_heuristics().struct_variant_width); + let mut items: Vec<_> = itemize_list_with(self.config.struct_variant_width()); + // If one of the variants use multiple lines, use multi-lined formatting for all variants. let has_multiline_variant = items.iter().any(|item| item.inner_as_ref().contains('\n')); let has_single_line_variant = items.iter().any(|item| !item.inner_as_ref().contains('\n')); @@ -1532,7 +1532,7 @@ fn format_tuple_struct( fields.iter(), shape, span, - context.config.width_heuristics().fn_call_width, + context.config.fn_call_width(), None, )?; } diff --git a/rustfmt-core/rustfmt-lib/src/lists.rs b/rustfmt-core/rustfmt-lib/src/lists.rs index fbaf9e17695..6546deb5d10 100644 --- a/rustfmt-core/rustfmt-lib/src/lists.rs +++ b/rustfmt-core/rustfmt-lib/src/lists.rs @@ -863,7 +863,7 @@ pub(crate) fn struct_lit_shape( }; let shape_width = shape.width.checked_sub(prefix_width + suffix_width); if let Some(w) = shape_width { - let shape_width = cmp::min(w, context.config.width_heuristics().struct_lit_width); + let shape_width = cmp::min(w, context.config.struct_lit_width()); Some((Some(Shape::legacy(shape_width, shape.indent)), v_shape)) } else { Some((None, v_shape)) diff --git a/rustfmt-core/rustfmt-lib/src/macros.rs b/rustfmt-core/rustfmt-lib/src/macros.rs index b1f2004f8be..b92faae8898 100644 --- a/rustfmt-core/rustfmt-lib/src/macros.rs +++ b/rustfmt-core/rustfmt-lib/src/macros.rs @@ -368,7 +368,7 @@ fn rewrite_macro_inner( arg_vec.iter(), shape, mac.span(), - context.config.width_heuristics().fn_call_width, + context.config.fn_call_width(), if trailing_comma { Some(SeparatorTactic::Always) } else { diff --git a/rustfmt-core/rustfmt-lib/src/overflow.rs b/rustfmt-core/rustfmt-lib/src/overflow.rs index e52d5fa0889..7357da3dc88 100644 --- a/rustfmt-core/rustfmt-lib/src/overflow.rs +++ b/rustfmt-core/rustfmt-lib/src/overflow.rs @@ -317,7 +317,7 @@ pub(crate) fn rewrite_with_square_brackets<'a, T: 'a + IntoOverflowableItem<'a>> span, lhs, rhs, - context.config.width_heuristics().array_width, + context.config.array_width(), force_separator_tactic, Some(("[", "]")), ) diff --git a/rustfmt-core/rustfmt-lib/tests/source/chains.rs b/rustfmt-core/rustfmt-lib/tests/source/chains.rs index c77f5bac4cb..3d8960035b4 100644 --- a/rustfmt-core/rustfmt-lib/tests/source/chains.rs +++ b/rustfmt-core/rustfmt-lib/tests/source/chains.rs @@ -1,4 +1,4 @@ -// rustfmt-use_small_heuristics: Off +// rustfmt-width_heuristics: Off // Test chain formatting. fn main() { diff --git a/rustfmt-core/rustfmt-lib/tests/source/configs/use_small_heuristics/max.rs b/rustfmt-core/rustfmt-lib/tests/source/configs/width_heuristics/max.rs similarity index 90% rename from rustfmt-core/rustfmt-lib/tests/source/configs/use_small_heuristics/max.rs rename to rustfmt-core/rustfmt-lib/tests/source/configs/width_heuristics/max.rs index 8d30932e2c2..fb1bbc7fe61 100644 --- a/rustfmt-core/rustfmt-lib/tests/source/configs/use_small_heuristics/max.rs +++ b/rustfmt-core/rustfmt-lib/tests/source/configs/width_heuristics/max.rs @@ -1,4 +1,4 @@ -// rustfmt-use_small_heuristics: Max +// rustfmt-width_heuristics: Max enum Lorem { Ipsum, diff --git a/rustfmt-core/rustfmt-lib/tests/source/configs/width_heuristics/off.rs b/rustfmt-core/rustfmt-lib/tests/source/configs/width_heuristics/off.rs new file mode 100644 index 00000000000..0001f4cc802 --- /dev/null +++ b/rustfmt-core/rustfmt-lib/tests/source/configs/width_heuristics/off.rs @@ -0,0 +1,25 @@ +// rustfmt-width_heuristics: Off + +enum Lorem { + Ipsum, + Dolor(bool), + Sit { + amet: Consectetur, + adipiscing: Elit, + }, +} + +fn main() { + lorem("lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing"); + + let lorem = Lorem { + ipsum: dolor, + sit: amet, + }; + + let lorem = if ipsum { + dolor + } else { + sit + }; +} diff --git a/rustfmt-core/rustfmt-lib/tests/source/configs/width_heuristics/scaled.rs b/rustfmt-core/rustfmt-lib/tests/source/configs/width_heuristics/scaled.rs new file mode 100644 index 00000000000..536a508cbae --- /dev/null +++ b/rustfmt-core/rustfmt-lib/tests/source/configs/width_heuristics/scaled.rs @@ -0,0 +1,25 @@ +// rustfmt-width_heuristics: Scaled + +enum Lorem { + Ipsum, + Dolor(bool), + Sit { + amet: Consectetur, + adipiscing: Elit, + }, +} + +fn main() { + lorem("lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing"); + + let lorem = Lorem { + ipsum: dolor, + sit: amet, + }; + + let lorem = if ipsum { + dolor + } else { + sit + }; +} diff --git a/rustfmt-core/rustfmt-lib/tests/target/chains.rs b/rustfmt-core/rustfmt-lib/tests/target/chains.rs index 292da298195..dbace9a5bd4 100644 --- a/rustfmt-core/rustfmt-lib/tests/target/chains.rs +++ b/rustfmt-core/rustfmt-lib/tests/target/chains.rs @@ -1,4 +1,4 @@ -// rustfmt-use_small_heuristics: Off +// rustfmt-width_heuristics: Off // Test chain formatting. fn main() { diff --git a/rustfmt-core/rustfmt-lib/tests/target/configs/use_small_heuristics/max.rs b/rustfmt-core/rustfmt-lib/tests/target/configs/width_heuristics/max.rs similarity index 88% rename from rustfmt-core/rustfmt-lib/tests/target/configs/use_small_heuristics/max.rs rename to rustfmt-core/rustfmt-lib/tests/target/configs/width_heuristics/max.rs index 785dfbea014..6963339ff4a 100644 --- a/rustfmt-core/rustfmt-lib/tests/target/configs/use_small_heuristics/max.rs +++ b/rustfmt-core/rustfmt-lib/tests/target/configs/width_heuristics/max.rs @@ -1,4 +1,4 @@ -// rustfmt-use_small_heuristics: Max +// rustfmt-width_heuristics: Max enum Lorem { Ipsum, diff --git a/rustfmt-core/rustfmt-lib/tests/target/configs/width_heuristics/off.rs b/rustfmt-core/rustfmt-lib/tests/target/configs/width_heuristics/off.rs new file mode 100644 index 00000000000..0001f4cc802 --- /dev/null +++ b/rustfmt-core/rustfmt-lib/tests/target/configs/width_heuristics/off.rs @@ -0,0 +1,25 @@ +// rustfmt-width_heuristics: Off + +enum Lorem { + Ipsum, + Dolor(bool), + Sit { + amet: Consectetur, + adipiscing: Elit, + }, +} + +fn main() { + lorem("lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing"); + + let lorem = Lorem { + ipsum: dolor, + sit: amet, + }; + + let lorem = if ipsum { + dolor + } else { + sit + }; +} diff --git a/rustfmt-core/rustfmt-lib/tests/target/configs/width_heuristics/scaled.rs b/rustfmt-core/rustfmt-lib/tests/target/configs/width_heuristics/scaled.rs new file mode 100644 index 00000000000..27a5850baf4 --- /dev/null +++ b/rustfmt-core/rustfmt-lib/tests/target/configs/width_heuristics/scaled.rs @@ -0,0 +1,26 @@ +// rustfmt-width_heuristics: Scaled + +enum Lorem { + Ipsum, + Dolor(bool), + Sit { amet: Consectetur, adipiscing: Elit }, +} + +fn main() { + lorem( + "lorem", + "ipsum", + "dolor", + "sit", + "amet", + "consectetur", + "adipiscing", + ); + + let lorem = Lorem { + ipsum: dolor, + sit: amet, + }; + + let lorem = if ipsum { dolor } else { sit }; +} diff --git a/rustfmt-core/rustfmt-lib/tests/target/issue_4049.rs b/rustfmt-core/rustfmt-lib/tests/target/issue_4049.rs index fe025a0f649..d015232673e 100644 --- a/rustfmt-core/rustfmt-lib/tests/target/issue_4049.rs +++ b/rustfmt-core/rustfmt-lib/tests/target/issue_4049.rs @@ -1,5 +1,5 @@ // rustfmt-max_width: 110 -// rustfmt-use_small_heuristics: Max +// rustfmt-width_heuristics: Max // rustfmt-hard_tabs: true // rustfmt-use_field_init_shorthand: true // rustfmt-overflow_delimited_expr: true