on master help sign in
Misc fixes and clean up of line ending detect code.
commit5d22e3c4e574eb24260966de7f20f582e6184e24 authorNathan Vegdahl <cessen@cessen.com> dateSun 20 Jun 2021, 00:40 (5 years ago) parent8634e04
5 files changed+50−64
helix-core/src/lib.rs+1−2 helix-core/src/line_ending.rs+43−56 helix-core/src/movement.rs+2−2 helix-term/src/ui/editor.rs+3−3 helix-view/src/document.rs+1−1
helix-core/src/lib.rs+1−2
@@ -113,7 +113,6 @@ pub use diagnostic::Diagnostic;
113113pub use state::State;
114114
115115pub use line_ending::{
116− auto_detect_line_ending, get_line_ending, rope_slice_to_line_ending, LineEnding,
117− DEFAULT_LINE_ENDING, line_end
116+ auto_detect_line_ending, get_line_ending, line_end, LineEnding, DEFAULT_LINE_ENDING,
118117};
119118pub use transaction::{Assoc, Change, ChangeSet, Operation, Transaction};
helix-core/src/line_ending.rs+43−56
@@ -5,11 +5,11 @@ use crate::{Rope, RopeGraphemes, RopeSlice};
55pub enum LineEnding {
66 Crlf, // CarriageReturn followed by LineFeed
77 LF, // U+000A -- LineFeed
8+ VT, // U+000B -- VerticalTab
9+ FF, // U+000C -- FormFeed
810 CR, // U+000D -- CarriageReturn
911 Nel, // U+0085 -- NextLine
1012 LS, // U+2028 -- Line Separator
11− VT, // U+000B -- VerticalTab
12− FF, // U+000C -- FormFeed
1313 PS, // U+2029 -- ParagraphSeparator
1414}
1515
@@ -21,74 +21,58 @@ impl LineEnding {
2121 }
2222 }
2323
24− pub fn as_str(&self) -> &str {
24+ pub fn as_str(&self) -> &'static str {
2525 match self {
2626 Self::Crlf => "\u{000D}\u{000A}",
2727 Self::LF => "\u{000A}",
28− Self::Nel => "\u{0085}",
29− Self::LS => "\u{2028}",
30− Self::CR => "\u{000D}",
3128 Self::VT => "\u{000B}",
3229 Self::FF => "\u{000C}",
30+ Self::CR => "\u{000D}",
31+ Self::Nel => "\u{0085}",
32+ Self::LS => "\u{2028}",
3333 Self::PS => "\u{2029}",
3434 }
3535 }
36−}
3736
38−pub fn rope_slice_to_line_ending(g: &RopeSlice) -> Option<LineEnding> {
39− if let Some(text) = g.as_str() {
40− str_to_line_ending(text)
41− }
42− else {
43− // Not a line ending
44− None
37+ pub fn from_str(g: &str) -> Option<LineEnding> {
38+ match g {
39+ "\u{000D}\u{000A}" => Some(LineEnding::Crlf),
40+ "\u{000A}" => Some(LineEnding::LF),
41+ "\u{000B}" => Some(LineEnding::VT),
42+ "\u{000C}" => Some(LineEnding::FF),
43+ "\u{000D}" => Some(LineEnding::CR),
44+ "\u{0085}" => Some(LineEnding::Nel),
45+ "\u{2028}" => Some(LineEnding::LS),
46+ "\u{2029}" => Some(LineEnding::PS),
47+ // Not a line ending
48+ _ => None,
49+ }
4550 }
46−}
4751
48−pub fn str_to_line_ending(g: &str) -> Option<LineEnding> {
49− match g {
50− "\u{000D}\u{000A}" => Some(LineEnding::Crlf),
51− "\u{000A}" => Some(LineEnding::LF),
52− "\u{000D}" => Some(LineEnding::CR),
53− "\u{0085}" => Some(LineEnding::Nel),
54− "\u{2028}" => Some(LineEnding::LS),
55− "\u{000B}" => Some(LineEnding::VT),
56− "\u{000C}" => Some(LineEnding::FF),
57− "\u{2029}" => Some(LineEnding::PS),
58− // Not a line ending
59− _ => None,
52+ pub fn from_rope_slice(g: &RopeSlice) -> Option<LineEnding> {
53+ if let Some(text) = g.as_str() {
54+ LineEnding::from_str(text)
55+ } else {
56+ // Non-contiguous, so it can't be a line ending.
57+ // Specifically, Ropey guarantees that CRLF is always
58+ // contiguous. And the remaining line endings are all
59+ // single `char`s, and therefore trivially contiguous.
60+ None
61+ }
6062 }
6163}
6264
65+/// Attempts to detect what line ending the passed document uses.
6366pub fn auto_detect_line_ending(doc: &Rope) -> Option<LineEnding> {
64− // based on https://github.com/cessen/led/blob/27572c8838a1c664ee378a19358604063881cc1d/src/editor/mod.rs#L88-L162
65−
66− let mut ending = None;
67− // return first matched line ending. Not all possible line endings are being matched, as they might be special-use only
67+ // Return first matched line ending. Not all possible line endings
68+ // are being matched, as they might be special-use only
6869 for line in doc.lines().take(100) {
69− ending = match line.len_chars() {
70− 1 => {
71− let g = RopeGraphemes::new(line.slice((line.len_chars() - 1)..))
72− .last()
73− .unwrap();
74− rope_slice_to_line_ending(&g)
75− }
76− n if n > 1 => {
77− let g = RopeGraphemes::new(line.slice((line.len_chars() - 2)..))
78− .last()
79− .unwrap();
80− rope_slice_to_line_ending(&g)
81− }
82− _ => None,
83− };
84− if ending.is_some() {
85− match ending {
86− Some(LineEnding::VT) | Some(LineEnding::FF) | Some(LineEnding::PS) => {}
87− _ => return ending,
88− }
70+ match get_line_ending(&line) {
71+ None | Some(LineEnding::VT) | Some(LineEnding::FF) | Some(LineEnding::PS) => {}
72+ ending => return ending,
8973 }
9074 }
91− ending
75+ None
9276}
9377
9478/// Returns the passed line's line ending, if any.
@@ -108,13 +92,16 @@ pub fn get_line_ending(line: &RopeSlice) -> Option<LineEnding> {
10892 .unwrap_or("");
10993
11094 // First check the two-character case for CRLF, then check the single-character case.
111− str_to_line_ending(g2).or_else(|| str_to_line_ending(g1))
95+ LineEnding::from_str(g2).or_else(|| LineEnding::from_str(g1))
11296}
11397
98+/// Returns the char index of the end of the given line, not including its line ending.
11499pub fn line_end(slice: &RopeSlice, line: usize) -> usize {
115− slice.line_to_char(line + 1).saturating_sub(get_line_ending(&slice.line(line))
116− .map(|le| le.len_chars())
117− .unwrap_or(0))
100+ slice.line_to_char(line + 1).saturating_sub(
101+ get_line_ending(&slice.line(line))
102+ .map(|le| le.len_chars())
103+ .unwrap_or(0),
104+ )
118105}
119106
120107#[cfg(target_os = "windows")]
helix-core/src/movement.rs+2−2
@@ -3,9 +3,9 @@ use std::iter::{self, from_fn, Peekable, SkipWhile};
33use ropey::iter::Chars;
44
55use crate::{
6− coords_at_pos,
6+ coords_at_pos, get_line_ending,
77 graphemes::{nth_next_grapheme_boundary, nth_prev_grapheme_boundary},
8− pos_at_coords, Position, Range, RopeSlice, get_line_ending, line_end
8+ line_end, pos_at_coords, Position, Range, RopeSlice,
99};
1010
1111#[derive(Debug, Copy, Clone, PartialEq, Eq)]
helix-term/src/ui/editor.rs+3−3
@@ -7,9 +7,9 @@ use crate::{
77};
88
99use helix_core::{
10− coords_at_pos, rope_slice_to_line_ending,
10+ coords_at_pos,
1111 syntax::{self, HighlightEvent},
12− Position, Range,
12+ LineEnding, Position, Range,
1313};
1414use helix_view::input::{KeyCode, KeyEvent, KeyModifiers};
1515use helix_view::{document::Mode, Document, Editor, Theme, View};
@@ -177,7 +177,7 @@ impl EditorView {
177177
178178 // iterate over range char by char
179179 for grapheme in RopeGraphemes::new(text) {
180− if rope_slice_to_line_ending(&grapheme).is_some() {
180+ if LineEnding::from_rope_slice(&grapheme).is_some() {
181181 visual_x = 0;
182182 line += 1;
183183
helix-view/src/document.rs+1−1
@@ -255,7 +255,7 @@ impl Document {
255255 use std::{fs::File, io::BufReader};
256256
257257 let doc = if !path.exists() {
258− Rope::from(DEFAULT_LINE_ENDING.as_str())
258+ Rope::from(DEFAULT_LINE_ENDING.as_str())
259259 } else {
260260 let file = File::open(&path).context(format!("unable to open {:?}", path))?;
261261 let mut doc = Rope::from_reader(BufReader::new(file))?;