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