1use super::line::Line;
2
3#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
5pub struct Cursor {
6 pub row: usize,
7 pub col: usize,
8 pub is_visible: bool,
9}
10
11impl Cursor {
12 pub fn hidden() -> Self {
14 Self::default()
15 }
16
17 pub fn visible(row: usize, col: usize) -> Self {
19 Self {
20 row,
21 col,
22 is_visible: true,
23 }
24 }
25}
26
27#[derive(Debug, Clone, PartialEq, Eq)]
29pub struct Frame {
30 lines: Vec<Line>,
31 cursor: Cursor,
32}
33
34impl Frame {
35 pub fn new(lines: Vec<Line>) -> Self {
36 Self {
37 lines,
38 cursor: Cursor::hidden(),
39 }
40 }
41
42 pub fn lines(&self) -> &[Line] {
43 &self.lines
44 }
45
46 pub fn cursor(&self) -> Cursor {
47 self.cursor
48 }
49
50 pub fn with_cursor(mut self, cursor: Cursor) -> Self {
52 self.cursor = cursor;
53 self
54 }
55
56 pub fn into_lines(self) -> Vec<Line> {
57 self.lines
58 }
59
60 pub fn into_parts(self) -> (Vec<Line>, Cursor) {
61 (self.lines, self.cursor)
62 }
63
64 pub fn clamp_cursor(mut self) -> Self {
65 if self.cursor.row >= self.lines.len() {
66 self.cursor.row = self.lines.len().saturating_sub(1);
67 }
68 self
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 #[test]
77 fn cursor_hidden_returns_invisible_cursor_at_origin() {
78 let cursor = Cursor::hidden();
79 assert_eq!(cursor.row, 0);
80 assert_eq!(cursor.col, 0);
81 assert!(!cursor.is_visible);
82 }
83
84 #[test]
85 fn cursor_visible_returns_visible_cursor_at_position() {
86 let cursor = Cursor::visible(5, 10);
87 assert_eq!(cursor.row, 5);
88 assert_eq!(cursor.col, 10);
89 assert!(cursor.is_visible);
90 }
91
92 #[test]
93 fn clamp_cursor_clamps_out_of_bounds_row() {
94 let frame = Frame::new(vec![Line::new("a")]).with_cursor(Cursor::visible(10, 100));
95 let frame = frame.clamp_cursor();
96 assert_eq!(frame.cursor().row, 0);
97 assert_eq!(frame.cursor().col, 100);
98 }
99
100 #[test]
101 fn with_cursor_replaces_cursor_without_cloning_lines() {
102 let frame = Frame::new(vec![Line::new("hello")]);
103 let new_cursor = Cursor::visible(0, 3);
104 let frame = frame.with_cursor(new_cursor);
105 assert_eq!(frame.cursor(), new_cursor);
106 assert_eq!(frame.lines()[0].plain_text(), "hello");
107 }
108}