intelli_shell/common/
process.rs1use anyhow::Result;
2use crossterm::event::{self, Event, KeyCode, KeyEventKind, KeyModifiers};
3use ratatui::{backend::Backend, layout::Rect, Frame, Terminal};
4
5use super::remove_newlines;
6use crate::theme::Theme;
7
8pub struct ProcessOutput {
10 pub message: Option<String>,
11 pub output: Option<String>,
12}
13
14impl ProcessOutput {
15 pub fn new(message: impl Into<String>, output: impl Into<String>) -> Self {
16 Self {
17 message: Some(message.into()),
18 output: Some(output.into()),
19 }
20 }
21
22 pub fn empty() -> Self {
23 Self {
24 message: None,
25 output: None,
26 }
27 }
28
29 pub fn message(message: impl Into<String>) -> Self {
30 Self {
31 message: Some(message.into()),
32 output: None,
33 }
34 }
35
36 pub fn output(output: impl Into<String>) -> Self {
37 Self {
38 output: Some(output.into()),
39 message: None,
40 }
41 }
42}
43
44#[derive(Clone, Copy)]
46pub struct ExecutionContext {
47 pub inline: bool,
48 pub theme: Theme,
49}
50
51pub trait Process {
53 fn min_height(&self) -> usize;
55
56 fn peek(&mut self) -> Result<Option<ProcessOutput>> {
59 Ok(None)
60 }
61
62 fn render<B: Backend>(&mut self, frame: &mut Frame<B>, area: Rect);
64
65 fn process_raw_event(&mut self, event: Event) -> Result<Option<ProcessOutput>>;
67
68 fn show<B, F>(mut self, terminal: &mut Terminal<B>, mut area: F) -> Result<ProcessOutput>
70 where
71 B: Backend,
72 F: FnMut(&Frame<B>) -> Rect,
73 Self: Sized,
74 {
75 loop {
76 terminal.draw(|f| {
78 let area = area(f);
79 self.render(f, area);
80 })?;
81
82 let event = event::read()?;
83 if let Event::Key(k) = &event {
84 if k.kind != KeyEventKind::Press {
86 continue;
87 }
88 if let KeyCode::Char(c) = k.code {
90 if c == 'c' && k.modifiers.contains(KeyModifiers::CONTROL) {
91 return Ok(ProcessOutput::empty());
92 }
93 }
94 }
95
96 if let Some(res) = self.process_raw_event(event)? {
98 return Ok(res);
99 }
100 }
101 }
102}
103
104pub trait InteractiveProcess: Process {
106 fn process_event(&mut self, event: Event) -> Result<Option<ProcessOutput>> {
108 match event {
109 Event::Paste(content) => self.insert_text(remove_newlines(content))?,
110 Event::Key(key) => {
111 let has_ctrl = key.modifiers.contains(KeyModifiers::CONTROL);
112 match key.code {
113 KeyCode::Char(c) if has_ctrl && c == 'd' => self.delete_current()?,
115 KeyCode::F(f) if f == 2 => self.edit_current()?,
117 KeyCode::Char(c) if has_ctrl && (c == 'e' || c == 'u') => self.edit_current()?,
118 KeyCode::Home => self.home(),
120 KeyCode::End => self.end(),
121 KeyCode::Char(c) if has_ctrl && c == 'k' => self.prev(),
122 KeyCode::Char(c) if has_ctrl && c == 'j' => self.next(),
123 KeyCode::Up => self.move_up(),
124 KeyCode::Down => self.move_down(),
125 KeyCode::Right => self.move_right(),
126 KeyCode::Left => self.move_left(),
127 KeyCode::Char(c) => self.insert_char(c)?,
129 KeyCode::Backspace => self.delete_char(true)?,
130 KeyCode::Delete => self.delete_char(false)?,
131 KeyCode::Enter | KeyCode::Tab => return self.accept_current(),
133 KeyCode::Esc => return self.exit().map(Some),
134 _ => (),
135 }
136 }
137 _ => (),
138 };
139
140 Ok(None)
142 }
143
144 fn move_up(&mut self);
146 fn move_down(&mut self);
148 fn move_left(&mut self);
150 fn move_right(&mut self);
152
153 fn prev(&mut self);
155 fn next(&mut self);
157
158 fn home(&mut self);
160 fn end(&mut self);
162
163 fn insert_text(&mut self, text: String) -> Result<()>;
165 fn insert_char(&mut self, c: char) -> Result<()>;
167 fn delete_char(&mut self, backspace: bool) -> Result<()>;
169
170 fn delete_current(&mut self) -> Result<()>;
172 fn edit_current(&mut self) -> Result<()>;
174 fn accept_current(&mut self) -> Result<Option<ProcessOutput>>;
176 fn exit(&mut self) -> Result<ProcessOutput>;
178}