#[derive(Debug, Clone)]
pub struct KillRing {
entries: Vec<String>,
capacity: usize,
}
impl KillRing {
#[must_use]
pub fn new(capacity: usize) -> Self {
let capacity = capacity.max(1);
Self {
entries: Vec::with_capacity(capacity),
capacity,
}
}
#[must_use]
pub fn len(&self) -> usize {
self.entries.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn kill(&mut self, text: String) {
if self.entries.len() == self.capacity {
self.entries.remove(0);
}
self.entries.push(text);
}
#[must_use]
pub fn yank(&self) -> Option<&str> {
self.entries.last().map(String::as_str)
}
pub fn yank_pop(&mut self) -> Option<&str> {
if self.entries.len() <= 1 {
return self.entries.last().map(String::as_str);
}
let last = self.entries.pop()?;
self.entries.insert(0, last);
self.entries.last().map(String::as_str)
}
#[must_use]
pub fn peek_at(&self, index: usize) -> Option<&str> {
self.entries.get(index).map(String::as_str)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_ring_yank_returns_none() {
let ring = KillRing::new(4);
assert!(ring.is_empty());
assert_eq!(ring.yank(), None);
assert_eq!(ring.len(), 0);
}
#[test]
fn kill_then_yank_returns_last() {
let mut ring = KillRing::new(4);
ring.kill("first".to_string());
ring.kill("second".to_string());
ring.kill("third".to_string());
assert_eq!(ring.yank(), Some("third"));
assert_eq!(ring.len(), 3);
}
#[test]
fn yank_pop_cycles_through_history() {
let mut ring = KillRing::new(4);
ring.kill("a".to_string());
ring.kill("b".to_string());
ring.kill("c".to_string());
assert_eq!(ring.yank_pop(), Some("b"));
assert_eq!(ring.yank_pop(), Some("a"));
assert_eq!(ring.yank_pop(), Some("c"));
assert_eq!(ring.yank_pop(), Some("b"));
}
#[test]
fn capacity_evicts_oldest() {
let mut ring = KillRing::new(3);
ring.kill("a".to_string());
ring.kill("b".to_string());
ring.kill("c".to_string());
ring.kill("d".to_string());
assert_eq!(ring.len(), 3);
assert_eq!(ring.peek_at(0), Some("b"));
assert_eq!(ring.yank(), Some("d"));
}
#[test]
fn capacity_zero_clamps_to_one() {
let mut ring = KillRing::new(0);
ring.kill("only".to_string());
assert_eq!(ring.len(), 1);
ring.kill("still_only".to_string());
assert_eq!(ring.len(), 1);
assert_eq!(ring.yank(), Some("still_only"));
}
#[test]
fn yank_pop_on_single_entry_is_noop() {
let mut ring = KillRing::new(4);
ring.kill("only".to_string());
assert_eq!(ring.yank_pop(), Some("only"));
assert_eq!(ring.yank_pop(), Some("only"));
}
#[test]
fn peek_at_out_of_bounds_returns_none() {
let mut ring = KillRing::new(4);
ring.kill("x".to_string());
assert_eq!(ring.peek_at(0), Some("x"));
assert_eq!(ring.peek_at(1), None);
}
}