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