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