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