xacli_components/components/input/
mod.rs

1use std::io::Write;
2
3use crossterm::{
4    cursor,
5    event::{Event, KeyCode, KeyModifiers},
6    queue, style, terminal,
7};
8use xacli_core::{Context, Error, InputValue, Result};
9
10pub struct Input {
11    prompt: String,
12    default: Option<String>,
13}
14
15impl Input {
16    pub fn new(prompt: impl Into<String>) -> Self {
17        Self {
18            prompt: prompt.into(),
19            default: None,
20        }
21    }
22
23    pub fn run(self, ctx: &mut dyn Context) -> Result<InputValue> {
24        // Enable raw mode for keyboard input
25        terminal::enable_raw_mode()?;
26        let result = self.run_inner(ctx);
27        // Always disable raw mode when done
28        let _ = terminal::disable_raw_mode();
29        result
30    }
31
32    fn run_inner(self, ctx: &mut dyn Context) -> Result<InputValue> {
33        let mut input = self.default.clone().unwrap_or_default();
34        let mut cursor_pos = input.len();
35
36        let stdout = &mut ctx.stdout();
37
38        // 显示提示
39        self.render(stdout, &input, cursor_pos)?;
40
41        loop {
42            if let Event::Key(key) = ctx.read_event()? {
43                match key.code {
44                    KeyCode::Enter => {
45                        return Ok(InputValue::String(input));
46                    }
47                    KeyCode::Esc => {
48                        return Err(Error::InterruptError);
49                    }
50                    KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
51                        return Err(Error::InterruptError);
52                    }
53                    KeyCode::Char(c) => {
54                        input.insert(cursor_pos, c);
55                        cursor_pos += 1;
56                    }
57                    KeyCode::Backspace => {
58                        if cursor_pos > 0 {
59                            cursor_pos -= 1;
60                            input.remove(cursor_pos);
61                        }
62                    }
63                    KeyCode::Delete => {
64                        if cursor_pos < input.len() {
65                            input.remove(cursor_pos);
66                        }
67                    }
68                    KeyCode::Left => {
69                        cursor_pos = cursor_pos.saturating_sub(1);
70                    }
71                    KeyCode::Right => {
72                        if cursor_pos < input.len() {
73                            cursor_pos += 1;
74                        }
75                    }
76                    KeyCode::Home => {
77                        cursor_pos = 0;
78                    }
79                    KeyCode::End => {
80                        cursor_pos = input.len();
81                    }
82                    _ => {}
83                }
84
85                self.render(stdout, &input, cursor_pos)?;
86            }
87        }
88    }
89
90    fn render<W: Write>(&self, stdout: &mut W, input: &str, cursor_pos: usize) -> Result<()> {
91        queue!(
92            stdout,
93            cursor::MoveToColumn(0),
94            terminal::Clear(terminal::ClearType::CurrentLine),
95            style::Print(&self.prompt),
96            style::Print(" "),
97            style::Print(input),
98            cursor::MoveToColumn((self.prompt.len() + 1 + cursor_pos) as u16)
99        )?;
100
101        stdout.flush()?;
102        Ok(())
103    }
104}