use std::ops::{Deref, Range};
use super::line_index::{LineIndex, LineStarts};
#[derive(Debug, Clone, Default, Eq)]
pub struct TextBuffer {
text: String,
line_starts: LineStarts,
}
impl PartialEq for TextBuffer {
fn eq(&self, other: &Self) -> bool {
self.text == other.text
}
}
impl TextBuffer {
pub fn new(text: String) -> Self {
let line_starts = LineStarts::new(&text);
Self { text, line_starts }
}
pub fn text(&self) -> &str {
&self.text
}
pub fn line_starts(&self) -> &LineStarts {
&self.line_starts
}
pub fn line_index(&self) -> LineIndex<'_> {
LineIndex::with_starts(&self.text, &self.line_starts)
}
pub fn replace_range(&mut self, range: Range<usize>, insert: &str) {
self.line_starts.patch(range.clone(), insert);
self.text.replace_range(range, insert);
self.debug_assert_in_step();
}
pub fn set_text(&mut self, text: String) {
self.text = text;
self.line_starts = LineStarts::new(&self.text);
}
pub fn into_string(self) -> String {
self.text
}
fn debug_assert_in_step(&self) {
debug_assert!(
self.line_starts == LineStarts::new(&self.text),
"line table drifted from the buffer"
);
}
}
impl Deref for TextBuffer {
type Target = str;
fn deref(&self) -> &str {
&self.text
}
}
impl PartialEq<str> for TextBuffer {
fn eq(&self, other: &str) -> bool {
self.text == other
}
}
impl PartialEq<TextBuffer> for str {
fn eq(&self, other: &TextBuffer) -> bool {
self == other.text.as_str()
}
}
impl From<String> for TextBuffer {
fn from(text: String) -> Self {
Self::new(text)
}
}
impl From<&str> for TextBuffer {
fn from(text: &str) -> Self {
Self::new(text.to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn an_empty_buffer_has_one_line() {
let buffer = TextBuffer::default();
assert_eq!(buffer.line_index().line_count(), 1);
assert_eq!(&*buffer, "");
}
#[test]
fn edits_keep_the_table_in_step() {
let mut buffer = TextBuffer::from("ab\ncd\nef");
buffer.replace_range(2..2, "\nxy");
assert_eq!(&*buffer, "ab\nxy\ncd\nef");
assert_eq!(buffer.line_starts(), &LineStarts::new(&buffer));
buffer.replace_range(2..9, "");
assert_eq!(&*buffer, "abef");
assert_eq!(buffer.line_starts(), &LineStarts::new(&buffer));
assert_eq!(buffer.line_index().line_count(), 1);
}
#[test]
fn set_text_rescans() {
let mut buffer = TextBuffer::from("one line");
buffer.set_text("two\nlines".to_string());
assert_eq!(buffer.line_index().line_count(), 2);
assert_eq!(buffer.line_starts(), &LineStarts::new(&buffer));
}
}