persistence.rs

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