diff --git a/src/rope/gap_buffer.rs b/src/rope/gap_buffer.rs index 8753680..7a2c9d6 100644 --- a/src/rope/gap_buffer.rs +++ b/src/rope/gap_buffer.rs @@ -402,12 +402,6 @@ impl GapBuffer { unreachable!("This can only be reached if the total length is zero"); } - /// Returns `true` if the buffer ends with a newline ('\n') character. - #[inline] - pub(super) fn has_trailing_newline(&self) -> bool { - self.last_chunk().ends_with('\n') - } - /// Inserts the string at the given byte offset, moving the gap to the new /// insertion point if necessary. /// diff --git a/src/rope/iterators.rs b/src/rope/iterators.rs index 3436f80..4ebfb28 100644 --- a/src/rope/iterators.rs +++ b/src/rope/iterators.rs @@ -486,7 +486,7 @@ impl<'a> Iterator for Lines<'a> { let (tree_slice, ByteMetric(advance)) = self.units.next()?; self.lines_yielded += 1; - let mut slice = RopeSlice { tree_slice, has_trailing_newline: false }; + let mut slice = RopeSlice { tree_slice }; // This handles CRLF pairs that have been split across chunks. For // example, if we have "aaa\r" and "\nbbb" we should yield "aaa", but @@ -514,7 +514,7 @@ impl DoubleEndedIterator for Lines<'_> { let (tree_slice, ByteMetric(advance)) = self.units.next_back()?; self.lines_yielded += 1; - let mut slice = RopeSlice { tree_slice, has_trailing_newline: false }; + let mut slice = RopeSlice { tree_slice }; // Same as above. if slice.tree_slice.end_slice().last_chunk().ends_with('\r') diff --git a/src/rope/rope.rs b/src/rope/rope.rs index ed0d97c..2382c65 100644 --- a/src/rope/rope.rs +++ b/src/rope/rope.rs @@ -30,7 +30,6 @@ pub(super) type RopeChunk = GapBuffer; #[derive(Clone, Default)] pub struct Rope { pub(super) tree: Tree<{ Self::arity() }, RopeChunk>, - pub(super) has_trailing_newline: bool, } impl Rope { @@ -38,9 +37,7 @@ impl Rope { pub fn assert_invariants(&self) { self.tree.assert_invariants(); - if let Some(last) = self.chunks().next_back() { - assert_eq!(self.has_trailing_newline, last.ends_with('\n')); - } else { + if self.chunks().next_back().is_none() { return; } @@ -473,7 +470,7 @@ impl Rope { .tree .slice(RawLineMetric(line_index)..RawLineMetric(line_index + 1)); - let mut line = RopeSlice { tree_slice, has_trailing_newline: false }; + let mut line = RopeSlice { tree_slice }; if line.tree_slice.summary().line_breaks() == 1 { line.truncate_trailing_line_break(); @@ -484,8 +481,7 @@ impl Rope { /// Returns the number of lines in the `Rope`. /// - /// The final line break is optional and doesn't count as a separate empty - /// line. + /// The final line break counts as a separate empty line. /// /// # Examples /// @@ -494,25 +490,23 @@ impl Rope { /// # /// let mut r = Rope::new(); /// - /// assert_eq!(r.line_len(), 0); + /// assert_eq!(r.line_len(), 1); /// /// r.insert(0, "a"); /// assert_eq!(r.line_len(), 1); /// /// r.insert(1, "\n"); - /// assert_eq!(r.line_len(), 1); + /// assert_eq!(r.line_len(), 2); /// /// r.insert(2, "b"); /// assert_eq!(r.line_len(), 2); /// /// r.insert(3, "\r\n"); - /// assert_eq!(r.line_len(), 2); + /// assert_eq!(r.line_len(), 3); /// ``` #[inline] pub fn line_len(&self) -> usize { self.tree.summary().line_breaks() + 1 - - (self.has_trailing_newline as usize) - - (self.is_empty() as usize) } /// Returns the line offset of the given byte. @@ -699,24 +693,7 @@ impl Rope { let text = text.as_ref(); - let mut update_trailing = false; - - if end == self.byte_len() { - if !text.is_empty() { - self.has_trailing_newline = text.ends_with('\n'); - } else if start == 0 { - self.has_trailing_newline = false; - } else { - update_trailing = true; - } - } - self.tree.replace(ByteMetric(start)..ByteMetric(end), text); - - if update_trailing { - self.has_trailing_newline = - self.chunks().next_back().unwrap().ends_with('\n'); - } } /// Returns the number of UTF-16 code units the `Rope` would have if it @@ -820,10 +797,7 @@ impl Rope { impl From> for Rope { #[inline] fn from(rope_slice: RopeSlice<'_>) -> Rope { - Self { - has_trailing_newline: rope_slice.has_trailing_newline, - tree: Tree::from(rope_slice.tree_slice), - } + Self { tree: Tree::from(rope_slice.tree_slice) } } } @@ -850,7 +824,6 @@ impl From<&str> for Rope { #[inline] fn from(s: &str) -> Self { Rope { - has_trailing_newline: s.ends_with('\n'), tree: Tree::from_leaves( RopeChunk::segmenter(s).map(RopeChunk::from), ), diff --git a/src/rope/rope_builder.rs b/src/rope/rope_builder.rs index fc6ac7a..6e69a3b 100644 --- a/src/rope/rope_builder.rs +++ b/src/rope/rope_builder.rs @@ -11,7 +11,6 @@ pub struct RopeBuilder { tree_builder: TreeBuilder<{ Rope::arity() }, RopeChunk>, buffer: RopeChunk, buffer_len_left: usize, - rope_has_trailing_newline: bool, } /// Pushes as mush of the slice as possible onto the left chunk of the gap @@ -72,8 +71,6 @@ impl RopeBuilder { text = rest; } - self.rope_has_trailing_newline = self.buffer.has_trailing_newline(); - self } @@ -110,16 +107,10 @@ impl RopeBuilder { self.buffer.left_summary = ChunkSummary::from(self.buffer_left_chunk()); - self.rope_has_trailing_newline = - self.buffer.has_trailing_newline(); - self.tree_builder.append(self.buffer); } - Rope { - tree: self.tree_builder.build(), - has_trailing_newline: self.rope_has_trailing_newline, - } + Rope { tree: self.tree_builder.build() } } /// Creates a new `RopeBuilder`. diff --git a/src/rope/rope_slice.rs b/src/rope/rope_slice.rs index 3d1ccef..598759a 100644 --- a/src/rope/rope_slice.rs +++ b/src/rope/rope_slice.rs @@ -12,7 +12,6 @@ use crate::tree::TreeSlice; #[derive(Copy, Clone)] pub struct RopeSlice<'a> { pub(super) tree_slice: TreeSlice<'a, { Rope::arity() }, RopeChunk>, - pub(super) has_trailing_newline: bool, } impl<'a> RopeSlice<'a> { @@ -25,8 +24,6 @@ impl<'a> RopeSlice<'a> { let last = self.tree_slice.end_slice(); last.assert_invariants(); - - assert_eq!(self.has_trailing_newline, last.has_trailing_newline()) } /// Returns the byte at `byte_index`. @@ -388,7 +385,7 @@ impl<'a> RopeSlice<'a> { .tree_slice .slice(RawLineMetric(line_index)..RawLineMetric(line_index + 1)); - let mut line = Self { tree_slice, has_trailing_newline: false }; + let mut line = Self { tree_slice }; if line.tree_slice.summary().line_breaks() == 1 { line.truncate_trailing_line_break(); @@ -399,8 +396,7 @@ impl<'a> RopeSlice<'a> { /// Returns the number of lines in the `RopeSlice`. /// - /// The final line break is optional and doesn't count as a separate empty - /// line. + /// The final line break counts as a separate empty line. /// /// # Examples /// @@ -410,25 +406,23 @@ impl<'a> RopeSlice<'a> { /// let r = Rope::from("a\nb\r\n"); /// /// let s = r.byte_slice(..); - /// assert_eq!(s.line_len(), 2); + /// assert_eq!(s.line_len(), 3); /// /// let s = r.byte_slice(..3); /// assert_eq!(s.line_len(), 2); /// /// let s = r.byte_slice(..2); - /// assert_eq!(s.line_len(), 1); + /// assert_eq!(s.line_len(), 2); /// /// let s = r.byte_slice(..1); /// assert_eq!(s.line_len(), 1); /// /// let s = r.byte_slice(..0); - /// assert_eq!(s.line_len(), 0); + /// assert_eq!(s.line_len(), 1); /// ``` #[inline] pub fn line_len(&self) -> usize { self.tree_slice.summary().line_breaks() + 1 - - (self.has_trailing_newline as usize) - - (self.is_empty() as usize) } /// Returns the line offset of the given byte. @@ -735,13 +729,7 @@ impl<'a> RopeSlice<'a> { impl<'a> From> for RopeSlice<'a> { #[inline] fn from(tree_slice: TreeSlice<'a, { Rope::arity() }, RopeChunk>) -> Self { - Self { - has_trailing_newline: tree_slice - .end_slice() - .has_trailing_newline(), - - tree_slice, - } + Self { tree_slice } } } diff --git a/tests/slicing.rs b/tests/slicing.rs index 228e674..a8185ce 100644 --- a/tests/slicing.rs +++ b/tests/slicing.rs @@ -110,7 +110,7 @@ fn line_slice_1() { assert_eq!("Hello world", r.line_slice(..)); let r = Rope::from("Hello world\n"); - assert_eq!(1, r.line_len()); + assert_eq!(2, r.line_len()); assert_eq!("Hello world\n", r.line_slice(..)); let r = Rope::from("Hello world\nthis is\na test"); @@ -152,15 +152,23 @@ fn line_slices_random() { let line_offsets = { let mut offset = 0; + let mut ends_in_newline = true; - rope_slice + let mut line_offsets = rope_slice .raw_lines() .map(|line| { let o = offset; + ends_in_newline = line.byte(line.byte_len() - 1) == b'\n'; offset += line.byte_len(); o }) - .collect::>() + .collect::>(); + + if ends_in_newline { + line_offsets.push(offset); + } + + line_offsets }; assert_eq!(line_offsets.len(), rope_slice.line_len());