diff --git a/src/config.rs b/src/config.rs index 3d41b51661e..357f99a6097 100644 --- a/src/config.rs +++ b/src/config.rs @@ -26,37 +26,56 @@ pub enum BlockIndentStyle { impl_enum_decodable!(BlockIndentStyle, Inherit, Tabbed, Visual); -#[derive(RustcDecodable, Clone)] -pub struct Config { - pub max_width: usize, - pub ideal_width: usize, - pub leeway: usize, - pub tab_spaces: usize, - pub newline_style: NewlineStyle, - pub fn_brace_style: BraceStyle, - pub fn_return_indent: ReturnIndent, - pub fn_args_paren_newline: bool, - pub struct_trailing_comma: SeparatorTactic, - pub struct_lit_trailing_comma: SeparatorTactic, - pub struct_lit_style: StructLitStyle, - pub enum_trailing_comma: bool, - pub report_todo: ReportTactic, - pub report_fixme: ReportTactic, - pub reorder_imports: bool, // Alphabetically, case sensitive. - pub expr_indent_style: BlockIndentStyle, -} +macro_rules! create_config { + ($($i:ident: $ty:ty),+ $(,)*) => ( + #[derive(RustcDecodable, Clone)] + pub struct Config { + $(pub $i: $ty),+ + } -impl Config { - pub fn from_toml(toml: &str) -> Config { - let parsed = toml.parse().unwrap(); - match toml::decode(parsed) { - Some(decoded) => decoded, - None => { - println!("Decoding config file failed. Config:\n{}", toml); - let parsed: toml::Value = toml.parse().unwrap(); - println!("\n\nParsed:\n{:?}", parsed); - panic!(); + impl Config { + pub fn from_toml(toml: &str) -> Config { + let parsed = toml.parse().unwrap(); + match toml::decode(parsed) { + Some(decoded) => decoded, + None => { + println!("Decoding config file failed. Config:\n{}", toml); + let parsed: toml::Value = toml.parse().unwrap(); + println!("\n\nParsed:\n{:?}", parsed); + panic!(); + } + } + } + + pub fn override_value(&mut self, key: &str, val: &str) { + match key { + $( + stringify!($i) => { + self.$i = val.parse::<$ty>().unwrap(); + } + )+ + _ => panic!("Bad config key!") + } } } - } + ) +} + +create_config! { + max_width: usize, + ideal_width: usize, + leeway: usize, + tab_spaces: usize, + newline_style: NewlineStyle, + fn_brace_style: BraceStyle, + fn_return_indent: ReturnIndent, + fn_args_paren_newline: bool, + struct_trailing_comma: SeparatorTactic, + struct_lit_trailing_comma: SeparatorTactic, + struct_lit_style: StructLitStyle, + enum_trailing_comma: bool, + report_todo: ReportTactic, + report_fixme: ReportTactic, + reorder_imports: bool, // Alphabetically, case sensitive. + expr_indent_style: BlockIndentStyle, } diff --git a/src/expr.rs b/src/expr.rs index 1dfff001475..2fbdcb7d6c9 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -617,10 +617,8 @@ fn rewrite_binary_op(context: &RewriteContext, let operator_str = context.codemap.span_to_snippet(op.span).unwrap(); // 1 = space between lhs expr and operator - let mut result = - try_opt!(lhs.rewrite(context, - context.config.max_width - offset - 1 - operator_str.len(), - offset)); + let max_width = try_opt!(context.config.max_width.checked_sub(operator_str.len() + offset + 1)); + let mut result = try_opt!(lhs.rewrite(context, max_width, offset)); result.push(' '); result.push_str(&operator_str); diff --git a/src/lib.rs b/src/lib.rs index cc7111aae27..cd43be56a99 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -80,7 +80,7 @@ pub enum WriteMode { NewFile(&'static str), // Write the output to stdout. Display, - // Return the result as a mapping from filenames to StringBuffers. + // Return the result as a mapping from filenames to Strings. Return(&'static Fn(HashMap)), } diff --git a/src/utils.rs b/src/utils.rs index 59f85b3ad27..a57ffb008ea 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -144,6 +144,19 @@ macro_rules! impl_enum_decodable { } } } + + impl ::std::str::FromStr for $e { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + match &*s { + $( + stringify!($x) => Ok($e::$x), + )* + _ => Err("Bad variant"), + } + } + } }; } diff --git a/tests/config/expr_visual_indent.toml b/tests/config/expr_visual_indent.toml deleted file mode 100644 index 38a099e3bed..00000000000 --- a/tests/config/expr_visual_indent.toml +++ /dev/null @@ -1,16 +0,0 @@ -max_width = 100 -ideal_width = 80 -leeway = 5 -tab_spaces = 4 -newline_style = "Unix" -fn_brace_style = "SameLineWhere" -fn_return_indent = "WithArgs" -fn_args_paren_newline = true -struct_trailing_comma = "Vertical" -struct_lit_style = "BlockIndent" -struct_lit_trailing_comma = "Vertical" -enum_trailing_comma = true -report_todo = "Always" -report_fixme = "Never" -reorder_imports = false -expr_indent_style = "Visual" diff --git a/tests/config/reorder_imports.toml b/tests/config/reorder_imports.toml deleted file mode 100644 index 5b1ce49a2f2..00000000000 --- a/tests/config/reorder_imports.toml +++ /dev/null @@ -1,16 +0,0 @@ -max_width = 100 -ideal_width = 80 -leeway = 5 -tab_spaces = 4 -newline_style = "Unix" -fn_brace_style = "SameLineWhere" -fn_return_indent = "WithArgs" -fn_args_paren_newline = true -struct_trailing_comma = "Vertical" -struct_lit_trailing_comma = "Vertical" -struct_lit_style = "BlockIndent" -enum_trailing_comma = true -report_todo = "Always" -report_fixme = "Never" -reorder_imports = true -expr_indent_style = "Tabbed" diff --git a/tests/config/visual_struct_lits.toml b/tests/config/visual_struct_lits.toml deleted file mode 100644 index 61bf4b0aee5..00000000000 --- a/tests/config/visual_struct_lits.toml +++ /dev/null @@ -1,16 +0,0 @@ -max_width = 100 -ideal_width = 80 -leeway = 5 -tab_spaces = 4 -newline_style = "Unix" -fn_brace_style = "SameLineWhere" -fn_return_indent = "WithArgs" -fn_args_paren_newline = true -struct_trailing_comma = "Vertical" -struct_lit_style = "VisualIndent" -struct_lit_trailing_comma = "Vertical" -enum_trailing_comma = true -report_todo = "Always" -report_fixme = "Never" -reorder_imports = false -expr_indent_style = "Tabbed" diff --git a/tests/source/expr-visual-indent.rs b/tests/source/expr-visual-indent.rs index c173d7bd7c4..3d7c1b92be8 100644 --- a/tests/source/expr-visual-indent.rs +++ b/tests/source/expr-visual-indent.rs @@ -1,4 +1,4 @@ -// rustfmt-config: expr_visual_indent.toml +// rustfmt-expr_indent_style: Visual // Visual level block indentation. diff --git a/tests/source/imports-reorder.rs b/tests/source/imports-reorder.rs index 2a90c212148..4feb8e90561 100644 --- a/tests/source/imports-reorder.rs +++ b/tests/source/imports-reorder.rs @@ -1,4 +1,4 @@ -// rustfmt-config: reorder_imports.toml +// rustfmt-reorder_imports: true use path::{C,/*A*/ A, B /* B */, self /* self */}; diff --git a/tests/source/struct_lits_visual.rs b/tests/source/struct_lits_visual.rs index f14a56397ec..ea76a088a32 100644 --- a/tests/source/struct_lits_visual.rs +++ b/tests/source/struct_lits_visual.rs @@ -1,4 +1,4 @@ -// rustfmt-config: visual_struct_lits.toml +// rustfmt-struct_lit_style: VisualIndent // Struct literal expressions. diff --git a/tests/system.rs b/tests/system.rs index 19a68fc4b7b..3f63d224576 100644 --- a/tests/system.rs +++ b/tests/system.rs @@ -97,8 +97,16 @@ fn print_mismatches(result: HashMap) { static HANDLE_RESULT: &'static Fn(HashMap) = &handle_result; pub fn idempotent_check(filename: String) -> Result<(), HashMap> { - let config = get_config(&filename); + let sig_comments = read_significant_comments(&filename); + let mut config = get_config(sig_comments.get("config").map(|x| &(*x)[..])); let args = vec!["rustfmt".to_owned(), filename]; + + for (key, val) in sig_comments { + if key != "target" && key != "config" { + config.override_value(&key, &val); + } + } + // this thread is not used for concurrency, but rather to workaround the issue that the passed // function handle needs to have static lifetime. Instead of using a global RefCell, we use // panic to return a result in case of failure. This has the advantage of smoothing the road to @@ -110,15 +118,15 @@ pub fn idempotent_check(filename: String) -> Result<(), HashMap> ) } -// Reads test config file from comments and loads it -fn get_config(file_name: &str) -> Box { - let config_file_name = read_significant_comment(file_name, "config") - .map(|file_name| { - let mut full_path = "tests/config/".to_owned(); - full_path.push_str(&file_name); - full_path - }) - .unwrap_or("default.toml".to_owned()); + +// Reads test config file from comments and reads its contents. +fn get_config(config_file: Option<&str>) -> Box { + let config_file_name = config_file.map(|file_name| { + let mut full_path = "tests/config/".to_owned(); + full_path.push_str(&file_name); + full_path + }) + .unwrap_or("default.toml".to_owned()); let mut def_config_file = fs::File::open(config_file_name).ok().expect("Couldn't open config."); let mut def_config = String::new(); @@ -127,14 +135,16 @@ fn get_config(file_name: &str) -> Box { Box::new(Config::from_toml(&def_config)) } -fn read_significant_comment(file_name: &str, option: &str) -> Option { - let file = fs::File::open(file_name).ok().expect("Couldn't read file for comment."); +// Reads significant comments of the form: // rustfmt-key: value +// into a hash map. +fn read_significant_comments(file_name: &str) -> HashMap { + let file = fs::File::open(file_name).ok().expect(&format!("Couldn't read file {}.", file_name)); let reader = BufReader::new(file); - let pattern = format!("^\\s*//\\s*rustfmt-{}:\\s*(\\S+)", option); + let pattern = r"^\s*//\s*rustfmt-([^:]+):\s*(\S+)"; let regex = regex::Regex::new(&pattern).ok().expect("Failed creating pattern 1."); - // matches exactly the lines containing significant comments or whitespace - let line_regex = regex::Regex::new(r"(^\s*$)|(^\s*//\s*rustfmt-[:alpha:]+:\s*\S+)") + // Matches lines containing significant comments or whitespace. + let line_regex = regex::Regex::new(r"(^\s*$)|(^\s*//\s*rustfmt-[^:]+:\s*\S+)") .ok().expect("Failed creating pattern 2."); reader.lines() @@ -142,20 +152,26 @@ fn read_significant_comment(file_name: &str, option: &str) -> Option { .take_while(|line| line_regex.is_match(&line)) .filter_map(|line| { regex.captures_iter(&line).next().map(|capture| { - capture.at(1).expect("Couldn't unwrap capture.").to_owned() + (capture.at(1).expect("Couldn't unwrap capture.").to_owned(), + capture.at(2).expect("Couldn't unwrap capture.").to_owned()) }) }) - .next() + .collect() } // Compare output to input. +// TODO: needs a better name, more explanation. fn handle_result(result: HashMap) { let mut failures = HashMap::new(); for (file_name, fmt_text) in result { - // If file is in tests/source, compare to file with same name in tests/target - let target_file_name = get_target(&file_name); - let mut f = fs::File::open(&target_file_name).ok().expect("Couldn't open target."); + // FIXME: reading significant comments again. Is there a way we can just + // pass the target to this function? + let sig_comments = read_significant_comments(&file_name); + + // If file is in tests/source, compare to file with same name in tests/target. + let target = get_target(&file_name, sig_comments.get("target").map(|x| &(*x)[..])); + let mut f = fs::File::open(&target).ok().expect("Couldn't open target."); let mut text = String::new(); // TODO: speedup by running through bytes iterator @@ -171,15 +187,11 @@ fn handle_result(result: HashMap) { } // Map source file paths to their target paths. -fn get_target(file_name: &str) -> String { +fn get_target(file_name: &str, target: Option<&str>) -> String { if file_name.starts_with("tests/source/") { - let target = read_significant_comment(file_name, "target"); - let base = target.unwrap_or(file_name.trim_left_matches("tests/source/").to_owned()); + let base = target.unwrap_or(file_name.trim_left_matches("tests/source/")); - let mut target_file = "tests/target/".to_owned(); - target_file.push_str(&base); - - target_file + format!("tests/target/{}", base) } else { file_name.to_owned() } diff --git a/tests/target/expr-visual-indent.rs b/tests/target/expr-visual-indent.rs index 3488e8c93c0..d74b5aae93c 100644 --- a/tests/target/expr-visual-indent.rs +++ b/tests/target/expr-visual-indent.rs @@ -1,4 +1,4 @@ -// rustfmt-config: expr_visual_indent.toml +// rustfmt-expr_indent_style: Visual // Visual level block indentation. diff --git a/tests/target/imports-reorder.rs b/tests/target/imports-reorder.rs index 27d394238e8..63ebbf1ec7b 100644 --- a/tests/target/imports-reorder.rs +++ b/tests/target/imports-reorder.rs @@ -1,4 +1,4 @@ -// rustfmt-config: reorder_imports.toml +// rustfmt-reorder_imports: true use path::{self /* self */, /* A */ A, B /* B */, C}; diff --git a/tests/target/struct_lits_visual.rs b/tests/target/struct_lits_visual.rs index 248839cb869..bf73db5a815 100644 --- a/tests/target/struct_lits_visual.rs +++ b/tests/target/struct_lits_visual.rs @@ -1,4 +1,4 @@ -// rustfmt-config: visual_struct_lits.toml +// rustfmt-struct_lit_style: VisualIndent // Struct literal expressions.