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