]> git.proxmox.com Git - rustc.git/blob - src/tools/rustfmt/src/formatting/newline_style.rs
ac62009490001ab8d5101cf6d2ddfd419e4c3f0b
[rustc.git] / src / tools / rustfmt / 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 *formatted_text = match effective_newline_style(newline_style, raw_input_text) {
15 EffectiveNewlineStyle::Windows => convert_to_windows_newlines(formatted_text),
16 EffectiveNewlineStyle::Unix => convert_to_unix_newlines(formatted_text),
17 }
18 }
19
20 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
21 enum EffectiveNewlineStyle {
22 Windows,
23 Unix,
24 }
25
26 fn effective_newline_style(
27 newline_style: NewlineStyle,
28 raw_input_text: &str,
29 ) -> EffectiveNewlineStyle {
30 match newline_style {
31 NewlineStyle::Auto => auto_detect_newline_style(raw_input_text),
32 NewlineStyle::Native => native_newline_style(),
33 NewlineStyle::Windows => EffectiveNewlineStyle::Windows,
34 NewlineStyle::Unix => EffectiveNewlineStyle::Unix,
35 }
36 }
37
38 const LINE_FEED: char = '\n';
39 const CARRIAGE_RETURN: char = '\r';
40 const WINDOWS_NEWLINE: &str = "\r\n";
41 const UNIX_NEWLINE: &str = "\n";
42
43 fn auto_detect_newline_style(raw_input_text: &str) -> EffectiveNewlineStyle {
44 let first_line_feed_pos = raw_input_text.chars().position(|ch| ch == LINE_FEED);
45 match first_line_feed_pos {
46 Some(first_line_feed_pos) => {
47 let char_before_line_feed_pos = first_line_feed_pos.saturating_sub(1);
48 let char_before_line_feed = raw_input_text.chars().nth(char_before_line_feed_pos);
49 match char_before_line_feed {
50 Some(CARRIAGE_RETURN) => EffectiveNewlineStyle::Windows,
51 _ => EffectiveNewlineStyle::Unix,
52 }
53 }
54 None => native_newline_style(),
55 }
56 }
57
58 fn native_newline_style() -> EffectiveNewlineStyle {
59 if cfg!(windows) {
60 EffectiveNewlineStyle::Windows
61 } else {
62 EffectiveNewlineStyle::Unix
63 }
64 }
65
66 fn convert_to_windows_newlines(formatted_text: &String) -> String {
67 let mut transformed = String::with_capacity(2 * formatted_text.capacity());
68 let mut chars = formatted_text.chars().peekable();
69 while let Some(current_char) = chars.next() {
70 let next_char = chars.peek();
71 match current_char {
72 LINE_FEED => transformed.push_str(WINDOWS_NEWLINE),
73 CARRIAGE_RETURN if next_char == Some(&LINE_FEED) => {}
74 current_char => transformed.push(current_char),
75 }
76 }
77 transformed
78 }
79
80 fn convert_to_unix_newlines(formatted_text: &String) -> String {
81 formatted_text.replace(WINDOWS_NEWLINE, UNIX_NEWLINE)
82 }
83
84 #[cfg(test)]
85 mod tests {
86 use super::*;
87
88 #[test]
89 fn auto_detects_unix_newlines() {
90 assert_eq!(
91 EffectiveNewlineStyle::Unix,
92 auto_detect_newline_style("One\nTwo\nThree")
93 );
94 }
95
96 #[test]
97 fn auto_detects_windows_newlines() {
98 assert_eq!(
99 EffectiveNewlineStyle::Windows,
100 auto_detect_newline_style("One\r\nTwo\r\nThree")
101 );
102 }
103
104 #[test]
105 fn auto_detects_windows_newlines_with_multibyte_char_on_first_line() {
106 assert_eq!(
107 EffectiveNewlineStyle::Windows,
108 auto_detect_newline_style("A 🎢 of a first line\r\nTwo\r\nThree")
109 );
110 }
111
112 #[test]
113 fn falls_back_to_native_newlines_if_no_newlines_are_found() {
114 let expected_newline_style = if cfg!(windows) {
115 EffectiveNewlineStyle::Windows
116 } else {
117 EffectiveNewlineStyle::Unix
118 };
119 assert_eq!(
120 expected_newline_style,
121 auto_detect_newline_style("One Two Three")
122 );
123 }
124
125 #[test]
126 fn auto_detects_and_applies_unix_newlines() {
127 let formatted_text = "One\nTwo\nThree";
128 let raw_input_text = "One\nTwo\nThree";
129
130 let mut out = String::from(formatted_text);
131 apply_newline_style(NewlineStyle::Auto, &mut out, raw_input_text);
132 assert_eq!("One\nTwo\nThree", &out, "auto should detect 'lf'");
133 }
134
135 #[test]
136 fn auto_detects_and_applies_windows_newlines() {
137 let formatted_text = "One\nTwo\nThree";
138 let raw_input_text = "One\r\nTwo\r\nThree";
139
140 let mut out = String::from(formatted_text);
141 apply_newline_style(NewlineStyle::Auto, &mut out, raw_input_text);
142 assert_eq!("One\r\nTwo\r\nThree", &out, "auto should detect 'crlf'");
143 }
144
145 #[test]
146 fn auto_detects_and_applies_native_newlines() {
147 let formatted_text = "One\nTwo\nThree";
148 let raw_input_text = "One Two Three";
149
150 let mut out = String::from(formatted_text);
151 apply_newline_style(NewlineStyle::Auto, &mut out, raw_input_text);
152
153 if cfg!(windows) {
154 assert_eq!(
155 "One\r\nTwo\r\nThree", &out,
156 "auto-native-windows should detect 'crlf'"
157 );
158 } else {
159 assert_eq!(
160 "One\nTwo\nThree", &out,
161 "auto-native-unix should detect 'lf'"
162 );
163 }
164 }
165
166 #[test]
167 fn applies_unix_newlines() {
168 test_newlines_are_applied_correctly(
169 "One\r\nTwo\nThree",
170 "One\nTwo\nThree",
171 NewlineStyle::Unix,
172 );
173 }
174
175 #[test]
176 fn applying_unix_newlines_changes_nothing_for_unix_newlines() {
177 let formatted_text = "One\nTwo\nThree";
178 test_newlines_are_applied_correctly(formatted_text, formatted_text, NewlineStyle::Unix);
179 }
180
181 #[test]
182 fn applies_unix_newlines_to_string_with_unix_and_windows_newlines() {
183 test_newlines_are_applied_correctly(
184 "One\r\nTwo\r\nThree\nFour",
185 "One\nTwo\nThree\nFour",
186 NewlineStyle::Unix,
187 );
188 }
189
190 #[test]
191 fn applies_windows_newlines_to_string_with_unix_and_windows_newlines() {
192 test_newlines_are_applied_correctly(
193 "One\nTwo\nThree\r\nFour",
194 "One\r\nTwo\r\nThree\r\nFour",
195 NewlineStyle::Windows,
196 );
197 }
198
199 #[test]
200 fn applying_windows_newlines_changes_nothing_for_windows_newlines() {
201 let formatted_text = "One\r\nTwo\r\nThree";
202 test_newlines_are_applied_correctly(formatted_text, formatted_text, NewlineStyle::Windows);
203 }
204
205 #[test]
206 fn keeps_carriage_returns_when_applying_windows_newlines_to_str_with_unix_newlines() {
207 test_newlines_are_applied_correctly(
208 "One\nTwo\nThree\rDrei",
209 "One\r\nTwo\r\nThree\rDrei",
210 NewlineStyle::Windows,
211 );
212 }
213
214 #[test]
215 fn keeps_carriage_returns_when_applying_unix_newlines_to_str_with_unix_newlines() {
216 test_newlines_are_applied_correctly(
217 "One\nTwo\nThree\rDrei",
218 "One\nTwo\nThree\rDrei",
219 NewlineStyle::Unix,
220 );
221 }
222
223 #[test]
224 fn keeps_carriage_returns_when_applying_windows_newlines_to_str_with_windows_newlines() {
225 test_newlines_are_applied_correctly(
226 "One\r\nTwo\r\nThree\rDrei",
227 "One\r\nTwo\r\nThree\rDrei",
228 NewlineStyle::Windows,
229 );
230 }
231
232 #[test]
233 fn keeps_carriage_returns_when_applying_unix_newlines_to_str_with_windows_newlines() {
234 test_newlines_are_applied_correctly(
235 "One\r\nTwo\r\nThree\rDrei",
236 "One\nTwo\nThree\rDrei",
237 NewlineStyle::Unix,
238 );
239 }
240
241 fn test_newlines_are_applied_correctly(
242 input: &str,
243 expected: &str,
244 newline_style: NewlineStyle,
245 ) {
246 let mut out = String::from(input);
247 apply_newline_style(newline_style, &mut out, input);
248 assert_eq!(expected, &out);
249 }
250 }