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