ted/
buffer.rs

1use ropey::Rope;
2use frappe::{Sink, Stream, Signal};
3use std::ops::{Deref, Range};
4
5pub struct Buffer {
6    text: Rope,
7    event_sink: Sink<FiringEvent>
8}
9
10impl Buffer {
11    pub fn from_str(text: &str) -> Self {
12        Self {
13            text: Rope::from_str(text),
14            event_sink: Sink::new()
15        }
16    }
17
18    pub fn text(&self) -> &Rope {
19        &self.text
20    }
21
22    pub fn events(&self) -> Stream<FiringEvent> {
23        self.event_sink.stream()
24    }
25
26    pub fn handle(&mut self, event: Event) {
27        let text = self.text.clone();
28
29        match *&event {
30            Event::Insert { char_idx, ref text } => self.text.insert(char_idx, text),
31            Event::Remove { ref char_idx_range } => self.text.remove(char_idx_range.clone())
32        }
33
34        self.event_sink.send(FiringEvent {
35            event: event,
36            text: text
37        });
38    }
39
40    pub fn signal_char_idx(&self, char_idx: usize) -> Signal<usize> {
41        let current_sink = Sink::new();
42        let current_signal = current_sink.stream().hold(char_idx);
43
44        self.event_sink.stream()
45            .map(move |event| {
46                let value = event.update_char_idx(current_signal.sample());
47                current_sink.send(value);
48                value
49            })
50            .hold(char_idx)
51    }
52
53    pub fn signal_line_idx(&self, line_idx: usize) -> Signal<usize> {
54        let current_sink = Sink::new();
55        let current_signal = current_sink.stream().hold(line_idx);
56
57        self.event_sink.stream()
58            .map(move |event| {
59                let value = event.update_line_idx(current_signal.sample());
60                current_sink.send(value);
61                value
62            })
63            .hold(line_idx)
64    }
65}
66
67/// Event wrapper with text it applies to.
68#[derive(Clone)]
69pub struct FiringEvent {
70    event: Event,
71    text: Rope
72}
73
74impl FiringEvent {
75    fn update_char_idx(&self, idx: usize) -> usize {
76        match &**self {
77            &Event::Insert { char_idx, ref text } if
78                char_idx <= idx
79             => idx + text.chars().count(),
80
81            &Event::Remove { ref char_idx_range } if
82                char_idx_range.start < idx
83             => if char_idx_range.end <= idx {
84                idx - (char_idx_range.end - char_idx_range.start)
85             } else {
86                char_idx_range.start
87             },
88
89            _ => idx
90        }
91    }
92
93    fn update_line_idx(&self, line_idx: usize) -> usize {
94        match &**self {
95            &Event::Insert { char_idx, ref text } if
96                char_idx <= self.text.line_to_char(line_idx)
97             => line_idx + text.matches('\n').count(),
98
99            &Event::Remove { ref char_idx_range } if
100                self.text.char_to_line(char_idx_range.start) < line_idx ||
101                self.text.char_to_line(char_idx_range.end)   < line_idx
102             => line_idx - self.text.slice(char_idx_range.clone()).to_string().matches('\n').count(),
103
104            _ => line_idx
105        }
106    }
107}
108
109impl Deref for FiringEvent {
110    type Target = Event;
111
112    fn deref(&self) -> &Self::Target {
113        &self.event
114    }
115}
116
117#[derive(Clone)]
118pub enum Event {
119    Insert {
120        char_idx: usize,
121        text: String
122    },
123    Remove {
124        char_idx_range: Range<usize>
125    }
126}
127
128impl super::history::Undo for FiringEvent {
129    type Undo = FiringEvent;
130
131    fn undo(&self) -> Self::Undo {
132        FiringEvent {
133            event: match **self {
134                Event::Insert { char_idx, ref text } => Event::Remove {
135                    char_idx_range: char_idx..char_idx + text.len()
136                },
137                Event::Remove { ref char_idx_range } => Event::Insert {
138                    char_idx: char_idx_range.start,
139                    text: self.text.slice(char_idx_range.clone()).to_string()
140                }
141            },
142            text: self.text.clone()
143        }
144    }
145}
146
147#[cfg(test)]
148mod tests {
149    use super::*;
150
151    #[test]
152    fn from_str() {
153        let text = "Hello, Ropey!";
154        let buf = Buffer::from_str(text);
155        assert_eq!(text, buf.text().to_string());
156    }
157
158    #[test]
159    fn signal_char_idx() {
160        fn sample(char_idc: &(Signal<usize>, Signal<usize>, Signal<usize>)) -> (usize, usize, usize) {(
161            char_idc.0.sample(),
162            char_idc.1.sample(),
163            char_idc.2.sample()
164        )}
165
166        let mut buf = Buffer::from_str("Hello, Ropey!");
167        let char_idc = (
168            buf.signal_char_idx(5),
169            buf.signal_char_idx(7),
170            buf.signal_char_idx(12)
171        );
172        assert_eq!((5, 7, 12), sample(&char_idc));
173
174        buf.handle(Event::Insert {
175            char_idx: 5,
176            text: " there".to_owned()
177        });
178        assert_eq!("Hello there, Ropey!", buf.text().to_string());
179        assert_eq!((11, 13, 18), sample(&char_idc));
180
181        buf.handle(Event::Insert {
182            char_idx: 18,
183            text: " my friend".to_owned()
184        });
185        assert_eq!("Hello there, Ropey my friend!", buf.text().to_string());
186        assert_eq!((11, 13, 28), sample(&char_idc));
187
188        buf.handle(Event::Remove {
189            char_idx_range: 5..11
190        });
191        assert_eq!("Hello, Ropey my friend!", buf.text().to_string());
192        assert_eq!((5, 7, 22), sample(&char_idc));
193
194        buf.handle(Event::Remove {
195            char_idx_range: 12..22
196        });
197        assert_eq!("Hello, Ropey!", buf.text().to_string());
198        assert_eq!((5, 7, 12), sample(&char_idc));
199    }
200
201    #[test]
202    fn signal_line_idx() {
203        fn sample(line_idc: &(Signal<usize>, Signal<usize>, Signal<usize>)) -> (usize, usize, usize) {(
204            line_idc.0.sample(),
205            line_idc.1.sample(),
206            line_idc.2.sample()
207        )}
208
209        let mut buf = Buffer::from_str("1\n2\n3");
210        let line_idc = (
211            buf.signal_line_idx(0),
212            buf.signal_line_idx(1),
213            buf.signal_line_idx(2)
214        );
215        assert_eq!((0, 1, 2), sample(&line_idc));
216
217        buf.handle(Event::Insert {
218            char_idx: 5,
219            text: "\n4".to_owned()
220        });
221        assert_eq!("1\n2\n3\n4", buf.text().to_string());
222        assert_eq!((0, 1, 2), sample(&line_idc));
223
224        buf.handle(Event::Insert {
225            char_idx: 3,
226            text: "a".to_owned()
227        });
228        assert_eq!("1\n2a\n3\n4", buf.text().to_string());
229        assert_eq!((0, 1, 2), sample(&line_idc));
230
231        buf.handle(Event::Insert {
232            char_idx: 5,
233            text: "3!\n".to_owned()
234        });
235        assert_eq!("1\n2a\n3!\n3\n4", buf.text().to_string());
236        assert_eq!((0, 1, 3), sample(&line_idc));
237
238        buf.handle(Event::Remove {
239            char_idx_range: 9..11
240        });
241        assert_eq!("1\n2a\n3!\n3", buf.text().to_string());
242        assert_eq!((0, 1, 3), sample(&line_idc));
243
244        buf.handle(Event::Remove {
245            char_idx_range: 0..2
246        });
247        assert_eq!("2a\n3!\n3", buf.text().to_string());
248        assert_eq!((0, 0, 2), sample(&line_idc));
249
250        buf.handle(Event::Remove {
251            char_idx_range: 3..6
252        });
253        assert_eq!("2a\n3", buf.text().to_string());
254        assert_eq!((0, 0, 1), sample(&line_idc));
255
256        buf.handle(Event::Remove {
257            char_idx_range: 0..4
258        });
259        assert!(buf.text().to_string().is_empty());
260        assert_eq!((0, 0, 0), sample(&line_idc));
261    }
262}