use smallvec::alloc::string::String;
use core::ops::{Bound, RangeBounds};
use alloc::sync::Arc;
use crate::iter::{Bytes, Chars, Chunks, Lines};
use crate::rope::Rope;
use crate::str_utils::{
byte_to_char_idx, byte_to_line_idx, byte_to_utf16_surrogate_idx, char_to_byte_idx,
char_to_line_idx, count_chars, count_line_breaks, count_utf16_surrogates, line_to_byte_idx,
line_to_char_idx, utf16_code_unit_to_char_idx,
};
use crate::tree::{Count, Node, TextInfo};
#[derive(Copy, Clone)]
pub struct RopeSlice<'a>(pub(crate) RSEnum<'a>);
#[derive(Copy, Clone)]
pub(crate) enum RSEnum<'a> {
Full {
node: &'a Arc<Node>,
start_info: TextInfo,
end_info: TextInfo,
},
Light {
text: &'a str,
char_count: Count,
utf16_surrogate_count: Count,
line_break_count: Count,
},
}
impl<'a> RopeSlice<'a> {
pub(crate) fn new_with_range(node: &'a Arc<Node>, start: usize, end: usize) -> Self {
assert!(start <= end);
assert!(end <= node.text_info().chars as usize);
if start == 0 && end == node.char_count() {
if node.is_leaf() {
let text = node.leaf_text();
return RopeSlice(RSEnum::Light {
text: text,
char_count: (end - start) as Count,
utf16_surrogate_count: count_utf16_surrogates(text) as Count,
line_break_count: count_line_breaks(text) as Count,
});
} else {
return RopeSlice(RSEnum::Full {
node: node,
start_info: TextInfo {
bytes: 0,
chars: 0,
utf16_surrogates: 0,
line_breaks: 0,
},
end_info: TextInfo {
bytes: node.byte_count() as Count,
chars: node.char_count() as Count,
utf16_surrogates: node.utf16_surrogate_count() as Count,
line_breaks: node.line_break_count() as Count,
},
});
}
}
let mut n_start = start;
let mut n_end = end;
let mut node = node;
'outer: loop {
match *(node as &Node) {
Node::Leaf(ref text) => {
let start_byte = char_to_byte_idx(&text, n_start);
let end_byte =
start_byte + char_to_byte_idx(&text[start_byte..], n_end - n_start);
return RopeSlice(RSEnum::Light {
text: &text[start_byte..end_byte],
char_count: (n_end - n_start) as Count,
utf16_surrogate_count: count_utf16_surrogates(&text[start_byte..end_byte])
as Count,
line_break_count: count_line_breaks(&text[start_byte..end_byte]) as Count,
});
}
Node::Internal(ref children) => {
let mut start_char = 0;
for (i, inf) in children.info().iter().enumerate() {
if n_start >= start_char && n_end < (start_char + inf.chars as usize) {
n_start -= start_char;
n_end -= start_char;
node = &children.nodes()[i];
continue 'outer;
}
start_char += inf.chars as usize;
}
break;
}
}
}
RopeSlice(RSEnum::Full {
node: node,
start_info: node.char_to_text_info(n_start),
end_info: node.char_to_text_info(n_end),
})
}
#[inline]
pub fn len_bytes(&self) -> usize {
match *self {
RopeSlice(RSEnum::Full {
end_info,
start_info,
..
}) => (end_info.bytes - start_info.bytes) as usize,
RopeSlice(RSEnum::Light { text, .. }) => text.len(),
}
}
#[inline]
pub fn len_chars(&self) -> usize {
match *self {
RopeSlice(RSEnum::Full {
end_info,
start_info,
..
}) => (end_info.chars - start_info.chars) as usize,
RopeSlice(RSEnum::Light { char_count, .. }) => char_count as usize,
}
}
#[inline]
pub fn len_lines(&self) -> usize {
match *self {
RopeSlice(RSEnum::Full {
end_info,
start_info,
..
}) => (end_info.line_breaks - start_info.line_breaks) as usize + 1,
RopeSlice(RSEnum::Light {
line_break_count, ..
}) => line_break_count as usize + 1,
}
}
#[inline]
pub fn len_utf16_cu(&self) -> usize {
match *self {
RopeSlice(RSEnum::Full {
end_info,
start_info,
..
}) => {
((end_info.chars + end_info.utf16_surrogates)
- (start_info.chars + start_info.utf16_surrogates)) as usize
}
RopeSlice(RSEnum::Light {
char_count,
utf16_surrogate_count,
..
}) => (char_count + utf16_surrogate_count) as usize,
}
}
#[inline]
pub fn byte_to_char(&self, byte_idx: usize) -> usize {
assert!(
byte_idx <= self.len_bytes(),
"Attempt to index past end of slice: byte index {}, slice byte length {}",
byte_idx,
self.len_bytes()
);
let (chunk, b, c, _) = self.chunk_at_byte(byte_idx);
c + byte_to_char_idx(chunk, byte_idx - b)
}
#[inline]
pub fn byte_to_line(&self, byte_idx: usize) -> usize {
assert!(
byte_idx <= self.len_bytes(),
"Attempt to index past end of slice: byte index {}, slice byte length {}",
byte_idx,
self.len_bytes()
);
let (chunk, b, _, l) = self.chunk_at_byte(byte_idx);
l + byte_to_line_idx(chunk, byte_idx - b)
}
#[inline]
pub fn char_to_byte(&self, char_idx: usize) -> usize {
assert!(
char_idx <= self.len_chars(),
"Attempt to index past end of slice: char index {}, slice char length {}",
char_idx,
self.len_chars()
);
let (chunk, b, c, _) = self.chunk_at_char(char_idx);
b + char_to_byte_idx(chunk, char_idx - c)
}
#[inline]
pub fn char_to_line(&self, char_idx: usize) -> usize {
assert!(
char_idx <= self.len_chars(),
"Attempt to index past end of slice: char index {}, slice char length {}",
char_idx,
self.len_chars()
);
let (chunk, _, c, l) = self.chunk_at_char(char_idx);
l + char_to_line_idx(chunk, char_idx - c)
}
#[inline]
pub fn char_to_utf16_cu(&self, char_idx: usize) -> usize {
assert!(
char_idx <= self.len_chars(),
"Attempt to index past end of slice: char index {}, slice char \
length {}",
char_idx,
self.len_chars()
);
match *self {
RopeSlice(RSEnum::Full {
ref node,
start_info,
..
}) => {
let char_idx = char_idx + start_info.chars as usize;
let (chunk, chunk_start_info) = node.get_chunk_at_char(char_idx);
let chunk_byte_idx =
char_to_byte_idx(chunk, char_idx - chunk_start_info.chars as usize);
let surrogate_count = byte_to_utf16_surrogate_idx(chunk, chunk_byte_idx);
char_idx + chunk_start_info.utf16_surrogates as usize + surrogate_count
- start_info.chars as usize
- start_info.utf16_surrogates as usize
}
RopeSlice(RSEnum::Light { text, .. }) => {
let byte_idx = char_to_byte_idx(text, char_idx);
let surrogate_count = byte_to_utf16_surrogate_idx(text, byte_idx);
char_idx + surrogate_count
}
}
}
#[inline]
pub fn utf16_cu_to_char(&self, utf16_cu_idx: usize) -> usize {
assert!(
utf16_cu_idx <= self.len_utf16_cu(),
"Attempt to index past end of slice: utf16 code unit index {}, \
slice utf16 code unit length {}",
utf16_cu_idx,
self.len_utf16_cu()
);
match *self {
RopeSlice(RSEnum::Full {
ref node,
start_info,
..
}) => {
let utf16_cu_idx =
utf16_cu_idx + (start_info.chars + start_info.utf16_surrogates) as usize;
let (chunk, chunk_start_info) = node.get_chunk_at_utf16_code_unit(utf16_cu_idx);
let chunk_utf16_cu_idx = utf16_cu_idx
- (chunk_start_info.chars + chunk_start_info.utf16_surrogates) as usize;
let chunk_char_idx = utf16_code_unit_to_char_idx(chunk, chunk_utf16_cu_idx);
chunk_start_info.chars as usize + chunk_char_idx - start_info.chars as usize
}
RopeSlice(RSEnum::Light { text, .. }) => {
utf16_code_unit_to_char_idx(text, utf16_cu_idx)
}
}
}
#[inline]
pub fn line_to_byte(&self, line_idx: usize) -> usize {
assert!(
line_idx <= self.len_lines(),
"Attempt to index past end of slice: line index {}, slice line length {}",
line_idx,
self.len_lines()
);
if line_idx == self.len_lines() {
self.len_bytes()
} else {
let (chunk, b, _, l) = self.chunk_at_line_break(line_idx);
b + line_to_byte_idx(chunk, line_idx - l)
}
}
#[inline]
pub fn line_to_char(&self, line_idx: usize) -> usize {
assert!(
line_idx <= self.len_lines(),
"Attempt to index past end of slice: line index {}, slice line length {}",
line_idx,
self.len_lines()
);
if line_idx == self.len_lines() {
self.len_chars()
} else {
let (chunk, _, c, l) = self.chunk_at_line_break(line_idx);
c + line_to_char_idx(chunk, line_idx - l)
}
}
#[inline]
pub fn byte(&self, byte_idx: usize) -> u8 {
assert!(
byte_idx < self.len_bytes(),
"Attempt to index past end of slice: byte index {}, slice byte length {}",
byte_idx,
self.len_bytes()
);
let (chunk, chunk_byte_idx, _, _) = self.chunk_at_byte(byte_idx);
let chunk_rel_byte_idx = byte_idx - chunk_byte_idx;
chunk.as_bytes()[chunk_rel_byte_idx]
}
#[inline]
pub fn char(&self, char_idx: usize) -> char {
assert!(
char_idx < self.len_chars(),
"Attempt to index past end of slice: char index {}, slice char length {}",
char_idx,
self.len_chars()
);
let (chunk, _, chunk_char_idx, _) = self.chunk_at_char(char_idx);
let byte_idx = char_to_byte_idx(chunk, char_idx - chunk_char_idx);
chunk[byte_idx..].chars().next().unwrap()
}
#[inline]
pub fn line(&self, line_idx: usize) -> RopeSlice<'a> {
let len_lines = self.len_lines();
assert!(
line_idx < len_lines,
"Attempt to index past end of slice: line index {}, slice line length {}",
line_idx,
len_lines
);
let (chunk_1, _, c1, l1) = self.chunk_at_line_break(line_idx);
let (chunk_2, _, c2, l2) = self.chunk_at_line_break(line_idx + 1);
if c1 == c2 {
let text1 = &chunk_1[line_to_byte_idx(chunk_1, line_idx - l1)..];
let text2 = &text1[..line_to_byte_idx(text1, 1)];
RopeSlice(RSEnum::Light {
text: text2,
char_count: count_chars(text2) as Count,
utf16_surrogate_count: count_utf16_surrogates(text2) as Count,
line_break_count: if line_idx == (len_lines - 1) { 0 } else { 1 },
})
} else {
let start = c1 + line_to_char_idx(chunk_1, line_idx - l1);
let end = c2 + line_to_char_idx(chunk_2, line_idx + 1 - l2);
self.slice(start..end)
}
}
pub fn chunk_at_byte(&self, byte_idx: usize) -> (&'a str, usize, usize, usize) {
assert!(
byte_idx <= self.len_bytes(),
"Attempt to index past end of slice: byte index {}, slice byte length {}",
byte_idx,
self.len_bytes()
);
match *self {
RopeSlice(RSEnum::Full {
node,
start_info,
end_info,
}) => {
let (chunk, chunk_start_info) =
node.get_chunk_at_byte(byte_idx + start_info.bytes as usize);
let chunk_start_byte_idx = start_info.bytes.saturating_sub(chunk_start_info.bytes);
let chunk_end_byte_idx =
(chunk.len() as Count).min(end_info.bytes - chunk_start_info.bytes);
(
&chunk[chunk_start_byte_idx as usize..chunk_end_byte_idx as usize],
chunk_start_info.bytes.saturating_sub(start_info.bytes) as usize,
chunk_start_info.chars.saturating_sub(start_info.chars) as usize,
chunk_start_info
.line_breaks
.saturating_sub(start_info.line_breaks) as usize,
)
}
RopeSlice(RSEnum::Light { text, .. }) => (text, 0, 0, 0),
}
}
pub fn chunk_at_char(&self, char_idx: usize) -> (&'a str, usize, usize, usize) {
assert!(
char_idx <= self.len_chars(),
"Attempt to index past end of slice: char index {}, slice char length {}",
char_idx,
self.len_chars()
);
match *self {
RopeSlice(RSEnum::Full {
node,
start_info,
end_info,
}) => {
let (chunk, chunk_start_info) =
node.get_chunk_at_char(char_idx + start_info.chars as usize);
let chunk_start_byte_idx = start_info.bytes.saturating_sub(chunk_start_info.bytes);
let chunk_end_byte_idx =
(chunk.len() as Count).min(end_info.bytes - chunk_start_info.bytes);
(
&chunk[chunk_start_byte_idx as usize..chunk_end_byte_idx as usize],
chunk_start_info.bytes.saturating_sub(start_info.bytes) as usize,
chunk_start_info.chars.saturating_sub(start_info.chars) as usize,
chunk_start_info
.line_breaks
.saturating_sub(start_info.line_breaks) as usize,
)
}
RopeSlice(RSEnum::Light { text, .. }) => (text, 0, 0, 0),
}
}
pub fn chunk_at_line_break(&self, line_break_idx: usize) -> (&'a str, usize, usize, usize) {
assert!(
line_break_idx <= self.len_lines(),
"Attempt to index past end of Rope: line break index {}, max index {}",
line_break_idx,
self.len_lines()
);
match *self {
RopeSlice(RSEnum::Full {
node,
start_info,
end_info,
}) => {
let (chunk, chunk_start_info) = if line_break_idx == 0 {
node.get_chunk_at_byte(start_info.bytes as usize)
} else if line_break_idx == self.len_lines() {
node.get_chunk_at_byte(end_info.bytes as usize)
} else {
node.get_chunk_at_line_break(line_break_idx + start_info.line_breaks as usize)
};
let chunk_start_byte_idx = start_info.bytes.saturating_sub(chunk_start_info.bytes);
let chunk_end_byte_idx =
(chunk.len() as Count).min(end_info.bytes - chunk_start_info.bytes);
(
&chunk[chunk_start_byte_idx as usize..chunk_end_byte_idx as usize],
chunk_start_info.bytes.saturating_sub(start_info.bytes) as usize,
chunk_start_info.chars.saturating_sub(start_info.chars) as usize,
chunk_start_info
.line_breaks
.saturating_sub(start_info.line_breaks) as usize,
)
}
RopeSlice(RSEnum::Light { text, .. }) => (text, 0, 0, 0),
}
}
#[inline]
pub fn as_str(&self) -> Option<&'a str> {
match *self {
RopeSlice(RSEnum::Full { .. }) => None,
RopeSlice(RSEnum::Light { text, .. }) => Some(text),
}
}
pub fn slice<R>(&self, char_range: R) -> Self
where
R: RangeBounds<usize>,
{
let (start, end) = {
let start_range = start_bound_to_num(char_range.start_bound());
let end_range = end_bound_to_num(char_range.end_bound());
if start_range == None && end_range == None {
return *self;
}
(
start_range.unwrap_or(0),
end_range.unwrap_or_else(|| self.len_chars()),
)
};
assert!(start <= end);
assert!(
end <= self.len_chars(),
"Attempt to slice past end of RopeSlice: slice end {}, RopeSlice length {}",
end,
self.len_chars()
);
match *self {
RopeSlice(RSEnum::Full {
node, start_info, ..
}) => RopeSlice::new_with_range(
node,
start_info.chars as usize + start,
start_info.chars as usize + end,
),
RopeSlice(RSEnum::Light { text, .. }) => {
let start_byte = char_to_byte_idx(text, start);
let end_byte = char_to_byte_idx(text, end);
let new_text = &text[start_byte..end_byte];
RopeSlice(RSEnum::Light {
text: new_text,
char_count: (end - start) as Count,
utf16_surrogate_count: count_utf16_surrogates(new_text) as Count,
line_break_count: count_line_breaks(new_text) as Count,
})
}
}
}
#[inline]
pub fn bytes(&self) -> Bytes<'a> {
match *self {
RopeSlice(RSEnum::Full {
node,
start_info,
end_info,
}) => Bytes::new_with_range(
node,
(start_info.bytes as usize, end_info.bytes as usize),
(start_info.chars as usize, end_info.chars as usize),
(
start_info.line_breaks as usize,
end_info.line_breaks as usize + 1,
),
),
RopeSlice(RSEnum::Light { text, .. }) => Bytes::from_str(text),
}
}
#[inline]
pub fn bytes_at(&self, byte_idx: usize) -> Bytes {
assert!(
byte_idx <= self.len_bytes(),
"Attempt to index past end of RopeSlice: byte index {}, RopeSlice byte length {}",
byte_idx,
self.len_bytes()
);
match *self {
RopeSlice(RSEnum::Full {
node,
start_info,
end_info,
}) => Bytes::new_with_range_at(
node,
start_info.bytes as usize + byte_idx,
(start_info.bytes as usize, end_info.bytes as usize),
(start_info.chars as usize, end_info.chars as usize),
(
start_info.line_breaks as usize,
end_info.line_breaks as usize + 1,
),
),
RopeSlice(RSEnum::Light { text, .. }) => Bytes::from_str_at(text, byte_idx),
}
}
#[inline]
pub fn chars(&self) -> Chars<'a> {
match *self {
RopeSlice(RSEnum::Full {
node,
start_info,
end_info,
}) => Chars::new_with_range(
node,
(start_info.bytes as usize, end_info.bytes as usize),
(start_info.chars as usize, end_info.chars as usize),
(
start_info.line_breaks as usize,
end_info.line_breaks as usize + 1,
),
),
RopeSlice(RSEnum::Light { text, .. }) => Chars::from_str(text),
}
}
#[inline]
pub fn chars_at(&self, char_idx: usize) -> Chars {
assert!(
char_idx <= self.len_chars(),
"Attempt to index past end of RopeSlice: char index {}, RopeSlice char length {}",
char_idx,
self.len_chars()
);
match *self {
RopeSlice(RSEnum::Full {
node,
start_info,
end_info,
}) => Chars::new_with_range_at(
node,
start_info.chars as usize + char_idx,
(start_info.bytes as usize, end_info.bytes as usize),
(start_info.chars as usize, end_info.chars as usize),
(
start_info.line_breaks as usize,
end_info.line_breaks as usize + 1,
),
),
RopeSlice(RSEnum::Light { text, .. }) => Chars::from_str_at(text, char_idx),
}
}
#[inline]
pub fn lines(&self) -> Lines<'a> {
match *self {
RopeSlice(RSEnum::Full {
node,
start_info,
end_info,
}) => Lines::new_with_range(
node,
(start_info.chars as usize, end_info.chars as usize),
(
start_info.line_breaks as usize,
end_info.line_breaks as usize + 1,
),
),
RopeSlice(RSEnum::Light { text, .. }) => Lines::from_str(text),
}
}
#[inline]
pub fn lines_at(&self, line_idx: usize) -> Lines {
assert!(
line_idx <= self.len_lines(),
"Attempt to index past end of RopeSlice: line index {}, RopeSlice line length {}",
line_idx,
self.len_lines()
);
match *self {
RopeSlice(RSEnum::Full {
node,
start_info,
end_info,
}) => Lines::new_with_range_at(
node,
start_info.line_breaks as usize + line_idx,
(start_info.chars as usize, end_info.chars as usize),
(
start_info.line_breaks as usize,
end_info.line_breaks as usize + 1,
),
),
RopeSlice(RSEnum::Light { text, .. }) => Lines::from_str_at(text, line_idx),
}
}
#[inline]
pub fn chunks(&self) -> Chunks<'a> {
match *self {
RopeSlice(RSEnum::Full {
node,
start_info,
end_info,
}) => Chunks::new_with_range(
node,
(start_info.bytes as usize, end_info.bytes as usize),
(start_info.chars as usize, end_info.chars as usize),
(
start_info.line_breaks as usize,
end_info.line_breaks as usize + 1,
),
),
RopeSlice(RSEnum::Light { text, .. }) => Chunks::from_str(text, false),
}
}
#[inline]
pub fn chunks_at_byte(&self, byte_idx: usize) -> (Chunks<'a>, usize, usize, usize) {
assert!(
byte_idx <= self.len_bytes(),
"Attempt to index past end of RopeSlice: byte index {}, RopeSlice byte length {}",
byte_idx,
self.len_bytes()
);
match *self {
RopeSlice(RSEnum::Full {
node,
start_info,
end_info,
}) => {
let (chunks, chunk_byte_idx, chunk_char_idx, chunk_line_idx) =
Chunks::new_with_range_at_byte(
node,
byte_idx + start_info.bytes as usize,
(start_info.bytes as usize, end_info.bytes as usize),
(start_info.chars as usize, end_info.chars as usize),
(
start_info.line_breaks as usize,
end_info.line_breaks as usize + 1,
),
);
(
chunks,
chunk_byte_idx.saturating_sub(start_info.bytes as usize),
chunk_char_idx.saturating_sub(start_info.chars as usize),
chunk_line_idx.saturating_sub(start_info.line_breaks as usize),
)
}
RopeSlice(RSEnum::Light {
text,
char_count,
line_break_count,
..
}) => {
let chunks = Chunks::from_str(text, byte_idx == text.len());
if byte_idx == text.len() {
(
chunks,
text.len(),
char_count as usize,
line_break_count as usize,
)
} else {
(chunks, 0, 0, 0)
}
}
}
}
#[inline]
pub fn chunks_at_char(&self, char_idx: usize) -> (Chunks<'a>, usize, usize, usize) {
assert!(
char_idx <= self.len_chars(),
"Attempt to index past end of RopeSlice: char index {}, RopeSlice char length {}",
char_idx,
self.len_chars()
);
match *self {
RopeSlice(RSEnum::Full {
node,
start_info,
end_info,
}) => {
let (chunks, chunk_byte_idx, chunk_char_idx, chunk_line_idx) =
Chunks::new_with_range_at_char(
node,
char_idx + start_info.chars as usize,
(start_info.bytes as usize, end_info.bytes as usize),
(start_info.chars as usize, end_info.chars as usize),
(
start_info.line_breaks as usize,
end_info.line_breaks as usize + 1,
),
);
(
chunks,
chunk_byte_idx.saturating_sub(start_info.bytes as usize),
chunk_char_idx.saturating_sub(start_info.chars as usize),
chunk_line_idx.saturating_sub(start_info.line_breaks as usize),
)
}
RopeSlice(RSEnum::Light {
text,
char_count,
line_break_count,
..
}) => {
let chunks = Chunks::from_str(text, char_idx == char_count as usize);
if char_idx == char_count as usize {
(
chunks,
text.len(),
char_count as usize,
line_break_count as usize,
)
} else {
(chunks, 0, 0, 0)
}
}
}
}
#[inline]
pub fn chunks_at_line_break(&self, line_break_idx: usize) -> (Chunks, usize, usize, usize) {
assert!(
line_break_idx <= self.len_lines(),
"Attempt to index past end of RopeSlice: line break index {}, RopeSlice line break max index {}",
line_break_idx,
self.len_lines()
);
match *self {
RopeSlice(RSEnum::Full {
node,
start_info,
end_info,
}) => {
let (chunks, chunk_byte_idx, chunk_char_idx, chunk_line_idx) =
if line_break_idx == 0 {
Chunks::new_with_range_at_byte(
node,
start_info.bytes as usize,
(start_info.bytes as usize, end_info.bytes as usize),
(start_info.chars as usize, end_info.chars as usize),
(
start_info.line_breaks as usize,
end_info.line_breaks as usize + 1,
),
)
} else if line_break_idx == self.len_lines() {
Chunks::new_with_range_at_byte(
node,
end_info.bytes as usize,
(start_info.bytes as usize, end_info.bytes as usize),
(start_info.chars as usize, end_info.chars as usize),
(
start_info.line_breaks as usize,
end_info.line_breaks as usize + 1,
),
)
} else {
Chunks::new_with_range_at_line_break(
node,
line_break_idx + start_info.line_breaks as usize,
(start_info.bytes as usize, end_info.bytes as usize),
(start_info.chars as usize, end_info.chars as usize),
(
start_info.line_breaks as usize,
end_info.line_breaks as usize + 1,
),
)
};
(
chunks,
chunk_byte_idx.saturating_sub(start_info.bytes as usize),
chunk_char_idx.saturating_sub(start_info.chars as usize),
chunk_line_idx.saturating_sub(start_info.line_breaks as usize),
)
}
RopeSlice(RSEnum::Light {
text,
char_count,
line_break_count,
..
}) => {
let chunks = Chunks::from_str(text, line_break_idx == line_break_count as usize);
if line_break_idx == line_break_count as usize {
(
chunks,
text.len(),
char_count as usize,
line_break_count as usize,
)
} else {
(chunks, 0, 0, 0)
}
}
}
}
}
#[inline(always)]
pub(crate) fn start_bound_to_num(b: Bound<&usize>) -> Option<usize> {
match b {
Bound::Included(n) => Some(*n),
Bound::Excluded(n) => Some(*n + 1),
Bound::Unbounded => None,
}
}
#[inline(always)]
pub(crate) fn end_bound_to_num(b: Bound<&usize>) -> Option<usize> {
match b {
Bound::Included(n) => Some(*n + 1),
Bound::Excluded(n) => Some(*n),
Bound::Unbounded => None,
}
}
impl<'a> From<&'a str> for RopeSlice<'a> {
#[inline]
fn from(text: &'a str) -> Self {
RopeSlice(RSEnum::Light {
text: text,
char_count: count_chars(text) as Count,
utf16_surrogate_count: count_utf16_surrogates(text) as Count,
line_break_count: count_line_breaks(text) as Count,
})
}
}
impl<'a> From<RopeSlice<'a>> for String {
#[inline]
fn from(s: RopeSlice<'a>) -> Self {
let mut text = String::with_capacity(s.len_bytes());
text.extend(s.chunks());
text
}
}
impl<'a> From<RopeSlice<'a>> for alloc::borrow::Cow<'a, str> {
#[inline]
fn from(s: RopeSlice<'a>) -> Self {
if let Some(text) = s.as_str() {
alloc::borrow::Cow::Borrowed(text)
} else {
alloc::borrow::Cow::Owned(String::from(s))
}
}
}
impl<'a> core::fmt::Debug for RopeSlice<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_list().entries(self.chunks()).finish()
}
}
impl<'a> core::fmt::Display for RopeSlice<'a> {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
for chunk in self.chunks() {
write!(f, "{}", chunk)?
}
Ok(())
}
}
impl<'a> core::cmp::Eq for RopeSlice<'a> {}
impl<'a, 'b> core::cmp::PartialEq<RopeSlice<'b>> for RopeSlice<'a> {
fn eq(&self, other: &RopeSlice<'b>) -> bool {
if self.len_bytes() != other.len_bytes() {
return false;
}
let mut chunk_itr_1 = self.chunks();
let mut chunk_itr_2 = other.chunks();
let mut chunk1 = chunk_itr_1.next().unwrap_or("");
let mut chunk2 = chunk_itr_2.next().unwrap_or("");
loop {
if chunk1.len() > chunk2.len() {
if &chunk1[..chunk2.len()] != chunk2 {
return false;
} else {
chunk1 = &chunk1[chunk2.len()..];
chunk2 = "";
}
} else if &chunk2[..chunk1.len()] != chunk1 {
return false;
} else {
chunk2 = &chunk2[chunk1.len()..];
chunk1 = "";
}
if chunk1.is_empty() {
if let Some(chunk) = chunk_itr_1.next() {
chunk1 = chunk;
} else {
break;
}
}
if chunk2.is_empty() {
if let Some(chunk) = chunk_itr_2.next() {
chunk2 = chunk;
} else {
break;
}
}
}
return true;
}
}
impl<'a, 'b> core::cmp::PartialEq<&'b str> for RopeSlice<'a> {
#[inline]
fn eq(&self, other: &&'b str) -> bool {
match *self {
RopeSlice(RSEnum::Full { .. }) => {
if self.len_bytes() != other.len() {
return false;
}
let mut idx = 0;
for chunk in self.chunks() {
if chunk != &other[idx..(idx + chunk.len())] {
return false;
}
idx += chunk.len();
}
return true;
}
RopeSlice(RSEnum::Light { text, .. }) => {
return text == *other;
}
}
}
}
impl<'a, 'b> core::cmp::PartialEq<RopeSlice<'a>> for &'b str {
#[inline]
fn eq(&self, other: &RopeSlice<'a>) -> bool {
other == self
}
}
impl<'a> core::cmp::PartialEq<str> for RopeSlice<'a> {
#[inline]
fn eq(&self, other: &str) -> bool {
core::cmp::PartialEq::<&str>::eq(self, &other)
}
}
impl<'a> core::cmp::PartialEq<RopeSlice<'a>> for str {
#[inline]
fn eq(&self, other: &RopeSlice<'a>) -> bool {
core::cmp::PartialEq::<&str>::eq(other, &self)
}
}
impl<'a> core::cmp::PartialEq<String> for RopeSlice<'a> {
#[inline]
fn eq(&self, other: &String) -> bool {
self == other.as_str()
}
}
impl<'a> core::cmp::PartialEq<RopeSlice<'a>> for String {
#[inline]
fn eq(&self, other: &RopeSlice<'a>) -> bool {
self.as_str() == other
}
}
impl<'a, 'b> core::cmp::PartialEq<alloc::borrow::Cow<'b, str>> for RopeSlice<'a> {
#[inline]
fn eq(&self, other: &alloc::borrow::Cow<'b, str>) -> bool {
*self == **other
}
}
impl<'a, 'b> core::cmp::PartialEq<RopeSlice<'a>> for alloc::borrow::Cow<'b, str> {
#[inline]
fn eq(&self, other: &RopeSlice<'a>) -> bool {
**self == *other
}
}
impl<'a> core::cmp::PartialEq<Rope> for RopeSlice<'a> {
#[inline]
fn eq(&self, other: &Rope) -> bool {
*self == other.slice(..)
}
}
impl<'a> core::cmp::PartialEq<RopeSlice<'a>> for Rope {
#[inline]
fn eq(&self, other: &RopeSlice<'a>) -> bool {
self.slice(..) == *other
}
}
impl<'a> core::cmp::Ord for RopeSlice<'a> {
#[allow(clippy::op_ref)] fn cmp(&self, other: &RopeSlice<'a>) -> core::cmp::Ordering {
let mut chunk_itr_1 = self.chunks();
let mut chunk_itr_2 = other.chunks();
let mut chunk1 = chunk_itr_1.next().unwrap_or("").as_bytes();
let mut chunk2 = chunk_itr_2.next().unwrap_or("").as_bytes();
loop {
if chunk1.len() >= chunk2.len() {
let compared = chunk1[..chunk2.len()].cmp(chunk2);
if compared != core::cmp::Ordering::Equal {
return compared;
}
chunk1 = &chunk1[chunk2.len()..];
chunk2 = &[];
} else {
let compared = chunk1.cmp(&chunk2[..chunk1.len()]);
if compared != core::cmp::Ordering::Equal {
return compared;
}
chunk1 = &[];
chunk2 = &chunk2[chunk1.len()..];
}
if chunk1.is_empty() {
if let Some(chunk) = chunk_itr_1.next() {
chunk1 = chunk.as_bytes();
} else {
break;
}
}
if chunk2.is_empty() {
if let Some(chunk) = chunk_itr_2.next() {
chunk2 = chunk.as_bytes();
} else {
break;
}
}
}
self.len_bytes().cmp(&other.len_bytes())
}
}
impl<'a, 'b> core::cmp::PartialOrd<RopeSlice<'b>> for RopeSlice<'a> {
#[inline]
fn partial_cmp(&self, other: &RopeSlice<'b>) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
#[cfg(test)]
mod tests {
use crate::str_utils::{
byte_to_char_idx, byte_to_line_idx, char_to_byte_idx, char_to_line_idx,
};
use crate::Rope;
use crate::slice::String;
const TEXT: &str = "Hello there! How're you doing? It's \
a fine day, isn't it? Aren't you glad \
we're alive? こんにちは、みんなさん!";
const TEXT_LINES: &str = "Hello there! How're you doing?\nIt's \
a fine day, isn't it?\nAren't you glad \
we're alive?\nこんにちは、みんなさん!";
const TEXT_EMOJI: &str = "Hello there!🐸 How're you doing?🐸 It's \
a fine day, isn't it?🐸 Aren't you glad \
we're alive?🐸 こんにちは、みんなさん!";
#[test]
fn len_bytes_01() {
let r = Rope::from_str(TEXT);
let s = r.slice(7..98);
assert_eq!(s.len_bytes(), 105);
}
#[test]
fn len_bytes_02() {
let r = Rope::from_str(TEXT);
let s = r.slice(43..43);
assert_eq!(s.len_bytes(), 0);
}
#[test]
fn len_chars_01() {
let r = Rope::from_str(TEXT);
let s = r.slice(7..98);
assert_eq!(s.len_chars(), 91);
}
#[test]
fn len_chars_02() {
let r = Rope::from_str(TEXT);
let s = r.slice(43..43);
assert_eq!(s.len_chars(), 0);
}
#[test]
fn len_lines_01() {
let r = Rope::from_str(TEXT_LINES);
let s = r.slice(34..98);
assert_eq!(s.len_lines(), 3);
}
#[test]
fn len_lines_02() {
let r = Rope::from_str(TEXT_LINES);
let s = r.slice(43..43);
assert_eq!(s.len_lines(), 1);
}
#[test]
fn len_utf16_cu_01() {
let r = Rope::from_str(TEXT);
let s = r.slice(..);
assert_eq!(s.len_utf16_cu(), 103);
}
#[test]
fn len_utf16_cu_02() {
let r = Rope::from_str(TEXT_EMOJI);
let s = r.slice(..);
assert_eq!(s.len_utf16_cu(), 111);
}
#[test]
fn len_utf16_cu_03() {
let r = Rope::from_str(TEXT_EMOJI);
let s = r.slice(13..33);
assert_eq!(s.len_utf16_cu(), 21);
}
#[test]
fn len_utf16_cu_04() {
let r = Rope::from_str("🐸");
let s = r.slice(..);
assert_eq!(s.len_utf16_cu(), 2);
}
#[test]
fn len_utf16_cu_05() {
let r = Rope::from_str("");
let s = r.slice(..);
assert_eq!(s.len_utf16_cu(), 0);
}
#[test]
fn byte_to_char_01() {
let r = Rope::from_str(TEXT);
let s = r.slice(88..102);
assert_eq!(0, s.byte_to_char(0));
assert_eq!(1, s.byte_to_char(1));
assert_eq!(2, s.byte_to_char(2));
assert_eq!(3, s.byte_to_char(3));
assert_eq!(3, s.byte_to_char(4));
assert_eq!(3, s.byte_to_char(5));
assert_eq!(4, s.byte_to_char(6));
assert_eq!(4, s.byte_to_char(7));
assert_eq!(4, s.byte_to_char(8));
assert_eq!(13, s.byte_to_char(33));
assert_eq!(13, s.byte_to_char(34));
assert_eq!(13, s.byte_to_char(35));
assert_eq!(14, s.byte_to_char(36));
}
#[test]
fn byte_to_line_01() {
let r = Rope::from_str(TEXT_LINES);
let s = r.slice(34..96);
assert_eq!(0, s.byte_to_line(0));
assert_eq!(0, s.byte_to_line(1));
assert_eq!(0, s.byte_to_line(24));
assert_eq!(1, s.byte_to_line(25));
assert_eq!(1, s.byte_to_line(26));
assert_eq!(1, s.byte_to_line(53));
assert_eq!(2, s.byte_to_line(54));
assert_eq!(2, s.byte_to_line(57));
assert_eq!(2, s.byte_to_line(78));
}
#[test]
fn byte_to_line_02() {
let r = Rope::from_str(TEXT_LINES);
let s = r.slice(50..50);
assert_eq!(0, s.byte_to_line(0));
}
#[test]
fn byte_to_line_03() {
let r = Rope::from_str("Hi there\nstranger!");
let s = r.slice(0..9);
assert_eq!(0, s.byte_to_line(0));
assert_eq!(0, s.byte_to_line(8));
assert_eq!(1, s.byte_to_line(9));
}
#[test]
#[should_panic]
fn byte_to_line_04() {
let r = Rope::from_str(TEXT_LINES);
let s = r.slice(34..96);
s.byte_to_line(79);
}
#[test]
fn char_to_byte_01() {
let r = Rope::from_str(TEXT);
let s = r.slice(88..102);
assert_eq!(0, s.char_to_byte(0));
assert_eq!(1, s.char_to_byte(1));
assert_eq!(2, s.char_to_byte(2));
assert_eq!(3, s.char_to_byte(3));
assert_eq!(6, s.char_to_byte(4));
assert_eq!(33, s.char_to_byte(13));
assert_eq!(36, s.char_to_byte(14));
}
#[test]
fn char_to_line_01() {
let r = Rope::from_str(TEXT_LINES);
let s = r.slice(34..96);
assert_eq!(0, s.char_to_line(0));
assert_eq!(0, s.char_to_line(1));
assert_eq!(0, s.char_to_line(24));
assert_eq!(1, s.char_to_line(25));
assert_eq!(1, s.char_to_line(26));
assert_eq!(1, s.char_to_line(53));
assert_eq!(2, s.char_to_line(54));
assert_eq!(2, s.char_to_line(55));
assert_eq!(2, s.char_to_line(62));
}
#[test]
fn char_to_line_02() {
let r = Rope::from_str(TEXT_LINES);
let s = r.slice(43..43);
assert_eq!(0, s.char_to_line(0));
}
#[test]
fn char_to_line_03() {
let r = Rope::from_str("Hi there\nstranger!");
let s = r.slice(0..9);
assert_eq!(0, s.char_to_line(0));
assert_eq!(0, s.char_to_line(8));
assert_eq!(1, s.char_to_line(9));
}
#[test]
#[should_panic]
fn char_to_line_04() {
let r = Rope::from_str(TEXT_LINES);
let s = r.slice(34..96);
s.char_to_line(63);
}
#[test]
fn line_to_byte_01() {
let r = Rope::from_str(TEXT_LINES);
let s = r.slice(34..96);
assert_eq!(0, s.line_to_byte(0));
assert_eq!(25, s.line_to_byte(1));
assert_eq!(54, s.line_to_byte(2));
assert_eq!(78, s.line_to_byte(3));
}
#[test]
fn line_to_byte_02() {
let r = Rope::from_str(TEXT_LINES);
let s = r.slice(43..43);
assert_eq!(0, s.line_to_byte(0));
assert_eq!(0, s.line_to_byte(1));
}
#[test]
#[should_panic]
fn line_to_byte_03() {
let r = Rope::from_str(TEXT_LINES);
let s = r.slice(34..96);
s.line_to_byte(4);
}
#[test]
fn line_to_char_01() {
let r = Rope::from_str(TEXT_LINES);
let s = r.slice(34..96);
assert_eq!(0, s.line_to_char(0));
assert_eq!(25, s.line_to_char(1));
assert_eq!(54, s.line_to_char(2));
assert_eq!(62, s.line_to_char(3));
}
#[test]
fn line_to_char_02() {
let r = Rope::from_str(TEXT_LINES);
let s = r.slice(43..43);
assert_eq!(0, s.line_to_char(0));
assert_eq!(0, s.line_to_char(1));
}
#[test]
#[should_panic]
fn line_to_char_03() {
let r = Rope::from_str(TEXT_LINES);
let s = r.slice(34..96);
s.line_to_char(4);
}
#[test]
fn char_to_utf16_cu_01() {
let r = Rope::from_str("");
let s = r.slice(..);
assert_eq!(0, s.char_to_utf16_cu(0));
}
#[test]
#[should_panic]
fn char_to_utf16_cu_02() {
let r = Rope::from_str("");
let s = r.slice(..);
s.char_to_utf16_cu(1);
}
#[test]
fn char_to_utf16_cu_03() {
let r = Rope::from_str("🐸");
let s = r.slice(..);
assert_eq!(0, s.char_to_utf16_cu(0));
assert_eq!(2, s.char_to_utf16_cu(1));
}
#[test]
#[should_panic]
fn char_to_utf16_cu_04() {
let r = Rope::from_str("🐸");
let s = r.slice(..);
s.char_to_utf16_cu(2);
}
#[test]
fn char_to_utf16_cu_05() {
let r = Rope::from_str(TEXT_EMOJI);
let s = r.slice(..);
assert_eq!(0, s.char_to_utf16_cu(0));
assert_eq!(12, s.char_to_utf16_cu(12));
assert_eq!(14, s.char_to_utf16_cu(13));
assert_eq!(33, s.char_to_utf16_cu(32));
assert_eq!(35, s.char_to_utf16_cu(33));
assert_eq!(63, s.char_to_utf16_cu(61));
assert_eq!(65, s.char_to_utf16_cu(62));
assert_eq!(95, s.char_to_utf16_cu(92));
assert_eq!(97, s.char_to_utf16_cu(93));
assert_eq!(111, s.char_to_utf16_cu(107));
}
#[test]
#[should_panic]
fn char_to_utf16_cu_06() {
let r = Rope::from_str(TEXT_EMOJI);
let s = r.slice(..);
s.char_to_utf16_cu(108);
}
#[test]
fn char_to_utf16_cu_07() {
let r = Rope::from_str(TEXT_EMOJI);
let s = r.slice(1..106);
assert_eq!(0, s.char_to_utf16_cu(0));
assert_eq!(11, s.char_to_utf16_cu(11));
assert_eq!(13, s.char_to_utf16_cu(12));
assert_eq!(32, s.char_to_utf16_cu(31));
assert_eq!(34, s.char_to_utf16_cu(32));
assert_eq!(62, s.char_to_utf16_cu(60));
assert_eq!(64, s.char_to_utf16_cu(61));
assert_eq!(94, s.char_to_utf16_cu(91));
assert_eq!(96, s.char_to_utf16_cu(92));
assert_eq!(109, s.char_to_utf16_cu(105));
}
#[test]
#[should_panic]
fn char_to_utf16_cu_08() {
let r = Rope::from_str(TEXT_EMOJI);
let s = r.slice(1..106);
s.char_to_utf16_cu(106);
}
#[test]
fn utf16_cu_to_char_01() {
let r = Rope::from_str("");
let s = r.slice(..);
assert_eq!(0, s.utf16_cu_to_char(0));
}
#[test]
#[should_panic]
fn utf16_cu_to_char_02() {
let r = Rope::from_str("");
let s = r.slice(..);
s.utf16_cu_to_char(1);
}
#[test]
fn utf16_cu_to_char_03() {
let r = Rope::from_str("🐸");
let s = r.slice(..);
assert_eq!(0, s.utf16_cu_to_char(0));
assert_eq!(0, s.utf16_cu_to_char(1));
assert_eq!(1, s.utf16_cu_to_char(2));
}
#[test]
#[should_panic]
fn utf16_cu_to_char_04() {
let r = Rope::from_str("🐸");
let s = r.slice(..);
s.utf16_cu_to_char(3);
}
#[test]
fn utf16_cu_to_char_05() {
let r = Rope::from_str(TEXT_EMOJI);
let s = r.slice(..);
assert_eq!(0, s.utf16_cu_to_char(0));
assert_eq!(12, s.utf16_cu_to_char(12));
assert_eq!(12, s.utf16_cu_to_char(13));
assert_eq!(13, s.utf16_cu_to_char(14));
assert_eq!(32, s.utf16_cu_to_char(33));
assert_eq!(32, s.utf16_cu_to_char(34));
assert_eq!(33, s.utf16_cu_to_char(35));
assert_eq!(61, s.utf16_cu_to_char(63));
assert_eq!(61, s.utf16_cu_to_char(64));
assert_eq!(62, s.utf16_cu_to_char(65));
assert_eq!(92, s.utf16_cu_to_char(95));
assert_eq!(92, s.utf16_cu_to_char(96));
assert_eq!(93, s.utf16_cu_to_char(97));
assert_eq!(107, s.utf16_cu_to_char(111));
}
#[test]
#[should_panic]
fn utf16_cu_to_char_06() {
let r = Rope::from_str(TEXT_EMOJI);
let s = r.slice(..);
s.utf16_cu_to_char(112);
}
#[test]
fn utf16_cu_to_char_07() {
let r = Rope::from_str(TEXT_EMOJI);
let s = r.slice(1..106);
assert_eq!(0, s.utf16_cu_to_char(0));
assert_eq!(11, s.utf16_cu_to_char(11));
assert_eq!(11, s.utf16_cu_to_char(12));
assert_eq!(12, s.utf16_cu_to_char(13));
assert_eq!(31, s.utf16_cu_to_char(32));
assert_eq!(31, s.utf16_cu_to_char(33));
assert_eq!(32, s.utf16_cu_to_char(34));
assert_eq!(60, s.utf16_cu_to_char(62));
assert_eq!(60, s.utf16_cu_to_char(63));
assert_eq!(61, s.utf16_cu_to_char(64));
assert_eq!(91, s.utf16_cu_to_char(94));
assert_eq!(91, s.utf16_cu_to_char(95));
assert_eq!(92, s.utf16_cu_to_char(96));
assert_eq!(105, s.utf16_cu_to_char(109));
}
#[test]
#[should_panic]
fn utf16_cu_to_char_08() {
let r = Rope::from_str(TEXT_EMOJI);
let s = r.slice(1..106);
s.utf16_cu_to_char(110);
}
#[test]
fn byte_01() {
let r = Rope::from_str(TEXT);
let s = r.slice(34..100);
assert_eq!(s.byte(0), b't');
assert_eq!(s.byte(10), b' ');
assert_eq!(s.byte(s.len_bytes() - 3), 0xE3);
assert_eq!(s.byte(s.len_bytes() - 2), 0x81);
assert_eq!(s.byte(s.len_bytes() - 1), 0xAA);
}
#[test]
#[should_panic]
fn byte_02() {
let r = Rope::from_str(TEXT);
let s = r.slice(34..100);
s.byte(s.len_bytes());
}
#[test]
#[should_panic]
fn byte_03() {
let r = Rope::from_str(TEXT);
let s = r.slice(42..42);
s.byte(0);
}
#[test]
fn char_01() {
let r = Rope::from_str(TEXT);
let s = r.slice(34..100);
assert_eq!(s.char(0), 't');
assert_eq!(s.char(10), ' ');
assert_eq!(s.char(18), 'n');
assert_eq!(s.char(65), 'な');
}
#[test]
#[should_panic]
fn char_02() {
let r = Rope::from_str(TEXT);
let s = r.slice(34..100);
s.char(66);
}
#[test]
#[should_panic]
fn char_03() {
let r = Rope::from_str(TEXT);
let s = r.slice(43..43);
s.char(0);
}
#[test]
fn line_01() {
let r = Rope::from_str(TEXT_LINES);
let s = r.slice(34..96);
let l0 = s.line(0);
assert_eq!(l0, "'s a fine day, isn't it?\n");
assert_eq!(l0.len_bytes(), 25);
assert_eq!(l0.len_chars(), 25);
assert_eq!(l0.len_lines(), 2);
let l1 = s.line(1);
assert_eq!(l1, "Aren't you glad we're alive?\n");
assert_eq!(l1.len_bytes(), 29);
assert_eq!(l1.len_chars(), 29);
assert_eq!(l1.len_lines(), 2);
let l2 = s.line(2);
assert_eq!(l2, "こんにちは、みん");
assert_eq!(l2.len_bytes(), 24);
assert_eq!(l2.len_chars(), 8);
assert_eq!(l2.len_lines(), 1);
}
#[test]
fn line_02() {
let r = Rope::from_str(TEXT_LINES);
let s = r.slice(34..59);
assert_eq!(s.line(0), "'s a fine day, isn't it?\n");
assert_eq!(s.line(1), "");
}
#[test]
fn line_03() {
let r = Rope::from_str("Hi\nHi\nHi\nHi\nHi\nHi\n");
let s = r.slice(1..17);
assert_eq!(s.line(0), "i\n");
assert_eq!(s.line(1), "Hi\n");
assert_eq!(s.line(2), "Hi\n");
assert_eq!(s.line(3), "Hi\n");
assert_eq!(s.line(4), "Hi\n");
assert_eq!(s.line(5), "Hi");
}
#[test]
fn line_04() {
let r = Rope::from_str(TEXT_LINES);
let s = r.slice(43..43);
assert_eq!(s.line(0), "");
}
#[test]
#[should_panic]
fn line_05() {
let r = Rope::from_str(TEXT_LINES);
let s = r.slice(34..96);
s.line(3);
}
#[test]
fn line_06() {
let r = Rope::from_str("1\n2\n3\n4\n5\n6\n7\n8");
let s = r.slice(1..11);
assert_eq!(s.line(0).len_lines(), 2);
assert_eq!(s.line(1).len_lines(), 2);
assert_eq!(s.line(2).len_lines(), 2);
assert_eq!(s.line(3).len_lines(), 2);
assert_eq!(s.line(4).len_lines(), 2);
assert_eq!(s.line(5).len_lines(), 1);
}
#[test]
fn chunk_at_byte() {
let r = Rope::from_str(TEXT_LINES);
let s = r.slice(34..96);
let text = &TEXT_LINES[34..112];
let mut t = text;
let mut prev_chunk = "";
for i in 0..s.len_bytes() {
let (chunk, b, c, l) = s.chunk_at_byte(i);
assert_eq!(c, byte_to_char_idx(text, b));
assert_eq!(l, byte_to_line_idx(text, b));
if chunk != prev_chunk {
assert_eq!(chunk, &t[..chunk.len()]);
t = &t[chunk.len()..];
prev_chunk = chunk;
}
let c1 = {
let i2 = byte_to_char_idx(text, i);
text.chars().nth(i2).unwrap()
};
let c2 = {
let i2 = i - b;
let i3 = byte_to_char_idx(chunk, i2);
chunk.chars().nth(i3).unwrap()
};
assert_eq!(c1, c2);
}
assert_eq!(t.len(), 0);
}
#[test]
fn chunk_at_char() {
let r = Rope::from_str(TEXT_LINES);
let s = r.slice(34..96);
let text = &TEXT_LINES[34..112];
let mut t = text;
let mut prev_chunk = "";
for i in 0..s.len_chars() {
let (chunk, b, c, l) = s.chunk_at_char(i);
assert_eq!(b, char_to_byte_idx(text, c));
assert_eq!(l, char_to_line_idx(text, c));
if chunk != prev_chunk {
assert_eq!(chunk, &t[..chunk.len()]);
t = &t[chunk.len()..];
prev_chunk = chunk;
}
let c1 = text.chars().nth(i).unwrap();
let c2 = {
let i2 = i - c;
chunk.chars().nth(i2).unwrap()
};
assert_eq!(c1, c2);
}
assert_eq!(t.len(), 0);
}
#[test]
fn chunk_at_line_break() {
let r = Rope::from_str(TEXT_LINES);
let s = r.slice(34..96);
let text = &TEXT_LINES[34..112];
{
let (chunk, b, c, l) = s.chunk_at_line_break(0);
assert_eq!(chunk, &text[..chunk.len()]);
assert_eq!(b, 0);
assert_eq!(c, 0);
assert_eq!(l, 0);
}
for i in 1..s.len_lines() {
let (chunk, b, c, l) = s.chunk_at_line_break(i);
assert_eq!(chunk, &text[b..(b + chunk.len())]);
assert_eq!(c, byte_to_char_idx(text, b));
assert_eq!(l, byte_to_line_idx(text, b));
assert!(l < i);
assert!(i <= byte_to_line_idx(text, b + chunk.len()));
}
{
let (chunk, b, c, l) = s.chunk_at_line_break(s.len_lines());
assert_eq!(chunk, &text[(text.len() - chunk.len())..]);
assert_eq!(chunk, &text[b..]);
assert_eq!(c, byte_to_char_idx(text, b));
assert_eq!(l, byte_to_line_idx(text, b));
}
}
#[test]
fn slice_01() {
let r = Rope::from_str(TEXT);
let s1 = r.slice(..);
let s2 = s1.slice(..);
assert_eq!(TEXT, s2);
}
#[test]
fn slice_02() {
let r = Rope::from_str(TEXT);
let s1 = r.slice(5..43);
let s2 = s1.slice(3..25);
assert_eq!(&TEXT[8..30], s2);
}
#[test]
fn slice_03() {
let r = Rope::from_str(TEXT);
let s1 = r.slice(31..97);
let s2 = s1.slice(7..64);
assert_eq!(&TEXT[38..103], s2);
}
#[test]
fn slice_04() {
let r = Rope::from_str(TEXT);
let s1 = r.slice(5..43);
let s2 = s1.slice(21..21);
assert_eq!("", s2);
}
#[test]
#[should_panic]
fn slice_05() {
let r = Rope::from_str(TEXT);
let s = r.slice(5..43);
s.slice(21..20);
}
#[test]
#[should_panic]
fn slice_06() {
let r = Rope::from_str(TEXT);
let s = r.slice(5..43);
s.slice(37..39);
}
#[test]
fn eq_str_01() {
let r = Rope::from_str(TEXT);
let slice = r.slice(..);
assert_eq!(slice, TEXT);
assert_eq!(TEXT, slice);
}
#[test]
fn eq_str_02() {
let r = Rope::from_str(TEXT);
let slice = r.slice(0..20);
assert_ne!(slice, TEXT);
assert_ne!(TEXT, slice);
}
#[test]
fn eq_str_03() {
let mut r = Rope::from_str(TEXT);
r.remove(20..21);
r.insert(20, "z");
let slice = r.slice(..);
assert_ne!(slice, TEXT);
assert_ne!(TEXT, slice);
}
#[test]
fn eq_str_04() {
let r = Rope::from_str(TEXT);
let slice = r.slice(..);
let s: String = TEXT.into();
assert_eq!(slice, s);
assert_eq!(s, slice);
}
#[test]
fn eq_rope_slice_01() {
let r = Rope::from_str(TEXT);
let s = r.slice(43..43);
assert_eq!(s, s);
}
#[test]
fn eq_rope_slice_02() {
let r = Rope::from_str(TEXT);
let s1 = r.slice(43..97);
let s2 = r.slice(43..97);
assert_eq!(s1, s2);
}
#[test]
fn eq_rope_slice_03() {
let r = Rope::from_str(TEXT);
let s1 = r.slice(43..43);
let s2 = r.slice(43..45);
assert_ne!(s1, s2);
}
#[test]
fn eq_rope_slice_04() {
let r = Rope::from_str(TEXT);
let s1 = r.slice(43..45);
let s2 = r.slice(43..43);
assert_ne!(s1, s2);
}
#[test]
fn eq_rope_slice_05() {
let r = Rope::from_str("");
let s = r.slice(0..0);
assert_eq!(s, s);
}
#[test]
fn cmp_rope_slice_01() {
let r1 = Rope::from_str("abcdefghijklmnopqrstuvwxyz");
let r2 = Rope::from_str("abcdefghijklmnopqrstuvwxyz");
let s1 = r1.slice(..);
let s2 = r2.slice(..);
assert_eq!(s1.cmp(&s2), core::cmp::Ordering::Equal);
assert_eq!(s1.slice(..24).cmp(&s2), core::cmp::Ordering::Less);
assert_eq!(s1.cmp(&s2.slice(..24)), core::cmp::Ordering::Greater);
}
#[test]
fn cmp_rope_slice_02() {
let r1 = Rope::from_str("abcdefghijklmnzpqrstuvwxyz");
let r2 = Rope::from_str("abcdefghijklmnopqrstuvwxyz");
let s1 = r1.slice(..);
let s2 = r2.slice(..);
assert_eq!(s1.cmp(&s2), core::cmp::Ordering::Greater);
assert_eq!(s2.cmp(&s1), core::cmp::Ordering::Less);
}
#[test]
fn to_string_01() {
let r = Rope::from_str(TEXT);
let slc = r.slice(..);
let s: String = slc.into();
assert_eq!(r, s);
assert_eq!(slc, s);
}
#[test]
fn to_string_02() {
let r = Rope::from_str(TEXT);
let slc = r.slice(0..24);
let s: String = slc.into();
assert_eq!(slc, s);
}
#[test]
fn to_string_03() {
let r = Rope::from_str(TEXT);
let slc = r.slice(13..89);
let s: String = slc.into();
assert_eq!(slc, s);
}
#[test]
fn to_string_04() {
let r = Rope::from_str(TEXT);
let slc = r.slice(13..41);
let s: String = slc.into();
assert_eq!(slc, s);
}
#[test]
fn to_cow_01() {
use alloc::borrow::Cow;
let r = Rope::from_str(TEXT);
let s = r.slice(13..83);
let cow: Cow<str> = s.into();
assert_eq!(s, cow);
}
#[test]
fn to_cow_02() {
use alloc::borrow::Cow;
let r = Rope::from_str(TEXT);
let s = r.slice(13..14);
let cow: Cow<str> = r.slice(13..14).into();
if let Cow::Owned(_) = cow {
panic!("Small Cow conversions should result in a borrow.");
}
assert_eq!(s, cow);
}
}