debugger: Add close button and coloring to debug panel session's menu (#28310)

This PR adds colors to debug panel's session menu that indicate the
state of each respective session. It also adds a close button to each
entry.

green - running
yellow - stopped
red - terminated/ended 


Release Notes:

- N/A
This commit is contained in:
Anthony Eid 2025-04-08 12:35:33 -04:00 committed by GitHub
parent ee7b1ec7f2
commit 1774cad933
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 173 additions and 44 deletions

View file

@ -12,8 +12,8 @@ use dap::{
};
use futures::{SinkExt as _, channel::mpsc};
use gpui::{
Action, App, AsyncWindowContext, Context, Entity, EventEmitter, FocusHandle, Focusable,
Subscription, Task, WeakEntity, actions,
Action, App, AsyncWindowContext, Context, Entity, EntityId, EventEmitter, FocusHandle,
Focusable, Subscription, Task, WeakEntity, actions,
};
use project::{
Project,
@ -336,6 +336,95 @@ impl DebugPanel {
})
}
fn close_session(&mut self, entity_id: EntityId, cx: &mut Context<Self>) {
let Some(session) = self
.sessions
.iter()
.find(|other| entity_id == other.entity_id())
else {
return;
};
session.update(cx, |session, cx| session.shutdown(cx));
self.sessions.retain(|other| entity_id != other.entity_id());
if let Some(active_session_id) = self
.active_session
.as_ref()
.map(|session| session.entity_id())
{
if active_session_id == entity_id {
self.active_session = self.sessions.first().cloned();
}
}
}
fn sessions_drop_down_menu(
&self,
active_session: &Entity<DebugSession>,
window: &mut Window,
cx: &mut Context<Self>,
) -> DropdownMenu {
let sessions = self.sessions.clone();
let weak = cx.weak_entity();
let label = active_session.read(cx).label_element(cx);
DropdownMenu::new_with_element(
"debugger-session-list",
label,
ContextMenu::build(window, cx, move |mut this, _, _| {
for session in sessions.into_iter() {
let weak_session = session.downgrade();
let weak_id = weak_session.entity_id();
this = this.custom_entry(
{
let weak = weak.clone();
move |_, cx| {
weak_session
.read_with(cx, |session, cx| {
h_flex()
.w_full()
.justify_between()
.child(session.label_element(cx))
.child(
IconButton::new(
"close-debug-session",
IconName::Close,
)
.icon_size(IconSize::Small)
.on_click({
let weak = weak.clone();
move |_, _, cx| {
weak.update(cx, |panel, cx| {
panel.close_session(weak_id, cx);
})
.ok();
}
}),
)
.into_any_element()
})
.unwrap_or_else(|_| div().into_any_element())
}
},
{
let weak = weak.clone();
move |window, cx| {
weak.update(cx, |panel, cx| {
panel.activate_session(session.clone(), window, cx);
})
.ok();
}
},
);
}
this
}),
)
}
fn top_controls_strip(&self, window: &mut Window, cx: &mut Context<Self>) -> Option<Div> {
let active_session = self.active_session.clone();
@ -529,34 +618,8 @@ impl DebugPanel {
},
)
.when_some(active_session.as_ref(), |this, session| {
let sessions = self.sessions.clone();
let weak = cx.weak_entity();
let label = session.read(cx).label(cx);
this.child(DropdownMenu::new(
"debugger-session-list",
label,
ContextMenu::build(window, cx, move |mut this, _, cx| {
for item in sessions {
let weak = weak.clone();
this = this.entry(
session.read(cx).label(cx),
None,
move |window, cx| {
weak.update(cx, |panel, cx| {
panel.activate_session(
item.clone(),
window,
cx,
);
})
.ok();
},
);
}
this
}),
))
.child(Divider::vertical())
let context_menu = self.sessions_drop_down_menu(session, window, cx);
this.child(context_menu).child(Divider::vertical())
})
.child(
IconButton::new("debug-new-session", IconName::Plus)

View file

@ -7,7 +7,7 @@ use project::debugger::{dap_store::DapStore, session::Session};
use project::worktree_store::WorktreeStore;
use rpc::proto::{self, PeerId};
use running::RunningState;
use ui::prelude::*;
use ui::{Indicator, prelude::*};
use workspace::{
FollowableItem, ViewId, Workspace,
item::{self, Item},
@ -81,7 +81,6 @@ impl DebugSession {
}
}
#[expect(unused)]
pub(crate) fn shutdown(&mut self, cx: &mut Context<Self>) {
match &self.mode {
DebugSessionState::Running(state) => state.update(cx, |state, cx| state.shutdown(cx)),
@ -108,6 +107,33 @@ impl DebugSession {
.expect("Remote Debug Sessions are not implemented yet")
.label()
}
pub(crate) fn label_element(&self, cx: &App) -> AnyElement {
let label = self.label(cx);
let (icon, color) = match &self.mode {
DebugSessionState::Running(state) => {
if state.read(cx).session().read(cx).is_terminated() {
(Some(Indicator::dot().color(Color::Error)), Color::Error)
} else {
match state.read(cx).thread_status(cx).unwrap_or_default() {
project::debugger::session::ThreadStatus::Stopped => (
Some(Indicator::dot().color(Color::Conflict)),
Color::Conflict,
),
_ => (Some(Indicator::dot().color(Color::Success)), Color::Success),
}
}
}
};
h_flex()
.gap_2()
.when_some(icon, |this, indicator| this.child(indicator))
.justify_between()
.child(Label::new(label).color(color))
.into_any_element()
}
}
impl EventEmitter<DebugPanelItemEvent> for DebugSession {}