veridian_kernel/devtools/ide/
editor.rs1use alloc::{
7 string::{String, ToString},
8 vec,
9 vec::Vec,
10};
11
12pub(crate) struct GapBuffer {
14 buf: Vec<u8>,
15 gap_start: usize,
16 gap_end: usize,
17}
18
19impl Default for GapBuffer {
20 fn default() -> Self {
21 Self::new()
22 }
23}
24
25impl GapBuffer {
26 const INITIAL_GAP: usize = 64;
27
28 pub(crate) fn new() -> Self {
29 Self {
30 buf: vec![0; Self::INITIAL_GAP],
31 gap_start: 0,
32 gap_end: Self::INITIAL_GAP,
33 }
34 }
35
36 pub(crate) fn from_text(text: &str) -> Self {
37 let bytes = text.as_bytes();
38 let gap_size = Self::INITIAL_GAP;
39 let mut buf = Vec::with_capacity(bytes.len() + gap_size);
40 buf.extend_from_slice(bytes);
41 buf.resize(bytes.len() + gap_size, 0);
42
43 Self {
44 buf,
45 gap_start: bytes.len(),
46 gap_end: bytes.len() + gap_size,
47 }
48 }
49
50 pub(crate) fn len(&self) -> usize {
51 self.buf.len() - self.gap_len()
52 }
53
54 pub(crate) fn is_empty(&self) -> bool {
55 self.len() == 0
56 }
57
58 fn gap_len(&self) -> usize {
59 self.gap_end - self.gap_start
60 }
61
62 fn move_gap_to(&mut self, pos: usize) {
63 if pos == self.gap_start {
64 return;
65 }
66 if pos < self.gap_start {
67 let count = self.gap_start - pos;
68 let src = pos;
69 let dst = self.gap_end - count;
70 self.buf.copy_within(src..src + count, dst);
71 self.gap_start = pos;
72 self.gap_end -= count;
73 } else {
74 let count = pos - self.gap_start;
75 let src = self.gap_end;
76 let dst = self.gap_start;
77 self.buf.copy_within(src..src + count, dst);
78 self.gap_start += count;
79 self.gap_end += count;
80 }
81 }
82
83 fn ensure_gap(&mut self, needed: usize) {
84 if self.gap_len() >= needed {
85 return;
86 }
87 let extra = core::cmp::max(needed * 2, 64);
88 let old_gap_end = self.gap_end;
89 let after_gap = self.buf.len() - old_gap_end;
90
91 self.buf.resize(self.buf.len() + extra, 0);
92 if after_gap > 0 {
94 self.buf
95 .copy_within(old_gap_end..old_gap_end + after_gap, old_gap_end + extra);
96 }
97 self.gap_end += extra;
98 }
99
100 pub(crate) fn insert(&mut self, pos: usize, ch: u8) {
102 self.move_gap_to(pos);
103 self.ensure_gap(1);
104 self.buf[self.gap_start] = ch;
105 self.gap_start += 1;
106 }
107
108 pub(crate) fn insert_str(&mut self, pos: usize, text: &str) {
110 self.move_gap_to(pos);
111 self.ensure_gap(text.len());
112 self.buf[self.gap_start..self.gap_start + text.len()].copy_from_slice(text.as_bytes());
113 self.gap_start += text.len();
114 }
115
116 pub(crate) fn delete(&mut self, pos: usize) -> Option<u8> {
118 if pos >= self.len() {
119 return None;
120 }
121 self.move_gap_to(pos);
122 let ch = self.buf[self.gap_end];
123 self.gap_end += 1;
124 Some(ch)
125 }
126
127 pub(crate) fn delete_range(&mut self, start: usize, end: usize) -> usize {
129 if start >= end || start >= self.len() {
130 return 0;
131 }
132 let end = core::cmp::min(end, self.len());
133 self.move_gap_to(start);
134 let count = end - start;
135 self.gap_end += count;
136 count
137 }
138
139 pub(crate) fn char_at(&self, pos: usize) -> Option<u8> {
141 if pos >= self.len() {
142 return None;
143 }
144 let actual_pos = if pos < self.gap_start {
145 pos
146 } else {
147 pos + self.gap_len()
148 };
149 Some(self.buf[actual_pos])
150 }
151
152 pub(crate) fn to_text(&self) -> String {
154 let mut result = Vec::with_capacity(self.len());
155 for i in 0..self.gap_start {
156 result.push(self.buf[i]);
157 }
158 for i in self.gap_end..self.buf.len() {
159 result.push(self.buf[i]);
160 }
161 String::from_utf8(result).unwrap_or_default()
162 }
163
164 pub(crate) fn line(&self, line_num: usize) -> Option<String> {
166 let text = self.to_text();
167 text.lines().nth(line_num).map(|s| s.to_string())
168 }
169
170 pub(crate) fn line_count(&self) -> usize {
172 let text = self.to_text();
173 text.lines().count().max(1)
174 }
175}
176
177#[derive(Debug, Clone)]
179enum EditOp {
180 Insert { pos: usize, text: String },
181 Delete { pos: usize, text: String },
182}
183
184pub(crate) struct EditorBuffer {
186 pub(crate) name: String,
187 pub(crate) gap_buf: GapBuffer,
188 pub(crate) cursor_pos: usize,
189 pub(crate) cursor_line: usize,
190 pub(crate) cursor_col: usize,
191 pub(crate) modified: bool,
192 undo_stack: Vec<EditOp>,
193 redo_stack: Vec<EditOp>,
194 pub(crate) scroll_top: usize,
195}
196
197impl EditorBuffer {
198 pub(crate) fn new(name: &str) -> Self {
199 Self {
200 name: name.to_string(),
201 gap_buf: GapBuffer::new(),
202 cursor_pos: 0,
203 cursor_line: 0,
204 cursor_col: 0,
205 modified: false,
206 undo_stack: Vec::new(),
207 redo_stack: Vec::new(),
208 scroll_top: 0,
209 }
210 }
211
212 pub(crate) fn from_text(name: &str, text: &str) -> Self {
213 Self {
214 name: name.to_string(),
215 gap_buf: GapBuffer::from_text(text),
216 cursor_pos: 0,
217 cursor_line: 0,
218 cursor_col: 0,
219 modified: false,
220 undo_stack: Vec::new(),
221 redo_stack: Vec::new(),
222 scroll_top: 0,
223 }
224 }
225
226 pub(crate) fn insert_char(&mut self, ch: u8) {
227 self.gap_buf.insert(self.cursor_pos, ch);
228 let text = String::from(ch as char);
229 self.undo_stack.push(EditOp::Insert {
230 pos: self.cursor_pos,
231 text,
232 });
233 self.redo_stack.clear();
234 self.cursor_pos += 1;
235 self.modified = true;
236 self.update_cursor_pos();
237 }
238
239 pub(crate) fn insert_text(&mut self, text: &str) {
240 self.gap_buf.insert_str(self.cursor_pos, text);
241 self.undo_stack.push(EditOp::Insert {
242 pos: self.cursor_pos,
243 text: text.to_string(),
244 });
245 self.redo_stack.clear();
246 self.cursor_pos += text.len();
247 self.modified = true;
248 self.update_cursor_pos();
249 }
250
251 pub(crate) fn delete_char(&mut self) -> Option<u8> {
252 if self.cursor_pos >= self.gap_buf.len() {
253 return None;
254 }
255 let ch = self.gap_buf.delete(self.cursor_pos)?;
256 self.undo_stack.push(EditOp::Delete {
257 pos: self.cursor_pos,
258 text: String::from(ch as char),
259 });
260 self.redo_stack.clear();
261 self.modified = true;
262 Some(ch)
263 }
264
265 pub(crate) fn backspace(&mut self) -> Option<u8> {
266 if self.cursor_pos == 0 {
267 return None;
268 }
269 self.cursor_pos -= 1;
270 self.delete_char()
271 }
272
273 pub(crate) fn undo(&mut self) -> bool {
274 let op = match self.undo_stack.pop() {
275 Some(op) => op,
276 None => return false,
277 };
278
279 match &op {
280 EditOp::Insert { pos, text } => {
281 self.gap_buf.delete_range(*pos, pos + text.len());
282 self.cursor_pos = *pos;
283 self.redo_stack.push(op);
284 }
285 EditOp::Delete { pos, text } => {
286 self.gap_buf.insert_str(*pos, text);
287 self.cursor_pos = pos + text.len();
288 self.redo_stack.push(op);
289 }
290 }
291
292 self.update_cursor_pos();
293 true
294 }
295
296 pub(crate) fn redo(&mut self) -> bool {
297 let op = match self.redo_stack.pop() {
298 Some(op) => op,
299 None => return false,
300 };
301
302 match &op {
303 EditOp::Insert { pos, text } => {
304 self.gap_buf.insert_str(*pos, text);
305 self.cursor_pos = pos + text.len();
306 self.undo_stack.push(op);
307 }
308 EditOp::Delete { pos, text: _ } => {
309 self.gap_buf.delete(self.cursor_pos);
310 self.cursor_pos = *pos;
311 self.undo_stack.push(op);
312 }
313 }
314
315 self.update_cursor_pos();
316 true
317 }
318
319 fn update_cursor_pos(&mut self) {
320 let text = self.gap_buf.to_text();
321 let before = &text[..self.cursor_pos.min(text.len())];
322 self.cursor_line = before.matches('\n').count();
323 self.cursor_col = before
324 .rfind('\n')
325 .map_or(before.len(), |p| before.len() - p - 1);
326 }
327
328 pub(crate) fn content(&self) -> String {
329 self.gap_buf.to_text()
330 }
331
332 pub(crate) fn line_count(&self) -> usize {
333 self.gap_buf.line_count()
334 }
335}
336
337pub(crate) struct Editor {
339 buffers: Vec<EditorBuffer>,
340 active: usize,
341}
342
343impl Default for Editor {
344 fn default() -> Self {
345 Self::new()
346 }
347}
348
349impl Editor {
350 pub(crate) fn new() -> Self {
351 Self {
352 buffers: Vec::new(),
353 active: 0,
354 }
355 }
356
357 pub(crate) fn open(&mut self, name: &str, content: &str) -> usize {
358 let idx = self.buffers.len();
359 self.buffers.push(EditorBuffer::from_text(name, content));
360 self.active = idx;
361 idx
362 }
363
364 pub(crate) fn close(&mut self, idx: usize) -> bool {
365 if idx >= self.buffers.len() {
366 return false;
367 }
368 self.buffers.remove(idx);
369 if self.active >= self.buffers.len() && !self.buffers.is_empty() {
370 self.active = self.buffers.len() - 1;
371 }
372 true
373 }
374
375 pub(crate) fn active_buffer(&self) -> Option<&EditorBuffer> {
376 self.buffers.get(self.active)
377 }
378
379 pub(crate) fn active_buffer_mut(&mut self) -> Option<&mut EditorBuffer> {
380 self.buffers.get_mut(self.active)
381 }
382
383 pub(crate) fn switch_to(&mut self, idx: usize) -> bool {
384 if idx < self.buffers.len() {
385 self.active = idx;
386 true
387 } else {
388 false
389 }
390 }
391
392 pub(crate) fn buffer_count(&self) -> usize {
393 self.buffers.len()
394 }
395}
396
397#[cfg(test)]
402mod tests {
403 use super::*;
404
405 #[test]
406 fn test_gap_buffer_new() {
407 let buf = GapBuffer::new();
408 assert_eq!(buf.len(), 0);
409 assert!(buf.is_empty());
410 }
411
412 #[test]
413 fn test_gap_buffer_insert() {
414 let mut buf = GapBuffer::new();
415 buf.insert(0, b'h');
416 buf.insert(1, b'i');
417 assert_eq!(buf.len(), 2);
418 assert_eq!(buf.to_text(), "hi");
419 }
420
421 #[test]
422 fn test_gap_buffer_insert_str() {
423 let mut buf = GapBuffer::new();
424 buf.insert_str(0, "hello");
425 assert_eq!(buf.to_text(), "hello");
426 assert_eq!(buf.len(), 5);
427 }
428
429 #[test]
430 fn test_gap_buffer_delete() {
431 let mut buf = GapBuffer::from_text("hello");
432 let ch = buf.delete(0);
433 assert_eq!(ch, Some(b'h'));
434 assert_eq!(buf.to_text(), "ello");
435 }
436
437 #[test]
438 fn test_gap_buffer_delete_range() {
439 let mut buf = GapBuffer::from_text("hello world");
440 let count = buf.delete_range(5, 11);
441 assert_eq!(count, 6);
442 assert_eq!(buf.to_text(), "hello");
443 }
444
445 #[test]
446 fn test_gap_buffer_char_at() {
447 let buf = GapBuffer::from_text("abc");
448 assert_eq!(buf.char_at(0), Some(b'a'));
449 assert_eq!(buf.char_at(2), Some(b'c'));
450 assert_eq!(buf.char_at(3), None);
451 }
452
453 #[test]
454 fn test_gap_buffer_line() {
455 let buf = GapBuffer::from_text("line1\nline2\nline3");
456 assert_eq!(buf.line(0), Some("line1".to_string()));
457 assert_eq!(buf.line(1), Some("line2".to_string()));
458 assert_eq!(buf.line(2), Some("line3".to_string()));
459 }
460
461 #[test]
462 fn test_gap_buffer_line_count() {
463 let buf = GapBuffer::from_text("a\nb\nc");
464 assert_eq!(buf.line_count(), 3);
465 }
466
467 #[test]
468 fn test_editor_buffer_insert() {
469 let mut buf = EditorBuffer::new("test.txt");
470 buf.insert_text("hello");
471 assert_eq!(buf.content(), "hello");
472 assert!(buf.modified);
473 }
474
475 #[test]
476 fn test_editor_buffer_undo_redo() {
477 let mut buf = EditorBuffer::new("test.txt");
478 buf.insert_text("hello");
479 assert_eq!(buf.content(), "hello");
480
481 assert!(buf.undo());
482 assert_eq!(buf.content(), "");
483
484 assert!(buf.redo());
485 assert_eq!(buf.content(), "hello");
486 }
487
488 #[test]
489 fn test_editor_multi_buffer() {
490 let mut editor = Editor::new();
491 editor.open("a.txt", "file A");
492 editor.open("b.txt", "file B");
493
494 assert_eq!(editor.buffer_count(), 2);
495 assert_eq!(editor.active_buffer().unwrap().name, "b.txt");
496
497 editor.switch_to(0);
498 assert_eq!(editor.active_buffer().unwrap().name, "a.txt");
499 }
500
501 #[test]
502 fn test_editor_close() {
503 let mut editor = Editor::new();
504 editor.open("a.txt", "");
505 editor.open("b.txt", "");
506 assert!(editor.close(0));
507 assert_eq!(editor.buffer_count(), 1);
508 }
509}