1use std::ops::Range;
2use std::path::Path;
3
4use ropey::Rope;
5
6use crate::{
7 coordinates::Point,
8 error::{EditorError, Result},
9 history::{Edit, History, Transaction},
10};
11
12#[derive(Debug)]
32pub struct EditorBuffer {
33 text: Rope,
34 cursor: usize,
35 history: History,
36 version: usize,
37}
38
39impl EditorBuffer {
40 pub fn new(initial_text: &str) -> Self {
45 Self {
46 text: Rope::from_str(initial_text),
47 cursor: 0,
48 history: History::default(),
49 version: 0,
50 }
51 }
52
53 pub fn version(&self) -> usize {
55 self.version
56 }
57
58 pub fn text(&self) -> &Rope {
63 &self.text
64 }
65
66 pub fn cursor_offset(&self) -> usize {
70 self.cursor
71 }
72
73 pub fn len_bytes(&self) -> usize {
75 self.text.len_bytes()
76 }
77
78 pub fn len_lines(&self) -> usize {
80 self.text.len_lines()
81 }
82
83 pub fn line_to_string(&self, line_idx: usize) -> String {
87 if line_idx >= self.text.len_lines() {
88 return String::new();
89 }
90
91 self.text.line(line_idx).to_string()
92 }
93
94 pub fn offset_to_point(&self, offset: usize) -> Point {
100 let clamped = offset.min(self.text.len_bytes());
101 let row = self.text.byte_to_line(clamped);
102 let line_start_byte = self.text.line_to_byte(row);
103 let column = clamped - line_start_byte;
104
105 Point::new(row, column)
106 }
107
108 pub fn point_to_offset(&self, point: Point) -> usize {
114 if point.row >= self.text.len_lines() {
115 return self.text.len_bytes();
116 }
117 let line_start_byte = self.text.line_to_byte(point.row);
118 let line_len = self.text.line(point.row).len_bytes();
119 let col = point.column.min(line_len);
120 line_start_byte + col
121 }
122
123 pub fn cursor_point(&self) -> Point {
125 self.offset_to_point(self.cursor)
126 }
127
128 pub fn set_cursor_offset(&mut self, offset: usize) {
135 let offset = offset.min(self.text.len_bytes());
136 self.cursor = self.text.char_to_byte(self.text.byte_to_char(offset));
137 }
138
139 pub fn set_cursor_point(&mut self, point: Point) {
143 self.cursor = self.point_to_offset(point);
144 }
145
146 pub fn move_cursor_right(&mut self) {
150 if self.cursor < self.text.len_bytes() {
151 let char_idx = self.text.byte_to_char(self.cursor);
152 let next_char = (char_idx + 1).min(self.text.len_chars());
153 self.cursor = self.text.char_to_byte(next_char);
154 }
155 }
156
157 pub fn move_cursor_up(&mut self) {
162 let point = self.cursor_point();
163 if point.row > 0 {
164 self.set_cursor_point(Point::new(point.row - 1, point.column));
165 }
166 }
167
168 pub fn move_cursor_down(&mut self) {
173 let point = self.cursor_point();
174 if point.row + 1 < self.text.len_lines() {
175 self.set_cursor_point(Point::new(point.row + 1, point.column));
176 }
177 }
178
179 pub fn move_cursor_left(&mut self) {
184 if self.cursor > 0 {
185 let char_idx = self.text.byte_to_char(self.cursor);
186 self.cursor = self.text.char_to_byte(char_idx - 1);
187 }
188 }
189
190 pub fn prev_word_offset(&self) -> usize {
192 crate::movement::find_prev_word_start(&self.text, self.cursor)
193 }
194
195 pub fn next_word_offset(&self) -> usize {
197 crate::movement::find_next_word_end(&self.text, self.cursor)
198 }
199
200 pub fn line_start_offset(&self) -> usize {
202 crate::movement::find_line_start(&self.text, self.cursor)
203 }
204
205 pub fn line_end_offset(&self) -> usize {
207 crate::movement::find_line_end(&self.text, self.cursor)
208 }
209
210 pub fn word_range_at(&self, offset: usize) -> Range<usize> {
212 crate::movement::find_word_range_at(&self.text, offset)
213 }
214
215 pub fn line_range_at(&self, offset: usize) -> Range<usize> {
218 crate::movement::find_line_range_at(&self.text, offset)
219 }
220
221 pub fn move_cursor_prev_word(&mut self) {
223 self.cursor = self.prev_word_offset();
224 }
225
226 pub fn move_cursor_next_word(&mut self) {
228 self.cursor = self.next_word_offset();
229 }
230
231 pub fn move_cursor_line_start(&mut self) {
233 self.cursor = self.line_start_offset();
234 }
235
236 pub fn move_cursor_line_end(&mut self) {
238 self.cursor = self.line_end_offset();
239 }
240
241 pub fn delete_prev_word(&mut self) -> bool {
245 let target = self.prev_word_offset();
246 if target < self.cursor {
247 self.delete_range(target..self.cursor);
248 true
249 } else {
250 false
251 }
252 }
253
254 pub fn delete_next_word(&mut self) -> bool {
258 let target = self.next_word_offset();
259 if self.cursor < target {
260 self.delete_range(self.cursor..target);
261 true
262 } else {
263 false
264 }
265 }
266
267 pub fn insert(&mut self, text: &str) {
274 let previous_cursor = self.cursor;
275 let char_idx = self.text.byte_to_char(self.cursor);
276 self.text.insert(char_idx, text);
277 self.cursor += text.len();
278
279 let tx = Transaction {
280 edits: vec![Edit {
281 bytes_range: previous_cursor..previous_cursor,
282 inserted_text: text.to_string(),
283 deleted_text: String::new(),
284 }],
285 previous_cursor,
286 resulting_cursor: self.cursor,
287 };
288
289 self.history.undo_stack.push(tx);
290 self.history.redo_stack.clear();
291 self.version += 1;
292 }
293
294 pub fn backspace(&mut self) {
304 if self.cursor == 0 {
305 return;
306 }
307
308 let char_idx = self.text.byte_to_char(self.cursor);
309 let previous_char_byte = self.text.char_to_byte(char_idx - 1);
310 let range_to_delete = previous_char_byte..self.cursor;
311 let deleted_text = self.text.byte_slice(range_to_delete.clone()).to_string();
312
313 let previous_cursor = self.cursor;
314 self.text.remove((char_idx - 1)..char_idx);
315 self.cursor = previous_char_byte;
316
317 let tx = Transaction {
318 edits: vec![Edit {
319 bytes_range: range_to_delete,
320 inserted_text: String::new(),
321 deleted_text,
322 }],
323 previous_cursor,
324 resulting_cursor: self.cursor,
325 };
326
327 self.history.undo_stack.push(tx);
328 self.history.redo_stack.clear();
329 self.version += 1;
330 }
331
332 pub fn delete(&mut self) {
340 if self.cursor >= self.text.len_bytes() {
341 return;
342 }
343
344 let char_idx = self.text.byte_to_char(self.cursor);
345 let next_char = char_idx + 1;
346
347 let end = self.text.char_to_byte(next_char);
348 let byte_range = self.cursor..end;
349 let deleted_text = self.text.byte_slice(byte_range.clone()).to_string();
350
351 self.text.remove(char_idx..next_char);
352
353 let tx = Transaction {
354 edits: vec![Edit {
355 bytes_range: byte_range,
356 inserted_text: String::new(),
357 deleted_text,
358 }],
359 previous_cursor: self.cursor,
360 resulting_cursor: self.cursor,
361 };
362
363 self.history.undo_stack.push(tx);
364 self.history.redo_stack.clear();
365 self.version += 1;
366 }
367
368 pub fn delete_range(&mut self, range: Range<usize>) {
373 let start = range.start.min(self.text.len_bytes());
374 let end = range.end.min(self.text.len_bytes());
375 if start >= end {
376 return;
377 }
378
379 let start_char = self.text.byte_to_char(start);
380 let end_char = self.text.byte_to_char(end);
381 let deleted_text = self.text.byte_slice(start..end).to_string();
382 let previous_cursor = self.cursor;
383
384 self.text.remove(start_char..end_char);
385 self.cursor = start;
386
387 let tx = Transaction {
388 edits: vec![Edit {
389 bytes_range: start..end,
390 inserted_text: String::new(),
391 deleted_text,
392 }],
393 previous_cursor,
394 resulting_cursor: self.cursor,
395 };
396
397 self.history.undo_stack.push(tx);
398 self.history.redo_stack.clear();
399 self.version += 1;
400 }
401
402 pub fn replace_range(&mut self, range: Range<usize>, text: &str) {
406 let start = range.start.min(self.text.len_bytes());
407 let end = range.end.min(self.text.len_bytes());
408 if start == end {
409 self.cursor = start;
410 self.insert(text);
411 return;
412 }
413
414 let start_char = self.text.byte_to_char(start);
415 let end_char = self.text.byte_to_char(end);
416 let deleted_text = self.text.byte_slice(start..end).to_string();
417 let previous_cursor = self.cursor;
418
419 self.text.remove(start_char..end_char);
420 self.text.insert(start_char, text);
421 self.cursor = start + text.len();
422
423 let tx = Transaction {
424 edits: vec![Edit {
425 bytes_range: start..end,
426 inserted_text: text.to_string(),
427 deleted_text,
428 }],
429 previous_cursor,
430 resulting_cursor: self.cursor,
431 };
432
433 self.history.undo_stack.push(tx);
434 self.history.redo_stack.clear();
435 self.version += 1;
436 }
437
438 pub fn replace_many(&mut self, replacements: Vec<(Range<usize>, String)>) -> usize {
446 let len = self.text.len_bytes();
447 let mut valid: Vec<(usize, usize, String)> = Vec::with_capacity(replacements.len());
448 for (range, text) in replacements {
449 if range.start >= range.end || range.end > len {
450 continue;
451 }
452 if !self.is_char_boundary(range.start) || !self.is_char_boundary(range.end) {
453 continue;
454 }
455 valid.push((range.start, range.end, text));
456 }
457 if valid.is_empty() {
458 return 0;
459 }
460 valid.sort_by_key(|(start, _, _)| *start);
461 let mut dedup: Vec<(usize, usize, String)> = Vec::with_capacity(valid.len());
464 for (start, end, text) in valid {
465 if let Some((_, last_end, _)) = dedup.last()
466 && start < *last_end
467 {
468 continue;
469 }
470 dedup.push((start, end, text));
471 }
472 if dedup.is_empty() {
473 return 0;
474 }
475
476 let previous_cursor = self.cursor;
477 let mut edits: Vec<Edit> = Vec::with_capacity(dedup.len());
480 for (start, end, text) in dedup.iter().rev() {
481 let deleted_text = self.text.byte_slice(*start..*end).to_string();
482 let start_char = self.text.byte_to_char(*start);
483 let end_char = self.text.byte_to_char(*end);
484 self.text.remove(start_char..end_char);
485 self.text.insert(start_char, text);
486 edits.push(Edit {
487 bytes_range: *start..*end,
488 inserted_text: text.clone(),
489 deleted_text,
490 });
491 }
492 edits.reverse();
493
494 let mut shift: i64 = 0;
497 for edit in &edits[..edits.len() - 1] {
498 shift += edit.inserted_text.len() as i64
499 - (edit.bytes_range.end - edit.bytes_range.start) as i64;
500 }
501 let last = &edits[edits.len() - 1];
502 let new_cursor = (last.bytes_range.start as i64 + shift + last.inserted_text.len() as i64)
503 .max(0) as usize;
504 self.cursor = new_cursor.min(self.text.len_bytes());
505
506 let tx = Transaction {
507 edits,
508 previous_cursor,
509 resulting_cursor: self.cursor,
510 };
511 let applied = tx.edits.len();
512
513 self.history.undo_stack.push(tx);
514 self.history.redo_stack.clear();
515 self.version += 1;
516 applied
517 }
518
519 pub fn undo(&mut self) {
524 if let Some(tx) = self.history.undo_stack.pop() {
525 let mut prefix = Vec::with_capacity(tx.edits.len() + 1);
529 prefix.push(0i64);
530 for edit in &tx.edits {
531 let delta = edit.inserted_text.len() as i64
532 - (edit.bytes_range.end - edit.bytes_range.start) as i64;
533 prefix.push(prefix.last().copied().unwrap_or(0) + delta);
534 }
535 for (index, edit) in tx.edits.iter().enumerate().rev() {
536 let start = (edit.bytes_range.start as i64 + prefix[index]).max(0) as usize;
537 let end = start + edit.inserted_text.len();
538
539 if end > start {
540 let start_char = self.text.byte_to_char(start);
541 let end_char = self.text.byte_to_char(end);
542 self.text.remove(start_char..end_char);
543 }
544 if !edit.deleted_text.is_empty() {
545 let start_char = self.text.byte_to_char(start);
546 self.text.insert(start_char, &edit.deleted_text);
547 }
548 }
549 self.cursor = tx.previous_cursor;
550 self.history.redo_stack.push(tx);
551 self.version += 1;
552 }
553 }
554
555 pub fn can_undo(&self) -> bool {
557 !self.history.undo_stack.is_empty()
558 }
559
560 pub fn can_redo(&self) -> bool {
562 !self.history.redo_stack.is_empty()
563 }
564
565 pub fn redo(&mut self) {
570 if let Some(tx) = self.history.redo_stack.pop() {
571 for edit in tx.edits.iter().rev() {
574 let start = edit.bytes_range.start;
575 let end = start + edit.deleted_text.len();
576
577 if end > start {
578 let start_char = self.text.byte_to_char(start);
579 let end_char = self.text.byte_to_char(end);
580 self.text.remove(start_char..end_char);
581 }
582 if !edit.inserted_text.is_empty() {
583 let start_char = self.text.byte_to_char(start);
584 self.text.insert(start_char, &edit.inserted_text);
585 }
586 }
587 self.cursor = tx.resulting_cursor;
588 self.history.undo_stack.push(tx);
589 self.version += 1;
590 }
591 }
592
593 pub fn is_char_boundary(&self, offset: usize) -> bool {
595 if offset > self.text.len_bytes() {
596 return false;
597 }
598 let char_idx = self.text.byte_to_char(offset);
599 self.text.char_to_byte(char_idx) == offset
600 }
601
602 pub fn validate_offset(&self, offset: usize) -> Result<()> {
604 let len = self.text.len_bytes();
605 if offset > len {
606 return Err(EditorError::OutOfBounds { offset, len });
607 }
608 if !self.is_char_boundary(offset) {
609 return Err(EditorError::InvalidCharBoundary { offset });
610 }
611 Ok(())
612 }
613
614 pub fn validate_range(&self, range: &Range<usize>) -> Result<()> {
616 let len = self.text.len_bytes();
617 if range.start > range.end || range.end > len {
618 return Err(EditorError::InvalidRange {
619 range: range.clone(),
620 len,
621 });
622 }
623 if !self.is_char_boundary(range.start) {
624 return Err(EditorError::InvalidCharBoundary {
625 offset: range.start,
626 });
627 }
628 if !self.is_char_boundary(range.end) {
629 return Err(EditorError::InvalidCharBoundary { offset: range.end });
630 }
631 Ok(())
632 }
633
634 pub fn try_line_to_string(&self, row: usize) -> Result<String> {
636 let total_lines = self.text.len_lines();
637 if row >= total_lines {
638 return Err(EditorError::InvalidRow { row, total_lines });
639 }
640 Ok(self.text.line(row).to_string())
641 }
642
643 pub fn try_replace_range(&mut self, range: Range<usize>, text: &str) -> Result<()> {
645 self.validate_range(&range)?;
646 self.replace_range(range, text);
647 Ok(())
648 }
649
650 pub fn try_delete_range(&mut self, range: Range<usize>) -> Result<()> {
652 self.validate_range(&range)?;
653 self.delete_range(range);
654 Ok(())
655 }
656
657 pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
659 let content = std::fs::read_to_string(path)?;
660 Ok(Self::new(&content))
661 }
662
663 pub fn save_to_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
665 std::fs::write(path, self.text.to_string())?;
666 Ok(())
667 }
668}