aboutsummaryrefslogtreecommitdiff
path: root/helix-term/src
diff options
context:
space:
mode:
authorIvan Tham2021-07-17 14:47:08 +0000
committerBlaž Hrastnik2021-08-03 00:32:21 +0000
commit821565e4efe84f97e08327138cbe8f09aba934e0 (patch)
treeeaf80ea3d12906db4805dfe27e30a51250014243 /helix-term/src
parentadb5d842ba3ff7e539a77de54a0a8db3018a3844 (diff)
Add ctrl-z to suspend
Diffstat (limited to 'helix-term/src')
-rw-r--r--helix-term/src/application.rs84
-rw-r--r--helix-term/src/commands.rs8
-rw-r--r--helix-term/src/compositor.rs17
-rw-r--r--helix-term/src/keymap.rs1
4 files changed, 98 insertions, 12 deletions
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