@@ -1,7 +1,6 @@
11use crate::{Rope, RopeGraphemes, RopeSlice};
22
33/// Represents one of the valid Unicode line endings.
4−/// VT, FF and PS are excluded here, as we don't expect them to show up as a default line break
54#[derive(PartialEq, Copy, Clone, Debug)]
65pub enum LineEnding {
76 Crlf, // CarriageReturn followed by LineFeed
@@ -9,6 +8,9 @@ pub enum LineEnding {
98 CR, // U+000D -- CarriageReturn
109 Nel, // U+0085 -- NextLine
1110 LS, // U+2028 -- Line Separator
11+ VT, // U+000B -- VerticalTab
12+ FF, // U+000C -- FormFeed
13+ PS, // U+2029 -- ParagraphSeparator
1214}
1315
1416pub fn rope_slice_to_line_ending(g: &RopeSlice) -> Option<LineEnding> {
@@ -38,8 +40,8 @@ pub fn auto_detect_line_ending(doc: &Rope) -> Option<LineEnding> {
3840 // based on https://github.com/cessen/led/blob/27572c8838a1c664ee378a19358604063881cc1d/src/editor/mod.rs#L88-L162
3941
4042 let mut ending = None;
41− for line in doc.lines().take(1) {
42− // check first line only - unsure how sound this is
43+ // return first matched line ending. Not all possible line endings are being matched, as they might be special-use only
44+ for line in doc.lines().take(100) {
4345 ending = match line.len_chars() {
4446 1 => {
4547 let g = RopeGraphemes::new(line.slice((line.len_chars() - 1)..))
@@ -54,6 +56,9 @@ pub fn auto_detect_line_ending(doc: &Rope) -> Option<LineEnding> {
5456 rope_slice_to_line_ending(&g)
5557 }
5658 _ => None,
59+ };
60+ if ending.is_some() {
61+ return ending;
5762 }
5863 }
5964 ending
@@ -62,4 +67,55 @@ pub fn auto_detect_line_ending(doc: &Rope) -> Option<LineEnding> {
6267#[cfg(target_os = "windows")]
6368pub const DEFAULT_LINE_ENDING: LineEnding = LineEnding::Crlf;
6469#[cfg(not(target_os = "windows"))]
65−pub const DEFAULT_LINE_ENDING: LineEnding = LineEnding::LF;
70+pub const DEFAULT_LINE_ENDING: LineEnding = LineEnding::LF;
71+
72+#[cfg(test)]
73+mod line_ending_tests {
74+ use super::*;
75+
76+ #[test]
77+ fn test_autodetect() {
78+ assert_eq!(
79+ auto_detect_line_ending(&Rope::from_str("\n")),
80+ Some(LineEnding::LF)
81+ );
82+ assert_eq!(
83+ auto_detect_line_ending(&Rope::from_str("\r\n")),
84+ Some(LineEnding::Crlf)
85+ );
86+ assert_eq!(auto_detect_line_ending(&Rope::from_str("hello")), None);
87+ assert_eq!(auto_detect_line_ending(&Rope::from_str("")), None);
88+ assert_eq!(
89+ auto_detect_line_ending(&Rope::from_str("hello\nhelix\r\n")),
90+ Some(LineEnding::LF)
91+ );
92+ assert_eq!(
93+ auto_detect_line_ending(&Rope::from_str("a formfeed\u{000C}")),
94+ None
95+ );
96+ assert_eq!(
97+ auto_detect_line_ending(&Rope::from_str("\n\u{000A}\n \u{000A}")),
98+ Some(LineEnding::LF)
99+ );
100+ assert_eq!(
101+ auto_detect_line_ending(&Rope::from_str(
102+ "a formfeed\u{000C} with a\u{000C} linefeed\u{000A}"
103+ )),
104+ Some(LineEnding::LF)
105+ );
106+ assert_eq!(auto_detect_line_ending(&Rope::from_str("a formfeed\u{000C} with a\u{000C} carriage return linefeed\u{000D}\u{000A} and a linefeed\u{000A}")), Some(LineEnding::Crlf));
107+ }
108+
109+ #[test]
110+ fn test_rope_slice_to_line_ending() {
111+ let r = Rope::from_str("\r\n");
112+ assert_eq!(
113+ rope_slice_to_line_ending(&r.slice(1..2)),
114+ Some(LineEnding::LF)
115+ );
116+ assert_eq!(
117+ rope_slice_to_line_ending(&r.slice(0..2)),
118+ Some(LineEnding::Crlf)
119+ );
120+ }
121+}