1
+ use std:: {
2
+ cell:: { Cell , RefCell } ,
3
+ io:: Write ,
4
+ sync:: { Arc , Mutex } ,
5
+ thread:: JoinHandle ,
6
+ } ;
7
+
1
8
use crate :: { float:: FloatContent , hint:: Shortcut } ;
9
+
2
10
use crossterm:: event:: { KeyCode , KeyEvent , KeyModifiers } ;
3
- use linutil_core :: Command ;
11
+
4
12
use oneshot:: { channel, Receiver } ;
13
+
5
14
use portable_pty:: {
6
15
ChildKiller , CommandBuilder , ExitStatus , MasterPty , NativePtySystem , PtySize , PtySystem ,
7
16
} ;
17
+
8
18
use ratatui:: {
9
- layout:: { Rect , Size } ,
10
- style:: { Color , Style , Stylize } ,
11
- text:: { Line , Span } ,
12
- widgets:: { Block , Borders } ,
19
+ layout:: Rect ,
20
+ style:: { Style , Stylize } ,
21
+ text:: Line ,
13
22
Frame ,
14
23
} ;
15
- use std:: {
16
- io:: Write ,
17
- sync:: { Arc , Mutex } ,
18
- thread:: JoinHandle ,
19
- } ;
24
+
20
25
use tui_term:: {
21
26
vt100:: { self , Screen } ,
22
27
widget:: PseudoTerminal ,
23
28
} ;
24
29
30
+ use linutil_core:: Command ;
31
+
25
32
pub struct RunningCommand {
26
33
/// A buffer to save all the command output (accumulates, until the command exits)
27
34
buffer : Arc < Mutex < Vec < u8 > > > ,
28
35
/// A handle for the thread running the command
29
- command_thread : Option < JoinHandle < ExitStatus > > ,
36
+ command_thread : Cell < Option < JoinHandle < ExitStatus > > > ,
30
37
/// A handle to kill the running process; it's an option because it can only be used once
31
38
child_killer : Option < Receiver < Box < dyn ChildKiller + Send + Sync > > > ,
32
39
/// A join handle for the thread that reads command output and sends it to the main thread
@@ -36,56 +43,31 @@ pub struct RunningCommand {
36
43
/// Used for sending keys to the emulated terminal
37
44
writer : Box < dyn Write + Send > ,
38
45
/// Only set after the process has ended
39
- status : Option < ExitStatus > ,
46
+ status : RefCell < Option < ExitStatus > > ,
40
47
scroll_offset : usize ,
41
48
}
42
49
43
50
impl FloatContent for RunningCommand {
44
- fn draw ( & mut self , frame : & mut Frame , area : Rect ) {
45
- // Calculate the inner size of the terminal area, considering borders
46
- let inner_size = Size {
47
- width : area. width - 2 , // Adjust for border width
48
- height : area. height - 2 ,
49
- } ;
50
-
51
- // Define the block for the terminal display
52
- let block = if !self . is_finished ( ) {
53
- // Display a block indicating the command is running
54
- Block :: default ( )
55
- . borders ( Borders :: ALL )
56
- . title_top ( Line :: from ( "Running the command...." ) . centered ( ) )
57
- . title_style ( Style :: default ( ) . reversed ( ) )
58
- . title_bottom ( Line :: from ( "Press Ctrl-C to KILL the command" ) )
51
+ fn top_title ( & self ) -> Option < Line < ' _ > > {
52
+ let ( content, content_style) = if !self . is_finished ( ) {
53
+ ( " Running command... " , Style :: default ( ) . reversed ( ) )
54
+ } else if self . wait_command ( ) . success ( ) {
55
+ ( " Success " , Style :: default ( ) . green ( ) . reversed ( ) )
59
56
} else {
60
- // Display a block with the command's exit status
61
- let mut title_line = if self . get_exit_status ( ) . success ( ) {
62
- Line :: from (
63
- Span :: default ( )
64
- . content ( "SUCCESS!" )
65
- . style ( Style :: default ( ) . fg ( Color :: Green ) . reversed ( ) ) ,
66
- )
67
- } else {
68
- Line :: from (
69
- Span :: default ( )
70
- . content ( "FAILED!" )
71
- . style ( Style :: default ( ) . fg ( Color :: Red ) . reversed ( ) ) ,
72
- )
73
- } ;
57
+ ( " Failed " , Style :: default ( ) . red ( ) . reversed ( ) )
58
+ } ;
74
59
75
- title_line. push_span (
76
- Span :: default ( )
77
- . content ( " press <ENTER> to close this window " )
78
- . style ( Style :: default ( ) ) ,
79
- ) ;
60
+ Some ( Line :: from ( content) . style ( content_style) )
61
+ }
80
62
81
- Block :: default ( )
82
- . borders ( Borders :: ALL )
83
- . title_top ( title_line. centered ( ) )
84
- } ;
63
+ fn bottom_title ( & self ) -> Option < Line < ' _ > > {
64
+ Some ( Line :: from ( "Press Ctrl-C to KILL the command" ) )
65
+ }
85
66
67
+ fn draw ( & mut self , frame : & mut Frame , area : Rect ) {
86
68
// Process the buffer and create the pseudo-terminal widget
87
- let screen = self . screen ( inner_size ) ;
88
- let pseudo_term = PseudoTerminal :: new ( & screen) . block ( block ) ;
69
+ let screen = self . screen ( area ) ;
70
+ let pseudo_term = PseudoTerminal :: new ( & screen) ;
89
71
90
72
// Render the widget on the frame
91
73
frame. render_widget ( pseudo_term, area) ;
@@ -116,12 +98,7 @@ impl FloatContent for RunningCommand {
116
98
}
117
99
118
100
fn is_finished ( & self ) -> bool {
119
- // Check if the command thread has finished
120
- if let Some ( command_thread) = & self . command_thread {
121
- command_thread. is_finished ( )
122
- } else {
123
- true
124
- }
101
+ self . status . borrow ( ) . is_some ( )
125
102
}
126
103
127
104
fn get_shortcut_list ( & self ) -> ( & str , Box < [ Shortcut ] > ) {
@@ -229,17 +206,17 @@ impl RunningCommand {
229
206
let writer = pair. master . take_writer ( ) . unwrap ( ) ;
230
207
Self {
231
208
buffer : command_buffer,
232
- command_thread : Some ( command_handle) ,
233
- child_killer : Some ( rx ) ,
209
+ command_thread : Some ( command_handle) . into ( ) ,
210
+ child_killer : rx . into ( ) ,
234
211
_reader_thread : reader_handle,
235
212
pty_master : pair. master ,
236
213
writer,
237
- status : None ,
214
+ status : None . into ( ) ,
238
215
scroll_offset : 0 ,
239
216
}
240
217
}
241
218
242
- fn screen ( & mut self , size : Size ) -> Screen {
219
+ fn screen ( & mut self , size : Rect ) -> Screen {
243
220
// Resize the emulated pty
244
221
self . pty_master
245
222
. resize ( PtySize {
@@ -263,14 +240,16 @@ impl RunningCommand {
263
240
}
264
241
265
242
/// This function will block if the command is not finished
266
- fn get_exit_status ( & mut self ) -> ExitStatus {
267
- if self . command_thread . is_some ( ) {
268
- let handle = self . command_thread . take ( ) . unwrap ( ) ;
269
- let exit_status = handle. join ( ) . unwrap ( ) ;
270
- self . status = Some ( exit_status. clone ( ) ) ;
271
- exit_status
272
- } else {
273
- self . status . as_ref ( ) . unwrap ( ) . clone ( )
243
+ fn wait_command ( & self ) -> ExitStatus {
244
+ let status = { self . status . borrow ( ) . clone ( ) } ;
245
+ match status {
246
+ Some ( status) => status,
247
+ None => {
248
+ let handle = self . command_thread . take ( ) . unwrap ( ) ;
249
+ let exit_status = handle. join ( ) . unwrap ( ) ;
250
+ self . status . replace ( Some ( exit_status. clone ( ) ) ) ;
251
+ exit_status
252
+ }
274
253
}
275
254
}
276
255
@@ -279,6 +258,7 @@ impl RunningCommand {
279
258
if !self . is_finished ( ) {
280
259
let mut killer = self . child_killer . take ( ) . unwrap ( ) . recv ( ) . unwrap ( ) ;
281
260
killer. kill ( ) . unwrap ( ) ;
261
+ self . wait_command ( ) ;
282
262
}
283
263
}
284
264
0 commit comments