rush_sync_server/input/
input.rs

1// =====================================================
2// FILE: input/input.rs - FINAL VERSION mit RESTART SUPPORT
3// =====================================================
4
5use crate::commands::handler::CommandHandler;
6use crate::commands::history::{
7    HistoryAction, HistoryConfig, HistoryEvent, HistoryEventHandler, HistoryKeyboardHandler,
8    HistoryManager,
9};
10use crate::core::prelude::*;
11use crate::input::keyboard::{KeyAction, KeyboardManager};
12use crate::ui::cursor::CursorState;
13use crate::ui::widget::{InputWidget, Widget};
14use ratatui::{
15    style::Style,
16    text::{Line, Span},
17    widgets::{Block, Borders, Padding, Paragraph},
18};
19use unicode_segmentation::UnicodeSegmentation;
20
21pub struct InputState<'a> {
22    content: String,
23    cursor: CursorState,
24    prompt: String,
25    history_manager: HistoryManager,
26    config: &'a Config,
27    command_handler: CommandHandler,
28    keyboard_manager: KeyboardManager,
29    waiting_for_exit_confirmation: bool,
30    waiting_for_restart_confirmation: bool, // ← NEU HINZUGEFÜGT
31}
32
33impl<'a> InputState<'a> {
34    pub fn new(prompt: &str, config: &'a Config) -> Self {
35        let history_config = HistoryConfig::from_main_config(config);
36
37        Self {
38            content: String::with_capacity(100),
39            cursor: CursorState::new(),
40            prompt: prompt.to_string(),
41            history_manager: HistoryManager::new(history_config.max_entries),
42            config,
43            command_handler: CommandHandler::new(),
44            keyboard_manager: KeyboardManager::new(),
45            waiting_for_exit_confirmation: false,
46            waiting_for_restart_confirmation: false, // ← NEU HINZUGEFÜGT
47        }
48    }
49
50    pub fn validate_input(&self, input: &str) -> crate::core::error::Result<()> {
51        if input.trim().is_empty() {
52            return Err(AppError::Validation(get_translation(
53                "system.input.empty",
54                &[],
55            )));
56        }
57
58        let grapheme_count = input.graphemes(true).count();
59        let max_length = 1024;
60
61        if grapheme_count > max_length {
62            return Err(AppError::Validation(get_translation(
63                "system.input.too_long",
64                &[&max_length.to_string()],
65            )));
66        }
67
68        Ok(())
69    }
70
71    pub fn reset_for_language_change(&mut self) {
72        self.waiting_for_exit_confirmation = false;
73        self.waiting_for_restart_confirmation = false; // ← NEU HINZUGEFÜGT
74        self.content.clear();
75        self.history_manager.reset_position();
76        self.cursor.move_to_start();
77        log::debug!("InputState reset for language change");
78    }
79
80    fn handle_exit_confirmation(&mut self, action: KeyAction) -> Option<String> {
81        match action {
82            KeyAction::Submit => {
83                self.waiting_for_exit_confirmation = false;
84
85                let confirm_short = crate::i18n::get_translation("system.input.confirm.short", &[]);
86                let cancel_short = crate::i18n::get_translation("system.input.cancel.short", &[]);
87
88                match self.content.trim().to_lowercase().as_str() {
89                    input if input == confirm_short.to_lowercase() => {
90                        self.content.clear();
91                        Some("__EXIT__".to_string())
92                    }
93                    input if input == cancel_short.to_lowercase() => {
94                        self.clear_input();
95                        Some(crate::i18n::get_translation("system.input.cancelled", &[]))
96                    }
97                    _ => {
98                        self.clear_input();
99                        Some(crate::i18n::get_translation("system.input.cancelled", &[]))
100                    }
101                }
102            }
103            KeyAction::InsertChar(c) => {
104                let confirm_short = crate::i18n::get_translation("system.input.confirm.short", &[]);
105                let cancel_short = crate::i18n::get_translation("system.input.cancel.short", &[]);
106
107                if c.to_lowercase().to_string() == confirm_short.to_lowercase()
108                    || c.to_lowercase().to_string() == cancel_short.to_lowercase()
109                {
110                    self.content.clear();
111                    self.content.push(c);
112                    self.cursor.update_text_length(&self.content);
113                    self.cursor.move_to_end();
114                }
115                None
116            }
117            KeyAction::Backspace | KeyAction::Delete | KeyAction::ClearLine => {
118                self.clear_input();
119                None
120            }
121            _ => None,
122        }
123    }
124
125    // ✅ NEU: Restart Confirmation Handler
126    fn handle_restart_confirmation(&mut self, action: KeyAction) -> Option<String> {
127        match action {
128            KeyAction::Submit => {
129                self.waiting_for_restart_confirmation = false;
130
131                let confirm_short = crate::i18n::get_translation("system.input.confirm.short", &[]);
132                let cancel_short = crate::i18n::get_translation("system.input.cancel.short", &[]);
133
134                match self.content.trim().to_lowercase().as_str() {
135                    input if input == confirm_short.to_lowercase() => {
136                        self.content.clear();
137                        Some("__RESTART__".to_string()) // ✅ RESTART auslösen
138                    }
139                    input if input == cancel_short.to_lowercase() => {
140                        self.clear_input();
141                        Some(crate::i18n::get_translation("system.input.cancelled", &[]))
142                    }
143                    _ => {
144                        self.clear_input();
145                        Some(crate::i18n::get_translation("system.input.cancelled", &[]))
146                    }
147                }
148            }
149            KeyAction::InsertChar(c) => {
150                let confirm_short = crate::i18n::get_translation("system.input.confirm.short", &[]);
151                let cancel_short = crate::i18n::get_translation("system.input.cancel.short", &[]);
152
153                if c.to_lowercase().to_string() == confirm_short.to_lowercase()
154                    || c.to_lowercase().to_string() == cancel_short.to_lowercase()
155                {
156                    self.content.clear();
157                    self.content.push(c);
158                    self.cursor.update_text_length(&self.content);
159                    self.cursor.move_to_end();
160                }
161                None
162            }
163            KeyAction::Backspace | KeyAction::Delete | KeyAction::ClearLine => {
164                self.clear_input();
165                None
166            }
167            _ => None,
168        }
169    }
170
171    fn clear_input(&mut self) {
172        self.content.clear();
173        self.history_manager.reset_position();
174        self.cursor.move_to_start();
175    }
176
177    fn handle_history_action(&mut self, action: HistoryAction) -> Option<String> {
178        match action {
179            HistoryAction::NavigatePrevious => {
180                if let Some(entry) = self.history_manager.navigate_previous() {
181                    self.content = entry;
182                    self.cursor.update_text_length(&self.content);
183                    self.cursor.move_to_end();
184                }
185            }
186            HistoryAction::NavigateNext => {
187                if let Some(entry) = self.history_manager.navigate_next() {
188                    self.content = entry;
189                    self.cursor.update_text_length(&self.content);
190                    self.cursor.move_to_end();
191                }
192            }
193        }
194        None
195    }
196
197    fn handle_history_event(&mut self, event: HistoryEvent) -> String {
198        match event {
199            HistoryEvent::Clear => {
200                self.history_manager.clear();
201                HistoryEventHandler::create_clear_response()
202            }
203            HistoryEvent::Add(entry) => {
204                self.history_manager.add_entry(entry);
205                String::new()
206            }
207            _ => String::new(),
208        }
209    }
210
211    pub fn execute(&self) -> crate::core::error::Result<String> {
212        Ok(format!(
213            "__CONFIRM_EXIT__{}",
214            get_translation("system.input.confirm_exit", &[])
215        ))
216    }
217
218    pub fn handle_key_event(&mut self, key: KeyEvent) -> Option<String> {
219        // ✅ 1. PRÜFE ZUERST auf History-Actions
220        if let Some(history_action) = HistoryKeyboardHandler::get_history_action(&key) {
221            return self.handle_history_action(history_action);
222        }
223
224        // ✅ 2. ESC wird NICHT hier behandelt - nur in ScreenManager!
225        if key.code == KeyCode::Esc {
226            return None;
227        }
228
229        // ✅ 3. NORMALE Keyboard-Actions (ohne ESC!)
230        let action = self.keyboard_manager.get_action(&key);
231
232        // ✅ 4. CONFIRMATION HANDLING erweitert
233        if self.waiting_for_exit_confirmation {
234            return self.handle_exit_confirmation(action);
235        }
236
237        if self.waiting_for_restart_confirmation {
238            // ← NEU HINZUFÜGEN
239            return self.handle_restart_confirmation(action);
240        }
241
242        // ✅ 5. NORMALE Eingabeverarbeitung
243        match action {
244            KeyAction::Submit => {
245                if self.content.is_empty() {
246                    return None;
247                }
248                if self.validate_input(&self.content).is_ok() {
249                    let content = std::mem::take(&mut self.content);
250                    self.cursor.reset_for_empty_text();
251                    self.history_manager.add_entry(content.clone());
252                    let result = self.command_handler.handle_input(&content);
253
254                    if let Some(event) = HistoryEventHandler::handle_command_result(&result.message)
255                    {
256                        return Some(self.handle_history_event(event));
257                    }
258
259                    if result.message.starts_with("__CONFIRM_EXIT__") {
260                        self.waiting_for_exit_confirmation = true;
261                        return Some(result.message.replace("__CONFIRM_EXIT__", ""));
262                    }
263
264                    // ✅ NEU: Restart Confirmation handling
265                    if result.message.starts_with("__CONFIRM_RESTART__") {
266                        self.waiting_for_restart_confirmation = true;
267                        return Some(result.message.replace("__CONFIRM_RESTART__", ""));
268                    }
269
270                    if result.should_exit {
271                        return Some(format!("__EXIT__{}", result.message));
272                    }
273                    return Some(result.message);
274                }
275                None
276            }
277            KeyAction::InsertChar(c) => {
278                if self.content.graphemes(true).count() < self.config.input_max_length {
279                    let byte_pos = self.cursor.get_byte_position(&self.content);
280                    self.content.insert(byte_pos, c);
281                    self.cursor.update_text_length(&self.content);
282                    self.cursor.move_right();
283                }
284                None
285            }
286            KeyAction::MoveLeft => {
287                self.cursor.move_left();
288                None
289            }
290            KeyAction::MoveRight => {
291                self.cursor.move_right();
292                None
293            }
294            KeyAction::MoveToStart => {
295                self.cursor.move_to_start();
296                None
297            }
298            KeyAction::MoveToEnd => {
299                self.cursor.move_to_end();
300                None
301            }
302            KeyAction::Backspace => {
303                if self.content.is_empty() || self.cursor.get_position() == 0 {
304                    return None;
305                }
306
307                let current_byte_pos = self.cursor.get_byte_position(&self.content);
308                let prev_byte_pos = self.cursor.get_prev_byte_position(&self.content);
309
310                if prev_byte_pos >= current_byte_pos || current_byte_pos > self.content.len() {
311                    self.cursor.update_text_length(&self.content);
312                    return None;
313                }
314
315                self.cursor.move_left();
316                self.content
317                    .replace_range(prev_byte_pos..current_byte_pos, "");
318                self.cursor.update_text_length(&self.content);
319
320                if self.content.is_empty() {
321                    self.cursor.reset_for_empty_text();
322                }
323                None
324            }
325            KeyAction::Delete => {
326                let text_length = self.content.graphemes(true).count();
327                if self.cursor.get_position() >= text_length || text_length == 0 {
328                    return None;
329                }
330
331                let current_byte_pos = self.cursor.get_byte_position(&self.content);
332                let next_byte_pos = self.cursor.get_next_byte_position(&self.content);
333
334                if current_byte_pos >= next_byte_pos || next_byte_pos > self.content.len() {
335                    self.cursor.update_text_length(&self.content);
336                    return None;
337                }
338
339                self.content
340                    .replace_range(current_byte_pos..next_byte_pos, "");
341                self.cursor.update_text_length(&self.content);
342
343                if self.content.is_empty() {
344                    self.cursor.reset_for_empty_text();
345                }
346                None
347            }
348
349            KeyAction::ClearLine
350            | KeyAction::ScrollUp
351            | KeyAction::ScrollDown
352            | KeyAction::PageUp
353            | KeyAction::PageDown
354            | KeyAction::Cancel
355            | KeyAction::Quit
356            | KeyAction::CopySelection
357            | KeyAction::PasteBuffer
358            | KeyAction::NoAction => None,
359        }
360    }
361}
362
363impl Widget for InputState<'_> {
364    fn render(&self) -> Paragraph {
365        let graphemes: Vec<&str> = self.content.graphemes(true).collect();
366        let cursor_pos = self.cursor.get_position();
367        let mut spans = Vec::with_capacity(4);
368
369        spans.push(Span::styled(
370            &self.prompt,
371            Style::default().fg(self.config.prompt.color.into()),
372        ));
373
374        let prompt_width = self.prompt.graphemes(true).count();
375        let available_width = self
376            .config
377            .input_max_length
378            .saturating_sub(prompt_width + 4);
379
380        let viewport_start = if cursor_pos > available_width {
381            cursor_pos - available_width + 10
382        } else {
383            0
384        };
385
386        if cursor_pos > 0 {
387            let visible_text = if viewport_start < cursor_pos {
388                graphemes[viewport_start..cursor_pos].join("")
389            } else {
390                String::new()
391            };
392
393            spans.push(Span::styled(
394                visible_text,
395                Style::default().fg(self.config.theme.input_text.into()),
396            ));
397        }
398
399        let cursor_char = graphemes.get(cursor_pos).map_or(" ", |&c| c);
400        let cursor_style = if self.cursor.is_visible() {
401            Style::default()
402                .fg(self.config.theme.input_text.into())
403                .bg(self.config.theme.cursor.into())
404        } else {
405            Style::default().fg(self.config.theme.input_text.into())
406        };
407        spans.push(Span::styled(cursor_char, cursor_style));
408
409        if cursor_pos < graphemes.len() {
410            let remaining_width = available_width.saturating_sub(cursor_pos - viewport_start);
411            let end_pos = (cursor_pos + 1 + remaining_width).min(graphemes.len());
412
413            if cursor_pos + 1 < end_pos {
414                spans.push(Span::styled(
415                    graphemes[cursor_pos + 1..end_pos].join(""),
416                    Style::default().fg(self.config.theme.input_text.into()),
417                ));
418            }
419        }
420
421        Paragraph::new(Line::from(spans)).block(
422            Block::default()
423                .padding(Padding::new(3, 1, 1, 1))
424                .borders(Borders::NONE)
425                .style(Style::default().bg(self.config.theme.input_bg.into())),
426        )
427    }
428
429    fn handle_input(&mut self, key: KeyEvent) -> Option<String> {
430        self.handle_key_event(key)
431    }
432
433    fn as_input_state(&mut self) -> Option<&mut dyn InputWidget> {
434        Some(self)
435    }
436}
437
438impl InputWidget for InputState<'_> {
439    fn update_cursor_blink(&mut self) {
440        self.cursor.update_blink();
441    }
442}