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}