Skip to content

Commit 3aba5b5

Browse files
committed
unfucking the cli: used raw mode to extrapolate events properly
1 parent f9b5a0e commit 3aba5b5

File tree

1 file changed

+117
-36
lines changed

1 file changed

+117
-36
lines changed

ahnlich/cli/src/term.rs

Lines changed: 117 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,39 @@
1+
use crossterm::event::{
2+
poll, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags,
3+
};
4+
15
use crossterm::{
6+
cursor,
27
event::{self, Event, KeyCode, KeyEvent},
8+
execute, queue,
39
style::{Color, Print, SetForegroundColor, Stylize},
10+
terminal::{disable_raw_mode, enable_raw_mode},
411
ExecutableCommand,
512
};
6-
use std::io::{self, stdout, Write};
13+
use std::io::{self, stdout, Stdout, Write};
714

815
use crate::connect::AgentPool;
916

1017
const RESERVED_WORDS: [&str; 3] = ["ping", "infoserver", "createpredindex"];
1118

19+
#[derive(Debug)]
20+
enum SpecialEntry {
21+
Enter,
22+
Up,
23+
Down,
24+
Break,
25+
Left,
26+
Right,
27+
}
28+
29+
#[derive(Debug)]
30+
enum Entry {
31+
Char(char),
32+
Special(SpecialEntry),
33+
Other(KeyCode),
34+
None,
35+
}
36+
1237
pub struct Term {
1338
client_pool: AgentPool,
1439
}
@@ -18,23 +43,20 @@ impl Term {
1843
Self { client_pool }
1944
}
2045

21-
pub(crate) fn read_line(&self) -> io::Result<String> {
22-
let mut line = String::new();
23-
while let Event::Key(KeyEvent { code, .. }) = event::read()? {
24-
match code {
25-
KeyCode::Enter => {
26-
break;
27-
}
28-
KeyCode::Char(c) => {
29-
line.push(c);
30-
}
31-
KeyCode::Esc => {
32-
break;
33-
}
34-
_ => {}
35-
}
46+
pub(crate) fn read_char(&self) -> io::Result<Entry> {
47+
if let Event::Key(KeyEvent { code, .. }) = event::read()? {
48+
Ok(match code {
49+
KeyCode::Enter => Entry::Special(SpecialEntry::Enter),
50+
KeyCode::Char(c) => Entry::Char(c),
51+
KeyCode::Left => Entry::Special(SpecialEntry::Left),
52+
KeyCode::Up => Entry::Special(SpecialEntry::Up),
53+
KeyCode::Down => Entry::Special(SpecialEntry::Down),
54+
KeyCode::Right => Entry::Special(SpecialEntry::Right),
55+
_ => Entry::Other(code),
56+
})
57+
} else {
58+
Ok(Entry::None)
3659
}
37-
Ok(line)
3860
}
3961
pub fn welcome_message(&self) -> io::Result<()> {
4062
let mut stdout = stdout();
@@ -48,50 +70,109 @@ impl Term {
4870
Ok(())
4971
}
5072

51-
pub(crate) fn ahnlich_prompt(&self) -> io::Result<()> {
52-
let mut stdout = stdout();
73+
pub(crate) fn ahnlich_prompt(&self, stdout: &mut Stdout) -> io::Result<()> {
5374
stdout.execute(SetForegroundColor(Color::White))?;
5475
stdout.execute(Print(">>> "))?;
5576
stdout.execute(SetForegroundColor(Color::White))?;
77+
5678
stdout.flush()?;
57-
//stdout.flush()?;
5879
Ok(())
5980
}
6081

61-
pub(crate) fn print_query(&self, query: &str) -> io::Result<()> {
62-
self.ahnlich_prompt()?;
63-
let output = String::from_iter(query.split(' ').map(|ex| {
64-
if RESERVED_WORDS.contains(&(ex.to_lowercase().as_str())) {
65-
format!("{} ", ex.magenta())
66-
} else {
67-
format!("{} ", ex.white())
68-
}
69-
}));
82+
// pub(crate) fn print_query(&self, query: &str) -> io::Result<()> {
83+
// self.ahnlich_prompt()?;
84+
// let output = String::from_iter(query.split(' ').map(|ex| {
85+
// if RESERVED_WORDS.contains(&(ex.to_lowercase().as_str())) {
86+
// format!("{} ", ex.magenta())
87+
// } else {
88+
// format!("{} ", ex.white())
89+
// }
90+
// }));
7091

71-
println!("{output}");
92+
// println!("{output}");
7293

73-
Ok(())
94+
// Ok(())
95+
// }
96+
97+
fn read_line(&self, stdout: &mut Stdout) -> io::Result<String> {
98+
let (start_pos_col, _) = cursor::position()?;
99+
let mut output = String::new();
100+
101+
loop {
102+
let char = self.read_char()?;
103+
let (current_pos_col, _) = cursor::position()?;
104+
match char {
105+
Entry::Char(c) => {
106+
output.push(c);
107+
stdout.execute(Print(c))?;
108+
stdout.flush()?;
109+
}
110+
Entry::Special(special) => match special {
111+
SpecialEntry::Up | SpecialEntry::Down => {
112+
continue;
113+
}
114+
SpecialEntry::Enter | SpecialEntry::Break => {
115+
queue!(stdout, Print("\n"), cursor::MoveToColumn(0))?;
116+
stdout.flush()?;
117+
break;
118+
}
119+
SpecialEntry::Left => {
120+
if start_pos_col < current_pos_col {
121+
stdout.execute(cursor::MoveLeft(1))?;
122+
}
123+
}
124+
SpecialEntry::Right => {
125+
if start_pos_col + output.len() as u16 > current_pos_col {
126+
stdout.execute(cursor::MoveRight(1))?;
127+
}
128+
}
129+
},
130+
Entry::Other(_) | Entry::None => {
131+
continue;
132+
}
133+
}
134+
}
135+
Ok(output)
74136
}
75137

76138
pub async fn run(&self) -> io::Result<()> {
139+
enable_raw_mode()?;
140+
let mut stdout = stdout();
141+
stdout.execute(cursor::EnableBlinking)?;
142+
stdout.execute(cursor::SetCursorStyle::BlinkingBar)?;
143+
77144
loop {
78-
self.ahnlich_prompt()?;
79-
let input = self.read_line()?;
145+
self.ahnlich_prompt(&mut stdout)?;
146+
let input = self.read_line(&mut stdout)?;
80147
match input.as_str() {
81148
"quit" | "exit()" => break,
82149
command => {
83-
self.print_query(command)?;
84150
let response = self.client_pool.parse_queries(command).await;
85151

86152
match response {
87153
Ok(success) => {
88-
println!("{}", success.join("\n\n"))
154+
for msg in success {
155+
queue!(
156+
stdout,
157+
Print(format!("{}\n", msg)),
158+
cursor::MoveToColumn(0)
159+
)?;
160+
}
161+
stdout.flush()?;
162+
}
163+
Err(err) => {
164+
queue!(
165+
stdout,
166+
Print(format!("{}\n", err.red())),
167+
cursor::MoveToColumn(0)
168+
)?;
169+
stdout.flush()?;
89170
}
90-
Err(err) => println!("{}", err.red()),
91171
}
92172
}
93173
};
94174
}
175+
disable_raw_mode()?;
95176
Ok(())
96177
}
97178
}

0 commit comments

Comments
 (0)