debugger: Remember pane layout from previous debugger session (#28692)
This PR makes a debugger's pane layout persistent across session's that use the same debug adapter. Release Notes: - N/A --------- Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Co-authored-by: Cole Miller <m@cole-miller.net>
This commit is contained in:
parent
b794919842
commit
d4761cea47
13 changed files with 550 additions and 150 deletions
259
crates/debugger_ui/src/persistence.rs
Normal file
259
crates/debugger_ui/src/persistence.rs
Normal file
|
@ -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<Vec<f32>>,
|
||||
children: Vec<SerializedPaneLayout>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub(crate) struct SerializedPane {
|
||||
pub children: Vec<DebuggerPaneItem>,
|
||||
pub active_item: Option<DebuggerPaneItem>,
|
||||
}
|
||||
|
||||
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::<Vec<_>>(),
|
||||
flexes: Some(flexes.lock().clone()),
|
||||
},
|
||||
Member::Pane(pane_handle) => SerializedPaneLayout::Pane(serialize_pane(pane_handle, cx)),
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_pane(pane: &Entity<Pane>, cx: &mut App) -> SerializedPane {
|
||||
let pane = pane.read(cx);
|
||||
let children = pane
|
||||
.items()
|
||||
.filter_map(|item| {
|
||||
item.act_as::<SubView>(cx)
|
||||
.map(|view| view.read(cx).view_kind())
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let active_item = pane
|
||||
.active_item()
|
||||
.and_then(|item| item.act_as::<SubView>(cx))
|
||||
.map(|view| view.read(cx).view_kind());
|
||||
|
||||
SerializedPane {
|
||||
children,
|
||||
active_item,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn get_serialized_pane_layout(
|
||||
adapter_name: impl AsRef<str>,
|
||||
) -> Option<SerializedPaneLayout> {
|
||||
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::<SerializedPaneLayout>(&value).ok())
|
||||
}
|
||||
|
||||
pub(crate) fn deserialize_pane_layout(
|
||||
serialized: SerializedPaneLayout,
|
||||
workspace: &WeakEntity<Workspace>,
|
||||
project: &Entity<Project>,
|
||||
stack_frame_list: &Entity<StackFrameList>,
|
||||
variable_list: &Entity<VariableList>,
|
||||
module_list: &Entity<ModuleList>,
|
||||
console: &Entity<Console>,
|
||||
breakpoint_list: &Entity<BreakpointList>,
|
||||
subscriptions: &mut HashMap<EntityId, Subscription>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<RunningState>,
|
||||
) -> Option<Member> {
|
||||
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()))
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue