@@ -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 }