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}