rustyline/
kill_ring.rs

1//! Kill Ring management
2use line_buffer::{DeleteListener, Direction};
3
4#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5enum Action {
6    Kill,
7    Yank(usize),
8    Other,
9}
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
12pub enum Mode {
13    Append,
14    Prepend,
15}
16
17pub struct KillRing {
18    slots: Vec<String>,
19    // where we are in the kill ring
20    index: usize,
21    // whether or not the last command was a kill or a yank
22    last_action: Action,
23    killing: bool,
24}
25
26impl KillRing {
27    /// Create a new kill-ring of the given `size`.
28    pub fn new(size: usize) -> KillRing {
29        KillRing {
30            slots: Vec::with_capacity(size),
31            index: 0,
32            last_action: Action::Other,
33            killing: false,
34        }
35    }
36
37    /// Reset `last_action` state.
38    pub fn reset(&mut self) {
39        self.last_action = Action::Other;
40    }
41
42    /// Add `text` to the kill-ring.
43    pub fn kill(&mut self, text: &str, dir: Mode) {
44        if let Action::Kill = self.last_action {
45            if self.slots.capacity() == 0 {
46                // disabled
47                return;
48            }
49            match dir {
50                Mode::Append => self.slots[self.index].push_str(text),
51                Mode::Prepend => self.slots[self.index].insert_str(0, text),
52            };
53        } else {
54            self.last_action = Action::Kill;
55            if self.slots.capacity() == 0 {
56                // disabled
57                return;
58            }
59            if self.index == self.slots.capacity() - 1 {
60                // full
61                self.index = 0;
62            } else if !self.slots.is_empty() {
63                self.index += 1;
64            }
65            if self.index == self.slots.len() {
66                self.slots.push(String::from(text))
67            } else {
68                self.slots[self.index] = String::from(text);
69            }
70        }
71    }
72
73    /// Yank previously killed text.
74    /// Return `None` when kill-ring is empty.
75    pub fn yank(&mut self) -> Option<&String> {
76        if self.slots.is_empty() {
77            None
78        } else {
79            self.last_action = Action::Yank(self.slots[self.index].len());
80            Some(&self.slots[self.index])
81        }
82    }
83
84    /// Yank killed text stored in previous slot.
85    /// Return `None` when the previous command was not a yank.
86    pub fn yank_pop(&mut self) -> Option<(usize, &String)> {
87        match self.last_action {
88            Action::Yank(yank_size) => {
89                if self.slots.is_empty() {
90                    return None;
91                }
92                if self.index == 0 {
93                    self.index = self.slots.len() - 1;
94                } else {
95                    self.index -= 1;
96                }
97                self.last_action = Action::Yank(self.slots[self.index].len());
98                Some((yank_size, &self.slots[self.index]))
99            }
100            _ => None,
101        }
102    }
103}
104
105impl DeleteListener for KillRing {
106    fn start_killing(&mut self) {
107        self.killing = true;
108    }
109
110    fn delete(&mut self, _: usize, string: &str, dir: Direction) {
111        if !self.killing {
112            return;
113        }
114        let mode = match dir {
115            Direction::Forward => Mode::Append,
116            Direction::Backward => Mode::Prepend,
117        };
118        self.kill(string, mode);
119    }
120
121    fn stop_killing(&mut self) {
122        self.killing = false;
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use super::{Action, KillRing, Mode};
129
130    #[test]
131    fn disabled() {
132        let mut kill_ring = KillRing::new(0);
133        kill_ring.kill("text", Mode::Append);
134        assert!(kill_ring.slots.is_empty());
135        assert_eq!(0, kill_ring.index);
136        assert_eq!(Action::Kill, kill_ring.last_action);
137
138        assert_eq!(None, kill_ring.yank());
139        assert_eq!(Action::Kill, kill_ring.last_action);
140    }
141
142    #[test]
143    fn one_kill() {
144        let mut kill_ring = KillRing::new(2);
145        kill_ring.kill("word1", Mode::Append);
146        assert_eq!(0, kill_ring.index);
147        assert_eq!(1, kill_ring.slots.len());
148        assert_eq!("word1", kill_ring.slots[0]);
149        assert_eq!(Action::Kill, kill_ring.last_action);
150    }
151
152    #[test]
153    fn kill_append() {
154        let mut kill_ring = KillRing::new(2);
155        kill_ring.kill("word1", Mode::Append);
156        kill_ring.kill(" word2", Mode::Append);
157        assert_eq!(0, kill_ring.index);
158        assert_eq!(1, kill_ring.slots.len());
159        assert_eq!("word1 word2", kill_ring.slots[0]);
160        assert_eq!(Action::Kill, kill_ring.last_action);
161    }
162
163    #[test]
164    fn kill_backward() {
165        let mut kill_ring = KillRing::new(2);
166        kill_ring.kill("word1", Mode::Prepend);
167        kill_ring.kill("word2 ", Mode::Prepend);
168        assert_eq!(0, kill_ring.index);
169        assert_eq!(1, kill_ring.slots.len());
170        assert_eq!("word2 word1", kill_ring.slots[0]);
171        assert_eq!(Action::Kill, kill_ring.last_action);
172    }
173
174    #[test]
175    fn kill_other_kill() {
176        let mut kill_ring = KillRing::new(2);
177        kill_ring.kill("word1", Mode::Append);
178        kill_ring.reset();
179        kill_ring.kill("word2", Mode::Append);
180        assert_eq!(1, kill_ring.index);
181        assert_eq!(2, kill_ring.slots.len());
182        assert_eq!("word1", kill_ring.slots[0]);
183        assert_eq!("word2", kill_ring.slots[1]);
184        assert_eq!(Action::Kill, kill_ring.last_action);
185    }
186
187    #[test]
188    fn many_kill() {
189        let mut kill_ring = KillRing::new(2);
190        kill_ring.kill("word1", Mode::Append);
191        kill_ring.reset();
192        kill_ring.kill("word2", Mode::Append);
193        kill_ring.reset();
194        kill_ring.kill("word3", Mode::Append);
195        kill_ring.reset();
196        kill_ring.kill("word4", Mode::Append);
197        assert_eq!(1, kill_ring.index);
198        assert_eq!(2, kill_ring.slots.len());
199        assert_eq!("word3", kill_ring.slots[0]);
200        assert_eq!("word4", kill_ring.slots[1]);
201        assert_eq!(Action::Kill, kill_ring.last_action);
202    }
203
204    #[test]
205    fn yank() {
206        let mut kill_ring = KillRing::new(2);
207        kill_ring.kill("word1", Mode::Append);
208        kill_ring.reset();
209        kill_ring.kill("word2", Mode::Append);
210
211        assert_eq!(Some(&"word2".to_owned()), kill_ring.yank());
212        assert_eq!(Action::Yank(5), kill_ring.last_action);
213        assert_eq!(Some(&"word2".to_owned()), kill_ring.yank());
214        assert_eq!(Action::Yank(5), kill_ring.last_action);
215    }
216
217    #[test]
218    fn yank_pop() {
219        let mut kill_ring = KillRing::new(2);
220        kill_ring.kill("word1", Mode::Append);
221        kill_ring.reset();
222        kill_ring.kill("longword2", Mode::Append);
223
224        assert_eq!(None, kill_ring.yank_pop());
225        kill_ring.yank();
226        assert_eq!(Some((9, &"word1".to_owned())), kill_ring.yank_pop());
227        assert_eq!(Some((5, &"longword2".to_owned())), kill_ring.yank_pop());
228        assert_eq!(Some((9, &"word1".to_owned())), kill_ring.yank_pop());
229    }
230}