1use crossterm::{QueueableCommand, cursor};
2use crossterm::event::{Event, EventStream, KeyCode, KeyEvent, KeyModifiers};
3use crossterm::style::Print;
4use crossterm::terminal::{Clear, ClearType};
5
6use futures::{select, StreamExt};
7use futures::future::FutureExt;
8use futures::channel::mpsc;
9
10use std::io::Write;
11use thiserror::Error;
12
13#[derive(Error, Debug)]
14pub enum Error {
15 #[error("interrupted")]
16 Interrupted,
17 #[error("end of file")]
18 Eof,
19 #[error("io error: {0}")]
20 IoError(#[from] std::io::Error),
21}
22
23pub struct Editor {
24 history: Vec<String>,
25 history_receiver: mpsc::UnboundedReceiver<String>,
26
27 events: EventStream,
28}
29
30impl Editor {
31 pub fn new() -> (Self, mpsc::UnboundedSender<String>) {
35 let (tx, rx) = mpsc::unbounded();
36 let editor = Self {
37 history: vec![],
38 history_receiver: rx,
39
40 events: EventStream::new(),
41 };
42 (editor, tx)
43 }
44
45 pub async fn readline(&mut self) -> (String, Result<(), Error>) {
49 let mut buffer = String::new();
50 if let Err(e) = self.output(&buffer) {
53 return (buffer, Err(e));
54 }
55
56 loop {
57 let mut event = self.events.next().fuse();
58
59 select! {
60 line = self.history_receiver.next() => match line {
61 Some(line) => self.history.push(line),
62 None => continue,
64 },
65 maybe_event = event => match maybe_event {
67 Some(Ok(Event::Key(key_event))) => {
68 if key_event == KeyCode::Enter.into() {
69 return (buffer, Ok(()));
70 } else if key_event == KeyCode::Backspace.into() {
71 let _ = buffer.pop();
72 } else if let KeyEvent { code: KeyCode::Char(c), modifiers } = key_event {
73 if c == 'c' && modifiers == KeyModifiers::CONTROL {
74 return (buffer, Err(Error::Interrupted));
75 } else if c == 'd' && modifiers == KeyModifiers::CONTROL {
76 return (buffer, Err(Error::Eof));
77 } else {
78 buffer.push(c);
79 }
80 } else {
81 continue;
82 }
83 }
84 Some(Ok(_)) => continue,
86 Some(Err(e)) => return (buffer, Err(e.into())),
87 None => return (buffer, Ok(())),
89 }
90 }
91
92 if let Err(e) = self.output(&buffer) {
93 return (buffer, Err(e));
94 }
95 }
96 }
97
98 fn output(&self, buffer: &str) -> Result<(), Error> {
99 let mut stdout = std::io::stdout();
102 stdout.queue(Clear(ClearType::All))?.queue(cursor::MoveTo(0, 0))?;
103 let (_cols, rows) = crossterm::terminal::size()?;
105 let max_history = rows as usize - 2;
106 let total_history = self.history.len();
107 let _to_show = max_history.min(total_history);
108 for line in &self.history {
111 stdout.queue(Print(line))?.queue(Print("\n\r"))?;
113 }
114 stdout.queue(Print(">> "))?.queue(Print(&buffer))?;
115 stdout.flush()?;
116
117 Ok(())
118 }
119}
120
121pub fn enable_raw_mode() -> Result<(), std::io::Error> {
122 crossterm::terminal::enable_raw_mode()
123}
124
125pub fn disable_raw_mode() -> Result<(), std::io::Error> {
126 crossterm::terminal::disable_raw_mode()
127}