@@ -30,28 +30,27 @@ use crate::{
3030} ;
3131
3232use log:: { debug, error, info, warn} ;
33- #[ cfg( not( feature = "integration" ) ) ]
34- use std:: io:: stdout;
35- use std:: { io:: stdin, path:: Path , sync:: Arc } ;
33+ use std:: {
34+ io:: { stdin, IsTerminal } ,
35+ path:: Path ,
36+ sync:: Arc ,
37+ } ;
3638
37- #[ cfg( not( windows) ) ]
38- use anyhow:: Context ;
39- use anyhow:: Error ;
39+ use anyhow:: { Context , Error } ;
4040
41- use crossterm:: { event:: Event as CrosstermEvent , tty:: IsTty } ;
4241#[ cfg( not( windows) ) ]
4342use { signal_hook:: consts:: signal, signal_hook_tokio:: Signals } ;
4443#[ cfg( windows) ]
4544type Signals = futures_util:: stream:: Empty < ( ) > ;
4645
4746#[ cfg( not( feature = "integration" ) ) ]
48- use tui:: backend:: CrosstermBackend ;
47+ use tui:: backend:: TerminaBackend ;
4948
5049#[ cfg( feature = "integration" ) ]
5150use tui:: backend:: TestBackend ;
5251
5352#[ cfg( not( feature = "integration" ) ) ]
54- type TerminalBackend = CrosstermBackend < std :: io :: Stdout > ;
53+ type TerminalBackend = TerminaBackend ;
5554
5655#[ cfg( feature = "integration" ) ]
5756type TerminalBackend = TestBackend ;
@@ -104,7 +103,8 @@ impl Application {
104103 let theme_loader = theme:: Loader :: new ( & theme_parent_dirs) ;
105104
106105 #[ cfg( not( feature = "integration" ) ) ]
107- let backend = CrosstermBackend :: new ( stdout ( ) , ( & config. editor ) . into ( ) ) ;
106+ let backend = TerminaBackend :: new ( ( & config. editor ) . into ( ) )
107+ . context ( "failed to create terminal backend" ) ?;
108108
109109 #[ cfg( feature = "integration" ) ]
110110 let backend = TestBackend :: new ( 120 , 150 ) ;
@@ -123,7 +123,11 @@ impl Application {
123123 } ) ) ,
124124 handlers,
125125 ) ;
126- Self :: load_configured_theme ( & mut editor, & config. load ( ) ) ;
126+ Self :: load_configured_theme (
127+ & mut editor,
128+ & config. load ( ) ,
129+ terminal. backend ( ) . supports_true_color ( ) ,
130+ ) ;
127131
128132 let keys = Box :: new ( Map :: new ( Arc :: clone ( & config) , |config : & Config | {
129133 & config. keys
@@ -214,7 +218,7 @@ impl Application {
214218 } else {
215219 editor. new_file ( Action :: VerticalSplit ) ;
216220 }
217- } else if stdin ( ) . is_tty ( ) || cfg ! ( feature = "integration" ) {
221+ } else if stdin ( ) . is_terminal ( ) || cfg ! ( feature = "integration" ) {
218222 editor. new_file ( Action :: VerticalSplit ) ;
219223 } else {
220224 editor
@@ -282,7 +286,7 @@ impl Application {
282286
283287 pub async fn event_loop < S > ( & mut self , input_stream : & mut S )
284288 where
285- S : Stream < Item = std:: io:: Result < crossterm :: event :: Event > > + Unpin ,
289+ S : Stream < Item = std:: io:: Result < termina :: Event > > + Unpin ,
286290 {
287291 self . render ( ) . await ;
288292
@@ -295,7 +299,7 @@ impl Application {
295299
296300 pub async fn event_loop_until_idle < S > ( & mut self , input_stream : & mut S ) -> bool
297301 where
298- S : Stream < Item = std:: io:: Result < crossterm :: event :: Event > > + Unpin ,
302+ S : Stream < Item = std:: io:: Result < termina :: Event > > + Unpin ,
299303 {
300304 loop {
301305 if self . editor . should_close ( ) {
@@ -396,7 +400,11 @@ impl Application {
396400 // the sake of locals highlighting.
397401 let lang_loader = helix_core:: config:: user_lang_loader ( ) ?;
398402 self . editor . syn_loader . store ( Arc :: new ( lang_loader) ) ;
399- Self :: load_configured_theme ( & mut self . editor , & default_config) ;
403+ Self :: load_configured_theme (
404+ & mut self . editor ,
405+ & default_config,
406+ self . terminal . backend ( ) . supports_true_color ( ) ,
407+ ) ;
400408
401409 // Re-parse any open documents with the new language config.
402410 let lang_loader = self . editor . syn_loader . load ( ) ;
@@ -429,8 +437,8 @@ impl Application {
429437 }
430438
431439 /// Load the theme set in configuration
432- fn load_configured_theme ( editor : & mut Editor , config : & Config ) {
433- let true_color = config. editor . true_color || crate :: true_color ( ) ;
440+ fn load_configured_theme ( editor : & mut Editor , config : & Config , terminal_true_color : bool ) {
441+ let true_color = terminal_true_color || config. editor . true_color || crate :: true_color ( ) ;
434442 let theme = config
435443 . theme
436444 . as_ref ( )
@@ -634,29 +642,29 @@ impl Application {
634642 false
635643 }
636644
637- pub async fn handle_terminal_events ( & mut self , event : std:: io:: Result < CrosstermEvent > ) {
645+ pub async fn handle_terminal_events ( & mut self , event : std:: io:: Result < termina :: Event > ) {
638646 let mut cx = crate :: compositor:: Context {
639647 editor : & mut self . editor ,
640648 jobs : & mut self . jobs ,
641649 scroll : None ,
642650 } ;
643651 // Handle key events
644652 let should_redraw = match event. unwrap ( ) {
645- CrosstermEvent :: Resize ( width , height ) => {
653+ termina :: Event :: WindowResized ( termina :: WindowSize { rows , cols , .. } ) => {
646654 self . terminal
647- . resize ( Rect :: new ( 0 , 0 , width , height ) )
655+ . resize ( Rect :: new ( 0 , 0 , cols , rows ) )
648656 . expect ( "Unable to resize terminal" ) ;
649657
650658 let area = self . terminal . size ( ) . expect ( "couldn't get terminal size" ) ;
651659
652660 self . compositor . resize ( area) ;
653661
654662 self . compositor
655- . handle_event ( & Event :: Resize ( width , height ) , & mut cx)
663+ . handle_event ( & Event :: Resize ( cols , rows ) , & mut cx)
656664 }
657665 // Ignore keyboard release events.
658- CrosstermEvent :: Key ( crossterm :: event:: KeyEvent {
659- kind : crossterm :: event:: KeyEventKind :: Release ,
666+ termina :: Event :: Key ( termina :: event:: KeyEvent {
667+ kind : termina :: event:: KeyEventKind :: Release ,
660668 ..
661669 } ) => false ,
662670 event => self . compositor . handle_event ( & event. into ( ) , & mut cx) ,
@@ -1107,22 +1115,40 @@ impl Application {
11071115 self . terminal . restore ( )
11081116 }
11091117
1118+ #[ cfg( not( feature = "integration" ) ) ]
1119+ pub fn event_stream ( & self ) -> impl Stream < Item = std:: io:: Result < termina:: Event > > + Unpin {
1120+ use termina:: Terminal as _;
1121+ let reader = self . terminal . backend ( ) . terminal ( ) . event_reader ( ) ;
1122+ termina:: EventStream :: new ( reader, |event| !event. is_escape ( ) )
1123+ }
1124+
1125+ #[ cfg( feature = "integration" ) ]
1126+ pub fn event_stream ( & self ) -> impl Stream < Item = std:: io:: Result < termina:: Event > > + Unpin {
1127+ use std:: {
1128+ pin:: Pin ,
1129+ task:: { Context , Poll } ,
1130+ } ;
1131+
1132+ /// A dummy stream that never polls as ready.
1133+ pub struct DummyEventStream ;
1134+
1135+ impl Stream for DummyEventStream {
1136+ type Item = std:: io:: Result < termina:: Event > ;
1137+
1138+ fn poll_next ( self : Pin < & mut Self > , _cx : & mut Context < ' _ > ) -> Poll < Option < Self :: Item > > {
1139+ Poll :: Pending
1140+ }
1141+ }
1142+
1143+ DummyEventStream
1144+ }
1145+
11101146 pub async fn run < S > ( & mut self , input_stream : & mut S ) -> Result < i32 , Error >
11111147 where
1112- S : Stream < Item = std:: io:: Result < crossterm :: event :: Event > > + Unpin ,
1148+ S : Stream < Item = std:: io:: Result < termina :: Event > > + Unpin ,
11131149 {
11141150 self . terminal . claim ( ) ?;
11151151
1116- // Exit the alternate screen and disable raw mode before panicking
1117- let hook = std:: panic:: take_hook ( ) ;
1118- std:: panic:: set_hook ( Box :: new ( move |info| {
1119- // We can't handle errors properly inside this closure. And it's
1120- // probably not a good idea to `unwrap()` inside a panic handler.
1121- // So we just ignore the `Result`.
1122- let _ = TerminalBackend :: force_restore ( ) ;
1123- hook ( info) ;
1124- } ) ) ;
1125-
11261152 self . event_loop ( input_stream) . await ;
11271153
11281154 let close_errs = self . close ( ) . await ;
0 commit comments