Added full undo/redo functionality.
This commit is contained in:
parent
e61149e514
commit
6b5b63dba1
|
@ -1,14 +1,17 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use std::mem;
|
||||
use std::collections::DList;
|
||||
|
||||
use self::node::{BufferNode, BufferNodeGraphemeIter, BufferNodeLineIter};
|
||||
|
||||
use self::line::{Line, LineEnding};
|
||||
use self::node::{BufferNode, BufferNodeGraphemeIter, BufferNodeLineIter};
|
||||
use self::undo_stack::{UndoStack};
|
||||
use self::undo_stack::Operation::*;
|
||||
use string_utils::{is_line_ending, grapheme_count};
|
||||
|
||||
pub mod line;
|
||||
mod node;
|
||||
mod undo_stack;
|
||||
|
||||
|
||||
//=============================================================
|
||||
|
@ -19,7 +22,7 @@ mod node;
|
|||
pub struct Buffer {
|
||||
text: BufferNode,
|
||||
pub line_ending_type: LineEnding,
|
||||
undo_stack: DList<Operation>,
|
||||
undo_stack: UndoStack,
|
||||
}
|
||||
|
||||
|
||||
|
@ -28,7 +31,7 @@ impl Buffer {
|
|||
Buffer {
|
||||
text: BufferNode::new(),
|
||||
line_ending_type: LineEnding::LF,
|
||||
undo_stack: DList::new(),
|
||||
undo_stack: UndoStack::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,7 +60,7 @@ impl Buffer {
|
|||
pub fn insert_text(&mut self, text: &str, pos: usize) {
|
||||
self._insert_text(text, pos);
|
||||
|
||||
self.undo_stack.push_back(Operation::InsertText(String::from_str(text), pos));
|
||||
self.undo_stack.push(InsertText(String::from_str(text), pos));
|
||||
}
|
||||
|
||||
fn _insert_text(&mut self, text: &str, pos: usize) {
|
||||
|
@ -72,7 +75,7 @@ impl Buffer {
|
|||
self._remove_text(pos_a, pos_b);
|
||||
|
||||
// Push operation to the undo stack
|
||||
self.undo_stack.push_back(Operation::RemoveText(removed_text, pos_a));
|
||||
self.undo_stack.push(RemoveText(removed_text, pos_a));
|
||||
}
|
||||
|
||||
fn _remove_text(&mut self, pos_a: usize, pos_b: usize) {
|
||||
|
@ -146,19 +149,50 @@ impl Buffer {
|
|||
/// Undoes operations that were pushed to the undo stack, and returns a
|
||||
/// cursor position that the cursor should jump to, if any.
|
||||
pub fn undo(&mut self) -> Option<usize> {
|
||||
if let Some(op) = self.undo_stack.pop_back() {
|
||||
if let Some(op) = self.undo_stack.prev() {
|
||||
match op {
|
||||
Operation::InsertText(ref s, p) => {
|
||||
InsertText(ref s, p) => {
|
||||
let size = grapheme_count(s.as_slice());
|
||||
self._remove_text(p, p+size);
|
||||
return Some(p);
|
||||
},
|
||||
|
||||
Operation::RemoveText(ref s, p) => {
|
||||
RemoveText(ref s, p) => {
|
||||
let size = grapheme_count(s.as_slice());
|
||||
self._insert_text(s.as_slice(), p);
|
||||
return Some(p+size);
|
||||
},
|
||||
|
||||
_ => {
|
||||
return None;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return None;
|
||||
}
|
||||
|
||||
|
||||
/// Redoes the last undone operation, and returns a cursor position that
|
||||
/// the cursor should jump to, if any.
|
||||
pub fn redo(&mut self) -> Option<usize> {
|
||||
if let Some(op) = self.undo_stack.next() {
|
||||
match op {
|
||||
InsertText(ref s, p) => {
|
||||
let size = grapheme_count(s.as_slice());
|
||||
self._insert_text(s.as_slice(), p);
|
||||
return Some(p+size);
|
||||
},
|
||||
|
||||
RemoveText(ref s, p) => {
|
||||
let size = grapheme_count(s.as_slice());
|
||||
self._remove_text(p, p+size);
|
||||
return Some(p);
|
||||
},
|
||||
|
||||
_ => {
|
||||
return None;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -379,25 +413,13 @@ impl<'a> Iterator for BufferLineIter<'a> {
|
|||
|
||||
|
||||
|
||||
//================================================================
|
||||
// Buffer undo structures
|
||||
//================================================================
|
||||
|
||||
enum Operation {
|
||||
InsertText(String, usize),
|
||||
RemoveText(String, usize),
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//================================================================
|
||||
// TESTS
|
||||
//================================================================
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Buffer, Operation, BufferGraphemeIter, BufferLineIter};
|
||||
use super::{Buffer, BufferGraphemeIter, BufferLineIter};
|
||||
|
||||
#[test]
|
||||
fn insert_text() {
|
||||
|
|
55
src/buffer/undo_stack.rs
Normal file
55
src/buffer/undo_stack.rs
Normal file
|
@ -0,0 +1,55 @@
|
|||
use std::collections::DList;
|
||||
|
||||
|
||||
/// A text editing operation
|
||||
#[derive(Clone)]
|
||||
pub enum Operation {
|
||||
InsertText(String, usize),
|
||||
RemoveText(String, usize),
|
||||
MoveText(usize, usize, usize),
|
||||
CompositeOp(Vec<Operation>),
|
||||
}
|
||||
|
||||
|
||||
/// An undo/redo stack of text editing operations
|
||||
pub struct UndoStack {
|
||||
stack_a: DList<Operation>,
|
||||
stack_b: DList<Operation>,
|
||||
}
|
||||
|
||||
impl UndoStack {
|
||||
pub fn new() -> UndoStack {
|
||||
UndoStack {
|
||||
stack_a: DList::new(),
|
||||
stack_b: DList::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn push(&mut self, op: Operation) {
|
||||
self.stack_a.push_back(op);
|
||||
self.stack_b.clear();
|
||||
}
|
||||
|
||||
|
||||
pub fn prev(&mut self) -> Option<Operation> {
|
||||
if let Some(op) = self.stack_a.pop_back() {
|
||||
self.stack_b.push_back(op.clone());
|
||||
return Some(op);
|
||||
}
|
||||
else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn next(&mut self) -> Option<Operation> {
|
||||
if let Some(op) = self.stack_b.pop_back() {
|
||||
self.stack_a.push_back(op.clone());
|
||||
return Some(op);
|
||||
}
|
||||
else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -200,6 +200,17 @@ impl Editor {
|
|||
}
|
||||
|
||||
|
||||
pub fn redo(&mut self) {
|
||||
if let Some(pos) = self.buffer.redo() {
|
||||
self.cursor.range.0 = pos;
|
||||
self.cursor.range.1 = pos;
|
||||
self.cursor.update_vis_start(&(self.buffer), self.tab_width);
|
||||
|
||||
self.move_view_to_cursor();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Moves the editor's view the minimum amount to show the cursor
|
||||
pub fn move_view_to_cursor(&mut self) {
|
||||
let (v, h) = self.buffer.pos_1d_to_closest_vis_2d(self.cursor.range.0, self.tab_width);
|
||||
|
|
|
@ -25,6 +25,7 @@ const K_CTRL_L: u16 = 12;
|
|||
const K_CTRL_O: u16 = 15;
|
||||
const K_CTRL_Q: u16 = 17;
|
||||
const K_CTRL_S: u16 = 19;
|
||||
const K_CTRL_Y: u16 = 25;
|
||||
const K_CTRL_Z: u16 = 26;
|
||||
|
||||
|
||||
|
@ -103,6 +104,10 @@ impl TermUI {
|
|||
self.editor.undo();
|
||||
},
|
||||
|
||||
K_CTRL_Y => {
|
||||
self.editor.redo();
|
||||
},
|
||||
|
||||
K_CTRL_L => {
|
||||
self.go_to_line_ui_loop();
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue
Block a user