Skip to content

Commit

Permalink
Pull undo redo to editor (#129)
Browse files Browse the repository at this point in the history
* Pull undo/redo functionality out from line_buffer to editor

* Update editor.rs

Co-authored-by: JT <547158+jntrnr@users.noreply.github.com>
  • Loading branch information
nixypanda and sophiajt authored Aug 12, 2021
1 parent f490dd4 commit d8684f5
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 73 deletions.
64 changes: 54 additions & 10 deletions src/core_editor/editor.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
use crate::core_editor::get_default_clipboard;

use super::{Clipboard, LineBuffer};

pub struct Editor {
line_buffer: LineBuffer,
cut_buffer: Box<dyn Clipboard>,

edits: Vec<LineBuffer>,
index_undo: usize,
}

impl Editor {
pub fn new(line_buffer: LineBuffer, clip_buffer: Box<dyn Clipboard>) -> Editor {
impl Default for Editor {
fn default() -> Self {
Editor {
line_buffer,
cut_buffer: clip_buffer,
line_buffer: LineBuffer::new(),
cut_buffer: Box::new(get_default_clipboard()),

// Note: Using list-zipper we can reduce these to one field
edits: vec![],
index_undo: 2,
}
}
}

impl Editor {
pub fn line_buffer(&mut self) -> &mut LineBuffer {
&mut self.line_buffer
}
Expand Down Expand Up @@ -128,20 +139,53 @@ impl Editor {
self.line_buffer.is_empty()
}

pub fn undo(&mut self) -> Option<()> {
self.line_buffer.undo()
pub fn reset_olds(&mut self) {
self.edits = vec![LineBuffer::new()];
self.index_undo = 2;
}

fn get_index_undo(&self) -> usize {
if let Some(c) = self.edits.len().checked_sub(self.index_undo) {
c
} else {
0
}
}

pub fn redo(&mut self) -> Option<()> {
self.line_buffer.redo()
if self.index_undo > 2 {
self.index_undo = self.index_undo.checked_sub(2)?;
self.undo()
} else {
None
}
}

pub fn reset_olds(&mut self) {
self.line_buffer.reset_olds()
pub fn undo(&mut self) -> Option<()> {
self.line_buffer = self.edits.get(self.get_index_undo())?.clone();

if self.index_undo <= self.edits.len() {
self.index_undo = self.index_undo.checked_add(1)?;
}
Some(())
}

pub fn set_previous_lines(&mut self, is_after_action: bool) -> Option<()> {
self.line_buffer.set_previous_lines(is_after_action)
self.reset_index_undo();

if self.edits.len() > 1
&& self.edits.last()?.word_count() == self.line_buffer.word_count()
&& !is_after_action
{
self.edits.pop();
}
self.edits.push(self.line_buffer.clone());

Some(())
}

pub fn reset_index_undo(&mut self) {
self.index_undo = 2;
}

pub fn cut_from_start(&mut self) {
Expand Down
78 changes: 20 additions & 58 deletions src/core_editor/line_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ impl Default for InsertionPoint {
/// In memory representation of the entered line(s) to facilitate cursor based editing.
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct LineBuffer {
old_lines: Vec<Vec<String>>,
old_insertion_point: Vec<InsertionPoint>,
index_undo: usize,
lines: Vec<String>,
insertion_point: InsertionPoint,
}
Expand All @@ -38,66 +35,11 @@ impl Default for LineBuffer {
impl LineBuffer {
pub fn new() -> LineBuffer {
LineBuffer {
old_lines: vec![vec![String::new()]],
old_insertion_point: vec![InsertionPoint::new()],
index_undo: 2,
lines: vec![String::new()],
insertion_point: InsertionPoint::new(),
}
}

pub fn reset_olds(&mut self) {
self.old_lines = vec![vec![String::new()]];
self.old_insertion_point = vec![InsertionPoint::new()];
self.index_undo = 2;
}

fn get_index_undo(&self) -> usize {
if let Some(c) = self.old_lines.len().checked_sub(self.index_undo) {
c
} else {
0
}
}

pub fn redo(&mut self) -> Option<()> {
if self.index_undo > 2 {
self.index_undo = self.index_undo.checked_sub(2)?;
self.undo()
} else {
None
}
}

pub fn undo(&mut self) -> Option<()> {
self.lines = self.old_lines.get(self.get_index_undo())?.clone();
let insertion_point = *self.old_insertion_point.get(self.get_index_undo())?;
self.set_insertion_point(insertion_point.line, insertion_point.offset);
if self.index_undo <= self.old_lines.len() {
self.index_undo = self.index_undo.checked_add(1)?;
}
Some(())
}

pub fn set_previous_lines(&mut self, is_after_action: bool) -> Option<()> {
self.reset_index_undo();
if self.old_lines.len() > 1
&& self.old_lines.last()?.concat().trim().split(' ').count()
== self.lines.concat().trim().split(' ').count()
&& !is_after_action
{
self.old_lines.pop();
self.old_insertion_point.pop();
}
self.old_lines.push(self.lines.clone());
self.old_insertion_point.push(self.insertion_point());
Some(())
}

pub fn reset_index_undo(&mut self) {
self.index_undo = 2;
}

/// Replaces the content between [`start`..`end`] with `text`
pub fn replace(&mut self, range: Range<usize>, line_num: usize, text: &str) {
self.lines[line_num].replace_range(range, text);
Expand Down Expand Up @@ -298,6 +240,10 @@ impl LineBuffer {
}
}

pub fn word_count(&self) -> usize {
self.lines.concat().trim().split_whitespace().count()
}

pub fn capitalize_char(&mut self) {
if self.on_whitespace() {
self.move_word_right();
Expand Down Expand Up @@ -592,6 +538,22 @@ mod test {

assert_eq!(expected_line_buffer, line_buffer);
}

#[test]
fn word_count_works() {
let line_buffer1 = buffer_with("This is a te");
let line_buffer2 = buffer_with("This is a test");

assert_eq!(4, line_buffer1.word_count());
assert_eq!(4, line_buffer2.word_count());
}

#[test]
fn word_count_works_with_multiple_spaces() {
let line_buffer = buffer_with("This is a test");

assert_eq!(4, line_buffer.word_count());
}
}

#[test]
Expand Down
9 changes: 4 additions & 5 deletions src/engine.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
use crate::{core_editor::LineBuffer, text_manipulation};

use {
crate::{
completer::{ComplationActionHandler, DefaultCompletionActionHandler},
core_editor::{get_default_clipboard, Editor},
core_editor::Editor,
default_emacs_keybindings,
hinter::{DefaultHinter, Hinter},
history::{FileBackedHistory, History, HistoryNavigationQuery},
keybindings::{default_vi_insert_keybindings, default_vi_normal_keybindings, Keybindings},
painter::Painter,
prompt::{PromptEditMode, PromptHistorySearch, PromptHistorySearchStatus, PromptViMode},
DefaultHighlighter, EditCommand, EditMode, Highlighter, Prompt, Signal, ViEngine,
text_manipulation, DefaultHighlighter, EditCommand, EditMode, Highlighter, Prompt, Signal,
ViEngine,
},
crossterm::{
cursor::position,
Expand Down Expand Up @@ -92,7 +91,7 @@ impl Reedline {
keybindings_hashmap.insert(EditMode::ViNormal, default_vi_normal_keybindings());

Reedline {
editor: Editor::new(LineBuffer::new(), Box::new(get_default_clipboard())),
editor: Editor::default(),
history,
input_mode: InputMode::Regular,
painter,
Expand Down

0 comments on commit d8684f5

Please sign in to comment.