1use collections::HashMap;
2use db::kvp::KEY_VALUE_STORE;
3use gpui::{Axis, Context, Entity, EntityId, Focusable, Subscription, WeakEntity, Window};
4use project::Project;
5use serde::{Deserialize, Serialize};
6use ui::{App, SharedString};
7use util::ResultExt;
8use workspace::{Member, Pane, PaneAxis, Workspace};
9
10use crate::session::running::{
11 self, RunningState, SubView, breakpoint_list::BreakpointList, console::Console,
12 module_list::ModuleList, stack_frame_list::StackFrameList, variable_list::VariableList,
13};
14
15#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
16pub(crate) enum DebuggerPaneItem {
17 Console,
18 Variables,
19 BreakpointList,
20 Frames,
21 Modules,
22}
23
24impl DebuggerPaneItem {
25 pub(crate) fn to_shared_string(self) -> SharedString {
26 match self {
27 DebuggerPaneItem::Console => SharedString::new_static("Console"),
28 DebuggerPaneItem::Variables => SharedString::new_static("Variables"),
29 DebuggerPaneItem::BreakpointList => SharedString::new_static("Breakpoints"),
30 DebuggerPaneItem::Frames => SharedString::new_static("Frames"),
31 DebuggerPaneItem::Modules => SharedString::new_static("Modules"),
32 }
33 }
34}
35
36#[derive(Debug, Serialize, Deserialize)]
37pub(crate) struct SerializedAxis(pub Axis);
38
39#[derive(Debug, Serialize, Deserialize)]
40pub(crate) enum SerializedPaneLayout {
41 Pane(SerializedPane),
42 Group {
43 axis: SerializedAxis,
44 flexes: Option<Vec<f32>>,
45 children: Vec<SerializedPaneLayout>,
46 },
47}
48
49#[derive(Debug, Serialize, Deserialize)]
50pub(crate) struct SerializedPane {
51 pub children: Vec<DebuggerPaneItem>,
52 pub active_item: Option<DebuggerPaneItem>,
53}
54
55const DEBUGGER_PANEL_PREFIX: &str = "debugger_panel_";
56
57pub(crate) async fn serialize_pane_layout(
58 adapter_name: SharedString,
59 pane_group: SerializedPaneLayout,
60) -> anyhow::Result<()> {
61 if let Ok(serialized_pane_group) = serde_json::to_string(&pane_group) {
62 KEY_VALUE_STORE
63 .write_kvp(
64 format!("{DEBUGGER_PANEL_PREFIX}-{adapter_name}"),
65 serialized_pane_group,
66 )
67 .await
68 } else {
69 Err(anyhow::anyhow!(
70 "Failed to serialize pane group with serde_json as a string"
71 ))
72 }
73}
74
75pub(crate) fn build_serialized_pane_layout(
76 pane_group: &Member,
77 cx: &mut App,
78) -> SerializedPaneLayout {
79 match pane_group {
80 Member::Axis(PaneAxis {
81 axis,
82 members,
83 flexes,
84 bounding_boxes: _,
85 }) => SerializedPaneLayout::Group {
86 axis: SerializedAxis(*axis),
87 children: members
88 .iter()
89 .map(|member| build_serialized_pane_layout(member, cx))
90 .collect::<Vec<_>>(),
91 flexes: Some(flexes.lock().clone()),
92 },
93 Member::Pane(pane_handle) => SerializedPaneLayout::Pane(serialize_pane(pane_handle, cx)),
94 }
95}
96
97fn serialize_pane(pane: &Entity<Pane>, cx: &mut App) -> SerializedPane {
98 let pane = pane.read(cx);
99 let children = pane
100 .items()
101 .filter_map(|item| {
102 item.act_as::<SubView>(cx)
103 .map(|view| view.read(cx).view_kind())
104 })
105 .collect::<Vec<_>>();
106
107 let active_item = pane
108 .active_item()
109 .and_then(|item| item.act_as::<SubView>(cx))
110 .map(|view| view.read(cx).view_kind());
111
112 SerializedPane {
113 children,
114 active_item,
115 }
116}
117
118pub(crate) async fn get_serialized_pane_layout(
119 adapter_name: impl AsRef<str>,
120) -> Option<SerializedPaneLayout> {
121 let key = format!("{DEBUGGER_PANEL_PREFIX}-{}", adapter_name.as_ref());
122
123 KEY_VALUE_STORE
124 .read_kvp(&key)
125 .log_err()
126 .flatten()
127 .and_then(|value| serde_json::from_str::<SerializedPaneLayout>(&value).ok())
128}
129
130pub(crate) fn deserialize_pane_layout(
131 serialized: SerializedPaneLayout,
132 workspace: &WeakEntity<Workspace>,
133 project: &Entity<Project>,
134 stack_frame_list: &Entity<StackFrameList>,
135 variable_list: &Entity<VariableList>,
136 module_list: &Entity<ModuleList>,
137 console: &Entity<Console>,
138 breakpoint_list: &Entity<BreakpointList>,
139 subscriptions: &mut HashMap<EntityId, Subscription>,
140 window: &mut Window,
141 cx: &mut Context<RunningState>,
142) -> Option<Member> {
143 match serialized {
144 SerializedPaneLayout::Group {
145 axis,
146 flexes,
147 children,
148 } => {
149 let mut members = Vec::new();
150 for child in children {
151 if let Some(new_member) = deserialize_pane_layout(
152 child,
153 workspace,
154 project,
155 stack_frame_list,
156 variable_list,
157 module_list,
158 console,
159 breakpoint_list,
160 subscriptions,
161 window,
162 cx,
163 ) {
164 members.push(new_member);
165 }
166 }
167
168 if members.is_empty() {
169 return None;
170 }
171
172 if members.len() == 1 {
173 return Some(members.remove(0));
174 }
175
176 Some(Member::Axis(PaneAxis::load(
177 axis.0,
178 members,
179 flexes.clone(),
180 )))
181 }
182 SerializedPaneLayout::Pane(serialized_pane) => {
183 let pane = running::new_debugger_pane(workspace.clone(), project.clone(), window, cx);
184 subscriptions.insert(
185 pane.entity_id(),
186 cx.subscribe_in(&pane, window, RunningState::handle_pane_event),
187 );
188
189 let sub_views: Vec<_> = serialized_pane
190 .children
191 .iter()
192 .map(|child| match child {
193 DebuggerPaneItem::Frames => Box::new(SubView::new(
194 pane.focus_handle(cx),
195 stack_frame_list.clone().into(),
196 DebuggerPaneItem::Frames,
197 None,
198 cx,
199 )),
200 DebuggerPaneItem::Variables => Box::new(SubView::new(
201 variable_list.focus_handle(cx),
202 variable_list.clone().into(),
203 DebuggerPaneItem::Variables,
204 None,
205 cx,
206 )),
207 DebuggerPaneItem::BreakpointList => Box::new(SubView::new(
208 breakpoint_list.focus_handle(cx),
209 breakpoint_list.clone().into(),
210 DebuggerPaneItem::BreakpointList,
211 None,
212 cx,
213 )),
214 DebuggerPaneItem::Modules => Box::new(SubView::new(
215 pane.focus_handle(cx),
216 module_list.clone().into(),
217 DebuggerPaneItem::Modules,
218 None,
219 cx,
220 )),
221
222 DebuggerPaneItem::Console => Box::new(SubView::new(
223 pane.focus_handle(cx),
224 console.clone().into(),
225 DebuggerPaneItem::Console,
226 Some(Box::new({
227 let console = console.clone().downgrade();
228 move |cx| {
229 console
230 .read_with(cx, |console, cx| console.show_indicator(cx))
231 .unwrap_or_default()
232 }
233 })),
234 cx,
235 )),
236 })
237 .collect();
238
239 pane.update(cx, |pane, cx| {
240 let mut active_idx = 0;
241 for (idx, sub_view) in sub_views.into_iter().enumerate() {
242 if serialized_pane
243 .active_item
244 .is_some_and(|active| active == sub_view.read(cx).view_kind())
245 {
246 active_idx = idx;
247 }
248 pane.add_item(sub_view, false, false, None, window, cx);
249 }
250
251 pane.activate_item(active_idx, false, false, window, cx);
252 });
253
254 Some(Member::Pane(pane.clone()))
255 }
256 }
257}