Merge pull request #2873 from aloucks/auto-detect-newline-style

Auto-detect newline style by default
This commit is contained in:
Nick Cameron 2018-08-01 10:06:31 +12:00 committed by GitHub
commit df2e8bb594
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 139 additions and 33 deletions

View File

@ -1338,10 +1338,29 @@ 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
#### `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

View File

@ -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";

View File

@ -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(2 * 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'"
);
}
}

View File

@ -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<CodeMap>, 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<F>(is_stdin: bool, config: &Config, f: F)
where
F: Fn(),