1use nalgebra::Point2;
2use std::iter::FromIterator;
3use std::iter::IntoIterator;
4use unicode_width::UnicodeWidthChar;
5
6pub const BLANK_CH: char = ' ';
7
8#[derive(Clone)]
13pub struct TextBuffer {
14 chars: Vec<Vec<Ch>>,
15 cursor: Point2<usize>,
16}
17
18impl Default for TextBuffer {
19 fn default() -> Self {
20 Self {
21 chars: vec![],
22 cursor: Point2::new(0, 0),
23 }
24 }
25}
26
27#[derive(Clone, Copy, Debug)]
28pub struct Ch {
29 pub ch: char,
30 pub width: usize,
31}
32
33impl Ch {
34 pub fn new(ch: char) -> Self {
35 Self {
36 width: ch.width().unwrap_or(0),
37 ch,
38 }
39 }
40}
41
42impl TextBuffer {
43 pub fn new_from_str(content: &str) -> Self {
44 Self {
45 chars: content
46 .lines()
47 .map(|line| line.chars().map(Ch::new).collect())
48 .collect(),
49 cursor: Point2::new(0, 0),
50 }
51 }
52
53 pub fn from_ch(chars: &[&[Ch]]) -> Self {
54 Self {
55 chars: chars.iter().map(|inner| inner.to_vec()).collect(),
56 cursor: Point2::new(0, 0),
57 }
58 }
59
60 pub fn is_empty(&self) -> bool {
61 self.chars.is_empty()
62 }
63
64 pub fn chars(&self) -> &[Vec<Ch>] {
65 &self.chars
66 }
67
68 pub fn total_chars(&self) -> usize {
71 self.chars.iter().map(|line| line.len()).sum()
72 }
73
74 pub fn split_line_at_point(&self, loc: Point2<usize>) -> (String, String) {
75 let loc = self.point_to_index(loc);
76 let first = &self.chars[loc.y][0..loc.x];
77 let first_str = String::from_iter(first.iter().map(|ch| ch.ch));
78 let second = &self.chars[loc.y][loc.x..];
79 let second_str = String::from_iter(second.iter().map(|ch| ch.ch));
80 (first_str, second_str)
81 }
82
83 pub fn split_line_at_2_points(
84 &self,
85 loc: Point2<usize>,
86 loc2: Point2<usize>,
87 ) -> (String, String, String) {
88 let loc = self.point_to_index(loc);
89 let loc2 = self.point_to_index(loc2);
90
91 let first = &self.chars[loc.y][0..loc.x];
92 let first_str = String::from_iter(first.iter().map(|ch| ch.ch));
93 let second = &self.chars[loc.y][loc.x..loc2.x];
94 let second_str = String::from_iter(second.iter().map(|ch| ch.ch));
95 let third = &self.chars[loc.y][loc2.x..];
96 let third_str = String::from_iter(third.iter().map(|ch| ch.ch));
97
98 (first_str, second_str, third_str)
99 }
100
101 pub fn cut_text_in_linear_mode(&mut self, start: Point2<usize>, end: Point2<usize>) -> String {
103 let start = self.point_to_index(start);
104 let end = self.point_to_index(end);
105 let is_one_line = start.y == end.y;
106 if is_one_line {
107 let selection: Vec<Ch> = self.chars[start.y].drain(start.x..=end.x).collect();
108 String::from_iter(selection.iter().map(|ch| ch.ch))
109 } else {
110 let end_text: Vec<Ch> = self.chars[end.y].drain(0..=end.x).collect();
111
112 let mid_text_range = start.y + 1..end.y;
113 let mid_text: Option<Vec<Vec<Ch>>> = if !mid_text_range.is_empty() {
114 Some(self.chars.drain(mid_text_range).collect())
115 } else {
116 None
117 };
118 let start_text: Vec<Ch> = self.chars[start.y].drain(start.x..).collect();
119
120 let start_text_str: String = String::from_iter(start_text.iter().map(|ch| ch.ch));
121
122 let end_text_str: String = String::from_iter(end_text.iter().map(|ch| ch.ch));
123
124 if let Some(mid_text) = mid_text {
125 let mid_text_str: String = mid_text
126 .iter()
127 .map(|line| String::from_iter(line.iter().map(|ch| ch.ch)))
128 .collect::<Vec<_>>()
129 .join("\n");
130
131 [start_text_str, mid_text_str, end_text_str].join("\n")
132 } else {
133 [start_text_str, end_text_str].join("\n")
134 }
135 }
136 }
137
138 pub fn get_text_in_linear_mode(&self, start: Point2<usize>, end: Point2<usize>) -> String {
140 let start = self.point_to_index(start);
141 let end = self.point_to_index(end);
142 let is_one_line = start.y == end.y;
143 if is_one_line {
144 let selection: &[Ch] = &self.chars[start.y][start.x..=end.x];
145 String::from_iter(selection.iter().map(|ch| ch.ch))
146 } else {
147 let start_text: &[Ch] = &self.chars[start.y][start.x..];
148
149 let mid_text_range = start.y + 1..end.y;
150 let mid_text: Option<&[Vec<Ch>]> = if !mid_text_range.is_empty() {
151 Some(&self.chars[mid_text_range])
152 } else {
153 None
154 };
155
156 let end_text: &[Ch] = &self.chars[end.y][0..=end.x];
157 let start_text_str: String = String::from_iter(start_text.iter().map(|ch| ch.ch));
158
159 let end_text_str: String = String::from_iter(end_text.iter().map(|ch| ch.ch));
160
161 if let Some(mid_text) = mid_text {
162 let mid_text_str: String = mid_text
163 .iter()
164 .map(|line| String::from_iter(line.iter().map(|ch| ch.ch)))
165 .collect::<Vec<_>>()
166 .join("\n");
167
168 [start_text_str, mid_text_str, end_text_str].join("\n")
169 } else {
170 [start_text_str, end_text_str].join("\n")
171 }
172 }
173 }
174
175 pub fn get_text_in_block_mode(&self, start: Point2<usize>, end: Point2<usize>) -> String {
177 let start = self.point_to_index(start);
178 let end = self.point_to_index(end);
179 log::info!("here: {} {}", start, end);
180 (start.y..=end.y)
181 .map(|y| {
182 if let Some(chars) = &self.chars.get(y) {
183 let text =
184 (start.x..=end.x).map(|x| chars.get(x).map(|ch| ch.ch).unwrap_or(BLANK_CH));
185 String::from_iter(text)
186 } else {
187 String::new()
188 }
189 })
190 .collect::<Vec<_>>()
191 .join("\n")
192 }
193
194 pub fn cut_text_in_block_mode(&mut self, start: Point2<usize>, end: Point2<usize>) -> String {
195 let start = self.point_to_index(start);
196 let end = self.point_to_index(end);
197 (start.y..=end.y)
198 .map(|y| {
199 let text = self.chars[y].drain(start.x..=end.x);
200 String::from_iter(text.map(|ch| ch.ch))
201 })
202 .collect::<Vec<_>>()
203 .join("\n")
204 }
205
206 pub fn paste_text_in_block_mode(&mut self, text_block: String) {
208 for (line_index, line) in text_block.lines().enumerate() {
209 let mut width = 0;
210 let y = self.cursor.y + line_index;
211 for ch in line.chars() {
212 let x = self.cursor.x + width;
213 self.replace_char(Point2::new(x, y), ch);
214 width += ch.width().unwrap_or(0);
215 }
216 }
217 }
218}
219
220impl TextBuffer {
224 pub fn total_lines(&self) -> usize {
226 self.chars.len()
227 }
228
229 pub fn numberline_wide(&self) -> usize {
232 self.total_lines().to_string().len()
233 }
234
235 pub fn lines(&self) -> Vec<String> {
236 self.chars
237 .iter()
238 .map(|line| String::from_iter(line.iter().map(|ch| ch.ch)))
239 .collect()
240 }
241
242 pub fn first_non_blank_line(&self) -> Option<usize> {
244 self.chars
245 .iter()
246 .enumerate()
247 .find_map(|(line_index, line)| {
248 if line.iter().any(|ch| ch.ch != BLANK_CH) {
249 Some(line_index)
250 } else {
251 None
252 }
253 })
254 }
255
256 pub fn last_non_blank_line(&self) -> Option<usize> {
258 self.chars
259 .iter()
260 .enumerate()
261 .rev()
262 .find_map(|(line_index, line)| {
263 if line.iter().any(|ch| ch.ch != BLANK_CH) {
264 Some(line_index)
265 } else {
266 None
267 }
268 })
269 }
270
271 pub fn line_width(&self, n: usize) -> usize {
273 self.chars
274 .get(n)
275 .map(|line| line.iter().map(|ch| ch.width).sum())
276 .unwrap_or(0)
277 }
278
279 pub fn max_column_width(&self) -> usize {
281 self.chars
282 .iter()
283 .map(|line| line.iter().map(|ch| ch.width).sum())
284 .max()
285 .unwrap_or(0)
286 }
287
288 pub fn max_position(&self) -> Point2<usize> {
291 let last_line = self.total_lines().saturating_sub(1);
292 let max_column = self.max_column_width();
293 Point2::new(max_column, last_line)
294 }
295
296 pub fn last_char_position(&self) -> Point2<usize> {
297 let last_line = self.total_lines().saturating_sub(1);
298 let bottom_last_x = self.line_width(last_line).saturating_sub(1);
299 Point2::new(bottom_last_x, last_line)
300 }
301
302 pub fn break_line(&mut self, loc: Point2<usize>) {
304 self.ensure_before_cell_exist(loc);
305 let line = &self.chars[loc.y];
306 if let Some(break_point) = self.column_index(loc) {
307 let (break1, break2): (Vec<_>, Vec<_>) = line
308 .iter()
309 .enumerate()
310 .partition(|(i, _ch)| *i < break_point);
311
312 let break1: Vec<Ch> = break1.into_iter().map(|(_, ch)| *ch).collect();
313 let break2: Vec<Ch> = break2.into_iter().map(|(_, ch)| *ch).collect();
314 self.chars.remove(loc.y);
315 self.chars.insert(loc.y, break2);
316 self.chars.insert(loc.y, break1);
317 } else {
318 self.chars.insert(loc.y + 1, vec![]);
319 }
320 }
321
322 pub fn join_line(&mut self, loc: Point2<usize>) {
323 let next_line_index = loc.y.saturating_add(1);
324 let mut next_line = self.chars.remove(next_line_index);
325 self.chars[loc.y].append(&mut next_line);
326 }
327
328 pub fn ensure_line_exist(&mut self, y: usize) {
330 let total_lines = self.total_lines();
331 let diff = y.saturating_add(1).saturating_sub(total_lines);
332 for _ in 0..diff {
333 self.chars.push(vec![]);
334 }
335 }
336
337 pub fn ensure_before_line_exist(&mut self, y: usize) {
338 if y > 0 {
339 self.ensure_line_exist(y.saturating_sub(1));
340 }
341 }
342
343 pub fn ensure_cell_exist(&mut self, loc: Point2<usize>) {
345 self.ensure_line_exist(loc.y);
346 let line_width = self.line_width(loc.y);
347 let diff = loc.x.saturating_add(1).saturating_sub(line_width);
348 for _ in 0..diff {
349 self.chars[loc.y].push(Ch::new(BLANK_CH));
350 }
351 }
352
353 pub fn ensure_before_cell_exist(&mut self, loc: Point2<usize>) {
354 self.ensure_line_exist(loc.y);
355 if loc.x > 0 {
356 self.ensure_cell_exist(Point2::new(loc.x.saturating_sub(1), loc.y));
357 }
358 }
359
360 fn column_index(&self, loc: Point2<usize>) -> Option<usize> {
363 if let Some(line) = self.chars.get(loc.y) {
364 let mut width_sum = 0;
365 for (i, ch) in line.iter().enumerate() {
366 if width_sum == loc.x {
367 return Some(i);
368 }
369 width_sum += ch.width;
370 }
371 None
372 } else {
373 None
374 }
375 }
376
377 fn point_to_index(&self, point: Point2<usize>) -> Point2<usize> {
380 let column_x = self.column_index(point).unwrap_or(point.x);
381 Point2::new(column_x, point.y)
382 }
383
384 pub fn insert_char(&mut self, loc: Point2<usize>, ch: char) {
386 self.ensure_before_cell_exist(loc);
387 let new_ch = Ch::new(ch);
388 if let Some(column_index) = self.column_index(loc) {
389 let insert_index = column_index;
390 self.chars[loc.y].insert(insert_index, new_ch);
391 } else {
392 self.chars[loc.y].push(new_ch);
393 }
394 }
395
396 fn insert_line_text(&mut self, loc: Point2<usize>, text: &str) {
398 let mut width_inc = 0;
399 for ch in text.chars() {
400 let new_ch = Ch::new(ch);
401 self.insert_char(Point2::new(loc.x + width_inc, loc.y), new_ch.ch);
402 width_inc += new_ch.width;
403 }
404 }
405
406 pub fn insert_text(&mut self, loc: Point2<usize>, text: &str) {
407 let mut start = loc.x;
408 for (i, line) in text.lines().enumerate() {
409 if i > 0 {
410 self.chars.insert(loc.y + 1, vec![]);
411 }
412 self.insert_line_text(Point2::new(start, loc.y + i), line);
413 start = 0;
414 }
415 }
416
417 pub fn replace_char(&mut self, loc: Point2<usize>, ch: char) -> Option<char> {
419 self.ensure_cell_exist(loc);
420 let column_index = self.column_index(loc).expect("must have a column index");
421 let ex_ch = self.chars[loc.y].remove(column_index);
422 self.chars[loc.y].insert(column_index, Ch::new(ch));
423 Some(ex_ch.ch)
424 }
425
426 pub fn get_char(&self, loc: Point2<usize>) -> Option<char> {
428 if let Some(line) = self.chars.get(loc.y) {
429 let column_index = self.column_index(loc);
430 column_index.and_then(|col| line.get(col).map(|ch| ch.ch))
431 } else {
432 None
433 }
434 }
435
436 pub fn delete_char(&mut self, loc: Point2<usize>) -> Option<char> {
438 if let Some(column_index) = self.column_index(loc) {
439 let ex_ch = self.chars[loc.y].remove(column_index);
440 Some(ex_ch.ch)
441 } else {
442 None
443 }
444 }
445
446 pub fn get_position(&self) -> Point2<usize> {
448 self.cursor
449 }
450}
451
452impl TextBuffer {
462 pub fn command_insert_char(&mut self, ch: char) {
463 self.insert_char(self.cursor, ch);
464 let width = ch.width().expect("must have a unicode width");
465 self.move_x(width);
466 }
467
468 pub fn command_replace_char(&mut self, ch: char) -> Option<char> {
469 self.replace_char(self.cursor, ch)
470 }
471
472 pub fn command_insert_text(&mut self, text: &str) {
473 self.insert_text(self.cursor, text);
474 }
475
476 pub fn move_left(&mut self) {
477 self.cursor.x = self.cursor.x.saturating_sub(1);
478 }
479
480 pub fn move_left_start(&mut self) {
481 self.cursor.x = 0;
482 }
483
484 pub fn move_right(&mut self) {
485 self.cursor.x = self.cursor.x.saturating_add(1);
486 }
487
488 fn line_max_column(&self, line: usize) -> usize {
489 self.chars.get(line).map(|line| line.len()).unwrap_or(0)
490 }
491
492 fn current_line_max_column(&self) -> usize {
493 self.line_max_column(self.cursor.y)
494 }
495
496 pub fn move_right_clamped(&mut self) {
497 if self.cursor.x < self.current_line_max_column() {
498 self.move_right();
499 }
500 }
501
502 pub fn move_right_end(&mut self) {
503 self.cursor.x = self.current_line_max_column();
504 }
505
506 pub fn move_x(&mut self, x: usize) {
507 self.cursor.x = self.cursor.x.saturating_add(x);
508 }
509
510 pub fn move_y(&mut self, y: usize) {
511 self.cursor.y = self.cursor.y.saturating_add(y);
512 }
513
514 pub fn move_up(&mut self) {
515 self.cursor.y = self.cursor.y.saturating_sub(1);
516 }
517
518 pub fn move_down(&mut self) {
519 self.cursor.y = self.cursor.y.saturating_add(1);
520 }
521
522 pub fn move_up_clamped(&mut self) {
523 let target_line = self.cursor.y.saturating_sub(1);
524 let target_line_max_column = self.line_max_column(target_line);
525 if target_line < self.total_lines() {
526 if self.cursor.x > target_line_max_column {
527 self.cursor.x = target_line_max_column;
528 }
529 self.move_up()
530 }
531 }
532
533 pub fn clamp_position(&self, loc: Point2<usize>) -> Point2<usize> {
534 let line = loc.y;
535 let line_max_column = self.line_max_column(line);
536 let loc_x = if loc.x > line_max_column {
537 line_max_column
538 } else {
539 loc.x
540 };
541 Point2::new(loc_x, loc.y)
542 }
543
544 pub fn move_down_clamped(&mut self) {
545 let target_line = self.cursor.y.saturating_add(1);
546 let target_line_max_column = self.line_max_column(target_line);
547 if target_line < self.total_lines() {
548 if self.cursor.x > target_line_max_column {
549 self.cursor.x = target_line_max_column;
550 }
551 self.move_down()
552 }
553 }
554
555 pub fn set_position(&mut self, pos: Point2<usize>) {
556 self.cursor = pos;
557 }
558
559 pub fn set_position_clamped(&mut self, pos: Point2<usize>) {
562 let total_lines = self.total_lines();
563 let mut y = pos.y;
564 if y > total_lines {
565 y = total_lines.saturating_sub(1);
566 }
567 let line_width = self.line_width(y);
568 let mut x = pos.x;
569 if x > line_width {
570 x = line_width.saturating_sub(1);
571 }
572 self.set_position(Point2::new(x, y))
573 }
574
575 pub fn command_break_line(&mut self, loc: Point2<usize>) {
576 self.break_line(loc);
577 self.move_left_start();
578 self.move_down();
579 }
580
581 pub fn command_join_line(&mut self, loc: Point2<usize>) {
582 self.join_line(loc);
583 self.set_position(loc);
584 }
585
586 pub fn command_delete_back(&mut self) -> Option<char> {
587 if self.cursor.x > 0 {
588 let c = self.delete_char(Point2::new(self.cursor.x.saturating_sub(1), self.cursor.y));
589 self.move_left();
590 c
591 } else {
592 None
593 }
594 }
595
596 pub fn command_delete_forward(&mut self) -> Option<char> {
597 self.delete_char(self.cursor)
598 }
599
600 pub fn move_to(&mut self, pos: Point2<usize>) {
602 self.set_position(pos);
603 }
604
605 pub fn clear(&mut self) {
607 self.chars.clear();
608 }
609}
610
611impl ToString for TextBuffer {
612 fn to_string(&self) -> String {
613 self.chars
614 .iter()
615 .map(|line| String::from_iter(line.iter().map(|ch| ch.ch)))
616 .collect::<Vec<_>>()
617 .join("\n")
618 }
619}