1use compact_str::CompactString;
6
7use crate::cells::{cell_len, get_character_cell_size, is_single_cell_widths, set_cell_size};
8use crate::style::Style;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12#[repr(u8)]
13pub enum ControlType {
14 Bell = 1,
16 CarriageReturn = 2,
18 Home = 3,
20 Clear = 4,
22 ShowCursor = 5,
24 HideCursor = 6,
26 EnableAltScreen = 7,
28 DisableAltScreen = 8,
30 CursorUp = 9,
32 CursorDown = 10,
34 CursorForward = 11,
36 CursorBackward = 12,
38 CursorMoveToColumn = 13,
40 CursorMoveTo = 14,
42 EraseInLine = 15,
44 SetWindowTitle = 16,
46 BeginSync = 17,
48 EndSync = 18,
50 SetClipboard = 19,
52 RequestClipboard = 20,
54}
55
56#[derive(Debug, Clone, PartialEq, Eq, Hash)]
58pub enum ControlCode {
59 Simple(ControlType),
61 WithParam(ControlType, i32),
63 WithParamStr(ControlType, String),
65 WithTwoParams(ControlType, i32, i32),
67}
68
69#[derive(Debug, Clone, PartialEq, Eq)]
71pub struct Segment {
72 pub text: CompactString,
74 pub(crate) style: Option<Style>,
80 pub control: Option<Vec<ControlCode>>,
82}
83
84impl Segment {
85 pub fn new(text: &str, style: Option<Style>, control: Option<Vec<ControlCode>>) -> Self {
98 Segment {
99 text: CompactString::from(text),
100 style,
101 control,
102 }
103 }
104
105 pub fn text(text: &str) -> Self {
117 Segment {
118 text: CompactString::from(text),
119 style: None,
120 control: None,
121 }
122 }
123
124 pub fn line() -> Self {
126 Segment::text("\n")
127 }
128
129 pub fn styled(text: &str, style: Style) -> Self {
142 Segment {
143 text: CompactString::from(text),
144 style: Some(style),
145 control: None,
146 }
147 }
148
149 pub fn cell_length(&self) -> usize {
162 if self.is_control() {
163 0
164 } else {
165 cell_len(&self.text)
166 }
167 }
168
169 pub fn is_control(&self) -> bool {
171 self.control.is_some()
172 }
173
174 #[inline]
180 pub fn style(&self) -> Option<&Style> {
181 self.style.as_ref()
182 }
183
184 #[inline]
188 pub fn style_mut(&mut self) -> &mut Option<Style> {
189 &mut self.style
190 }
191
192 #[inline]
195 pub fn set_style(&mut self, style: Option<Style>) {
196 self.style = style;
197 }
198
199 pub fn style_owned(&self) -> Style {
211 self.style.clone().unwrap_or_else(Style::null)
212 }
213
214 pub fn is_empty(&self) -> bool {
216 self.text.is_empty()
217 }
218
219 pub fn split_cells(&self, cut: usize) -> (Segment, Segment) {
235 let text_len = self.text.len();
236 let cell_length = cell_len(&self.text);
237
238 if cut >= cell_length {
240 return (self.clone(), Segment::new("", self.style.clone(), None));
241 }
242
243 if is_single_cell_widths(&self.text) {
245 let byte_pos = cut.min(text_len);
246 return (
247 Segment::new(&self.text[..byte_pos], self.style.clone(), None),
248 Segment::new(&self.text[byte_pos..], self.style.clone(), None),
249 );
250 }
251
252 let mut cell_pos = 0;
254
255 for (idx, ch) in self.text.char_indices() {
256 let char_width = get_character_cell_size(ch);
257
258 if cell_pos == cut {
259 return (
261 Segment::new(&self.text[..idx], self.style.clone(), None),
262 Segment::new(&self.text[idx..], self.style.clone(), None),
263 );
264 } else if cell_pos + char_width > cut {
265 let before = format!("{} ", &self.text[..idx]);
268 let after = format!(" {}", &self.text[idx + ch.len_utf8()..]);
269 return (
270 Segment::new(&before, self.style.clone(), None),
271 Segment::new(&after, self.style.clone(), None),
272 );
273 }
274
275 cell_pos += char_width;
276 }
277
278 (self.clone(), Segment::new("", self.style.clone(), None))
280 }
281
282 pub fn apply_style(
303 segments: &[Segment],
304 style: Option<Style>,
305 post_style: Option<Style>,
306 ) -> Vec<Segment> {
307 if style.is_none() && post_style.is_none() {
308 return segments.to_vec();
309 }
310
311 segments
312 .iter()
313 .map(|seg| {
314 if seg.is_control() {
315 seg.clone()
316 } else {
317 let mut new_style = seg.style.clone();
318
319 if let Some(ref base) = style {
320 new_style = Some(base.clone() + new_style);
321 }
322
323 if let Some(ref post) = post_style {
324 new_style = Some(new_style.unwrap_or_else(Style::null) + post.clone());
325 }
326
327 Segment::new(&seg.text, new_style, None)
328 }
329 })
330 .collect()
331 }
332
333 pub fn filter_control(segments: &[Segment], is_control: bool) -> Vec<Segment> {
351 segments
352 .iter()
353 .filter(|seg| seg.is_control() == is_control)
354 .cloned()
355 .collect()
356 }
357
358 pub fn split_lines(segments: &[Segment]) -> Vec<Vec<Segment>> {
375 let mut lines = Vec::new();
376 let mut current_line = Vec::new();
377
378 for segment in segments {
379 if segment.is_control() {
380 current_line.push(segment.clone());
381 } else {
382 let parts: Vec<&str> = segment.text.split('\n').collect();
383
384 for (i, part) in parts.iter().enumerate() {
385 if i > 0 {
386 lines.push(current_line);
387 current_line = Vec::new();
388 }
389
390 if !part.is_empty() {
391 current_line.push(Segment::new(part, segment.style.clone(), None));
392 }
393 }
394
395 if segment.text.ends_with('\n') && !parts.is_empty() {
397 lines.push(current_line);
398 current_line = Vec::new();
399 }
400 }
401 }
402
403 if !current_line.is_empty() || lines.is_empty() {
404 lines.push(current_line);
405 }
406
407 lines
408 }
409
410 pub fn adjust_line_length(
426 line: &[Segment],
427 length: usize,
428 style: &Style,
429 pad: bool,
430 ) -> Vec<Segment> {
431 let line_length = Segment::get_line_length(line);
432
433 if line_length == length {
434 return line.to_vec();
435 }
436
437 if line_length < length {
438 if pad {
439 let mut result = line.to_vec();
440 let spaces = " ".repeat(length - line_length);
441 result.push(Segment::styled(&spaces, style.clone()));
442 result
443 } else {
444 line.to_vec()
445 }
446 } else {
447 let mut result = Vec::new();
449 let mut current_length = 0;
450
451 for segment in line {
452 if segment.is_control() {
453 result.push(segment.clone());
454 continue;
455 }
456
457 let segment_length = segment.cell_length();
458
459 if current_length + segment_length <= length {
460 result.push(segment.clone());
461 current_length += segment_length;
462 } else {
463 let remaining = length - current_length;
465 if remaining > 0 {
466 let cropped_text = set_cell_size(&segment.text, remaining);
467 result.push(Segment::new(&cropped_text, segment.style.clone(), None));
468 }
469 break;
470 }
471 }
472
473 result
474 }
475 }
476
477 pub fn get_line_length(line: &[Segment]) -> usize {
490 line.iter()
491 .filter(|seg| !seg.is_control())
492 .map(|seg| seg.cell_length())
493 .sum()
494 }
495
496 pub fn get_shape(lines: &[Vec<Segment>]) -> (usize, usize) {
510 let max_width = lines
511 .iter()
512 .map(|line| Segment::get_line_length(line))
513 .max()
514 .unwrap_or(0);
515 let height = lines.len();
516 (max_width, height)
517 }
518
519 pub fn set_shape(
524 lines: &[Vec<Segment>],
525 width: usize,
526 height: Option<usize>,
527 style: Option<&Style>,
528 _new_lines: bool,
529 ) -> Vec<Vec<Segment>> {
530 let default_style = Style::null();
531 let style = style.unwrap_or(&default_style);
532
533 let mut shaped_lines: Vec<Vec<Segment>> = lines
534 .iter()
535 .map(|line| Segment::adjust_line_length(line, width, style, true))
536 .collect();
537
538 if let Some(target_height) = height {
539 if shaped_lines.len() < target_height {
540 let empty_line = vec![Segment::styled(&" ".repeat(width), style.clone())];
541 while shaped_lines.len() < target_height {
542 shaped_lines.push(empty_line.clone());
543 }
544 } else if shaped_lines.len() > target_height {
545 shaped_lines.truncate(target_height);
546 }
547 }
548
549 shaped_lines
550 }
551
552 pub fn simplify(segments: &[Segment]) -> Vec<Segment> {
572 if segments.is_empty() {
573 return Vec::new();
574 }
575
576 let mut result = Vec::new();
577 let mut current = segments[0].clone();
578
579 for segment in &segments[1..] {
580 if !current.is_control()
581 && !segment.is_control()
582 && current.style == segment.style
583 && current.control == segment.control
584 {
585 current.text.push_str(&segment.text);
586 } else {
587 result.push(current);
588 current = segment.clone();
589 }
590 }
591
592 result.push(current);
593 result
594 }
595
596 pub fn strip_links(segments: &[Segment]) -> Vec<Segment> {
598 segments
599 .iter()
600 .map(|seg| {
601 if let Some(ref style) = seg.style {
602 if style.link().is_some() {
603 let new_style = style.update_link(None);
604 return Segment::new(&seg.text, Some(new_style), seg.control.clone());
605 }
606 }
607 seg.clone()
608 })
609 .collect()
610 }
611
612 pub fn strip_styles(segments: &[Segment]) -> Vec<Segment> {
614 segments
615 .iter()
616 .map(|seg| Segment::new(&seg.text, None, seg.control.clone()))
617 .collect()
618 }
619
620 pub fn remove_color(segments: &[Segment]) -> Vec<Segment> {
623 segments
624 .iter()
625 .map(|seg| {
626 if let Some(ref style) = seg.style {
627 let new_style = style.without_color();
628 Segment::new(&seg.text, Some(new_style), seg.control.clone())
629 } else {
630 seg.clone()
631 }
632 })
633 .collect()
634 }
635
636 pub fn divide(segments: &[Segment], cuts: &[usize]) -> Vec<Vec<Segment>> {
652 if cuts.is_empty() {
653 return Vec::new();
654 }
655
656 if segments.is_empty() {
657 return vec![vec![]; cuts.len()];
658 }
659
660 let mut result = Vec::new();
661 let mut current_portion = Vec::new();
662 let mut cell_position = 0;
663 let mut cut_index = 0;
664
665 let mut remaining_segments: Vec<Segment> = segments.to_vec();
667 let mut seg_idx = 0;
668
669 while cut_index < cuts.len() && seg_idx < remaining_segments.len() {
670 let cut = cuts[cut_index];
671
672 while seg_idx < remaining_segments.len() && cell_position < cut {
673 let segment = &remaining_segments[seg_idx];
674
675 if segment.is_control() {
676 current_portion.push(segment.clone());
677 seg_idx += 1;
678 continue;
679 }
680
681 let segment_length = segment.cell_length();
682 let segment_end = cell_position + segment_length;
683
684 if segment_end <= cut {
685 current_portion.push(segment.clone());
687 cell_position = segment_end;
688 seg_idx += 1;
689 } else {
690 let offset = cut - cell_position;
692 let (before, after) = segment.split_cells(offset);
693
694 if !before.is_empty() {
695 current_portion.push(before);
696 }
697
698 if !after.is_empty() {
700 remaining_segments[seg_idx] = after;
701 } else {
702 seg_idx += 1;
703 }
704
705 cell_position = cut;
706 break;
707 }
708 }
709
710 result.push(current_portion);
711 current_portion = Vec::new();
712 cut_index += 1;
713 }
714
715 result
716 }
717
718 pub fn align_top(
720 lines: &[Vec<Segment>],
721 width: usize,
722 height: usize,
723 style: &Style,
724 new_lines: bool,
725 ) -> Vec<Vec<Segment>> {
726 Segment::set_shape(lines, width, Some(height), Some(style), new_lines)
727 }
728
729 pub fn align_bottom(
731 lines: &[Vec<Segment>],
732 width: usize,
733 height: usize,
734 style: &Style,
735 new_lines: bool,
736 ) -> Vec<Vec<Segment>> {
737 let mut shaped = Segment::set_shape(lines, width, Some(height), Some(style), new_lines);
738
739 if lines.len() < height {
740 let padding = height - lines.len();
741 let empty_line = vec![Segment::styled(&" ".repeat(width), style.clone())];
742 let mut padding_lines = vec![empty_line; padding];
743 padding_lines.extend(
744 lines
745 .iter()
746 .map(|line| Segment::adjust_line_length(line, width, style, true)),
747 );
748 shaped = padding_lines;
749 }
750
751 shaped
752 }
753
754 pub fn align_middle(
756 lines: &[Vec<Segment>],
757 width: usize,
758 height: usize,
759 style: &Style,
760 new_lines: bool,
761 ) -> Vec<Vec<Segment>> {
762 if lines.len() >= height {
763 return Segment::set_shape(lines, width, Some(height), Some(style), new_lines);
764 }
765
766 let padding = height - lines.len();
767 let top_padding = padding / 2;
768 let bottom_padding = padding - top_padding;
769
770 let empty_line = vec![Segment::styled(&" ".repeat(width), style.clone())];
771 let mut result = vec![empty_line.clone(); top_padding];
772
773 for line in lines {
774 result.push(Segment::adjust_line_length(line, width, style, true));
775 }
776
777 for _ in 0..bottom_padding {
778 result.push(empty_line.clone());
779 }
780
781 result
782 }
783
784 pub fn split_and_crop_lines(
799 segments: &[Segment],
800 length: usize,
801 style: Option<&Style>,
802 pad: bool,
803 include_new_lines: bool,
804 ) -> Vec<Vec<Segment>> {
805 let mut result = Vec::new();
806 let mut line: Vec<Segment> = Vec::new();
807
808 for segment in segments {
809 if segment.text.contains('\n') && segment.control.is_none() {
810 let seg_style = segment.style.clone();
811 let mut remaining = segment.text.as_str();
812 while !remaining.is_empty() {
813 if let Some(pos) = remaining.find('\n') {
814 let before = &remaining[..pos];
815 if !before.is_empty() {
816 line.push(Segment::new(before, seg_style.clone(), None));
817 }
818 let mut cropped = Segment::adjust_line_length(
819 &line,
820 length,
821 &style.cloned().unwrap_or_else(Style::null),
822 pad,
823 );
824 if include_new_lines {
825 cropped.push(Segment::line());
826 }
827 result.push(cropped);
828 line.clear();
829 remaining = &remaining[pos + 1..];
830 } else {
831 if !remaining.is_empty() {
832 line.push(Segment::new(remaining, seg_style.clone(), None));
833 }
834 break;
835 }
836 }
837 } else {
838 line.push(segment.clone());
839 }
840 }
841 if !line.is_empty() {
842 let cropped = Segment::adjust_line_length(
843 &line,
844 length,
845 &style.cloned().unwrap_or_else(Style::null),
846 pad,
847 );
848 result.push(cropped);
849 }
850 result
851 }
852
853 pub fn split_lines_terminator(segments: &[Segment]) -> Vec<(Vec<Segment>, bool)> {
869 let mut result = Vec::new();
870 let mut line: Vec<Segment> = Vec::new();
871
872 for segment in segments {
873 if segment.text.contains('\n') && segment.control.is_none() {
874 let seg_style = segment.style.clone();
875 let mut remaining = segment.text.as_str();
876 while !remaining.is_empty() {
877 if let Some(pos) = remaining.find('\n') {
878 let before = &remaining[..pos];
879 if !before.is_empty() {
880 line.push(Segment::new(before, seg_style.clone(), None));
881 }
882 result.push((std::mem::take(&mut line), true));
883 remaining = &remaining[pos + 1..];
884 } else {
885 if !remaining.is_empty() {
886 line.push(Segment::new(remaining, seg_style.clone(), None));
887 }
888 break;
889 }
890 }
891 } else {
892 line.push(segment.clone());
893 }
894 }
895 if !line.is_empty() {
896 result.push((line, false));
897 }
898 result
899 }
900}
901
902#[cfg(test)]
903mod tests {
904 use super::*;
905
906 #[test]
907 fn test_line() {
908 assert_eq!(Segment::line(), Segment::text("\n"));
909 }
910
911 #[test]
912 fn test_apply_style() {
913 let segments = vec![
914 Segment::text("foo"),
915 Segment::styled("bar", Style::parse("bold")),
916 ];
917 let result = Segment::apply_style(&segments, Some(Style::parse("italic")), None);
918 assert_eq!(
919 result,
920 vec![
921 Segment::styled("foo", Style::parse("italic")),
922 Segment::styled("bar", Style::parse("italic bold")),
923 ]
924 );
925 }
926
927 #[test]
928 fn test_split_lines() {
929 let lines = vec![Segment::text("Hello\nWorld")];
930 let result = Segment::split_lines(&lines);
931 assert_eq!(
932 result,
933 vec![vec![Segment::text("Hello")], vec![Segment::text("World")]]
934 );
935 }
936
937 #[test]
938 fn test_adjust_line_length_pad() {
939 let line = vec![Segment::text("Hello")];
940 let style = Style::parse("red");
941 let result = Segment::adjust_line_length(&line, 10, &style, true);
942 assert_eq!(Segment::get_line_length(&result), 10);
943 }
944
945 #[test]
946 fn test_adjust_line_length_crop() {
947 let line = vec![Segment::text("H"), Segment::text("ello, World!")];
948 let result = Segment::adjust_line_length(&line, 5, &Style::null(), true);
949 assert_eq!(Segment::get_line_length(&result), 5);
950 }
951
952 #[test]
953 fn test_get_line_length() {
954 assert_eq!(
955 Segment::get_line_length(&[Segment::text("foo"), Segment::text("bar")]),
956 6
957 );
958 }
959
960 #[test]
961 fn test_get_shape() {
962 assert_eq!(Segment::get_shape(&[vec![Segment::text("Hello")]]), (5, 1));
963 assert_eq!(
964 Segment::get_shape(&[vec![Segment::text("Hello")], vec![Segment::text("World!")]]),
965 (6, 2)
966 );
967 }
968
969 #[test]
970 fn test_simplify() {
971 let segments = vec![
972 Segment::text("Hello"),
973 Segment::text(" "),
974 Segment::text("World!"),
975 ];
976 assert_eq!(
977 Segment::simplify(&segments),
978 vec![Segment::text("Hello World!")]
979 );
980 }
981
982 #[test]
983 fn test_filter_control() {
984 let control_code = vec![ControlCode::WithParam(ControlType::Home, 0)];
985 let segments = vec![
986 Segment::text("foo"),
987 Segment::new("bar", None, Some(control_code.clone())),
988 ];
989 assert_eq!(
990 Segment::filter_control(&segments, false),
991 vec![Segment::text("foo")]
992 );
993 }
994
995 #[test]
996 fn test_strip_styles() {
997 let segments = vec![Segment::styled("foo", Style::parse("bold"))];
998 assert_eq!(Segment::strip_styles(&segments), vec![Segment::text("foo")]);
999 }
1000
1001 #[test]
1002 fn test_strip_links() {
1003 let segments = vec![Segment::styled(
1004 "foo",
1005 Style::parse("bold link https://www.example.org"),
1006 )];
1007 let result = Segment::strip_links(&segments);
1008 assert_eq!(result[0].style.as_ref().unwrap().link(), None);
1009 assert_eq!(result[0].style.as_ref().unwrap().bold(), Some(true));
1010 }
1011
1012 #[test]
1013 fn test_remove_color() {
1014 let segments = vec![
1015 Segment::styled("foo", Style::parse("bold red")),
1016 Segment::text("bar"),
1017 ];
1018 let result = Segment::remove_color(&segments);
1019 assert_eq!(result[0].style.as_ref().unwrap().color(), None);
1020 assert_eq!(result[0].style.as_ref().unwrap().bold(), Some(true));
1021 }
1022
1023 #[test]
1024 fn test_is_control() {
1025 assert!(!Segment::text("foo").is_control());
1026 assert!(Segment::new("foo", None, Some(vec![])).is_control());
1027 }
1028
1029 #[test]
1030 fn test_divide() {
1031 let bold = Style::parse("bold");
1032 let italic = Style::parse("italic");
1033 let segments = vec![
1034 Segment::styled("Hello", bold.clone()),
1035 Segment::styled(" World!", italic.clone()),
1036 ];
1037 assert_eq!(Segment::divide(&segments, &[]), Vec::<Vec<Segment>>::new());
1038 assert_eq!(Segment::divide(&[], &[1]), vec![vec![]]);
1039 assert_eq!(
1040 Segment::divide(&segments, &[1]),
1041 vec![vec![Segment::styled("H", bold.clone())]]
1042 );
1043 assert_eq!(
1044 Segment::divide(&segments, &[4, 20]),
1045 vec![
1046 vec![Segment::styled("Hell", bold.clone())],
1047 vec![
1048 Segment::styled("o", bold.clone()),
1049 Segment::styled(" World!", italic.clone())
1050 ],
1051 ]
1052 );
1053 }
1054
1055 #[test]
1056 fn test_split_cells_emoji() {
1057 let segment = Segment::text("💩");
1058 let (before, after) = segment.split_cells(1);
1059 assert_eq!(before.text, " ");
1060 assert_eq!(after.text, " ");
1061 }
1062
1063 #[test]
1064 fn test_split_cells_ascii() {
1065 let segment = Segment::text("XY");
1066 let (before, after) = segment.split_cells(1);
1067 assert_eq!(before.text, "X");
1068 assert_eq!(after.text, "Y");
1069 }
1070
1071 #[test]
1072 fn test_split_cells_mixed() {
1073 let segment = Segment::text("X💩Y");
1074 let (before, after) = segment.split_cells(2);
1075 assert_eq!(before.text, "X ");
1076 assert_eq!(after.text, " Y");
1077 }
1078
1079 #[test]
1080 fn test_align_top() {
1081 let lines = vec![vec![Segment::text("X")]];
1082 assert_eq!(
1083 Segment::align_top(&lines, 3, 1, &Style::null(), false),
1084 Segment::set_shape(&lines, 3, Some(1), Some(&Style::null()), false)
1085 );
1086 assert_eq!(
1087 Segment::align_top(&lines, 3, 3, &Style::null(), false).len(),
1088 3
1089 );
1090 }
1091
1092 #[test]
1093 fn test_align_middle() {
1094 let lines = vec![vec![Segment::text("X")]];
1095 let result = Segment::align_middle(&lines, 5, 3, &Style::null(), false);
1096 assert_eq!(result.len(), 3);
1097 assert_eq!(Segment::get_line_length(&result[0]), 5); assert_eq!(Segment::get_line_length(&result[1]), 5); assert_eq!(Segment::get_line_length(&result[2]), 5); }
1102
1103 #[test]
1104 fn test_align_bottom() {
1105 let lines = vec![vec![Segment::text("X")]];
1106 let result = Segment::align_bottom(&lines, 5, 3, &Style::null(), false);
1107 assert_eq!(result.len(), 3);
1108 assert_eq!(Segment::get_line_length(&result[0]), 5); assert_eq!(Segment::get_line_length(&result[1]), 5); assert_eq!(Segment::get_line_length(&result[2]), 5); }
1113
1114 #[test]
1115 fn test_set_shape() {
1116 let result = Segment::set_shape(&[vec![Segment::text("Hello")]], 10, None, None, false);
1117 assert_eq!(Segment::get_line_length(&result[0]), 10);
1118 }
1119
1120 #[test]
1121 fn test_cell_length() {
1122 assert_eq!(Segment::text("abc").cell_length(), 3);
1123 assert_eq!(Segment::text("💩").cell_length(), 2);
1124 assert_eq!(
1125 Segment::new(
1126 "abc",
1127 None,
1128 Some(vec![ControlCode::Simple(ControlType::Bell)])
1129 )
1130 .cell_length(),
1131 0
1132 );
1133 }
1134
1135 #[test]
1136 fn test_split_lines_multiple_newlines() {
1137 let segments = vec![Segment::text("Hello\n\nWorld")];
1138 let result = Segment::split_lines(&segments);
1139 assert_eq!(result.len(), 3);
1140 assert_eq!(result[0], vec![Segment::text("Hello")]);
1141 assert_eq!(result[1], Vec::<Segment>::new());
1142 assert_eq!(result[2], vec![Segment::text("World")]);
1143 }
1144
1145 #[test]
1146 fn test_split_lines_trailing_newline() {
1147 let segments = vec![Segment::text("Hello\n")];
1148 let result = Segment::split_lines(&segments);
1149 assert_eq!(result.len(), 2);
1150 assert_eq!(result[0], vec![Segment::text("Hello")]);
1151 assert_eq!(result[1], Vec::<Segment>::new());
1152 }
1153
1154 #[test]
1155 fn test_simplify_different_styles() {
1156 let segments = vec![
1157 Segment::styled("Hello", Style::parse("bold")),
1158 Segment::styled("World", Style::parse("italic")),
1159 ];
1160 let result = Segment::simplify(&segments);
1161 assert_eq!(result.len(), 2); }
1163
1164 #[test]
1165 fn test_simplify_with_control() {
1166 let segments = vec![
1167 Segment::text("Hello"),
1168 Segment::new("", None, Some(vec![ControlCode::Simple(ControlType::Bell)])),
1169 Segment::text("World"),
1170 ];
1171 let result = Segment::simplify(&segments);
1172 assert_eq!(result.len(), 3); }
1174
1175 #[test]
1176 fn test_divide_empty_segments() {
1177 let result = Segment::divide(&[], &[1, 2, 3]);
1178 assert_eq!(result.len(), 3);
1179 assert!(result[0].is_empty());
1180 assert!(result[1].is_empty());
1181 assert!(result[2].is_empty());
1182 }
1183
1184 #[test]
1185 fn test_split_cells_beyond_length() {
1186 let segment = Segment::text("Hello");
1187 let (before, after) = segment.split_cells(10);
1188 assert_eq!(before.text, "Hello");
1189 assert_eq!(after.text, "");
1190 }
1191
1192 #[test]
1193 fn test_split_cells_cjk() {
1194 let segment = Segment::text("あいう"); let (before, after) = segment.split_cells(2);
1196 assert_eq!(before.text, "あ");
1197 assert_eq!(after.text, "いう");
1198
1199 let (before, after) = segment.split_cells(3);
1200 assert_eq!(before.text, "あ ");
1202 assert_eq!(after.text, " う");
1203 }
1204
1205 #[test]
1206 fn test_apply_style_with_control_segments() {
1207 let control_code = vec![ControlCode::Simple(ControlType::Bell)];
1208 let segments = vec![
1209 Segment::text("foo"),
1210 Segment::new("", None, Some(control_code.clone())),
1211 Segment::text("bar"),
1212 ];
1213 let result = Segment::apply_style(&segments, Some(Style::parse("bold")), None);
1214
1215 assert_eq!(result[0].style.as_ref().unwrap().bold(), Some(true));
1216 assert!(result[1].is_control());
1217 assert_eq!(result[1].style, None);
1218 assert_eq!(result[2].style.as_ref().unwrap().bold(), Some(true));
1219 }
1220
1221 #[test]
1222 fn test_apply_style_post_style() {
1223 let segments = vec![Segment::styled("foo", Style::parse("bold"))];
1224 let result = Segment::apply_style(&segments, None, Some(Style::parse("italic")));
1225 assert_eq!(result[0].style.as_ref().unwrap().bold(), Some(true));
1226 assert_eq!(result[0].style.as_ref().unwrap().italic(), Some(true));
1227 }
1228
1229 #[test]
1230 fn test_get_shape_empty() {
1231 assert_eq!(Segment::get_shape(&[]), (0, 0));
1232 assert_eq!(Segment::get_shape(&[vec![]]), (0, 1));
1233 }
1234
1235 #[test]
1236 fn test_adjust_line_length_exact() {
1237 let line = vec![Segment::text("Hello")];
1238 let result = Segment::adjust_line_length(&line, 5, &Style::null(), true);
1239 assert_eq!(result, line);
1240 }
1241
1242 #[test]
1243 fn test_adjust_line_length_no_pad() {
1244 let line = vec![Segment::text("Hi")];
1245 let result = Segment::adjust_line_length(&line, 10, &Style::null(), false);
1246 assert_eq!(Segment::get_line_length(&result), 2); }
1248
1249 #[test]
1250 fn test_divide_with_control_segments() {
1251 let control_code = vec![ControlCode::Simple(ControlType::Bell)];
1252 let segments = vec![
1253 Segment::text("Hello"),
1254 Segment::new("", None, Some(control_code.clone())),
1255 Segment::text("World"),
1256 ];
1257 let result = Segment::divide(&segments, &[5, 10]);
1258 assert_eq!(result.len(), 2);
1259 assert_eq!(result[0].len(), 1);
1261 assert_eq!(result[0][0].text, "Hello");
1262 }
1263
1264 #[test]
1265 fn test_split_cells_zero_cut() {
1266 let segment = Segment::text("Hello");
1267 let (before, after) = segment.split_cells(0);
1268 assert_eq!(before.text, "");
1269 assert_eq!(after.text, "Hello");
1270 }
1271
1272 #[test]
1273 fn test_align_methods_preserve_content() {
1274 let lines = vec![vec![Segment::text("ABC")]];
1275 let width = 5;
1276 let height = 3;
1277
1278 let top = Segment::align_top(&lines, width, height, &Style::null(), false);
1279 let middle = Segment::align_middle(&lines, width, height, &Style::null(), false);
1280 let bottom = Segment::align_bottom(&lines, width, height, &Style::null(), false);
1281
1282 assert_eq!(top.len(), height);
1284 assert_eq!(middle.len(), height);
1285 assert_eq!(bottom.len(), height);
1286
1287 assert!(top
1289 .iter()
1290 .any(|line| { line.iter().any(|seg| seg.text.contains("ABC")) }));
1291 assert!(middle
1292 .iter()
1293 .any(|line| { line.iter().any(|seg| seg.text.contains("ABC")) }));
1294 assert!(bottom
1295 .iter()
1296 .any(|line| { line.iter().any(|seg| seg.text.contains("ABC")) }));
1297 }
1298
1299 #[test]
1300 fn test_cell_length_with_mixed_content() {
1301 assert_eq!(Segment::text("a💩b").cell_length(), 4); assert_eq!(Segment::text("あa").cell_length(), 3); }
1304
1305 #[test]
1306 fn test_simplify_empty_segments() {
1307 let segments = vec![Segment::text(""), Segment::text("Hello"), Segment::text("")];
1308 let result = Segment::simplify(&segments);
1309 assert_eq!(result.len(), 1);
1310 assert_eq!(result[0].text, "Hello");
1311 }
1312
1313 #[test]
1314 fn test_apply_style_none_params() {
1315 let segments = vec![Segment::text("foo")];
1316 let result = Segment::apply_style(&segments, None, None);
1317 assert_eq!(result, segments);
1318 }
1319
1320 #[test]
1321 fn test_set_shape_with_height() {
1322 let lines = vec![vec![Segment::text("A")], vec![Segment::text("B")]];
1323 let result = Segment::set_shape(&lines, 3, Some(4), Some(&Style::null()), false);
1324 assert_eq!(result.len(), 4);
1325 assert_eq!(Segment::get_line_length(&result[0]), 3);
1326 assert_eq!(Segment::get_line_length(&result[3]), 3);
1327 }
1328
1329 #[test]
1330 fn test_set_shape_truncate() {
1331 let lines = vec![
1332 vec![Segment::text("A")],
1333 vec![Segment::text("B")],
1334 vec![Segment::text("C")],
1335 ];
1336 let result = Segment::set_shape(&lines, 3, Some(2), Some(&Style::null()), false);
1337 assert_eq!(result.len(), 2);
1338 }
1339
1340 #[test]
1341 fn test_split_lines_with_styled_segments() {
1342 let bold = Style::parse("bold");
1343 let segments = vec![Segment::styled("Hello\nWorld", bold.clone())];
1344 let result = Segment::split_lines(&segments);
1345 assert_eq!(result.len(), 2);
1346 assert_eq!(result[0][0].text, "Hello");
1347 assert_eq!(result[1][0].text, "World");
1348 assert_eq!(result[0][0].style.as_ref().unwrap().bold(), Some(true));
1350 assert_eq!(result[1][0].style.as_ref().unwrap().bold(), Some(true));
1351 }
1352
1353 #[test]
1354 fn test_divide_exact_boundaries() {
1355 let segments = vec![Segment::text("ABCDE")];
1356 let result = Segment::divide(&segments, &[2, 4]);
1357 assert_eq!(result.len(), 2);
1358 assert_eq!(result[0][0].text, "AB");
1359 assert_eq!(result[1][0].text, "CD");
1360 }
1361
1362 #[test]
1363 fn test_is_empty() {
1364 assert!(Segment::text("").is_empty());
1365 assert!(!Segment::text("a").is_empty());
1366 }
1367
1368 #[test]
1369 fn test_control_types_coverage() {
1370 let _ = ControlCode::Simple(ControlType::Bell);
1372 let _ = ControlCode::WithParam(ControlType::CursorUp, 5);
1373 let _ = ControlCode::WithParamStr(ControlType::SetWindowTitle, "Test".to_string());
1374 let _ = ControlCode::WithTwoParams(ControlType::CursorMoveTo, 10, 20);
1375
1376 assert_ne!(ControlType::Bell as u8, ControlType::Home as u8);
1378 assert_ne!(ControlType::ShowCursor as u8, ControlType::HideCursor as u8);
1379 }
1380
1381 #[test]
1382 fn test_split_and_crop_lines_basic() {
1383 let segments = vec![Segment::text("Hello\nWorld")];
1384 let lines = Segment::split_and_crop_lines(&segments, 10, None, true, false);
1385 assert_eq!(lines.len(), 2);
1386 let line0_text: String = lines[0].iter().map(|s| s.text.as_str()).collect();
1388 assert_eq!(line0_text.trim_end(), "Hello");
1389 assert_eq!(lines[0].iter().map(|s| s.cell_length()).sum::<usize>(), 10);
1390 }
1391
1392 #[test]
1393 fn test_split_and_crop_lines_no_pad() {
1394 let segments = vec![Segment::text("Hi\nWorld")];
1395 let lines = Segment::split_and_crop_lines(&segments, 10, None, false, false);
1396 assert_eq!(lines.len(), 2);
1397 let line0_text: String = lines[0].iter().map(|s| s.text.as_str()).collect();
1398 assert_eq!(line0_text, "Hi");
1399 }
1400
1401 #[test]
1402 fn test_split_and_crop_lines_with_newline_segments() {
1403 let segments = vec![Segment::text("Hello\nWorld")];
1404 let lines = Segment::split_and_crop_lines(&segments, 10, None, false, true);
1405 assert_eq!(lines.len(), 2);
1406 assert_eq!(lines[0].last().unwrap().text, "\n");
1408 }
1409
1410 #[test]
1411 fn test_split_and_crop_lines_crop() {
1412 let segments = vec![Segment::text("Hello, World!")];
1413 let lines = Segment::split_and_crop_lines(&segments, 5, None, false, false);
1414 assert_eq!(lines.len(), 1);
1415 let line_text: String = lines[0].iter().map(|s| s.text.as_str()).collect();
1416 assert_eq!(line_text, "Hello");
1417 }
1418
1419 #[test]
1420 fn test_split_lines_terminator_basic() {
1421 let segments = vec![Segment::text("Hello\nWorld")];
1422 let lines = Segment::split_lines_terminator(&segments);
1423 assert_eq!(lines.len(), 2);
1424 assert!(lines[0].1); assert!(!lines[1].1); let text0: String = lines[0].0.iter().map(|s| s.text.as_str()).collect();
1427 assert_eq!(text0, "Hello");
1428 }
1429
1430 #[test]
1431 fn test_split_lines_terminator_no_newline() {
1432 let segments = vec![Segment::text("Hello")];
1433 let lines = Segment::split_lines_terminator(&segments);
1434 assert_eq!(lines.len(), 1);
1435 assert!(!lines[0].1);
1436 }
1437
1438 #[test]
1439 fn test_split_lines_terminator_trailing_newline() {
1440 let segments = vec![Segment::text("Hello\n")];
1441 let lines = Segment::split_lines_terminator(&segments);
1442 assert_eq!(lines.len(), 1);
1443 assert!(lines[0].1);
1444 }
1445}