@@ -30,28 +30,27 @@ use crate::{
30
30
} ;
31
31
32
32
use 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
+ } ;
36
38
37
- #[ cfg( not( windows) ) ]
38
- use anyhow:: Context ;
39
- use anyhow:: Error ;
39
+ use anyhow:: { Context , Error } ;
40
40
41
- use crossterm:: { event:: Event as CrosstermEvent , tty:: IsTty } ;
42
41
#[ cfg( not( windows) ) ]
43
42
use { signal_hook:: consts:: signal, signal_hook_tokio:: Signals } ;
44
43
#[ cfg( windows) ]
45
44
type Signals = futures_util:: stream:: Empty < ( ) > ;
46
45
47
46
#[ cfg( not( feature = "integration" ) ) ]
48
- use tui:: backend:: CrosstermBackend ;
47
+ use tui:: backend:: TerminaBackend ;
49
48
50
49
#[ cfg( feature = "integration" ) ]
51
50
use tui:: backend:: TestBackend ;
52
51
53
52
#[ cfg( not( feature = "integration" ) ) ]
54
- type TerminalBackend = CrosstermBackend < std :: io :: Stdout > ;
53
+ type TerminalBackend = TerminaBackend ;
55
54
56
55
#[ cfg( feature = "integration" ) ]
57
56
type TerminalBackend = TestBackend ;
@@ -104,7 +103,8 @@ impl Application {
104
103
let theme_loader = theme:: Loader :: new ( & theme_parent_dirs) ;
105
104
106
105
#[ 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" ) ?;
108
108
109
109
#[ cfg( feature = "integration" ) ]
110
110
let backend = TestBackend :: new ( 120 , 150 ) ;
@@ -123,7 +123,11 @@ impl Application {
123
123
} ) ) ,
124
124
handlers,
125
125
) ;
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
+ ) ;
127
131
128
132
let keys = Box :: new ( Map :: new ( Arc :: clone ( & config) , |config : & Config | {
129
133
& config. keys
@@ -214,7 +218,7 @@ impl Application {
214
218
} else {
215
219
editor. new_file ( Action :: VerticalSplit ) ;
216
220
}
217
- } else if stdin ( ) . is_tty ( ) || cfg ! ( feature = "integration" ) {
221
+ } else if stdin ( ) . is_terminal ( ) || cfg ! ( feature = "integration" ) {
218
222
editor. new_file ( Action :: VerticalSplit ) ;
219
223
} else {
220
224
editor
@@ -282,7 +286,7 @@ impl Application {
282
286
283
287
pub async fn event_loop < S > ( & mut self , input_stream : & mut S )
284
288
where
285
- S : Stream < Item = std:: io:: Result < crossterm :: event :: Event > > + Unpin ,
289
+ S : Stream < Item = std:: io:: Result < termina :: Event > > + Unpin ,
286
290
{
287
291
self . render ( ) . await ;
288
292
@@ -295,7 +299,7 @@ impl Application {
295
299
296
300
pub async fn event_loop_until_idle < S > ( & mut self , input_stream : & mut S ) -> bool
297
301
where
298
- S : Stream < Item = std:: io:: Result < crossterm :: event :: Event > > + Unpin ,
302
+ S : Stream < Item = std:: io:: Result < termina :: Event > > + Unpin ,
299
303
{
300
304
loop {
301
305
if self . editor . should_close ( ) {
@@ -396,7 +400,11 @@ impl Application {
396
400
// the sake of locals highlighting.
397
401
let lang_loader = helix_core:: config:: user_lang_loader ( ) ?;
398
402
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
+ ) ;
400
408
401
409
// Re-parse any open documents with the new language config.
402
410
let lang_loader = self . editor . syn_loader . load ( ) ;
@@ -429,8 +437,8 @@ impl Application {
429
437
}
430
438
431
439
/// 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 ( ) ;
434
442
let theme = config
435
443
. theme
436
444
. as_ref ( )
@@ -634,29 +642,29 @@ impl Application {
634
642
false
635
643
}
636
644
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 > ) {
638
646
let mut cx = crate :: compositor:: Context {
639
647
editor : & mut self . editor ,
640
648
jobs : & mut self . jobs ,
641
649
scroll : None ,
642
650
} ;
643
651
// Handle key events
644
652
let should_redraw = match event. unwrap ( ) {
645
- CrosstermEvent :: Resize ( width , height ) => {
653
+ termina :: Event :: WindowResized ( termina :: WindowSize { rows , cols , .. } ) => {
646
654
self . terminal
647
- . resize ( Rect :: new ( 0 , 0 , width , height ) )
655
+ . resize ( Rect :: new ( 0 , 0 , cols , rows ) )
648
656
. expect ( "Unable to resize terminal" ) ;
649
657
650
658
let area = self . terminal . size ( ) . expect ( "couldn't get terminal size" ) ;
651
659
652
660
self . compositor . resize ( area) ;
653
661
654
662
self . compositor
655
- . handle_event ( & Event :: Resize ( width , height ) , & mut cx)
663
+ . handle_event ( & Event :: Resize ( cols , rows ) , & mut cx)
656
664
}
657
665
// 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 ,
660
668
..
661
669
} ) => false ,
662
670
event => self . compositor . handle_event ( & event. into ( ) , & mut cx) ,
@@ -1107,22 +1115,40 @@ impl Application {
1107
1115
self . terminal . restore ( )
1108
1116
}
1109
1117
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
+
1110
1146
pub async fn run < S > ( & mut self , input_stream : & mut S ) -> Result < i32 , Error >
1111
1147
where
1112
- S : Stream < Item = std:: io:: Result < crossterm :: event :: Event > > + Unpin ,
1148
+ S : Stream < Item = std:: io:: Result < termina :: Event > > + Unpin ,
1113
1149
{
1114
1150
self . terminal . claim ( ) ?;
1115
1151
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
-
1126
1152
self . event_loop ( input_stream) . await ;
1127
1153
1128
1154
let close_errs = self . close ( ) . await ;
0 commit comments