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 )
542 .with_target(target)
543 .with_cli(cli);
544 let cmd = builder.build_command();
545
546 // Set current directory appropriately.
547 // if target.kind == TargetKind::ManifestTauri {
548 // let manifest_dir = manifest_path.parent().expect("Expected parent directory");
549 // cmd.current_dir(manifest_dir);
550 // } else if target.extended {
551 // if let Some(dir) = manifest_path.parent() {
552 // cmd.current_dir(dir);
553 // }
554 // }
555
556 println!("Running command: {:?}", cmd);
557 // If the target is extended, we want to run it from its directory.
558 if target.extended {
559 Path::new(&manifest_path).parent().map(|p| p.to_owned())
560 } else {
561 None
562 };
563
564 // Before spawning, patch the manifest if needed.
565 let manifest_path_obj = Path::new(&manifest_path);
566 let backup = maybe_patch_manifest_for_run(manifest_path_obj)?;
567
568 // // // Build the command.
569 // // let mut cmd = Command::new("cargo");
570 // // cmd.args(&args);
571 // // if let Some(ref dir) = current_dir {
572 // // cmd.current_dir(dir);
573 // // }
574 // // Convert command args into &str slices for spawn_cargo_process.
575 // // (Assuming spawn_cargo_process accepts a slice of &str.)
576 // let owned_args: Vec<String> = cmd
577 // .get_args()
578 // .map(|arg| arg.to_string_lossy().to_string())
579 // .collect();
580 // // Now create a vector of &str references valid as long as `owned_args` is in scope:
581 // let args_ref: Vec<&str> = owned_args.iter().map(|s| s.as_str()).collect();
582
583 // // let args_ref: Vec<&str> = args.iter().map(|s| &**s).collect();
584 // let mut child = crate::e_runner::spawn_cargo_process(&args_ref)?;
585
586 let pid = Arc::new(builder).run(|pid, handle| {
587 manager.register(pid, handle);
588 })?;
589 // let ret=manager.wait(pid, None)?;
590 let handle = manager.get(pid).unwrap().clone();
591 let start_time = handle.lock().unwrap().start_time;
592 // let mut child = cmd.spawn()?;
593 // if cli.print_instruction {
594 // println!("Process started. Press Ctrl+C to terminate or 'd' to detach...");
595 // }
596 let mut update_history = true;
597 let mut status_code: i32 = -255;
598 let mut detached = false;
599 let shared_child = handle.clone();
600 let system = Arc::new(Mutex::new(System::new_all()));
601 std::thread::sleep(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL);
602 // Refresh CPU usage to get actual value.
603 let mut system_guard = system.lock().unwrap();
604 system_guard.refresh_processes_specifics(
605 ProcessesToUpdate::All,
606 true,
607 ProcessRefreshKind::nothing().with_cpu(),
608 );
609 drop(system_guard);
610 // Now we enter an event loop, periodically checking if the child has exited
611 // and polling for keyboard input.
612 {
613 let mut stdout = std::io::stdout();
614 crossterm::execute!(stdout, crossterm::cursor::Hide)?;
615 }
616 let _cursor_guard = crate::e_processmanager::CursorGuard;
617
618 const SAMPLE_INTERVAL: usize = 10; // Update status every 10 iterations
619
620 let mut sample_count: usize = 0;
621 loop {
622 sample_count += 1;
623
624 // Check if the process has finished.
625 {
626 let mut child_guard = shared_child.lock().unwrap();
627 if let Some(status) = child_guard.child.try_wait()? {
628 let status_code = status.code().unwrap_or(1);
629 println!("Process exited with status: {}", status_code);
630 break;
631 }
632 }
633 // // Check if the child process has finished.
634 if let Some(status) = handle.clone().lock().unwrap().child.try_wait()? {
635 status_code = status.code().unwrap_or(1);
636
637 println!("Process exited with status: {}", status_code);
638 // println!("HERE IS THE RESULT!{} {:?}",pid,manager.get(pid));
639 // manager.print_shortened_output();
640 manager.print_compact();
641 break;
642 }
643 // Poll for input events with a 100ms timeout.
644 if event::poll(Duration::from_millis(100))? {
645 if let Event::Key(key_event) = event::read()? {
646 if key_event.code == KeyCode::Char('c')
647 && key_event.modifiers.contains(event::KeyModifiers::CONTROL)
648 {
649 if cli.print_instruction {
650 println!("Ctrl+C detected in event loop, killing process...");
651 }
652 shared_child.lock().expect("expected child").child.kill()?;
653 update_history = false; // do not update history if cancelled
654 // Optionally, you can also wait for the child after killing.
655 let status = shared_child
656 .lock()
657 .expect("shared child can't lock")
658 .child
659 .wait()?;
660 status_code = status.code().unwrap_or(1);
661 break;
662 } else if key_event.code == KeyCode::Char('d') && key_event.modifiers.is_empty()
663 {
664 if cli.print_instruction {
665 println!(
666 "'d' pressed; detaching process. Process will continue running."
667 );
668 }
669 detached = true;
670 update_history = false;
671 // Do not kill or wait on the child.
672 // Break out of the loop immediately.
673 // We can optionally leave the process running.
674 status_code = 0;
675 break;
676 }
677 }
678 }
679
680 // Only update the status display every SAMPLE_INTERVAL iterations.
681 if sample_count % SAMPLE_INTERVAL == 0 && !cli.no_status_lines {
682 // system.refresh_all();
683 let mut system_guard = system.lock().unwrap();
684 system_guard.refresh_processes_specifics(
685 ProcessesToUpdate::All,
686 true,
687 ProcessRefreshKind::nothing().with_cpu(),
688 );
689 drop(system_guard);
690 // Refresh CPU usage to get actual value.
691 let status_display = ProcessManager::format_process_status(
692 pid,
693 Some(start_time),
694 &target,
695 (index + 1, examples.len()),
696 );
697 ProcessManager::update_status_line(&status_display, true).ok();
698 }
699 }
700 // Restore the manifest if it was patched.
701 if let Some(original) = backup {
702 fs::write(manifest_path_obj, original)?;
703 }
704 // Wrap the child process so that we can share it with our Ctrl+C handler.
705 // let child_arc = Arc::new(Mutex::new(child));
706 // let child_for_handler = Arc::clone(&child_arc);
707
708 // Set up a Ctrl+C handler to kill the spawned process.
709 // ctrlc::set_handler(move || {
710 // eprintln!("Ctrl+C pressed, terminating process...");
711 // if let Ok(mut child) = child_for_handler.lock() {
712 // let _ = child.kill();
713 // }
714 // })?;
715
716 // Wait for the process to finish.
717 // let status = child_arc.lock().unwrap().wait()?;
718 // println!("Process exited with status: {:?}", status.code());
719
720 if !detached {
721 // Only update run history if update_history is true and exit code is zero.
722 if update_history && status_code == 0 {
723 use std::io::Write;
724 let mut file = fs::OpenOptions::new()
725 .create(true)
726 .append(true)
727 .open(history_path)?;
728 // Append a newline-separated entry.
729 writeln!(file, "{}", target.name)?;
730 }
731 // let message = if cli.print_exit_code {
732 // format!("Exitcode {:?}. Press any key to continue...", status_code)
733 // } else {
734 // "".to_string()
735 // };
736 // PROMPT let _ = crate::e_prompts::prompt(&message, cli.wait)?;
737 }
738
739 reinit_terminal(terminal)?; // Reinitialize the terminal after running the target.
740
741 // // Flush stray input events.
742 // while event::poll(std::time::Duration::from_millis(0))? {
743 // let _ = event::read()?;
744 // }
745 // std::thread::sleep(std::time::Duration::from_millis(50));
746
747 // // // Reinitialize the terminal.
748 // enable_raw_mode()?;
749 // let mut stdout = io::stdout();
750 // execute!(
751 // stdout,
752 // EnterAlternateScreen,
753 // crossterm::event::EnableMouseCapture,
754 // Clear(ClearType::All)
755 // )?;
756 // *terminal = Terminal::new(CrosstermBackend::new(stdout))?;
757 Ok(())
758 }
759}