Basic undo functionality.
This commit is contained in:
parent
0fb338f05b
commit
b837d488a5
|
@ -5,8 +5,6 @@ use std::mem;
|
||||||
use std::str::Graphemes;
|
use std::str::Graphemes;
|
||||||
use string_utils::{grapheme_count, grapheme_pos_to_byte_pos, is_line_ending};
|
use string_utils::{grapheme_count, grapheme_pos_to_byte_pos, is_line_ending};
|
||||||
|
|
||||||
const TAB_WIDTH: usize = 4;
|
|
||||||
|
|
||||||
|
|
||||||
/// Returns the visual width of a grapheme given a starting
|
/// Returns the visual width of a grapheme given a starting
|
||||||
/// position on a line.
|
/// position on a line.
|
||||||
|
@ -690,16 +688,21 @@ impl<'a> Iterator for LineGraphemeVisIter<'a> {
|
||||||
// Line tests
|
// Line tests
|
||||||
//=========================================================================
|
//=========================================================================
|
||||||
|
|
||||||
#[test]
|
mod tests {
|
||||||
fn new_text_line() {
|
use super::{Line, LineEnding, LineGraphemeIter, LineGraphemeVisIter};
|
||||||
|
const TAB_WIDTH: usize = 4;
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new_text_line() {
|
||||||
let tl = Line::new();
|
let tl = Line::new();
|
||||||
|
|
||||||
assert!(tl.text.len() == 0);
|
assert!(tl.text.len() == 0);
|
||||||
assert!(tl.ending == LineEnding::None);
|
assert!(tl.ending == LineEnding::None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn new_text_line_from_str() {
|
fn new_text_line_from_str() {
|
||||||
let tl = Line::new_from_str("Hello!");
|
let tl = Line::new_from_str("Hello!");
|
||||||
|
|
||||||
assert!(tl.text.len() == 6);
|
assert!(tl.text.len() == 6);
|
||||||
|
@ -710,18 +713,18 @@ fn new_text_line_from_str() {
|
||||||
assert!(tl.text[4] == ('o' as u8));
|
assert!(tl.text[4] == ('o' as u8));
|
||||||
assert!(tl.text[5] == ('!' as u8));
|
assert!(tl.text[5] == ('!' as u8));
|
||||||
assert!(tl.ending == LineEnding::None);
|
assert!(tl.ending == LineEnding::None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn new_text_line_from_empty_str() {
|
fn new_text_line_from_empty_str() {
|
||||||
let tl = Line::new_from_str("");
|
let tl = Line::new_from_str("");
|
||||||
|
|
||||||
assert!(tl.text.len() == 0);
|
assert!(tl.text.len() == 0);
|
||||||
assert!(tl.ending == LineEnding::None);
|
assert!(tl.ending == LineEnding::None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn new_text_line_from_str_with_lf() {
|
fn new_text_line_from_str_with_lf() {
|
||||||
let tl = Line::new_from_str("Hello!\n");
|
let tl = Line::new_from_str("Hello!\n");
|
||||||
|
|
||||||
assert!(tl.text.len() == 6);
|
assert!(tl.text.len() == 6);
|
||||||
|
@ -732,10 +735,10 @@ fn new_text_line_from_str_with_lf() {
|
||||||
assert!(tl.text[4] == ('o' as u8));
|
assert!(tl.text[4] == ('o' as u8));
|
||||||
assert!(tl.text[5] == ('!' as u8));
|
assert!(tl.text[5] == ('!' as u8));
|
||||||
assert!(tl.ending == LineEnding::LF);
|
assert!(tl.ending == LineEnding::LF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn new_text_line_from_str_with_crlf() {
|
fn new_text_line_from_str_with_crlf() {
|
||||||
let tl = Line::new_from_str("Hello!\r\n");
|
let tl = Line::new_from_str("Hello!\r\n");
|
||||||
|
|
||||||
assert!(tl.text.len() == 6);
|
assert!(tl.text.len() == 6);
|
||||||
|
@ -746,10 +749,10 @@ fn new_text_line_from_str_with_crlf() {
|
||||||
assert!(tl.text[4] == ('o' as u8));
|
assert!(tl.text[4] == ('o' as u8));
|
||||||
assert!(tl.text[5] == ('!' as u8));
|
assert!(tl.text[5] == ('!' as u8));
|
||||||
assert!(tl.ending == LineEnding::CRLF);
|
assert!(tl.ending == LineEnding::CRLF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn new_text_line_from_str_with_crlf_and_too_long() {
|
fn new_text_line_from_str_with_crlf_and_too_long() {
|
||||||
let tl = Line::new_from_str("Hello!\r\nLa la la la");
|
let tl = Line::new_from_str("Hello!\r\nLa la la la");
|
||||||
|
|
||||||
assert!(tl.text.len() == 6);
|
assert!(tl.text.len() == 6);
|
||||||
|
@ -760,10 +763,10 @@ fn new_text_line_from_str_with_crlf_and_too_long() {
|
||||||
assert!(tl.text[4] == ('o' as u8));
|
assert!(tl.text[4] == ('o' as u8));
|
||||||
assert!(tl.text[5] == ('!' as u8));
|
assert!(tl.text[5] == ('!' as u8));
|
||||||
assert!(tl.ending == LineEnding::CRLF);
|
assert!(tl.ending == LineEnding::CRLF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn new_text_line_from_string_unchecked() {
|
fn new_text_line_from_string_unchecked() {
|
||||||
let s = String::from_str("Hello!");
|
let s = String::from_str("Hello!");
|
||||||
|
|
||||||
let tl = Line::new_from_string_unchecked(s);
|
let tl = Line::new_from_string_unchecked(s);
|
||||||
|
@ -776,10 +779,10 @@ fn new_text_line_from_string_unchecked() {
|
||||||
assert!(tl.text[4] == ('o' as u8));
|
assert!(tl.text[4] == ('o' as u8));
|
||||||
assert!(tl.text[5] == ('!' as u8));
|
assert!(tl.text[5] == ('!' as u8));
|
||||||
assert!(tl.ending == LineEnding::None);
|
assert!(tl.ending == LineEnding::None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn new_text_line_from_string_unchecked_with_lf() {
|
fn new_text_line_from_string_unchecked_with_lf() {
|
||||||
let s = String::from_str("Hello!\u{000A}");
|
let s = String::from_str("Hello!\u{000A}");
|
||||||
|
|
||||||
let tl = Line::new_from_string_unchecked(s);
|
let tl = Line::new_from_string_unchecked(s);
|
||||||
|
@ -792,10 +795,10 @@ fn new_text_line_from_string_unchecked_with_lf() {
|
||||||
assert!(tl.text[4] == ('o' as u8));
|
assert!(tl.text[4] == ('o' as u8));
|
||||||
assert!(tl.text[5] == ('!' as u8));
|
assert!(tl.text[5] == ('!' as u8));
|
||||||
assert!(tl.ending == LineEnding::LF);
|
assert!(tl.ending == LineEnding::LF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn new_text_line_from_string_unchecked_with_crlf() {
|
fn new_text_line_from_string_unchecked_with_crlf() {
|
||||||
let s = String::from_str("Hello!\u{000D}\u{000A}");
|
let s = String::from_str("Hello!\u{000D}\u{000A}");
|
||||||
|
|
||||||
let tl = Line::new_from_string_unchecked(s);
|
let tl = Line::new_from_string_unchecked(s);
|
||||||
|
@ -808,10 +811,10 @@ fn new_text_line_from_string_unchecked_with_crlf() {
|
||||||
assert!(tl.text[4] == ('o' as u8));
|
assert!(tl.text[4] == ('o' as u8));
|
||||||
assert!(tl.text[5] == ('!' as u8));
|
assert!(tl.text[5] == ('!' as u8));
|
||||||
assert!(tl.ending == LineEnding::CRLF);
|
assert!(tl.ending == LineEnding::CRLF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn new_text_line_from_string_unchecked_with_ls() {
|
fn new_text_line_from_string_unchecked_with_ls() {
|
||||||
let s = String::from_str("Hello!\u{2028}");
|
let s = String::from_str("Hello!\u{2028}");
|
||||||
|
|
||||||
let tl = Line::new_from_string_unchecked(s);
|
let tl = Line::new_from_string_unchecked(s);
|
||||||
|
@ -824,10 +827,10 @@ fn new_text_line_from_string_unchecked_with_ls() {
|
||||||
assert!(tl.text[4] == ('o' as u8));
|
assert!(tl.text[4] == ('o' as u8));
|
||||||
assert!(tl.text[5] == ('!' as u8));
|
assert!(tl.text[5] == ('!' as u8));
|
||||||
assert!(tl.ending == LineEnding::LS);
|
assert!(tl.ending == LineEnding::LS);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn text_line_insert_text() {
|
fn text_line_insert_text() {
|
||||||
let mut tl = Line::new_from_str("Hello!\r\n");
|
let mut tl = Line::new_from_str("Hello!\r\n");
|
||||||
|
|
||||||
tl.insert_text(" world", 5);
|
tl.insert_text(" world", 5);
|
||||||
|
@ -846,10 +849,10 @@ fn text_line_insert_text() {
|
||||||
assert!(tl.text[10] == ('d' as u8));
|
assert!(tl.text[10] == ('d' as u8));
|
||||||
assert!(tl.text[11] == ('!' as u8));
|
assert!(tl.text[11] == ('!' as u8));
|
||||||
assert!(tl.ending == LineEnding::CRLF);
|
assert!(tl.ending == LineEnding::CRLF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn text_line_append_text() {
|
fn text_line_append_text() {
|
||||||
let mut tl = Line::new_from_str("Hello\r\n");
|
let mut tl = Line::new_from_str("Hello\r\n");
|
||||||
|
|
||||||
tl.append_text(" world!");
|
tl.append_text(" world!");
|
||||||
|
@ -868,10 +871,10 @@ fn text_line_append_text() {
|
||||||
assert!(tl.text[10] == ('d' as u8));
|
assert!(tl.text[10] == ('d' as u8));
|
||||||
assert!(tl.text[11] == ('!' as u8));
|
assert!(tl.text[11] == ('!' as u8));
|
||||||
assert!(tl.ending == LineEnding::CRLF);
|
assert!(tl.ending == LineEnding::CRLF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn text_line_remove_text() {
|
fn text_line_remove_text() {
|
||||||
let mut tl = Line::new_from_str("Hello world!\r\n");
|
let mut tl = Line::new_from_str("Hello world!\r\n");
|
||||||
|
|
||||||
tl.remove_text(5, 11);
|
tl.remove_text(5, 11);
|
||||||
|
@ -884,10 +887,10 @@ fn text_line_remove_text() {
|
||||||
assert!(tl.text[4] == ('o' as u8));
|
assert!(tl.text[4] == ('o' as u8));
|
||||||
assert!(tl.text[5] == ('!' as u8));
|
assert!(tl.text[5] == ('!' as u8));
|
||||||
assert!(tl.ending == LineEnding::CRLF);
|
assert!(tl.ending == LineEnding::CRLF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn text_line_split() {
|
fn text_line_split() {
|
||||||
let mut tl1 = Line::new_from_str("Hello world!\r\n");
|
let mut tl1 = Line::new_from_str("Hello world!\r\n");
|
||||||
|
|
||||||
let tl2 = tl1.split(LineEnding::LF, 5);
|
let tl2 = tl1.split(LineEnding::LF, 5);
|
||||||
|
@ -909,10 +912,10 @@ fn text_line_split() {
|
||||||
assert!(tl2.text[5] == ('d' as u8));
|
assert!(tl2.text[5] == ('d' as u8));
|
||||||
assert!(tl2.text[6] == ('!' as u8));
|
assert!(tl2.text[6] == ('!' as u8));
|
||||||
assert!(tl2.ending == LineEnding::CRLF);
|
assert!(tl2.ending == LineEnding::CRLF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn text_line_split_beginning() {
|
fn text_line_split_beginning() {
|
||||||
let mut tl1 = Line::new_from_str("Hello!\r\n");
|
let mut tl1 = Line::new_from_str("Hello!\r\n");
|
||||||
|
|
||||||
let tl2 = tl1.split(LineEnding::LF, 0);
|
let tl2 = tl1.split(LineEnding::LF, 0);
|
||||||
|
@ -928,43 +931,43 @@ fn text_line_split_beginning() {
|
||||||
assert!(tl2.text[4] == ('o' as u8));
|
assert!(tl2.text[4] == ('o' as u8));
|
||||||
assert!(tl2.text[5] == ('!' as u8));
|
assert!(tl2.text[5] == ('!' as u8));
|
||||||
assert!(tl2.ending == LineEnding::CRLF);
|
assert!(tl2.ending == LineEnding::CRLF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn grapheme_index_to_closest_vis_pos_1() {
|
fn grapheme_index_to_closest_vis_pos_1() {
|
||||||
let tl = Line::new_from_str("Hello!");
|
let tl = Line::new_from_str("Hello!");
|
||||||
|
|
||||||
assert!(tl.grapheme_index_to_closest_vis_pos(0) == 0);
|
assert!(tl.grapheme_index_to_closest_vis_pos(0, TAB_WIDTH) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn grapheme_index_to_closest_vis_pos_2() {
|
fn grapheme_index_to_closest_vis_pos_2() {
|
||||||
let tl = Line::new_from_str("\tHello!");
|
let tl = Line::new_from_str("\tHello!");
|
||||||
|
|
||||||
assert!(tl.grapheme_index_to_closest_vis_pos(1) == TAB_WIDTH);
|
assert!(tl.grapheme_index_to_closest_vis_pos(1, TAB_WIDTH) == TAB_WIDTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn vis_pos_to_closest_grapheme_index_1() {
|
fn vis_pos_to_closest_grapheme_index_1() {
|
||||||
let tl = Line::new_from_str("Hello!");
|
let tl = Line::new_from_str("Hello!");
|
||||||
|
|
||||||
assert!(tl.vis_pos_to_closest_grapheme_index(0) == 0);
|
assert!(tl.vis_pos_to_closest_grapheme_index(0, TAB_WIDTH) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn vis_pos_to_closest_grapheme_index_2() {
|
fn vis_pos_to_closest_grapheme_index_2() {
|
||||||
let tl = Line::new_from_str("\tHello!");
|
let tl = Line::new_from_str("\tHello!");
|
||||||
|
|
||||||
assert!(tl.vis_pos_to_closest_grapheme_index(TAB_WIDTH) == 1);
|
assert!(tl.vis_pos_to_closest_grapheme_index(TAB_WIDTH, TAB_WIDTH) == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//=========================================================================
|
//=========================================================================
|
||||||
// LineGraphemeIter tests
|
// LineGraphemeIter tests
|
||||||
//=========================================================================
|
//=========================================================================
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn text_line_grapheme_iter() {
|
fn text_line_grapheme_iter() {
|
||||||
let tl = Line::new_from_str("Hello!");
|
let tl = Line::new_from_str("Hello!");
|
||||||
let mut iter = tl.grapheme_iter();
|
let mut iter = tl.grapheme_iter();
|
||||||
|
|
||||||
|
@ -975,10 +978,10 @@ fn text_line_grapheme_iter() {
|
||||||
assert!(iter.next() == Some("o"));
|
assert!(iter.next() == Some("o"));
|
||||||
assert!(iter.next() == Some("!"));
|
assert!(iter.next() == Some("!"));
|
||||||
assert!(iter.next() == None);
|
assert!(iter.next() == None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn text_line_grapheme_iter_with_lf() {
|
fn text_line_grapheme_iter_with_lf() {
|
||||||
let tl = Line::new_from_str("Hello!\n");
|
let tl = Line::new_from_str("Hello!\n");
|
||||||
let mut iter = tl.grapheme_iter();
|
let mut iter = tl.grapheme_iter();
|
||||||
|
|
||||||
|
@ -990,10 +993,10 @@ fn text_line_grapheme_iter_with_lf() {
|
||||||
assert!(iter.next() == Some("!"));
|
assert!(iter.next() == Some("!"));
|
||||||
assert!(iter.next() == Some("\n"));
|
assert!(iter.next() == Some("\n"));
|
||||||
assert!(iter.next() == None);
|
assert!(iter.next() == None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn text_line_grapheme_iter_with_crlf() {
|
fn text_line_grapheme_iter_with_crlf() {
|
||||||
let tl = Line::new_from_str("Hello!\r\n");
|
let tl = Line::new_from_str("Hello!\r\n");
|
||||||
let mut iter = tl.grapheme_iter();
|
let mut iter = tl.grapheme_iter();
|
||||||
|
|
||||||
|
@ -1005,10 +1008,10 @@ fn text_line_grapheme_iter_with_crlf() {
|
||||||
assert!(iter.next() == Some("!"));
|
assert!(iter.next() == Some("!"));
|
||||||
assert!(iter.next() == Some("\r\n"));
|
assert!(iter.next() == Some("\r\n"));
|
||||||
assert!(iter.next() == None);
|
assert!(iter.next() == None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn text_line_grapheme_iter_at_index() {
|
fn text_line_grapheme_iter_at_index() {
|
||||||
let tl = Line::new_from_str("Hello!");
|
let tl = Line::new_from_str("Hello!");
|
||||||
let mut iter = tl.grapheme_iter_at_index(2);
|
let mut iter = tl.grapheme_iter_at_index(2);
|
||||||
|
|
||||||
|
@ -1017,21 +1020,23 @@ fn text_line_grapheme_iter_at_index() {
|
||||||
assert!(iter.next() == Some("o"));
|
assert!(iter.next() == Some("o"));
|
||||||
assert!(iter.next() == Some("!"));
|
assert!(iter.next() == Some("!"));
|
||||||
assert!(iter.next() == None);
|
assert!(iter.next() == None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn text_line_grapheme_iter_at_index_past_end() {
|
fn text_line_grapheme_iter_at_index_past_end() {
|
||||||
let tl = Line::new_from_str("Hello!");
|
let tl = Line::new_from_str("Hello!");
|
||||||
let mut iter = tl.grapheme_iter_at_index(10);
|
let mut iter = tl.grapheme_iter_at_index(10);
|
||||||
|
|
||||||
assert!(iter.next() == None);
|
assert!(iter.next() == None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn text_line_grapheme_iter_at_index_at_lf() {
|
fn text_line_grapheme_iter_at_index_at_lf() {
|
||||||
let tl = Line::new_from_str("Hello!\n");
|
let tl = Line::new_from_str("Hello!\n");
|
||||||
let mut iter = tl.grapheme_iter_at_index(6);
|
let mut iter = tl.grapheme_iter_at_index(6);
|
||||||
|
|
||||||
assert!(iter.next() == Some("\n"));
|
assert!(iter.next() == Some("\n"));
|
||||||
assert!(iter.next() == None);
|
assert!(iter.next() == None);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -4,7 +4,7 @@ use std::mem;
|
||||||
|
|
||||||
use self::node::{BufferNode, BufferNodeGraphemeIter, BufferNodeLineIter};
|
use self::node::{BufferNode, BufferNodeGraphemeIter, BufferNodeLineIter};
|
||||||
use self::line::{Line, LineEnding};
|
use self::line::{Line, LineEnding};
|
||||||
use string_utils::{is_line_ending};
|
use string_utils::{is_line_ending, grapheme_count};
|
||||||
|
|
||||||
pub mod line;
|
pub mod line;
|
||||||
mod node;
|
mod node;
|
||||||
|
@ -18,6 +18,7 @@ mod node;
|
||||||
pub struct Buffer {
|
pub struct Buffer {
|
||||||
text: BufferNode,
|
text: BufferNode,
|
||||||
pub line_ending_type: LineEnding,
|
pub line_ending_type: LineEnding,
|
||||||
|
undo_stack: Vec<Operation>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,6 +27,7 @@ impl Buffer {
|
||||||
Buffer {
|
Buffer {
|
||||||
text: BufferNode::new(),
|
text: BufferNode::new(),
|
||||||
line_ending_type: LineEnding::LF,
|
line_ending_type: LineEnding::LF,
|
||||||
|
undo_stack: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,12 +140,27 @@ impl Buffer {
|
||||||
|
|
||||||
/// Insert 'text' at grapheme position 'pos'.
|
/// Insert 'text' at grapheme position 'pos'.
|
||||||
pub fn insert_text(&mut self, text: &str, pos: usize) {
|
pub fn insert_text(&mut self, text: &str, pos: usize) {
|
||||||
|
self._insert_text(text, pos);
|
||||||
|
|
||||||
|
self.undo_stack.push(Operation::InsertText(String::from_str(text), pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _insert_text(&mut self, text: &str, pos: usize) {
|
||||||
self.text.insert_text(text, pos);
|
self.text.insert_text(text, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Remove the text between grapheme positions 'pos_a' and 'pos_b'.
|
/// Remove the text between grapheme positions 'pos_a' and 'pos_b'.
|
||||||
pub fn remove_text(&mut self, pos_a: usize, pos_b: usize) {
|
pub fn remove_text(&mut self, pos_a: usize, pos_b: usize) {
|
||||||
|
let removed_text = self.string_from_range(pos_a, pos_b);
|
||||||
|
|
||||||
|
self._remove_text(pos_a, pos_b);
|
||||||
|
|
||||||
|
// Push operation to the undo stack
|
||||||
|
self.undo_stack.push(Operation::RemoveText(removed_text, pos_a));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _remove_text(&mut self, pos_a: usize, pos_b: usize) {
|
||||||
// Nothing to do
|
// Nothing to do
|
||||||
if pos_a == pos_b {
|
if pos_a == pos_b {
|
||||||
return;
|
return;
|
||||||
|
@ -171,6 +188,58 @@ 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() {
|
||||||
|
match op {
|
||||||
|
Operation::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) => {
|
||||||
|
let size = grapheme_count(s.as_slice());
|
||||||
|
self._insert_text(s.as_slice(), p);
|
||||||
|
return Some(p+size);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Creates a String from the buffer text in grapheme range [pos_a, posb).
|
||||||
|
fn string_from_range(&self, pos_a: usize, pos_b: usize) -> String {
|
||||||
|
// Bounds checks
|
||||||
|
if pos_b < pos_a {
|
||||||
|
panic!("Buffer::string_from_range(): pos_a must be less than or equal to pos_b.");
|
||||||
|
}
|
||||||
|
else if pos_b > self.len() {
|
||||||
|
panic!("Buffer::string_from_range(): specified range is past end of buffer text.");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut s = String::with_capacity(pos_b - pos_a);
|
||||||
|
|
||||||
|
let mut iter = self.grapheme_iter_at_index(pos_a);
|
||||||
|
let mut i = 0;
|
||||||
|
let i_end = pos_b - pos_a;
|
||||||
|
|
||||||
|
for g in iter {
|
||||||
|
if i == i_end {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
s.push_str(g);
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates an iterator at the first character
|
/// Creates an iterator at the first character
|
||||||
pub fn grapheme_iter<'a>(&'a self) -> BufferGraphemeIter<'a> {
|
pub fn grapheme_iter<'a>(&'a self) -> BufferGraphemeIter<'a> {
|
||||||
BufferGraphemeIter {
|
BufferGraphemeIter {
|
||||||
|
@ -278,13 +347,27 @@ impl<'a> Iterator for BufferLineIter<'a> {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//================================================================
|
||||||
|
// Buffer undo structures
|
||||||
|
//================================================================
|
||||||
|
|
||||||
|
enum Operation {
|
||||||
|
InsertText(String, usize),
|
||||||
|
RemoveText(String, usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================================================================
|
//================================================================
|
||||||
// TESTS
|
// TESTS
|
||||||
//================================================================
|
//================================================================
|
||||||
|
|
||||||
#[test]
|
mod tests {
|
||||||
fn insert_text() {
|
use super::{Buffer, Operation, BufferGraphemeIter, BufferLineIter};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn insert_text() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
|
|
||||||
buf.insert_text("Hello 世界!", 0);
|
buf.insert_text("Hello 世界!", 0);
|
||||||
|
@ -303,11 +386,11 @@ fn insert_text() {
|
||||||
assert!(Some("界") == iter.next());
|
assert!(Some("界") == iter.next());
|
||||||
assert!(Some("!") == iter.next());
|
assert!(Some("!") == iter.next());
|
||||||
assert!(None == iter.next());
|
assert!(None == iter.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn insert_text_with_newlines() {
|
fn insert_text_with_newlines() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
|
|
||||||
buf.insert_text("Hello\n 世界\r\n!", 0);
|
buf.insert_text("Hello\n 世界\r\n!", 0);
|
||||||
|
@ -328,11 +411,11 @@ fn insert_text_with_newlines() {
|
||||||
assert!(Some("\r\n") == iter.next());
|
assert!(Some("\r\n") == iter.next());
|
||||||
assert!(Some("!") == iter.next());
|
assert!(Some("!") == iter.next());
|
||||||
assert!(None == iter.next());
|
assert!(None == iter.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn insert_text_in_non_empty_buffer_1() {
|
fn insert_text_in_non_empty_buffer_1() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
|
|
||||||
buf.insert_text("Hello\n 世界\r\n!", 0);
|
buf.insert_text("Hello\n 世界\r\n!", 0);
|
||||||
|
@ -360,11 +443,11 @@ fn insert_text_in_non_empty_buffer_1() {
|
||||||
assert!(Some("\r\n") == iter.next());
|
assert!(Some("\r\n") == iter.next());
|
||||||
assert!(Some("!") == iter.next());
|
assert!(Some("!") == iter.next());
|
||||||
assert!(None == iter.next());
|
assert!(None == iter.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn insert_text_in_non_empty_buffer_2() {
|
fn insert_text_in_non_empty_buffer_2() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
|
|
||||||
buf.insert_text("Hello\n 世界\r\n!", 0);
|
buf.insert_text("Hello\n 世界\r\n!", 0);
|
||||||
|
@ -392,11 +475,11 @@ fn insert_text_in_non_empty_buffer_2() {
|
||||||
assert!(Some("\r\n") == iter.next());
|
assert!(Some("\r\n") == iter.next());
|
||||||
assert!(Some("!") == iter.next());
|
assert!(Some("!") == iter.next());
|
||||||
assert!(None == iter.next());
|
assert!(None == iter.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn insert_text_in_non_empty_buffer_3() {
|
fn insert_text_in_non_empty_buffer_3() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
|
|
||||||
buf.insert_text("Hello\n 世界\r\n!", 0);
|
buf.insert_text("Hello\n 世界\r\n!", 0);
|
||||||
|
@ -423,11 +506,11 @@ fn insert_text_in_non_empty_buffer_3() {
|
||||||
assert!(Some("\r\n") == iter.next());
|
assert!(Some("\r\n") == iter.next());
|
||||||
assert!(Some("!") == iter.next());
|
assert!(Some("!") == iter.next());
|
||||||
assert!(None == iter.next());
|
assert!(None == iter.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn insert_text_in_non_empty_buffer_4() {
|
fn insert_text_in_non_empty_buffer_4() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
|
|
||||||
buf.insert_text("Hello\n 世界\r\n!", 0);
|
buf.insert_text("Hello\n 世界\r\n!", 0);
|
||||||
|
@ -454,11 +537,11 @@ fn insert_text_in_non_empty_buffer_4() {
|
||||||
assert!(Some("i") == iter.next());
|
assert!(Some("i") == iter.next());
|
||||||
assert!(Some("n") == iter.next());
|
assert!(Some("n") == iter.next());
|
||||||
assert!(None == iter.next());
|
assert!(None == iter.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn insert_text_in_non_empty_buffer_5() {
|
fn insert_text_in_non_empty_buffer_5() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
|
|
||||||
buf.insert_text("Hello\n 世界\r\n!", 0);
|
buf.insert_text("Hello\n 世界\r\n!", 0);
|
||||||
|
@ -486,11 +569,11 @@ fn insert_text_in_non_empty_buffer_5() {
|
||||||
assert!(Some("!") == iter.next());
|
assert!(Some("!") == iter.next());
|
||||||
|
|
||||||
assert!(None == iter.next());
|
assert!(None == iter.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn insert_text_in_non_empty_buffer_6() {
|
fn insert_text_in_non_empty_buffer_6() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
|
|
||||||
buf.insert_text("Hello\n 世界\r\n!", 0);
|
buf.insert_text("Hello\n 世界\r\n!", 0);
|
||||||
|
@ -518,11 +601,11 @@ fn insert_text_in_non_empty_buffer_6() {
|
||||||
assert!(Some("!") == iter.next());
|
assert!(Some("!") == iter.next());
|
||||||
|
|
||||||
assert!(None == iter.next());
|
assert!(None == iter.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn insert_text_in_non_empty_buffer_7() {
|
fn insert_text_in_non_empty_buffer_7() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
|
|
||||||
buf.insert_text("Hello\n 世界\r\n!", 0);
|
buf.insert_text("Hello\n 世界\r\n!", 0);
|
||||||
|
@ -554,11 +637,11 @@ fn insert_text_in_non_empty_buffer_7() {
|
||||||
assert!(Some("!") == iter.next());
|
assert!(Some("!") == iter.next());
|
||||||
|
|
||||||
assert!(None == iter.next());
|
assert!(None == iter.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_text_1() {
|
fn remove_text_1() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
|
|
||||||
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
||||||
|
@ -598,11 +681,11 @@ fn remove_text_1() {
|
||||||
assert!(Some("d") == iter.next());
|
assert!(Some("d") == iter.next());
|
||||||
assert!(Some("!") == iter.next());
|
assert!(Some("!") == iter.next());
|
||||||
assert!(None == iter.next());
|
assert!(None == iter.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_text_2() {
|
fn remove_text_2() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
|
|
||||||
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
||||||
|
@ -633,11 +716,11 @@ fn remove_text_2() {
|
||||||
assert!(Some("d") == iter.next());
|
assert!(Some("d") == iter.next());
|
||||||
assert!(Some("!") == iter.next());
|
assert!(Some("!") == iter.next());
|
||||||
assert!(None == iter.next());
|
assert!(None == iter.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_text_3() {
|
fn remove_text_3() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
|
|
||||||
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
||||||
|
@ -668,11 +751,11 @@ fn remove_text_3() {
|
||||||
assert!(Some("d") == iter.next());
|
assert!(Some("d") == iter.next());
|
||||||
assert!(Some("!") == iter.next());
|
assert!(Some("!") == iter.next());
|
||||||
assert!(None == iter.next());
|
assert!(None == iter.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_text_4() {
|
fn remove_text_4() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
|
|
||||||
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
||||||
|
@ -709,11 +792,11 @@ fn remove_text_4() {
|
||||||
assert!(Some("e") == iter.next());
|
assert!(Some("e") == iter.next());
|
||||||
assert!(Some("\n") == iter.next());
|
assert!(Some("\n") == iter.next());
|
||||||
assert!(None == iter.next());
|
assert!(None == iter.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_text_5() {
|
fn remove_text_5() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
|
|
||||||
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
||||||
|
@ -744,11 +827,11 @@ fn remove_text_5() {
|
||||||
assert!(Some("\n") == iter.next());
|
assert!(Some("\n") == iter.next());
|
||||||
assert!(Some("o") == iter.next());
|
assert!(Some("o") == iter.next());
|
||||||
assert!(None == iter.next());
|
assert!(None == iter.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_text_6() {
|
fn remove_text_6() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
|
|
||||||
buf.insert_text("Hello\nworld!", 0);
|
buf.insert_text("Hello\nworld!", 0);
|
||||||
|
@ -765,11 +848,11 @@ fn remove_text_6() {
|
||||||
assert!(Some("e") == iter.next());
|
assert!(Some("e") == iter.next());
|
||||||
assert!(Some("l") == iter.next());
|
assert!(Some("l") == iter.next());
|
||||||
assert!(None == iter.next());
|
assert!(None == iter.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_text_7() {
|
fn remove_text_7() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
|
|
||||||
buf.insert_text("Hi\nthere\nworld!", 0);
|
buf.insert_text("Hi\nthere\nworld!", 0);
|
||||||
|
@ -788,11 +871,11 @@ fn remove_text_7() {
|
||||||
assert!(Some("t") == iter.next());
|
assert!(Some("t") == iter.next());
|
||||||
assert!(Some("h") == iter.next());
|
assert!(Some("h") == iter.next());
|
||||||
assert!(None == iter.next());
|
assert!(None == iter.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_text_8() {
|
fn remove_text_8() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
|
|
||||||
buf.insert_text("Hello\nworld!", 0);
|
buf.insert_text("Hello\nworld!", 0);
|
||||||
|
@ -810,11 +893,11 @@ fn remove_text_8() {
|
||||||
assert!(Some("l") == iter.next());
|
assert!(Some("l") == iter.next());
|
||||||
assert!(Some("!") == iter.next());
|
assert!(Some("!") == iter.next());
|
||||||
assert!(None == iter.next());
|
assert!(None == iter.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_text_9() {
|
fn remove_text_9() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
|
|
||||||
buf.insert_text("Hello\nworld!", 0);
|
buf.insert_text("Hello\nworld!", 0);
|
||||||
|
@ -836,11 +919,11 @@ fn remove_text_9() {
|
||||||
assert!(Some("w") == iter.next());
|
assert!(Some("w") == iter.next());
|
||||||
assert!(Some("o") == iter.next());
|
assert!(Some("o") == iter.next());
|
||||||
assert!(None == iter.next());
|
assert!(None == iter.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_text_10() {
|
fn remove_text_10() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
|
|
||||||
buf.insert_text("12\n34\n56\n78", 0);
|
buf.insert_text("12\n34\n56\n78", 0);
|
||||||
|
@ -858,11 +941,11 @@ fn remove_text_10() {
|
||||||
assert!(Some("\n") == iter.next());
|
assert!(Some("\n") == iter.next());
|
||||||
assert!(Some("3") == iter.next());
|
assert!(Some("3") == iter.next());
|
||||||
assert!(None == iter.next());
|
assert!(None == iter.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_text_11() {
|
fn remove_text_11() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
|
|
||||||
buf.insert_text("1234567890", 0);
|
buf.insert_text("1234567890", 0);
|
||||||
|
@ -885,11 +968,11 @@ fn remove_text_11() {
|
||||||
assert!(Some("8") == iter.next());
|
assert!(Some("8") == iter.next());
|
||||||
assert!(Some("9") == iter.next());
|
assert!(Some("9") == iter.next());
|
||||||
assert!(None == iter.next());
|
assert!(None == iter.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_lines_1() {
|
fn remove_lines_1() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
|
|
||||||
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
||||||
|
@ -916,11 +999,11 @@ fn remove_lines_1() {
|
||||||
assert!(Some("d") == iter.next());
|
assert!(Some("d") == iter.next());
|
||||||
assert!(Some("!") == iter.next());
|
assert!(Some("!") == iter.next());
|
||||||
assert!(None == iter.next());
|
assert!(None == iter.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_lines_2() {
|
fn remove_lines_2() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
|
|
||||||
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
||||||
|
@ -947,11 +1030,11 @@ fn remove_lines_2() {
|
||||||
assert!(Some("d") == iter.next());
|
assert!(Some("d") == iter.next());
|
||||||
assert!(Some("!") == iter.next());
|
assert!(Some("!") == iter.next());
|
||||||
assert!(None == iter.next());
|
assert!(None == iter.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remove_lines_3() {
|
fn remove_lines_3() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
|
|
||||||
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
||||||
|
@ -980,65 +1063,87 @@ fn remove_lines_3() {
|
||||||
assert!(Some("l") == iter.next());
|
assert!(Some("l") == iter.next());
|
||||||
assert!(Some("e") == iter.next());
|
assert!(Some("e") == iter.next());
|
||||||
assert!(None == iter.next());
|
assert!(None == iter.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn pos_2d_to_closest_1d_1() {
|
fn pos_2d_to_closest_1d_1() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
||||||
|
|
||||||
let pos = buf.pos_2d_to_closest_1d((2, 3));
|
let pos = buf.pos_2d_to_closest_1d((2, 3));
|
||||||
|
|
||||||
assert!(pos == 12);
|
assert!(pos == 12);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn pos_2d_to_closest_1d_2() {
|
fn pos_2d_to_closest_1d_2() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
||||||
|
|
||||||
let pos = buf.pos_2d_to_closest_1d((2, 10));
|
let pos = buf.pos_2d_to_closest_1d((2, 10));
|
||||||
|
|
||||||
assert!(pos == 15);
|
assert!(pos == 15);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn pos_2d_to_closest_1d_3() {
|
fn pos_2d_to_closest_1d_3() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
||||||
|
|
||||||
let pos = buf.pos_2d_to_closest_1d((10, 2));
|
let pos = buf.pos_2d_to_closest_1d((10, 2));
|
||||||
|
|
||||||
assert!(pos == 29);
|
assert!(pos == 29);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn pos_1d_to_closest_2d_1() {
|
fn pos_1d_to_closest_2d_1() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
||||||
|
|
||||||
let pos = buf.pos_1d_to_closest_2d(5);
|
let pos = buf.pos_1d_to_closest_2d(5);
|
||||||
|
|
||||||
assert!(pos == (1, 2));
|
assert!(pos == (1, 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn pos_1d_to_closest_2d_2() {
|
fn pos_1d_to_closest_2d_2() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
||||||
|
|
||||||
let pos = buf.pos_1d_to_closest_2d(50);
|
let pos = buf.pos_1d_to_closest_2d(50);
|
||||||
|
|
||||||
assert!(pos == (5, 6));
|
assert!(pos == (5, 6));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn grapheme_iter_at_index_1() {
|
fn string_from_range_1() {
|
||||||
|
let mut buf = Buffer::new();
|
||||||
|
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
||||||
|
|
||||||
|
let s = buf.string_from_range(1, 12);
|
||||||
|
|
||||||
|
assert!(s.as_slice() == "i\nthere\npeo");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn string_from_range_2() {
|
||||||
|
let mut buf = Buffer::new();
|
||||||
|
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
||||||
|
|
||||||
|
let s = buf.string_from_range(0, 29);
|
||||||
|
|
||||||
|
assert!(s.as_slice() == "Hi\nthere\npeople\nof\nthe\nworld!");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn grapheme_iter_at_index_1() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
||||||
|
|
||||||
|
@ -1058,19 +1163,19 @@ fn grapheme_iter_at_index_1() {
|
||||||
assert!(Some("d") == iter.next());
|
assert!(Some("d") == iter.next());
|
||||||
assert!(Some("!") == iter.next());
|
assert!(Some("!") == iter.next());
|
||||||
assert!(None == iter.next());
|
assert!(None == iter.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn grapheme_iter_at_index_2() {
|
fn grapheme_iter_at_index_2() {
|
||||||
let mut buf = Buffer::new();
|
let mut buf = Buffer::new();
|
||||||
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
buf.insert_text("Hi\nthere\npeople\nof\nthe\nworld!", 0);
|
||||||
|
|
||||||
let mut iter = buf.grapheme_iter_at_index(29);
|
let mut iter = buf.grapheme_iter_at_index(29);
|
||||||
|
|
||||||
assert!(None == iter.next());
|
assert!(None == iter.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -189,6 +189,17 @@ impl Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn undo(&mut self) {
|
||||||
|
if let Some(pos) = self.buffer.undo() {
|
||||||
|
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
|
/// Moves the editor's view the minimum amount to show the cursor
|
||||||
pub fn move_view_to_cursor(&mut self) {
|
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);
|
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_O: u16 = 15;
|
||||||
const K_CTRL_Q: u16 = 17;
|
const K_CTRL_Q: u16 = 17;
|
||||||
const K_CTRL_S: u16 = 19;
|
const K_CTRL_S: u16 = 19;
|
||||||
|
const K_CTRL_Z: u16 = 26;
|
||||||
|
|
||||||
|
|
||||||
pub struct TermUI {
|
pub struct TermUI {
|
||||||
|
@ -98,6 +99,10 @@ impl TermUI {
|
||||||
self.editor.save_if_dirty();
|
self.editor.save_if_dirty();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
K_CTRL_Z => {
|
||||||
|
self.editor.undo();
|
||||||
|
},
|
||||||
|
|
||||||
K_CTRL_L => {
|
K_CTRL_L => {
|
||||||
self.go_to_line_ui_loop();
|
self.go_to_line_ui_loop();
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue
Block a user