thoth_cli/
code_block_popup.rs1#[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}