use std::{env, fs};
use std::cell::Cell;
+use std::default::Default;
use std::fs::File;
use std::io::{Error, ErrorKind, Read};
use std::path::{Path, PathBuf};
}
}
-configuration_option_enum! { Style:
- Rfc, // Follow the style RFCs style.
- Legacy, // Follow the traditional Rustfmt style.
-}
-
configuration_option_enum! { NewlineStyle:
Windows, // \r\n
Unix, // \n
AlwaysNextLine,
}
-// How to indent a function's return type.
-configuration_option_enum! { ReturnIndent:
- // Aligned with the arguments
- WithArgs,
- // Aligned with the where clause
- WithWhereClause,
-}
-
configuration_option_enum! { IndentStyle:
// First line on the same line as the opening brace, all lines aligned with
// the first line.
Compressed,
// Use more lines.
Tall,
- // Try to compress if the body is empty.
- CompressedIfEmpty,
// Place every item on a separate line.
Vertical,
}
pub fn to_list_tactic(self) -> ListTactic {
match self {
Density::Compressed => ListTactic::Mixed,
- Density::Tall | Density::CompressedIfEmpty => ListTactic::HorizontalVertical,
+ Density::Tall => ListTactic::HorizontalVertical,
Density::Vertical => ListTactic::Vertical,
}
}
}
-configuration_option_enum! { MultilineStyle:
- // Use horizontal layout if it fits in one line, fall back to vertical
- PreferSingle,
- // Use vertical layout
- ForceMulti,
-}
-
-impl MultilineStyle {
- pub fn to_list_tactic(self) -> ListTactic {
- match self {
- MultilineStyle::PreferSingle => ListTactic::HorizontalVertical,
- MultilineStyle::ForceMulti => ListTactic::Vertical,
- }
- }
-}
-
configuration_option_enum! { ReportTactic:
Always,
Unnumbered,
Checkstyle,
}
+configuration_option_enum! { Color:
+ // Always use color, whether it is a piped or terminal output
+ Always,
+ // Never use color
+ Never,
+ // Automatically use color, if supported by terminal
+ Auto,
+}
+
+#[derive(Deserialize, Serialize, Clone, Debug)]
+pub struct WidthHeuristics {
+ // Maximum width of the args of a function call before falling back
+ // to vertical formatting.
+ pub fn_call_width: usize,
+ // Maximum width in the body of a struct lit before falling back to
+ // vertical formatting.
+ pub struct_lit_width: usize,
+ // Maximum width in the body of a struct variant before falling back
+ // to vertical formatting.
+ pub struct_variant_width: usize,
+ // Maximum width of an array literal before falling back to vertical
+ // formatting.
+ pub array_width: usize,
+ // Maximum length of a chain to fit on a single line.
+ pub chain_width: usize,
+ // Maximum line length for single line if-else expressions. A value
+ // of zero means always break if-else expressions.
+ pub single_line_if_else_max_width: usize,
+}
+
+impl WidthHeuristics {
+ // Using this WidthHeuristics means we ignore heuristics.
+ fn null() -> WidthHeuristics {
+ WidthHeuristics {
+ fn_call_width: usize::max_value(),
+ struct_lit_width: 0,
+ struct_variant_width: 0,
+ array_width: usize::max_value(),
+ chain_width: usize::max_value(),
+ single_line_if_else_max_width: 0,
+ }
+ }
+}
+
+impl Default for WidthHeuristics {
+ fn default() -> WidthHeuristics {
+ WidthHeuristics {
+ fn_call_width: 60,
+ struct_lit_width: 18,
+ struct_variant_width: 35,
+ array_width: 60,
+ chain_width: 60,
+ single_line_if_else_max_width: 50,
+ }
+ }
+}
+
+impl ::std::str::FromStr for WidthHeuristics {
+ type Err = &'static str;
+
+ fn from_str(_: &str) -> Result<Self, Self::Err> {
+ Err("WidthHeuristics is not parsable")
+ }
+}
+
+impl ::config::ConfigType for WidthHeuristics {
+ fn doc_hint() -> String {
+ String::new()
+ }
+}
+
/// Trait for types that can be used in `Config`.
pub trait ConfigType: Sized {
/// Returns hint text for use in `Config::print_docs()`. For enum types, this is a
impl PartialConfig {
pub fn to_toml(&self) -> Result<String, String> {
- // file_lines can't be specified in TOML
+ // Non-user-facing options can't be specified in TOML
let mut cloned = self.clone();
cloned.file_lines = None;
+ cloned.verbose = None;
+ cloned.width_heuristics = None;
toml::to_string(&cloned)
.map_err(|e| format!("Could not output config: {}", e.to_string()))
$(
pub fn $i(&mut self, value: $ty) {
(self.0).$i.2 = value;
+ if stringify!($i) == "use_small_heuristics" {
+ self.0.set_heuristics();
+ }
}
)+
}
fn fill_from_parsed_config(mut self, parsed: PartialConfig) -> Config {
$(
if let Some(val) = parsed.$i {
- if !self.$i.3 {
+ if self.$i.3 {
self.$i.1 = true;
self.$i.2 = val;
} else {
self.$i.1 = true;
self.$i.2 = val;
} else {
- println!("Warning: can't set some features as unstable \
- features are only available in nightly channel.");
+ println!("Warning: can't set `{} = {:?}`, unstable features are only \
+ available in nightly channel.", stringify!($i), val);
}
}
}
)+
+ self.set_heuristics();
self
}
)+
_ => panic!("Unknown config key in override: {}", key)
}
+
+ if key == "use_small_heuristics" {
+ self.set_heuristics();
+ }
}
/// Construct a `Config` from the toml file specified at `file_path`.
$(
println!("{}{}", space_str, $dstring);
)+
- println!("");
+ println!();
)+
}
+
+ fn set_heuristics(&mut self) {
+ if self.use_small_heuristics.2 {
+ self.set().width_heuristics(WidthHeuristics::default());
+ } else {
+ self.set().width_heuristics(WidthHeuristics::null());
+ }
+ }
}
// Template for the default configuration
///
/// Return the path if a config file exists, empty if no file exists, and Error for IO errors
pub fn get_toml_path(dir: &Path) -> Result<Option<PathBuf>, Error> {
- const CONFIG_FILE_NAMES: [&'static str; 2] = [".rustfmt.toml", "rustfmt.toml"];
+ const CONFIG_FILE_NAMES: [&str; 2] = [".rustfmt.toml", "rustfmt.toml"];
for config_file_name in &CONFIG_FILE_NAMES {
let config_file = dir.join(config_file_name);
match fs::metadata(&config_file) {
Ok(ref md) if md.is_file() => return Ok(Some(config_file)),
// Return the error if it's something other than `NotFound`; otherwise we didn't
// find the project file yet, and continue searching.
- Err(e) => if e.kind() != ErrorKind::NotFound {
- return Err(e);
- },
+ Err(e) => {
+ if e.kind() != ErrorKind::NotFound {
+ return Err(e);
+ }
+ }
_ => {}
}
}
create_config! {
- unstable_features: bool, false, true,
- "Enables unstable features. Only available on nightly channel";
- verbose: bool, false, false, "Use verbose output";
- disable_all_formatting: bool, false, false, "Don't reformat anything";
- skip_children: bool, false, false, "Don't reformat out of line modules";
- 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";
- max_width: usize, 100, false, "Maximum width of each line";
- error_on_line_overflow: bool, true, false, "Error if unable to get all lines within max_width";
- error_on_line_overflow_comments: bool, true, false,
- "Error if unable to get comments within max_width";
- tab_spaces: usize, 4, false, "Number of spaces per tab";
- fn_call_width: usize, 60, false,
- "Maximum width of the args of a function call before falling back to vertical formatting";
- struct_lit_width: usize, 18, false,
- "Maximum width in the body of a struct lit before falling back to vertical formatting";
- struct_variant_width: usize, 35, false,
- "Maximum width in the body of a struct variant before falling back to vertical formatting";
- force_explicit_abi: bool, true, false, "Always print the abi for extern items";
- newline_style: NewlineStyle, NewlineStyle::Unix, false, "Unix or Windows line endings";
- fn_brace_style: BraceStyle, BraceStyle::SameLineWhere, false, "Brace style for functions";
- item_brace_style: BraceStyle, BraceStyle::SameLineWhere, false,
- "Brace style for structs and enums";
- control_style: Style, Style::Rfc, false, "Indent style for control flow statements";
- control_brace_style: ControlBraceStyle, ControlBraceStyle::AlwaysSameLine, false,
- "Brace style for control flow constructs";
- impl_empty_single_line: bool, true, false, "Put empty-body implementations on a single line";
- trailing_comma: SeparatorTactic, SeparatorTactic::Vertical, false,
- "How to handle trailing commas for lists";
- trailing_semicolon: bool, true, false,
- "Add trailing semicolon after break, continue and return";
- fn_empty_single_line: bool, true, false, "Put empty-body functions on a single line";
+ // Fundamental stuff
+ max_width: usize, 100, true, "Maximum width of each line";
+ 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::Unix, true, "Unix or Windows line endings";
+ indent_style: IndentStyle, IndentStyle::Block, false, "How do we indent expressions or items.";
+ use_small_heuristics: bool, true, false, "Whether to use different formatting for items and\
+ expressions if they satisfy a heuristic notion of 'small'.";
+
+ // strings and comments
+ format_strings: bool, false, false, "Format string literals where necessary";
+ wrap_comments: bool, false, true, "Break comments to fit on the line";
+ comment_width: usize, 80, false,
+ "Maximum length of comments. No effect unless wrap_comments = true";
+ normalize_comments: bool, false, true, "Convert /* */ comments to // comments where possible";
+
+ // Single line expressions and items.
+ empty_item_single_line: bool, true, false,
+ "Put empty-body functions and impls on a single line";
+ struct_lit_single_line: bool, true, false,
+ "Put small struct literals on a single line";
fn_single_line: bool, false, false, "Put single-expression functions on a single line";
- fn_return_indent: ReturnIndent, ReturnIndent::WithArgs, false,
- "Location of return type in function declaration";
- fn_args_paren_newline: bool, false, false, "If function argument parenthesis goes on a newline";
- fn_args_density: Density, Density::Tall, false, "Argument density in functions";
- fn_args_indent: IndentStyle, IndentStyle::Block, false,
- "Layout of function arguments and tuple structs";
- array_indent: IndentStyle, IndentStyle::Block, false, "Indent on arrays";
- array_width: usize, 60, false,
- "Maximum width of an array literal before falling back to vertical formatting";
- array_horizontal_layout_threshold: usize, 0, false,
- "How many elements array must have before rustfmt uses horizontal layout.";
- type_punctuation_density: TypeDensity, TypeDensity::Wide, false,
- "Determines if '+' or '=' are wrapped in spaces in the punctuation of types";
- where_style: Style, Style::Rfc, false, "Overall strategy for where clauses";
- // TODO:
- // 1. Should we at least try to put the where clause on the same line as the rest of the
- // function decl?
- // 2. Currently options `Tall` and `Vertical` produce the same output.
- where_density: Density, Density::Vertical, false, "Density of a where clause";
- where_layout: ListTactic, ListTactic::Vertical, false, "Element layout inside a where clause";
- where_pred_indent: IndentStyle, IndentStyle::Visual, false,
- "Indentation style of a where predicate";
- generics_indent: IndentStyle, IndentStyle::Block, false, "Indentation of generics";
- struct_lit_style: IndentStyle, IndentStyle::Block, false, "Style of struct definition";
- struct_lit_multiline_style: MultilineStyle, MultilineStyle::PreferSingle, false,
- "Multiline style on literal structs";
- fn_call_style: IndentStyle, IndentStyle::Block, false, "Indentation for function calls, etc.";
- report_todo: ReportTactic, ReportTactic::Never, false,
- "Report all, none or unnumbered occurrences of TODO in source file comments";
- report_fixme: ReportTactic, ReportTactic::Never, false,
- "Report all, none or unnumbered occurrences of FIXME in source file comments";
- chain_indent: IndentStyle, IndentStyle::Block, false, "Indentation of chain";
- chain_one_line_max: usize, 60, false, "Maximum length of a chain to fit on a single line";
- chain_split_single_child: bool, false, false, "Split a chain with a single child if its length \
- exceeds `chain_one_line_max`";
+ where_single_line: bool, false, false, "To force single line where layout";
+
+ // Imports
imports_indent: IndentStyle, IndentStyle::Visual, false, "Indent of imports";
imports_layout: ListTactic, ListTactic::Mixed, false, "Item layout inside a import block";
+
+ // Ordering
reorder_extern_crates: bool, true, false, "Reorder extern crate statements alphabetically";
reorder_extern_crates_in_group: bool, true, false, "Reorder extern crate statements in group";
reorder_imports: bool, false, false, "Reorder import statements alphabetically";
reorder_imports_in_group: bool, false, false, "Reorder import statements in group";
reorder_imported_names: bool, true, false,
"Reorder lists of names in import statements alphabetically";
- single_line_if_else_max_width: usize, 50, false, "Maximum line length for single line if-else \
- expressions. A value of zero means always break \
- if-else expressions.";
- format_strings: bool, false, false, "Format string literals where necessary";
- force_format_strings: bool, false, false, "Always format string literals";
- take_source_hints: bool, false, false,
- "Retain some formatting characteristics from the source code";
- hard_tabs: bool, false, false, "Use tab characters for indentation, spaces for alignment";
- wrap_comments: bool, false, false, "Break comments to fit on the line";
- comment_width: usize, 80, false,
- "Maximum length of comments. No effect unless wrap_comments = true";
- normalize_comments: bool, false, false, "Convert /* */ comments to // comments where possible";
- wrap_match_arms: bool, true, false, "Wrap the body of arms in blocks when it does not fit on \
- the same line with the pattern of arms";
- match_block_trailing_comma: bool, false, false,
- "Put a trailing comma after a block based match arm (non-block arms are not affected)";
- match_arm_forces_newline: bool, false, false,
- "Force match arm bodies to be in a new lines";
- indent_match_arms: bool, true, false, "Indent match arms instead of keeping them at the same \
- indentation level as the match keyword";
- match_pattern_separator_break_point: SeparatorPlace, SeparatorPlace::Back, false,
- "Put a match sub-patterns' separator in front or back.";
- closure_block_indent_threshold: isize, 7, false,
- "How many lines a closure must have before it is block indented. \
- -1 means never use block indent.";
- space_before_type_annotation: bool, false, false,
- "Leave a space before the colon in a type annotation";
- space_after_type_annotation_colon: bool, true, false,
- "Leave a space after the colon in a type annotation";
- space_before_struct_lit_field_colon: bool, false, false,
- "Leave a space before the colon in a struct literal field";
- space_after_struct_lit_field_colon: bool, true, false,
- "Leave a space after the colon in a struct literal field";
- space_before_bound: bool, false, false,
- "Leave a space before the colon in a trait or lifetime bound";
- space_after_bound_colon: bool, true, false,
- "Leave a space after the colon in a trait or lifetime bound";
+
+ // Spaces around punctuation
+ binop_separator: SeparatorPlace, SeparatorPlace::Front, false,
+ "Where to put a binary operator when a binary expression goes multiline.";
+ type_punctuation_density: TypeDensity, TypeDensity::Wide, false,
+ "Determines if '+' or '=' are wrapped in spaces in the punctuation of types";
+ space_before_colon: bool, false, false, "Leave a space before the colon";
+ space_after_colon: bool, true, false, "Leave a space after the colon";
spaces_around_ranges: bool, false, false, "Put spaces around the .. and ... range operators";
- spaces_within_angle_brackets: bool, false, false,
- "Put spaces within non-empty generic arguments";
- spaces_within_square_brackets: bool, false, false,
- "Put spaces within non-empty square brackets";
- spaces_within_parens: bool, false, false, "Put spaces within non-empty parentheses";
- use_try_shorthand: bool, false, false, "Replace uses of the try! macro by the ? shorthand";
- write_mode: WriteMode, WriteMode::Overwrite, false,
- "What Write Mode to use when none is supplied: \
- Replace, Overwrite, Display, Plain, Diff, Coverage";
- condense_wildcard_suffixes: bool, false, false, "Replace strings of _ wildcards by a single .. \
- in tuple patterns";
+ spaces_within_parens_and_brackets: bool, false, false,
+ "Put spaces within non-empty parentheses or brackets";
+
+ // Misc.
combine_control_expr: bool, true, false, "Combine control expressions with function calls.";
struct_field_align_threshold: usize, 0, false, "Align struct fields if their diffs fits within \
threshold.";
remove_blank_lines_at_start_or_end_of_block: bool, true, false,
"Remove blank lines at start or end of a block";
- attributes_on_same_line_as_field: bool, true, false,
- "Try to put attributes on the same line as fields.";
- attributes_on_same_line_as_variant: bool, true, false,
- "Try to put attributes on the same line as variants in enum declarations.";
- multiline_closure_forces_block: bool, false, false,
- "Force multiline closure bodies to be wrapped in a block";
- multiline_match_arm_forces_block: bool, false, false,
- "Force multiline match arm bodies to be wrapped in a block";
- merge_derives: bool, true, false, "Merge multiple `#[derive(...)]` into a single one";
- binop_separator: SeparatorPlace, SeparatorPlace::Front, false,
- "Where to put a binary operator when a binary expression goes multiline.";
+ same_line_attributes: bool, true, false,
+ "Try to put attributes on the same line as fields and variants.";
+ match_arm_blocks: bool, true, false, "Wrap the body of arms in blocks when it does not fit on \
+ the same line with the pattern of arms";
+ force_multiline_blocks: bool, false, false,
+ "Force multiline closure bodies and match arms to be wrapped in a block";
+ fn_args_density: Density, Density::Tall, false, "Argument density in functions";
+ brace_style: BraceStyle, BraceStyle::SameLineWhere, false, "Brace style for items";
+ control_brace_style: ControlBraceStyle, ControlBraceStyle::AlwaysSameLine, false,
+ "Brace style for control flow constructs";
+ trailing_comma: SeparatorTactic, SeparatorTactic::Vertical, false,
+ "How to handle trailing commas for lists";
+ trailing_semicolon: bool, true, false,
+ "Add trailing semicolon after break, continue and return";
+ match_block_trailing_comma: bool, false, false,
+ "Put a trailing comma after a block based match arm (non-block arms are not affected)";
+
+ // Options that can change the source code beyond whitespace/blocks (somewhat linty things)
+ merge_derives: bool, true, true, "Merge multiple `#[derive(...)]` into a single one";
+ use_try_shorthand: bool, false, false, "Replace uses of the try! macro by the ? shorthand";
+ condense_wildcard_suffixes: bool, false, false, "Replace strings of _ wildcards by a single .. \
+ in tuple patterns";
+ force_explicit_abi: bool, true, true, "Always print the abi for extern items";
+
+ // Control options (changes the operation of rustfmt, rather than the formatting)
+ write_mode: WriteMode, WriteMode::Overwrite, false,
+ "What Write Mode to use when none is supplied: \
+ Replace, Overwrite, Display, Plain, Diff, Coverage";
+ color: Color, Color::Auto, false,
+ "What Color option to use when none is supplied: Always, Never, Auto";
required_version: String, env!("CARGO_PKG_VERSION").to_owned(), false,
- "Require a specific version of rustfmt."
+ "Require a specific version of rustfmt.";
+ unstable_features: bool, false, true,
+ "Enables unstable features. Only available on nightly channel";
+ disable_all_formatting: bool, false, false, "Don't reformat anything";
+ skip_children: bool, false, false, "Don't reformat out of line modules";
+ error_on_line_overflow: bool, true, false, "Error if unable to get all lines within max_width";
+ error_on_line_overflow_comments: bool, true, false,
+ "Error if unable to get comments within max_width";
+ report_todo: ReportTactic, ReportTactic::Never, false,
+ "Report all, none or unnumbered occurrences of TODO in source file comments";
+ report_fixme: ReportTactic, ReportTactic::Never, false,
+ "Report all, none or unnumbered occurrences of FIXME in source file comments";
+
+ // Not user-facing.
+ verbose: bool, false, false, "Use verbose output";
+ 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::default(), false,
+ "'small' heuristic values";
}
#[cfg(test)]
fn test_config_used_to_toml() {
let config = Config::default();
- let verbose = config.verbose();
+ let merge_derives = config.merge_derives();
let skip_children = config.skip_children();
let used_options = config.used_options();
let toml = used_options.to_toml().unwrap();
assert_eq!(
toml,
- format!("verbose = {}\nskip_children = {}\n", verbose, skip_children)
+ format!(
+ "merge_derives = {}\nskip_children = {}\n",
+ merge_derives, skip_children,
+ )
);
}
assert_eq!(config.was_set().verbose(), false);
}
- #[test]
- fn test_as_not_nightly_channel() {
- let mut config = Config::default();
- assert_eq!(config.was_set().unstable_features(), false);
- config.set().unstable_features(true);
- assert_eq!(config.was_set().unstable_features(), false);
- }
-
- #[test]
- fn test_as_nightly_channel() {
- let v = ::std::env::var("CFG_RELEASE_CHANNEL").unwrap_or(String::from(""));
- ::std::env::set_var("CFG_RELEASE_CHANNEL", "nightly");
- let mut config = Config::default();
- config.set().unstable_features(true);
- assert_eq!(config.was_set().unstable_features(), false);
- config.set().unstable_features(true);
- assert_eq!(config.unstable_features(), true);
- ::std::env::set_var("CFG_RELEASE_CHANNEL", v);
- }
-
- #[test]
- fn test_unstable_from_toml() {
- let mut config = Config::from_toml("unstable_features = true").unwrap();
- assert_eq!(config.was_set().unstable_features(), false);
- let v = ::std::env::var("CFG_RELEASE_CHANNEL").unwrap_or(String::from(""));
- ::std::env::set_var("CFG_RELEASE_CHANNEL", "nightly");
- config = Config::from_toml("unstable_features = true").unwrap();
- assert_eq!(config.was_set().unstable_features(), true);
- assert_eq!(config.unstable_features(), true);
- ::std::env::set_var("CFG_RELEASE_CHANNEL", v);
- }
-
+ // FIXME(#2183) these tests cannot be run in parallel because they use env vars
+ // #[test]
+ // fn test_as_not_nightly_channel() {
+ // let mut config = Config::default();
+ // assert_eq!(config.was_set().unstable_features(), false);
+ // config.set().unstable_features(true);
+ // assert_eq!(config.was_set().unstable_features(), false);
+ // }
+
+ // #[test]
+ // fn test_as_nightly_channel() {
+ // let v = ::std::env::var("CFG_RELEASE_CHANNEL").unwrap_or(String::from(""));
+ // ::std::env::set_var("CFG_RELEASE_CHANNEL", "nightly");
+ // let mut config = Config::default();
+ // config.set().unstable_features(true);
+ // assert_eq!(config.was_set().unstable_features(), false);
+ // config.set().unstable_features(true);
+ // assert_eq!(config.unstable_features(), true);
+ // ::std::env::set_var("CFG_RELEASE_CHANNEL", v);
+ // }
+
+ // #[test]
+ // fn test_unstable_from_toml() {
+ // let mut config = Config::from_toml("unstable_features = true").unwrap();
+ // assert_eq!(config.was_set().unstable_features(), false);
+ // let v = ::std::env::var("CFG_RELEASE_CHANNEL").unwrap_or(String::from(""));
+ // ::std::env::set_var("CFG_RELEASE_CHANNEL", "nightly");
+ // config = Config::from_toml("unstable_features = true").unwrap();
+ // assert_eq!(config.was_set().unstable_features(), true);
+ // assert_eq!(config.unstable_features(), true);
+ // ::std::env::set_var("CFG_RELEASE_CHANNEL", v);
+ // }
}