debugger: More tidy up for SSH (#28993)

Split `locator` out of DebugTaskDefinition to make it clearer when
location needs to happen.

Release Notes:

- N/A

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
Co-authored-by: Anthony <anthony@zed.dev>
Co-authored-by: Cole Miller <m@cole-miller.net>
This commit is contained in:
Conrad Irwin 2025-04-21 10:00:03 -06:00 committed by GitHub
parent d13cd007a2
commit 9d35f0389d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
57 changed files with 1146 additions and 884 deletions

View file

@ -1,4 +1,4 @@
use std::{cmp, ops::ControlFlow, path::PathBuf, sync::Arc, time::Duration};
use std::{cmp, ops::ControlFlow, path::PathBuf, process::ExitStatus, sync::Arc, time::Duration};
use crate::{
TerminalView, default_working_directory,
@ -9,7 +9,7 @@ use crate::{
use breadcrumbs::Breadcrumbs;
use collections::HashMap;
use db::kvp::KEY_VALUE_STORE;
use futures::future::join_all;
use futures::{channel::oneshot, future::join_all};
use gpui::{
Action, AnyView, App, AsyncApp, AsyncWindowContext, Context, Corner, Entity, EventEmitter,
ExternalPaths, FocusHandle, Focusable, IntoElement, ParentElement, Pixels, Render, Styled,
@ -279,17 +279,9 @@ impl TerminalPanel {
};
if let Some(workspace) = workspace.upgrade() {
terminal_panel
.update_in(&mut cx, |_, window, cx| {
cx.subscribe_in(&workspace, window, |terminal_panel, _, e, window, cx| {
if let workspace::Event::SpawnTask {
action: spawn_in_terminal,
} = e
{
terminal_panel.spawn_task(spawn_in_terminal, window, cx);
};
})
.detach();
workspace
.update(&mut cx, |workspace, _| {
workspace.set_terminal_provider(TerminalProvider(terminal_panel.clone()))
})
.ok();
}
@ -486,12 +478,17 @@ impl TerminalPanel {
.detach_and_log_err(cx);
}
fn spawn_task(&mut self, task: &SpawnInTerminal, window: &mut Window, cx: &mut Context<Self>) {
fn spawn_task(
&mut self,
task: &SpawnInTerminal,
window: &mut Window,
cx: &mut Context<Self>,
) -> Task<Result<Entity<Terminal>>> {
let Ok(is_local) = self
.workspace
.update(cx, |workspace, cx| workspace.project().read(cx).is_local())
else {
return;
return Task::ready(Err(anyhow!("Project is not local")));
};
let builder = ShellBuilder::new(is_local, &task.shell);
@ -506,58 +503,53 @@ impl TerminalPanel {
};
if task.allow_concurrent_runs && task.use_new_terminal {
self.spawn_in_new_terminal(task, window, cx)
.detach_and_log_err(cx);
return;
return self.spawn_in_new_terminal(task, window, cx);
}
let mut terminals_for_task = self.terminals_for_task(&task.full_label, cx);
let Some(existing) = terminals_for_task.pop() else {
self.spawn_in_new_terminal(task, window, cx)
.detach_and_log_err(cx);
return;
return self.spawn_in_new_terminal(task, window, cx);
};
let (existing_item_index, task_pane, existing_terminal) = existing;
if task.allow_concurrent_runs {
self.replace_terminal(
return self.replace_terminal(
task,
task_pane,
existing_item_index,
existing_terminal,
window,
cx,
)
.detach();
return;
);
}
let (tx, rx) = oneshot::channel();
self.deferred_tasks.insert(
task.id.clone(),
cx.spawn_in(window, async move |terminal_panel, cx| {
wait_for_terminals_tasks(terminals_for_task, cx).await;
let task = terminal_panel.update_in(cx, |terminal_panel, window, cx| {
if task.use_new_terminal {
terminal_panel
.spawn_in_new_terminal(task, window, cx)
.detach_and_log_err(cx);
None
terminal_panel.spawn_in_new_terminal(task, window, cx)
} else {
Some(terminal_panel.replace_terminal(
terminal_panel.replace_terminal(
task,
task_pane,
existing_item_index,
existing_terminal,
window,
cx,
))
)
}
});
if let Ok(Some(task)) = task {
task.await;
if let Ok(task) = task {
tx.send(task.await).ok();
}
}),
);
cx.spawn(async move |_, _| rx.await?)
}
pub fn spawn_in_new_terminal(
@ -810,60 +802,47 @@ impl TerminalPanel {
terminal_to_replace: Entity<TerminalView>,
window: &mut Window,
cx: &mut Context<Self>,
) -> Task<Option<()>> {
) -> Task<Result<Entity<Terminal>>> {
let reveal = spawn_task.reveal;
let reveal_target = spawn_task.reveal_target;
let window_handle = window.window_handle();
let task_workspace = self.workspace.clone();
cx.spawn_in(window, async move |terminal_panel, cx| {
let project = terminal_panel
.update(cx, |this, cx| {
this.workspace
.update(cx, |workspace, _| workspace.project().clone())
.ok()
})
.ok()
.flatten()?;
let project = terminal_panel.update(cx, |this, cx| {
this.workspace
.update(cx, |workspace, _| workspace.project().clone())
})??;
let new_terminal = project
.update(cx, |project, cx| {
project.create_terminal(TerminalKind::Task(spawn_task), window_handle, cx)
})
.ok()?
.await
.log_err()?;
terminal_to_replace
.update_in(cx, |terminal_to_replace, window, cx| {
terminal_to_replace.set_terminal(new_terminal, window, cx);
})
.ok()?;
})?
.await?;
terminal_to_replace.update_in(cx, |terminal_to_replace, window, cx| {
terminal_to_replace.set_terminal(new_terminal.clone(), window, cx);
})?;
match reveal {
RevealStrategy::Always => match reveal_target {
RevealTarget::Center => {
task_workspace
.update_in(cx, |workspace, window, cx| {
workspace
.active_item(cx)
.context("retrieving active terminal item in the workspace")
.log_err()?
.item_focus_handle(cx)
.focus(window);
Some(())
})
.ok()??;
task_workspace.update_in(cx, |workspace, window, cx| {
workspace
.active_item(cx)
.context("retrieving active terminal item in the workspace")?
.item_focus_handle(cx)
.focus(window);
anyhow::Ok(())
})??;
}
RevealTarget::Dock => {
terminal_panel
.update_in(cx, |terminal_panel, window, cx| {
terminal_panel.activate_terminal_view(
&task_pane,
terminal_item_index,
true,
window,
cx,
)
})
.ok()?;
terminal_panel.update_in(cx, |terminal_panel, window, cx| {
terminal_panel.activate_terminal_view(
&task_pane,
terminal_item_index,
true,
window,
cx,
)
})?;
cx.spawn(async move |cx| {
task_workspace
@ -877,24 +856,20 @@ impl TerminalPanel {
},
RevealStrategy::NoFocus => match reveal_target {
RevealTarget::Center => {
task_workspace
.update_in(cx, |workspace, window, cx| {
workspace.active_pane().focus_handle(cx).focus(window);
})
.ok()?;
task_workspace.update_in(cx, |workspace, window, cx| {
workspace.active_pane().focus_handle(cx).focus(window);
})?;
}
RevealTarget::Dock => {
terminal_panel
.update_in(cx, |terminal_panel, window, cx| {
terminal_panel.activate_terminal_view(
&task_pane,
terminal_item_index,
false,
window,
cx,
)
})
.ok()?;
terminal_panel.update_in(cx, |terminal_panel, window, cx| {
terminal_panel.activate_terminal_view(
&task_pane,
terminal_item_index,
false,
window,
cx,
)
})?;
cx.spawn(async move |cx| {
task_workspace
@ -909,7 +884,7 @@ impl TerminalPanel {
RevealStrategy::Never => {}
}
Some(())
Ok(new_terminal)
})
}
@ -1158,7 +1133,7 @@ async fn wait_for_terminals_tasks(
})
.ok()
});
let _: Vec<()> = join_all(pending_tasks).await;
let _: Vec<_> = join_all(pending_tasks).await;
}
fn add_paths_to_terminal(
@ -1475,6 +1450,34 @@ impl Panel for TerminalPanel {
}
}
struct TerminalProvider(Entity<TerminalPanel>);
impl workspace::TerminalProvider for TerminalProvider {
fn spawn(
&self,
task: SpawnInTerminal,
window: &mut Window,
cx: &mut App,
) -> Task<Result<ExitStatus>> {
let this = self.0.clone();
window.spawn(cx, async move |cx| {
let terminal = this
.update_in(cx, |terminal_panel, window, cx| {
terminal_panel.spawn_task(&task, window, cx)
})?
.await?;
let Some(exit_code) = terminal
.read_with(cx, |terminal, cx| terminal.wait_for_completed_task(cx))?
.await
else {
return Err(anyhow!("Task cancelled"));
};
Ok(exit_code)
})
}
}
struct InlineAssistTabBarButton {
focus_handle: FocusHandle,
}