diff options
author | Ivan Tham | 2021-07-17 14:47:08 +0000 |
---|---|---|
committer | Blaž Hrastnik | 2021-08-03 00:32:21 +0000 |
commit | 821565e4efe84f97e08327138cbe8f09aba934e0 (patch) | |
tree | eaf80ea3d12906db4805dfe27e30a51250014243 /helix-term | |
parent | adb5d842ba3ff7e539a77de54a0a8db3018a3844 (diff) |
Add ctrl-z to suspend
Diffstat (limited to 'helix-term')
-rw-r--r-- | helix-term/Cargo.toml | 6 | ||||
-rw-r--r-- | helix-term/src/application.rs | 84 | ||||
-rw-r--r-- | helix-term/src/commands.rs | 8 | ||||
-rw-r--r-- | helix-term/src/compositor.rs | 17 | ||||
-rw-r--r-- | helix-term/src/keymap.rs | 1 |
5 files changed, 103 insertions, 13 deletions
diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index 1fc14ad2..0e2baae3 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -28,10 +28,11 @@ helix-lsp = { version = "0.3", path = "../helix-lsp" } anyhow = "1" once_cell = "1.8" -tokio = { version = "1", features = ["full"] } +tokio = { version = "1", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot"] } num_cpus = "1" tui = { path = "../helix-tui", package = "helix-tui", default-features = false, features = ["crossterm"] } crossterm = { version = "0.20", features = ["event-stream"] } +signal-hook = "0.3" futures-util = { version = "0.3", features = ["std", "async-await"], default-features = false } @@ -53,3 +54,6 @@ toml = "0.5" serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } + +[target.'cfg(not(windows))'.dependencies] # https://github.com/vorner/signal-hook/issues/100 +signal-hook-tokio = { version = "0.3", features = ["futures-v0_3"] } diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 5f350671..9e659fb0 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -18,6 +18,10 @@ use crossterm::{ event::{DisableMouseCapture, EnableMouseCapture, Event, EventStream}, execute, terminal, }; +#[cfg(not(windows))] +use signal_hook::{consts::signal, low_level}; +#[cfg(not(windows))] +use signal_hook_tokio::Signals; pub struct Application { compositor: Compositor, @@ -36,6 +40,8 @@ pub struct Application { #[allow(dead_code)] syn_loader: Arc<syntax::Loader>, + #[cfg(not(windows))] + signals: Signals, jobs: Jobs, lsp_progress: LspProgressMap, } @@ -102,6 +108,9 @@ impl Application { editor.set_theme(theme); + #[cfg(not(windows))] + let signals = Signals::new(&[signal::SIGTSTP, signal::SIGCONT])?; + let app = Self { compositor, editor, @@ -111,6 +120,8 @@ impl Application { theme_loader, syn_loader, + #[cfg(not(windows))] + signals, jobs: Jobs::new(), lsp_progress: LspProgressMap::new(), }; @@ -147,6 +158,51 @@ impl Application { use futures_util::StreamExt; + #[cfg(not(windows))] + tokio::select! { + biased; + + event = reader.next() => { + self.handle_terminal_events(event) + } + Some(signal) = self.signals.next() => { + use helix_view::graphics::Rect; + match signal { + signal::SIGTSTP => { + self.compositor.save_cursor(); + self.restore_term().unwrap(); + low_level::emulate_default_handler(signal::SIGTSTP).unwrap(); + } + signal::SIGCONT => { + self.claim_term().await.unwrap(); + // redraw the terminal + let Rect { width, height, .. } = self.compositor.size(); + self.compositor.resize(width, height); + self.compositor.load_cursor(); + self.render(); + } + _ => unreachable!(), + } + } + Some((id, call)) = self.editor.language_servers.incoming.next() => { + self.handle_language_server_message(call, id).await; + // limit render calls for fast language server messages + let last = self.editor.language_servers.incoming.is_empty(); + if last || last_render.elapsed() > deadline { + self.render(); + last_render = Instant::now(); + } + } + Some(callback) = self.jobs.futures.next() => { + self.jobs.handle_callback(&mut self.editor, &mut self.compositor, callback); + self.render(); + } + Some(callback) = self.jobs.wait_futures.next() => { + self.jobs.handle_callback(&mut self.editor, &mut self.compositor, callback); + self.render(); + } + } + #[cfg(windows)] tokio::select! { biased; @@ -443,15 +499,29 @@ impl Application { } } - pub async fn run(&mut self) -> Result<(), Error> { + async fn claim_term(&mut self) -> Result<(), Error> { terminal::enable_raw_mode()?; - let mut stdout = stdout(); - execute!(stdout, terminal::EnterAlternateScreen)?; + self.editor.close_language_servers(None).await?; if self.config.terminal.mouse { execute!(stdout, EnableMouseCapture)?; } + Ok(()) + } + + fn restore_term(&mut self) -> Result<(), Error> { + let mut stdout = stdout(); + // reset cursor shape + write!(stdout, "\x1B[2 q")?; + execute!(stdout, DisableMouseCapture)?; + execute!(stdout, terminal::LeaveAlternateScreen)?; + terminal::disable_raw_mode()?; + Ok(()) + } + + pub async fn run(&mut self) -> Result<(), Error> { + self.claim_term().await?; // Exit the alternate screen and disable raw mode before panicking let hook = std::panic::take_hook(); @@ -469,13 +539,7 @@ impl Application { self.editor.close_language_servers(None).await?; - // reset cursor shape - write!(stdout, "\x1B[2 q")?; - - execute!(stdout, DisableMouseCapture)?; - execute!(stdout, terminal::LeaveAlternateScreen)?; - - terminal::disable_raw_mode()?; + self.restore_term()?; Ok(()) } diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index cc2c2cca..5cbab34f 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -291,7 +291,8 @@ impl Command { surround_replace, "Surround replace", surround_delete, "Surround delete", select_textobject_around, "Select around object", - select_textobject_inner, "Select inside object" + select_textobject_inner, "Select inside object", + suspend, "Suspend" ); } @@ -3894,3 +3895,8 @@ fn surround_delete(cx: &mut Context) { } }) } + +fn suspend(_cx: &mut Context) { + #[cfg(not(windows))] + signal_hook::low_level::raise(signal_hook::consts::signal::SIGTSTP).unwrap(); +} diff --git a/helix-term/src/compositor.rs b/helix-term/src/compositor.rs index c2cfa3a7..628c4e13 100644 --- a/helix-term/src/compositor.rs +++ b/helix-term/src/compositor.rs @@ -68,7 +68,7 @@ pub trait Component: Any + AnyComponent { use anyhow::Error; use std::io::stdout; -use tui::backend::CrosstermBackend; +use tui::backend::{Backend, CrosstermBackend}; type Terminal = tui::terminal::Terminal<CrosstermBackend<std::io::Stdout>>; pub struct Compositor { @@ -99,6 +99,21 @@ impl Compositor { .expect("Unable to resize terminal") } + pub fn save_cursor(&mut self) { + if self.terminal.cursor_kind() == CursorKind::Hidden { + self.terminal + .backend_mut() + .show_cursor(CursorKind::Block) + .ok(); + } + } + + pub fn load_cursor(&mut self) { + if self.terminal.cursor_kind() == CursorKind::Hidden { + self.terminal.backend_mut().hide_cursor().ok(); + } + } + pub fn push(&mut self, mut layer: Box<dyn Component>) { let size = self.size(); // trigger required_size on init diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs index 053b92e6..5fe730a1 100644 --- a/helix-term/src/keymap.rs +++ b/helix-term/src/keymap.rs @@ -502,6 +502,7 @@ impl Default for Keymaps { }, "\"" => select_register, + "C-z" => suspend, }); // TODO: decide whether we want normal mode to also be select mode (kakoune-like), or whether // we keep this separate select mode. More keys can fit into normal mode then, but it's weird |