Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-term/src/application.rs')
| -rw-r--r-- | helix-term/src/application.rs | 94 |
1 files changed, 60 insertions, 34 deletions
diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index de661f30..8487e245 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -30,28 +30,27 @@ use crate::{ }; use log::{debug, error, info, warn}; -#[cfg(not(feature = "integration"))] -use std::io::stdout; -use std::{io::stdin, path::Path, sync::Arc}; +use std::{ + io::{stdin, IsTerminal}, + path::Path, + sync::Arc, +}; -#[cfg(not(windows))] -use anyhow::Context; -use anyhow::Error; +use anyhow::{Context, Error}; -use crossterm::{event::Event as CrosstermEvent, tty::IsTty}; #[cfg(not(windows))] use {signal_hook::consts::signal, signal_hook_tokio::Signals}; #[cfg(windows)] type Signals = futures_util::stream::Empty<()>; #[cfg(not(feature = "integration"))] -use tui::backend::CrosstermBackend; +use tui::backend::TerminaBackend; #[cfg(feature = "integration")] use tui::backend::TestBackend; #[cfg(not(feature = "integration"))] -type TerminalBackend = CrosstermBackend<std::io::Stdout>; +type TerminalBackend = TerminaBackend; #[cfg(feature = "integration")] type TerminalBackend = TestBackend; @@ -104,7 +103,8 @@ impl Application { let theme_loader = theme::Loader::new(&theme_parent_dirs); #[cfg(not(feature = "integration"))] - let backend = CrosstermBackend::new(stdout(), (&config.editor).into()); + let backend = TerminaBackend::new((&config.editor).into()) + .context("failed to create terminal backend")?; #[cfg(feature = "integration")] let backend = TestBackend::new(120, 150); @@ -123,7 +123,11 @@ impl Application { })), handlers, ); - Self::load_configured_theme(&mut editor, &config.load()); + Self::load_configured_theme( + &mut editor, + &config.load(), + terminal.backend().supports_true_color(), + ); let keys = Box::new(Map::new(Arc::clone(&config), |config: &Config| { &config.keys @@ -214,7 +218,7 @@ impl Application { } else { editor.new_file(Action::VerticalSplit); } - } else if stdin().is_tty() || cfg!(feature = "integration") { + } else if stdin().is_terminal() || cfg!(feature = "integration") { editor.new_file(Action::VerticalSplit); } else { editor @@ -282,7 +286,7 @@ impl Application { pub async fn event_loop<S>(&mut self, input_stream: &mut S) where - S: Stream<Item = std::io::Result<crossterm::event::Event>> + Unpin, + S: Stream<Item = std::io::Result<termina::Event>> + Unpin, { self.render().await; @@ -295,7 +299,7 @@ impl Application { pub async fn event_loop_until_idle<S>(&mut self, input_stream: &mut S) -> bool where - S: Stream<Item = std::io::Result<crossterm::event::Event>> + Unpin, + S: Stream<Item = std::io::Result<termina::Event>> + Unpin, { loop { if self.editor.should_close() { @@ -396,7 +400,11 @@ impl Application { // the sake of locals highlighting. let lang_loader = helix_core::config::user_lang_loader()?; self.editor.syn_loader.store(Arc::new(lang_loader)); - Self::load_configured_theme(&mut self.editor, &default_config); + Self::load_configured_theme( + &mut self.editor, + &default_config, + self.terminal.backend().supports_true_color(), + ); // Re-parse any open documents with the new language config. let lang_loader = self.editor.syn_loader.load(); @@ -429,8 +437,8 @@ impl Application { } /// Load the theme set in configuration - fn load_configured_theme(editor: &mut Editor, config: &Config) { - let true_color = config.editor.true_color || crate::true_color(); + fn load_configured_theme(editor: &mut Editor, config: &Config, terminal_true_color: bool) { + let true_color = terminal_true_color || config.editor.true_color || crate::true_color(); let theme = config .theme .as_ref() @@ -634,7 +642,7 @@ impl Application { false } - pub async fn handle_terminal_events(&mut self, event: std::io::Result<CrosstermEvent>) { + pub async fn handle_terminal_events(&mut self, event: std::io::Result<termina::Event>) { let mut cx = crate::compositor::Context { editor: &mut self.editor, jobs: &mut self.jobs, @@ -642,9 +650,9 @@ impl Application { }; // Handle key events let should_redraw = match event.unwrap() { - CrosstermEvent::Resize(width, height) => { + termina::Event::WindowResized(termina::WindowSize { rows, cols, .. }) => { self.terminal - .resize(Rect::new(0, 0, width, height)) + .resize(Rect::new(0, 0, cols, rows)) .expect("Unable to resize terminal"); let area = self.terminal.size().expect("couldn't get terminal size"); @@ -652,11 +660,11 @@ impl Application { self.compositor.resize(area); self.compositor - .handle_event(&Event::Resize(width, height), &mut cx) + .handle_event(&Event::Resize(cols, rows), &mut cx) } // Ignore keyboard release events. - CrosstermEvent::Key(crossterm::event::KeyEvent { - kind: crossterm::event::KeyEventKind::Release, + termina::Event::Key(termina::event::KeyEvent { + kind: termina::event::KeyEventKind::Release, .. }) => false, event => self.compositor.handle_event(&event.into(), &mut cx), @@ -1107,22 +1115,40 @@ impl Application { self.terminal.restore() } + #[cfg(not(feature = "integration"))] + pub fn event_stream(&self) -> impl Stream<Item = std::io::Result<termina::Event>> + Unpin { + use termina::Terminal as _; + let reader = self.terminal.backend().terminal().event_reader(); + termina::EventStream::new(reader, |event| !event.is_escape()) + } + + #[cfg(feature = "integration")] + pub fn event_stream(&self) -> impl Stream<Item = std::io::Result<termina::Event>> + Unpin { + use std::{ + pin::Pin, + task::{Context, Poll}, + }; + + /// A dummy stream that never polls as ready. + pub struct DummyEventStream; + + impl Stream for DummyEventStream { + type Item = std::io::Result<termina::Event>; + + fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { + Poll::Pending + } + } + + DummyEventStream + } + pub async fn run<S>(&mut self, input_stream: &mut S) -> Result<i32, Error> where - S: Stream<Item = std::io::Result<crossterm::event::Event>> + Unpin, + S: Stream<Item = std::io::Result<termina::Event>> + Unpin, { self.terminal.claim()?; - // Exit the alternate screen and disable raw mode before panicking - let hook = std::panic::take_hook(); - std::panic::set_hook(Box::new(move |info| { - // We can't handle errors properly inside this closure. And it's - // probably not a good idea to `unwrap()` inside a panic handler. - // So we just ignore the `Result`. - let _ = TerminalBackend::force_restore(); - hook(info); - })); - self.event_loop(input_stream).await; let close_errs = self.close().await; |