From 6ff2a5e0f3416a140f4e2df6fad0a135da7addc1 Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Sat, 28 Jul 2018 16:18:58 -0400 Subject: [PATCH 1/3] Auto-detect newline style by default --- Configurations.md | 4 +- src/config/mod.rs | 2 +- src/config/options.rs | 113 ++++++++++++++++++++++++++++++++++++++++++ src/formatting.rs | 34 ++----------- 4 files changed, 120 insertions(+), 33 deletions(-) diff --git a/Configurations.md b/Configurations.md index 403154ea6d8..4c621e7b5d2 100644 --- a/Configurations.md +++ b/Configurations.md @@ -1338,8 +1338,8 @@ fn main() { Unix or Windows line endings -- **Default value**: `"Native"` -- **Possible values**: `"Native"`, `"Unix"`, `"Windows"` +- **Default value**: `"Auto"` +- **Possible values**: `"Auto"`, `"Native"`, `"Unix"`, `"Windows"` - **Stable**: Yes ## `normalize_comments` diff --git a/src/config/mod.rs b/src/config/mod.rs index 42686483419..f240c7b13c6 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -39,7 +39,7 @@ create_config! { 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::Native, true, "Unix or Windows line endings"; + 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"; diff --git a/src/config/options.rs b/src/config/options.rs index c48f9a4d727..f17e95e57e6 100644 --- a/src/config/options.rs +++ b/src/config/options.rs @@ -105,11 +105,68 @@ macro_rules! configuration_option_enum{ } configuration_option_enum! { NewlineStyle: + Auto, // Auto-detect based on the raw source input Windows, // \r\n Unix, // \n Native, // \r\n in Windows, \n on other platforms } +impl NewlineStyle { + fn auto_detect(raw_input_text: &str) -> NewlineStyle { + if let Some(pos) = raw_input_text.find('\n') { + let pos = pos.saturating_sub(1); + if let Some('\r') = raw_input_text.chars().nth(pos) { + NewlineStyle::Windows + } else { + NewlineStyle::Unix + } + } else { + NewlineStyle::Native + } + } + + fn native() -> NewlineStyle { + if cfg!(windows) { + NewlineStyle::Windows + } else { + NewlineStyle::Unix + } + } + + /// Apply this newline style to the formatted text. When the style is set + /// to `Auto`, the `raw_input_text` is used to detect the existing line + /// endings. + /// + /// If the style is set to `Auto` and `raw_input_text` contains no + /// newlines, the `Native` style will be used. + pub(crate) fn apply(self, formatted_text: &mut String, raw_input_text: &str) { + use NewlineStyle::*; + let mut style = self; + if style == Auto { + style = Self::auto_detect(raw_input_text); + } + if style == Native { + style = Self::native(); + } + match style { + Windows => { + let mut transformed = String::with_capacity(formatted_text.capacity()); + for c in formatted_text.chars() { + match c { + '\n' => transformed.push_str("\r\n"), + '\r' => continue, + c => transformed.push(c), + } + } + *formatted_text = transformed; + } + Unix => return, + Native => unreachable!("NewlineStyle::Native"), + Auto => unreachable!("NewlineStyle::Auto"), + } + } +} + configuration_option_enum! { BraceStyle: AlwaysNextLine, PreferSameLine, @@ -367,3 +424,59 @@ impl Edition { } } } + +#[test] +fn test_newline_style_auto_detect() { + let lf = "One\nTwo\nThree"; + let crlf = "One\r\nTwo\r\nThree"; + let none = "One Two Three"; + + assert_eq!(NewlineStyle::Unix, NewlineStyle::auto_detect(lf)); + assert_eq!(NewlineStyle::Windows, NewlineStyle::auto_detect(crlf)); + assert_eq!(NewlineStyle::Native, NewlineStyle::auto_detect(none)); +} + +#[test] +fn test_newline_style_auto_apply() { + let auto = NewlineStyle::Auto; + + let formatted_text = "One\nTwo\nThree"; + let raw_input_text = "One\nTwo\nThree"; + + let mut out = String::from(formatted_text); + auto.apply(&mut out, raw_input_text); + assert_eq!("One\nTwo\nThree", &out, "auto should detect 'lf'"); + + let formatted_text = "One\nTwo\nThree"; + let raw_input_text = "One\r\nTwo\r\nThree"; + + let mut out = String::from(formatted_text); + auto.apply(&mut out, raw_input_text); + assert_eq!("One\r\nTwo\r\nThree", &out, "auto should detect 'crlf'"); + + #[cfg(not(windows))] + { + let formatted_text = "One\nTwo\nThree"; + let raw_input_text = "One Two Three"; + + let mut out = String::from(formatted_text); + auto.apply(&mut out, raw_input_text); + assert_eq!( + "One\nTwo\nThree", &out, + "auto-native-unix should detect 'lf'" + ); + } + + #[cfg(windows)] + { + let formatted_text = "One\nTwo\nThree"; + let raw_input_text = "One Two Three"; + + let mut out = String::from(formatted_text); + auto.apply(&mut out, raw_input_text); + assert_eq!( + "One\r\nTwo\r\nThree", &out, + "auto-native-windows should detect 'crlf'" + ); + } +} diff --git a/src/formatting.rs b/src/formatting.rs index ceada0d4040..d29c9398174 100644 --- a/src/formatting.rs +++ b/src/formatting.rs @@ -13,7 +13,7 @@ use syntax::errors::Handler; use syntax::parse::{self, ParseSess}; use comment::{CharClasses, FullCodeCharKind}; -use config::{Config, FileName, NewlineStyle, Verbosity}; +use config::{Config, FileName, Verbosity}; use issues::BadIssueSeeker; use visitor::{FmtVisitor, SnippetProvider}; use {filemap, modules, ErrorKind, FormatReport, Input, Session}; @@ -166,7 +166,9 @@ impl<'a, T: FormatHandler + 'a> FormatContext<'a, T> { &self.config, &self.report, ); - replace_with_system_newlines(&mut visitor.buffer, &self.config); + self.config + .newline_style() + .apply(&mut visitor.buffer, &big_snippet); if visitor.macro_rewrite_failure { self.report.add_macro_format_failure(); @@ -645,34 +647,6 @@ fn make_parse_sess(codemap: Rc, config: &Config) -> ParseSess { ParseSess::with_span_handler(tty_handler, codemap) } -fn replace_with_system_newlines(text: &mut String, config: &Config) -> () { - let style = if config.newline_style() == NewlineStyle::Native { - if cfg!(windows) { - NewlineStyle::Windows - } else { - NewlineStyle::Unix - } - } else { - config.newline_style() - }; - - match style { - NewlineStyle::Unix => return, - NewlineStyle::Windows => { - let mut transformed = String::with_capacity(text.capacity()); - for c in text.chars() { - match c { - '\n' => transformed.push_str("\r\n"), - '\r' => continue, - c => transformed.push(c), - } - } - *text = transformed; - } - NewlineStyle::Native => unreachable!(), - } -} - fn should_emit_verbose(is_stdin: bool, config: &Config, f: F) where F: Fn(), From e7932fa9c2591c45a37a24305de90cb63128afcf Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Sun, 29 Jul 2018 17:02:32 -0400 Subject: [PATCH 2/3] Updating newline_style documentation --- Configurations.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Configurations.md b/Configurations.md index 4c621e7b5d2..ab91f11be04 100644 --- a/Configurations.md +++ b/Configurations.md @@ -1342,6 +1342,25 @@ Unix or Windows line endings - **Possible values**: `"Auto"`, `"Native"`, `"Unix"`, `"Windows"` - **Stable**: Yes +#### `Auto` (default): + +The newline style is detected automatically on a per-file basis. Files +with mixed line endings will be converted to the first detected line +ending style. + +#### `Native` + +Line endings will be converted to `\r\n` on Windows and `\n` on all +other platforms. + +#### `Unix` + +Line endings will be converted to `\n`. + +#### `Windows` + +Line endings will be converted to `\r\n`. + ## `normalize_comments` Convert /* */ comments to // comments where possible From dab572e0b0c3ee7373d51a76af5bdf0c7e81ce68 Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Mon, 30 Jul 2018 23:19:46 -0400 Subject: [PATCH 3/3] Increase capacity for newline conversion buffer --- src/config/options.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/options.rs b/src/config/options.rs index f17e95e57e6..d2a74f54677 100644 --- a/src/config/options.rs +++ b/src/config/options.rs @@ -150,7 +150,7 @@ impl NewlineStyle { } match style { Windows => { - let mut transformed = String::with_capacity(formatted_text.capacity()); + let mut transformed = String::with_capacity(2 * formatted_text.capacity()); for c in formatted_text.chars() { match c { '\n' => transformed.push_str("\r\n"),