From f61abe02471016d866a33a6c8b2480aa74751f38 Mon Sep 17 00:00:00 2001 From: Aleksei Gusev Date: Thu, 11 Jul 2024 17:50:00 +0300 Subject: [PATCH] Pass `hold: true` to Alacritty for tasks (#13898) It seems `hold: false` causes alacritty to close the channel earlier, without waiting for the output from the child command to go to Zed. Fixes [#13683](https://github.com/zed-industries/zed/issues/13683) Release Notes: - Fixed loosing output of a spawned task ([#13683](https://github.com/zed-industries/zed/issues/13683)). [Screencast from 2024-07-06 18-28-56.webm](https://github.com/zed-industries/zed/assets/39293/4ebef8b5-7c0d-46be-9341-4ac0d809458d) --- crates/terminal/src/terminal.rs | 2 +- crates/zed/src/zed.rs | 97 +++++++++++++++++++++++++++++++-- 2 files changed, 93 insertions(+), 6 deletions(-) diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index 6975f5cc8e..d66be6c474 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -340,7 +340,7 @@ impl TerminalBuilder { alacritty_terminal::tty::Options { shell: alac_shell, working_directory: working_directory.clone(), - hold: false, + hold: !matches!(shell.clone(), Shell::System), env: env.into_iter().collect(), } }; diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 16a3c14107..28eee430ee 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -958,8 +958,9 @@ fn open_settings_file( #[cfg(test)] mod tests { use super::*; + use anyhow::anyhow; use assets::Assets; - use collections::HashSet; + use collections::{HashMap, HashSet}; use editor::{display_map::DisplayRow, scroll::Autoscroll, DisplayPoint, Editor}; use gpui::{ actions, Action, AnyWindowHandle, AppContext, AssetSource, BorrowAppContext, Entity, @@ -970,6 +971,7 @@ mod tests { use serde_json::json; use settings::{handle_settings_file_changes, watch_config_file, SettingsStore}; use std::path::{Path, PathBuf}; + use task::{RevealStrategy, SpawnInTerminal}; use theme::{ThemeRegistry, ThemeSettings}; use workspace::{ item::{Item, ItemHandle}, @@ -3167,11 +3169,96 @@ mod tests { cx.run_until_parked(); } - fn init_test(cx: &mut TestAppContext) -> Arc { - cx.update(|cx| { - env_logger::builder().is_test(true).try_init().ok(); + #[gpui::test] + async fn test_spawn_terminal_task_real_fs(cx: &mut TestAppContext) { + let mut app_state = cx.update(|cx| AppState::test(cx)); + let state = Arc::get_mut(&mut app_state).unwrap(); + state.fs = Arc::new(fs::RealFs::default()); + let app_state = init_test_with_state(cx, app_state); - let mut app_state = AppState::test(cx); + cx.executor().allow_parking(); + let project_root = util::test::temp_tree(json!({ + "sample.txt": "" + })); + + let spawn_in_terminal = SpawnInTerminal { + command: "echo SAMPLE-OUTPUT".to_string(), + cwd: None, + env: HashMap::default(), + id: task::TaskId(String::from("sample-id")), + full_label: String::from("sample-full_label"), + label: String::from("sample-label"), + args: vec![], + command_label: String::from("sample-command_label"), + use_new_terminal: false, + allow_concurrent_runs: false, + reveal: RevealStrategy::Always, + }; + let project = Project::test(app_state.fs.clone(), [project_root.path()], cx).await; + let window = cx.add_window(|cx| Workspace::test_new(project, cx)); + cx.run_until_parked(); + cx.update(|cx| { + window + .update(cx, |_workspace, cx| { + cx.emit(workspace::Event::SpawnTask(spawn_in_terminal)); + }) + .unwrap(); + }); + cx.run_until_parked(); + + run_until(|| { + cx.update(|cx| { + window + .read_with(cx, |workspace, cx| { + let terminal = workspace + .project() + .read(cx) + .local_terminal_handles() + .first() + .unwrap() + .upgrade() + .unwrap() + .read(cx); + terminal + .last_n_non_empty_lines(99) + .join("") + .contains("SAMPLE-OUTPUT") + }) + .unwrap() + }) + }) + .await; + } + + async fn run_until(predicate: impl Fn() -> bool) { + let timer = async { smol::Timer::after(std::time::Duration::from_secs(3)).await }; + + use futures::FutureExt as _; + use smol::future::FutureExt as _; + + async { + loop { + if predicate() { + return Ok(()); + } + smol::Timer::after(std::time::Duration::from_millis(10)).await; + } + } + .race(timer.map(|_| Err(anyhow!("condition timed out")))) + .await + .unwrap(); + } + + fn init_test(cx: &mut TestAppContext) -> Arc { + init_test_with_state(cx, cx.update(|cx| AppState::test(cx))) + } + + fn init_test_with_state( + cx: &mut TestAppContext, + mut app_state: Arc, + ) -> Arc { + cx.update(move |cx| { + env_logger::builder().is_test(true).try_init().ok(); let state = Arc::get_mut(&mut app_state).unwrap(); state.build_window_options = build_window_options;