Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions src/rope/gap_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,12 +402,6 @@ impl<const MAX_BYTES: usize> GapBuffer<MAX_BYTES> {
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.
///
Expand Down
4 changes: 2 additions & 2 deletions src/rope/iterators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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')
Expand Down
41 changes: 7 additions & 34 deletions src/rope/rope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,14 @@ pub(super) type RopeChunk = GapBuffer<CHUNK_MAX_BYTES>;
#[derive(Clone, Default)]
pub struct Rope {
pub(super) tree: Tree<{ Self::arity() }, RopeChunk>,
pub(super) has_trailing_newline: bool,
}

impl Rope {
#[doc(hidden)]
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;
}

Expand Down Expand Up @@ -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();
Expand All @@ -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
///
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -820,10 +797,7 @@ impl Rope {
impl From<RopeSlice<'_>> 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) }
}
}

Expand All @@ -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),
),
Expand Down
11 changes: 1 addition & 10 deletions src/rope/rope_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -72,8 +71,6 @@ impl RopeBuilder {
text = rest;
}

self.rope_has_trailing_newline = self.buffer.has_trailing_newline();

self
}

Expand Down Expand Up @@ -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`.
Expand Down
24 changes: 6 additions & 18 deletions src/rope/rope_slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand All @@ -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`.
Expand Down Expand Up @@ -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();
Expand All @@ -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
///
Expand All @@ -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.
Expand Down Expand Up @@ -735,13 +729,7 @@ impl<'a> RopeSlice<'a> {
impl<'a> From<TreeSlice<'a, { Rope::arity() }, RopeChunk>> 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 }
}
}

Expand Down
14 changes: 11 additions & 3 deletions tests/slicing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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::<Vec<_>>()
.collect::<Vec<_>>();

if ends_in_newline {
line_offsets.push(offset);
}

line_offsets
};

assert_eq!(line_offsets.len(), rope_slice.line_len());
Expand Down