1use super::{SerializedAxis, SerializedWindowBounds};
2use crate::{
3 Member, Pane, PaneAxis, SerializableItemRegistry, Workspace, WorkspaceId, item::ItemHandle,
4 path_list::PathList,
5};
6use anyhow::Result;
7use async_recursion::async_recursion;
8use db::sqlez::{
9 bindable::{Bind, Column, StaticColumnCount},
10 statement::Statement,
11};
12use gpui::{AsyncWindowContext, Entity, WeakEntity};
13
14use project::{Project, debugger::breakpoint_store::SourceBreakpoint};
15use remote::ssh_session::SshProjectId;
16use serde::{Deserialize, Serialize};
17use std::{
18 collections::BTreeMap,
19 path::{Path, PathBuf},
20 sync::Arc,
21};
22use util::ResultExt;
23use uuid::Uuid;
24
25#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
26pub struct SerializedSshConnection {
27 pub id: SshProjectId,
28 pub host: String,
29 pub port: Option<u16>,
30 pub user: Option<String>,
31}
32
33#[derive(Debug, PartialEq, Clone)]
34pub enum SerializedWorkspaceLocation {
35 Local,
36 Ssh(SerializedSshConnection),
37}
38
39impl SerializedWorkspaceLocation {
40 /// Get sorted paths
41 pub fn sorted_paths(&self) -> Arc<Vec<PathBuf>> {
42 unimplemented!()
43 }
44}
45
46#[derive(Debug, PartialEq, Clone)]
47pub(crate) struct SerializedWorkspace {
48 pub(crate) id: WorkspaceId,
49 pub(crate) location: SerializedWorkspaceLocation,
50 pub(crate) paths: PathList,
51 pub(crate) center_group: SerializedPaneGroup,
52 pub(crate) window_bounds: Option<SerializedWindowBounds>,
53 pub(crate) centered_layout: bool,
54 pub(crate) display: Option<Uuid>,
55 pub(crate) docks: DockStructure,
56 pub(crate) session_id: Option<String>,
57 pub(crate) breakpoints: BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
58 pub(crate) window_id: Option<u64>,
59}
60
61#[derive(Debug, PartialEq, Clone, Default)]
62pub struct DockStructure {
63 pub(crate) left: DockData,
64 pub(crate) right: DockData,
65 pub(crate) bottom: DockData,
66}
67
68impl Column for DockStructure {
69 fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
70 let (left, next_index) = DockData::column(statement, start_index)?;
71 let (right, next_index) = DockData::column(statement, next_index)?;
72 let (bottom, next_index) = DockData::column(statement, next_index)?;
73 Ok((
74 DockStructure {
75 left,
76 right,
77 bottom,
78 },
79 next_index,
80 ))
81 }
82}
83
84impl Bind for DockStructure {
85 fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
86 let next_index = statement.bind(&self.left, start_index)?;
87 let next_index = statement.bind(&self.right, next_index)?;
88 statement.bind(&self.bottom, next_index)
89 }
90}
91
92#[derive(Debug, PartialEq, Clone, Default)]
93pub struct DockData {
94 pub(crate) visible: bool,
95 pub(crate) active_panel: Option<String>,
96 pub(crate) zoom: bool,
97}
98
99impl Column for DockData {
100 fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
101 let (visible, next_index) = Option::<bool>::column(statement, start_index)?;
102 let (active_panel, next_index) = Option::<String>::column(statement, next_index)?;
103 let (zoom, next_index) = Option::<bool>::column(statement, next_index)?;
104 Ok((
105 DockData {
106 visible: visible.unwrap_or(false),
107 active_panel,
108 zoom: zoom.unwrap_or(false),
109 },
110 next_index,
111 ))
112 }
113}
114
115impl Bind for DockData {
116 fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
117 let next_index = statement.bind(&self.visible, start_index)?;
118 let next_index = statement.bind(&self.active_panel, next_index)?;
119 statement.bind(&self.zoom, next_index)
120 }
121}
122
123#[derive(Debug, PartialEq, Clone)]
124pub(crate) enum SerializedPaneGroup {
125 Group {
126 axis: SerializedAxis,
127 flexes: Option<Vec<f32>>,
128 children: Vec<SerializedPaneGroup>,
129 },
130 Pane(SerializedPane),
131}
132
133#[cfg(test)]
134impl Default for SerializedPaneGroup {
135 fn default() -> Self {
136 Self::Pane(SerializedPane {
137 children: vec![SerializedItem::default()],
138 active: false,
139 pinned_count: 0,
140 })
141 }
142}
143
144impl SerializedPaneGroup {
145 #[async_recursion(?Send)]
146 pub(crate) async fn deserialize(
147 self,
148 project: &Entity<Project>,
149 workspace_id: WorkspaceId,
150 workspace: WeakEntity<Workspace>,
151 cx: &mut AsyncWindowContext,
152 ) -> Option<(
153 Member,
154 Option<Entity<Pane>>,
155 Vec<Option<Box<dyn ItemHandle>>>,
156 )> {
157 match self {
158 SerializedPaneGroup::Group {
159 axis,
160 children,
161 flexes,
162 } => {
163 let mut current_active_pane = None;
164 let mut members = Vec::new();
165 let mut items = Vec::new();
166 for child in children {
167 if let Some((new_member, active_pane, new_items)) = child
168 .deserialize(project, workspace_id, workspace.clone(), cx)
169 .await
170 {
171 members.push(new_member);
172 items.extend(new_items);
173 current_active_pane = current_active_pane.or(active_pane);
174 }
175 }
176
177 if members.is_empty() {
178 return None;
179 }
180
181 if members.len() == 1 {
182 return Some((members.remove(0), current_active_pane, items));
183 }
184
185 Some((
186 Member::Axis(PaneAxis::load(axis.0, members, flexes)),
187 current_active_pane,
188 items,
189 ))
190 }
191 SerializedPaneGroup::Pane(serialized_pane) => {
192 let pane = workspace
193 .update_in(cx, |workspace, window, cx| {
194 workspace.add_pane(window, cx).downgrade()
195 })
196 .log_err()?;
197 let active = serialized_pane.active;
198 let new_items = serialized_pane
199 .deserialize_to(project, &pane, workspace_id, workspace.clone(), cx)
200 .await
201 .log_err()?;
202
203 if pane
204 .read_with(cx, |pane, _| pane.items_len() != 0)
205 .log_err()?
206 {
207 let pane = pane.upgrade()?;
208 Some((
209 Member::Pane(pane.clone()),
210 active.then_some(pane),
211 new_items,
212 ))
213 } else {
214 let pane = pane.upgrade()?;
215 workspace
216 .update_in(cx, |workspace, window, cx| {
217 workspace.force_remove_pane(&pane, &None, window, cx)
218 })
219 .log_err()?;
220 None
221 }
222 }
223 }
224 }
225}
226
227#[derive(Debug, PartialEq, Eq, Default, Clone)]
228pub struct SerializedPane {
229 pub(crate) active: bool,
230 pub(crate) children: Vec<SerializedItem>,
231 pub(crate) pinned_count: usize,
232}
233
234impl SerializedPane {
235 pub fn new(children: Vec<SerializedItem>, active: bool, pinned_count: usize) -> Self {
236 SerializedPane {
237 children,
238 active,
239 pinned_count,
240 }
241 }
242
243 pub async fn deserialize_to(
244 &self,
245 project: &Entity<Project>,
246 pane: &WeakEntity<Pane>,
247 workspace_id: WorkspaceId,
248 workspace: WeakEntity<Workspace>,
249 cx: &mut AsyncWindowContext,
250 ) -> Result<Vec<Option<Box<dyn ItemHandle>>>> {
251 let mut item_tasks = Vec::new();
252 let mut active_item_index = None;
253 let mut preview_item_index = None;
254 for (index, item) in self.children.iter().enumerate() {
255 let project = project.clone();
256 item_tasks.push(pane.update_in(cx, |_, window, cx| {
257 SerializableItemRegistry::deserialize(
258 &item.kind,
259 project,
260 workspace.clone(),
261 workspace_id,
262 item.item_id,
263 window,
264 cx,
265 )
266 })?);
267 if item.active {
268 active_item_index = Some(index);
269 }
270 if item.preview {
271 preview_item_index = Some(index);
272 }
273 }
274
275 let mut items = Vec::new();
276 for item_handle in futures::future::join_all(item_tasks).await {
277 let item_handle = item_handle.log_err();
278 items.push(item_handle.clone());
279
280 if let Some(item_handle) = item_handle {
281 pane.update_in(cx, |pane, window, cx| {
282 pane.add_item(item_handle.clone(), true, true, None, window, cx);
283 })?;
284 }
285 }
286
287 if let Some(active_item_index) = active_item_index {
288 pane.update_in(cx, |pane, window, cx| {
289 pane.activate_item(active_item_index, false, false, window, cx);
290 })?;
291 }
292
293 if let Some(preview_item_index) = preview_item_index {
294 pane.update(cx, |pane, cx| {
295 if let Some(item) = pane.item_for_index(preview_item_index) {
296 pane.set_preview_item_id(Some(item.item_id()), cx);
297 }
298 })?;
299 }
300 pane.update(cx, |pane, _| {
301 pane.set_pinned_count(self.pinned_count.min(items.len()));
302 })?;
303
304 anyhow::Ok(items)
305 }
306}
307
308pub type GroupId = i64;
309pub type PaneId = i64;
310pub type ItemId = u64;
311
312#[derive(Debug, PartialEq, Eq, Clone)]
313pub struct SerializedItem {
314 pub kind: Arc<str>,
315 pub item_id: ItemId,
316 pub active: bool,
317 pub preview: bool,
318}
319
320impl SerializedItem {
321 pub fn new(kind: impl AsRef<str>, item_id: ItemId, active: bool, preview: bool) -> Self {
322 Self {
323 kind: Arc::from(kind.as_ref()),
324 item_id,
325 active,
326 preview,
327 }
328 }
329}
330
331#[cfg(test)]
332impl Default for SerializedItem {
333 fn default() -> Self {
334 SerializedItem {
335 kind: Arc::from("Terminal"),
336 item_id: 100000,
337 active: false,
338 preview: false,
339 }
340 }
341}
342
343impl StaticColumnCount for SerializedItem {
344 fn column_count() -> usize {
345 4
346 }
347}
348impl Bind for &SerializedItem {
349 fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
350 let next_index = statement.bind(&self.kind, start_index)?;
351 let next_index = statement.bind(&self.item_id, next_index)?;
352 let next_index = statement.bind(&self.active, next_index)?;
353 statement.bind(&self.preview, next_index)
354 }
355}
356
357impl Column for SerializedItem {
358 fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
359 let (kind, next_index) = Arc::<str>::column(statement, start_index)?;
360 let (item_id, next_index) = ItemId::column(statement, next_index)?;
361 let (active, next_index) = bool::column(statement, next_index)?;
362 let (preview, next_index) = bool::column(statement, next_index)?;
363 Ok((
364 SerializedItem {
365 kind,
366 item_id,
367 active,
368 preview,
369 },
370 next_index,
371 ))
372 }
373}