debugger: Add spinners while session is starting up (#31548)
Release Notes: - Debugger Beta: Added a spinner to the debug panel when a session is starting up. --------- Co-authored-by: Remco Smits <djsmits12@gmail.com> Co-authored-by: Julia <julia@zed.dev>
This commit is contained in:
parent
384b11392a
commit
f9407db7d6
4 changed files with 93 additions and 33 deletions
|
@ -311,6 +311,31 @@ impl ActivityIndicator {
|
|||
});
|
||||
}
|
||||
|
||||
if let Some(session) = self
|
||||
.project
|
||||
.read(cx)
|
||||
.dap_store()
|
||||
.read(cx)
|
||||
.sessions()
|
||||
.find(|s| !s.read(cx).is_started())
|
||||
{
|
||||
return Some(Content {
|
||||
icon: Some(
|
||||
Icon::new(IconName::ArrowCircle)
|
||||
.size(IconSize::Small)
|
||||
.with_animation(
|
||||
"arrow-circle",
|
||||
Animation::new(Duration::from_secs(2)).repeat(),
|
||||
|icon, delta| icon.transform(Transformation::rotate(percentage(delta))),
|
||||
)
|
||||
.into_any_element(),
|
||||
),
|
||||
message: format!("Debug: {}", session.read(cx).adapter()),
|
||||
tooltip_message: Some(session.read(cx).label().to_string()),
|
||||
on_click: None,
|
||||
});
|
||||
}
|
||||
|
||||
let current_job = self
|
||||
.project
|
||||
.read(cx)
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use gpui::Entity;
|
||||
use std::time::Duration;
|
||||
|
||||
use gpui::{Animation, AnimationExt as _, Entity, Transformation, percentage};
|
||||
use project::debugger::session::{ThreadId, ThreadStatus};
|
||||
use ui::{ContextMenu, DropdownMenu, DropdownStyle, Indicator, prelude::*};
|
||||
|
||||
|
@ -23,31 +25,40 @@ impl DebugPanel {
|
|||
let sessions = self.sessions().clone();
|
||||
let weak = cx.weak_entity();
|
||||
let running_state = running_state.read(cx);
|
||||
let label = if let Some(active_session) = active_session {
|
||||
let label = if let Some(active_session) = active_session.clone() {
|
||||
active_session.read(cx).session(cx).read(cx).label()
|
||||
} else {
|
||||
SharedString::new_static("Unknown Session")
|
||||
};
|
||||
|
||||
let is_terminated = running_state.session().read(cx).is_terminated();
|
||||
let session_state_indicator = {
|
||||
if is_terminated {
|
||||
Some(Indicator::dot().color(Color::Error))
|
||||
} else {
|
||||
match running_state.thread_status(cx).unwrap_or_default() {
|
||||
project::debugger::session::ThreadStatus::Stopped => {
|
||||
Some(Indicator::dot().color(Color::Conflict))
|
||||
}
|
||||
_ => Some(Indicator::dot().color(Color::Success)),
|
||||
let is_started = active_session
|
||||
.is_some_and(|session| session.read(cx).session(cx).read(cx).is_started());
|
||||
|
||||
let session_state_indicator = if is_terminated {
|
||||
Indicator::dot().color(Color::Error).into_any_element()
|
||||
} else if !is_started {
|
||||
Icon::new(IconName::ArrowCircle)
|
||||
.size(IconSize::Small)
|
||||
.color(Color::Muted)
|
||||
.with_animation(
|
||||
"arrow-circle",
|
||||
Animation::new(Duration::from_secs(2)).repeat(),
|
||||
|icon, delta| icon.transform(Transformation::rotate(percentage(delta))),
|
||||
)
|
||||
.into_any_element()
|
||||
} else {
|
||||
match running_state.thread_status(cx).unwrap_or_default() {
|
||||
ThreadStatus::Stopped => {
|
||||
Indicator::dot().color(Color::Conflict).into_any_element()
|
||||
}
|
||||
_ => Indicator::dot().color(Color::Success).into_any_element(),
|
||||
}
|
||||
};
|
||||
|
||||
let trigger = h_flex()
|
||||
.gap_2()
|
||||
.when_some(session_state_indicator, |this, indicator| {
|
||||
this.child(indicator)
|
||||
})
|
||||
.child(session_state_indicator)
|
||||
.justify_between()
|
||||
.child(
|
||||
DebugPanel::dropdown_label(label)
|
||||
|
|
|
@ -110,7 +110,7 @@ impl Console {
|
|||
}
|
||||
|
||||
fn is_running(&self, cx: &Context<Self>) -> bool {
|
||||
self.session.read(cx).is_local()
|
||||
self.session.read(cx).is_running()
|
||||
}
|
||||
|
||||
fn handle_stack_frame_list_events(
|
||||
|
|
|
@ -121,16 +121,17 @@ impl From<dap::Thread> for Thread {
|
|||
|
||||
pub enum Mode {
|
||||
Building,
|
||||
Running(LocalMode),
|
||||
Running(RunningMode),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LocalMode {
|
||||
pub struct RunningMode {
|
||||
client: Arc<DebugAdapterClient>,
|
||||
binary: DebugAdapterBinary,
|
||||
tmp_breakpoint: Option<SourceBreakpoint>,
|
||||
worktree: WeakEntity<Worktree>,
|
||||
executor: BackgroundExecutor,
|
||||
is_started: bool,
|
||||
}
|
||||
|
||||
fn client_source(abs_path: &Path) -> dap::Source {
|
||||
|
@ -148,7 +149,7 @@ fn client_source(abs_path: &Path) -> dap::Source {
|
|||
}
|
||||
}
|
||||
|
||||
impl LocalMode {
|
||||
impl RunningMode {
|
||||
async fn new(
|
||||
session_id: SessionId,
|
||||
parent_session: Option<Entity<Session>>,
|
||||
|
@ -181,6 +182,7 @@ impl LocalMode {
|
|||
tmp_breakpoint: None,
|
||||
binary,
|
||||
executor: cx.background_executor().clone(),
|
||||
is_started: false,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -373,7 +375,7 @@ impl LocalMode {
|
|||
capabilities: &Capabilities,
|
||||
initialized_rx: oneshot::Receiver<()>,
|
||||
dap_store: WeakEntity<DapStore>,
|
||||
cx: &App,
|
||||
cx: &mut Context<Session>,
|
||||
) -> Task<Result<()>> {
|
||||
let raw = self.binary.request_args.clone();
|
||||
|
||||
|
@ -405,7 +407,7 @@ impl LocalMode {
|
|||
let this = self.clone();
|
||||
let worktree = self.worktree().clone();
|
||||
let configuration_sequence = cx.spawn({
|
||||
async move |cx| {
|
||||
async move |_, cx| {
|
||||
let breakpoint_store =
|
||||
dap_store.read_with(cx, |dap_store, _| dap_store.breakpoint_store().clone())?;
|
||||
initialized_rx.await?;
|
||||
|
@ -453,9 +455,20 @@ impl LocalMode {
|
|||
}
|
||||
});
|
||||
|
||||
cx.background_spawn(async move {
|
||||
futures::future::try_join(launch, configuration_sequence).await?;
|
||||
Ok(())
|
||||
let task = cx.background_spawn(futures::future::try_join(launch, configuration_sequence));
|
||||
|
||||
cx.spawn(async move |this, cx| {
|
||||
task.await?;
|
||||
|
||||
this.update(cx, |this, cx| {
|
||||
if let Some(this) = this.as_running_mut() {
|
||||
this.is_started = true;
|
||||
cx.notify();
|
||||
}
|
||||
})
|
||||
.ok();
|
||||
|
||||
anyhow::Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -704,7 +717,7 @@ impl Session {
|
|||
cx.subscribe(&breakpoint_store, |this, store, event, cx| match event {
|
||||
BreakpointStoreEvent::BreakpointsUpdated(path, reason) => {
|
||||
if let Some(local) = (!this.ignore_breakpoints)
|
||||
.then(|| this.as_local_mut())
|
||||
.then(|| this.as_running_mut())
|
||||
.flatten()
|
||||
{
|
||||
local
|
||||
|
@ -714,7 +727,7 @@ impl Session {
|
|||
}
|
||||
BreakpointStoreEvent::BreakpointsCleared(paths) => {
|
||||
if let Some(local) = (!this.ignore_breakpoints)
|
||||
.then(|| this.as_local_mut())
|
||||
.then(|| this.as_running_mut())
|
||||
.flatten()
|
||||
{
|
||||
local.unset_breakpoints_from_paths(paths, cx).detach();
|
||||
|
@ -806,7 +819,7 @@ impl Session {
|
|||
let parent_session = self.parent_session.clone();
|
||||
|
||||
cx.spawn(async move |this, cx| {
|
||||
let mode = LocalMode::new(
|
||||
let mode = RunningMode::new(
|
||||
id,
|
||||
parent_session,
|
||||
worktree.downgrade(),
|
||||
|
@ -906,18 +919,29 @@ impl Session {
|
|||
return tx;
|
||||
}
|
||||
|
||||
pub fn is_local(&self) -> bool {
|
||||
pub fn is_started(&self) -> bool {
|
||||
match &self.mode {
|
||||
Mode::Building => false,
|
||||
Mode::Running(running) => running.is_started,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_building(&self) -> bool {
|
||||
matches!(self.mode, Mode::Building)
|
||||
}
|
||||
|
||||
pub fn is_running(&self) -> bool {
|
||||
matches!(self.mode, Mode::Running(_))
|
||||
}
|
||||
|
||||
pub fn as_local_mut(&mut self) -> Option<&mut LocalMode> {
|
||||
pub fn as_running_mut(&mut self) -> Option<&mut RunningMode> {
|
||||
match &mut self.mode {
|
||||
Mode::Running(local_mode) => Some(local_mode),
|
||||
Mode::Building => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_local(&self) -> Option<&LocalMode> {
|
||||
pub fn as_running(&self) -> Option<&RunningMode> {
|
||||
match &self.mode {
|
||||
Mode::Running(local_mode) => Some(local_mode),
|
||||
Mode::Building => None,
|
||||
|
@ -1140,7 +1164,7 @@ impl Session {
|
|||
body: Option<serde_json::Value>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
let Some(local_session) = self.as_local() else {
|
||||
let Some(local_session) = self.as_running() else {
|
||||
unreachable!("Cannot respond to remote client");
|
||||
};
|
||||
let client = local_session.client.clone();
|
||||
|
@ -1162,7 +1186,7 @@ impl Session {
|
|||
fn handle_stopped_event(&mut self, event: StoppedEvent, cx: &mut Context<Self>) {
|
||||
// todo(debugger): Find a clean way to get around the clone
|
||||
let breakpoint_store = self.breakpoint_store.clone();
|
||||
if let Some((local, path)) = self.as_local_mut().and_then(|local| {
|
||||
if let Some((local, path)) = self.as_running_mut().and_then(|local| {
|
||||
let breakpoint = local.tmp_breakpoint.take()?;
|
||||
let path = breakpoint.path.clone();
|
||||
Some((local, path))
|
||||
|
@ -1528,7 +1552,7 @@ impl Session {
|
|||
|
||||
self.ignore_breakpoints = ignore;
|
||||
|
||||
if let Some(local) = self.as_local() {
|
||||
if let Some(local) = self.as_running() {
|
||||
local.send_source_breakpoints(ignore, &self.breakpoint_store, cx)
|
||||
} else {
|
||||
// todo(debugger): We need to propagate this change to downstream sessions and send a message to upstream sessions
|
||||
|
@ -1550,7 +1574,7 @@ impl Session {
|
|||
}
|
||||
|
||||
fn send_exception_breakpoints(&mut self, cx: &App) {
|
||||
if let Some(local) = self.as_local() {
|
||||
if let Some(local) = self.as_running() {
|
||||
let exception_filters = self
|
||||
.exception_breakpoints
|
||||
.values()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue