debugger: Add breakpoint list to the empty state of debug panel (#32930)

Piotr Osiewicz created

![image](https://github.com/user-attachments/assets/3c80855a-3046-42b6-a1a7-409b03cd735d)

Release Notes:

- Debugger: Added breakpoint list to the empty debug panel

Change summary

crates/debugger_ui/src/debugger_panel.rs                  | 14 +
crates/debugger_ui/src/session/running.rs                 |  3 
crates/debugger_ui/src/session/running/breakpoint_list.rs | 60 ++++----
3 files changed, 44 insertions(+), 33 deletions(-)

Detailed changes

crates/debugger_ui/src/debugger_panel.rs 🔗

@@ -1,6 +1,7 @@
 use crate::persistence::DebuggerPaneItem;
 use crate::session::DebugSession;
 use crate::session::running::RunningState;
+use crate::session::running::breakpoint_list::BreakpointList;
 use crate::{
     ClearAllBreakpoints, Continue, CopyDebugAdapterArguments, Detach, FocusBreakpointList,
     FocusConsole, FocusFrames, FocusLoadedSources, FocusModules, FocusTerminal, FocusVariables,
@@ -72,6 +73,7 @@ pub struct DebugPanel {
     fs: Arc<dyn Fs>,
     is_zoomed: bool,
     _subscriptions: [Subscription; 1],
+    breakpoint_list: Entity<BreakpointList>,
 }
 
 impl DebugPanel {
@@ -99,6 +101,7 @@ impl DebugPanel {
                 sessions: vec![],
                 active_session: None,
                 focus_handle,
+                breakpoint_list: BreakpointList::new(None, workspace.weak_handle(), &project, cx),
                 project,
                 workspace: workspace.weak_handle(),
                 context_menu: None,
@@ -1459,11 +1462,16 @@ impl Render for DebugPanel {
                             .items_center()
                             .justify_center()
                             .child(
-                                h_flex()
+                                h_flex().size_full()
                                     .items_start()
-                                    .gap_8()
+
+                                    .child(v_flex().items_start().min_w_1_3().h_full().p_1()
+                                        .child(h_flex().px_1().child(Label::new("Breakpoints").size(LabelSize::Small)))
+                                        .child(Divider::horizontal())
+                                        .child(self.breakpoint_list.clone()))
+                                    .child(Divider::vertical())
                                     .child(
-                                        v_flex()
+                                        v_flex().w_2_3().h_full().items_center().justify_center()
                                             .gap_2()
                                             .pr_8()
                                             .child(

crates/debugger_ui/src/session/running.rs 🔗

@@ -638,7 +638,8 @@ impl RunningState {
             )
         });
 
-        let breakpoint_list = BreakpointList::new(session.clone(), workspace.clone(), &project, cx);
+        let breakpoint_list =
+            BreakpointList::new(Some(session.clone()), workspace.clone(), &project, cx);
 
         let _subscriptions = vec![
             cx.observe(&module_list, |_, _, cx| cx.notify()),

crates/debugger_ui/src/session/running/breakpoint_list.rs 🔗

@@ -36,7 +36,7 @@ pub(crate) struct BreakpointList {
     worktree_store: Entity<WorktreeStore>,
     scrollbar_state: ScrollbarState,
     breakpoints: Vec<BreakpointEntry>,
-    session: Entity<Session>,
+    session: Option<Entity<Session>>,
     hide_scrollbar_task: Option<Task<()>>,
     show_scrollbar: bool,
     focus_handle: FocusHandle,
@@ -51,8 +51,8 @@ impl Focusable for BreakpointList {
 }
 
 impl BreakpointList {
-    pub(super) fn new(
-        session: Entity<Session>,
+    pub(crate) fn new(
+        session: Option<Entity<Session>>,
         workspace: WeakEntity<Workspace>,
         project: &Entity<Project>,
         cx: &mut App,
@@ -64,21 +64,18 @@ impl BreakpointList {
         let scroll_handle = UniformListScrollHandle::new();
         let scrollbar_state = ScrollbarState::new(scroll_handle.clone());
 
-        cx.new(|_| {
-            Self {
-                breakpoint_store,
-                worktree_store,
-                scrollbar_state,
-                // list_state,
-                breakpoints: Default::default(),
-                hide_scrollbar_task: None,
-                show_scrollbar: false,
-                workspace,
-                session,
-                focus_handle,
-                scroll_handle,
-                selected_ix: None,
-            }
+        cx.new(|_| Self {
+            breakpoint_store,
+            worktree_store,
+            scrollbar_state,
+            breakpoints: Default::default(),
+            hide_scrollbar_task: None,
+            show_scrollbar: false,
+            workspace,
+            session,
+            focus_handle,
+            scroll_handle,
+            selected_ix: None,
         })
     }
 
@@ -229,10 +226,12 @@ impl BreakpointList {
                 self.edit_line_breakpoint(path, row, BreakpointEditAction::InvertState, cx);
             }
             BreakpointEntryKind::ExceptionBreakpoint(exception_breakpoint) => {
-                let id = exception_breakpoint.id.clone();
-                self.session.update(cx, |session, cx| {
-                    session.toggle_exception_breakpoint(&id, cx);
-                });
+                if let Some(session) = &self.session {
+                    let id = exception_breakpoint.id.clone();
+                    session.update(cx, |session, cx| {
+                        session.toggle_exception_breakpoint(&id, cx);
+                    });
+                }
             }
         }
         cx.notify();
@@ -385,8 +384,8 @@ impl Render for BreakpointList {
                 })
             })
         });
-        let exception_breakpoints =
-            self.session
+        let exception_breakpoints = self.session.as_ref().into_iter().flat_map(|session| {
+            session
                 .read(cx)
                 .exception_breakpoints()
                 .map(|(data, is_enabled)| BreakpointEntry {
@@ -396,7 +395,8 @@ impl Render for BreakpointList {
                         is_enabled: *is_enabled,
                     }),
                     weak: weak.clone(),
-                });
+                })
+        });
         self.breakpoints
             .extend(breakpoints.chain(exception_breakpoints));
         v_flex()
@@ -639,10 +639,12 @@ impl ExceptionBreakpoint {
                     let list = list.clone();
                     move |_, _, cx| {
                         list.update(cx, |this, cx| {
-                            this.session.update(cx, |this, cx| {
-                                this.toggle_exception_breakpoint(&id, cx);
-                            });
-                            cx.notify();
+                            if let Some(session) = &this.session {
+                                session.update(cx, |this, cx| {
+                                    this.toggle_exception_breakpoint(&id, cx);
+                                });
+                                cx.notify();
+                            }
                         })
                         .ok();
                     }