debugger: Update the default layout (#31057)

Cole Miller and Remco Smits created

- Remove the modules list and loaded sources list from the default
layout
- Move the console to the center pane so it's visible initially

Release Notes:

- Debugger Beta: changed the default layout of the debugger panel,
hiding the modules list and loaded sources list by default and making
the console more prominent.

---------

Co-authored-by: Remco Smits <djsmits12@gmail.com>

Change summary

crates/debugger_ui/src/session/running.rs               | 56 +++-------
crates/debugger_ui/src/session/running/console.rs       | 16 ++
crates/debugger_ui/src/session/running/variable_list.rs | 24 ++--
crates/debugger_ui/src/tests/module_list.rs             |  4 
crates/debugger_ui/src/tests/variable_list.rs           |  9 +
crates/editor/src/editor.rs                             |  1 
crates/project/src/debugger/session.rs                  | 38 ++++---
7 files changed, 78 insertions(+), 70 deletions(-)

Detailed changes

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

@@ -628,10 +628,9 @@ impl RunningState {
                 &workspace,
                 &stack_frame_list,
                 &variable_list,
-                &module_list,
-                &loaded_source_list,
                 &console,
                 &breakpoint_list,
+                &debug_terminal,
                 dock_axis,
                 &mut pane_close_subscriptions,
                 window,
@@ -1468,10 +1467,9 @@ impl RunningState {
         workspace: &WeakEntity<Workspace>,
         stack_frame_list: &Entity<StackFrameList>,
         variable_list: &Entity<VariableList>,
-        module_list: &Entity<ModuleList>,
-        loaded_source_list: &Entity<LoadedSourceList>,
         console: &Entity<Console>,
         breakpoints: &Entity<BreakpointList>,
+        debug_terminal: &Entity<DebugTerminal>,
         dock_axis: Axis,
         subscriptions: &mut HashMap<EntityId, Subscription>,
         window: &mut Window,
@@ -1512,12 +1510,17 @@ impl RunningState {
         let center_pane = new_debugger_pane(workspace.clone(), project.clone(), window, cx);
 
         center_pane.update(cx, |this, cx| {
+            let weak_console = console.downgrade();
             this.add_item(
                 Box::new(SubView::new(
-                    variable_list.focus_handle(cx),
-                    variable_list.clone().into(),
-                    DebuggerPaneItem::Variables,
-                    None,
+                    console.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,
@@ -1526,30 +1529,16 @@ impl RunningState {
                 window,
                 cx,
             );
-            this.add_item(
-                Box::new(SubView::new(
-                    module_list.focus_handle(cx),
-                    module_list.clone().into(),
-                    DebuggerPaneItem::Modules,
-                    None,
-                    cx,
-                )),
-                false,
-                false,
-                None,
-                window,
-                cx,
-            );
 
             this.add_item(
                 Box::new(SubView::new(
-                    loaded_source_list.focus_handle(cx),
-                    loaded_source_list.clone().into(),
-                    DebuggerPaneItem::LoadedSources,
+                    variable_list.focus_handle(cx),
+                    variable_list.clone().into(),
+                    DebuggerPaneItem::Variables,
                     None,
                     cx,
                 )),
-                false,
+                true,
                 false,
                 None,
                 window,
@@ -1560,20 +1549,15 @@ impl RunningState {
 
         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()
-                    })),
+                    debug_terminal.focus_handle(cx),
+                    debug_terminal.clone().into(),
+                    DebuggerPaneItem::Terminal,
+                    None,
                     cx,
                 )),
-                true,
+                false,
                 false,
                 None,
                 window,

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

@@ -14,7 +14,7 @@ use language::{Buffer, CodeLabel, ToOffset};
 use menu::Confirm;
 use project::{
     Completion,
-    debugger::session::{CompletionsQuery, OutputToken, Session},
+    debugger::session::{CompletionsQuery, OutputToken, Session, SessionEvent},
 };
 use settings::Settings;
 use std::{cell::RefCell, rc::Rc, usize};
@@ -79,6 +79,11 @@ impl Console {
 
         let _subscriptions = vec![
             cx.subscribe(&stack_frame_list, Self::handle_stack_frame_list_events),
+            cx.subscribe_in(&session, window, |this, _, event, window, cx| {
+                if let SessionEvent::ConsoleOutput = event {
+                    this.update_output(window, cx)
+                }
+            }),
             cx.on_focus_in(&focus_handle, window, |console, window, cx| {
                 if console.is_running(cx) {
                     console.query_bar.focus_handle(cx).focus(window);
@@ -200,12 +205,11 @@ impl Console {
     fn render_query_bar(&self, cx: &Context<Self>) -> impl IntoElement {
         EditorElement::new(&self.query_bar, self.editor_style(cx))
     }
-}
 
-impl Render for Console {
-    fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
+    fn update_output(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         let session = self.session.clone();
         let token = self.last_token;
+
         self.update_output_task = cx.spawn_in(window, async move |this, cx| {
             _ = session.update_in(cx, move |session, window, cx| {
                 let (output, last_processed_token) = session.output(token);
@@ -220,7 +224,11 @@ impl Render for Console {
                 });
             });
         });
+    }
+}
 
+impl Render for Console {
+    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         v_flex()
             .track_focus(&self.focus_handle)
             .key_context("DebugConsole")

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

@@ -154,12 +154,15 @@ impl VariableList {
 
         let _subscriptions = vec![
             cx.subscribe(&stack_frame_list, Self::handle_stack_frame_list_events),
-            cx.subscribe(&session, |this, _, event, _| match event {
+            cx.subscribe(&session, |this, _, event, cx| match event {
                 SessionEvent::Stopped(_) => {
                     this.selection.take();
                     this.edited_path.take();
                     this.selected_stack_frame_id.take();
                 }
+                SessionEvent::Variables => {
+                    this.build_entries(cx);
+                }
                 _ => {}
             }),
             cx.on_focus_out(&focus_handle, window, |this, _, _, cx| {
@@ -300,7 +303,7 @@ impl VariableList {
         match event {
             StackFrameListEvent::SelectedStackFrameChanged(stack_frame_id) => {
                 self.selected_stack_frame_id = Some(*stack_frame_id);
-                cx.notify();
+                self.build_entries(cx);
             }
             StackFrameListEvent::BuiltEntries => {}
         }
@@ -344,14 +347,14 @@ impl VariableList {
         };
 
         entry.is_expanded = !entry.is_expanded;
-        cx.notify();
+        self.build_entries(cx);
     }
 
     fn select_first(&mut self, _: &SelectFirst, window: &mut Window, cx: &mut Context<Self>) {
         self.cancel_variable_edit(&Default::default(), window, cx);
         if let Some(variable) = self.entries.first() {
             self.selection = Some(variable.path.clone());
-            cx.notify();
+            self.build_entries(cx);
         }
     }
 
@@ -359,7 +362,7 @@ impl VariableList {
         self.cancel_variable_edit(&Default::default(), window, cx);
         if let Some(variable) = self.entries.last() {
             self.selection = Some(variable.path.clone());
-            cx.notify();
+            self.build_entries(cx);
         }
     }
 
@@ -378,7 +381,7 @@ impl VariableList {
                 index.and_then(|ix| self.entries.get(ix).map(|var| var.path.clone()))
             {
                 self.selection = Some(new_selection);
-                cx.notify();
+                self.build_entries(cx);
             } else {
                 self.select_last(&SelectLast, window, cx);
             }
@@ -402,7 +405,7 @@ impl VariableList {
                 index.and_then(|ix| self.entries.get(ix).map(|var| var.path.clone()))
             {
                 self.selection = Some(new_selection);
-                cx.notify();
+                self.build_entries(cx);
             } else {
                 self.select_first(&SelectFirst, window, cx);
             }
@@ -464,7 +467,7 @@ impl VariableList {
                 self.select_prev(&SelectPrevious, window, cx);
             } else {
                 entry_state.is_expanded = false;
-                cx.notify();
+                self.build_entries(cx);
             }
         }
     }
@@ -485,7 +488,7 @@ impl VariableList {
                 self.select_next(&SelectNext, window, cx);
             } else {
                 entry_state.is_expanded = true;
-                cx.notify();
+                self.build_entries(cx);
             }
         }
     }
@@ -929,8 +932,6 @@ impl Focusable for VariableList {
 
 impl Render for VariableList {
     fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
-        self.build_entries(cx);
-
         v_flex()
             .track_focus(&self.focus_handle)
             .key_context("VariableList")
@@ -946,7 +947,6 @@ impl Render for VariableList {
             .on_action(cx.listener(Self::collapse_selected_entry))
             .on_action(cx.listener(Self::cancel_variable_edit))
             .on_action(cx.listener(Self::confirm_variable_edit))
-            //
             .child(
                 uniform_list(
                     cx.entity().clone(),

crates/debugger_ui/src/tests/module_list.rs 🔗

@@ -1,5 +1,6 @@
 use crate::{
     debugger_panel::DebugPanel,
+    persistence::DebuggerPaneItem,
     tests::{active_debug_session_panel, init_test, init_test_workspace, start_debug_session},
 };
 use dap::{
@@ -110,7 +111,8 @@ async fn test_module_list(executor: BackgroundExecutor, cx: &mut TestAppContext)
         });
 
     running_state.update_in(cx, |this, window, cx| {
-        this.activate_item(crate::persistence::DebuggerPaneItem::Modules, window, cx);
+        this.ensure_pane_item(DebuggerPaneItem::Modules, window, cx);
+        this.activate_item(DebuggerPaneItem::Modules, window, cx);
         cx.refresh_windows();
     });
 

crates/debugger_ui/src/tests/variable_list.rs 🔗

@@ -5,6 +5,7 @@ use std::sync::{
 
 use crate::{
     DebugPanel,
+    persistence::DebuggerPaneItem,
     session::running::variable_list::{CollapseSelectedEntry, ExpandSelectedEntry},
     tests::{active_debug_session_panel, init_test, init_test_workspace, start_debug_session},
 };
@@ -706,7 +707,13 @@ async fn test_keyboard_navigation(executor: BackgroundExecutor, cx: &mut TestApp
             cx.focus_self(window);
             let running = item.running_state().clone();
 
-            let variable_list = running.read_with(cx, |state, _| state.variable_list().clone());
+            let variable_list = running.update(cx, |state, cx| {
+                // have to do this because the variable list pane should be shown/active
+                // for testing keyboard navigation
+                state.activate_item(DebuggerPaneItem::Variables, window, cx);
+
+                state.variable_list().clone()
+            });
             variable_list.update(cx, |_, cx| cx.focus_self(window));
             running
         });

crates/editor/src/editor.rs 🔗

@@ -20244,6 +20244,7 @@ impl SemanticsProvider for Entity<Project> {
     fn inline_values(
         &self,
         buffer_handle: Entity<Buffer>,
+
         range: Range<text::Anchor>,
         cx: &mut App,
     ) -> Option<Task<anyhow::Result<Vec<InlayHint>>>> {

crates/project/src/debugger/session.rs 🔗

@@ -24,7 +24,7 @@ use dap::{
     messages::{Events, Message},
 };
 use dap::{
-    ExceptionBreakpointsFilter, ExceptionFilterOptions, OutputEventCategory,
+    ExceptionBreakpointsFilter, ExceptionFilterOptions, OutputEvent, OutputEventCategory,
     RunInTerminalRequestArguments, StartDebuggingRequestArguments,
 };
 use futures::channel::{mpsc, oneshot};
@@ -674,6 +674,7 @@ pub enum SessionEvent {
         request: RunInTerminalRequestArguments,
         sender: mpsc::Sender<Result<u32>>,
     },
+    ConsoleOutput,
 }
 
 #[derive(Clone, Debug, PartialEq, Eq)]
@@ -885,9 +886,8 @@ impl Session {
 
         cx.spawn(async move |this, cx| {
             while let Some(output) = rx.next().await {
-                this.update(cx, |this, _| {
-                    this.output_token.0 += 1;
-                    this.output.push_back(dap::OutputEvent {
+                this.update(cx, |this, cx| {
+                    let event = dap::OutputEvent {
                         category: None,
                         output,
                         group: None,
@@ -897,7 +897,8 @@ impl Session {
                         column: None,
                         data: None,
                         location_reference: None,
-                    });
+                    };
+                    this.push_output(event, cx);
                 })?;
             }
             anyhow::Ok(())
@@ -1266,8 +1267,7 @@ impl Session {
                     return;
                 }
 
-                self.output.push_back(event);
-                self.output_token.0 += 1;
+                self.push_output(event, cx);
                 cx.notify();
             }
             Events::Breakpoint(event) => self.breakpoint_store.update(cx, |store, _| {
@@ -1445,6 +1445,12 @@ impl Session {
             });
     }
 
+    fn push_output(&mut self, event: OutputEvent, cx: &mut Context<Self>) {
+        self.output.push_back(event);
+        self.output_token.0 += 1;
+        cx.emit(SessionEvent::ConsoleOutput);
+    }
+
     pub fn any_stopped_thread(&self) -> bool {
         self.thread_states.any_stopped_thread()
     }
@@ -2063,8 +2069,7 @@ impl Session {
         source: Option<Source>,
         cx: &mut Context<Self>,
     ) -> Task<()> {
-        self.output_token.0 += 1;
-        self.output.push_back(dap::OutputEvent {
+        let event = dap::OutputEvent {
             category: None,
             output: format!("> {expression}"),
             group: None,
@@ -2074,7 +2079,8 @@ impl Session {
             column: None,
             data: None,
             location_reference: None,
-        });
+        };
+        self.push_output(event, cx);
         let request = self.mode.request_dap(EvaluateCommand {
             expression,
             context,
@@ -2086,8 +2092,7 @@ impl Session {
             this.update(cx, |this, cx| {
                 match response {
                     Ok(response) => {
-                        this.output_token.0 += 1;
-                        this.output.push_back(dap::OutputEvent {
+                        let event = dap::OutputEvent {
                             category: None,
                             output: format!("< {}", &response.result),
                             group: None,
@@ -2097,11 +2102,11 @@ impl Session {
                             column: None,
                             data: None,
                             location_reference: None,
-                        });
+                        };
+                        this.push_output(event, cx);
                     }
                     Err(e) => {
-                        this.output_token.0 += 1;
-                        this.output.push_back(dap::OutputEvent {
+                        let event = dap::OutputEvent {
                             category: None,
                             output: format!("{}", e),
                             group: None,
@@ -2111,7 +2116,8 @@ impl Session {
                             column: None,
                             data: None,
                             location_reference: None,
-                        });
+                        };
+                        this.push_output(event, cx);
                     }
                 };
                 this.invalidate_command_type::<ScopesCommand>();