Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ctrl-z to suspend #464

Merged
merged 2 commits into from
Aug 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion helix-lsp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ lsp-types = { version = "0.89", features = ["proposed"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
thiserror = "1.0"
tokio = { version = "1.9", features = ["full"] }
tokio = { version = "1.9", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot"] }
tokio-stream = "0.1.7"
6 changes: 5 additions & 1 deletion helix-term/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }

Expand All @@ -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"] }
70 changes: 60 additions & 10 deletions helix-term/src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ use crossterm::{
event::{DisableMouseCapture, EnableMouseCapture, Event, EventStream},
execute, terminal,
};
#[cfg(not(windows))]
use {
signal_hook::{consts::signal, low_level},
signal_hook_tokio::Signals,
};
#[cfg(windows)]
type Signals = futures_util::stream::Empty<()>;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I didn't know you mean like this. I thought you just do futures_util::stream::Empty below instead.


pub struct Application {
compositor: Compositor,
Expand All @@ -36,6 +43,7 @@ pub struct Application {
#[allow(dead_code)]
syn_loader: Arc<syntax::Loader>,

signals: Signals,
jobs: Jobs,
lsp_progress: LspProgressMap,
}
Expand Down Expand Up @@ -102,6 +110,11 @@ impl Application {

editor.set_theme(theme);

#[cfg(windows)]
let signals = futures_util::stream::empty();
#[cfg(not(windows))]
let signals = Signals::new(&[signal::SIGTSTP, signal::SIGCONT])?;

let app = Self {
compositor,
editor,
Expand All @@ -111,6 +124,7 @@ impl Application {
theme_loader,
syn_loader,

signals,
jobs: Jobs::new(),
lsp_progress: LspProgressMap::new(),
};
Expand Down Expand Up @@ -153,6 +167,9 @@ impl Application {
event = reader.next() => {
self.handle_terminal_events(event)
}
Some(signal) = self.signals.next() => {
self.handle_signals(signal).await;
}
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
Expand All @@ -174,6 +191,31 @@ impl Application {
}
}

#[cfg(windows)]
// no signal handling available on windows
pub async fn handle_signals(&mut self, _signal: ()) {}

#[cfg(not(windows))]
pub async fn handle_signals(&mut self, signal: i32) {
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!(),
}
}

pub fn handle_terminal_events(&mut self, event: Option<Result<Event, crossterm::ErrorKind>>) {
let mut cx = crate::compositor::Context {
editor: &mut self.editor,
Expand Down Expand Up @@ -443,15 +485,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();
Expand All @@ -469,13 +525,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(())
}
Expand Down
8 changes: 7 additions & 1 deletion helix-term/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"
);
}

Expand Down Expand Up @@ -3877,3 +3878,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();
}
17 changes: 16 additions & 1 deletion helix-term/src/compositor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions helix-term/src/keymap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,7 @@ impl Default for Keymaps {
},

"\"" => select_register,
"C-z" => suspend,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Document this in the book/

Copy link
Contributor Author

@pickfire pickfire Aug 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is something that we need to explicitly document. Users will expect this to work since all cli is expected to use ctrl-z for suspend (at least I am not aware of any that does not support this). I did look into that but not sure which category to put last time.

});
// 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
Expand Down
17 changes: 11 additions & 6 deletions helix-tui/src/terminal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ where
buffers: [Buffer; 2],
/// Index of the current buffer in the previous array
current: usize,
/// Whether the cursor is currently hidden
hidden_cursor: bool,
/// Kind of cursor (hidden or others)
cursor_kind: CursorKind,
/// Viewport
viewport: Viewport,
}
Expand All @@ -57,7 +57,7 @@ where
{
fn drop(&mut self) {
// Attempt to restore the cursor state
if self.hidden_cursor {
if self.cursor_kind == CursorKind::Hidden {
if let Err(err) = self.show_cursor(CursorKind::Block) {
eprintln!("Failed to show the cursor: {}", err);
}
Expand Down Expand Up @@ -93,7 +93,7 @@ where
Buffer::empty(options.viewport.area),
],
current: 0,
hidden_cursor: false,
cursor_kind: CursorKind::Block,
viewport: options.viewport,
})
}
Expand Down Expand Up @@ -185,15 +185,20 @@ where
Ok(())
}

#[inline]
pub fn cursor_kind(&self) -> CursorKind {
self.cursor_kind
}

pub fn hide_cursor(&mut self) -> io::Result<()> {
self.backend.hide_cursor()?;
self.hidden_cursor = true;
self.cursor_kind = CursorKind::Hidden;
Ok(())
}

pub fn show_cursor(&mut self, kind: CursorKind) -> io::Result<()> {
self.backend.show_cursor(kind)?;
self.hidden_cursor = false;
self.cursor_kind = kind;
Ok(())
}

Expand Down
2 changes: 1 addition & 1 deletion helix-view/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ crossterm = { version = "0.20", optional = true }
once_cell = "1.8"
url = "2"

tokio = { version = "1", features = ["full"] }
tokio = { version = "1", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot"] }
futures-util = { version = "0.3", features = ["std", "async-await"], default-features = false }

slotmap = "1"
Expand Down
2 changes: 1 addition & 1 deletion helix-view/src/graphics.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use bitflags::bitflags;
use std::cmp::{max, min};

#[derive(Debug)]
#[derive(Debug, Clone, Copy, PartialEq)]
/// UNSTABLE
pub enum CursorKind {
/// █
Expand Down