blob: f897597c65e4a856069844e025459bb2e9487c34 [file] [log] [blame]
//! Kill Ring
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Action {
Kill,
Yank(usize),
Other,
}
pub struct KillRing {
slots: Vec<String>,
index: usize,
last_action: Action,
}
impl KillRing {
/// Create a new kill-ring of the given `size`.
pub fn new(size: usize) -> KillRing {
KillRing {
slots: Vec::with_capacity(size),
index: 0,
last_action: Action::Other,
}
}
/// Reset `last_action` state.
pub fn reset(&mut self) {
self.last_action = Action::Other;
}
/// Add `text` to the kill-ring.
pub fn kill(&mut self, text: &str, forward: bool) {
match self.last_action {
Action::Kill => {
if self.slots.capacity() == 0 {
// disabled
return;
}
if forward {
// append
self.slots[self.index].push_str(text);
} else {
// prepend
self.slots[self.index] = String::from(text) + &self.slots[self.index];
}
}
_ => {
self.last_action = Action::Kill;
if self.slots.capacity() == 0 {
// disabled
return;
}
if self.index == self.slots.capacity() - 1 {
// full
self.index = 0;
} else if !self.slots.is_empty() {
self.index += 1;
}
if self.index == self.slots.len() {
self.slots.push(String::from(text))
} else {
self.slots[self.index] = String::from(text);
}
}
}
}
/// Yank previously killed text.
/// Return `None` when kill-ring is empty.
pub fn yank(&mut self) -> Option<&String> {
if self.slots.is_empty() {
None
} else {
self.last_action = Action::Yank(self.slots[self.index].len());
Some(&self.slots[self.index])
}
}
/// Yank killed text stored in previous slot.
/// Return `None` when the previous command was not a yank.
pub fn yank_pop(&mut self) -> Option<(usize, &String)> {
match self.last_action {
Action::Yank(yank_size) => {
if self.slots.is_empty() {
return None;
}
if self.index == 0 {
self.index = self.slots.len() - 1;
} else {
self.index -= 1;
}
self.last_action = Action::Yank(self.slots[self.index].len());
Some((yank_size, &self.slots[self.index]))
}
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::{Action, KillRing};
#[test]
fn disabled() {
let mut kill_ring = KillRing::new(0);
kill_ring.kill("text", true);
assert!(kill_ring.slots.is_empty());
assert_eq!(0, kill_ring.index);
assert_eq!(Action::Kill, kill_ring.last_action);
assert_eq!(None, kill_ring.yank());
assert_eq!(Action::Kill, kill_ring.last_action);
}
#[test]
fn one_kill() {
let mut kill_ring = KillRing::new(2);
kill_ring.kill("word1", true);
assert_eq!(0, kill_ring.index);
assert_eq!(1, kill_ring.slots.len());
assert_eq!("word1", kill_ring.slots[0]);
assert_eq!(Action::Kill, kill_ring.last_action);
}
#[test]
fn kill_kill_forward() {
let mut kill_ring = KillRing::new(2);
kill_ring.kill("word1", true);
kill_ring.kill(" word2", true);
assert_eq!(0, kill_ring.index);
assert_eq!(1, kill_ring.slots.len());
assert_eq!("word1 word2", kill_ring.slots[0]);
assert_eq!(Action::Kill, kill_ring.last_action);
}
#[test]
fn kill_kill_backward() {
let mut kill_ring = KillRing::new(2);
kill_ring.kill("word1", false);
kill_ring.kill("word2 ", false);
assert_eq!(0, kill_ring.index);
assert_eq!(1, kill_ring.slots.len());
assert_eq!("word2 word1", kill_ring.slots[0]);
assert_eq!(Action::Kill, kill_ring.last_action);
}
#[test]
fn kill_other_kill() {
let mut kill_ring = KillRing::new(2);
kill_ring.kill("word1", true);
kill_ring.reset();
kill_ring.kill("word2", true);
assert_eq!(1, kill_ring.index);
assert_eq!(2, kill_ring.slots.len());
assert_eq!("word1", kill_ring.slots[0]);
assert_eq!("word2", kill_ring.slots[1]);
assert_eq!(Action::Kill, kill_ring.last_action);
}
#[test]
fn many_kill() {
let mut kill_ring = KillRing::new(2);
kill_ring.kill("word1", true);
kill_ring.reset();
kill_ring.kill("word2", true);
kill_ring.reset();
kill_ring.kill("word3", true);
kill_ring.reset();
kill_ring.kill("word4", true);
assert_eq!(1, kill_ring.index);
assert_eq!(2, kill_ring.slots.len());
assert_eq!("word3", kill_ring.slots[0]);
assert_eq!("word4", kill_ring.slots[1]);
assert_eq!(Action::Kill, kill_ring.last_action);
}
#[test]
fn yank() {
let mut kill_ring = KillRing::new(2);
kill_ring.kill("word1", true);
kill_ring.reset();
kill_ring.kill("word2", true);
assert_eq!(Some(&"word2".to_string()), kill_ring.yank());
assert_eq!(Action::Yank(5), kill_ring.last_action);
assert_eq!(Some(&"word2".to_string()), kill_ring.yank());
assert_eq!(Action::Yank(5), kill_ring.last_action);
}
#[test]
fn yank_pop() {
let mut kill_ring = KillRing::new(2);
kill_ring.kill("word1", true);
kill_ring.reset();
kill_ring.kill("longword2", true);
assert_eq!(None, kill_ring.yank_pop());
kill_ring.yank();
assert_eq!(Some((9, &"word1".to_string())), kill_ring.yank_pop());
assert_eq!(Some((5, &"longword2".to_string())), kill_ring.yank_pop());
assert_eq!(Some((9, &"word1".to_string())), kill_ring.yank_pop());
}
}