diff --git a/Configurations.md b/Configurations.md index c92be5df010..37cb7474130 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 [`use_small_heuristics`](#use_small_heuristics), but a value set directly for `array_width` will take precedence. + +See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_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 [`use_small_heuristics`](#use_small_heuristics), but a value set directly for `attr_fn_like_width` will take precedence. + +See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_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 [`use_small_heuristics`](#use_small_heuristics), but a value set directly for `chain_width` will take precedence. + +See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_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 [`use_small_heuristics`](#use_small_heuristics), but a value set directly for `fn_call_width` will take precedence. + +See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics) ## `fn_single_line` @@ -2079,6 +2124,18 @@ Don't reformat out of line modules - **Possible values**: `true`, `false` - **Stable**: No (tracking issue: #3389) +## `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 `use_small_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 [`use_small_heuristics`](#use_small_heuristics), but a value set directly for `single_line_if_else_max_width` will take precedence. + +See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics) + ## `space_after_colon` Leave a space after the colon. @@ -2256,6 +2313,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 `use_small_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 [`use_small_heuristics`](#use_small_heuristics), but a value set directly for `struct_lit_width` will take precedence. + +See also [`max_width`](#max_width), [`use_small_heuristics`](#use_small_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 `use_small_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 [`use_small_heuristics`](#use_small_heuristics), but a value set directly for `struct_variant_width` will take precedence. + +See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics) ## `tab_spaces` @@ -2448,13 +2528,43 @@ fn main() { ## `use_small_heuristics` -Whether to use different formatting for items and expressions if they satisfy a heuristic notion of 'small'. +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. + +Note that explicitly provided values for the width configuration settings take precedence and override the calculated values determined by `use_small_heuristics`. - **Default value**: `"Default"` - **Possible values**: `"Default"`, `"Off"`, `"Max"` - **Stable**: Yes #### `Default` (default): +When `use_small_heuristics` is set to `Default`, 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 { @@ -2485,6 +2595,7 @@ fn main() { ``` #### `Off`: +When `use_small_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 { @@ -2513,6 +2624,16 @@ fn main() { ``` #### `Max`: +When `use_small_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 { @@ -2530,6 +2651,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/src/attr.rs b/src/attr.rs index 015e9f9b252..0b5a0aba330 100644 --- a/src/attr.rs +++ b/src/attr.rs @@ -300,7 +300,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/src/chains.rs b/src/chains.rs index ac14ec9e110..8053f0e8fec 100644 --- a/src/chains.rs +++ b/src/chains.rs @@ -577,7 +577,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/src/config/config_type.rs b/src/config/config_type.rs index bd4a847e0f1..2f567b25521 100644 --- a/src/config/config_type.rs +++ b/src/config/config_type.rs @@ -95,7 +95,15 @@ macro_rules! create_config { pub fn $i(&mut self, value: $ty) { (self.0).$i.2 = value; match stringify!($i) { - "max_width" | "use_small_heuristics" => self.0.set_heuristics(), + "max_width" + | "use_small_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(), "merge_imports" => self.0.set_merge_imports(), &_ => (), @@ -230,7 +238,15 @@ macro_rules! create_config { } match key { - "max_width" | "use_small_heuristics" => self.set_heuristics(), + "max_width" + | "use_small_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(), "merge_imports" => self.set_merge_imports(), &_ => (), @@ -281,16 +297,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.use_small_heuristics.2 { + Heuristics::Default => + 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/src/config/mod.rs b/src/config/mod.rs index dfce7977bfe..8c04363b1fd 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -35,9 +35,26 @@ 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"; + indent_style: IndentStyle, IndentStyle::Block, false, "How do we indent expressions or items"; + + // Width Heuristics 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: WidthHeuristics, WidthHeuristics::scaled(100), false, + "'small' heuristic values"; + 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"; @@ -154,8 +171,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"; make_backup: bool, false, false, "Backup changed files"; @@ -394,9 +409,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, @@ -408,13 +420,33 @@ 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"; + // merge_imports deprecation imports_granularity: ImportGranularity, ImportGranularity::Preserve, false, "Merge imports"; merge_imports: bool, false, false, "(deprecated: use imports_granularity instead)"; + // Width Heuristics + use_small_heuristics: Heuristics, Heuristics::Default, true, + "Whether to use different formatting for items and \ + expressions if they satisfy a heuristic notion of 'small'."; + width_heuristics: WidthHeuristics, WidthHeuristics::scaled(100), false, + "'small' heuristic values"; + + 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"; unstable_option: bool, false, false, "An unstable option"; @@ -519,8 +551,15 @@ mod test { hard_tabs = false tab_spaces = 4 newline_style = "Auto" -use_small_heuristics = "Default" indent_style = "Block" +use_small_heuristics = "Default" +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 @@ -683,4 +722,242 @@ make_backup = false assert_eq!(config.imports_granularity(), ImportGranularity::Module); } } + + #[cfg(test)] + mod use_small_heuristics { + use super::*; + + #[test] + fn test_default_sets_correct_widths() { + let toml = r#" + use_small_heuristics = "Default" + 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#" + use_small_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#" + use_small_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_default() { + let toml = r#" + use_small_heuristics = "Default" + 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#" + use_small_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#" + use_small_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/src/config/options.rs b/src/config/options.rs index c0491630c00..3b91021813c 100644 --- a/src/config/options.rs +++ b/src/config/options.rs @@ -79,13 +79,15 @@ 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 + /// Use scaled values based on the value of `max_width` Default, } diff --git a/src/expr.rs b/src/expr.rs index d3fd22653b4..ced382c4915 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -899,22 +899,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)); } } @@ -1246,7 +1235,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), ) } @@ -1785,7 +1774,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/src/items.rs b/src/items.rs index 61b49911e76..ecbd0bd12ec 100644 --- a/src/items.rs +++ b/src/items.rs @@ -505,8 +505,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')); @@ -1479,7 +1479,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/src/lists.rs b/src/lists.rs index bc55ef6686a..ccf8f784c04 100644 --- a/src/lists.rs +++ b/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/src/macros.rs b/src/macros.rs index 190f4b599b0..52534b216a6 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -382,7 +382,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/src/overflow.rs b/src/overflow.rs index f1b870101e5..d670b0a41e8 100644 --- a/src/overflow.rs +++ b/src/overflow.rs @@ -318,7 +318,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/tests/source/configs/chain_width/always.rs b/tests/source/configs/chain_width/always.rs new file mode 100644 index 00000000000..2d16d66aec1 --- /dev/null +++ b/tests/source/configs/chain_width/always.rs @@ -0,0 +1,23 @@ +// rustfmt-chain_width: 1 +// setting an unachievable chain_width to always get chains +// on separate lines + +struct Fluent {} + +impl Fluent { + fn blorp(&self) -> &Self { + self + } +} + +fn main() { + let test = Fluent {}; + + // should be left alone + test.blorp(); + + // should be wrapped + test.blorp().blorp(); + test.blorp().blorp().blorp(); + test.blorp().blorp().blorp().blorp(); +} diff --git a/tests/source/configs/chain_width/small.rs b/tests/source/configs/chain_width/small.rs new file mode 100644 index 00000000000..26f9354537a --- /dev/null +++ b/tests/source/configs/chain_width/small.rs @@ -0,0 +1,23 @@ +// rustfmt-chain_width: 40 + +struct Fluent {} + +impl Fluent { + fn blorp(&self) -> &Self { + self + } +} + +fn main() { + let test = Fluent {}; + + // should not be wrapped + test.blorp(); + test.blorp().blorp(); + test.blorp().blorp().blorp(); + test.blorp().blorp().blorp().blorp(); + + // should be wrapped + test.blorp().blorp().blorp().blorp().blorp(); + test.blorp().blorp().blorp().blorp().blorp().blorp(); +} diff --git a/tests/source/configs/chain_width/tiny.rs b/tests/source/configs/chain_width/tiny.rs new file mode 100644 index 00000000000..fffc81dd5d6 --- /dev/null +++ b/tests/source/configs/chain_width/tiny.rs @@ -0,0 +1,21 @@ +// rustfmt-chain_width: 20 + +struct Fluent {} + +impl Fluent { + fn blorp(&self) -> &Self { + self + } +} + +fn main() { + let test = Fluent {}; + + // should not be wrapped + test.blorp(); + test.blorp().blorp(); + + // should be wrapped + test.blorp().blorp().blorp(); + test.blorp().blorp().blorp().blorp(); +} diff --git a/tests/source/configs/use_small_heuristics/default.rs b/tests/source/configs/use_small_heuristics/default.rs new file mode 100644 index 00000000000..68bc40271a1 --- /dev/null +++ b/tests/source/configs/use_small_heuristics/default.rs @@ -0,0 +1,25 @@ +// rustfmt-use_small_heuristics: Default + +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/tests/source/configs/use_small_heuristics/off.rs b/tests/source/configs/use_small_heuristics/off.rs new file mode 100644 index 00000000000..f76392d2404 --- /dev/null +++ b/tests/source/configs/use_small_heuristics/off.rs @@ -0,0 +1,25 @@ +// rustfmt-use_small_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/tests/target/configs/chain_width/always.rs b/tests/target/configs/chain_width/always.rs new file mode 100644 index 00000000000..b16d25251f6 --- /dev/null +++ b/tests/target/configs/chain_width/always.rs @@ -0,0 +1,29 @@ +// rustfmt-chain_width: 1 +// setting an unachievable chain_width to always get chains +// on separate lines + +struct Fluent {} + +impl Fluent { + fn blorp(&self) -> &Self { + self + } +} + +fn main() { + let test = Fluent {}; + + // should be left alone + test.blorp(); + + // should be wrapped + test.blorp() + .blorp(); + test.blorp() + .blorp() + .blorp(); + test.blorp() + .blorp() + .blorp() + .blorp(); +} diff --git a/tests/target/configs/chain_width/small.rs b/tests/target/configs/chain_width/small.rs new file mode 100644 index 00000000000..2f2f72777f8 --- /dev/null +++ b/tests/target/configs/chain_width/small.rs @@ -0,0 +1,32 @@ +// rustfmt-chain_width: 40 + +struct Fluent {} + +impl Fluent { + fn blorp(&self) -> &Self { + self + } +} + +fn main() { + let test = Fluent {}; + + // should not be wrapped + test.blorp(); + test.blorp().blorp(); + test.blorp().blorp().blorp(); + test.blorp().blorp().blorp().blorp(); + + // should be wrapped + test.blorp() + .blorp() + .blorp() + .blorp() + .blorp(); + test.blorp() + .blorp() + .blorp() + .blorp() + .blorp() + .blorp(); +} diff --git a/tests/target/configs/chain_width/tiny.rs b/tests/target/configs/chain_width/tiny.rs new file mode 100644 index 00000000000..960d245f8a1 --- /dev/null +++ b/tests/target/configs/chain_width/tiny.rs @@ -0,0 +1,26 @@ +// rustfmt-chain_width: 20 + +struct Fluent {} + +impl Fluent { + fn blorp(&self) -> &Self { + self + } +} + +fn main() { + let test = Fluent {}; + + // should not be wrapped + test.blorp(); + test.blorp().blorp(); + + // should be wrapped + test.blorp() + .blorp() + .blorp(); + test.blorp() + .blorp() + .blorp() + .blorp(); +} diff --git a/tests/target/configs/use_small_heuristics/default.rs b/tests/target/configs/use_small_heuristics/default.rs new file mode 100644 index 00000000000..d67bd9aafaf --- /dev/null +++ b/tests/target/configs/use_small_heuristics/default.rs @@ -0,0 +1,26 @@ +// rustfmt-use_small_heuristics: Default + +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/tests/target/configs/use_small_heuristics/off.rs b/tests/target/configs/use_small_heuristics/off.rs new file mode 100644 index 00000000000..f76392d2404 --- /dev/null +++ b/tests/target/configs/use_small_heuristics/off.rs @@ -0,0 +1,25 @@ +// rustfmt-use_small_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/tests/target/issue_4049.rs b/tests/target/issue_4049.rs new file mode 100644 index 00000000000..fe025a0f649 --- /dev/null +++ b/tests/target/issue_4049.rs @@ -0,0 +1,26 @@ +// rustfmt-max_width: 110 +// rustfmt-use_small_heuristics: Max +// rustfmt-hard_tabs: true +// rustfmt-use_field_init_shorthand: true +// rustfmt-overflow_delimited_expr: true + +// https://github.com/rust-lang/rustfmt/issues/4049 +fn foo() { + { + { + if let Some(MpcEv::PlayDrum(pitch, vel)) = + // self.mpc.handle_input(e, /*btn_ctrl_down,*/ tx_launch_to_daw, state_view) + self.mpc.handle_input(e, &mut MyBorrowedState { tx_launch_to_daw, state_view }) + { + println!("bar"); + } + + if let Some(e) = + // self.note_input.handle_input(e, /*btn_ctrl_down,*/ tx_launch_to_daw, state_view) + self.note_input.handle_input(e, &mut MyBorrowedState { tx_launch_to_daw, state_view }) + { + println!("baz"); + } + } + } +}