1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum Intent {
6 Generate,
8 Explain,
10 Fix,
12 Refactor,
14 Test,
16 Document,
18 Execute,
20 Help,
22 Chat,
24}
25
26pub struct InputAnalyzer;
28
29impl InputAnalyzer {
30 pub fn detect_intent(input: &str) -> Intent {
32 let lower = input.to_lowercase();
33
34 if lower.contains("generate") || lower.contains("create") || lower.contains("write") {
35 Intent::Generate
36 } else if lower.contains("explain")
37 || lower.contains("what is")
38 || lower.contains("how does")
39 {
40 Intent::Explain
41 } else if lower.contains("fix") || lower.contains("bug") || lower.contains("error") {
42 Intent::Fix
43 } else if lower.contains("refactor")
44 || lower.contains("improve")
45 || lower.contains("optimize")
46 {
47 Intent::Refactor
48 } else if lower.contains("test") || lower.contains("unit test") {
49 Intent::Test
50 } else if lower.contains("document") || lower.contains("comment") {
51 Intent::Document
52 } else if lower.contains("execute") || lower.contains("run") || lower.contains("command") {
53 Intent::Execute
54 } else if lower.contains("help") || lower.contains("?") {
55 Intent::Help
56 } else {
57 Intent::Chat
58 }
59 }
60
61 pub fn suggest_commands(intent: Intent) -> Vec<&'static str> {
63 match intent {
64 Intent::Generate => vec!["generate", "create", "scaffold"],
65 Intent::Explain => vec!["explain", "describe", "clarify"],
66 Intent::Fix => vec!["fix", "debug", "resolve"],
67 Intent::Refactor => vec!["refactor", "improve", "optimize"],
68 Intent::Test => vec!["test", "unit-test", "validate"],
69 Intent::Document => vec!["document", "comment", "annotate"],
70 Intent::Execute => vec!["execute", "run", "apply"],
71 Intent::Help => vec!["help", "guide", "tutorial"],
72 Intent::Chat => vec!["chat", "discuss", "ask"],
73 }
74 }
75
76 pub fn validate_input(input: &str) -> Result<(), String> {
78 if input.trim().is_empty() {
79 return Err("Input cannot be empty".to_string());
80 }
81
82 if input.len() > 10000 {
83 return Err("Input is too long (max 10000 characters)".to_string());
84 }
85
86 Ok(())
87 }
88}
89
90pub struct ChatInputWidget {
92 pub text: String,
94 pub cursor: usize,
96 pub history: Vec<String>,
98 pub history_index: Option<usize>,
100 pub intent: Intent,
102}
103
104impl ChatInputWidget {
105 pub fn new() -> Self {
107 Self {
108 text: String::new(),
109 cursor: 0,
110 history: Vec::new(),
111 history_index: None,
112 intent: Intent::Chat,
113 }
114 }
115
116 pub fn insert_char(&mut self, ch: char) {
118 self.text.insert(self.cursor, ch);
119 self.cursor += 1;
120 self.update_intent();
121 }
122
123 pub fn backspace(&mut self) {
125 if self.cursor > 0 {
126 self.text.remove(self.cursor - 1);
127 self.cursor -= 1;
128 self.update_intent();
129 }
130 }
131
132 pub fn delete(&mut self) {
134 if self.cursor < self.text.len() {
135 self.text.remove(self.cursor);
136 self.update_intent();
137 }
138 }
139
140 pub fn move_left(&mut self) {
142 if self.cursor > 0 {
143 self.cursor -= 1;
144 }
145 }
146
147 pub fn move_right(&mut self) {
149 if self.cursor < self.text.len() {
150 self.cursor += 1;
151 }
152 }
153
154 pub fn move_start(&mut self) {
156 self.cursor = 0;
157 }
158
159 pub fn move_end(&mut self) {
161 self.cursor = self.text.len();
162 }
163
164 pub fn submit(&mut self) -> String {
166 let input = self.text.clone();
167 self.history.push(input.clone());
168 self.text.clear();
169 self.cursor = 0;
170 self.history_index = None;
171 self.intent = Intent::Chat;
172 input
173 }
174
175 pub fn history_up(&mut self) {
177 if self.history.is_empty() {
178 return;
179 }
180
181 match self.history_index {
182 None => {
183 self.history_index = Some(self.history.len() - 1);
184 self.text = self.history[self.history.len() - 1].clone();
185 }
186 Some(idx) if idx > 0 => {
187 self.history_index = Some(idx - 1);
188 self.text = self.history[idx - 1].clone();
189 }
190 _ => {}
191 }
192
193 self.cursor = self.text.len();
194 }
195
196 pub fn history_down(&mut self) {
198 match self.history_index {
199 Some(idx) if idx < self.history.len() - 1 => {
200 self.history_index = Some(idx + 1);
201 self.text = self.history[idx + 1].clone();
202 self.cursor = self.text.len();
203 }
204 Some(_) => {
205 self.history_index = None;
206 self.text.clear();
207 self.cursor = 0;
208 }
209 None => {}
210 }
211 }
212
213 pub fn update_intent(&mut self) {
215 self.intent = InputAnalyzer::detect_intent(&self.text);
216 }
217
218 pub fn suggestions(&self) -> Vec<&'static str> {
220 InputAnalyzer::suggest_commands(self.intent)
221 }
222}
223
224impl Default for ChatInputWidget {
225 fn default() -> Self {
226 Self::new()
227 }
228}
229
230#[cfg(test)]
231mod tests {
232 use super::*;
233
234 #[test]
235 fn test_intent_detection() {
236 assert_eq!(
237 InputAnalyzer::detect_intent("generate code"),
238 Intent::Generate
239 );
240 assert_eq!(
241 InputAnalyzer::detect_intent("explain this"),
242 Intent::Explain
243 );
244 assert_eq!(InputAnalyzer::detect_intent("fix the bug"), Intent::Fix);
245 assert_eq!(
246 InputAnalyzer::detect_intent("refactor this"),
247 Intent::Refactor
248 );
249 assert_eq!(InputAnalyzer::detect_intent("unit test"), Intent::Test);
250 assert_eq!(
251 InputAnalyzer::detect_intent("document this"),
252 Intent::Document
253 );
254 assert_eq!(
255 InputAnalyzer::detect_intent("execute command"),
256 Intent::Execute
257 );
258 assert_eq!(InputAnalyzer::detect_intent("help me"), Intent::Help);
259 assert_eq!(InputAnalyzer::detect_intent("hello"), Intent::Chat);
260 }
261
262 #[test]
263 fn test_input_validation() {
264 assert!(InputAnalyzer::validate_input("hello").is_ok());
265 assert!(InputAnalyzer::validate_input("").is_err());
266 assert!(InputAnalyzer::validate_input(" ").is_err());
267 assert!(InputAnalyzer::validate_input(&"x".repeat(10001)).is_err());
268 }
269
270 #[test]
271 fn test_chat_input_widget() {
272 let widget = ChatInputWidget::new();
273 assert!(widget.text.is_empty());
274 assert_eq!(widget.cursor, 0);
275 }
276
277 #[test]
278 fn test_insert_char() {
279 let mut widget = ChatInputWidget::new();
280 widget.insert_char('h');
281 widget.insert_char('i');
282 assert_eq!(widget.text, "hi");
283 assert_eq!(widget.cursor, 2);
284 }
285
286 #[test]
287 fn test_backspace() {
288 let mut widget = ChatInputWidget::new();
289 widget.insert_char('h');
290 widget.insert_char('i');
291 widget.backspace();
292 assert_eq!(widget.text, "h");
293 assert_eq!(widget.cursor, 1);
294 }
295
296 #[test]
297 fn test_cursor_movement() {
298 let mut widget = ChatInputWidget::new();
299 widget.text = "hello".to_string();
300 widget.cursor = 5;
301
302 widget.move_left();
303 assert_eq!(widget.cursor, 4);
304
305 widget.move_right();
306 assert_eq!(widget.cursor, 5);
307
308 widget.move_start();
309 assert_eq!(widget.cursor, 0);
310
311 widget.move_end();
312 assert_eq!(widget.cursor, 5);
313 }
314
315 #[test]
316 fn test_submit() {
317 let mut widget = ChatInputWidget::new();
318 widget.text = "hello".to_string();
319 widget.cursor = 5;
320
321 let submitted = widget.submit();
322 assert_eq!(submitted, "hello");
323 assert!(widget.text.is_empty());
324 assert_eq!(widget.cursor, 0);
325 assert_eq!(widget.history.len(), 1);
326 }
327
328 #[test]
329 fn test_history_navigation() {
330 let mut widget = ChatInputWidget::new();
331 widget.submit_text("first");
332 widget.submit_text("second");
333 widget.submit_text("third");
334
335 widget.history_up();
336 assert_eq!(widget.text, "third");
337
338 widget.history_up();
339 assert_eq!(widget.text, "second");
340
341 widget.history_down();
342 assert_eq!(widget.text, "third");
343
344 widget.history_down();
345 assert!(widget.text.is_empty());
346 }
347}
348
349impl ChatInputWidget {
350 #[cfg(test)]
352 fn submit_text(&mut self, text: &str) {
353 self.text = text.to_string();
354 self.submit();
355 }
356}