]> git.lizzy.rs Git - rust.git/blob - src/formatting/newline_style.rs
Preserve standalone carriage returns on windows also
[rust.git] / src / formatting / newline_style.rs
1 use crate::NewlineStyle;
2
3 /// Apply this newline style to the formatted text. When the style is set
4 /// to `Auto`, the `raw_input_text` is used to detect the existing line
5 /// endings.
6 ///
7 /// If the style is set to `Auto` and `raw_input_text` contains no
8 /// newlines, the `Native` style will be used.
9 pub(crate) fn apply_newline_style(
10     newline_style: NewlineStyle,
11     formatted_text: &mut String,
12     raw_input_text: &str,
13 ) {
14     match effective_newline_style(newline_style, raw_input_text) {
15         EffectiveNewlineStyle::Windows => {
16             *formatted_text = convert_to_windows_newlines(formatted_text);
17         }
18         EffectiveNewlineStyle::Unix => {}
19     }
20 }
21
22 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
23 enum EffectiveNewlineStyle {
24     Windows,
25     Unix,
26 }
27
28 fn effective_newline_style(
29     newline_style: NewlineStyle,
30     raw_input_text: &str,
31 ) -> EffectiveNewlineStyle {
32     match newline_style {
33         NewlineStyle::Auto => auto_detect_newline_style(raw_input_text),
34         NewlineStyle::Native => native_newline_style(),
35         NewlineStyle::Windows => EffectiveNewlineStyle::Windows,
36         NewlineStyle::Unix => EffectiveNewlineStyle::Unix,
37     }
38 }
39
40 const LINE_FEED: char = '\n';
41 const CARRIAGE_RETURN: char = '\r';
42
43 fn auto_detect_newline_style(raw_input_text: &str) -> EffectiveNewlineStyle {
44     if let Some(pos) = raw_input_text.chars().position(|ch| ch == LINE_FEED) {
45         let pos = pos.saturating_sub(1);
46         if let Some(CARRIAGE_RETURN) = raw_input_text.chars().nth(pos) {
47             EffectiveNewlineStyle::Windows
48         } else {
49             EffectiveNewlineStyle::Unix
50         }
51     } else {
52         native_newline_style()
53     }
54 }
55
56 fn native_newline_style() -> EffectiveNewlineStyle {
57     if cfg!(windows) {
58         EffectiveNewlineStyle::Windows
59     } else {
60         EffectiveNewlineStyle::Unix
61     }
62 }
63
64 fn convert_to_windows_newlines(formatted_text: &String) -> String {
65     let mut transformed = String::with_capacity(2 * formatted_text.capacity());
66     for c in formatted_text.chars() {
67         const WINDOWS_NEWLINE: &str = "\r\n";
68         match c {
69             LINE_FEED => transformed.push_str(WINDOWS_NEWLINE),
70             c => transformed.push(c),
71         }
72     }
73     transformed
74 }
75
76 #[cfg(test)]
77 mod tests {
78     use super::*;
79
80     #[test]
81     fn auto_detects_unix_newlines() {
82         assert_eq!(
83             EffectiveNewlineStyle::Unix,
84             auto_detect_newline_style("One\nTwo\nThree")
85         );
86     }
87
88     #[test]
89     fn auto_detects_windows_newlines() {
90         assert_eq!(
91             EffectiveNewlineStyle::Windows,
92             auto_detect_newline_style("One\r\nTwo\r\nThree")
93         );
94     }
95
96     #[test]
97     fn auto_detects_windows_newlines_with_multibyte_char_on_first_line() {
98         assert_eq!(
99             EffectiveNewlineStyle::Windows,
100             auto_detect_newline_style("A ðŸŽ¢ of a first line\r\nTwo\r\nThree")
101         );
102     }
103
104     #[test]
105     fn falls_back_to_native_newlines_if_no_newlines_are_found() {
106         let expected_newline_style = if cfg!(windows) {
107             EffectiveNewlineStyle::Windows
108         } else {
109             EffectiveNewlineStyle::Unix
110         };
111         assert_eq!(
112             expected_newline_style,
113             auto_detect_newline_style("One Two Three")
114         );
115     }
116
117     #[test]
118     fn auto_detects_and_applies_unix_newlines() {
119         let formatted_text = "One\nTwo\nThree";
120         let raw_input_text = "One\nTwo\nThree";
121
122         let mut out = String::from(formatted_text);
123         apply_newline_style(NewlineStyle::Auto, &mut out, raw_input_text);
124         assert_eq!("One\nTwo\nThree", &out, "auto should detect 'lf'");
125     }
126
127     #[test]
128     fn auto_detects_and_applies_windows_newlines() {
129         let formatted_text = "One\nTwo\nThree";
130         let raw_input_text = "One\r\nTwo\r\nThree";
131
132         let mut out = String::from(formatted_text);
133         apply_newline_style(NewlineStyle::Auto, &mut out, raw_input_text);
134         assert_eq!("One\r\nTwo\r\nThree", &out, "auto should detect 'crlf'");
135     }
136
137     #[test]
138     fn auto_detects_and_applies_native_newlines() {
139         let formatted_text = "One\nTwo\nThree";
140         let raw_input_text = "One Two Three";
141
142         let mut out = String::from(formatted_text);
143         apply_newline_style(NewlineStyle::Auto, &mut out, raw_input_text);
144
145         if cfg!(windows) {
146             assert_eq!(
147                 "One\r\nTwo\r\nThree", &out,
148                 "auto-native-windows should detect 'crlf'"
149             );
150         } else {
151             assert_eq!(
152                 "One\nTwo\nThree", &out,
153                 "auto-native-unix should detect 'lf'"
154             );
155         }
156     }
157
158     #[test]
159     fn preserves_standalone_carriage_returns_when_applying_windows_newlines() {
160         let formatted_text = "One\nTwo\nThree\rDrei";
161         let raw_input_text = "One\nTwo\nThree\rDrei";
162
163         let mut out = String::from(formatted_text);
164         apply_newline_style(NewlineStyle::Windows, &mut out, raw_input_text);
165
166         assert_eq!("One\r\nTwo\r\nThree\rDrei", &out);
167     }
168
169     #[test]
170     fn preserves_standalone_carriage_returns_when_applying_unix_newlines() {
171         let formatted_text = "One\nTwo\nThree\rDrei";
172         let raw_input_text = "One\nTwo\nThree\rDrei";
173
174         let mut out = String::from(formatted_text);
175         apply_newline_style(NewlineStyle::Unix, &mut out, raw_input_text);
176
177         assert_eq!("One\nTwo\nThree\rDrei", &out);
178     }
179 }