diff options
author | Blaž Hrastnik | 2020-05-28 08:47:35 +0000 |
---|---|---|
committer | Blaž Hrastnik | 2020-05-28 08:47:35 +0000 |
commit | 6905ff03c29c9fc020a6333adf9e9d5004c3f3c5 (patch) | |
tree | 39b0a23ccafce90cc7c767cee5859c75666fd3d6 /helix-term | |
parent | 1984410ac998ac5561f4a3de87d3d125f2f61178 (diff) |
Start swapping from termwiz to crossterm + async.
Diffstat (limited to 'helix-term')
-rw-r--r-- | helix-term/Cargo.toml | 17 | ||||
-rw-r--r-- | helix-term/src/line.rs | 172 | ||||
-rw-r--r-- | helix-term/src/main.rs | 8 |
3 files changed, 96 insertions, 101 deletions
diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index ea4d5b55..0ab6209e 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -10,13 +10,22 @@ edition = "2018" name = "hx" path = "src/main.rs" -# [[bin]] -# name = "line" -# path = "src/line.rs" +[[bin]] +name = "line" +path = "src/line.rs" [dependencies] # termwiz = { git = "https://github.com/wez/wezterm", features = ["widgets"] } -termwiz = { path = "../../wezterm/termwiz", default-features = false, features = ["widgets"] } +# termwiz = { path = "../../wezterm/termwiz", default-features = false, features = ["widgets"] } + anyhow = "1.0.31" argh = "0.1.3" helix-core = { path = "../helix-core" } +# crossterm = { version = "0.17.5", features = ["event-stream"] } +crossterm = { git = "https://github.com/crossterm-rs/crossterm", branch = "mio-update", features = ["event-stream"] } + +futures = { version = "0.3.5", default-features = false, features = ["std"] } +# futures-timer = "3.0.2" +smol = "0.1.10" +num_cpus = "1.13.0" +piper = "0.1.2" diff --git a/helix-term/src/line.rs b/helix-term/src/line.rs index 90fb5e6f..58d4c9d8 100644 --- a/helix-term/src/line.rs +++ b/helix-term/src/line.rs @@ -1,111 +1,97 @@ -use termwiz::cell::AttributeChange; -use termwiz::color::{AnsiColor, ColorAttribute, RgbColor}; -use termwiz::lineedit::*; +//! Demonstrates how to read events asynchronously with async-std. +//! +//! cargo run --features="event-stream" --example event-stream-async-std -#[derive(Default)] -struct Host { - history: BasicHistory, -} +use std::{ + io::{stdout, Write}, + time::Duration, +}; -impl LineEditorHost for Host { - // Render the prompt with a darkslateblue background color if - // the terminal supports true color, otherwise render it with - // a navy blue ansi color. - fn render_prompt(&self, prompt: &str) -> Vec<OutputElement> { - vec![ - OutputElement::Attribute(AttributeChange::Background( - ColorAttribute::TrueColorWithPaletteFallback( - RgbColor::from_named("darkslateblue").unwrap(), - AnsiColor::Navy.into(), - ), - )), - OutputElement::Text(prompt.to_owned()), - ] - } +use futures::{future::FutureExt, select, StreamExt}; +use smol::Timer; +// use futures_timer::Delay; - fn history(&mut self) -> &mut dyn History { - &mut self.history - } +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(); - /// Demo of the completion API for words starting with "h" or "he" - fn complete(&self, line: &str, cursor_position: usize) -> Vec<CompletionCandidate> { - let mut candidates = vec![]; - if let Some((range, word)) = word_at_cursor(line, cursor_position) { - let words = &["hello", "help", "he-man"]; - - for w in words { - if w.starts_with(word) { - candidates.push(CompletionCandidate { - range: range.clone(), - text: w.to_string(), - }); + 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()); + } + + if event == Event::Key(KeyCode::Esc.into()) { + break; + } + } + Some(Err(e)) => println!("Error: {:?}\r", e), + None => break, } } - } - candidates + }; } } -/// This is a conceptually simple function that computes the bounds -/// of the whitespace delimited word at the specified cursor position -/// in the supplied line string. -/// It returns the range and the corresponding slice out of the line. -/// This function is sufficient for example purposes; in a real application -/// the equivalent function would need to be aware of quoting and other -/// application specific context. -fn word_at_cursor(line: &str, cursor_position: usize) -> Option<(std::ops::Range<usize>, &str)> { - let char_indices: Vec<(usize, char)> = line.char_indices().collect(); - if char_indices.is_empty() { - return None; - } - let char_position = char_indices - .iter() - .position(|(idx, _)| *idx == cursor_position) - .unwrap_or(char_indices.len()); - - // Look back until we find whitespace - let mut start_position = char_position; - while start_position > 0 - && start_position <= char_indices.len() - && !char_indices[start_position - 1].1.is_whitespace() - { - start_position -= 1; - } +fn main() -> Result<()> { + println!("{}", HELP); - // Look forwards until we find whitespace - let mut end_position = char_position; - while end_position < char_indices.len() && !char_indices[end_position].1.is_whitespace() { - end_position += 1; - } + 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); - if end_position > start_position { - let range = char_indices[start_position].0 - ..char_indices - .get(end_position) - .map(|c| c.0 + 1) - .unwrap_or(line.len()); - Some((range.clone(), &line[range])) - } else { - None + // 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()))); } -} -fn main() -> anyhow::Result<()> { - println!("Type `exit` to quit this example, or start a word with `h` and press Tab."); - let mut terminal = line_editor_terminal()?; - let mut editor = LineEditor::new(&mut terminal); + // No need to `run()`, now we can just block on the main future. + smol::block_on(print_events()); - let mut host = Host::default(); - loop { - if let Some(line) = editor.read_line(&mut host)? { - println!("read line: {:?}", line); - if line == "exit" { - break; - } + // Send a shutdown signal. + drop(s); - host.history().add(&line); - } + // Wait for threads to finish. + for t in threads { + t.join().unwrap(); } - Ok(()) + execute!(stdout, DisableMouseCapture)?; + + disable_raw_mode() } diff --git a/helix-term/src/main.rs b/helix-term/src/main.rs index 9dfa3767..aaa83a86 100644 --- a/helix-term/src/main.rs +++ b/helix-term/src/main.rs @@ -1,6 +1,6 @@ -mod editor; +// mod editor; -use editor::Editor; +// use editor::Editor; use argh::FromArgs; use std::{env, path::PathBuf}; @@ -16,9 +16,9 @@ pub struct Args { fn main() -> Result<(), Error> { let args: Args = argh::from_env(); - let mut editor = Editor::new(args)?; + // let mut editor = Editor::new(args)?; - editor.run()?; + // editor.run()?; Ok(()) } |