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
 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}