use ropey::{iter::Chunks, str_utils::byte_to_char_idx, Rope, RopeSlice};
use std::ops::RangeBounds;
use unicode_segmentation::{GraphemeCursor, GraphemeIncomplete};
use super::{ByteIndex, CharIndex, LineIndex, TextStorage, TextStorageMut};
impl<'a> TextStorage<'a> for Rope {
type Slice = RopeSlice<'a>;
type GraphemeIterator = RopeGraphemes<'a>;
fn len_bytes(&self) -> ByteIndex {
self.len_bytes().into()
}
fn len_chars(&self) -> CharIndex {
self.len_chars().into()
}
fn len_lines(&self) -> LineIndex {
self.len_lines().into()
}
fn len_graphemes(&self) -> usize {
grapheme_width(&self.slice(..))
}
fn char_to_line(&self, char_index: CharIndex) -> LineIndex {
self.char_to_line(char_index.into()).into()
}
fn line_to_char(&self, line_index: LineIndex) -> CharIndex {
self.line_to_char(line_index.into()).into()
}
fn char(&self, char_index: CharIndex) -> char {
Rope::char(self, char_index.0)
}
fn graphemes(&'a self) -> Self::GraphemeIterator {
RopeGraphemes::new(&self.slice(..))
}
fn line(&'a self, line_index: LineIndex) -> Self::Slice {
self.line(line_index.into())
}
fn slice(&'a self, range: impl RangeBounds<usize>) -> Self::Slice {
Rope::slice(self, range)
}
fn prev_grapheme_boundary(&self, char_index: CharIndex) -> CharIndex {
prev_grapheme_boundary(&self.slice(..), char_index)
}
fn next_grapheme_boundary(&self, char_index: CharIndex) -> CharIndex {
next_grapheme_boundary(&self.slice(..), char_index)
}
}
impl<'a> TextStorageMut<'a> for Rope {
fn insert_char(&mut self, char_index: CharIndex, character: char) {
Rope::insert_char(self, char_index.0, character);
}
fn remove(&mut self, range: impl RangeBounds<usize>) {
Rope::remove(self, range);
}
}
impl<'a> TextStorage<'a> for RopeSlice<'a> {
type Slice = RopeSlice<'a>;
type GraphemeIterator = RopeGraphemes<'a>;
fn len_bytes(&self) -> ByteIndex {
self.len_bytes().into()
}
fn len_chars(&self) -> CharIndex {
self.len_chars().into()
}
fn len_lines(&self) -> LineIndex {
self.len_lines().into()
}
fn len_graphemes(&self) -> usize {
grapheme_width(self)
}
fn char_to_line(&self, char_index: CharIndex) -> LineIndex {
self.char_to_line(char_index.into()).into()
}
fn line_to_char(&self, line_index: LineIndex) -> CharIndex {
self.line_to_char(line_index.into()).into()
}
fn char(&self, char_index: CharIndex) -> char {
RopeSlice::char(self, char_index.0)
}
fn graphemes(&self) -> Self::GraphemeIterator {
RopeGraphemes::new(&self.slice(..))
}
fn line(&self, line_index: LineIndex) -> Self::Slice {
self.line(line_index.into())
}
fn slice(&self, range: impl RangeBounds<usize>) -> Self::Slice {
RopeSlice::slice(self, range)
}
fn prev_grapheme_boundary(&self, char_index: CharIndex) -> CharIndex {
prev_grapheme_boundary(self, char_index)
}
fn next_grapheme_boundary(&self, char_index: CharIndex) -> CharIndex {
next_grapheme_boundary(self, char_index)
}
}
fn prev_grapheme_boundary(slice: &RopeSlice, char_index: CharIndex) -> CharIndex {
debug_assert!(char_index.0 <= slice.len_chars());
let byte_index = slice.char_to_byte(char_index.0);
let (mut chunk, mut chunk_byte_index, mut chunk_char_index, _) =
slice.chunk_at_byte(byte_index);
let mut gc = GraphemeCursor::new(byte_index, slice.len_bytes(), true);
loop {
match gc.prev_boundary(chunk, chunk_byte_index) {
Ok(None) => return CharIndex(0),
Ok(Some(n)) => {
let tmp = byte_to_char_idx(chunk, n - chunk_byte_index);
return CharIndex(chunk_char_index + tmp);
}
Err(GraphemeIncomplete::PrevChunk) => {
let (a, b, c, _) = slice.chunk_at_byte(chunk_byte_index - 1);
chunk = a;
chunk_byte_index = b;
chunk_char_index = c;
}
Err(GraphemeIncomplete::PreContext(n)) => {
let ctx_chunk = slice.chunk_at_byte(n - 1).0;
gc.provide_context(ctx_chunk, n - ctx_chunk.len());
}
_ => unreachable!(),
}
}
}
fn next_grapheme_boundary(slice: &RopeSlice, char_index: CharIndex) -> CharIndex {
debug_assert!(char_index.0 <= slice.len_chars());
let byte_index = slice.char_to_byte(char_index.0);
let (mut chunk, mut chunk_byte_index, mut chunk_char_index, _) =
slice.chunk_at_byte(byte_index);
let mut cursor = GraphemeCursor::new(byte_index, slice.len_bytes(), true);
loop {
match cursor.next_boundary(chunk, chunk_byte_index) {
Ok(None) => return CharIndex(slice.len_chars()),
Ok(Some(n)) => {
let tmp = byte_to_char_idx(chunk, n - chunk_byte_index);
return CharIndex(chunk_char_index + tmp);
}
Err(GraphemeIncomplete::NextChunk) => {
chunk_byte_index += chunk.len();
let (a, _, c, _) = slice.chunk_at_byte(chunk_byte_index);
chunk = a;
chunk_char_index = c;
}
Err(GraphemeIncomplete::PreContext(n)) => {
let ctx_chunk = slice.chunk_at_byte(n - 1).0;
cursor.provide_context(ctx_chunk, n - ctx_chunk.len());
}
_ => unreachable!(),
}
}
}
pub struct RopeGraphemes<'a> {
text: RopeSlice<'a>,
chunks: Chunks<'a>,
cur_chunk: &'a str,
cur_chunk_start: usize,
cursor: GraphemeCursor,
}
impl<'a> RopeGraphemes<'a> {
pub fn new<'b>(slice: &RopeSlice<'b>) -> RopeGraphemes<'b> {
let mut chunks = slice.chunks();
let first_chunk = chunks.next().unwrap_or("");
RopeGraphemes {
text: *slice,
chunks,
cur_chunk: first_chunk,
cur_chunk_start: 0,
cursor: GraphemeCursor::new(0, slice.len_bytes(), true),
}
}
}
impl<'a> Iterator for RopeGraphemes<'a> {
type Item = RopeSlice<'a>;
fn next(&mut self) -> Option<RopeSlice<'a>> {
let a = self.cursor.cur_cursor();
let b;
loop {
match self
.cursor
.next_boundary(self.cur_chunk, self.cur_chunk_start)
{
Ok(None) => {
return None;
}
Ok(Some(n)) => {
b = n;
break;
}
Err(GraphemeIncomplete::NextChunk) => {
self.cur_chunk_start += self.cur_chunk.len();
self.cur_chunk = self.chunks.next().unwrap_or("");
}
error => {
eprintln!("{:?}", error);
unreachable!();
}
}
}
if a < self.cur_chunk_start {
let a_char = self.text.byte_to_char(a);
let b_char = self.text.byte_to_char(b);
Some(self.text.slice(a_char..b_char))
} else {
let a2 = a - self.cur_chunk_start;
let b2 = b - self.cur_chunk_start;
Some((&self.cur_chunk[a2..b2]).into())
}
}
}
fn grapheme_width(slice: &RopeSlice) -> usize {
RopeGraphemes::new(slice).count()
}