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