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