1pub mod helpers;
47pub mod memoization;
48
49#[cfg(test)]
50mod tests;
51
52use helpers::*;
53use memoization::MemoizedWrap;
54
55use crate::{cursor, viewport, Component};
56use bubbletea_rs::{Cmd, Model as BubbleTeaModel};
57use lipgloss_extras::lipgloss;
58use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
59
60const MIN_HEIGHT: usize = 1;
62const DEFAULT_HEIGHT: usize = 6;
63const DEFAULT_WIDTH: usize = 40;
64const DEFAULT_CHAR_LIMIT: usize = 0; const DEFAULT_MAX_HEIGHT: usize = 99;
66const DEFAULT_MAX_WIDTH: usize = 500;
67const MAX_LINES: usize = 10000;
68
69#[derive(Debug, Clone)]
71pub struct PasteMsg(pub String);
72
73#[derive(Debug, Clone)]
75pub struct PasteErrMsg(pub String);
76
77#[derive(Debug, Clone, Default)]
80pub struct LineInfo {
81 pub width: usize,
83 pub char_width: usize,
85 pub height: usize,
87 pub start_column: usize,
89 pub column_offset: usize,
91 pub row_offset: usize,
93 pub char_offset: usize,
95}
96
97#[derive(Debug)]
100pub struct Model {
101 pub err: Option<String>,
104
105 cache: MemoizedWrap,
107
108 pub prompt: String,
111 pub placeholder: String,
113 pub show_line_numbers: bool,
115 pub end_of_buffer_character: char,
117
118 pub key_map: TextareaKeyMap,
121
122 pub focused_style: TextareaStyle,
125 pub blurred_style: TextareaStyle,
127 current_style: TextareaStyle,
129
130 pub cursor: cursor::Model,
133
134 pub char_limit: usize,
137 pub max_height: usize,
139 pub max_width: usize,
141
142 prompt_func: Option<fn(usize) -> String>,
144 prompt_width: usize,
146
147 width: usize,
150 height: usize,
152
153 value: Vec<Vec<char>>,
156
157 focus: bool,
160 col: usize,
162 row: usize,
164 last_char_offset: usize,
166
167 viewport: viewport::Model,
169}
170
171impl Model {
172 pub fn new() -> Self {
174 let vp = viewport::Model::new(0, 0);
175 let cur = cursor::Model::new();
178
179 let (focused_style, blurred_style) = default_styles();
180
181 let mut model = Self {
182 err: None,
183 cache: MemoizedWrap::new(),
184 prompt: format!("{} ", lipgloss::thick_border().left),
185 placeholder: String::new(),
186 show_line_numbers: true,
187 end_of_buffer_character: ' ',
188 key_map: TextareaKeyMap::default(),
189 focused_style: focused_style.clone(),
190 blurred_style: blurred_style.clone(),
191 current_style: blurred_style, cursor: cur,
193 char_limit: DEFAULT_CHAR_LIMIT,
194 max_height: DEFAULT_MAX_HEIGHT,
195 max_width: DEFAULT_MAX_WIDTH,
196 prompt_func: None,
197 prompt_width: 0,
198 width: DEFAULT_WIDTH,
199 height: DEFAULT_HEIGHT,
200 value: vec![vec![]; MIN_HEIGHT],
201 focus: false,
202 col: 0,
203 row: 0,
204 last_char_offset: 0,
205 viewport: vp,
206 };
207
208 model.value.reserve(MAX_LINES);
210 model.set_height(DEFAULT_HEIGHT);
211 model.set_width(DEFAULT_WIDTH);
212
213 model
214 }
215
216 pub fn set_value(&mut self, s: impl Into<String>) {
218 self.reset();
219 self.insert_string(s.into());
220 self.row = self.value.len().saturating_sub(1);
222 if let Some(line) = self.value.get(self.row) {
223 self.set_cursor(line.len());
224 }
225 }
226
227 pub fn insert_string(&mut self, s: impl Into<String>) {
229 let s = s.into();
230 let runes: Vec<char> = s.chars().collect();
231 self.insert_runes_from_user_input(runes);
232 }
233
234 pub fn insert_rune(&mut self, r: char) {
236 self.insert_runes_from_user_input(vec![r]);
237 }
238
239 pub fn value(&self) -> String {
241 if self.value.is_empty() {
242 return String::new();
243 }
244
245 let mut result = String::new();
246 for (i, line) in self.value.iter().enumerate() {
247 if i > 0 {
248 result.push('\n');
249 }
250 result.extend(line.iter());
251 }
252 result
253 }
254
255 pub fn length(&self) -> usize {
257 let mut l = 0;
258 for row in &self.value {
259 l += row
260 .iter()
261 .map(|&ch| UnicodeWidthChar::width(ch).unwrap_or(0))
262 .sum::<usize>();
263 }
264 l + self.value.len().saturating_sub(1)
266 }
267
268 pub fn line_count(&self) -> usize {
270 self.value.len()
271 }
272
273 pub fn line(&self) -> usize {
275 self.row
276 }
277
278 pub fn focused(&self) -> bool {
280 self.focus
281 }
282
283 pub fn reset(&mut self) {
285 self.value = vec![vec![]; MIN_HEIGHT];
286 self.value.reserve(MAX_LINES);
287 self.col = 0;
288 self.row = 0;
289 self.viewport.goto_top();
290 self.set_cursor(0);
291 }
292
293 pub fn width(&self) -> usize {
295 self.width
296 }
297
298 pub fn height(&self) -> usize {
300 self.height
301 }
302
303 pub fn set_width(&mut self, w: usize) {
305 if self.prompt_func.is_none() {
307 self.prompt_width = self.prompt.width();
308 }
309
310 let reserved_outer = 0; let mut reserved_inner = self.prompt_width;
315
316 if self.show_line_numbers {
318 let ln_width = 4; reserved_inner += ln_width;
320 }
321
322 let min_width = reserved_inner + reserved_outer + 1;
324 let mut input_width = w.max(min_width);
325
326 if self.max_width > 0 {
328 input_width = input_width.min(self.max_width);
329 }
330
331 self.viewport.width = input_width.saturating_sub(reserved_outer);
332 self.width = input_width
333 .saturating_sub(reserved_outer)
334 .saturating_sub(reserved_inner);
335 }
336
337 pub fn set_height(&mut self, h: usize) {
339 if self.max_height > 0 {
340 self.height = clamp(h, MIN_HEIGHT, self.max_height);
341 self.viewport.height = clamp(h, MIN_HEIGHT, self.max_height);
342 } else {
343 self.height = h.max(MIN_HEIGHT);
344 self.viewport.height = h.max(MIN_HEIGHT);
345 }
346 }
347
348 pub fn set_prompt_func(&mut self, prompt_width: usize, func: fn(usize) -> String) {
351 self.prompt_func = Some(func);
352 self.prompt_width = prompt_width;
353 }
354
355 pub fn set_cursor(&mut self, col: usize) {
357 self.col = clamp(
358 col,
359 0,
360 self.value.get(self.row).map_or(0, |line| line.len()),
361 );
362 self.last_char_offset = 0;
364 }
365
366 pub fn cursor_start(&mut self) {
368 self.set_cursor(0);
369 }
370
371 pub fn cursor_end(&mut self) {
373 if let Some(line) = self.value.get(self.row) {
374 self.set_cursor(line.len());
375 }
376 }
377
378 pub fn cursor_down(&mut self) {
380 let li = self.line_info();
381 let char_offset = self.last_char_offset.max(li.char_offset);
382 self.last_char_offset = char_offset;
383
384 if li.row_offset + 1 >= li.height && self.row < self.value.len().saturating_sub(1) {
385 self.row += 1;
386 self.col = 0;
387 } else {
388 const TRAILING_SPACE: usize = 2;
390 if let Some(line) = self.value.get(self.row) {
391 self.col =
392 (li.start_column + li.width + TRAILING_SPACE).min(line.len().saturating_sub(1));
393 }
394 }
395
396 let nli = self.line_info();
397 self.col = nli.start_column;
398
399 if nli.width == 0 {
400 return;
401 }
402
403 let mut offset = 0;
404 while offset < char_offset {
405 if self.row >= self.value.len()
406 || self.col >= self.value.get(self.row).map_or(0, |line| line.len())
407 || offset >= nli.char_width.saturating_sub(1)
408 {
409 break;
410 }
411 if let Some(line) = self.value.get(self.row) {
412 if let Some(&ch) = line.get(self.col) {
413 offset += UnicodeWidthChar::width(ch).unwrap_or(0);
414 }
415 }
416 self.col += 1;
417 }
418 }
419
420 pub fn cursor_up(&mut self) {
422 let li = self.line_info();
423 let char_offset = self.last_char_offset.max(li.char_offset);
424 self.last_char_offset = char_offset;
425
426 if li.row_offset == 0 && self.row > 0 {
427 self.row -= 1;
428 if let Some(line) = self.value.get(self.row) {
429 self.col = line.len();
430 }
431 } else {
432 const TRAILING_SPACE: usize = 2;
434 self.col = li.start_column.saturating_sub(TRAILING_SPACE);
435 }
436
437 let nli = self.line_info();
438 self.col = nli.start_column;
439
440 if nli.width == 0 {
441 return;
442 }
443
444 let mut offset = 0;
445 while offset < char_offset {
446 if let Some(line) = self.value.get(self.row) {
447 if self.col >= line.len() || offset >= nli.char_width.saturating_sub(1) {
448 break;
449 }
450 if let Some(&ch) = line.get(self.col) {
451 offset += UnicodeWidthChar::width(ch).unwrap_or(0);
452 }
453 self.col += 1;
454 } else {
455 break;
456 }
457 }
458 }
459
460 pub fn move_to_begin(&mut self) {
462 self.row = 0;
463 self.set_cursor(0);
464 }
465
466 pub fn move_to_end(&mut self) {
468 self.row = self.value.len().saturating_sub(1);
469 if let Some(line) = self.value.get(self.row) {
470 self.set_cursor(line.len());
471 }
472 }
473
474 fn insert_runes_from_user_input(&mut self, mut runes: Vec<char>) {
478 runes = self.sanitize_runes(runes);
480
481 if self.char_limit > 0 {
482 let avail_space = self.char_limit.saturating_sub(self.length());
483 if avail_space == 0 {
484 return;
485 }
486 if avail_space < runes.len() {
487 runes.truncate(avail_space);
488 }
489 }
490
491 let mut lines = Vec::new();
493 let mut lstart = 0;
494
495 for (i, &r) in runes.iter().enumerate() {
496 if r == '\n' {
497 lines.push(runes[lstart..i].to_vec());
498 lstart = i + 1;
499 }
500 }
501
502 if lstart <= runes.len() {
503 lines.push(runes[lstart..].to_vec());
504 }
505
506 if MAX_LINES > 0 && self.value.len() + lines.len() - 1 > MAX_LINES {
508 let allowed_height = (MAX_LINES - self.value.len() + 1).max(0);
509 lines.truncate(allowed_height);
510 }
511
512 if lines.is_empty() {
513 return;
514 }
515
516 while self.row >= self.value.len() {
518 self.value.push(Vec::new());
519 }
520
521 let tail = if self.col < self.value[self.row].len() {
523 self.value[self.row][self.col..].to_vec()
524 } else {
525 Vec::new()
526 };
527
528 if self.col <= self.value[self.row].len() {
530 self.value[self.row].truncate(self.col);
531 }
532 self.value[self.row].extend_from_slice(&lines[0]);
533 self.col += lines[0].len();
534
535 if lines.len() > 1 {
536 for (i, line) in lines[1..].iter().enumerate() {
538 self.value.insert(self.row + 1 + i, line.clone());
539 }
540 self.row += lines.len() - 1;
542 self.col = lines.last().map(|l| l.len()).unwrap_or(0);
543 self.value[self.row].extend_from_slice(&tail);
545 } else {
546 self.value[self.row].extend_from_slice(&tail);
548 }
549
550 self.set_cursor(self.col);
551 }
552
553 fn sanitize_runes(&self, runes: Vec<char>) -> Vec<char> {
555 runes
557 }
558
559 pub fn line_info(&mut self) -> LineInfo {
562 if self.row >= self.value.len() {
563 return LineInfo::default();
564 }
565
566 let current_line = self.value[self.row].clone();
568 let width = self.width;
569 let grid = self.cache.wrap(¤t_line, width);
570
571 let mut counter = 0;
573 for (i, line) in grid.iter().enumerate() {
574 if counter + line.len() == self.col && i + 1 < grid.len() {
576 return LineInfo {
578 char_offset: 0,
579 column_offset: 0,
580 height: grid.len(),
581 row_offset: i + 1,
582 start_column: self.col,
583 width: grid.get(i + 1).map_or(0, |l| l.len()),
584 char_width: line
585 .iter()
586 .map(|&ch| UnicodeWidthChar::width(ch).unwrap_or(0))
587 .sum(),
588 };
589 }
590
591 if counter + line.len() >= self.col {
592 let col_in_line = self.col.saturating_sub(counter);
593 let char_off: usize = line[..col_in_line.min(line.len())]
594 .iter()
595 .map(|&ch| UnicodeWidthChar::width(ch).unwrap_or(0))
596 .sum();
597 return LineInfo {
598 char_offset: char_off,
599 column_offset: col_in_line, height: grid.len(),
601 row_offset: i,
602 start_column: counter,
603 width: line.len(),
604 char_width: line
605 .iter()
606 .map(|&ch| UnicodeWidthChar::width(ch).unwrap_or(0))
607 .sum(),
608 };
609 }
610
611 counter += line.len();
612 }
613
614 LineInfo::default()
615 }
616
617 pub fn delete_before_cursor(&mut self) {
619 if let Some(line) = self.value.get_mut(self.row) {
620 let tail = if self.col <= line.len() {
621 line[self.col..].to_vec()
622 } else {
623 Vec::new()
624 };
625 *line = tail;
626 }
627 self.set_cursor(0);
628 }
629
630 pub fn delete_after_cursor(&mut self) {
632 if let Some(line) = self.value.get_mut(self.row) {
633 line.truncate(self.col);
634 let line_len = line.len();
635 self.set_cursor(line_len);
636 }
637 }
638
639 pub fn delete_character_backward(&mut self) {
641 self.col = clamp(
642 self.col,
643 0,
644 self.value.get(self.row).map_or(0, |line| line.len()),
645 );
646 if self.col == 0 {
647 self.merge_line_above(self.row);
648 return;
649 }
650
651 if let Some(line) = self.value.get_mut(self.row) {
652 if !line.is_empty() && self.col > 0 {
653 line.remove(self.col - 1);
654 self.set_cursor(self.col - 1);
655 }
656 }
657 }
658
659 pub fn delete_character_forward(&mut self) {
661 if let Some(line) = self.value.get_mut(self.row) {
662 if !line.is_empty() && self.col < line.len() {
663 line.remove(self.col);
664 }
665 }
666
667 if self.col >= self.value.get(self.row).map_or(0, |line| line.len()) {
668 self.merge_line_below(self.row);
669 }
670 }
671
672 pub fn delete_word_backward(&mut self) {
674 if self.col == 0 {
675 self.merge_line_above(self.row);
676 return;
677 }
678
679 let line = if let Some(line) = self.value.get(self.row) {
680 line.clone()
681 } else {
682 return;
683 };
684
685 if line.is_empty() {
686 return;
687 }
688
689 let mut start = self.col;
691 let mut end = self.col;
692
693 while end < line.len() && line.get(end).is_some_and(|&c| !c.is_whitespace()) {
695 end += 1;
696 }
697
698 while start > 0 && line.get(start - 1).is_some_and(|&c| !c.is_whitespace()) {
700 start -= 1;
701 }
702
703 if self.col < line.len() && line.get(self.col).is_some_and(|&c| !c.is_whitespace()) {
705 if start > 0 && line.get(start - 1).is_some_and(|&c| c.is_whitespace()) {
707 start -= 1;
708 }
709 }
710
711 if let Some(line_mut) = self.value.get_mut(self.row) {
712 let end_clamped = end.min(line_mut.len());
713 let start_clamped = start.min(end_clamped);
714 line_mut.drain(start_clamped..end_clamped);
715 }
716
717 self.set_cursor(start);
718 }
719
720 pub fn delete_word_forward(&mut self) {
722 let line = if let Some(line) = self.value.get(self.row) {
723 line.clone()
724 } else {
725 return;
726 };
727
728 if self.col >= line.len() || line.is_empty() {
729 self.merge_line_below(self.row);
730 return;
731 }
732
733 let old_col = self.col;
734 let mut new_col = self.col;
735
736 while new_col < line.len() {
738 if let Some(&ch) = line.get(new_col) {
739 if ch.is_whitespace() {
740 new_col += 1;
741 } else {
742 break;
743 }
744 } else {
745 break;
746 }
747 }
748
749 while new_col < line.len() {
751 if let Some(&ch) = line.get(new_col) {
752 if !ch.is_whitespace() {
753 new_col += 1;
754 } else {
755 break;
756 }
757 } else {
758 break;
759 }
760 }
761
762 if let Some(line) = self.value.get_mut(self.row) {
764 if new_col > line.len() {
765 line.truncate(old_col);
766 } else {
767 line.drain(old_col..new_col);
768 }
769 }
770
771 self.set_cursor(old_col);
772 }
773
774 fn merge_line_below(&mut self, row: usize) {
776 if row >= self.value.len().saturating_sub(1) {
777 return;
778 }
779
780 if let Some(next_line) = self.value.get(row + 1).cloned() {
782 if let Some(current_line) = self.value.get_mut(row) {
783 current_line.extend_from_slice(&next_line);
784 }
785 }
786
787 self.value.remove(row + 1);
789 }
790
791 fn merge_line_above(&mut self, row: usize) {
793 if row == 0 {
794 return;
795 }
796
797 if let Some(prev_line) = self.value.get(row - 1) {
798 self.col = prev_line.len();
799 }
800 self.row = row - 1;
801
802 if let Some(current_line) = self.value.get(row).cloned() {
804 if let Some(prev_line) = self.value.get_mut(row - 1) {
805 prev_line.extend_from_slice(¤t_line);
806 }
807 }
808
809 self.value.remove(row);
811 }
812
813 fn split_line(&mut self, row: usize, col: usize) {
815 if let Some(line) = self.value.get(row) {
816 let head = line[..col].to_vec();
817 let tail = line[col..].to_vec();
818
819 self.value[row] = head;
821
822 self.value.insert(row + 1, tail);
824
825 self.col = 0;
826 self.row += 1;
827 }
828 }
829
830 pub fn insert_newline(&mut self) {
832 if self.max_height > 0 && self.value.len() >= self.max_height {
833 return;
834 }
835
836 self.col = clamp(
837 self.col,
838 0,
839 self.value.get(self.row).map_or(0, |line| line.len()),
840 );
841 self.split_line(self.row, self.col);
842 }
843
844 pub fn character_left(&mut self, inside_line: bool) {
846 if self.col == 0 && self.row != 0 {
847 self.row -= 1;
848 if let Some(line) = self.value.get(self.row) {
849 self.col = line.len();
850 if !inside_line {
851 return;
852 }
853 }
854 }
855 if self.col > 0 {
856 self.set_cursor(self.col - 1);
857 }
858 }
859
860 pub fn character_right(&mut self) {
862 if let Some(line) = self.value.get(self.row) {
863 if self.col < line.len() {
864 self.set_cursor(self.col + 1);
865 } else if self.row < self.value.len() - 1 {
866 self.row += 1;
867 self.cursor_start();
868 }
869 }
870 }
871
872 pub fn word_left(&mut self) {
874 while self.col > 0 {
876 if let Some(line) = self.value.get(self.row) {
877 if line.get(self.col - 1).is_some_and(|c| c.is_whitespace()) {
878 self.set_cursor(self.col - 1);
879 } else {
880 break;
881 }
882 } else {
883 break;
884 }
885 }
886 while self.col > 0 {
888 if let Some(line) = self.value.get(self.row) {
889 if line.get(self.col - 1).is_some_and(|c| !c.is_whitespace()) {
890 self.set_cursor(self.col - 1);
891 } else {
892 break;
893 }
894 } else {
895 break;
896 }
897 }
898 }
899
900 pub fn word_right(&mut self) {
902 self.do_word_right(|_, _| {});
903 }
904
905 fn do_word_right<F>(&mut self, mut func: F)
907 where
908 F: FnMut(usize, usize),
909 {
910 if self.row >= self.value.len() {
911 return;
912 }
913
914 let line = match self.value.get(self.row) {
915 Some(line) => line.clone(),
916 None => return,
917 };
918
919 if self.col >= line.len() {
920 return;
921 }
922
923 let mut pos = self.col;
924 let mut char_idx = 0;
925
926 while pos < line.len() && line[pos].is_whitespace() {
928 pos += 1;
929 }
930
931 while pos < line.len() && !line[pos].is_whitespace() {
933 func(char_idx, pos);
934 pos += 1;
935 char_idx += 1;
936 }
937
938 self.set_cursor(pos);
940 }
941
942 pub fn uppercase_right(&mut self) {
944 let start_col = self.col;
945 let start_row = self.row;
946
947 self.word_right(); let end_col = self.col;
950
951 if let Some(line) = self.value.get_mut(start_row) {
953 let end_idx = end_col.min(line.len());
954 if let Some(slice) = line.get_mut(start_col..end_idx) {
955 for ch in slice.iter_mut() {
956 *ch = ch.to_uppercase().next().unwrap_or(*ch);
957 }
958 }
959 }
960 }
961
962 pub fn lowercase_right(&mut self) {
964 let start_col = self.col;
965 let start_row = self.row;
966
967 self.word_right(); let end_col = self.col;
970
971 if let Some(line) = self.value.get_mut(start_row) {
973 let end_idx = end_col.min(line.len());
974 if let Some(slice) = line.get_mut(start_col..end_idx) {
975 for ch in slice.iter_mut() {
976 *ch = ch.to_lowercase().next().unwrap_or(*ch);
977 }
978 }
979 }
980 }
981
982 pub fn capitalize_right(&mut self) {
984 let start_col = self.col;
985 let start_row = self.row;
986
987 self.word_right(); let end_col = self.col;
990
991 if let Some(line) = self.value.get_mut(start_row) {
993 let end_idx = end_col.min(line.len());
994 if let Some(slice) = line.get_mut(start_col..end_idx) {
995 for (i, ch) in slice.iter_mut().enumerate() {
996 if i == 0 {
997 *ch = ch.to_uppercase().next().unwrap_or(*ch);
998 }
999 }
1000 }
1001 }
1002 }
1003
1004 pub fn transpose_left(&mut self) {
1006 let row = self.row;
1007 let mut col = self.col;
1008
1009 if let Some(line) = self.value.get_mut(row) {
1010 if col == 0 || line.len() < 2 {
1011 return;
1012 }
1013
1014 if col >= line.len() {
1015 col -= 1;
1016 self.col = col;
1017 }
1018
1019 if col > 0 && col < line.len() {
1020 line.swap(col - 1, col);
1021 if col < line.len() {
1022 self.col = col + 1;
1023 }
1024 }
1025 }
1026 }
1027
1028 pub fn view(&self) -> String {
1030 if self.value.is_empty() || (self.value.len() == 1 && self.value[0].is_empty()) {
1032 return self.placeholder_view();
1033 }
1034
1035 let mut lines = Vec::new();
1036 let style = &self.current_style;
1037
1038 let start_line = self.viewport.y_offset;
1040 let end_line = (start_line + self.height).min(self.value.len());
1041
1042 for (line_idx, line) in self
1043 .value
1044 .iter()
1045 .enumerate()
1046 .skip(start_line)
1047 .take(end_line - start_line)
1048 {
1049 let mut line_str = String::new();
1050
1051 if let Some(prompt_func) = self.prompt_func {
1053 line_str.push_str(&style.computed_prompt().render(&prompt_func(line_idx + 1)));
1054 } else {
1055 line_str.push_str(&style.computed_prompt().render(&self.prompt));
1056 }
1057
1058 if self.show_line_numbers {
1060 let line_num = format!("{:>3} ", line_idx + 1);
1061 line_str.push_str(&style.computed_line_number().render(&line_num));
1062 }
1063
1064 let mut cache = self.cache.clone();
1066 let wrapped_lines = cache.wrap(line, self.width);
1067
1068 for (wrap_idx, wrapped_line) in wrapped_lines.iter().enumerate() {
1069 let mut display_line = line_str.clone();
1070
1071 if wrap_idx > 0 {
1072 if self.show_line_numbers {
1074 display_line =
1075 format!("{} ", style.computed_prompt().render(&self.prompt));
1076 } else {
1077 display_line = style.computed_prompt().render(&self.prompt);
1078 }
1079 }
1080
1081 let wrapped_content: String = wrapped_line.iter().collect();
1082
1083 if line_idx == self.row {
1085 display_line.push_str(&style.computed_cursor_line().render(&wrapped_content));
1086 } else {
1087 display_line.push_str(&style.computed_text().render(&wrapped_content));
1088 }
1089
1090 lines.push(display_line);
1091 }
1092 }
1093
1094 while lines.len() < self.height {
1096 let mut empty_line = String::new();
1097
1098 empty_line.push_str(&style.computed_prompt().render(&self.prompt));
1100
1101 if self.end_of_buffer_character != ' ' {
1103 empty_line.push_str(
1104 &style
1105 .computed_end_of_buffer()
1106 .render(&self.end_of_buffer_character.to_string()),
1107 );
1108 }
1109
1110 lines.push(empty_line);
1111 }
1112
1113 let content = lines.join("\n");
1115 let styled = style.base.render(&content);
1116 lipgloss::strip_ansi(&styled)
1117 }
1118
1119 fn placeholder_view(&self) -> String {
1121 if self.placeholder.is_empty() {
1122 return String::new();
1123 }
1124
1125 let mut lines = Vec::new();
1126 let style = &self.current_style;
1127
1128 let placeholder_lines: Vec<&str> = self.placeholder.lines().collect();
1130
1131 for (line_idx, &placeholder_line) in placeholder_lines.iter().enumerate() {
1132 let mut line_str = String::new();
1133
1134 if let Some(prompt_func) = self.prompt_func {
1136 line_str.push_str(&style.computed_prompt().render(&prompt_func(line_idx + 1)));
1137 } else {
1138 line_str.push_str(&style.computed_prompt().render(&self.prompt));
1139 }
1140
1141 if self.show_line_numbers {
1143 if line_idx == 0 {
1144 line_str.push_str(&style.computed_line_number().render(" 1 "));
1145 } else {
1146 line_str.push_str(&style.computed_line_number().render(" "));
1147 }
1148 }
1149
1150 let mut cache = self.cache.clone();
1152 let wrapped = cache.wrap(&placeholder_line.chars().collect::<Vec<_>>(), self.width);
1153
1154 for (wrap_idx, wrapped_line) in wrapped.iter().enumerate() {
1155 let mut display_line = line_str.clone();
1156
1157 if wrap_idx > 0 {
1158 if self.show_line_numbers {
1160 display_line =
1161 format!("{} ", style.computed_prompt().render(&self.prompt));
1162 } else {
1163 display_line = style.computed_prompt().render(&self.prompt);
1164 }
1165 }
1166
1167 let wrapped_content: String = wrapped_line.iter().collect();
1168 display_line.push_str(&style.computed_placeholder().render(&wrapped_content));
1169
1170 lines.push(display_line);
1171
1172 if lines.len() >= self.height {
1173 break;
1174 }
1175 }
1176
1177 if lines.len() >= self.height {
1178 break;
1179 }
1180 }
1181
1182 while lines.len() < self.height {
1184 let mut empty_line = String::new();
1185
1186 empty_line.push_str(&style.computed_prompt().render(&self.prompt));
1188
1189 if self.end_of_buffer_character != ' ' {
1191 empty_line.push_str(
1192 &style
1193 .computed_end_of_buffer()
1194 .render(&self.end_of_buffer_character.to_string()),
1195 );
1196 }
1197
1198 lines.push(empty_line);
1199 }
1200
1201 let content = lines.join("\n");
1203 let styled = style.base.render(&content);
1204 lipgloss::strip_ansi(&styled)
1205 }
1206
1207 pub fn scroll_down(&mut self, lines: usize) {
1209 self.viewport.set_y_offset(self.viewport.y_offset + lines);
1210 }
1211
1212 pub fn scroll_up(&mut self, lines: usize) {
1214 self.viewport
1215 .set_y_offset(self.viewport.y_offset.saturating_sub(lines));
1216 }
1217
1218 pub fn cursor_line_number(&mut self) -> usize {
1220 if self.row >= self.value.len() {
1221 return 0;
1222 }
1223
1224 let mut line_count = 0;
1226 for i in 0..self.row {
1227 if let Some(line) = self.value.get(i).cloned() {
1228 let wrapped_lines = self.cache.wrap(&line, self.width);
1229 line_count += wrapped_lines.len();
1230 }
1231 }
1232
1233 line_count += self.line_info().row_offset;
1235 line_count
1236 }
1237
1238 pub fn update(&mut self, msg: Option<bubbletea_rs::Msg>) -> Option<bubbletea_rs::Cmd> {
1240 if !self.focus {
1241 return None;
1242 }
1243
1244 if let Some(msg) = msg {
1245 if let Some(paste_msg) = msg.downcast_ref::<PasteMsg>() {
1247 self.insert_string(paste_msg.0.clone());
1248 return None;
1249 }
1250
1251 if let Some(_paste_err) = msg.downcast_ref::<PasteErrMsg>() {
1252 return None;
1254 }
1255
1256 if let Some(key_msg) = msg.downcast_ref::<bubbletea_rs::KeyMsg>() {
1258 return self.handle_key_msg(key_msg);
1259 }
1260
1261 let cursor_cmd = self.cursor.update(&msg);
1263 let viewport_cmd = self.viewport.update(msg);
1264
1265 cursor_cmd.or(viewport_cmd)
1267 } else {
1268 None
1269 }
1270 }
1271
1272 fn handle_key_msg(&mut self, key_msg: &bubbletea_rs::KeyMsg) -> Option<bubbletea_rs::Cmd> {
1274 use crate::key::matches_binding;
1275
1276 if matches_binding(key_msg, &self.key_map.character_forward) {
1278 self.character_right();
1279 } else if matches_binding(key_msg, &self.key_map.character_backward) {
1280 self.character_left(false);
1281
1282 } else if matches_binding(key_msg, &self.key_map.word_forward) {
1284 self.word_right();
1285 } else if matches_binding(key_msg, &self.key_map.word_backward) {
1286 self.word_left();
1287
1288 } else if matches_binding(key_msg, &self.key_map.line_next) {
1290 self.cursor_down();
1291 } else if matches_binding(key_msg, &self.key_map.line_previous) {
1292 self.cursor_up();
1293 } else if matches_binding(key_msg, &self.key_map.line_start) {
1294 self.cursor_start();
1295 } else if matches_binding(key_msg, &self.key_map.line_end) {
1296 self.cursor_end();
1297
1298 } else if matches_binding(key_msg, &self.key_map.input_begin) {
1300 self.move_to_begin();
1301 } else if matches_binding(key_msg, &self.key_map.input_end) {
1302 self.move_to_end();
1303
1304 } else if matches_binding(key_msg, &self.key_map.delete_character_backward) {
1306 self.delete_character_backward();
1307 } else if matches_binding(key_msg, &self.key_map.delete_character_forward) {
1308 self.delete_character_forward();
1309 } else if matches_binding(key_msg, &self.key_map.delete_word_backward) {
1310 self.delete_word_backward();
1311 } else if matches_binding(key_msg, &self.key_map.delete_word_forward) {
1312 self.delete_word_forward();
1313 } else if matches_binding(key_msg, &self.key_map.delete_after_cursor) {
1314 self.delete_after_cursor();
1315 } else if matches_binding(key_msg, &self.key_map.delete_before_cursor) {
1316 self.delete_before_cursor();
1317
1318 } else if matches_binding(key_msg, &self.key_map.insert_newline) {
1320 self.insert_newline();
1321
1322 } else if matches_binding(key_msg, &self.key_map.paste) {
1324 return Some(self.paste_command());
1325
1326 } else if matches_binding(key_msg, &self.key_map.uppercase_word_forward) {
1328 self.uppercase_right();
1329 } else if matches_binding(key_msg, &self.key_map.lowercase_word_forward) {
1330 self.lowercase_right();
1331 } else if matches_binding(key_msg, &self.key_map.capitalize_word_forward) {
1332 self.capitalize_right();
1333 } else if matches_binding(key_msg, &self.key_map.transpose_character_backward) {
1334 self.transpose_left();
1335 } else {
1336 if let Some(ch) = self.extract_character_from_key_msg(key_msg) {
1338 if ch.is_control() {
1339 return None;
1341 }
1342 self.insert_rune(ch);
1343 }
1344 }
1345
1346 None
1347 }
1348
1349 fn extract_character_from_key_msg(&self, _key_msg: &bubbletea_rs::KeyMsg) -> Option<char> {
1351 None
1355 }
1356
1357 fn paste_command(&self) -> bubbletea_rs::Cmd {
1359 bubbletea_rs::tick(
1360 std::time::Duration::from_nanos(1),
1361 |_| match Self::read_clipboard() {
1362 Ok(content) => Box::new(PasteMsg(content)) as bubbletea_rs::Msg,
1363 Err(err) => Box::new(PasteErrMsg(err)) as bubbletea_rs::Msg,
1364 },
1365 )
1366 }
1367
1368 fn read_clipboard() -> Result<String, String> {
1370 #[cfg(feature = "clipboard-support")]
1371 {
1372 use clipboard::{ClipboardContext, ClipboardProvider};
1373
1374 let mut ctx: ClipboardContext = ClipboardProvider::new()
1375 .map_err(|e| format!("Failed to create clipboard context: {}", e))?;
1376
1377 ctx.get_contents()
1378 .map_err(|e| format!("Failed to read clipboard: {}", e))
1379 }
1380 #[cfg(not(feature = "clipboard-support"))]
1381 {
1382 Err("Clipboard support not enabled".to_string())
1383 }
1384 }
1385
1386 pub fn copy_to_clipboard(&self, text: &str) -> Result<(), String> {
1388 #[cfg(feature = "clipboard-support")]
1389 {
1390 use clipboard::{ClipboardContext, ClipboardProvider};
1391
1392 let mut ctx: ClipboardContext = ClipboardProvider::new()
1393 .map_err(|e| format!("Failed to create clipboard context: {}", e))?;
1394
1395 ctx.set_contents(text.to_string())
1396 .map_err(|e| format!("Failed to write to clipboard: {}", e))
1397 }
1398 #[cfg(not(feature = "clipboard-support"))]
1399 {
1400 let _ = text; Err("Clipboard support not enabled".to_string())
1402 }
1403 }
1404
1405 pub fn copy_selection(&self) -> Result<(), String> {
1407 let content = self.value();
1410 self.copy_to_clipboard(&content)
1411 }
1412
1413 pub fn cut_selection(&mut self) -> Result<(), String> {
1415 let content = self.value();
1418 self.copy_to_clipboard(&content)?;
1419 self.reset();
1420 Ok(())
1421 }
1422}
1423
1424impl Default for Model {
1425 fn default() -> Self {
1426 Self::new()
1427 }
1428}
1429
1430impl Component for Model {
1432 fn focus(&mut self) -> Option<Cmd> {
1433 self.focus = true;
1434 self.current_style = self.focused_style.clone();
1435 self.cursor.focus()
1436 }
1437
1438 fn blur(&mut self) {
1439 self.focus = false;
1440 self.current_style = self.blurred_style.clone();
1441 self.cursor.blur();
1442 }
1443
1444 fn focused(&self) -> bool {
1445 self.focus
1446 }
1447}
1448
1449pub fn default_styles() -> (TextareaStyle, TextareaStyle) {
1451 let focused = default_focused_style();
1452 let blurred = default_blurred_style();
1453 (focused, blurred)
1454}
1455
1456pub fn new() -> Model {
1458 Model::new()
1459}