use ropey::Rope;
use std::fmt;
use std::ops::Range;
#[derive(Clone)]
pub struct TextBuffer {
rope: Rope,
revision: u64,
}
impl TextBuffer {
pub fn new() -> Self {
Self {
rope: Rope::new(),
revision: 0,
}
}
pub fn from_str(text: &str) -> Self {
Self {
rope: Rope::from_str(text),
revision: 0,
}
}
}
impl Default for TextBuffer {
fn default() -> Self {
Self::new()
}
}
impl TextBuffer {
pub fn text(&self) -> &Rope {
&self.rope
}
pub fn len_bytes(&self) -> usize {
self.rope.len_bytes()
}
pub fn len_chars(&self) -> usize {
self.rope.len_chars()
}
pub fn len_lines(&self) -> usize {
self.rope.len_lines()
}
pub fn line(&self, line_idx: usize) -> ropey::RopeSlice<'_> {
self.rope.line(line_idx)
}
pub fn slice(&self, byte_range: Range<usize>) -> ropey::RopeSlice<'_> {
let start_char = self.rope.byte_to_char(byte_range.start);
let end_char = self.rope.byte_to_char(byte_range.end);
self.rope.slice(start_char..end_char)
}
pub fn revision(&self) -> u64 {
self.revision
}
pub fn is_empty(&self) -> bool {
self.len_chars() == 0
}
}
impl TextBuffer {
pub fn insert(&mut self, byte_offset: usize, text: &str) {
let char_idx = self.rope.byte_to_char(byte_offset);
self.rope.insert(char_idx, text);
self.revision += 1;
}
pub fn delete(&mut self, byte_range: Range<usize>) {
let start_char = self.rope.byte_to_char(byte_range.start);
let end_char = self.rope.byte_to_char(byte_range.end);
self.rope.remove(start_char..end_char);
self.revision += 1;
}
pub fn replace(&mut self, byte_range: Range<usize>, text: &str) {
let start_char = self.rope.byte_to_char(byte_range.start);
let end_char = self.rope.byte_to_char(byte_range.end);
self.rope.remove(start_char..end_char);
self.rope.insert(start_char, text);
self.revision += 1;
}
}
impl fmt::Display for TextBuffer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for chunk in self.rope.chunks() {
f.write_str(chunk)?;
}
Ok(())
}
}
impl fmt::Debug for TextBuffer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TextBuffer")
.field("len_bytes", &self.len_bytes())
.field("len_chars", &self.len_chars())
.field("len_lines", &self.len_lines())
.field("revision", &self.revision)
.finish()
}
}