From 065cbcee9e63703e5ffb3a2c044ed0b49a2b7fff Mon Sep 17 00:00:00 2001 From: Jan Hrastnik Date: Sun, 7 Jun 2020 14:11:08 +0200 Subject: [PATCH] fixed async loop --- .gitignore | 1 + helix-term/src/editor.rs | 69 ++++++++++++++------------- helix-term/src/test.rs | 100 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+), 33 deletions(-) create mode 100644 helix-term/src/test.rs diff --git a/.gitignore b/.gitignore index eb5a316cb..f6ea6b5d9 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ target +helix-term/rustfmt.toml diff --git a/helix-term/src/editor.rs b/helix-term/src/editor.rs index e2446578d..24e2448ab 100644 --- a/helix-term/src/editor.rs +++ b/helix-term/src/editor.rs @@ -1,14 +1,15 @@ -use std::io::{self, stdout, Write}; -use std::path::PathBuf; - use crossterm::{ cursor, cursor::position, - event::{self, read, Event, KeyCode, KeyEvent}, + event::{self, read, Event, EventStream, KeyCode, KeyEvent}, execute, queue, style, terminal::{self, disable_raw_mode, enable_raw_mode}, Result, }; +use futures::{future::FutureExt, select, StreamExt}; +use std::io::{self, stdout, Write}; +use std::path::PathBuf; +use std::time::Duration; const HELP: &str = r#" - Use q to quit @@ -20,40 +21,40 @@ pub struct Editor { } impl Editor { - pub fn read_char() -> Result { - loop { - if let Ok(Event::Key(KeyEvent { - code: KeyCode::Char(c), - .. - })) = event::read() - { - return Ok(c); - } - } - } - - pub async fn print_events() -> Result<()> { + pub async fn print_events() { + let mut reader = EventStream::new(); loop { // Handle key events - match Editor::read_char()? { - 'h' => execute!(io::stdout(), cursor::MoveLeft(1))?, - 'j' => execute!(io::stdout(), cursor::MoveDown(1))?, - 'k' => execute!(io::stdout(), cursor::MoveUp(1))?, - 'l' => execute!(io::stdout(), cursor::MoveRight(1))?, - 'q' => { - execute!( - io::stdout(), - style::ResetColor, - cursor::Show, - terminal::LeaveAlternateScreen - )?; - break; + let mut event = reader.next().await; + match event { + Some(Ok(x)) => { + if let Event::Key(KeyEvent { + code: KeyCode::Char(c), + .. + }) = x + { + match c { + 'h' => execute!(io::stdout(), cursor::MoveLeft(1)).unwrap(), + 'j' => execute!(io::stdout(), cursor::MoveDown(1)).unwrap(), + 'k' => execute!(io::stdout(), cursor::MoveUp(1)).unwrap(), + 'l' => execute!(io::stdout(), cursor::MoveRight(1)).unwrap(), + 'q' => { + execute!( + io::stdout(), + style::ResetColor, + cursor::Show, + terminal::LeaveAlternateScreen + ); + break; + } + _ => println!("{:?}", x), + } + } } - _ => println!("use 'q' to quit."), + Some(Err(x)) => panic!(x), + None => break, } } - - Ok(()) } pub fn run() -> Result<()> { @@ -85,6 +86,8 @@ impl Editor { // Send a shutdown signal. drop(s); + execute!(stdout, terminal::LeaveAlternateScreen)?; + // Wait for threads to finish. for t in threads { t.join().unwrap(); diff --git a/helix-term/src/test.rs b/helix-term/src/test.rs new file mode 100644 index 000000000..12b4f377a --- /dev/null +++ b/helix-term/src/test.rs @@ -0,0 +1,100 @@ +//! Demonstrates how to read events asynchronously with async-std. +//! +//! cargo run --features="event-stream" --example event-stream-async-std + +use std::{ + io::{stdout, Write}, + time::Duration, +}; + +use futures::{future::FutureExt, select, StreamExt}; +use smol::Timer; +// use futures_timer::Delay; + +use crossterm::{ + cursor::position, + event::{DisableMouseCapture, EnableMouseCapture, Event, EventStream, KeyCode}, + execute, + terminal::{disable_raw_mode, enable_raw_mode}, + Result, +}; + +const HELP: &str = r#"EventStream based on futures::Stream with async-std + - Keyboard, mouse and terminal resize events enabled + - Prints "." every second if there's no event + - Hit "c" to print current cursor position + - Use Esc to quit +"#; + +async fn print_events() { + let mut reader = EventStream::new(); + + loop { + let mut delay = Timer::after(Duration::from_millis(1_000)).fuse(); + let mut event = reader.next().fuse(); + + select! { + _ = delay => { println!(".\r"); }, + maybe_event = event => { + match maybe_event { + Some(Ok(event)) => { + println!("Event::{:?}\r", event); + + if event == Event::Key(KeyCode::Char('c').into()) { + println!("Cursor position: {:?}\r", position()); + + } + + println!("test"); + + if event == Event::Key(KeyCode::Esc.into()) { + break; + } + } + Some(Err(e)) => println!("Error: {:?}\r", e), + None => break, + } + } + }; + } +} + +fn main() -> Result<()> { + println!("{}", HELP); + + enable_raw_mode()?; + + let mut stdout = stdout(); + execute!(stdout, EnableMouseCapture)?; + + use std::thread; + + // Same number of threads as there are CPU cores. + let num_threads = num_cpus::get().max(1); + + // A channel that sends the shutdown signal. + let (s, r) = piper::chan::<()>(0); + let mut threads = Vec::new(); + + // Create an executor thread pool. + for _ in 0..num_threads { + // Spawn an executor thread that waits for the shutdown signal. + let r = r.clone(); + threads.push(thread::spawn(move || smol::run(r.recv()))); + } + + // No need to `run()`, now we can just block on the main future. + smol::block_on(print_events()); + + // Send a shutdown signal. + drop(s); + + // Wait for threads to finish. + for t in threads { + t.join().unwrap(); + } + + execute!(stdout, DisableMouseCapture)?; + + disable_raw_mode() +}