diff --git a/Cargo.lock b/Cargo.lock index b95c9dce18..2c9513970f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4177,6 +4177,7 @@ dependencies = [ "collections", "command_palette_hooks", "dap", + "db", "editor", "env_logger 0.11.8", "feature_flags", diff --git a/crates/db/src/kvp.rs b/crates/db/src/kvp.rs index c9d994d34d..d501368c85 100644 --- a/crates/db/src/kvp.rs +++ b/crates/db/src/kvp.rs @@ -1,6 +1,7 @@ use sqlez_macros::sql; use crate::{define_connection, query}; +pub static DEBUGGER_PANEL_PREFIX: &str = "debugger_panel_"; define_connection!(pub static ref KEY_VALUE_STORE: KeyValueStore<()> = &[sql!( diff --git a/crates/debugger_ui/Cargo.toml b/crates/debugger_ui/Cargo.toml index 239c47d07d..ac180f308f 100644 --- a/crates/debugger_ui/Cargo.toml +++ b/crates/debugger_ui/Cargo.toml @@ -28,6 +28,7 @@ client.workspace = true collections.workspace = true command_palette_hooks.workspace = true dap.workspace = true +db.workspace = true editor.workspace = true feature_flags.workspace = true futures.workspace = true diff --git a/crates/debugger_ui/src/debugger_panel.rs b/crates/debugger_ui/src/debugger_panel.rs index 378709450e..9cec74d27e 100644 --- a/crates/debugger_ui/src/debugger_panel.rs +++ b/crates/debugger_ui/src/debugger_panel.rs @@ -1,6 +1,6 @@ use crate::{ ClearAllBreakpoints, Continue, CreateDebuggingSession, Disconnect, Pause, Restart, StepBack, - StepInto, StepOut, StepOver, Stop, ToggleIgnoreBreakpoints, + StepInto, StepOut, StepOver, Stop, ToggleIgnoreBreakpoints, persistence, }; use crate::{new_session_modal::NewSessionModal, session::DebugSession}; use anyhow::{Result, anyhow}; @@ -293,35 +293,49 @@ impl DebugPanel { ); }; - let Some(project) = self.project.upgrade() else { - return log::error!("Debug Panel out lived it's weak reference to Project"); - }; + let adapter_name = session.read(cx).adapter_name(); - if self - .sessions - .iter() - .any(|item| item.read(cx).session_id(cx) == *session_id) - { - // We already have an item for this session. - return; - } - let session_item = DebugSession::running( - project, - self.workspace.clone(), - session, - cx.weak_entity(), - window, - cx, - ); + let session_id = *session_id; + cx.spawn_in(window, async move |this, cx| { + let serialized_layout = + persistence::get_serialized_pane_layout(adapter_name).await; - if let Some(running) = session_item.read(cx).mode().as_running().cloned() { - // We might want to make this an event subscription and only notify when a new thread is selected - // This is used to filter the command menu correctly - cx.observe(&running, |_, _, cx| cx.notify()).detach(); - } + this.update_in(cx, |this, window, cx| { + let Some(project) = this.project.upgrade() else { + return log::error!( + "Debug Panel out lived it's weak reference to Project" + ); + }; - self.sessions.push(session_item.clone()); - self.activate_session(session_item, window, cx); + if this + .sessions + .iter() + .any(|item| item.read(cx).session_id(cx) == session_id) + { + // We already have an item for this session. + return; + } + let session_item = DebugSession::running( + project, + this.workspace.clone(), + session, + cx.weak_entity(), + serialized_layout, + window, + cx, + ); + + if let Some(running) = session_item.read(cx).mode().as_running().cloned() { + // We might want to make this an event subscription and only notify when a new thread is selected + // This is used to filter the command menu correctly + cx.observe(&running, |_, _, cx| cx.notify()).detach(); + } + + this.sessions.push(session_item.clone()); + this.activate_session(session_item, window, cx); + }) + }) + .detach(); } dap_store::DapStoreEvent::RunInTerminal { title, diff --git a/crates/debugger_ui/src/debugger_ui.rs b/crates/debugger_ui/src/debugger_ui.rs index 7a89b275ea..55eea2315e 100644 --- a/crates/debugger_ui/src/debugger_ui.rs +++ b/crates/debugger_ui/src/debugger_ui.rs @@ -13,6 +13,7 @@ use workspace::{ShutdownDebugAdapters, Workspace}; pub mod attach_modal; pub mod debugger_panel; mod new_session_modal; +mod persistence; pub(crate) mod session; #[cfg(test)] diff --git a/crates/debugger_ui/src/persistence.rs b/crates/debugger_ui/src/persistence.rs new file mode 100644 index 0000000000..0472675268 --- /dev/null +++ b/crates/debugger_ui/src/persistence.rs @@ -0,0 +1,259 @@ +use collections::HashMap; +use db::kvp::KEY_VALUE_STORE; +use gpui::{Axis, Context, Entity, EntityId, Focusable, Subscription, WeakEntity, Window}; +use project::Project; +use serde::{Deserialize, Serialize}; +use ui::{App, SharedString}; +use util::ResultExt; +use workspace::{Member, Pane, PaneAxis, Workspace}; + +use crate::session::running::{ + self, RunningState, SubView, breakpoint_list::BreakpointList, console::Console, + module_list::ModuleList, stack_frame_list::StackFrameList, variable_list::VariableList, +}; + +#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)] +pub(crate) enum DebuggerPaneItem { + Console, + Variables, + BreakpointList, + Frames, + Modules, +} + +impl DebuggerPaneItem { + pub(crate) fn to_shared_string(self) -> SharedString { + match self { + DebuggerPaneItem::Console => SharedString::new_static("Console"), + DebuggerPaneItem::Variables => SharedString::new_static("Variables"), + DebuggerPaneItem::BreakpointList => SharedString::new_static("Breakpoints"), + DebuggerPaneItem::Frames => SharedString::new_static("Frames"), + DebuggerPaneItem::Modules => SharedString::new_static("Modules"), + } + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub(crate) struct SerializedAxis(pub Axis); + +#[derive(Debug, Serialize, Deserialize)] +pub(crate) enum SerializedPaneLayout { + Pane(SerializedPane), + Group { + axis: SerializedAxis, + flexes: Option>, + children: Vec, + }, +} + +#[derive(Debug, Serialize, Deserialize)] +pub(crate) struct SerializedPane { + pub children: Vec, + pub active_item: Option, +} + +pub(crate) async fn serialize_pane_layout( + adapter_name: SharedString, + pane_group: SerializedPaneLayout, +) -> anyhow::Result<()> { + if let Ok(serialized_pane_group) = serde_json::to_string(&pane_group) { + KEY_VALUE_STORE + .write_kvp( + format!("{}-{adapter_name}", db::kvp::DEBUGGER_PANEL_PREFIX), + serialized_pane_group, + ) + .await + } else { + Err(anyhow::anyhow!( + "Failed to serialize pane group with serde_json as a string" + )) + } +} + +pub(crate) fn build_serialized_pane_layout( + pane_group: &Member, + cx: &mut App, +) -> SerializedPaneLayout { + match pane_group { + Member::Axis(PaneAxis { + axis, + members, + flexes, + bounding_boxes: _, + }) => SerializedPaneLayout::Group { + axis: SerializedAxis(*axis), + children: members + .iter() + .map(|member| build_serialized_pane_layout(member, cx)) + .collect::>(), + flexes: Some(flexes.lock().clone()), + }, + Member::Pane(pane_handle) => SerializedPaneLayout::Pane(serialize_pane(pane_handle, cx)), + } +} + +fn serialize_pane(pane: &Entity, cx: &mut App) -> SerializedPane { + let pane = pane.read(cx); + let children = pane + .items() + .filter_map(|item| { + item.act_as::(cx) + .map(|view| view.read(cx).view_kind()) + }) + .collect::>(); + + let active_item = pane + .active_item() + .and_then(|item| item.act_as::(cx)) + .map(|view| view.read(cx).view_kind()); + + SerializedPane { + children, + active_item, + } +} + +pub(crate) async fn get_serialized_pane_layout( + adapter_name: impl AsRef, +) -> Option { + let key = format!( + "{}-{}", + db::kvp::DEBUGGER_PANEL_PREFIX, + adapter_name.as_ref() + ); + + KEY_VALUE_STORE + .read_kvp(&key) + .log_err() + .flatten() + .and_then(|value| serde_json::from_str::(&value).ok()) +} + +pub(crate) fn deserialize_pane_layout( + serialized: SerializedPaneLayout, + workspace: &WeakEntity, + project: &Entity, + stack_frame_list: &Entity, + variable_list: &Entity, + module_list: &Entity, + console: &Entity, + breakpoint_list: &Entity, + subscriptions: &mut HashMap, + window: &mut Window, + cx: &mut Context, +) -> Option { + match serialized { + SerializedPaneLayout::Group { + axis, + flexes, + children, + } => { + let mut members = Vec::new(); + for child in children { + if let Some(new_member) = deserialize_pane_layout( + child, + workspace, + project, + stack_frame_list, + variable_list, + module_list, + console, + breakpoint_list, + subscriptions, + window, + cx, + ) { + members.push(new_member); + } + } + + if members.is_empty() { + return None; + } + + if members.len() == 1 { + return Some(members.remove(0)); + } + + Some(Member::Axis(PaneAxis::load( + axis.0, + members, + flexes.clone(), + ))) + } + SerializedPaneLayout::Pane(serialized_pane) => { + let pane = running::new_debugger_pane(workspace.clone(), project.clone(), window, cx); + subscriptions.insert( + pane.entity_id(), + cx.subscribe_in(&pane, window, RunningState::handle_pane_event), + ); + + let sub_views: Vec<_> = serialized_pane + .children + .iter() + .map(|child| match child { + DebuggerPaneItem::Frames => Box::new(SubView::new( + pane.focus_handle(cx), + stack_frame_list.clone().into(), + DebuggerPaneItem::Frames, + None, + cx, + )), + DebuggerPaneItem::Variables => Box::new(SubView::new( + variable_list.focus_handle(cx), + variable_list.clone().into(), + DebuggerPaneItem::Variables, + None, + cx, + )), + DebuggerPaneItem::BreakpointList => Box::new(SubView::new( + breakpoint_list.focus_handle(cx), + breakpoint_list.clone().into(), + DebuggerPaneItem::BreakpointList, + None, + cx, + )), + DebuggerPaneItem::Modules => Box::new(SubView::new( + pane.focus_handle(cx), + module_list.clone().into(), + DebuggerPaneItem::Modules, + None, + cx, + )), + + DebuggerPaneItem::Console => Box::new(SubView::new( + pane.focus_handle(cx), + console.clone().into(), + DebuggerPaneItem::Console, + Some(Box::new({ + let console = console.clone().downgrade(); + move |cx| { + console + .read_with(cx, |console, cx| console.show_indicator(cx)) + .unwrap_or_default() + } + })), + cx, + )), + }) + .collect(); + + pane.update(cx, |pane, cx| { + let mut active_idx = 0; + for (idx, sub_view) in sub_views.into_iter().enumerate() { + if serialized_pane + .active_item + .is_some_and(|active| active == sub_view.read(cx).view_kind()) + { + active_idx = idx; + } + pane.add_item(sub_view, false, false, None, window, cx); + } + + pane.activate_item(active_idx, false, false, window, cx); + }); + + Some(Member::Pane(pane.clone())) + } + } +} diff --git a/crates/debugger_ui/src/session.rs b/crates/debugger_ui/src/session.rs index c69a2259b2..93fbdc1111 100644 --- a/crates/debugger_ui/src/session.rs +++ b/crates/debugger_ui/src/session.rs @@ -16,6 +16,7 @@ use workspace::{ }; use crate::debugger_panel::DebugPanel; +use crate::persistence::SerializedPaneLayout; pub(crate) enum DebugSessionState { Running(Entity), @@ -52,6 +53,7 @@ impl DebugSession { workspace: WeakEntity, session: Entity, _debug_panel: WeakEntity, + serialized_pane_layout: Option, window: &mut Window, cx: &mut App, ) -> Entity { @@ -60,6 +62,7 @@ impl DebugSession { session.clone(), project.clone(), workspace.clone(), + serialized_pane_layout, window, cx, ) diff --git a/crates/debugger_ui/src/session/running.rs b/crates/debugger_ui/src/session/running.rs index 25386ae005..d3d3d5637e 100644 --- a/crates/debugger_ui/src/session/running.rs +++ b/crates/debugger_ui/src/session/running.rs @@ -1,11 +1,13 @@ -mod breakpoint_list; -mod console; -mod loaded_source_list; -mod module_list; +pub(crate) mod breakpoint_list; +pub(crate) mod console; +pub(crate) mod loaded_source_list; +pub(crate) mod module_list; pub mod stack_frame_list; pub mod variable_list; -use std::{any::Any, ops::ControlFlow, sync::Arc}; +use std::{any::Any, ops::ControlFlow, sync::Arc, time::Duration}; + +use crate::persistence::{self, DebuggerPaneItem, SerializedPaneLayout}; use super::DebugPanelItemEvent; use breakpoint_list::BreakpointList; @@ -14,7 +16,7 @@ use console::Console; use dap::{Capabilities, Thread, client::SessionId, debugger_settings::DebuggerSettings}; use gpui::{ Action as _, AnyView, AppContext, Entity, EntityId, EventEmitter, FocusHandle, Focusable, - NoAction, Subscription, WeakEntity, + NoAction, Subscription, Task, WeakEntity, }; use loaded_source_list::LoadedSourceList; use module_list::ModuleList; @@ -33,8 +35,8 @@ use ui::{ use util::ResultExt; use variable_list::VariableList; use workspace::{ - ActivePaneDecorator, DraggedTab, Item, Pane, PaneGroup, Workspace, item::TabContentParams, - move_item, pane::Event, + ActivePaneDecorator, DraggedTab, Item, Member, Pane, PaneGroup, Workspace, + item::TabContentParams, move_item, pane::Event, }; pub struct RunningState { @@ -51,6 +53,7 @@ pub struct RunningState { _console: Entity, panes: PaneGroup, pane_close_subscriptions: HashMap, + _schedule_serialize: Option>, } impl Render for RunningState { @@ -84,28 +87,32 @@ impl Render for RunningState { } } -struct SubView { +pub(crate) struct SubView { inner: AnyView, pane_focus_handle: FocusHandle, - tab_name: SharedString, + kind: DebuggerPaneItem, show_indicator: Box bool>, } impl SubView { - fn new( + pub(crate) fn new( pane_focus_handle: FocusHandle, view: AnyView, - tab_name: SharedString, + kind: DebuggerPaneItem, show_indicator: Option bool>>, cx: &mut App, ) -> Entity { cx.new(|_| Self { - tab_name, + kind, inner: view, pane_focus_handle, show_indicator: show_indicator.unwrap_or(Box::new(|_| false)), }) } + + pub(crate) fn view_kind(&self) -> DebuggerPaneItem { + self.kind + } } impl Focusable for SubView { fn focus_handle(&self, _: &App) -> FocusHandle { @@ -116,13 +123,19 @@ impl EventEmitter<()> for SubView {} impl Item for SubView { type Event = (); + /// This is used to serialize debugger pane layouts + /// A SharedString gets converted to a enum and back during serialization/deserialization. + fn tab_content_text(&self, _window: &Window, _cx: &App) -> Option { + Some(self.kind.to_shared_string()) + } + fn tab_content( &self, params: workspace::item::TabContentParams, _: &Window, cx: &App, ) -> AnyElement { - let label = Label::new(self.tab_name.clone()) + let label = Label::new(self.kind.to_shared_string()) .size(ui::LabelSize::Small) .color(params.text_color()) .line_height_style(ui::LineHeightStyle::UiLabel); @@ -146,7 +159,7 @@ impl Render for SubView { } } -fn new_debugger_pane( +pub(crate) fn new_debugger_pane( workspace: WeakEntity, project: Entity, window: &mut Window, @@ -185,7 +198,7 @@ fn new_debugger_pane( new_debugger_pane(workspace.clone(), project.clone(), window, cx); let _previous_subscription = running.pane_close_subscriptions.insert( new_pane.entity_id(), - cx.subscribe(&new_pane, RunningState::handle_pane_event), + cx.subscribe_in(&new_pane, window, RunningState::handle_pane_event), ); debug_assert!(_previous_subscription.is_none()); running @@ -354,6 +367,7 @@ impl RunningState { session: Entity, project: Entity, workspace: WeakEntity, + serialized_pane_layout: Option, window: &mut Window, cx: &mut Context, ) -> Self { @@ -382,6 +396,8 @@ impl RunningState { ) }); + let breakpoints = BreakpointList::new(session.clone(), workspace.clone(), &project, cx); + let _subscriptions = vec![ cx.observe(&module_list, |_, _, cx| cx.notify()), cx.subscribe_in(&session, window, |this, _, event, window, cx| { @@ -407,112 +423,40 @@ impl RunningState { }), ]; - let leftmost_pane = new_debugger_pane(workspace.clone(), project.clone(), window, cx); - leftmost_pane.update(cx, |this, cx| { - this.add_item( - Box::new(SubView::new( - this.focus_handle(cx), - stack_frame_list.clone().into(), - SharedString::new_static("Frames"), - None, - cx, - )), - true, - false, - None, + let mut pane_close_subscriptions = HashMap::default(); + let panes = if let Some(root) = serialized_pane_layout.and_then(|serialized_layout| { + persistence::deserialize_pane_layout( + serialized_layout, + &workspace, + &project, + &stack_frame_list, + &variable_list, + &module_list, + &console, + &breakpoints, + &mut pane_close_subscriptions, + window, + cx, + ) + }) { + workspace::PaneGroup::with_root(root) + } else { + pane_close_subscriptions.clear(); + let root = Self::default_pane_layout( + project, + &workspace, + &stack_frame_list, + &variable_list, + &module_list, + &console, + breakpoints, + &mut pane_close_subscriptions, window, cx, ); - let breakpoints = BreakpointList::new(session.clone(), workspace.clone(), &project, cx); - this.add_item( - Box::new(SubView::new( - breakpoints.focus_handle(cx), - breakpoints.into(), - SharedString::new_static("Breakpoints"), - None, - cx, - )), - true, - false, - None, - window, - cx, - ); - this.activate_item(0, false, false, window, cx); - }); - let center_pane = new_debugger_pane(workspace.clone(), project.clone(), window, cx); - center_pane.update(cx, |this, cx| { - this.add_item( - Box::new(SubView::new( - variable_list.focus_handle(cx), - variable_list.clone().into(), - SharedString::new_static("Variables"), - None, - cx, - )), - true, - false, - None, - window, - cx, - ); - this.add_item( - Box::new(SubView::new( - this.focus_handle(cx), - module_list.clone().into(), - SharedString::new_static("Modules"), - None, - cx, - )), - false, - false, - None, - window, - cx, - ); - this.activate_item(0, false, false, window, cx); - }); - let rightmost_pane = new_debugger_pane(workspace.clone(), project.clone(), window, cx); - rightmost_pane.update(cx, |this, cx| { - let weak_console = console.downgrade(); - this.add_item( - Box::new(SubView::new( - this.focus_handle(cx), - console.clone().into(), - SharedString::new_static("Console"), - Some(Box::new(move |cx| { - weak_console - .read_with(cx, |console, cx| console.show_indicator(cx)) - .unwrap_or_default() - })), - cx, - )), - true, - false, - None, - window, - cx, - ); - }); - let pane_close_subscriptions = HashMap::from_iter( - [&leftmost_pane, ¢er_pane, &rightmost_pane] - .into_iter() - .map(|entity| { - ( - entity.entity_id(), - cx.subscribe(entity, Self::handle_pane_event), - ) - }), - ); - let group_root = workspace::PaneAxis::new( - gpui::Axis::Horizontal, - [leftmost_pane, center_pane, rightmost_pane] - .into_iter() - .map(workspace::Member::Pane) - .collect(), - ); - let panes = PaneGroup::with_root(workspace::Member::Axis(group_root)); + workspace::PaneGroup::with_root(root) + }; Self { session, @@ -528,21 +472,57 @@ impl RunningState { _module_list: module_list, _console: console, pane_close_subscriptions, + _schedule_serialize: None, } } - fn handle_pane_event( + fn serialize_layout(&mut self, window: &mut Window, cx: &mut Context) { + if self._schedule_serialize.is_none() { + self._schedule_serialize = Some(cx.spawn_in(window, async move |this, cx| { + cx.background_executor() + .timer(Duration::from_millis(100)) + .await; + + let Some((adapter_name, pane_group)) = this + .update(cx, |this, cx| { + let adapter_name = this.session.read(cx).adapter_name(); + ( + adapter_name, + persistence::build_serialized_pane_layout(&this.panes.root, cx), + ) + }) + .ok() + else { + return; + }; + + persistence::serialize_pane_layout(adapter_name, pane_group) + .await + .log_err(); + + this.update(cx, |this, _| { + this._schedule_serialize.take(); + }) + .ok(); + })); + } + } + + pub(crate) fn handle_pane_event( this: &mut RunningState, - source_pane: Entity, + source_pane: &Entity, event: &Event, + window: &mut Window, cx: &mut Context, ) { + this.serialize_layout(window, cx); if let Event::Remove { .. } = event { let _did_find_pane = this.panes.remove(&source_pane).is_ok(); debug_assert!(_did_find_pane); cx.notify(); } } + pub(crate) fn go_to_selected_stack_frame(&self, window: &Window, cx: &mut Context) { if self.thread_id.is_some() { self.stack_frame_list @@ -586,7 +566,7 @@ impl RunningState { .find_map(|pane| { pane.read(cx) .items_of_type::() - .position(|view| view.read(cx).tab_name == *"Modules") + .position(|view| view.read(cx).view_kind().to_shared_string() == *"Modules") .map(|view| (view, pane)) }) .unwrap(); @@ -802,6 +782,127 @@ impl RunningState { }), ) } + + fn default_pane_layout( + project: Entity, + workspace: &WeakEntity, + stack_frame_list: &Entity, + variable_list: &Entity, + module_list: &Entity, + console: &Entity, + breakpoints: Entity, + subscriptions: &mut HashMap, + window: &mut Window, + cx: &mut Context<'_, RunningState>, + ) -> Member { + let leftmost_pane = new_debugger_pane(workspace.clone(), project.clone(), window, cx); + leftmost_pane.update(cx, |this, cx| { + this.add_item( + Box::new(SubView::new( + this.focus_handle(cx), + stack_frame_list.clone().into(), + DebuggerPaneItem::Frames, + None, + cx, + )), + true, + false, + None, + window, + cx, + ); + this.add_item( + Box::new(SubView::new( + breakpoints.focus_handle(cx), + breakpoints.into(), + DebuggerPaneItem::BreakpointList, + None, + cx, + )), + true, + false, + None, + window, + cx, + ); + this.activate_item(0, false, false, window, cx); + }); + let center_pane = new_debugger_pane(workspace.clone(), project.clone(), window, cx); + center_pane.update(cx, |this, cx| { + this.add_item( + Box::new(SubView::new( + variable_list.focus_handle(cx), + variable_list.clone().into(), + DebuggerPaneItem::Variables, + None, + cx, + )), + true, + false, + None, + window, + cx, + ); + this.add_item( + Box::new(SubView::new( + this.focus_handle(cx), + module_list.clone().into(), + DebuggerPaneItem::Modules, + None, + cx, + )), + false, + false, + None, + window, + cx, + ); + this.activate_item(0, false, false, window, cx); + }); + let rightmost_pane = new_debugger_pane(workspace.clone(), project.clone(), window, cx); + rightmost_pane.update(cx, |this, cx| { + let weak_console = console.downgrade(); + this.add_item( + Box::new(SubView::new( + this.focus_handle(cx), + console.clone().into(), + DebuggerPaneItem::Console, + Some(Box::new(move |cx| { + weak_console + .read_with(cx, |console, cx| console.show_indicator(cx)) + .unwrap_or_default() + })), + cx, + )), + true, + false, + None, + window, + cx, + ); + }); + + subscriptions.extend( + [&leftmost_pane, ¢er_pane, &rightmost_pane] + .into_iter() + .map(|entity| { + ( + entity.entity_id(), + cx.subscribe_in(entity, window, Self::handle_pane_event), + ) + }), + ); + + let group_root = workspace::PaneAxis::new( + gpui::Axis::Horizontal, + [leftmost_pane, center_pane, rightmost_pane] + .into_iter() + .map(workspace::Member::Pane) + .collect(), + ); + + Member::Axis(group_root) + } } impl EventEmitter for RunningState {} diff --git a/crates/debugger_ui/src/session/running/breakpoint_list.rs b/crates/debugger_ui/src/session/running/breakpoint_list.rs index ff7ead4123..d9972284fe 100644 --- a/crates/debugger_ui/src/session/running/breakpoint_list.rs +++ b/crates/debugger_ui/src/session/running/breakpoint_list.rs @@ -27,7 +27,7 @@ use ui::{ use util::{ResultExt, maybe}; use workspace::Workspace; -pub(super) struct BreakpointList { +pub(crate) struct BreakpointList { workspace: WeakEntity, breakpoint_store: Entity, worktree_store: Entity, diff --git a/crates/debugger_ui/src/tests.rs b/crates/debugger_ui/src/tests.rs index 0db43bb379..6fbe84a9e5 100644 --- a/crates/debugger_ui/src/tests.rs +++ b/crates/debugger_ui/src/tests.rs @@ -68,6 +68,7 @@ pub async fn init_test_workspace( workspace_handle } +#[track_caller] pub fn active_debug_session_panel( workspace: WindowHandle, cx: &mut TestAppContext, diff --git a/crates/debugger_ui/src/tests/debugger_panel.rs b/crates/debugger_ui/src/tests/debugger_panel.rs index 0dd83f7143..a19d852a85 100644 --- a/crates/debugger_ui/src/tests/debugger_panel.rs +++ b/crates/debugger_ui/src/tests/debugger_panel.rs @@ -81,6 +81,8 @@ async fn test_basic_show_debug_panel(executor: BackgroundExecutor, cx: &mut Test }) .await; + cx.run_until_parked(); + // assert we have a debug panel item before the session has stopped workspace .update(cx, |workspace, _window, cx| { @@ -229,6 +231,8 @@ async fn test_we_can_only_have_one_panel_per_debug_session( }) .await; + cx.run_until_parked(); + // assert we have a debug panel item before the session has stopped workspace .update(cx, |workspace, _window, cx| { @@ -1052,6 +1056,8 @@ async fn test_debug_panel_item_thread_status_reset_on_failure( })) .await; + cx.run_until_parked(); + let running_state = active_debug_session_panel(workspace, cx).update_in(cx, |item, _, _| { item.mode() .as_running() diff --git a/crates/debugger_ui/src/tests/variable_list.rs b/crates/debugger_ui/src/tests/variable_list.rs index 8c352731fb..c4001cc5e2 100644 --- a/crates/debugger_ui/src/tests/variable_list.rs +++ b/crates/debugger_ui/src/tests/variable_list.rs @@ -1538,6 +1538,8 @@ async fn test_variable_list_only_sends_requests_when_rendering( }) .await; + cx.run_until_parked(); + let running_state = active_debug_session_panel(workspace, cx).update_in(cx, |item, _, _| { let state = item .mode() diff --git a/crates/project/src/debugger/session.rs b/crates/project/src/debugger/session.rs index ff861771ef..401494ddcc 100644 --- a/crates/project/src/debugger/session.rs +++ b/crates/project/src/debugger/session.rs @@ -30,7 +30,8 @@ use dap::{ use futures::channel::oneshot; use futures::{FutureExt, future::Shared}; use gpui::{ - App, AppContext, AsyncApp, BackgroundExecutor, Context, Entity, EventEmitter, Task, WeakEntity, + App, AppContext, AsyncApp, BackgroundExecutor, Context, Entity, EventEmitter, SharedString, + Task, WeakEntity, }; use rpc::AnyProtoClient; use serde_json::{Value, json}; @@ -125,6 +126,7 @@ type UpstreamProjectId = u64; struct RemoteConnection { _client: AnyProtoClient, _upstream_project_id: UpstreamProjectId, + _adapter_name: SharedString, } impl RemoteConnection { @@ -996,6 +998,7 @@ impl Session { ) -> Self { Self { mode: Mode::Remote(RemoteConnection { + _adapter_name: SharedString::new(""), // todo(debugger) we need to pipe in the right values to deserialize the debugger pane layout _client: client, _upstream_project_id: upstream_project_id, }), @@ -1044,6 +1047,13 @@ impl Session { &self.capabilities } + pub fn adapter_name(&self) -> SharedString { + match &self.mode { + Mode::Local(local_mode) => local_mode.adapter.name().into(), + Mode::Remote(remote_mode) => remote_mode._adapter_name.clone(), + } + } + pub fn configuration(&self) -> Option { if let Mode::Local(local_mode) = &self.mode { Some(local_mode.config.clone())