acp_thread: Properly use project terminal API (#38186)
Closes https://github.com/zed-industries/zed/issues/35603 Release Notes: - Fixed shell selection for terminal tool
This commit is contained in:
@@ -52,7 +52,7 @@ pub fn init(http_client: Arc<HttpClientWithUrl>, cx: &mut App) {
|
||||
assistant_tool::init(cx);
|
||||
|
||||
let registry = ToolRegistry::global(cx);
|
||||
registry.register_tool(TerminalTool::new(cx));
|
||||
registry.register_tool(TerminalTool);
|
||||
registry.register_tool(CreateDirectoryTool);
|
||||
registry.register_tool(CopyPathTool);
|
||||
registry.register_tool(DeletePathTool);
|
||||
|
||||
@@ -6,7 +6,7 @@ use action_log::ActionLog;
|
||||
use agent_settings;
|
||||
use anyhow::{Context as _, Result, anyhow};
|
||||
use assistant_tool::{Tool, ToolCard, ToolResult, ToolUseStatus};
|
||||
use futures::{FutureExt as _, future::Shared};
|
||||
use futures::FutureExt as _;
|
||||
use gpui::{
|
||||
AnyWindowHandle, App, AppContext, Empty, Entity, EntityId, Task, TextStyleRefinement,
|
||||
WeakEntity, Window,
|
||||
@@ -26,11 +26,12 @@ use std::{
|
||||
sync::Arc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use task::{Shell, ShellBuilder};
|
||||
use terminal_view::TerminalView;
|
||||
use theme::ThemeSettings;
|
||||
use ui::{CommonAnimationExt, Disclosure, Tooltip, prelude::*};
|
||||
use util::{
|
||||
ResultExt, get_system_shell, markdown::MarkdownInlineCode, size::format_file_size,
|
||||
ResultExt, get_default_system_shell, markdown::MarkdownInlineCode, size::format_file_size,
|
||||
time::duration_alt_display,
|
||||
};
|
||||
use workspace::Workspace;
|
||||
@@ -45,29 +46,10 @@ pub struct TerminalToolInput {
|
||||
cd: String,
|
||||
}
|
||||
|
||||
pub struct TerminalTool {
|
||||
determine_shell: Shared<Task<String>>,
|
||||
}
|
||||
pub struct TerminalTool;
|
||||
|
||||
impl TerminalTool {
|
||||
pub const NAME: &str = "terminal";
|
||||
|
||||
pub(crate) fn new(cx: &mut App) -> Self {
|
||||
let determine_shell = cx.background_spawn(async move {
|
||||
if cfg!(windows) {
|
||||
return get_system_shell();
|
||||
}
|
||||
|
||||
if which::which("bash").is_ok() {
|
||||
"bash".into()
|
||||
} else {
|
||||
get_system_shell()
|
||||
}
|
||||
});
|
||||
Self {
|
||||
determine_shell: determine_shell.shared(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Tool for TerminalTool {
|
||||
@@ -135,19 +117,6 @@ impl Tool for TerminalTool {
|
||||
Ok(dir) => dir,
|
||||
Err(err) => return Task::ready(Err(err)).into(),
|
||||
};
|
||||
let program = self.determine_shell.clone();
|
||||
let command = if cfg!(windows) {
|
||||
format!("$null | & {{{}}}", input.command.replace("\"", "'"))
|
||||
} else if let Some(cwd) = working_dir
|
||||
.as_ref()
|
||||
.and_then(|cwd| cwd.as_os_str().to_str())
|
||||
{
|
||||
// Make sure once we're *inside* the shell, we cd into `cwd`
|
||||
format!("(cd {cwd}; {}) </dev/null", input.command)
|
||||
} else {
|
||||
format!("({}) </dev/null", input.command)
|
||||
};
|
||||
let args = vec!["-c".into(), command];
|
||||
|
||||
let cwd = working_dir.clone();
|
||||
let env = match &working_dir {
|
||||
@@ -156,6 +125,11 @@ impl Tool for TerminalTool {
|
||||
}),
|
||||
None => Task::ready(None).shared(),
|
||||
};
|
||||
let remote_shell = project.update(cx, |project, cx| {
|
||||
project
|
||||
.remote_client()
|
||||
.and_then(|r| r.read(cx).default_system_shell())
|
||||
});
|
||||
|
||||
let env = cx.spawn(async move |_| {
|
||||
let mut env = env.await.unwrap_or_default();
|
||||
@@ -171,8 +145,13 @@ impl Tool for TerminalTool {
|
||||
let task = cx.background_spawn(async move {
|
||||
let env = env.await;
|
||||
let pty_system = native_pty_system();
|
||||
let program = program.await;
|
||||
let mut cmd = CommandBuilder::new(program);
|
||||
let (command, args) = ShellBuilder::new(
|
||||
remote_shell.as_deref(),
|
||||
&Shell::Program(get_default_system_shell()),
|
||||
)
|
||||
.redirect_stdin_to_dev_null()
|
||||
.build(Some(input.command.clone()), &[]);
|
||||
let mut cmd = CommandBuilder::new(command);
|
||||
cmd.args(args);
|
||||
for (k, v) in env {
|
||||
cmd.env(k, v);
|
||||
@@ -208,16 +187,22 @@ impl Tool for TerminalTool {
|
||||
};
|
||||
};
|
||||
|
||||
let command = input.command.clone();
|
||||
let terminal = cx.spawn({
|
||||
let project = project.downgrade();
|
||||
async move |cx| {
|
||||
let program = program.await;
|
||||
let (command, args) = ShellBuilder::new(
|
||||
remote_shell.as_deref(),
|
||||
&Shell::Program(get_default_system_shell()),
|
||||
)
|
||||
.redirect_stdin_to_dev_null()
|
||||
.build(Some(input.command), &[]);
|
||||
let env = env.await;
|
||||
project
|
||||
.update(cx, |project, cx| {
|
||||
project.create_terminal_task(
|
||||
task::SpawnInTerminal {
|
||||
command: Some(program),
|
||||
command: Some(command),
|
||||
args,
|
||||
cwd,
|
||||
env,
|
||||
@@ -230,14 +215,8 @@ impl Tool for TerminalTool {
|
||||
}
|
||||
});
|
||||
|
||||
let command_markdown = cx.new(|cx| {
|
||||
Markdown::new(
|
||||
format!("```bash\n{}\n```", input.command).into(),
|
||||
None,
|
||||
None,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
let command_markdown =
|
||||
cx.new(|cx| Markdown::new(format!("```bash\n{}\n```", command).into(), None, None, cx));
|
||||
|
||||
let card = cx.new(|cx| {
|
||||
TerminalToolCard::new(
|
||||
@@ -288,7 +267,7 @@ impl Tool for TerminalTool {
|
||||
let previous_len = content.len();
|
||||
let (processed_content, finished_with_empty_output) = process_content(
|
||||
&content,
|
||||
&input.command,
|
||||
&command,
|
||||
exit_status.map(portable_pty::ExitStatus::from),
|
||||
);
|
||||
|
||||
@@ -740,7 +719,6 @@ mod tests {
|
||||
if cfg!(windows) {
|
||||
return;
|
||||
}
|
||||
|
||||
init_test(&executor, cx);
|
||||
|
||||
let fs = Arc::new(RealFs::new(None, executor));
|
||||
@@ -763,7 +741,7 @@ mod tests {
|
||||
};
|
||||
let result = cx.update(|cx| {
|
||||
TerminalTool::run(
|
||||
Arc::new(TerminalTool::new(cx)),
|
||||
Arc::new(TerminalTool),
|
||||
serde_json::to_value(input).unwrap(),
|
||||
Arc::default(),
|
||||
project.clone(),
|
||||
@@ -783,7 +761,6 @@ mod tests {
|
||||
if cfg!(windows) {
|
||||
return;
|
||||
}
|
||||
|
||||
init_test(&executor, cx);
|
||||
|
||||
let fs = Arc::new(RealFs::new(None, executor));
|
||||
@@ -798,7 +775,7 @@ mod tests {
|
||||
|
||||
let check = |input, expected, cx: &mut App| {
|
||||
let headless_result = TerminalTool::run(
|
||||
Arc::new(TerminalTool::new(cx)),
|
||||
Arc::new(TerminalTool),
|
||||
serde_json::to_value(input).unwrap(),
|
||||
Arc::default(),
|
||||
project.clone(),
|
||||
|
||||
Reference in New Issue
Block a user