thoth_cli/
code_block_popup.rs

1#[derive(Clone)]
2pub struct CodeBlockPopup {
3    pub code_blocks: Vec<CodeBlock>,
4    pub filtered_blocks: Vec<CodeBlock>,
5    pub selected_index: usize,
6    pub visible: bool,
7    pub scroll_offset: usize,
8}
9
10#[derive(Clone)]
11pub struct CodeBlock {
12    pub content: String,
13    pub language: String,
14    pub start_line: usize,
15    pub end_line: usize,
16}
17
18impl CodeBlock {
19    pub fn new(content: String, language: String, start_line: usize, end_line: usize) -> Self {
20        Self {
21            content,
22            language,
23            start_line,
24            end_line,
25        }
26    }
27}
28
29impl Default for CodeBlockPopup {
30    fn default() -> Self {
31        Self::new()
32    }
33}
34
35impl CodeBlockPopup {
36    pub fn new() -> Self {
37        CodeBlockPopup {
38            code_blocks: Vec::new(),
39            filtered_blocks: Vec::new(),
40            selected_index: 0,
41            visible: false,
42            scroll_offset: 0,
43        }
44    }
45
46    pub fn set_code_blocks(&mut self, code_blocks: Vec<CodeBlock>) {
47        self.code_blocks = code_blocks;
48        self.filtered_blocks = self.code_blocks.clone();
49        self.selected_index = 0;
50        self.scroll_offset = 0;
51    }
52
53    pub fn move_selection_up(&mut self, visible_items: usize) {
54        if self.filtered_blocks.is_empty() {
55            return;
56        }
57
58        if self.selected_index > 0 {
59            self.selected_index -= 1;
60        } else {
61            self.selected_index = self.filtered_blocks.len() - 1;
62        }
63
64        if self.selected_index <= self.scroll_offset {
65            self.scroll_offset = self.selected_index;
66        }
67        if self.selected_index == self.filtered_blocks.len() - 1 {
68            self.scroll_offset = self.filtered_blocks.len().saturating_sub(visible_items);
69        }
70    }
71
72    pub fn move_selection_down(&mut self, visible_items: usize) {
73        if self.filtered_blocks.is_empty() {
74            return;
75        }
76
77        if self.selected_index < self.filtered_blocks.len() - 1 {
78            self.selected_index += 1;
79        } else {
80            self.selected_index = 0;
81            self.scroll_offset = 0;
82        }
83
84        let max_scroll = self.filtered_blocks.len().saturating_sub(visible_items);
85        if self.selected_index >= self.scroll_offset + visible_items {
86            self.scroll_offset = (self.selected_index + 1).saturating_sub(visible_items);
87            if self.scroll_offset > max_scroll {
88                self.scroll_offset = max_scroll;
89            }
90        }
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97
98    #[test]
99    fn test_new_code_block_popup() {
100        let popup = CodeBlockPopup::new();
101        assert!(popup.code_blocks.is_empty());
102        assert_eq!(popup.selected_index, 0);
103        assert!(!popup.visible);
104    }
105
106    #[test]
107    fn test_set_code_blocks() {
108        let mut popup = CodeBlockPopup::new();
109        let blocks = vec![
110            CodeBlock::new("fn main() {}".to_string(), "rust".to_string(), 0, 2),
111            CodeBlock::new("def hello(): pass".to_string(), "python".to_string(), 3, 5),
112        ];
113        popup.set_code_blocks(blocks);
114        assert_eq!(popup.code_blocks.len(), 2);
115        assert_eq!(popup.code_blocks[0].language, "rust");
116        assert_eq!(popup.code_blocks[1].language, "python");
117    }
118
119    #[test]
120    fn test_move_selection() {
121        let mut popup = CodeBlockPopup::new();
122        let blocks = vec![
123            CodeBlock::new("Block 1".to_string(), "rust".to_string(), 0, 2),
124            CodeBlock::new("Block 2".to_string(), "python".to_string(), 3, 5),
125            CodeBlock::new("Block 3".to_string(), "js".to_string(), 6, 8),
126        ];
127        popup.set_code_blocks(blocks);
128
129        popup.move_selection_down(2);
130        assert_eq!(popup.selected_index, 1);
131
132        popup.move_selection_up(2);
133        assert_eq!(popup.selected_index, 0);
134
135        popup.move_selection_up(2);
136        assert_eq!(popup.selected_index, 2);
137
138        popup.move_selection_down(2);
139        assert_eq!(popup.selected_index, 0);
140    }
141}