1use crate::cursor;
14use crate::internal::clipboard;
15use crate::internal::memoization;
16use crate::internal::runeutil::{self, Sanitizer};
17use crate::key::{self, Binding};
18use crate::viewport;
19use rusty_bubbletea::cursor::CursorShape;
20use rusty_bubbletea::key::KeyPressMsg;
21use rusty_bubbletea::model::{Cmd, Msg};
22use rusty_bubbletea::paste::PasteMsg;
23use rusty_lipgloss::{self, Color, Style};
24use std::fmt;
25use std::time::Duration;
26use unicode_width::UnicodeWidthChar;
27
28const MIN_HEIGHT: usize = 1;
29const DEFAULT_HEIGHT: usize = 6;
30const DEFAULT_WIDTH: usize = 40;
31const DEFAULT_CHAR_LIMIT: usize = 0; const DEFAULT_MAX_HEIGHT: usize = 99;
33const DEFAULT_MAX_WIDTH: usize = 500;
34
35const MAX_LINES: usize = 10000;
37
38#[derive(Debug)]
40pub struct PasteMsgInternal(pub String);
41
42#[derive(Debug)]
43pub struct PasteErrMsg(pub String);
44
45#[derive(Debug, Clone)]
47pub struct KeyMap {
48 pub character_backward: Binding,
50 pub character_forward: Binding,
52 pub delete_after_cursor: Binding,
54 pub delete_before_cursor: Binding,
56 pub delete_character_backward: Binding,
58 pub delete_character_forward: Binding,
60 pub delete_word_backward: Binding,
62 pub delete_word_forward: Binding,
64 pub insert_newline: Binding,
66 pub line_end: Binding,
68 pub line_next: Binding,
70 pub line_previous: Binding,
72 pub line_start: Binding,
74 pub page_up: Binding,
76 pub page_down: Binding,
78 pub paste: Binding,
80 pub word_backward: Binding,
82 pub word_forward: Binding,
84 pub input_begin: Binding,
86 pub input_end: Binding,
88
89 pub uppercase_word_forward: Binding,
91 pub lowercase_word_forward: Binding,
93 pub capitalize_word_forward: Binding,
95
96 pub transpose_character_backward: Binding,
98}
99
100pub fn default_key_map() -> KeyMap {
103 KeyMap {
104 character_forward: key::new_binding(vec![
105 key::with_keys(&["right", "ctrl+f"]),
106 key::with_help("right", "character forward"),
107 ]),
108 character_backward: key::new_binding(vec![
109 key::with_keys(&["left", "ctrl+b"]),
110 key::with_help("left", "character backward"),
111 ]),
112 word_forward: key::new_binding(vec![
113 key::with_keys(&["alt+right", "alt+f"]),
114 key::with_help("alt+right", "word forward"),
115 ]),
116 word_backward: key::new_binding(vec![
117 key::with_keys(&["alt+left", "alt+b"]),
118 key::with_help("alt+left", "word backward"),
119 ]),
120 line_next: key::new_binding(vec![
121 key::with_keys(&["down", "ctrl+n"]),
122 key::with_help("down", "next line"),
123 ]),
124 line_previous: key::new_binding(vec![
125 key::with_keys(&["up", "ctrl+p"]),
126 key::with_help("up", "previous line"),
127 ]),
128 delete_word_backward: key::new_binding(vec![
129 key::with_keys(&["alt+backspace", "ctrl+w"]),
130 key::with_help("alt+backspace", "delete word backward"),
131 ]),
132 delete_word_forward: key::new_binding(vec![
133 key::with_keys(&["alt+delete", "alt+d"]),
134 key::with_help("alt+delete", "delete word forward"),
135 ]),
136 delete_after_cursor: key::new_binding(vec![
137 key::with_keys(&["ctrl+k"]),
138 key::with_help("ctrl+k", "delete after cursor"),
139 ]),
140 delete_before_cursor: key::new_binding(vec![
141 key::with_keys(&["ctrl+u"]),
142 key::with_help("ctrl+u", "delete before cursor"),
143 ]),
144 insert_newline: key::new_binding(vec![
145 key::with_keys(&["enter", "ctrl+m"]),
146 key::with_help("enter", "insert newline"),
147 ]),
148 delete_character_backward: key::new_binding(vec![
149 key::with_keys(&["backspace", "ctrl+h"]),
150 key::with_help("backspace", "delete character backward"),
151 ]),
152 delete_character_forward: key::new_binding(vec![
153 key::with_keys(&["delete", "ctrl+d"]),
154 key::with_help("delete", "delete character forward"),
155 ]),
156 line_start: key::new_binding(vec![
157 key::with_keys(&["home", "ctrl+a"]),
158 key::with_help("home", "line start"),
159 ]),
160 line_end: key::new_binding(vec![
161 key::with_keys(&["end", "ctrl+e"]),
162 key::with_help("end", "line end"),
163 ]),
164 page_up: key::new_binding(vec![
165 key::with_keys(&["pgup"]),
166 key::with_help("pgup", "page up"),
167 ]),
168 page_down: key::new_binding(vec![
169 key::with_keys(&["pgdown"]),
170 key::with_help("pgdown", "page down"),
171 ]),
172 paste: key::new_binding(vec![
173 key::with_keys(&["ctrl+v"]),
174 key::with_help("ctrl+v", "paste"),
175 ]),
176 input_begin: key::new_binding(vec![
177 key::with_keys(&["alt+<", "ctrl+home"]),
178 key::with_help("alt+<", "input begin"),
179 ]),
180 input_end: key::new_binding(vec![
181 key::with_keys(&["alt+>", "ctrl+end"]),
182 key::with_help("alt+>", "input end"),
183 ]),
184 capitalize_word_forward: key::new_binding(vec![
185 key::with_keys(&["alt+c"]),
186 key::with_help("alt+c", "capitalize word forward"),
187 ]),
188 lowercase_word_forward: key::new_binding(vec![
189 key::with_keys(&["alt+l"]),
190 key::with_help("alt+l", "lowercase word forward"),
191 ]),
192 uppercase_word_forward: key::new_binding(vec![
193 key::with_keys(&["alt+u"]),
194 key::with_help("alt+u", "uppercase word forward"),
195 ]),
196 transpose_character_backward: key::new_binding(vec![
197 key::with_keys(&["ctrl+t"]),
198 key::with_help("ctrl+t", "transpose character backward"),
199 ]),
200 }
201}
202
203#[derive(Debug, Clone, Copy)]
206pub struct LineInfo {
207 pub width: usize,
209
210 pub char_width: usize,
213
214 pub height: usize,
216
217 pub start_column: usize,
219
220 pub column_offset: usize,
223
224 pub row_offset: usize,
227
228 pub char_offset: usize,
233}
234
235#[derive(Debug, Clone, Copy)]
238pub struct PromptInfo {
239 pub line_number: usize,
241 pub focused: bool,
243}
244
245#[derive(Debug, Clone)]
247pub struct CursorStyle {
248 pub color: Color,
251
252 pub shape: CursorShape,
258
259 pub blink: bool,
261
262 pub blink_speed: Duration,
266}
267
268#[derive(Debug, Clone)]
272pub struct Styles {
273 pub focused: StyleState,
275 pub blurred: StyleState,
277 pub cursor: CursorStyle,
279}
280
281#[derive(Debug, Clone)]
286pub struct StyleState {
287 pub base: Style,
289 pub text: Style,
291 pub line_number: Style,
293 pub cursor_line_number: Style,
295 pub cursor_line: Style,
297 pub end_of_buffer: Style,
299 pub placeholder: Style,
301 pub prompt: Style,
303}
304
305impl StyleState {
306 fn computed_cursor_line(&self) -> Style {
307 self.cursor_line.clone().inherit(&self.base).inline(true)
308 }
309
310 fn computed_cursor_line_number(&self) -> Style {
311 self.cursor_line_number
312 .clone()
313 .inherit(&self.cursor_line)
314 .inherit(&self.base)
315 .inline(true)
316 }
317
318 fn computed_end_of_buffer(&self) -> Style {
319 self.end_of_buffer.clone().inherit(&self.base).inline(true)
320 }
321
322 fn computed_line_number(&self) -> Style {
323 self.line_number.clone().inherit(&self.base).inline(true)
324 }
325
326 fn computed_placeholder(&self) -> Style {
327 self.placeholder.clone().inherit(&self.base).inline(true)
328 }
329
330 fn computed_prompt(&self) -> Style {
331 self.prompt.clone().inherit(&self.base).inline(true)
332 }
333
334 fn computed_text(&self) -> Style {
335 self.text.clone().inherit(&self.base).inline(true)
336 }
337}
338
339pub struct Model {
341 pub err: Option<String>,
343
344 cache: memoization::MemoCache<Vec<Vec<char>>>,
346
347 pub prompt: String,
349
350 pub placeholder: String,
353
354 pub show_line_numbers: bool,
357
358 pub end_of_buffer_character: char,
360
361 pub key_map: KeyMap,
363
364 pub virtual_cursor: cursor::Model,
366
367 pub char_limit: usize,
370
371 pub max_height: usize,
374
375 pub max_width: usize,
378
379 pub dynamic_height: bool,
383
384 pub min_height: usize,
387
388 pub max_content_height: usize,
392
393 pub styles: Styles,
395
396 pub use_virtual_cursor: bool,
398
399 pub prompt_func: Option<Box<dyn Fn(PromptInfo) -> String + Send + Sync>>,
402
403 pub prompt_width: usize,
405
406 pub width: usize,
409
410 pub height: usize,
412
413 value: Vec<Vec<char>>,
415
416 pub focus: bool,
419
420 col: usize,
422
423 row: usize,
425
426 last_char_offset: usize,
429
430 viewport: viewport::Model,
433
434 rsan: Option<runeutil::Sanitizer_>,
436}
437
438impl fmt::Debug for Model {
439 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
440 f.debug_struct("textarea::Model")
441 .field("focus", &self.focus)
442 .field("row", &self.row)
443 .field("col", &self.col)
444 .field("lines", &self.value.len())
445 .finish()
446 }
447}
448
449pub fn new() -> Model {
451 let vp = viewport::new(vec![viewport::with_key_map(viewport::KeyMap {
455 page_down: key::new_binding(vec![]),
456 page_up: key::new_binding(vec![]),
457 half_page_up: key::new_binding(vec![]),
458 half_page_down: key::new_binding(vec![]),
459 up: key::new_binding(vec![]),
460 down: key::new_binding(vec![]),
461 left: key::new_binding(vec![]),
462 right: key::new_binding(vec![]),
463 })]);
464
465 let cur = cursor::new();
466
467 let styles = default_dark_styles();
468
469 let mut m = Model {
470 char_limit: DEFAULT_CHAR_LIMIT,
471 max_height: DEFAULT_MAX_HEIGHT,
472 max_width: DEFAULT_MAX_WIDTH,
473 prompt: format!("{} ", rusty_lipgloss::border::thick_border().left),
474 styles,
475 cache: memoization::new_memo_cache(MAX_LINES),
476 end_of_buffer_character: ' ',
477 show_line_numbers: true,
478 use_virtual_cursor: true,
479 virtual_cursor: cur,
480 key_map: default_key_map(),
481
482 value: vec![vec![]; MIN_HEIGHT],
483 focus: false,
484 col: 0,
485 row: 0,
486
487 viewport: vp,
488 err: None,
489 placeholder: String::new(),
490 dynamic_height: false,
491 min_height: 0,
492 max_content_height: 0,
493 prompt_func: None,
494 prompt_width: 0,
495 width: 0,
496 height: 0,
497 last_char_offset: 0,
498 rsan: None,
499 };
500
501 m.set_height(DEFAULT_HEIGHT);
502 m.set_width(DEFAULT_WIDTH);
503
504 m
505}
506
507pub fn default_styles(is_dark: bool) -> Styles {
510 let light_dark = rusty_lipgloss::color::light_dark(is_dark);
511
512 Styles {
513 focused: StyleState {
514 base: rusty_lipgloss::new_style(),
515 cursor_line: rusty_lipgloss::new_style()
516 .background_color(light_dark(Color::parse("255"), Color::parse("0"))),
517 cursor_line_number: rusty_lipgloss::new_style()
518 .foreground_color(light_dark(Color::parse("240"), Color::parse("240"))),
519 end_of_buffer: rusty_lipgloss::new_style()
520 .foreground_color(light_dark(Color::parse("254"), Color::parse("0"))),
521 line_number: rusty_lipgloss::new_style()
522 .foreground_color(light_dark(Color::parse("249"), Color::parse("7"))),
523 placeholder: rusty_lipgloss::new_style().foreground_color(Color::parse("240")),
524 prompt: rusty_lipgloss::new_style().foreground_color(Color::parse("7")),
525 text: rusty_lipgloss::new_style(),
526 },
527 blurred: StyleState {
528 base: rusty_lipgloss::new_style(),
529 cursor_line: rusty_lipgloss::new_style()
530 .foreground_color(light_dark(Color::parse("245"), Color::parse("7"))),
531 cursor_line_number: rusty_lipgloss::new_style()
532 .foreground_color(light_dark(Color::parse("249"), Color::parse("7"))),
533 end_of_buffer: rusty_lipgloss::new_style()
534 .foreground_color(light_dark(Color::parse("254"), Color::parse("0"))),
535 line_number: rusty_lipgloss::new_style()
536 .foreground_color(light_dark(Color::parse("249"), Color::parse("7"))),
537 placeholder: rusty_lipgloss::new_style().foreground_color(Color::parse("240")),
538 prompt: rusty_lipgloss::new_style().foreground_color(Color::parse("7")),
539 text: rusty_lipgloss::new_style()
540 .foreground_color(light_dark(Color::parse("245"), Color::parse("7"))),
541 },
542 cursor: CursorStyle {
543 color: Color::parse("7"),
544 shape: CursorShape::CursorBlock,
545 blink: true,
546 blink_speed: Duration::from_millis(530),
547 },
548 }
549}
550
551pub fn default_light_styles() -> Styles {
553 default_styles(false)
554}
555
556pub fn default_dark_styles() -> Styles {
558 default_styles(true)
559}
560
561impl Model {
562 pub fn styles(&self) -> &Styles {
564 &self.styles
565 }
566
567 pub fn set_styles(&mut self, s: Styles) {
569 self.styles = s;
570 self.update_virtual_cursor_style();
571 }
572
573 pub fn virtual_cursor(&self) -> bool {
575 self.use_virtual_cursor
576 }
577
578 pub fn set_virtual_cursor(&mut self, v: bool) {
580 self.use_virtual_cursor = v;
581 self.update_virtual_cursor_style();
582 }
583
584 fn update_virtual_cursor_style(&mut self) {
587 if !self.use_virtual_cursor {
588 self.virtual_cursor.set_mode(cursor::Mode::Hide);
589 return;
590 }
591
592 self.virtual_cursor.style =
593 rusty_lipgloss::new_style().foreground_color(self.styles.cursor.color.clone());
594
595 if self.styles.cursor.blink {
598 if self.styles.cursor.blink_speed > Duration::ZERO {
599 self.virtual_cursor.blink_speed = self.styles.cursor.blink_speed;
600 }
601 self.virtual_cursor.set_mode(cursor::Mode::Blink);
602 return;
603 }
604 self.virtual_cursor.set_mode(cursor::Mode::Static);
605 }
606
607 pub fn set_value(&mut self, s: &str) {
609 self.reset();
610 self.insert_string(s);
611 self.recalculate_height();
612 }
613
614 pub fn insert_string(&mut self, s: &str) {
616 self.insert_runes_from_user_input(&s.chars().collect::<Vec<char>>());
617 self.recalculate_height();
618 }
619
620 pub fn insert_rune(&mut self, r: char) {
622 self.insert_runes_from_user_input(&[r]);
623 self.recalculate_height();
624 }
625
626 fn insert_runes_from_user_input(&mut self, input: &[char]) {
629 let mut runes = self.san().sanitize(input);
632
633 if self.char_limit > 0 {
634 let avail_space = self.char_limit - self.length();
635 if avail_space == 0 {
637 return;
638 }
639 if avail_space < runes.len() {
642 runes.truncate(avail_space);
643 }
644 }
645
646 let mut lines: Vec<Vec<char>> = vec![];
648 let mut lstart = 0;
649 for (i, r) in runes.iter().enumerate() {
650 if *r == '\n' {
651 lines.push(runes[lstart..i].to_vec());
653 lstart = i + 1;
654 }
655 }
656 if lstart <= runes.len() {
657 lines.push(runes[lstart..].to_vec());
660 }
661
662 if MAX_LINES > 0 && self.value.len() + lines.len() - 1 > MAX_LINES {
664 let allowed_height = MAX_LINES - self.value.len() + 1;
665 lines.truncate(allowed_height);
666 }
667
668 if self.max_content_height > 0 {
670 let budget = self.max_content_height - self.total_visual_lines();
671 while lines.len() > 1 && self.visual_lines_for_insert(&lines) > budget {
673 lines.truncate(lines.len() - 1);
674 }
675 if self.visual_lines_for_insert(&lines) > budget {
676 return;
677 }
678 }
679
680 if lines.is_empty() {
681 return;
683 }
684
685 let tail: Vec<char> = self.value[self.row][self.col..].to_vec();
688
689 let mut first = self.value[self.row][..self.col].to_vec();
691 first.extend_from_slice(&lines[0]);
692 self.value[self.row] = first;
693 self.col += lines[0].len();
694
695 let num_extra_lines = lines.len() - 1;
696 if num_extra_lines > 0 {
697 let mut new_grid: Vec<Vec<char>> = self.value.clone();
699 new_grid.resize(self.value.len() + num_extra_lines, vec![]);
700 let shift = self.row + 1 + num_extra_lines;
703 for (idx, src) in (self.row + 1..self.value.len()).enumerate() {
704 new_grid[shift + idx] = self.value[src].clone();
705 }
706 self.value = new_grid;
707 for l in &lines[1..] {
709 self.row += 1;
710 self.value[self.row] = l.clone();
711 self.col = l.len();
712 }
713 }
714
715 self.value[self.row].extend_from_slice(&tail);
717
718 self.set_cursor_column(self.col);
719 }
720
721 pub fn value(&self) -> String {
724 if self.value.is_empty() {
725 return String::new();
726 }
727
728 let mut v = String::new();
729 for l in &self.value {
730 v.push_str(&String::from_iter(l.iter()));
731 v.push('\n');
732 }
733
734 v.trim_end_matches('\n').to_string()
735 }
736
737 pub fn length(&self) -> usize {
739 let mut l = 0;
740 for row in &self.value {
741 l += string_width(&String::from_iter(row.iter()));
742 }
743 l + self.value.len() - 1
745 }
746
747 pub fn line_count(&self) -> usize {
750 self.value.len()
751 }
752
753 pub fn line(&self) -> usize {
755 self.row
756 }
757
758 pub fn column(&self) -> usize {
760 self.col
761 }
762
763 pub fn scroll_y_offset(&self) -> usize {
766 self.viewport.y_offset()
767 }
768
769 pub fn set_scroll_y_offset(&mut self, offset: usize) {
775 self.viewport.set_y_offset(offset);
776 }
777
778 pub fn scroll_percent(&self) -> f64 {
781 self.viewport.scroll_percent()
782 }
783
784 fn set_cursor_line_relative(&mut self, delta: isize) {
788 if delta == 0 {
789 return;
790 }
791
792 let mut li = self.line_info();
793 let char_offset = self.last_char_offset.max(li.char_offset);
794 self.last_char_offset = char_offset;
795
796 const TRAILING_SPACE: usize = 2;
798
799 if delta > 0 {
800 for _ in 0..delta {
802 if li.row_offset + 1 >= li.height && self.row < self.value.len() - 1 {
803 self.row += 1;
804 self.col = 0;
805 } else {
806 self.col = (li.start_column + li.width + TRAILING_SPACE)
808 .min(self.value[self.row].len().saturating_sub(1));
809 }
810 li = self.line_info();
811 }
812 } else {
813 for _ in 0..(-delta) {
815 if li.row_offset == 0 && self.row > 0 {
816 self.row -= 1;
817 self.col = self.value[self.row].len();
818 } else {
819 self.col = li.start_column.saturating_sub(TRAILING_SPACE);
821 }
822 li = self.line_info();
823 }
824 }
825
826 let nli = self.line_info();
827 self.col = nli.start_column;
828
829 if nli.width == 0 {
830 self.reposition_view();
831 return;
832 }
833
834 let mut offset = 0;
835 while offset < char_offset {
836 if self.row >= self.value.len()
837 || self.col >= self.value[self.row].len()
838 || offset >= nli.char_width.saturating_sub(1)
839 {
840 break;
841 }
842 offset += char_width(self.value[self.row][self.col]);
843 self.col += 1;
844 }
845 self.reposition_view();
846 }
847
848 pub fn cursor_down(&mut self) {
850 self.set_cursor_line_relative(1);
851 }
852
853 pub fn cursor_up(&mut self) {
855 self.set_cursor_line_relative(-1);
856 }
857
858 pub fn cursor_position(&self) -> (usize, usize) {
864 (self.row, self.col)
865 }
866
867 pub fn set_cursor_position(&mut self, row: usize, col: usize) {
872 if row >= self.value.len() {
873 return;
874 }
875 self.row = row;
876 self.col = clamp(col, 0, self.value[row].len());
877 self.last_char_offset = 0;
878 }
879
880 pub fn set_cursor_column(&mut self, col: usize) {
884 self.col = clamp(col, 0, self.value[self.row].len());
885 self.last_char_offset = 0;
889 }
890
891 pub fn cursor_start(&mut self) {
893 self.set_cursor_column(0);
894 }
895
896 pub fn cursor_end(&mut self) {
898 self.set_cursor_column(self.value[self.row].len());
899 }
900
901 pub fn focused(&self) -> bool {
903 self.focus
904 }
905
906 fn active_style(&self) -> &StyleState {
909 if self.focus {
910 &self.styles.focused
911 } else {
912 &self.styles.blurred
913 }
914 }
915
916 pub fn focus(&mut self) -> Cmd {
919 self.focus = true;
920 self.virtual_cursor.focus()
921 }
922
923 pub fn blur(&mut self) {
926 self.focus = false;
927 self.virtual_cursor.blur();
928 }
929
930 pub fn reset(&mut self) {
932 self.value = vec![vec![]; MIN_HEIGHT];
933 self.col = 0;
934 self.row = 0;
935 self.viewport.goto_top();
936 self.set_cursor_column(0);
937 self.recalculate_height();
938 }
939
940 pub fn word(&self) -> String {
943 let line = &self.value[self.row];
944 let col = self.col.saturating_sub(1);
945
946 if self.col == 0 {
947 return String::new();
948 }
949
950 if col >= line.len() {
952 return String::new();
953 }
954
955 if line[col].is_whitespace() {
957 return String::new();
958 }
959
960 let mut start = col;
962 while start > 0 && !line[start - 1].is_whitespace() {
963 start -= 1;
964 }
965
966 let mut end = col;
968 while end < line.len() && !line[end].is_whitespace() {
969 end += 1;
970 }
971
972 String::from_iter(line[start..end].iter())
973 }
974
975 fn san(&mut self) -> &runeutil::Sanitizer_ {
977 if self.rsan.is_none() {
978 self.rsan = Some(runeutil::new_sanitizer(vec![]));
979 }
980 self.rsan.as_ref().unwrap()
981 }
982
983 fn delete_before_cursor(&mut self) {
985 self.value[self.row] = self.value[self.row][self.col..].to_vec();
986 self.set_cursor_column(0);
987 }
988
989 fn delete_after_cursor(&mut self) {
991 self.value[self.row] = self.value[self.row][..self.col].to_vec();
992 self.set_cursor_column(self.value[self.row].len());
993 }
994
995 fn transpose_left(&mut self) {
998 if self.col == 0 || self.value[self.row].len() < 2 {
999 return;
1000 }
1001 if self.col >= self.value[self.row].len() {
1002 self.set_cursor_column(self.col - 1);
1003 }
1004 self.value[self.row].swap(self.col - 1, self.col);
1005 if self.col < self.value[self.row].len() {
1006 self.set_cursor_column(self.col + 1);
1007 }
1008 }
1009
1010 fn delete_word_left(&mut self) {
1012 if self.col == 0 || self.value[self.row].is_empty() {
1013 return;
1014 }
1015
1016 let old_col = self.col;
1019
1020 self.set_cursor_column(self.col - 1);
1021 loop {
1022 if self.col == 0 {
1023 break;
1024 }
1025 if !self.value[self.row][self.col].is_whitespace() {
1026 break;
1027 }
1028 self.set_cursor_column(self.col - 1);
1030 }
1031
1032 while self.col > 0 {
1033 if !self.value[self.row][self.col].is_whitespace() {
1034 self.set_cursor_column(self.col - 1);
1035 } else {
1036 if self.col > 0 {
1037 self.set_cursor_column(self.col + 1);
1039 }
1040 break;
1041 }
1042 }
1043
1044 if old_col > self.value[self.row].len() {
1045 self.value[self.row] = self.value[self.row][..self.col].to_vec();
1046 } else {
1047 let mut v = self.value[self.row][..self.col].to_vec();
1048 v.extend_from_slice(&self.value[self.row][old_col..]);
1049 self.value[self.row] = v;
1050 }
1051 }
1052
1053 fn delete_word_right(&mut self) {
1055 if self.col >= self.value[self.row].len() || self.value[self.row].is_empty() {
1056 return;
1057 }
1058
1059 let old_col = self.col;
1060
1061 while self.col < self.value[self.row].len()
1062 && self.value[self.row][self.col].is_whitespace()
1063 {
1064 self.set_cursor_column(self.col + 1);
1066 }
1067
1068 while self.col < self.value[self.row].len() {
1069 if !self.value[self.row][self.col].is_whitespace() {
1070 self.set_cursor_column(self.col + 1);
1071 } else {
1072 break;
1073 }
1074 }
1075
1076 if self.col > self.value[self.row].len() {
1077 self.value[self.row] = self.value[self.row][..old_col].to_vec();
1078 } else {
1079 let mut v = self.value[self.row][..old_col].to_vec();
1080 v.extend_from_slice(&self.value[self.row][self.col..]);
1081 self.value[self.row] = v;
1082 }
1083
1084 self.set_cursor_column(old_col);
1085 }
1086
1087 fn character_right(&mut self) {
1089 if self.col < self.value[self.row].len() {
1090 self.set_cursor_column(self.col + 1);
1091 } else if self.row < self.value.len() - 1 {
1092 self.row += 1;
1093 self.cursor_start();
1094 }
1095 }
1096
1097 fn character_left(&mut self, inside_line: bool) {
1099 if self.col == 0 && self.row != 0 {
1100 self.row -= 1;
1101 self.cursor_end();
1102 if !inside_line {
1103 return;
1104 }
1105 }
1106 if self.col > 0 {
1107 self.set_cursor_column(self.col - 1);
1108 }
1109 }
1110
1111 fn word_left(&mut self) {
1113 loop {
1114 self.character_left(true );
1115 if self.col < self.value[self.row].len()
1116 && !self.value[self.row][self.col].is_whitespace()
1117 {
1118 break;
1119 }
1120 }
1121
1122 while self.col > 0 {
1123 if self.value[self.row][self.col - 1].is_whitespace() {
1124 break;
1125 }
1126 self.set_cursor_column(self.col - 1);
1127 }
1128 }
1129
1130 fn word_right(&mut self) {
1132 self.do_word_right(&mut |_, _| {});
1133 }
1134
1135 fn do_word_right(&mut self, f: &mut dyn FnMut(usize, usize)) {
1136 while self.col >= self.value[self.row].len()
1138 || self.value[self.row][self.col].is_whitespace()
1139 {
1140 if self.row == self.value.len() - 1 && self.col == self.value[self.row].len() {
1141 break;
1143 }
1144 self.character_right();
1145 }
1146
1147 let mut char_idx = 0;
1148 while self.col < self.value[self.row].len() {
1149 if self.value[self.row][self.col].is_whitespace() {
1150 break;
1151 }
1152 f(char_idx, self.col);
1153 self.set_cursor_column(self.col + 1);
1154 char_idx += 1;
1155 }
1156 }
1157
1158 fn uppercase_right(&mut self) {
1160 let idxs: Vec<usize> = self.collect_word_right_indices();
1161 for i in idxs {
1162 self.value[self.row][i] = self.value[self.row][i].to_uppercase().next().unwrap();
1163 }
1164 }
1165
1166 fn lowercase_right(&mut self) {
1168 let idxs: Vec<usize> = self.collect_word_right_indices();
1169 for i in idxs {
1170 self.value[self.row][i] = self.value[self.row][i].to_lowercase().next().unwrap();
1171 }
1172 }
1173
1174 fn capitalize_right(&mut self) {
1176 let idxs: Vec<usize> = self.collect_word_right_indices();
1177 for (char_idx, i) in idxs.iter().enumerate() {
1178 if char_idx == 0 {
1179 self.value[self.row][*i] = self.value[self.row][*i].to_uppercase().next().unwrap();
1180 }
1181 }
1182 }
1183
1184 fn collect_word_right_indices(&mut self) -> Vec<usize> {
1185 let mut idxs = vec![];
1186 self.do_word_right(&mut |_, i| idxs.push(i));
1187 idxs
1188 }
1189
1190 pub fn line_info(&self) -> LineInfo {
1193 let grid = self.memoized_wrap(&self.value[self.row], self.width);
1194
1195 let mut counter = 0;
1199 for (i, line) in grid.iter().enumerate() {
1200 if counter + line.len() == self.col && i + 1 < grid.len() {
1202 return LineInfo {
1206 char_offset: 0,
1207 column_offset: 0,
1208 height: grid.len(),
1209 row_offset: i + 1,
1210 start_column: self.col,
1211 width: grid[i + 1].len(),
1212 char_width: string_width(&String::from_iter(line.iter())),
1213 };
1214 }
1215
1216 if counter + line.len() >= self.col {
1217 return LineInfo {
1218 char_offset: string_width(&String::from_iter(
1219 line[..self.col.saturating_sub(counter)].iter(),
1220 )),
1221 column_offset: self.col - counter,
1222 height: grid.len(),
1223 row_offset: i,
1224 start_column: counter,
1225 width: line.len(),
1226 char_width: string_width(&String::from_iter(line.iter())),
1227 };
1228 }
1229
1230 counter += line.len();
1231 }
1232 LineInfo {
1233 width: 0,
1234 char_width: 0,
1235 height: 0,
1236 start_column: 0,
1237 column_offset: 0,
1238 row_offset: 0,
1239 char_offset: 0,
1240 }
1241 }
1242
1243 fn reposition_view(&mut self) {
1246 let minimum = self.viewport.y_offset();
1247 let maximum = minimum + self.viewport.height() - 1;
1248 let row = self.cursor_line_number();
1249 if row < minimum {
1250 self.viewport.scroll_up(minimum - row);
1251 } else if row > maximum {
1252 self.viewport.scroll_down(row - maximum);
1253 }
1254 }
1255
1256 pub fn width(&self) -> usize {
1258 self.width
1259 }
1260
1261 pub fn move_to_begin(&mut self) {
1263 self.row = 0;
1264 self.set_cursor_column(0);
1265 self.reposition_view();
1266 }
1267
1268 pub fn move_to_end(&mut self) {
1270 self.row = self.value.len() - 1;
1271 self.set_cursor_column(self.value[self.row].len());
1272 self.reposition_view();
1273 }
1274
1275 pub fn page_up(&mut self) {
1278 let offset = self.viewport.y_offset() as isize - self.cursor_line_number() as isize;
1280 if offset < 0 {
1281 self.set_cursor_line_relative(offset);
1282 return;
1283 }
1284
1285 self.set_cursor_line_relative(-(self.height as isize));
1287 }
1288
1289 pub fn page_down(&mut self) {
1292 let offset = self.cursor_line_number() as isize - self.viewport.y_offset() as isize;
1294 if offset < (self.height - 1) as isize {
1295 self.set_cursor_line_relative((self.height - 1) as isize - offset);
1296 return;
1297 }
1298
1299 self.set_cursor_line_relative(self.height as isize);
1301 }
1302
1303 pub fn set_width(&mut self, w: usize) {
1306 if self.prompt_func.is_none() {
1308 self.prompt_width = string_width(&self.prompt);
1309 }
1310
1311 let reserved_outer = self.active_style().base.get_horizontal_frame_size();
1313
1314 let mut reserved_inner = self.prompt_width;
1316
1317 if self.show_line_numbers {
1319 const GAP: usize = 2;
1322
1323 reserved_inner += num_digits(self.max_height) + GAP;
1325 }
1326
1327 let min_width = reserved_inner + reserved_outer + 1;
1330 let mut input_width = w.max(min_width);
1331
1332 if self.max_width > 0 {
1334 input_width = input_width.min(self.max_width);
1335 }
1336
1337 self.viewport.set_width(input_width - reserved_outer);
1341 self.width = input_width - reserved_outer - reserved_inner;
1342 self.recalculate_height();
1343 }
1344
1345 pub fn set_prompt_func(
1348 &mut self,
1349 prompt_width: usize,
1350 f: Box<dyn Fn(PromptInfo) -> String + Send + Sync>,
1351 ) {
1352 self.prompt_func = Some(f);
1353 self.prompt_width = prompt_width;
1354 }
1355
1356 pub fn height(&self) -> usize {
1358 self.height
1359 }
1360
1361 pub fn cursor(&self) -> Option<rusty_bubbletea::cursor::Cursor> {
1365 if self.use_virtual_cursor || !self.focus {
1366 return None;
1367 }
1368
1369 let li = self.line_info();
1370 let base_style = &self.active_style().base;
1371
1372 let x_offset = li.char_offset
1373 + self.prompt_width
1374 + self.line_number_width()
1375 + base_style.get_margin_left()
1376 + base_style.get_padding_left()
1377 + base_style.get_border_left_size();
1378
1379 let y_offset = self
1380 .cursor_line_number()
1381 .saturating_sub(self.viewport.y_offset())
1382 + base_style.get_margin_top()
1383 + base_style.get_padding_top()
1384 + base_style.get_border_top_size();
1385
1386 let style = &self.styles.cursor;
1387 let mut c = rusty_bubbletea::cursor::Cursor::new(x_offset, y_offset);
1388 c.blink = style.blink;
1389 let (r, g, b, _) = style.color.rgba_bytes();
1392 c.color = Some(rusty_x_ansi::color::RGBColor { r, g, b });
1393 c.shape = style.shape;
1394 Some(c)
1395 }
1396
1397 fn line_number_width(&self) -> usize {
1400 if !self.show_line_numbers {
1401 return 0;
1402 }
1403 num_digits(self.max_height) + 2
1405 }
1406
1407 pub fn set_height(&mut self, h: usize) {
1409 if self.max_height > 0 {
1410 self.height = clamp(h, MIN_HEIGHT, self.max_height);
1411 self.viewport
1412 .set_height(clamp(h, MIN_HEIGHT, self.max_height));
1413 } else {
1414 self.height = h.max(MIN_HEIGHT);
1415 self.viewport.set_height(h.max(MIN_HEIGHT));
1416 }
1417
1418 self.reposition_view();
1419 }
1420
1421 pub fn update(&mut self, msg: &dyn Msg) -> Cmd {
1423 if !self.focus {
1424 self.virtual_cursor.blur();
1425 return None;
1426 }
1427
1428 let (old_row, old_col) = (self.cursor_line_number(), self.col);
1430
1431 let mut cmds: Vec<Cmd> = Vec::new();
1432
1433 if self.value[self.row].is_empty() && self.value[self.row].is_empty() {
1434 }
1436
1437 if self.max_height > 0 && self.max_height != self.cache.capacity() {
1438 self.cache = memoization::new_memo_cache(self.max_height);
1439 }
1440
1441 if let Some(pm) = msg.as_any().downcast_ref::<PasteMsg>() {
1442 self.insert_runes_from_user_input(&pm.content.chars().collect::<Vec<char>>());
1443 }
1444
1445 if let Some(m) = msg.as_any().downcast_ref::<KeyPressMsg>() {
1446 let k = &m.0;
1447 if key::matches(k, std::slice::from_ref(&self.key_map.delete_after_cursor)) {
1448 self.col = clamp(self.col, 0, self.value[self.row].len());
1449 if self.col >= self.value[self.row].len() {
1450 self.merge_line_below(self.row);
1451 } else {
1452 self.delete_after_cursor();
1453 }
1454 } else if key::matches(k, std::slice::from_ref(&self.key_map.delete_before_cursor)) {
1455 self.col = clamp(self.col, 0, self.value[self.row].len());
1456 if self.col == 0 {
1457 self.merge_line_above(self.row);
1458 } else {
1459 self.delete_before_cursor();
1460 }
1461 } else if key::matches(
1462 k,
1463 std::slice::from_ref(&self.key_map.delete_character_backward),
1464 ) {
1465 self.col = clamp(self.col, 0, self.value[self.row].len());
1466 if self.col == 0 {
1467 self.merge_line_above(self.row);
1468 } else if !self.value[self.row].is_empty() {
1469 let mut v = self.value[self.row][..self.col.max(1) - 1].to_vec();
1470 v.extend_from_slice(&self.value[self.row][self.col..]);
1471 self.value[self.row] = v;
1472 if self.col > 0 {
1473 self.set_cursor_column(self.col - 1);
1474 }
1475 }
1476 } else if key::matches(
1477 k,
1478 std::slice::from_ref(&self.key_map.delete_character_forward),
1479 ) {
1480 if !self.value[self.row].is_empty() && self.col < self.value[self.row].len() {
1481 self.value[self.row].remove(self.col);
1482 }
1483 if self.col >= self.value[self.row].len() {
1484 self.merge_line_below(self.row);
1485 }
1486 } else if key::matches(k, std::slice::from_ref(&self.key_map.delete_word_backward)) {
1487 if self.col == 0 {
1488 self.merge_line_above(self.row);
1489 } else {
1490 self.delete_word_left();
1491 }
1492 } else if key::matches(k, std::slice::from_ref(&self.key_map.delete_word_forward)) {
1493 self.col = clamp(self.col, 0, self.value[self.row].len());
1494 if self.col >= self.value[self.row].len() {
1495 self.merge_line_below(self.row);
1496 } else {
1497 self.delete_word_right();
1498 }
1499 } else if key::matches(k, std::slice::from_ref(&self.key_map.insert_newline)) {
1500 if self.at_content_limit() {
1501 return None;
1502 }
1503 self.col = clamp(self.col, 0, self.value[self.row].len());
1504 self.split_line(self.row, self.col);
1505 } else if key::matches(k, std::slice::from_ref(&self.key_map.line_end)) {
1506 self.cursor_end();
1507 } else if key::matches(k, std::slice::from_ref(&self.key_map.line_start)) {
1508 self.cursor_start();
1509 } else if key::matches(k, std::slice::from_ref(&self.key_map.character_forward)) {
1510 self.character_right();
1511 } else if key::matches(k, std::slice::from_ref(&self.key_map.line_next)) {
1512 self.cursor_down();
1513 } else if key::matches(k, std::slice::from_ref(&self.key_map.word_forward)) {
1514 self.word_right();
1515 } else if key::matches(k, std::slice::from_ref(&self.key_map.paste)) {
1516 return self.paste_cmd();
1517 } else if key::matches(k, std::slice::from_ref(&self.key_map.character_backward)) {
1518 self.character_left(false );
1519 } else if key::matches(k, std::slice::from_ref(&self.key_map.line_previous)) {
1520 self.cursor_up();
1521 } else if key::matches(k, std::slice::from_ref(&self.key_map.word_backward)) {
1522 self.word_left();
1523 } else if key::matches(k, std::slice::from_ref(&self.key_map.input_begin)) {
1524 self.move_to_begin();
1525 } else if key::matches(k, std::slice::from_ref(&self.key_map.input_end)) {
1526 self.move_to_end();
1527 } else if key::matches(k, std::slice::from_ref(&self.key_map.page_up)) {
1528 self.page_up();
1529 } else if key::matches(k, std::slice::from_ref(&self.key_map.page_down)) {
1530 self.page_down();
1531 } else if key::matches(
1532 k,
1533 std::slice::from_ref(&self.key_map.lowercase_word_forward),
1534 ) {
1535 self.lowercase_right();
1536 } else if key::matches(
1537 k,
1538 std::slice::from_ref(&self.key_map.uppercase_word_forward),
1539 ) {
1540 self.uppercase_right();
1541 } else if key::matches(
1542 k,
1543 std::slice::from_ref(&self.key_map.capitalize_word_forward),
1544 ) {
1545 self.capitalize_right();
1546 } else if key::matches(
1547 k,
1548 std::slice::from_ref(&self.key_map.transpose_character_backward),
1549 ) {
1550 self.transpose_left();
1551 } else {
1552 self.insert_runes_from_user_input(&k.text.chars().collect::<Vec<char>>());
1553 }
1554 }
1555
1556 if let Some(pm) = msg.as_any().downcast_ref::<PasteMsgInternal>() {
1557 self.insert_runes_from_user_input(&pm.0.chars().collect::<Vec<char>>());
1558 }
1559
1560 if let Some(pm) = msg.as_any().downcast_ref::<PasteErrMsg>() {
1561 self.err = Some(pm.0.clone());
1562 }
1563
1564 self.recalculate_height();
1565
1566 let view = self.view_inner();
1568 self.viewport.set_content(&view);
1569 let vp_cmd = self.viewport.update(msg);
1570 cmds.push(vp_cmd);
1571
1572 if self.use_virtual_cursor {
1573 let cmd = self.virtual_cursor.update(msg);
1574 let mut cmd = cmd;
1575
1576 let (new_row, new_col) = (self.cursor_line_number(), self.col);
1580 if (new_row != old_row || new_col != old_col)
1581 && self.virtual_cursor.mode() == cursor::Mode::Blink
1582 {
1583 self.virtual_cursor.is_blinked = false;
1584 cmd = self.virtual_cursor.blink();
1585 }
1586 cmds.push(cmd);
1587 }
1588
1589 self.reposition_view();
1590
1591 rusty_bubbletea::commands::batch(cmds)
1592 }
1593
1594 fn view_inner(&self) -> String {
1595 if self.value().is_empty() && self.row == 0 && self.col == 0 && !self.placeholder.is_empty()
1596 {
1597 return self.placeholder_view();
1598 }
1599 self.view_content()
1600 }
1601
1602 fn view_content(&self) -> String {
1603 let mut s = String::new();
1604 let styles = self.active_style();
1605 let mut vc = self.virtual_cursor.clone();
1610 vc.text_style = styles.computed_cursor_line();
1611 let mut new_lines = 0usize;
1612 let mut widest_line_number = 0usize;
1613 let line_info = self.line_info();
1614 let mut display_line = 0usize;
1615 for (l, line) in self.value.iter().enumerate() {
1616 let wrapped_lines = self.memoized_wrap(line, self.width);
1617
1618 let style = if self.row == l {
1619 styles.computed_cursor_line()
1620 } else {
1621 styles.computed_text()
1622 };
1623
1624 for (wl, wrapped_line) in wrapped_lines.iter().enumerate() {
1625 let mut prompt = self.prompt_view(display_line);
1626 prompt = styles.computed_prompt().render(&prompt);
1627 s += &style.render(&prompt);
1628 display_line += 1;
1629
1630 let ln = String::new();
1631 if self.show_line_numbers {
1632 if wl == 0 {
1633 let is_cursor_line = self.row == l;
1635 s += &self.line_number_view((l + 1) as isize, is_cursor_line);
1636 } else {
1637 let is_cursor_line = self.row == l;
1639 s += &self.line_number_view(-1, is_cursor_line);
1640 }
1641 }
1642
1643 let lnw = string_width(&ln);
1647 if lnw > widest_line_number {
1648 widest_line_number = lnw;
1649 }
1650
1651 let mut wrapped_line = wrapped_line.clone();
1652 let strwidth = string_width(&String::from_iter(wrapped_line.iter()));
1653 let mut padding = self.width - strwidth;
1654 if strwidth > self.width {
1657 while wrapped_line.last() == Some(&' ') {
1660 wrapped_line.pop();
1661 }
1662 padding = padding.saturating_sub(self.width - strwidth);
1663 }
1664 if self.row == l && line_info.row_offset == wl {
1665 s += &style.render(&String::from_iter(
1666 wrapped_line[..line_info.column_offset.min(wrapped_line.len())].iter(),
1667 ));
1668 if self.col >= line.len() && line_info.char_offset >= self.width {
1669 vc.set_char(" ");
1670 s += &vc.view();
1671 } else {
1672 let col = line_info.column_offset.min(wrapped_line.len());
1673 let ch = if col < wrapped_line.len() {
1674 String::from_iter(wrapped_line[col..col + 1].iter())
1675 } else {
1676 String::new()
1677 };
1678 vc.set_char(&ch);
1679 s += &style.render(&vc.view());
1680 s += &style.render(&String::from_iter(wrapped_line[col + 1..].iter()));
1681 }
1682 } else {
1683 s += &style.render(&String::from_iter(wrapped_line.iter()));
1684 }
1685 s += &style.render(&" ".repeat(padding));
1686 s += "\n";
1687 new_lines += 1;
1688 }
1689 }
1690
1691 for _ in 0..self.height {
1695 let prompt = self.prompt_view(display_line);
1696 s += &prompt;
1697 display_line += 1;
1698
1699 let left_gutter = self.end_of_buffer_character.to_string();
1701 let right_gap_width =
1702 self.width().saturating_sub(string_width(&left_gutter)) + widest_line_number;
1703 let right_gap = " ".repeat(right_gap_width);
1704 s += &styles
1705 .computed_end_of_buffer()
1706 .render(&(left_gutter + &right_gap));
1707 s += "\n";
1708 }
1709
1710 let _ = new_lines;
1711 s
1712 }
1713
1714 pub fn view(&self) -> String {
1716 let mut viewport = self.viewport.clone();
1719 viewport.set_content(&self.view_inner());
1720 let view = viewport.view();
1721 let styles = self.active_style();
1722 styles.base.clone().render(&view)
1723 }
1724
1725 pub fn prompt_view(&self, display_line: usize) -> String {
1727 let mut prompt = self.prompt.clone();
1728 if let Some(f) = &self.prompt_func {
1729 prompt = f(PromptInfo {
1730 line_number: display_line,
1731 focused: self.focus,
1732 });
1733 let width = rusty_lipgloss::size::width(&prompt);
1734 if width < self.prompt_width {
1735 prompt = format!("{}{}", " ".repeat(self.prompt_width - width), prompt);
1736 }
1737 }
1738
1739 prompt
1740 }
1741
1742 fn line_number_view(&self, n: isize, is_cursor_line: bool) -> String {
1744 if !self.show_line_numbers {
1745 return String::new();
1746 }
1747
1748 let mut str_: String;
1749 if n <= 0 {
1750 str_ = " ".to_string();
1751 } else {
1752 str_ = n.to_string();
1753 }
1754
1755 let mut text_style = self.active_style().computed_text();
1757 let mut line_number_style = self.active_style().computed_line_number();
1758 if is_cursor_line {
1759 text_style = self.active_style().computed_cursor_line();
1760 line_number_style = self.active_style().computed_cursor_line_number();
1761 }
1762
1763 let digits = num_digits(self.max_height);
1766 str_ = format!(" {:>width$} ", str_, width = digits);
1767
1768 text_style.render(&line_number_style.render(&str_))
1769 }
1770
1771 fn placeholder_view(&self) -> String {
1773 let mut s = String::new();
1774 let p = self.placeholder.clone();
1775 let styles = self.active_style();
1776 let pwordwrap = wordwrap(&p, self.width, "");
1778 let pwrap = hardwrap(&pwordwrap, self.width, true);
1780 let plines: Vec<String> = pwrap.trim().split('\n').map(|x| x.to_string()).collect();
1782
1783 for i in 0..self.height {
1784 let is_line_number = plines.len() > i;
1785
1786 let mut line_style = styles.computed_placeholder();
1787 if plines.len() > i {
1788 line_style = styles.computed_cursor_line();
1789 }
1790
1791 let prompt = self.prompt_view(i);
1793 let prompt = styles.computed_prompt().render(&prompt);
1794 s += &line_style.render(&prompt);
1795
1796 if self.show_line_numbers {
1799 let mut ln = 0isize;
1800
1801 match i {
1802 0 => {
1803 ln = (i + 1) as isize;
1804 if plines.len() > i {
1805 s += &self.line_number_view(ln, is_line_number);
1806 }
1807 }
1808 _ => {
1809 if plines.len() > i {
1810 s += &self.line_number_view(ln, is_line_number);
1811 }
1812 }
1813 }
1814 }
1815
1816 match i {
1817 0 => {
1819 let mut vc = self.virtual_cursor.clone();
1821 vc.text_style = styles.computed_placeholder();
1822
1823 let ch = plines[0].chars().next().unwrap_or(' ');
1824 let rest: String = plines[0].chars().skip(1).collect();
1825 vc.set_char(&ch.to_string());
1826 s += &line_style.render(&vc.view());
1827
1828 s += &line_style.render(&styles.computed_placeholder().render(&rest));
1830
1831 let gap = " ".repeat(
1833 self.width
1834 .saturating_sub(rusty_lipgloss::size::width(&plines[0])),
1835 );
1836 s += &line_style.render(&gap);
1837 }
1838 _ => {
1840 if plines.len() > i {
1841 let placeholder_line = &plines[i];
1843 let gap = " ".repeat(self.width.saturating_sub(string_width(&plines[i])));
1844 s += &line_style.render(&(placeholder_line.clone() + &gap));
1845 } else {
1846 let eob = styles
1848 .computed_end_of_buffer()
1849 .render(&self.end_of_buffer_character.to_string());
1850 s += &eob;
1851 }
1852 }
1853 }
1854
1855 s += "\n";
1857 }
1858
1859 let mut viewport = self.viewport.clone();
1860 viewport.set_content(&s);
1861 let v = viewport.view();
1862 styles.base.clone().render(&v)
1863 }
1864
1865 fn memoized_wrap(&self, runes: &[char], width: usize) -> Vec<Vec<char>> {
1866 let _ = runes;
1869 let _ = width;
1870 let _ = &self.cache;
1873 wrap(runes, width)
1874 }
1875
1876 pub fn cursor_line_number(&self) -> usize {
1879 let mut line = 0;
1880 for i in 0..self.row {
1881 line += self.memoized_wrap(&self.value[i], self.width).len();
1884 }
1885 line + self.line_info().row_offset
1886 }
1887
1888 pub fn total_visual_lines(&self) -> usize {
1891 let mut n = 0;
1892 for line in &self.value {
1893 n += self.memoized_wrap(line, self.width).len();
1894 }
1895 n
1896 }
1897
1898 fn recalculate_height(&mut self) {
1901 if !self.dynamic_height {
1902 return;
1903 }
1904 let min_h = self.min_height.max(MIN_HEIGHT);
1905 let total = self.total_visual_lines();
1906 let mut h = total.max(min_h);
1907 if self.max_height > 0 {
1908 h = h.min(self.max_height);
1909 }
1910 let max_offset = total.saturating_sub(h);
1911 if self.viewport.y_offset() > max_offset {
1912 self.viewport.set_y_offset(max_offset);
1913 }
1914 self.set_height(h);
1915 }
1916
1917 fn at_content_limit(&self) -> bool {
1920 if self.max_content_height > 0 {
1921 return self.total_visual_lines() >= self.max_content_height;
1922 }
1923 self.max_height > 0 && self.value.len() >= self.max_height
1924 }
1925
1926 fn visual_lines_for_insert(&self, lines: &[Vec<char>]) -> usize {
1930 if lines.is_empty() {
1931 return 0;
1932 }
1933
1934 let current_row_visual = self.memoized_wrap(&self.value[self.row], self.width).len();
1936
1937 let mut merged: Vec<char> = self.value[self.row][..self.col].to_vec();
1939 merged.extend_from_slice(&lines[0]);
1940 if lines.len() == 1 {
1941 merged.extend_from_slice(&self.value[self.row][self.col..]);
1942 }
1943 let delta = self.memoized_wrap(&merged, self.width).len() - current_row_visual;
1944
1945 let mut delta = delta;
1947 for (i, content) in lines.iter().enumerate() {
1948 let mut content = content.clone();
1949 if i == lines.len() - 1 {
1950 content.extend_from_slice(&self.value[self.row][self.col..]);
1951 }
1952 delta += self.memoized_wrap(&content, self.width).len();
1953 }
1954
1955 delta
1956 }
1957
1958 fn merge_line_below(&mut self, row: usize) {
1961 if row >= self.value.len() - 1 {
1962 return;
1963 }
1964
1965 let mut merged = self.value[row].clone();
1967 merged.extend_from_slice(&self.value[row + 1]);
1968 self.value[row] = merged;
1969
1970 for i in row + 1..self.value.len() - 1 {
1972 self.value[i] = self.value[i + 1].clone();
1973 }
1974
1975 if !self.value.is_empty() {
1977 self.value.pop();
1978 }
1979 }
1980
1981 fn merge_line_above(&mut self, row: usize) {
1984 if row == 0 {
1985 return;
1986 }
1987
1988 self.col = self.value[row - 1].len();
1989 self.row -= 1;
1990
1991 let mut merged = self.value[row - 1].clone();
1993 merged.extend_from_slice(&self.value[row]);
1994 self.value[row - 1] = merged;
1995
1996 for i in row..self.value.len() - 1 {
1998 self.value[i] = self.value[i + 1].clone();
1999 }
2000
2001 if !self.value.is_empty() {
2003 self.value.pop();
2004 }
2005 }
2006
2007 fn split_line(&mut self, row: usize, col: usize) {
2008 let head: Vec<char> = self.value[row][..col].to_vec();
2013 let tail: Vec<char> = self.value[row][col..].to_vec();
2014
2015 self.value.insert(row + 1, tail);
2016
2017 self.value[row] = head;
2018
2019 self.col = 0;
2020 self.row += 1;
2021 }
2022
2023 fn paste_cmd(&self) -> Cmd {
2026 Some(Box::new(|| match clipboard::read_all() {
2027 Ok(str) => Some(Box::new(PasteMsgInternal(str))),
2028 Err(err) => Some(Box::new(PasteErrMsg(err))),
2029 }))
2030 }
2031}
2032
2033pub fn blink() -> Box<dyn Msg> {
2035 crate::cursor::blink()
2036}
2037
2038fn wrap(runes: &[char], width: usize) -> Vec<Vec<char>> {
2039 let mut lines: Vec<Vec<char>> = vec![vec![]];
2040 let mut word: Vec<char> = vec![];
2041 let mut row = 0usize;
2042 let mut spaces = 0usize;
2043
2044 for r in runes {
2046 if r.is_whitespace() {
2047 spaces += 1;
2048 } else {
2049 word.push(*r);
2050 }
2051
2052 if spaces > 0 {
2053 if string_width(&String::from_iter(lines[row].iter()))
2054 + string_width(&String::from_iter(word.iter()))
2055 + spaces
2056 > width
2057 {
2058 row += 1;
2059 lines.push(vec![]);
2060 lines[row].extend_from_slice(&word);
2061 lines[row].extend_from_slice(&repeat_spaces(spaces));
2062 spaces = 0;
2063 word.clear();
2064 } else {
2065 lines[row].extend_from_slice(&word);
2066 lines[row].extend_from_slice(&repeat_spaces(spaces));
2067 spaces = 0;
2068 word.clear();
2069 }
2070 } else if !word.is_empty() {
2071 let last_char_len = char_width(*word.last().unwrap());
2075 if string_width(&String::from_iter(word.iter())) + last_char_len > width {
2076 if !lines[row].is_empty() {
2080 row += 1;
2081 lines.push(vec![]);
2082 }
2083 lines[row].extend_from_slice(&word);
2084 word.clear();
2085 }
2086 }
2087 }
2088
2089 if string_width(&String::from_iter(lines[row].iter()))
2090 + string_width(&String::from_iter(word.iter()))
2091 + spaces
2092 >= width
2093 {
2094 lines.push(vec![]);
2095 lines[row + 1].extend_from_slice(&word);
2096 spaces += 1;
2100 lines[row + 1].extend_from_slice(&repeat_spaces(spaces));
2101 } else {
2102 lines[row].extend_from_slice(&word);
2103 spaces += 1;
2104 lines[row].extend_from_slice(&repeat_spaces(spaces));
2105 }
2106
2107 lines
2108}
2109
2110fn repeat_spaces(n: usize) -> Vec<char> {
2111 vec![' '; n]
2112}
2113
2114fn num_digits(n: usize) -> usize {
2116 if n == 0 {
2117 return 1;
2118 }
2119 let mut count = 0;
2120 let mut num = n;
2121 while num > 0 {
2122 count += 1;
2123 num /= 10;
2124 }
2125 count
2126}
2127
2128fn clamp(v: usize, low: usize, high: usize) -> usize {
2129 if high < low {
2130 return low;
2131 }
2132 v.max(low).min(high)
2133}
2134
2135fn char_width(c: char) -> usize {
2136 UnicodeWidthChar::width(c).unwrap_or(0)
2137}
2138
2139fn string_width(s: &str) -> usize {
2140 s.chars().map(char_width).sum()
2141}
2142
2143fn wordwrap(s: &str, limit: usize, breakpoints: &str) -> String {
2146 if limit < 1 {
2147 return s.to_string();
2148 }
2149
2150 let mut buf = String::new();
2151 let mut word = String::new();
2152 let mut space = String::new();
2153 let mut cur_width = 0usize;
2154 let mut word_len = 0usize;
2155
2156 let add_space = |buf: &mut String, space: &mut String, cur_width: &mut usize| {
2159 *cur_width += space.len();
2160 buf.push_str(space);
2161 space.clear();
2162 };
2163 let add_word = |buf: &mut String,
2166 space: &mut String,
2167 word: &mut String,
2168 cur_width: &mut usize,
2169 word_len: &mut usize| {
2170 if word.is_empty() {
2171 return;
2172 }
2173 add_space(buf, space, cur_width);
2174 *cur_width += *word_len;
2175 buf.push_str(word);
2176 word.clear();
2177 *word_len = 0;
2178 };
2179 let add_newline = |buf: &mut String, space: &mut String, cur_width: &mut usize| {
2180 buf.push('\n');
2181 *cur_width = 0;
2182 space.clear();
2183 };
2184
2185 for c in s.chars() {
2186 if c == '\n' {
2187 if word_len == 0 {
2188 if cur_width + space.len() > limit {
2189 cur_width = 0;
2190 } else {
2191 buf.push_str(&space);
2192 }
2193 space.clear();
2194 }
2195 add_word(
2196 &mut buf,
2197 &mut space,
2198 &mut word,
2199 &mut cur_width,
2200 &mut word_len,
2201 );
2202 add_newline(&mut buf, &mut space, &mut cur_width);
2203 } else if c.is_whitespace() && c != '\u{00A0}' {
2204 add_word(
2205 &mut buf,
2206 &mut space,
2207 &mut word,
2208 &mut cur_width,
2209 &mut word_len,
2210 );
2211 space.push(c);
2212 } else if c == '-' || breakpoints.contains(c) {
2213 add_space(&mut buf, &mut space, &mut cur_width);
2214 add_word(
2215 &mut buf,
2216 &mut space,
2217 &mut word,
2218 &mut cur_width,
2219 &mut word_len,
2220 );
2221 buf.push(c);
2222 cur_width += 1;
2223 } else {
2224 word.push(c);
2225 word_len += char_width(c);
2226 if cur_width + space.len() + word_len > limit && word_len < limit {
2227 add_newline(&mut buf, &mut space, &mut cur_width);
2228 }
2229 }
2230 }
2231
2232 add_word(
2233 &mut buf,
2234 &mut space,
2235 &mut word,
2236 &mut cur_width,
2237 &mut word_len,
2238 );
2239 buf
2240}
2241
2242fn hardwrap(s: &str, limit: usize, preserve_space: bool) -> String {
2245 if limit < 1 {
2246 return s.to_string();
2247 }
2248
2249 let mut buf = String::new();
2250 let mut cur_width = 0usize;
2251 let mut force_newline = false;
2252
2253 for c in s.chars() {
2254 if c == '\n' {
2255 buf.push('\n');
2256 cur_width = 0;
2257 force_newline = false;
2258 continue;
2259 }
2260
2261 let w = char_width(c);
2262 if cur_width + w > limit {
2263 buf.push('\n');
2264 cur_width = 0;
2265 force_newline = true;
2266 }
2267
2268 if cur_width == 0 {
2270 if !preserve_space && force_newline && c.is_whitespace() {
2271 continue;
2272 }
2273 force_newline = false;
2274 }
2275
2276 buf.push(c);
2277 cur_width += w;
2278 }
2279
2280 buf
2281}