use crate::buffer::operation::Operation;
use crate::buffer::{Buffer, Position, Range};
use std::clone::Clone;
use std::convert::Into;
use unicode_segmentation::UnicodeSegmentation;
#[derive(Clone)]
pub struct Insert {
content: String,
position: Position,
}
impl Operation for Insert {
fn run(&mut self, buffer: &mut Buffer) {
buffer
.data
.borrow_mut()
.insert(&self.content, &self.position);
if let Some(ref callback) = buffer.change_callback {
callback(self.position)
}
}
fn reverse(&mut self, buffer: &mut Buffer) {
let line_count = self.content.chars().filter(|&c| c == '\n').count() + 1;
let end_line = self.position.line + line_count - 1;
let end_offset = if line_count == 1 {
self.position.offset + self.content.graphemes(true).count()
} else {
match self.content.split('\n').last() {
Some(line) => line.graphemes(true).count(),
None => return,
}
};
let end_position = Position {
line: end_line,
offset: end_offset,
};
let range = Range::new(self.position, end_position);
buffer.data.borrow_mut().delete(&range);
if let Some(ref callback) = buffer.change_callback {
callback(self.position)
}
}
fn clone_operation(&self) -> Box<dyn Operation> {
Box::new(self.clone())
}
}
impl Insert {
pub fn new(content: String, position: Position) -> Insert {
Insert { content, position }
}
}
impl Buffer {
pub fn insert<T: Into<String>>(&mut self, data: T) {
let mut op = Insert::new(data.into(), self.cursor.position);
op.run(self);
match self.operation_group {
Some(ref mut group) => group.add(Box::new(op)),
None => self.history.add(Box::new(op)),
};
}
}
#[cfg(test)]
mod tests {
use super::Insert;
use crate::buffer::operation::Operation;
use crate::buffer::position::Position;
use crate::buffer::Buffer;
use std::cell::RefCell;
use std::rc::Rc;
#[test]
fn run_and_reverse_add_and_remove_content_without_newlines_at_cursor_position() {
let mut buffer = Buffer::new();
buffer.insert("something");
let insert_position = Position { line: 0, offset: 9 };
let mut insert_operation = Insert::new(" else".to_string(), insert_position);
insert_operation.run(&mut buffer);
assert_eq!(buffer.data(), "something else");
insert_operation.reverse(&mut buffer);
assert_eq!(buffer.data(), "something");
}
#[test]
fn run_and_reverse_add_and_remove_content_with_newlines_at_cursor_position() {
let mut buffer = Buffer::new();
buffer.insert("\n something");
let insert_position = Position {
line: 1,
offset: 10,
};
let mut insert_operation = Insert::new("\n else\n entirely".to_string(), insert_position);
insert_operation.run(&mut buffer);
assert_eq!(buffer.data(), "\n something\n else\n entirely");
insert_operation.reverse(&mut buffer);
assert_eq!(buffer.data(), "\n something");
}
#[test]
fn reverse_removes_a_newline() {
let mut buffer = Buffer::new();
let mut insert_operation = Insert::new("\n".to_string(), Position { line: 0, offset: 0 });
insert_operation.run(&mut buffer);
assert_eq!(buffer.data(), "\n");
insert_operation.reverse(&mut buffer);
assert_eq!(buffer.data(), "");
}
#[test]
fn reverse_correctly_removes_line_ranges() {
let mut buffer = Buffer::new();
buffer.insert("scribe\nlibrary\n");
let mut insert_operation =
Insert::new("editor\n".to_string(), Position { line: 1, offset: 0 });
insert_operation.run(&mut buffer);
assert_eq!(buffer.data(), "scribe\neditor\nlibrary\n");
insert_operation.reverse(&mut buffer);
assert_eq!(buffer.data(), "scribe\nlibrary\n");
}
#[test]
fn reverse_correctly_removes_single_line_content_with_graphemes() {
let mut buffer = Buffer::new();
buffer.insert("scribe\nlibrary");
let mut insert_operation =
Insert::new("नी editor ".to_string(), Position { line: 1, offset: 0 });
insert_operation.run(&mut buffer);
assert_eq!(buffer.data(), "scribe\nनी editor library");
insert_operation.reverse(&mut buffer);
assert_eq!(buffer.data(), "scribe\nlibrary");
}
#[test]
fn reverse_correctly_removes_multi_line_content_with_graphemes() {
let mut buffer = Buffer::new();
buffer.insert("scribe\nlibrary");
let mut insert_operation =
Insert::new("\nनी editor".to_string(), Position { line: 0, offset: 6 });
insert_operation.run(&mut buffer);
assert_eq!(buffer.data(), "scribe\nनी editor\nlibrary");
insert_operation.reverse(&mut buffer);
assert_eq!(buffer.data(), "scribe\nlibrary");
}
#[test]
fn run_calls_change_callback_with_position() {
let mut buffer = Buffer::new();
buffer.insert("something");
let insert_position = Position { line: 0, offset: 9 };
let tracked_position = Rc::new(RefCell::new(Position::new()));
let callback_position = tracked_position.clone();
buffer.change_callback = Some(Box::new(move |change_position| {
*callback_position.borrow_mut() = change_position
}));
let mut insert_operation = Insert::new(" else".to_string(), insert_position);
insert_operation.run(&mut buffer);
assert_eq!(*tracked_position.borrow(), Position { line: 0, offset: 9 });
}
#[test]
fn reverse_calls_change_callback_with_position() {
let mut buffer = Buffer::new();
buffer.insert("something");
let insert_position = Position { line: 0, offset: 9 };
let mut insert_operation = Insert::new(" else".to_string(), insert_position);
insert_operation.run(&mut buffer);
let tracked_position = Rc::new(RefCell::new(Position::new()));
let callback_position = tracked_position.clone();
buffer.change_callback = Some(Box::new(move |change_position| {
*callback_position.borrow_mut() = change_position
}));
insert_operation.reverse(&mut buffer);
assert_eq!(*tracked_position.borrow(), Position { line: 0, offset: 9 });
}
}