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, WindowId};
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/// A workspace entry from a previous session, containing all the info needed
53/// to restore it including which window it belonged to (for MultiWorkspace grouping).
54#[derive(Debug, PartialEq, Clone)]
55pub struct SessionWorkspace {
56 pub workspace_id: WorkspaceId,
57 pub location: SerializedWorkspaceLocation,
58 pub paths: PathList,
59 pub window_id: Option<WindowId>,
60}
61
62/// Per-window state for a MultiWorkspace, persisted to KVP.
63#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
64pub struct MultiWorkspaceState {
65 pub active_workspace_id: Option<WorkspaceId>,
66 pub sidebar_open: bool,
67}
68
69/// The serialized state of a single MultiWorkspace window from a previous session:
70/// all workspaces that shared the window, which one was active, and whether the
71/// sidebar was open.
72#[derive(Debug, Clone)]
73pub struct SerializedMultiWorkspace {
74 pub workspaces: Vec<SessionWorkspace>,
75 pub state: MultiWorkspaceState,
76}
77
78#[derive(Debug, PartialEq, Clone)]
79pub(crate) struct SerializedWorkspace {
80 pub(crate) id: WorkspaceId,
81 pub(crate) location: SerializedWorkspaceLocation,
82 pub(crate) paths: PathList,
83 pub(crate) center_group: SerializedPaneGroup,
84 pub(crate) window_bounds: Option<SerializedWindowBounds>,
85 pub(crate) centered_layout: bool,
86 pub(crate) display: Option<Uuid>,
87 pub(crate) docks: DockStructure,
88 pub(crate) session_id: Option<String>,
89 pub(crate) breakpoints: BTreeMap<Arc<Path>, Vec<SourceBreakpoint>>,
90 pub(crate) user_toolchains: BTreeMap<ToolchainScope, IndexSet<Toolchain>>,
91 pub(crate) window_id: Option<u64>,
92}
93
94#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize)]
95pub struct DockStructure {
96 pub(crate) left: DockData,
97 pub(crate) right: DockData,
98 pub(crate) bottom: DockData,
99}
100
101impl RemoteConnectionKind {
102 pub(crate) fn serialize(&self) -> &'static str {
103 match self {
104 RemoteConnectionKind::Ssh => "ssh",
105 RemoteConnectionKind::Wsl => "wsl",
106 RemoteConnectionKind::Docker => "docker",
107 }
108 }
109
110 pub(crate) fn deserialize(text: &str) -> Option<Self> {
111 match text {
112 "ssh" => Some(Self::Ssh),
113 "wsl" => Some(Self::Wsl),
114 "docker" => Some(Self::Docker),
115 _ => None,
116 }
117 }
118}
119
120impl Column for DockStructure {
121 fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
122 let (left, next_index) = DockData::column(statement, start_index)?;
123 let (right, next_index) = DockData::column(statement, next_index)?;
124 let (bottom, next_index) = DockData::column(statement, next_index)?;
125 Ok((
126 DockStructure {
127 left,
128 right,
129 bottom,
130 },
131 next_index,
132 ))
133 }
134}
135
136impl Bind for DockStructure {
137 fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
138 let next_index = statement.bind(&self.left, start_index)?;
139 let next_index = statement.bind(&self.right, next_index)?;
140 statement.bind(&self.bottom, next_index)
141 }
142}
143
144#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize)]
145pub struct DockData {
146 pub(crate) visible: bool,
147 pub(crate) active_panel: Option<String>,
148 pub(crate) zoom: bool,
149}
150
151impl Column for DockData {
152 fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
153 let (visible, next_index) = Option::<bool>::column(statement, start_index)?;
154 let (active_panel, next_index) = Option::<String>::column(statement, next_index)?;
155 let (zoom, next_index) = Option::<bool>::column(statement, next_index)?;
156 Ok((
157 DockData {
158 visible: visible.unwrap_or(false),
159 active_panel,
160 zoom: zoom.unwrap_or(false),
161 },
162 next_index,
163 ))
164 }
165}
166
167impl Bind for DockData {
168 fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
169 let next_index = statement.bind(&self.visible, start_index)?;
170 let next_index = statement.bind(&self.active_panel, next_index)?;
171 statement.bind(&self.zoom, next_index)
172 }
173}
174
175#[derive(Debug, PartialEq, Clone)]
176pub(crate) enum SerializedPaneGroup {
177 Group {
178 axis: SerializedAxis,
179 flexes: Option<Vec<f32>>,
180 children: Vec<SerializedPaneGroup>,
181 },
182 Pane(SerializedPane),
183}
184
185#[cfg(test)]
186impl Default for SerializedPaneGroup {
187 fn default() -> Self {
188 Self::Pane(SerializedPane {
189 children: vec![SerializedItem::default()],
190 active: false,
191 pinned_count: 0,
192 })
193 }
194}
195
196impl SerializedPaneGroup {
197 #[async_recursion(?Send)]
198 pub(crate) async fn deserialize(
199 self,
200 project: &Entity<Project>,
201 workspace_id: WorkspaceId,
202 workspace: WeakEntity<Workspace>,
203 cx: &mut AsyncWindowContext,
204 ) -> Option<(
205 Member,
206 Option<Entity<Pane>>,
207 Vec<Option<Box<dyn ItemHandle>>>,
208 )> {
209 match self {
210 SerializedPaneGroup::Group {
211 axis,
212 children,
213 flexes,
214 } => {
215 let mut current_active_pane = None;
216 let mut members = Vec::new();
217 let mut items = Vec::new();
218 for child in children {
219 if let Some((new_member, active_pane, new_items)) = child
220 .deserialize(project, workspace_id, workspace.clone(), cx)
221 .await
222 {
223 members.push(new_member);
224 items.extend(new_items);
225 current_active_pane = current_active_pane.or(active_pane);
226 }
227 }
228
229 if members.is_empty() {
230 return None;
231 }
232
233 if members.len() == 1 {
234 return Some((members.remove(0), current_active_pane, items));
235 }
236
237 Some((
238 Member::Axis(PaneAxis::load(axis.0, members, flexes)),
239 current_active_pane,
240 items,
241 ))
242 }
243 SerializedPaneGroup::Pane(serialized_pane) => {
244 let pane = workspace
245 .update_in(cx, |workspace, window, cx| {
246 workspace.add_pane(window, cx).downgrade()
247 })
248 .log_err()?;
249 let active = serialized_pane.active;
250 let new_items = serialized_pane
251 .deserialize_to(project, &pane, workspace_id, workspace.clone(), cx)
252 .await
253 .context("Could not deserialize pane)")
254 .log_err()?;
255
256 if pane
257 .read_with(cx, |pane, _| pane.items_len() != 0)
258 .log_err()?
259 {
260 let pane = pane.upgrade()?;
261 Some((
262 Member::Pane(pane.clone()),
263 active.then_some(pane),
264 new_items,
265 ))
266 } else {
267 let pane = pane.upgrade()?;
268 workspace
269 .update_in(cx, |workspace, window, cx| {
270 workspace.force_remove_pane(&pane, &None, window, cx)
271 })
272 .log_err()?;
273 None
274 }
275 }
276 }
277 }
278}
279
280#[derive(Debug, PartialEq, Eq, Default, Clone)]
281pub struct SerializedPane {
282 pub(crate) active: bool,
283 pub(crate) children: Vec<SerializedItem>,
284 pub(crate) pinned_count: usize,
285}
286
287impl SerializedPane {
288 pub fn new(children: Vec<SerializedItem>, active: bool, pinned_count: usize) -> Self {
289 SerializedPane {
290 children,
291 active,
292 pinned_count,
293 }
294 }
295
296 pub async fn deserialize_to(
297 &self,
298 project: &Entity<Project>,
299 pane: &WeakEntity<Pane>,
300 workspace_id: WorkspaceId,
301 workspace: WeakEntity<Workspace>,
302 cx: &mut AsyncWindowContext,
303 ) -> Result<Vec<Option<Box<dyn ItemHandle>>>> {
304 let mut item_tasks = Vec::new();
305 let mut active_item_index = None;
306 let mut preview_item_index = None;
307 for (index, item) in self.children.iter().enumerate() {
308 let project = project.clone();
309 item_tasks.push(pane.update_in(cx, |_, window, cx| {
310 SerializableItemRegistry::deserialize(
311 &item.kind,
312 project,
313 workspace.clone(),
314 workspace_id,
315 item.item_id,
316 window,
317 cx,
318 )
319 })?);
320 if item.active {
321 active_item_index = Some(index);
322 }
323 if item.preview {
324 preview_item_index = Some(index);
325 }
326 }
327
328 let mut items = Vec::new();
329 for item_handle in futures::future::join_all(item_tasks).await {
330 let item_handle = item_handle.log_err();
331 items.push(item_handle.clone());
332
333 if let Some(item_handle) = item_handle {
334 pane.update_in(cx, |pane, window, cx| {
335 pane.add_item(item_handle.clone(), true, true, None, window, cx);
336 })?;
337 }
338 }
339
340 if let Some(active_item_index) = active_item_index {
341 pane.update_in(cx, |pane, window, cx| {
342 pane.activate_item(active_item_index, false, false, window, cx);
343 })?;
344 }
345
346 if let Some(preview_item_index) = preview_item_index {
347 pane.update(cx, |pane, cx| {
348 if let Some(item) = pane.item_for_index(preview_item_index) {
349 pane.set_preview_item_id(Some(item.item_id()), cx);
350 }
351 })?;
352 }
353 pane.update(cx, |pane, _| {
354 pane.set_pinned_count(self.pinned_count.min(items.len()));
355 })?;
356
357 anyhow::Ok(items)
358 }
359}
360
361pub type GroupId = i64;
362pub type PaneId = i64;
363pub type ItemId = u64;
364
365#[derive(Debug, PartialEq, Eq, Clone)]
366pub struct SerializedItem {
367 pub kind: Arc<str>,
368 pub item_id: ItemId,
369 pub active: bool,
370 pub preview: bool,
371}
372
373impl SerializedItem {
374 pub fn new(kind: impl AsRef<str>, item_id: ItemId, active: bool, preview: bool) -> Self {
375 Self {
376 kind: Arc::from(kind.as_ref()),
377 item_id,
378 active,
379 preview,
380 }
381 }
382}
383
384#[cfg(test)]
385impl Default for SerializedItem {
386 fn default() -> Self {
387 SerializedItem {
388 kind: Arc::from("Terminal"),
389 item_id: 100000,
390 active: false,
391 preview: false,
392 }
393 }
394}
395
396impl StaticColumnCount for SerializedItem {
397 fn column_count() -> usize {
398 4
399 }
400}
401impl Bind for &SerializedItem {
402 fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
403 let next_index = statement.bind(&self.kind, start_index)?;
404 let next_index = statement.bind(&self.item_id, next_index)?;
405 let next_index = statement.bind(&self.active, next_index)?;
406 statement.bind(&self.preview, next_index)
407 }
408}
409
410impl Column for SerializedItem {
411 fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
412 let (kind, next_index) = Arc::<str>::column(statement, start_index)?;
413 let (item_id, next_index) = ItemId::column(statement, next_index)?;
414 let (active, next_index) = bool::column(statement, next_index)?;
415 let (preview, next_index) = bool::column(statement, next_index)?;
416 Ok((
417 SerializedItem {
418 kind,
419 item_id,
420 active,
421 preview,
422 },
423 next_index,
424 ))
425 }
426}