persistence.rs

  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}