debugger: Update the default layout (#31057)

- 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>
This commit is contained in:
Cole Miller 2025-05-22 00:32:44 -04:00 committed by GitHub
parent 97e437c632
commit 71fb17c507
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 85 additions and 77 deletions

View file

@ -628,10 +628,9 @@ impl RunningState {
&workspace, &workspace,
&stack_frame_list, &stack_frame_list,
&variable_list, &variable_list,
&module_list,
&loaded_source_list,
&console, &console,
&breakpoint_list, &breakpoint_list,
&debug_terminal,
dock_axis, dock_axis,
&mut pane_close_subscriptions, &mut pane_close_subscriptions,
window, window,
@ -1468,10 +1467,9 @@ impl RunningState {
workspace: &WeakEntity<Workspace>, workspace: &WeakEntity<Workspace>,
stack_frame_list: &Entity<StackFrameList>, stack_frame_list: &Entity<StackFrameList>,
variable_list: &Entity<VariableList>, variable_list: &Entity<VariableList>,
module_list: &Entity<ModuleList>,
loaded_source_list: &Entity<LoadedSourceList>,
console: &Entity<Console>, console: &Entity<Console>,
breakpoints: &Entity<BreakpointList>, breakpoints: &Entity<BreakpointList>,
debug_terminal: &Entity<DebugTerminal>,
dock_axis: Axis, dock_axis: Axis,
subscriptions: &mut HashMap<EntityId, Subscription>, subscriptions: &mut HashMap<EntityId, Subscription>,
window: &mut Window, window: &mut Window,
@ -1512,6 +1510,26 @@ impl RunningState {
let center_pane = new_debugger_pane(workspace.clone(), project.clone(), window, cx); let center_pane = new_debugger_pane(workspace.clone(), project.clone(), window, cx);
center_pane.update(cx, |this, cx| { center_pane.update(cx, |this, cx| {
let weak_console = console.downgrade();
this.add_item(
Box::new(SubView::new(
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,
false,
None,
window,
cx,
);
this.add_item( this.add_item(
Box::new(SubView::new( Box::new(SubView::new(
variable_list.focus_handle(cx), variable_list.focus_handle(cx),
@ -1526,54 +1544,20 @@ impl RunningState {
window, window,
cx, 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,
None,
cx,
)),
false,
false,
None,
window,
cx,
);
this.activate_item(0, false, false, window, cx); this.activate_item(0, false, false, window, cx);
}); });
let rightmost_pane = new_debugger_pane(workspace.clone(), project.clone(), window, cx); let rightmost_pane = new_debugger_pane(workspace.clone(), project.clone(), window, cx);
rightmost_pane.update(cx, |this, cx| { rightmost_pane.update(cx, |this, cx| {
let weak_console = console.downgrade();
this.add_item( this.add_item(
Box::new(SubView::new( Box::new(SubView::new(
this.focus_handle(cx), debug_terminal.focus_handle(cx),
console.clone().into(), debug_terminal.clone().into(),
DebuggerPaneItem::Console, DebuggerPaneItem::Terminal,
Some(Box::new(move |cx| { None,
weak_console
.read_with(cx, |console, cx| console.show_indicator(cx))
.unwrap_or_default()
})),
cx, cx,
)), )),
true, false,
false, false,
None, None,
window, window,

View file

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

View file

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

View file

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

View file

@ -5,6 +5,7 @@ use std::sync::{
use crate::{ use crate::{
DebugPanel, DebugPanel,
persistence::DebuggerPaneItem,
session::running::variable_list::{CollapseSelectedEntry, ExpandSelectedEntry}, session::running::variable_list::{CollapseSelectedEntry, ExpandSelectedEntry},
tests::{active_debug_session_panel, init_test, init_test_workspace, start_debug_session}, 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); cx.focus_self(window);
let running = item.running_state().clone(); 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)); variable_list.update(cx, |_, cx| cx.focus_self(window));
running running
}); });

View file

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

View file

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