oxihuman_core/
piece_table.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum PieceSource {
11 Original,
12 Added,
13}
14
15#[derive(Debug, Clone)]
17pub struct Piece {
18 pub source: PieceSource,
19 pub start: usize,
20 pub length: usize,
21}
22
23pub struct PieceTable {
25 original: Vec<char>,
26 added: Vec<char>,
27 pieces: Vec<Piece>,
28}
29
30impl PieceTable {
31 pub fn new(initial: &str) -> Self {
33 let original: Vec<char> = initial.chars().collect();
34 let len = original.len();
35 let pieces = if len > 0 {
36 vec![Piece {
37 source: PieceSource::Original,
38 start: 0,
39 length: len,
40 }]
41 } else {
42 Vec::new()
43 };
44 Self {
45 original,
46 added: Vec::new(),
47 pieces,
48 }
49 }
50
51 pub fn len(&self) -> usize {
53 self.pieces.iter().map(|p| p.length).sum()
54 }
55
56 pub fn is_empty(&self) -> bool {
58 self.len() == 0
59 }
60
61 pub fn insert(&mut self, pos: usize, text: &str) {
63 let add_start = self.added.len();
64 self.added.extend(text.chars());
65 let add_len = self.added.len() - add_start;
66 if add_len == 0 {
67 return;
68 }
69 let new_piece = Piece {
70 source: PieceSource::Added,
71 start: add_start,
72 length: add_len,
73 };
74 let mut cur = 0usize;
76 let mut insert_idx = self.pieces.len();
77 for (i, p) in self.pieces.iter().enumerate() {
78 if cur + p.length >= pos {
79 let offset = pos - cur;
80 if offset == 0 {
81 insert_idx = i;
82 } else if offset == p.length {
83 insert_idx = i + 1;
84 } else {
85 let left = Piece {
87 source: p.source,
88 start: p.start,
89 length: offset,
90 };
91 let right = Piece {
92 source: p.source,
93 start: p.start + offset,
94 length: p.length - offset,
95 };
96 self.pieces.remove(i);
97 self.pieces.insert(i, right);
98 self.pieces.insert(i, new_piece.clone());
99 self.pieces.insert(i, left);
100 return;
101 }
102 break;
103 }
104 cur += p.length;
105 }
106 self.pieces.insert(insert_idx, new_piece);
107 }
108
109 pub fn as_string(&self) -> String {
111 let mut s = String::with_capacity(self.len());
112 for p in &self.pieces {
113 let src = match p.source {
114 PieceSource::Original => &self.original,
115 PieceSource::Added => &self.added,
116 };
117 s.extend(src[p.start..p.start + p.length].iter());
118 }
119 s
120 }
121
122 pub fn piece_count(&self) -> usize {
124 self.pieces.len()
125 }
126}
127
128pub fn new_piece_table(initial: &str) -> PieceTable {
130 PieceTable::new(initial)
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 #[test]
138 fn test_initial_content() {
139 let pt = PieceTable::new("hello");
140 assert_eq!(pt.as_string(), "hello"); }
142
143 #[test]
144 fn test_len() {
145 let pt = PieceTable::new("abc");
146 assert_eq!(pt.len(), 3); }
148
149 #[test]
150 fn test_is_empty() {
151 let pt = PieceTable::new("");
152 assert!(pt.is_empty()); }
154
155 #[test]
156 fn test_insert_at_end() {
157 let mut pt = PieceTable::new("hello");
158 pt.insert(5, " world");
159 assert_eq!(pt.as_string(), "hello world"); }
161
162 #[test]
163 fn test_insert_at_start() {
164 let mut pt = PieceTable::new("world");
165 pt.insert(0, "hello ");
166 assert_eq!(pt.as_string(), "hello world"); }
168
169 #[test]
170 fn test_insert_in_middle() {
171 let mut pt = PieceTable::new("helo");
172 pt.insert(3, "l");
173 assert_eq!(pt.as_string(), "hello"); }
175
176 #[test]
177 fn test_multiple_inserts() {
178 let mut pt = PieceTable::new("");
179 pt.insert(0, "c");
180 pt.insert(0, "b");
181 pt.insert(0, "a");
182 assert_eq!(pt.as_string(), "abc"); }
184
185 #[test]
186 fn test_piece_count_grows() {
187 let mut pt = PieceTable::new("ab");
188 pt.insert(1, "X");
189 assert!(pt.piece_count() > 1); }
191
192 #[test]
193 fn test_new_helper() {
194 let pt = new_piece_table("test");
195 assert_eq!(pt.as_string(), "test"); }
197
198 #[test]
199 fn test_insert_empty_string() {
200 let mut pt = PieceTable::new("abc");
201 pt.insert(1, "");
202 assert_eq!(pt.as_string(), "abc"); }
204}