Skip to content

Commit

Permalink
refactor: try
Browse files Browse the repository at this point in the history
  • Loading branch information
h-a-n-a committed Dec 12, 2024
1 parent bdf97fb commit e60d734
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 14 deletions.
11 changes: 5 additions & 6 deletions src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use std::{
ops::Range,
};

use itertools::Either;
use rustc_hash::FxHashMap as HashMap;

use crate::{
Expand Down Expand Up @@ -201,7 +200,7 @@ const EMPTY_ROPE: Rope = Rope::new();
/// Split the string with a needle, each string will contain the needle.
///
/// Copied and modified from https://github.com/rust-lang/cargo/blob/30efe860c0e4adc1a6d7057ad223dc6e47d34edf/src/cargo/sources/registry/index.rs#L1048-L1072
pub fn split<'a>(
fn split<'a>(
haystack: &Rope<'a>,
needle: u8,
) -> impl Iterator<Item = Rope<'a>> {
Expand Down Expand Up @@ -1324,10 +1323,10 @@ pub trait SourceText<'a>: Default + Clone + ToString {

impl<'a> SourceText<'a> for Rope<'a> {
fn split_into_lines(&self) -> impl Iterator<Item = Self> {
if let Some(s) = self.get_simple() {
return Either::Left(split_str(s, b'\n').map(Rope::from));
}
Either::Right(split(self, b'\n'))
// Split the text into lines, including the line ending character.
// If the text ends with a newline, the last line will be ignored
// For example: "abc\nefg\n" => ["abc\n", "efg\n"]
self.lines_impl(false)
}

#[inline]
Expand Down
48 changes: 40 additions & 8 deletions src/rope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,16 @@ impl<'a> Rope<'a> {

/// Returns an iterator over the lines of the rope.
pub fn lines(&self) -> Lines<'_, 'a> {
self.lines_impl(true)
}

/// Returns an iterator over the lines of the rope.
///
/// If `end_line_break_as_newline` is true, the end of the rope with ('\n') is treated as an empty newline
pub(crate) fn lines_impl(
&self,
end_line_break_as_newline: bool,
) -> Lines<'_, 'a> {
Lines {
iter: match &self.repr {
Repr::Simple(s) => LinesEnum::Simple(s),
Expand All @@ -431,6 +441,7 @@ impl<'a> Rope<'a> {
chunk_idx: 0,
ended: false,
total_bytes: self.len(),
end_line_break_as_newline,
}
}

Expand Down Expand Up @@ -484,9 +495,12 @@ pub struct Lines<'a, 'b> {
chunk_idx: usize,
ended: bool,
total_bytes: usize,

/// Whether to treat the end of the rope with ('\n') as an empty newline.
end_line_break_as_newline: bool,
}

impl<'a, 'b> Iterator for Lines<'a, 'b> {
impl<'a> Iterator for Lines<'_, 'a> {
type Item = Rope<'a>;

fn next(&mut self) -> Option<Self::Item> {
Expand All @@ -496,13 +510,17 @@ impl<'a, 'b> Iterator for Lines<'a, 'b> {
ref mut byte_idx,
ref mut ended,
ref total_bytes,
end_line_break_as_newline,
..
} => {
if *ended {
return None;
} else if byte_idx == total_bytes {
*ended = true;
return Some(Rope::from(""));
if end_line_break_as_newline {
*ended = true;
return Some(Rope::from(""));
}
return None;
} else if let Some(idx) =
memchr::memchr(b'\n', &s.as_bytes()[*byte_idx..])
{
Expand All @@ -512,7 +530,7 @@ impl<'a, 'b> Iterator for Lines<'a, 'b> {
return Some(rope);
}
*ended = true;
return Some(Rope::from(&s[*byte_idx..]));
Some(Rope::from(&s[*byte_idx..]))
}
Lines {
iter: LinesEnum::Complex(chunks),
Expand All @@ -521,12 +539,16 @@ impl<'a, 'b> Iterator for Lines<'a, 'b> {
ref mut chunk_idx,
ref mut ended,
ref total_bytes,
end_line_break_as_newline,
} => {
if *ended {
return None;
} else if byte_idx == total_bytes {
*ended = true;
return Some(Rope::from(""));
if end_line_break_as_newline {
*ended = true;
return Some(Rope::from(""));
}
return None;
}

debug_assert!(*chunk_idx < chunks.len());
Expand Down Expand Up @@ -629,9 +651,9 @@ impl<'a, 'b> Iterator for Lines<'a, 'b> {
});
// Advance the byte index to the end of the rope.
*byte_idx += len;
return Some(Rope {
Some(Rope {
repr: Repr::Complex(Rc::new(raw)),
});
})
}
}
}
Expand Down Expand Up @@ -1093,13 +1115,23 @@ mod tests {

#[test]
fn lines1() {
let rope = Rope::from("abc");
let lines = rope.lines().collect::<Vec<_>>();
assert_eq!(lines, ["abc"]);

// empty line at the end if the line before ends with a newline ('\n')
let rope = Rope::from("abc\ndef\n");
let lines = rope.lines().collect::<Vec<_>>();
assert_eq!(lines, ["abc\n", "def\n", ""]);

// no empty line at the end if the line before does not end with a newline ('\n')
let rope = Rope::from("abc\ndef");
let lines = rope.lines().collect::<Vec<_>>();
assert_eq!(lines, ["abc\n", "def"]);

let rope = Rope::from("Test\nTest\nTest\n");
let lines = rope.lines().collect::<Vec<_>>();
assert_eq!(lines, ["Test\n", "Test\n", "Test\n", ""]);
}

#[test]
Expand Down

0 comments on commit e60d734

Please sign in to comment.