use ropey::Rope;
#[derive(Clone)]
pub(crate) struct TextBuffer {
rope: Rope,
}
impl TextBuffer {
pub(crate) fn new() -> Self {
Self { rope: Rope::new() }
}
pub(crate) fn from_text(text: &str) -> Self {
Self {
rope: Rope::from_str(text),
}
}
pub(crate) fn len_chars(&self) -> usize {
self.rope.len_chars()
}
pub(crate) fn len_bytes(&self) -> usize {
self.rope.len_bytes()
}
pub(crate) fn line_count(&self) -> usize {
self.rope.len_lines()
}
pub(crate) fn insert(&mut self, char_offset: usize, text: &str) {
let char_offset = char_offset.min(self.rope.len_chars());
self.rope.insert(char_offset, text);
}
pub(crate) fn delete(&mut self, start_char: usize, len_chars: usize) {
let start_char = start_char.min(self.rope.len_chars());
let end_char = start_char
.saturating_add(len_chars)
.min(self.rope.len_chars());
if start_char < end_char {
self.rope.remove(start_char..end_char);
}
}
pub(crate) fn get_text(&self) -> String {
self.rope.to_string()
}
pub(crate) fn get_range(&self, start_char: usize, len_chars: usize) -> String {
let start_char = start_char.min(self.rope.len_chars());
let end_char = start_char
.saturating_add(len_chars)
.min(self.rope.len_chars());
self.rope.slice(start_char..end_char).to_string()
}
pub(crate) fn get_line_text(&self, line_number: usize) -> Option<String> {
if line_number >= self.rope.len_lines() {
return None;
}
let mut text = self.rope.line(line_number).to_string();
if text.ends_with('\n') {
text.pop();
}
Some(text)
}
pub(crate) fn position_to_char_offset(&self, line: usize, column: usize) -> usize {
if line >= self.rope.len_lines() {
return self.rope.len_chars();
}
let line_start_char = self.rope.line_to_char(line);
let line_len = if line + 1 < self.rope.len_lines() {
self.rope.line_to_char(line + 1) - line_start_char - 1
} else {
self.rope.len_chars() - line_start_char
};
line_start_char + column.min(line_len)
}
pub(crate) fn char_offset_to_position(&self, char_offset: usize) -> (usize, usize) {
let char_offset = char_offset.min(self.rope.len_chars());
let line_idx = self.rope.char_to_line(char_offset);
let line_start_char = self.rope.line_to_char(line_idx);
(line_idx, char_offset - line_start_char)
}
pub(crate) fn char_offset_to_byte_offset(&self, char_offset: usize) -> usize {
let char_offset = char_offset.min(self.rope.len_chars());
self.rope.char_to_byte(char_offset)
}
pub(crate) fn byte_offset_to_char_offset(&self, byte_offset: usize) -> usize {
let byte_offset = byte_offset.min(self.rope.len_bytes());
self.rope.byte_to_char(byte_offset)
}
pub(crate) fn char_at(&self, char_offset: usize) -> Option<char> {
if char_offset >= self.rope.len_chars() {
None
} else {
Some(self.rope.char(char_offset))
}
}
pub(crate) fn line_to_char(&self, line: usize) -> usize {
self.rope.line_to_char(line)
}
pub(crate) fn line_to_byte(&self, line: usize) -> usize {
self.rope.line_to_byte(line)
}
pub(crate) fn char_to_line(&self, char_offset: usize) -> usize {
self.rope.char_to_line(char_offset)
}
}
impl Default for TextBuffer {
fn default() -> Self {
Self::new()
}
}