on master help sign in
Add ctrl-z to suspend
commit821565e4efe84f97e08327138cbe8f09aba934e0 authorIvan Tham <pickfire@riseup.net> dateSat 17 Jul 2021, 22:47 (5 years ago) parentadb5d84
10 files changed+131−22
Cargo.lock+14 helix-lsp/Cargo.toml+1−1 helix-term/Cargo.toml+5−1 helix-term/src/application.rs+74−10 helix-term/src/commands.rs+7−1 helix-term/src/compositor.rs+16−1 helix-term/src/keymap.rs+1 helix-tui/src/terminal.rs+11−6 helix-view/Cargo.toml+1−1 helix-view/src/graphics.rs+1−1
Cargo.lock+14
@@ -378,6 +378,8 @@ dependencies = [
378378 "pulldown-cmark",
379379 "serde",
380380 "serde_json",
381+ "signal-hook",
382+ "signal-hook-tokio",
381383 "tokio",
382384 "toml",
383385]
@@ -865,6 +867,18 @@ dependencies = [
865867 "libc",
866868]
867869
870+[[package]]
871+name = "signal-hook-tokio"
872+version = "0.3.0"
873+source = "registry+https://github.com/rust-lang/crates.io-index"
874+checksum = "f6c5d32165ff8b94e68e7b3bdecb1b082e958c22434b363482cfb89dcd6f3ff8"
875+dependencies = [
876+ "futures-core",
877+ "libc",
878+ "signal-hook",
879+ "tokio",
880+]
881+
868882[[package]]
869883name = "similar"
870884version = "1.3.0"
helix-lsp/Cargo.toml+1−1
@@ -23,5 +23,5 @@ lsp-types = { version = "0.89", features = ["proposed"] }
2323serde = { version = "1.0", features = ["derive"] }
2424serde_json = "1.0"
2525thiserror = "1.0"
26−tokio = { version = "1.9", features = ["full"] }
26+tokio = { version = "1.9", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot"] }
2727tokio-stream = "0.1.7"
helix-term/Cargo.toml+5−1
@@ -28,10 +28,11 @@ helix-lsp = { version = "0.3", path = "../helix-lsp" }
2828anyhow = "1"
2929once_cell = "1.8"
3030
31−tokio = { version = "1", features = ["full"] }
31+tokio = { version = "1", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot"] }
3232num_cpus = "1"
3333tui = { path = "../helix-tui", package = "helix-tui", default-features = false, features = ["crossterm"] }
3434crossterm = { version = "0.20", features = ["event-stream"] }
35+signal-hook = "0.3"
3536
3637futures-util = { version = "0.3", features = ["std", "async-await"], default-features = false }
3738
@@ -53,3 +54,6 @@ toml = "0.5"
5354
5455serde_json = "1.0"
5556serde = { version = "1.0", features = ["derive"] }
57+
58+[target.'cfg(not(windows))'.dependencies] # https://github.com/vorner/signal-hook/issues/100
59+signal-hook-tokio = { version = "0.3", features = ["futures-v0_3"] }
helix-term/src/application.rs+74−10
@@ -18,6 +18,10 @@ use crossterm::{
1818 event::{DisableMouseCapture, EnableMouseCapture, Event, EventStream},
1919 execute, terminal,
2020};
21+#[cfg(not(windows))]
22+use signal_hook::{consts::signal, low_level};
23+#[cfg(not(windows))]
24+use signal_hook_tokio::Signals;
2125
2226pub struct Application {
2327 compositor: Compositor,
@@ -36,6 +40,8 @@ pub struct Application {
3640 #[allow(dead_code)]
3741 syn_loader: Arc<syntax::Loader>,
3842
43+ #[cfg(not(windows))]
44+ signals: Signals,
3945 jobs: Jobs,
4046 lsp_progress: LspProgressMap,
4147}
@@ -102,6 +108,9 @@ impl Application {
102108
103109 editor.set_theme(theme);
104110
111+ #[cfg(not(windows))]
112+ let signals = Signals::new(&[signal::SIGTSTP, signal::SIGCONT])?;
113+
105114 let app = Self {
106115 compositor,
107116 editor,
@@ -111,6 +120,8 @@ impl Application {
111120 theme_loader,
112121 syn_loader,
113122
123+ #[cfg(not(windows))]
124+ signals,
114125 jobs: Jobs::new(),
115126 lsp_progress: LspProgressMap::new(),
116127 };
@@ -147,6 +158,51 @@ impl Application {
147158
148159 use futures_util::StreamExt;
149160
161+ #[cfg(not(windows))]
162+ tokio::select! {
163+ biased;
164+
165+ event = reader.next() => {
166+ self.handle_terminal_events(event)
167+ }
168+ Some(signal) = self.signals.next() => {
169+ use helix_view::graphics::Rect;
170+ match signal {
171+ signal::SIGTSTP => {
172+ self.compositor.save_cursor();
173+ self.restore_term().unwrap();
174+ low_level::emulate_default_handler(signal::SIGTSTP).unwrap();
175+ }
176+ signal::SIGCONT => {
177+ self.claim_term().await.unwrap();
178+ // redraw the terminal
179+ let Rect { width, height, .. } = self.compositor.size();
180+ self.compositor.resize(width, height);
181+ self.compositor.load_cursor();
182+ self.render();
183+ }
184+ _ => unreachable!(),
185+ }
186+ }
187+ Some((id, call)) = self.editor.language_servers.incoming.next() => {
188+ self.handle_language_server_message(call, id).await;
189+ // limit render calls for fast language server messages
190+ let last = self.editor.language_servers.incoming.is_empty();
191+ if last || last_render.elapsed() > deadline {
192+ self.render();
193+ last_render = Instant::now();
194+ }
195+ }
196+ Some(callback) = self.jobs.futures.next() => {
197+ self.jobs.handle_callback(&mut self.editor, &mut self.compositor, callback);
198+ self.render();
199+ }
200+ Some(callback) = self.jobs.wait_futures.next() => {
201+ self.jobs.handle_callback(&mut self.editor, &mut self.compositor, callback);
202+ self.render();
203+ }
204+ }
205+ #[cfg(windows)]
150206 tokio::select! {
151207 biased;
152208
@@ -443,15 +499,29 @@ impl Application {
443499 }
444500 }
445501
446− pub async fn run(&mut self) -> Result<(), Error> {
502+ async fn claim_term(&mut self) -> Result<(), Error> {
447503 terminal::enable_raw_mode()?;
448−
449504 let mut stdout = stdout();
450−
451505 execute!(stdout, terminal::EnterAlternateScreen)?;
506+ self.editor.close_language_servers(None).await?;
452507 if self.config.terminal.mouse {
453508 execute!(stdout, EnableMouseCapture)?;
454509 }
510+ Ok(())
511+ }
512+
513+ fn restore_term(&mut self) -> Result<(), Error> {
514+ let mut stdout = stdout();
515+ // reset cursor shape
516+ write!(stdout, "\x1B[2 q")?;
517+ execute!(stdout, DisableMouseCapture)?;
518+ execute!(stdout, terminal::LeaveAlternateScreen)?;
519+ terminal::disable_raw_mode()?;
520+ Ok(())
521+ }
522+
523+ pub async fn run(&mut self) -> Result<(), Error> {
524+ self.claim_term().await?;
455525
456526 // Exit the alternate screen and disable raw mode before panicking
457527 let hook = std::panic::take_hook();
@@ -469,13 +539,7 @@ impl Application {
469539
470540 self.editor.close_language_servers(None).await?;
471541
472− // reset cursor shape
473− write!(stdout, "\x1B[2 q")?;
474−
475− execute!(stdout, DisableMouseCapture)?;
476− execute!(stdout, terminal::LeaveAlternateScreen)?;
477−
478− terminal::disable_raw_mode()?;
542+ self.restore_term()?;
479543
480544 Ok(())
481545 }
helix-term/src/commands.rs+7−1
@@ -291,7 +291,8 @@ impl Command {
291291 surround_replace, "Surround replace",
292292 surround_delete, "Surround delete",
293293 select_textobject_around, "Select around object",
294− select_textobject_inner, "Select inside object"
294+ select_textobject_inner, "Select inside object",
295+ suspend, "Suspend"
295296 );
296297}
297298
@@ -3894,3 +3895,8 @@ fn surround_delete(cx: &mut Context) {
38943895 }
38953896 })
38963897}
3898+
3899+fn suspend(_cx: &mut Context) {
3900+ #[cfg(not(windows))]
3901+ signal_hook::low_level::raise(signal_hook::consts::signal::SIGTSTP).unwrap();
3902+}
helix-term/src/compositor.rs+16−1
@@ -68,7 +68,7 @@ pub trait Component: Any + AnyComponent {
6868
6969use anyhow::Error;
7070use std::io::stdout;
71−use tui::backend::CrosstermBackend;
71+use tui::backend::{Backend, CrosstermBackend};
7272type Terminal = tui::terminal::Terminal<CrosstermBackend<std::io::Stdout>>;
7373
7474pub struct Compositor {
@@ -99,6 +99,21 @@ impl Compositor {
9999 .expect("Unable to resize terminal")
100100 }
101101
102+ pub fn save_cursor(&mut self) {
103+ if self.terminal.cursor_kind() == CursorKind::Hidden {
104+ self.terminal
105+ .backend_mut()
106+ .show_cursor(CursorKind::Block)
107+ .ok();
108+ }
109+ }
110+
111+ pub fn load_cursor(&mut self) {
112+ if self.terminal.cursor_kind() == CursorKind::Hidden {
113+ self.terminal.backend_mut().hide_cursor().ok();
114+ }
115+ }
116+
102117 pub fn push(&mut self, mut layer: Box<dyn Component>) {
103118 let size = self.size();
104119 // trigger required_size on init
helix-term/src/keymap.rs+1
@@ -502,6 +502,7 @@ impl Default for Keymaps {
502502 },
503503
504504 "\"" => select_register,
505+ "C-z" => suspend,
505506 });
506507 // TODO: decide whether we want normal mode to also be select mode (kakoune-like), or whether
507508 // we keep this separate select mode. More keys can fit into normal mode then, but it's weird
helix-tui/src/terminal.rs+11−6
@@ -45,8 +45,8 @@ where
4545 buffers: [Buffer; 2],
4646 /// Index of the current buffer in the previous array
4747 current: usize,
48− /// Whether the cursor is currently hidden
49− hidden_cursor: bool,
48+ /// Kind of cursor (hidden or others)
49+ cursor_kind: CursorKind,
5050 /// Viewport
5151 viewport: Viewport,
5252}
@@ -57,7 +57,7 @@ where
5757{
5858 fn drop(&mut self) {
5959 // Attempt to restore the cursor state
60− if self.hidden_cursor {
60+ if self.cursor_kind == CursorKind::Hidden {
6161 if let Err(err) = self.show_cursor(CursorKind::Block) {
6262 eprintln!("Failed to show the cursor: {}", err);
6363 }
@@ -93,7 +93,7 @@ where
9393 Buffer::empty(options.viewport.area),
9494 ],
9595 current: 0,
96− hidden_cursor: false,
96+ cursor_kind: CursorKind::Block,
9797 viewport: options.viewport,
9898 })
9999 }
@@ -185,15 +185,20 @@ where
185185 Ok(())
186186 }
187187
188+ #[inline]
189+ pub fn cursor_kind(&self) -> CursorKind {
190+ self.cursor_kind
191+ }
192+
188193 pub fn hide_cursor(&mut self) -> io::Result<()> {
189194 self.backend.hide_cursor()?;
190− self.hidden_cursor = true;
195+ self.cursor_kind = CursorKind::Hidden;
191196 Ok(())
192197 }
193198
194199 pub fn show_cursor(&mut self, kind: CursorKind) -> io::Result<()> {
195200 self.backend.show_cursor(kind)?;
196− self.hidden_cursor = false;
201+ self.cursor_kind = kind;
197202 Ok(())
198203 }
199204
helix-view/Cargo.toml+1−1
@@ -24,7 +24,7 @@ crossterm = { version = "0.20", optional = true }
2424once_cell = "1.8"
2525url = "2"
2626
27−tokio = { version = "1", features = ["full"] }
27+tokio = { version = "1", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot"] }
2828futures-util = { version = "0.3", features = ["std", "async-await"], default-features = false }
2929
3030slotmap = "1"
helix-view/src/graphics.rs+1−1
@@ -1,7 +1,7 @@
11use bitflags::bitflags;
22use std::cmp::{max, min};
33
4−#[derive(Debug)]
4+#[derive(Debug, Clone, Copy, PartialEq)]
55/// UNSTABLE
66pub enum CursorKind {
77 /// █