diff --git a/Cargo.lock b/Cargo.lock index de61706ff4f54d..fd934529c33ec6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13020,6 +13020,7 @@ dependencies = [ "shellexpand 2.1.2", "smol", "sysinfo", + "task", "thiserror 2.0.12", "toml 0.8.20", "unindent", diff --git a/crates/acp_thread/src/acp_thread.rs b/crates/acp_thread/src/acp_thread.rs index 61486a475c4601..01777f81bf4c5e 100644 --- a/crates/acp_thread/src/acp_thread.rs +++ b/crates/acp_thread/src/acp_thread.rs @@ -2084,6 +2084,7 @@ impl AcpThread { output_byte_limit: Option, cx: &mut Context, ) -> Task>> { + dbg!(&command, &args); let env = match &cwd { Some(dir) => self.project.update(cx, |project, cx| { let worktree = project.find_worktree(dir.as_path(), cx); @@ -2128,6 +2129,7 @@ impl AcpThread { let (task_command, task_args) = ShellBuilder::new(&Shell::Program(shell)) .redirect_stdin_to_dev_null() .build(Some(command.clone()), &args); + dbg!(&task_command, &task_args); let terminal = project .update(cx, |project, cx| { project.create_terminal_task( @@ -2135,7 +2137,7 @@ impl AcpThread { command: Some(task_command), args: task_args, cwd: cwd.clone(), - env, + env: dbg!(env), ..Default::default() }, cx, diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 2b6c9bfe6c45bf..f91faa54a5700a 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -66,7 +66,7 @@ pub use environment::ProjectEnvironment; #[cfg(test)] use futures::future::join_all; use futures::{ - StreamExt, + FutureExt as _, StreamExt, channel::mpsc::{self, UnboundedReceiver}, future::{Shared, try_join_all}, }; @@ -1894,12 +1894,32 @@ impl Project { }) } + // FIXME migrate project environment stuff to use proto instead of bailing when not local + pub fn directory_environment( &self, shell: &Shell, abs_path: Arc, cx: &mut App, ) -> Shared>>> { + if let Some(remote_client) = self.remote_client() { + let response = + remote_client + .read(cx) + .proto_client() + .request(proto::GetDirectoryEnvironment { + // FIXME + project_id: REMOTE_SERVER_PROJECT_ID, + shell: Some(shell.clone().to_proto()), + directory: abs_path.to_string_lossy().to_string(), + }); + return cx + .spawn(async move |_| { + let environment = response.await.log_err()?; + Some(environment.environment.into_iter().collect()) + }) + .shared(); + } self.environment.update(cx, |environment, cx| { environment.get_directory_environment_for_shell(shell, abs_path, cx) }) diff --git a/crates/proto/proto/task.proto b/crates/proto/proto/task.proto index 8fc3a6d18e1398..1844087d623cc3 100644 --- a/crates/proto/proto/task.proto +++ b/crates/proto/proto/task.proto @@ -48,3 +48,13 @@ message SpawnInTerminal { map env = 4; optional string cwd = 5; } + +message GetDirectoryEnvironment { + uint64 project_id = 1; + Shell shell = 2; + string directory = 3; +} + +message DirectoryEnvironment { + map environment = 1; +} diff --git a/crates/proto/proto/zed.proto b/crates/proto/proto/zed.proto index 2c8380661a31f3..c2fc764e819f35 100644 --- a/crates/proto/proto/zed.proto +++ b/crates/proto/proto/zed.proto @@ -418,7 +418,10 @@ message Envelope { GitRenameBranch git_rename_branch = 380; - RemoteStarted remote_started = 381; // current max + RemoteStarted remote_started = 381; + + GetDirectoryEnvironment get_directory_environment = 382; + DirectoryEnvironment directory_environment = 383; // current max } reserved 87 to 88; diff --git a/crates/proto/src/proto.rs b/crates/proto/src/proto.rs index 2217cabbba9627..3710d77262f872 100644 --- a/crates/proto/src/proto.rs +++ b/crates/proto/src/proto.rs @@ -319,6 +319,8 @@ messages!( (GitClone, Background), (GitCloneResponse, Background), (ToggleLspLogs, Background), + (GetDirectoryEnvironment, Background), + (DirectoryEnvironment, Background), (GetAgentServerCommand, Background), (AgentServerCommand, Background), (ExternalAgentsUpdated, Background), @@ -497,6 +499,7 @@ request_messages!( (GetDefaultBranch, GetDefaultBranchResponse), (GitClone, GitCloneResponse), (ToggleLspLogs, Ack), + (GetDirectoryEnvironment, DirectoryEnvironment), (GetProcesses, GetProcessesResponse), (GetAgentServerCommand, AgentServerCommand), (RemoteStarted, Ack), @@ -634,6 +637,7 @@ entity_messages!( GitCheckoutFiles, SetIndexText, ToggleLspLogs, + GetDirectoryEnvironment, Push, Fetch, diff --git a/crates/remote_server/Cargo.toml b/crates/remote_server/Cargo.toml index 92777d1a5950cf..37c77299ef4657 100644 --- a/crates/remote_server/Cargo.toml +++ b/crates/remote_server/Cargo.toml @@ -60,6 +60,7 @@ settings.workspace = true shellexpand.workspace = true smol.workspace = true sysinfo.workspace = true +task.workspace = true util.workspace = true watch.workspace = true worktree.workspace = true diff --git a/crates/remote_server/src/headless_project.rs b/crates/remote_server/src/headless_project.rs index 1d5e72ff9bd245..5008cd4efcfcbe 100644 --- a/crates/remote_server/src/headless_project.rs +++ b/crates/remote_server/src/headless_project.rs @@ -50,6 +50,7 @@ pub struct HeadlessProject { pub languages: Arc, pub extensions: Entity, pub git_store: Entity, + pub environment: Entity, // Used mostly to keep alive the toolchain store for RPC handlers. // Local variant is used within LSP store, but that's a separate entity. pub _toolchain_store: Entity, @@ -196,7 +197,7 @@ impl HeadlessProject { let agent_server_store = cx.new(|cx| { let mut agent_server_store = - AgentServerStore::local(node_runtime.clone(), fs.clone(), environment, cx); + AgentServerStore::local(node_runtime.clone(), fs.clone(), environment.clone(), cx); agent_server_store.shared(REMOTE_SERVER_PROJECT_ID, session.clone(), cx); agent_server_store }); @@ -249,6 +250,7 @@ impl HeadlessProject { session.add_entity_request_handler(Self::handle_open_new_buffer); session.add_entity_request_handler(Self::handle_find_search_candidates); session.add_entity_request_handler(Self::handle_open_server_settings); + session.add_entity_request_handler(Self::handle_get_directory_environment); session.add_entity_message_handler(Self::handle_toggle_lsp_logs); session.add_entity_request_handler(BufferStore::handle_update_buffer); @@ -289,6 +291,7 @@ impl HeadlessProject { languages, extensions, git_store, + environment, _toolchain_store: toolchain_store, } } @@ -758,6 +761,26 @@ impl HeadlessProject { Ok(proto::GetProcessesResponse { processes }) } + + async fn handle_get_directory_environment( + this: Entity, + envelope: TypedEnvelope, + mut cx: AsyncApp, + ) -> Result { + let shell = task::Shell::from_proto(envelope.payload.shell.context("missing shell")?)?; + let directory = PathBuf::from(envelope.payload.directory); + let environment = this + .update(&mut cx, |this, cx| { + this.environment.update(cx, |environment, cx| { + environment.get_directory_environment_for_shell(&shell, directory.into(), cx) + }) + })? + .await + .context("failed to get directory environment")? + .into_iter() + .collect(); + Ok(proto::DirectoryEnvironment { environment }) + } } fn prompt_to_proto( diff --git a/crates/task/src/task.rs b/crates/task/src/task.rs index 9f7a10f2c5cace..0b23a1cde0ccde 100644 --- a/crates/task/src/task.rs +++ b/crates/task/src/task.rs @@ -9,6 +9,7 @@ mod task_template; mod vscode_debug_format; mod vscode_format; +use anyhow::Context as _; use collections::{HashMap, HashSet, hash_map}; use gpui::SharedString; use schemars::JsonSchema; @@ -361,6 +362,38 @@ impl Shell { Shell::System => ShellKind::system(), } } + + pub fn from_proto(proto: proto::Shell) -> anyhow::Result { + let shell_type = proto.shell_type.context("invalid shell type")?; + let shell = match shell_type { + proto::shell::ShellType::System(_) => Self::System, + proto::shell::ShellType::Program(program) => Self::Program(program), + proto::shell::ShellType::WithArguments(program) => Self::WithArguments { + program: program.program, + args: program.args, + title_override: None, + }, + }; + Ok(shell) + } + + pub fn to_proto(self) -> proto::Shell { + let shell_type = match self { + Shell::System => proto::shell::ShellType::System(proto::System {}), + Shell::Program(program) => proto::shell::ShellType::Program(program), + Shell::WithArguments { + program, + args, + title_override: _, + } => proto::shell::ShellType::WithArguments(proto::shell::WithArguments { + program, + args, + }), + }; + proto::Shell { + shell_type: Some(shell_type), + } + } } type VsCodeEnvVariable = String; diff --git a/crates/util/src/shell_env.rs b/crates/util/src/shell_env.rs index 3559bf2c78d485..2cf40c58d579e6 100644 --- a/crates/util/src/shell_env.rs +++ b/crates/util/src/shell_env.rs @@ -20,6 +20,7 @@ pub async fn capture( args: &[String], directory: impl AsRef, ) -> Result> { + dbg!(shell_path.as_ref(), &args, directory.as_ref()); #[cfg(windows)] return capture_windows(shell_path.as_ref(), args, directory.as_ref()).await; #[cfg(unix)]