cargo_e/e_tui.rs
1#[cfg(feature = "tui")]
2pub mod tui_interactive {
3 use crate::e_command_builder::CargoCommandBuilder;
4 use crate::e_manifest::maybe_patch_manifest_for_run;
5 use crate::e_processmanager::ProcessManager;
6 use crate::e_prompts::prompt_line;
7 use crate::e_target::CargoTarget;
8 use crate::prelude::*;
9 use crate::{e_bacon, e_findmain, Cli};
10 use crossterm::event::KeyEventKind;
11 use crossterm::event::{poll, read};
12 use crossterm::{
13 event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, MouseEventKind},
14 execute,
15 terminal::{
16 disable_raw_mode, enable_raw_mode, Clear, ClearType, EnterAlternateScreen,
17 LeaveAlternateScreen,
18 },
19 };
20 use ratatui::{
21 backend::CrosstermBackend,
22 layout::{Constraint, Direction, Layout, Rect},
23 style::{Color, Style},
24 text::{Line, Span},
25 widgets::{Block, Borders, List, ListItem, ListState},
26 Terminal,
27 };
28 use std::{collections::HashSet, thread, time::Duration};
29 use sysinfo::{ProcessRefreshKind, ProcessesToUpdate, System};
30
31 /// Flushes the input event queue, ignoring any stray Enter key events.
32 pub fn flush_input() -> Result<(), Box<dyn std::error::Error>> {
33 while poll(Duration::from_millis(0))? {
34 if let Event::Key(key_event) = read()? {
35 // Optionally, log or ignore specific keys.
36 if key_event.code == KeyCode::Enter {
37 // Filtering out stray Return keys.
38 continue;
39 }
40 // You can also choose to ignore all events:
41 continue;
42 }
43 }
44 Ok(())
45 }
46
47 /// Try to collect an escape sequence if the first event is Esc.
48 /// Returns Some(arrow) if the sequence matches an arrow key, otherwise None.
49 fn try_collect_arrow_sequence() -> Result<Option<KeyCode>, Box<dyn std::error::Error>> {
50 // Buffer to hold the sequence. We already know the first event is Esc.
51 let mut sequence = vec![];
52 let start = Instant::now();
53 // Give a short window (e.g. 50 ms) to collect additional events.
54 while start.elapsed() < Duration::from_millis(50) {
55 if poll(Duration::from_millis(0))? {
56 if let Event::Key(key) = read()? {
57 // Only consider Press events.
58 if key.kind == KeyEventKind::Press {
59 sequence.push(key);
60 }
61 }
62 }
63 }
64 // Now, an arrow key should have a sequence like: Esc, '[' and then 'A' (or 'B', 'C', 'D').
65 if sequence.len() >= 2 {
66 if sequence[0].code == KeyCode::Char('[') {
67 // Check the third element if available.
68 if let Some(third) = sequence.get(1) {
69 // Compare the character case-insensitively (to handle unexpected modifiers).
70 if let KeyCode::Char(ch) = third.code {
71 let ch = ch.to_ascii_uppercase();
72 return Ok(match ch {
73 'A' => Some(KeyCode::Up),
74 'B' => Some(KeyCode::Down),
75 'C' => Some(KeyCode::Right),
76 'D' => Some(KeyCode::Left),
77 _ => None,
78 });
79 }
80 }
81 }
82 }
83 Ok(None)
84 }
85
86 /// Launches an interactive terminal UI for selecting an example.
87 pub fn launch_tui(
88 manager: Arc<ProcessManager>,
89 cli: &Cli,
90 examples: &[CargoTarget],
91 ) -> Result<(), Box<dyn std::error::Error>> {
92 flush_input()?; // Clear any buffered input (like stray Return keys)
93 let mut exs = examples.to_vec();
94 if exs.is_empty() {
95 println!("No examples found!");
96 return Ok(());
97 }
98 exs.sort_by(|a, b| a.display_name.cmp(&b.display_name));
99 // Determine the directory containing the Cargo.toml at runtime.
100 let manifest_dir = crate::e_manifest::find_manifest_dir()?;
101 let history_path = manifest_dir.join("run_history.txt");
102 let mut run_history: HashSet<String> = HashSet::new();
103 if let Ok(contents) = fs::read_to_string(&history_path) {
104 for line in contents.lines() {
105 if !line.trim().is_empty() {
106 run_history.insert(line.trim().to_string());
107 }
108 }
109 }
110
111 enable_raw_mode()?;
112 let mut stdout = io::stdout();
113 execute!(
114 stdout,
115 EnterAlternateScreen,
116 EnableMouseCapture,
117 Clear(ClearType::All)
118 )?;
119 let backend = CrosstermBackend::new(stdout);
120 let mut terminal = Terminal::new(backend)?;
121
122 let mut list_state = ListState::default();
123 list_state.select(Some(0));
124 let mut exit_hover = false;
125 let mut run_history_map = crate::e_parser::read_run_history(&history_path);
126 'main_loop: loop {
127 terminal.draw(|f| {
128 let size = f.area();
129 let area = Rect::new(0, 0, size.width, size.height);
130 let chunks = Layout::default()
131 .direction(Direction::Vertical)
132 .margin(2)
133 .constraints([Constraint::Min(0)].as_ref())
134 .split(area);
135 let list_area = chunks[0];
136
137 let left_text = format!("Select target ({} found)", exs.len());
138 let separator = " ┃ ";
139 let right_text = "q to EXIT";
140 let title_line = if exit_hover {
141 Line::from(vec![
142 Span::raw(left_text),
143 Span::raw(separator),
144 Span::styled(right_text, Style::default().fg(Color::Yellow)),
145 ])
146 } else {
147 Line::from(vec![
148 Span::raw(left_text),
149 Span::raw(separator),
150 Span::styled("q to ", Style::default().fg(Color::White)),
151 Span::styled("EXIT", Style::default().fg(Color::Red)),
152 ])
153 };
154 let pad_width = exs.len().to_string().len();
155 // Compute the maximum width for the ex.kind values.
156 let max_kind_width = exs
157 .iter()
158 .map(|ex| format!("{:?}", ex.kind).len())
159 .max()
160 .unwrap_or(0);
161 let mut line_number = 0;
162 let block = Block::default().borders(Borders::ALL).title(title_line);
163 let items: Vec<ListItem> = exs
164 .iter()
165 .map(|ex| {
166 let kind_str = format!("{:?}", ex.kind);
167 let mut display = format!(
168 "{:>width$}: [{:>max_kind_width$}] {}",
169 line_number,
170 kind_str,
171 &ex.display_name,
172 width = pad_width,
173 max_kind_width = max_kind_width
174 );
175 if let Some(count) = run_history_map.get(&ex.name) {
176 display.push_str(&format!(
177 "({} run{})",
178 count,
179 if *count == 1 { "" } else { "s" }
180 ));
181 }
182 line_number = line_number + 1;
183 let mut item = ListItem::new(display);
184 if run_history_map.get(&ex.name).is_some() {
185 item = item.style(Style::default().fg(Color::Blue));
186 }
187 item
188 })
189 .collect();
190 let list = List::new(items)
191 .block(block)
192 .highlight_style(Style::default().fg(Color::Yellow))
193 .highlight_symbol(">> ");
194 f.render_stateful_widget(list, list_area, &mut list_state);
195 })?;
196
197 {
198 match event::read()? {
199 Event::Key(key) => {
200 // Only process key-press events.
201 if key.kind == KeyEventKind::Press {
202 if key.code == KeyCode::Char('c')
203 && key.modifiers.contains(event::KeyModifiers::CONTROL)
204 {
205 eprintln!(
206 "CTRL-C detected within TUI event loop. Terminating processes."
207 );
208 manager.kill_all();
209 // Optionally, exit the TUI or perform additional cleanup.
210 }
211 // Check if we might be starting an escape sequence for an arrow key.
212 if key.code == KeyCode::Esc {
213 // Try to collect the rest of the sequence.
214 if let Some(arrow_code) = try_collect_arrow_sequence()? {
215 match arrow_code {
216 KeyCode::Up => {
217 let new_index = match list_state.selected() {
218 Some(0) | None => 0,
219 Some(i) => i.saturating_sub(1),
220 };
221 list_state.select(Some(new_index));
222 }
223 KeyCode::Down => {
224 let new_index = match list_state.selected() {
225 Some(i) if i >= exs.len() - 1 => i,
226 Some(i) => i + 1,
227 None => 0,
228 };
229 list_state.select(Some(new_index));
230 }
231 KeyCode::Left => {
232 // Handle left arrow if needed.
233 }
234 KeyCode::Right => {
235 // Handle right arrow if needed.
236 }
237 _ => {}
238 }
239 // We've handled the arrow, so skip further processing.
240 continue;
241 } else {
242 // No follow-up sequence—treat it as a standalone Esc if needed.
243 // For example, you might decide not to exit on Esc now.
244 // println!("Standalone Esc detected (ignoring).");
245 continue;
246 }
247 }
248 match key.code {
249 KeyCode::Char('q') => {
250 // Exit the TUI mode when 'q' is pressed.
251 println!("Exiting TUI mode...");
252 break 'main_loop;
253 }
254 KeyCode::Down => {
255 let i = match list_state.selected() {
256 Some(i) if i >= exs.len() - 1 => i,
257 Some(i) => i + 1,
258 None => 0,
259 };
260 list_state.select(Some(i));
261 thread::sleep(Duration::from_millis(50));
262 }
263 KeyCode::Up => {
264 let i = match list_state.selected() {
265 Some(0) | None => 0,
266 Some(i) => i - 1,
267 };
268 list_state.select(Some(i));
269 thread::sleep(Duration::from_millis(50));
270 }
271 KeyCode::PageDown => {
272 // Compute page size based on the terminal's current height.
273 let page = terminal
274 .size()
275 .map(|r| r.height.saturating_sub(4)) // subtract borders/margins; adjust as needed
276 .unwrap_or(5)
277 as usize;
278 let current = list_state.selected().unwrap_or(0);
279 let new = std::cmp::min(current + page, exs.len() - 1);
280 list_state.select(Some(new));
281 }
282 KeyCode::PageUp => {
283 let page = terminal
284 .size()
285 .map(|r| r.height.saturating_sub(4))
286 .unwrap_or(5)
287 as usize;
288 let current = list_state.selected().unwrap_or(0);
289 let new = current.saturating_sub(page);
290 list_state.select(Some(new));
291 }
292 KeyCode::Char('b') => {
293 if let Some(selected) = list_state.selected() {
294 let sample = &examples[selected];
295 // Run bacon in detached mode. Extra arguments can be added if needed.
296 if let Err(e) = e_bacon::run_bacon(sample, &Vec::new()) {
297 eprintln!("Error running bacon: {}", e);
298 } else {
299 println!("Bacon launched for sample: {}", sample.name);
300 }
301 reinit_terminal(&mut terminal)?;
302 }
303 }
304 KeyCode::Char('e') => {
305 if let Some(selected) = list_state.selected() {
306 // Disable raw mode for debug printing.
307 crossterm::terminal::disable_raw_mode()?;
308 crossterm::execute!(
309 std::io::stdout(),
310 crossterm::terminal::LeaveAlternateScreen
311 )?;
312 // When 'e' is pressed, attempt to open the sample in VSCode.
313 let sample = &exs[selected];
314 println!(
315 "Opening VSCode for path: {}",
316 sample
317 .manifest_path
318 .to_str()
319 .unwrap_or_default()
320 .to_owned()
321 );
322 // Here we block on the asynchronous open_vscode call.
323 // futures::executor::block_on(open_vscode(Path::new(&sample.manifest_path)));
324 futures::executor::block_on(
325 e_findmain::open_vscode_for_sample(sample),
326 );
327 std::thread::sleep(std::time::Duration::from_secs(5));
328 reinit_terminal(&mut terminal)?;
329 }
330 }
331 KeyCode::Char('i') => {
332 if let Some(selected) = list_state.selected() {
333 // Disable raw mode for debug printing.
334 crossterm::terminal::disable_raw_mode()?;
335 crossterm::execute!(
336 std::io::stdout(),
337 crossterm::terminal::LeaveAlternateScreen
338 )?;
339 let target = &exs[selected];
340 println!("Target: {:?}", target);
341 futures::executor::block_on(
342 crate::e_runner::open_ai_summarize_for_target(target),
343 );
344 prompt_line("", 120).ok();
345 reinit_terminal(&mut terminal)?;
346 }
347 }
348 // KeyCode::Char('v') => {
349 // if let Some(selected) = list_state.selected() {
350 // // Disable raw mode for debug printing.
351 // crossterm::terminal::disable_raw_mode()?;
352 // crossterm::execute!(
353 // std::io::stdout(),
354 // crossterm::terminal::LeaveAlternateScreen
355 // )?;
356 // // When 'e' is pressed, attempt to open the sample in VSCode.
357 // let sample = &examples[selected];
358 // println!("Opening VIM for path: {}", sample.manifest_path);
359 // // Here we block on the asynchronous open_vscode call.
360 // // futures::executor::block_on(open_vscode(Path::new(&sample.manifest_path)));
361 // e_findmain::open_vim_for_sample(sample);
362 // std::thread::sleep(std::time::Duration::from_secs(5));
363 // reinit_terminal(&mut terminal)?;
364 // }
365 // }
366 KeyCode::Enter => {
367 if let Some(selected) = list_state.selected() {
368 run_piece(
369 manager.clone(),
370 &exs,
371 selected,
372 &history_path,
373 &mut run_history,
374 &mut terminal,
375 cli,
376 )?;
377 run_history_map =
378 crate::e_parser::read_run_history(&history_path);
379 reinit_terminal(&mut terminal)?;
380 }
381 }
382 _ => {
383 //println!("Unhandled key event: {:?}", key.code);
384 }
385 }
386 }
387 }
388 Event::Mouse(mouse_event) => {
389 let size = terminal.size()?;
390 let area = Rect::new(0, 0, size.width, size.height);
391 let chunks = Layout::default()
392 .direction(Direction::Vertical)
393 .margin(2)
394 .constraints([Constraint::Min(0)].as_ref())
395 .split(area);
396 let list_area = chunks[0];
397 let title_row = list_area.y;
398 let title_start = list_area.x + 2;
399 let left_text = format!("Select target ({} found)", exs.len());
400 let separator = " ┃ ";
401 let right_text = "q to EXIT";
402 let offset = (left_text.len() + separator.len()) as u16;
403 let right_region_start = title_start + offset;
404 let right_region_end = right_region_start + (right_text.len() as u16);
405
406 match mouse_event.kind {
407 MouseEventKind::ScrollDown => {
408 let current = list_state.selected().unwrap_or(0);
409 let new = std::cmp::min(current + 1, exs.len() - 1);
410 list_state.select(Some(new));
411 }
412 MouseEventKind::ScrollUp => {
413 let current = list_state.selected().unwrap_or(0);
414 let new = if current == 0 { 0 } else { current - 1 };
415 list_state.select(Some(new));
416 }
417
418 MouseEventKind::Moved => {
419 if mouse_event.row == title_row {
420 exit_hover = mouse_event.column >= right_region_start
421 && mouse_event.column < right_region_end;
422 } else {
423 exit_hover = false;
424 let inner_y = list_area.y + 1;
425 let inner_height = list_area.height.saturating_sub(2);
426 if mouse_event.column > list_area.x + 1
427 && mouse_event.column < list_area.x + list_area.width - 1
428 && mouse_event.row >= inner_y
429 && mouse_event.row < inner_y + inner_height
430 {
431 let index = (mouse_event.row - inner_y) as usize;
432 if index < exs.len() {
433 list_state.select(Some(index));
434 }
435 }
436 }
437 }
438 MouseEventKind::Down(_) => {
439 if mouse_event.row == title_row
440 && mouse_event.column >= right_region_start
441 && mouse_event.column < right_region_end
442 {
443 println!("Exiting TUI mode...");
444 break 'main_loop;
445 }
446 let inner_y = list_area.y + 1;
447 let inner_height = list_area.height.saturating_sub(2);
448 if mouse_event.column > list_area.x + 1
449 && mouse_event.column < list_area.x + list_area.width - 1
450 && mouse_event.row >= inner_y
451 && mouse_event.row < inner_y + inner_height
452 {
453 let index = (mouse_event.row - inner_y) as usize;
454 if index < exs.len() {
455 list_state.select(Some(index));
456 run_piece(
457 manager.clone(),
458 &exs.clone(),
459 index,
460 &history_path,
461 &mut run_history,
462 &mut terminal,
463 cli,
464 )?;
465 run_history_map =
466 crate::e_parser::read_run_history(&history_path);
467 }
468 }
469 }
470 _ => {}
471 }
472 }
473 _ => {}
474 }
475 }
476 }
477
478 disable_raw_mode()?;
479 let mut stdout = io::stdout();
480 execute!(
481 stdout,
482 LeaveAlternateScreen,
483 DisableMouseCapture,
484 Clear(ClearType::All)
485 )?;
486 terminal.show_cursor()?;
487 Ok(())
488 }
489
490 /// Reinitializes the terminal: enables raw mode, enters the alternate screen,
491 /// enables mouse capture, clears the screen, and creates a new Terminal instance.
492 /// This function updates the provided terminal reference.
493 pub fn reinit_terminal(
494 terminal: &mut Terminal<CrosstermBackend<io::Stdout>>,
495 ) -> Result<(), Box<dyn Error>> {
496 enable_raw_mode()?;
497 let mut stdout = io::stdout();
498 execute!(
499 stdout,
500 EnterAlternateScreen,
501 EnableMouseCapture,
502 Clear(ClearType::All)
503 )?;
504 *terminal = Terminal::new(CrosstermBackend::new(stdout))?;
505 flush_input()?; // Clear any buffered input after reinitializing the terminal.
506 Ok(())
507 }
508
509 /// Runs the given example (or binary) target. It leaves TUI mode, spawns a cargo process,
510 /// installs a Ctrl+C handler to kill the process, waits for it to finish, updates history,
511 /// flushes stray input, and then reinitializes the terminal.
512 pub fn run_piece(
513 manager: Arc<crate::e_processmanager::ProcessManager>,
514 examples: &[CargoTarget],
515 index: usize,
516 history_path: &Path,
517 _run_history: &mut HashSet<String>,
518 terminal: &mut Terminal<CrosstermBackend<io::Stdout>>,
519 cli: &Cli,
520 ) -> Result<(), Box<dyn Error>> {
521 let target = &examples[index];
522 // Leave TUI mode before running the target.
523 disable_raw_mode()?;
524 execute!(
525 terminal.backend_mut(),
526 LeaveAlternateScreen,
527 crossterm::event::DisableMouseCapture
528 )?;
529 terminal.show_cursor()?;
530
531 let manifest_path = PathBuf::from(target.manifest_path.clone());
532 let builder = CargoCommandBuilder::new(&manifest_path, &cli.subcommand, cli.filter)
533 .with_target(target)
534 .with_required_features(&manifest_path, target)
535 .with_cli(cli);
536 let cmd = builder.build_command();
537
538 // Set current directory appropriately.
539 // if target.kind == TargetKind::ManifestTauri {
540 // let manifest_dir = manifest_path.parent().expect("Expected parent directory");
541 // cmd.current_dir(manifest_dir);
542 // } else if target.extended {
543 // if let Some(dir) = manifest_path.parent() {
544 // cmd.current_dir(dir);
545 // }
546 // }
547
548 println!("Running command: {:?}", cmd);
549 // If the target is extended, we want to run it from its directory.
550 if target.extended {
551 Path::new(&manifest_path).parent().map(|p| p.to_owned())
552 } else {
553 None
554 };
555
556 // Before spawning, patch the manifest if needed.
557 let manifest_path_obj = Path::new(&manifest_path);
558 let backup = maybe_patch_manifest_for_run(manifest_path_obj)?;
559
560 // // // Build the command.
561 // // let mut cmd = Command::new("cargo");
562 // // cmd.args(&args);
563 // // if let Some(ref dir) = current_dir {
564 // // cmd.current_dir(dir);
565 // // }
566 // // Convert command args into &str slices for spawn_cargo_process.
567 // // (Assuming spawn_cargo_process accepts a slice of &str.)
568 // let owned_args: Vec<String> = cmd
569 // .get_args()
570 // .map(|arg| arg.to_string_lossy().to_string())
571 // .collect();
572 // // Now create a vector of &str references valid as long as `owned_args` is in scope:
573 // let args_ref: Vec<&str> = owned_args.iter().map(|s| s.as_str()).collect();
574
575 // // let args_ref: Vec<&str> = args.iter().map(|s| &**s).collect();
576 // let mut child = crate::e_runner::spawn_cargo_process(&args_ref)?;
577
578 let pid = Arc::new(builder).run(|_pid, handle| {
579 manager.register(handle);
580 })?;
581 // let ret=manager.wait(pid, None)?;
582 let handle = manager.get(pid).unwrap().clone();
583 // let mut child = cmd.spawn()?;
584 // if cli.print_instruction {
585 // println!("Process started. Press Ctrl+C to terminate or 'd' to detach...");
586 // }
587 let mut update_history = true;
588 let mut status_code: i32 = -255;
589 let mut detached = false;
590 let shared_child = handle.clone();
591 let mut system = System::new_all();
592 std::thread::sleep(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL);
593 // Refresh CPU usage to get actual value.
594 system.refresh_processes_specifics(
595 ProcessesToUpdate::All,
596 true,
597 ProcessRefreshKind::nothing().with_cpu(),
598 );
599 // Now we enter an event loop, periodically checking if the child has exited
600 // and polling for keyboard input.
601 {
602 let mut stdout = std::io::stdout();
603 crossterm::execute!(stdout, crossterm::cursor::Hide)?;
604 }
605 let _cursor_guard = crate::e_processmanager::CursorGuard;
606
607 const SAMPLE_INTERVAL: usize = 10; // Update status every 10 iterations
608
609 let mut sample_count: usize = 0;
610 loop {
611 sample_count += 1;
612
613 // Check if the process has finished.
614 {
615 let mut child_guard = shared_child.lock().unwrap();
616 if let Some(status) = child_guard.child.try_wait()? {
617 let status_code = status.code().unwrap_or(1);
618 println!("Process exited with status: {}", status_code);
619 break;
620 }
621 }
622 // // Check if the child process has finished.
623 if let Some(status) = handle.clone().lock().unwrap().child.try_wait()? {
624 status_code = status.code().unwrap_or(1);
625
626 println!("Process exited with status: {}", status_code);
627 // println!("HERE IS THE RESULT!{} {:?}",pid,manager.get(pid));
628 // manager.print_shortened_output();
629 manager.print_compact();
630 break;
631 }
632 // Poll for input events with a 100ms timeout.
633 if event::poll(Duration::from_millis(100))? {
634 if let Event::Key(key_event) = event::read()? {
635 if key_event.code == KeyCode::Char('c')
636 && key_event.modifiers.contains(event::KeyModifiers::CONTROL)
637 {
638 if cli.print_instruction {
639 println!("Ctrl+C detected in event loop, killing process...");
640 }
641 shared_child.lock().expect("expected child").child.kill()?;
642 update_history = false; // do not update history if cancelled
643 // Optionally, you can also wait for the child after killing.
644 let status = shared_child
645 .lock()
646 .expect("shared child can't lock")
647 .child
648 .wait()?;
649 status_code = status.code().unwrap_or(1);
650 break;
651 } else if key_event.code == KeyCode::Char('d') && key_event.modifiers.is_empty()
652 {
653 if cli.print_instruction {
654 println!(
655 "'d' pressed; detaching process. Process will continue running."
656 );
657 }
658 detached = true;
659 update_history = false;
660 // Do not kill or wait on the child.
661 // Break out of the loop immediately.
662 // We can optionally leave the process running.
663 status_code = 0;
664 break;
665 }
666 }
667 }
668
669 // Only update the status display every SAMPLE_INTERVAL iterations.
670 if sample_count % SAMPLE_INTERVAL == 0 {
671 // system.refresh_all();
672
673 // Refresh CPU usage to get actual value.
674 system.refresh_processes_specifics(
675 ProcessesToUpdate::All,
676 true,
677 ProcessRefreshKind::nothing().with_cpu(),
678 );
679 let status_display = ProcessManager::format_process_status(
680 pid,
681 &handle,
682 &system,
683 &target,
684 (index + 1, examples.len()),
685 );
686 ProcessManager::update_status_line(&status_display, true).ok();
687 }
688 }
689 // Restore the manifest if it was patched.
690 if let Some(original) = backup {
691 fs::write(manifest_path_obj, original)?;
692 }
693 // Wrap the child process so that we can share it with our Ctrl+C handler.
694 // let child_arc = Arc::new(Mutex::new(child));
695 // let child_for_handler = Arc::clone(&child_arc);
696
697 // Set up a Ctrl+C handler to kill the spawned process.
698 // ctrlc::set_handler(move || {
699 // eprintln!("Ctrl+C pressed, terminating process...");
700 // if let Ok(mut child) = child_for_handler.lock() {
701 // let _ = child.kill();
702 // }
703 // })?;
704
705 // Wait for the process to finish.
706 // let status = child_arc.lock().unwrap().wait()?;
707 // println!("Process exited with status: {:?}", status.code());
708
709 if !detached {
710 // Only update run history if update_history is true and exit code is zero.
711 if update_history && status_code == 0 {
712 use std::io::Write;
713 let mut file = fs::OpenOptions::new()
714 .create(true)
715 .append(true)
716 .open(history_path)?;
717 // Append a newline-separated entry.
718 writeln!(file, "{}", target.name)?;
719 }
720 let message = if cli.print_exit_code {
721 format!("Exitcode {:?}. Press any key to continue...", status_code)
722 } else {
723 "".to_string()
724 };
725 // PROMPT let _ = crate::e_prompts::prompt(&message, cli.wait)?;
726 }
727
728 reinit_terminal(terminal)?; // Reinitialize the terminal after running the target.
729
730 // // Flush stray input events.
731 // while event::poll(std::time::Duration::from_millis(0))? {
732 // let _ = event::read()?;
733 // }
734 // std::thread::sleep(std::time::Duration::from_millis(50));
735
736 // // // Reinitialize the terminal.
737 // enable_raw_mode()?;
738 // let mut stdout = io::stdout();
739 // execute!(
740 // stdout,
741 // EnterAlternateScreen,
742 // crossterm::event::EnableMouseCapture,
743 // Clear(ClearType::All)
744 // )?;
745 // *terminal = Terminal::new(CrosstermBackend::new(stdout))?;
746 Ok(())
747 }
748}