Add line ending abstraction (#250)
* Add line ending abstraction * Make Buffer::set_text use LineIter * Add ctrl+s for saving to editor
This commit is contained in:
parent
ff5501d9a3
commit
0cfd9b64ef
10 changed files with 194 additions and 15 deletions
98
src/line_ending.rs
Normal file
98
src/line_ending.rs
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
use core::ops::Range;
|
||||
|
||||
/// Line ending
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||
pub enum LineEnding {
|
||||
/// Use `\n` for line ending (POSIX-style)
|
||||
#[default]
|
||||
Lf,
|
||||
/// Use `\r\n` for line ending (Windows-style)
|
||||
CrLf,
|
||||
/// Use `\r` for line ending (many legacy systems)
|
||||
Cr,
|
||||
/// Use `\n\r` for line ending (some legacy systems)
|
||||
LfCr,
|
||||
/// No line ending
|
||||
None,
|
||||
}
|
||||
|
||||
impl LineEnding {
|
||||
/// Get the line ending as a str
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Lf => "\n",
|
||||
Self::CrLf => "\r\n",
|
||||
Self::Cr => "\r",
|
||||
Self::LfCr => "\n\r",
|
||||
Self::None => "",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator over lines terminated by [`LineEnding`]
|
||||
#[derive(Debug)]
|
||||
pub struct LineIter<'a> {
|
||||
string: &'a str,
|
||||
start: usize,
|
||||
end: usize,
|
||||
}
|
||||
|
||||
impl<'a> LineIter<'a> {
|
||||
/// Create an iterator of lines in a string slice
|
||||
pub fn new(string: &'a str) -> Self {
|
||||
Self {
|
||||
string,
|
||||
start: 0,
|
||||
end: string.len(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for LineIter<'a> {
|
||||
type Item = (Range<usize>, LineEnding);
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let start = self.start;
|
||||
match self.string[start..self.end].find(&['\r', '\n']) {
|
||||
Some(i) => {
|
||||
let end = start + i;
|
||||
self.start = end;
|
||||
let after = &self.string[end..];
|
||||
let ending = if after.starts_with("\r\n") {
|
||||
LineEnding::CrLf
|
||||
} else if after.starts_with("\n\r") {
|
||||
LineEnding::LfCr
|
||||
} else if after.starts_with("\n") {
|
||||
LineEnding::Lf
|
||||
} else if after.starts_with("\r") {
|
||||
LineEnding::Cr
|
||||
} else {
|
||||
//TODO: this should not be possible
|
||||
LineEnding::None
|
||||
};
|
||||
self.start += ending.as_str().len();
|
||||
Some((start..end, ending))
|
||||
}
|
||||
None => {
|
||||
if self.start < self.end {
|
||||
self.start = self.end;
|
||||
Some((start..self.end, LineEnding::None))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: DoubleEndedIterator
|
||||
|
||||
#[test]
|
||||
fn test_line_iter() {
|
||||
let string = "LF\nCRLF\r\nCR\rLFCR\n\rNONE";
|
||||
let mut iter = LineIter::new(string);
|
||||
assert_eq!(iter.next(), Some((0..2, LineEnding::Lf)));
|
||||
assert_eq!(iter.next(), Some((3..7, LineEnding::CrLf)));
|
||||
assert_eq!(iter.next(), Some((9..11, LineEnding::Cr)));
|
||||
assert_eq!(iter.next(), Some((12..16, LineEnding::LfCr)));
|
||||
assert_eq!(iter.next(), Some((18..22, LineEnding::None)));
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue