Remove dev servers (#19638)
TODO: - [ ] Check that workspace migration worked - [ ] Add server migrations and make sure SeaORM files are in sync (maybe?) Release Notes: - N/A --------- Co-authored-by: Conrad <conrad@zed.dev> Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
This commit is contained in:
parent
b5f816dde5
commit
02718284ef
55 changed files with 391 additions and 5024 deletions
|
@ -16,7 +16,6 @@ doctest = false
|
|||
anyhow.workspace = true
|
||||
auto_update.workspace = true
|
||||
release_channel.workspace = true
|
||||
client.workspace = true
|
||||
editor.workspace = true
|
||||
file_finder.workspace = true
|
||||
futures.workspace = true
|
||||
|
@ -30,15 +29,12 @@ menu.workspace = true
|
|||
ordered-float.workspace = true
|
||||
picker.workspace = true
|
||||
project.workspace = true
|
||||
dev_server_projects.workspace = true
|
||||
remote.workspace = true
|
||||
rpc.workspace = true
|
||||
schemars.workspace = true
|
||||
serde.workspace = true
|
||||
settings.workspace = true
|
||||
smol.workspace = true
|
||||
task.workspace = true
|
||||
terminal_view.workspace = true
|
||||
theme.workspace = true
|
||||
ui.workspace = true
|
||||
util.workspace = true
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use dev_server_projects::DevServer;
|
||||
use gpui::{ClickEvent, DismissEvent, EventEmitter, FocusHandle, FocusableView, Render, WeakView};
|
||||
use project::project_settings::ProjectSettings;
|
||||
use remote::SshConnectionOptions;
|
||||
|
@ -12,14 +11,10 @@ use ui::{
|
|||
};
|
||||
use workspace::{notifications::DetachAndPromptErr, ModalView, OpenOptions, Workspace};
|
||||
|
||||
use crate::{
|
||||
open_dev_server_project, open_ssh_project, remote_servers::reconnect_to_dev_server_project,
|
||||
RemoteServerProjects, SshSettings,
|
||||
};
|
||||
use crate::{open_ssh_project, SshSettings};
|
||||
|
||||
enum Host {
|
||||
RemoteProject,
|
||||
DevServerProject(DevServer),
|
||||
SshRemoteProject(SshConnectionOptions),
|
||||
}
|
||||
|
||||
|
@ -55,20 +50,9 @@ impl DisconnectedOverlay {
|
|||
return;
|
||||
}
|
||||
let handle = cx.view().downgrade();
|
||||
let dev_server = project
|
||||
.read(cx)
|
||||
.dev_server_project_id()
|
||||
.and_then(|id| {
|
||||
dev_server_projects::Store::global(cx)
|
||||
.read(cx)
|
||||
.dev_server_for_project(id)
|
||||
})
|
||||
.cloned();
|
||||
|
||||
let ssh_connection_options = project.read(cx).ssh_connection_options(cx);
|
||||
let host = if let Some(dev_server) = dev_server {
|
||||
Host::DevServerProject(dev_server)
|
||||
} else if let Some(ssh_connection_options) = ssh_connection_options {
|
||||
let host = if let Some(ssh_connection_options) = ssh_connection_options {
|
||||
Host::SshRemoteProject(ssh_connection_options)
|
||||
} else {
|
||||
Host::RemoteProject
|
||||
|
@ -89,9 +73,6 @@ impl DisconnectedOverlay {
|
|||
cx.emit(DismissEvent);
|
||||
|
||||
match &self.host {
|
||||
Host::DevServerProject(dev_server) => {
|
||||
self.reconnect_to_dev_server(dev_server.clone(), cx);
|
||||
}
|
||||
Host::SshRemoteProject(ssh_connection_options) => {
|
||||
self.reconnect_to_ssh_remote(ssh_connection_options.clone(), cx);
|
||||
}
|
||||
|
@ -99,50 +80,6 @@ impl DisconnectedOverlay {
|
|||
}
|
||||
}
|
||||
|
||||
fn reconnect_to_dev_server(&self, dev_server: DevServer, cx: &mut ViewContext<Self>) {
|
||||
let Some(workspace) = self.workspace.upgrade() else {
|
||||
return;
|
||||
};
|
||||
let Some(dev_server_project_id) = workspace
|
||||
.read(cx)
|
||||
.project()
|
||||
.read(cx)
|
||||
.dev_server_project_id()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
if let Some(project_id) = dev_server_projects::Store::global(cx)
|
||||
.read(cx)
|
||||
.dev_server_project(dev_server_project_id)
|
||||
.and_then(|project| project.project_id)
|
||||
{
|
||||
return workspace.update(cx, move |_, cx| {
|
||||
open_dev_server_project(true, dev_server_project_id, project_id, cx)
|
||||
.detach_and_prompt_err("Failed to reconnect", cx, |_, _| None)
|
||||
});
|
||||
}
|
||||
|
||||
if dev_server.ssh_connection_string.is_some() {
|
||||
let task = workspace.update(cx, |_, cx| {
|
||||
reconnect_to_dev_server_project(
|
||||
cx.view().clone(),
|
||||
dev_server,
|
||||
dev_server_project_id,
|
||||
true,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
task.detach_and_prompt_err("Failed to reconnect", cx, |_, _| None);
|
||||
} else {
|
||||
return workspace.update(cx, |workspace, cx| {
|
||||
let handle = cx.view().downgrade();
|
||||
workspace.toggle_modal(cx, |cx| RemoteServerProjects::new(cx, handle))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn reconnect_to_ssh_remote(
|
||||
&self,
|
||||
connection_options: SshConnectionOptions,
|
||||
|
@ -200,13 +137,10 @@ impl DisconnectedOverlay {
|
|||
|
||||
impl Render for DisconnectedOverlay {
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
let can_reconnect = matches!(
|
||||
self.host,
|
||||
Host::DevServerProject(_) | Host::SshRemoteProject(_)
|
||||
);
|
||||
let can_reconnect = matches!(self.host, Host::SshRemoteProject(_));
|
||||
|
||||
let message = match &self.host {
|
||||
Host::RemoteProject | Host::DevServerProject(_) => {
|
||||
Host::RemoteProject => {
|
||||
"Your connection to the remote project has been lost.".to_string()
|
||||
}
|
||||
Host::SshRemoteProject(options) => {
|
||||
|
|
|
@ -4,7 +4,6 @@ mod ssh_connections;
|
|||
use remote::SshConnectionOptions;
|
||||
pub use ssh_connections::open_ssh_project;
|
||||
|
||||
use client::{DevServerProjectId, ProjectId};
|
||||
use disconnected_overlay::DisconnectedOverlay;
|
||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||
use gpui::{
|
||||
|
@ -17,9 +16,7 @@ use picker::{
|
|||
highlighted_match_with_paths::{HighlightedMatchWithPaths, HighlightedText},
|
||||
Picker, PickerDelegate,
|
||||
};
|
||||
use remote_servers::reconnect_to_dev_server_project;
|
||||
pub use remote_servers::RemoteServerProjects;
|
||||
use rpc::proto::DevServerStatus;
|
||||
use serde::Deserialize;
|
||||
use settings::Settings;
|
||||
pub use ssh_connections::SshSettings;
|
||||
|
@ -28,13 +25,12 @@ use std::{
|
|||
sync::Arc,
|
||||
};
|
||||
use ui::{
|
||||
prelude::*, tooltip_container, ButtonLike, IconWithIndicator, Indicator, KeyBinding, ListItem,
|
||||
ListItemSpacing, Tooltip,
|
||||
prelude::*, tooltip_container, ButtonLike, KeyBinding, ListItem, ListItemSpacing, Tooltip,
|
||||
};
|
||||
use util::{paths::PathExt, ResultExt};
|
||||
use workspace::{
|
||||
AppState, CloseIntent, ModalView, OpenOptions, SerializedWorkspaceLocation, Workspace,
|
||||
WorkspaceId, WORKSPACE_DB,
|
||||
CloseIntent, ModalView, OpenOptions, SerializedWorkspaceLocation, Workspace, WorkspaceId,
|
||||
WORKSPACE_DB,
|
||||
};
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize, Default)]
|
||||
|
@ -101,7 +97,7 @@ impl RecentProjects {
|
|||
}
|
||||
}
|
||||
|
||||
fn register(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
|
||||
fn register(workspace: &mut Workspace, _cx: &mut ViewContext<Workspace>) {
|
||||
workspace.register_action(|workspace, open_recent: &OpenRecent, cx| {
|
||||
let Some(recent_projects) = workspace.active_modal::<Self>(cx) else {
|
||||
Self::open(workspace, open_recent.create_new_window, cx);
|
||||
|
@ -114,20 +110,6 @@ impl RecentProjects {
|
|||
.update(cx, |picker, cx| picker.cycle_selection(cx))
|
||||
});
|
||||
});
|
||||
if workspace
|
||||
.project()
|
||||
.read(cx)
|
||||
.dev_server_project_id()
|
||||
.is_some()
|
||||
{
|
||||
workspace.register_action(|workspace, _: &workspace::Open, cx| {
|
||||
if workspace.active_modal::<Self>(cx).is_some() {
|
||||
cx.propagate();
|
||||
} else {
|
||||
Self::open(workspace, true, cx);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open(
|
||||
|
@ -254,13 +236,6 @@ impl PickerDelegate for RecentProjectsDelegate {
|
|||
.map(|(_, path)| path.compact().to_string_lossy().into_owned())
|
||||
.collect::<Vec<_>>()
|
||||
.join(""),
|
||||
SerializedWorkspaceLocation::DevServer(dev_server_project) => {
|
||||
format!(
|
||||
"{}{}",
|
||||
dev_server_project.dev_server_name,
|
||||
dev_server_project.paths.join("")
|
||||
)
|
||||
}
|
||||
SerializedWorkspaceLocation::Ssh(ssh_project) => ssh_project
|
||||
.ssh_urls()
|
||||
.iter()
|
||||
|
@ -321,7 +296,10 @@ impl PickerDelegate for RecentProjectsDelegate {
|
|||
cx.spawn(move |workspace, mut cx| async move {
|
||||
let continue_replacing = workspace
|
||||
.update(&mut cx, |workspace, cx| {
|
||||
workspace.prepare_to_close(CloseIntent::ReplaceWindow, cx)
|
||||
workspace.prepare_to_close(
|
||||
CloseIntent::ReplaceWindow,
|
||||
cx,
|
||||
)
|
||||
})?
|
||||
.await?;
|
||||
if continue_replacing {
|
||||
|
@ -339,74 +317,56 @@ impl PickerDelegate for RecentProjectsDelegate {
|
|||
workspace.open_workspace_for_paths(false, paths, cx)
|
||||
}
|
||||
}
|
||||
SerializedWorkspaceLocation::DevServer(dev_server_project) => {
|
||||
let store = dev_server_projects::Store::global(cx);
|
||||
let Some(project_id) = store.read(cx)
|
||||
.dev_server_project(dev_server_project.id)
|
||||
.and_then(|p| p.project_id)
|
||||
else {
|
||||
let server = store.read(cx).dev_server_for_project(dev_server_project.id);
|
||||
if server.is_some_and(|server| server.ssh_connection_string.is_some()) {
|
||||
return reconnect_to_dev_server_project(cx.view().clone(), server.unwrap().clone(), dev_server_project.id, replace_current_window, cx);
|
||||
} else {
|
||||
let dev_server_name = dev_server_project.dev_server_name.clone();
|
||||
return cx.spawn(|workspace, mut cx| async move {
|
||||
let response =
|
||||
cx.prompt(gpui::PromptLevel::Warning,
|
||||
"Dev Server is offline",
|
||||
Some(format!("Cannot connect to {}. To debug open the remote project settings.", dev_server_name).as_str()),
|
||||
&["Ok", "Open Settings"]
|
||||
).await?;
|
||||
if response == 1 {
|
||||
workspace.update(&mut cx, |workspace, cx| {
|
||||
let handle = cx.view().downgrade();
|
||||
workspace.toggle_modal(cx, |cx| RemoteServerProjects::new(cx, handle))
|
||||
})?;
|
||||
} else {
|
||||
workspace.update(&mut cx, |workspace, cx| {
|
||||
RecentProjects::open(workspace, true, cx);
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
SerializedWorkspaceLocation::Ssh(ssh_project) => {
|
||||
let app_state = workspace.app_state().clone();
|
||||
|
||||
let replace_window = if replace_current_window {
|
||||
cx.window_handle().downcast::<Workspace>()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
open_dev_server_project(replace_current_window, dev_server_project.id, project_id, cx)
|
||||
}
|
||||
SerializedWorkspaceLocation::Ssh(ssh_project) => {
|
||||
let app_state = workspace.app_state().clone();
|
||||
|
||||
let replace_window = if replace_current_window {
|
||||
cx.window_handle().downcast::<Workspace>()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let open_options = OpenOptions {
|
||||
replace_window,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let open_options = OpenOptions {
|
||||
replace_window,
|
||||
..Default::default()
|
||||
};
|
||||
let args = SshSettings::get_global(cx).args_for(
|
||||
&ssh_project.host,
|
||||
ssh_project.port,
|
||||
&ssh_project.user,
|
||||
);
|
||||
let nickname = SshSettings::get_global(cx).nickname_for(
|
||||
&ssh_project.host,
|
||||
ssh_project.port,
|
||||
&ssh_project.user,
|
||||
);
|
||||
let connection_options = SshConnectionOptions {
|
||||
host: ssh_project.host.clone(),
|
||||
username: ssh_project.user.clone(),
|
||||
port: ssh_project.port,
|
||||
password: None,
|
||||
args,
|
||||
};
|
||||
|
||||
let args = SshSettings::get_global(cx).args_for(&ssh_project.host, ssh_project.port, &ssh_project.user);
|
||||
let nickname = SshSettings::get_global(cx).nickname_for(&ssh_project.host, ssh_project.port, &ssh_project.user);
|
||||
let connection_options = SshConnectionOptions {
|
||||
host: ssh_project.host.clone(),
|
||||
username: ssh_project.user.clone(),
|
||||
port: ssh_project.port,
|
||||
password: None,
|
||||
args,
|
||||
};
|
||||
let paths = ssh_project.paths.iter().map(PathBuf::from).collect();
|
||||
|
||||
let paths = ssh_project.paths.iter().map(PathBuf::from).collect();
|
||||
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
open_ssh_project(connection_options, paths, app_state, open_options, nickname, &mut cx).await
|
||||
})
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
open_ssh_project(
|
||||
connection_options,
|
||||
paths,
|
||||
app_state,
|
||||
open_options,
|
||||
nickname,
|
||||
&mut cx,
|
||||
)
|
||||
.await
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
.detach_and_log_err(cx);
|
||||
cx.emit(DismissEvent);
|
||||
}
|
||||
}
|
||||
|
@ -431,20 +391,6 @@ impl PickerDelegate for RecentProjectsDelegate {
|
|||
|
||||
let (_, location) = self.workspaces.get(hit.candidate_id)?;
|
||||
|
||||
let dev_server_status =
|
||||
if let SerializedWorkspaceLocation::DevServer(dev_server_project) = location {
|
||||
let store = dev_server_projects::Store::global(cx).read(cx);
|
||||
Some(
|
||||
store
|
||||
.dev_server_project(dev_server_project.id)
|
||||
.and_then(|p| store.dev_server(p.dev_server_id))
|
||||
.map(|s| s.status)
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut path_start_offset = 0;
|
||||
let paths = match location {
|
||||
SerializedWorkspaceLocation::Local(paths, order) => Arc::new(
|
||||
|
@ -457,13 +403,6 @@ impl PickerDelegate for RecentProjectsDelegate {
|
|||
.collect(),
|
||||
),
|
||||
SerializedWorkspaceLocation::Ssh(ssh_project) => Arc::new(ssh_project.ssh_urls()),
|
||||
SerializedWorkspaceLocation::DevServer(dev_server_project) => {
|
||||
Arc::new(vec![PathBuf::from(format!(
|
||||
"{}:{}",
|
||||
dev_server_project.dev_server_name,
|
||||
dev_server_project.paths.join(", ")
|
||||
))])
|
||||
}
|
||||
};
|
||||
|
||||
let (match_labels, paths): (Vec<_>, Vec<_>) = paths
|
||||
|
@ -478,13 +417,7 @@ impl PickerDelegate for RecentProjectsDelegate {
|
|||
.unzip();
|
||||
|
||||
let highlighted_match = HighlightedMatchWithPaths {
|
||||
match_label: HighlightedText::join(match_labels.into_iter().flatten(), ", ").color(
|
||||
if matches!(dev_server_status, Some(DevServerStatus::Offline)) {
|
||||
Color::Disabled
|
||||
} else {
|
||||
Color::Default
|
||||
},
|
||||
),
|
||||
match_label: HighlightedText::join(match_labels.into_iter().flatten(), ", "),
|
||||
paths,
|
||||
};
|
||||
|
||||
|
@ -507,24 +440,6 @@ impl PickerDelegate for RecentProjectsDelegate {
|
|||
SerializedWorkspaceLocation::Ssh(_) => Icon::new(IconName::Server)
|
||||
.color(Color::Muted)
|
||||
.into_any_element(),
|
||||
SerializedWorkspaceLocation::DevServer(_) => {
|
||||
let indicator_color = match dev_server_status {
|
||||
Some(DevServerStatus::Online) => Color::Created,
|
||||
Some(DevServerStatus::Offline) => Color::Hidden,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
IconWithIndicator::new(
|
||||
Icon::new(IconName::Server).color(Color::Muted),
|
||||
Some(Indicator::dot()),
|
||||
)
|
||||
.indicator_color(indicator_color)
|
||||
.indicator_border_color(if selected {
|
||||
Some(cx.theme().colors().element_selected)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.into_any_element()
|
||||
}
|
||||
})
|
||||
})
|
||||
.child({
|
||||
|
@ -597,59 +512,6 @@ impl PickerDelegate for RecentProjectsDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
fn open_dev_server_project(
|
||||
replace_current_window: bool,
|
||||
dev_server_project_id: DevServerProjectId,
|
||||
project_id: ProjectId,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) -> Task<anyhow::Result<()>> {
|
||||
if let Some(app_state) = AppState::global(cx).upgrade() {
|
||||
let handle = if replace_current_window {
|
||||
cx.window_handle().downcast::<Workspace>()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(handle) = handle {
|
||||
cx.spawn(move |workspace, mut cx| async move {
|
||||
let continue_replacing = workspace
|
||||
.update(&mut cx, |workspace, cx| {
|
||||
workspace.prepare_to_close(CloseIntent::ReplaceWindow, cx)
|
||||
})?
|
||||
.await?;
|
||||
if continue_replacing {
|
||||
workspace
|
||||
.update(&mut cx, |_workspace, cx| {
|
||||
workspace::join_dev_server_project(
|
||||
dev_server_project_id,
|
||||
project_id,
|
||||
app_state,
|
||||
Some(handle),
|
||||
cx,
|
||||
)
|
||||
})?
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
} else {
|
||||
let task = workspace::join_dev_server_project(
|
||||
dev_server_project_id,
|
||||
project_id,
|
||||
app_state,
|
||||
None,
|
||||
cx,
|
||||
);
|
||||
cx.spawn(|_, _| async move {
|
||||
task.await?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
} else {
|
||||
Task::ready(Err(anyhow::anyhow!("App state not found")))
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the highlighted text for the name and path
|
||||
fn highlights_for_path(
|
||||
path: &Path,
|
||||
|
|
|
@ -1,19 +1,12 @@
|
|||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
use dev_server_projects::{DevServer, DevServerId, DevServerProjectId};
|
||||
use editor::Editor;
|
||||
use file_finder::OpenPathDelegate;
|
||||
use futures::channel::oneshot;
|
||||
use futures::future::Shared;
|
||||
use futures::FutureExt;
|
||||
use gpui::canvas;
|
||||
use gpui::AsyncWindowContext;
|
||||
use gpui::ClipboardItem;
|
||||
use gpui::Task;
|
||||
use gpui::WeakView;
|
||||
|
@ -22,17 +15,10 @@ use gpui::{
|
|||
PromptLevel, ScrollHandle, View, ViewContext,
|
||||
};
|
||||
use picker::Picker;
|
||||
use project::terminals::wrap_for_ssh;
|
||||
use project::terminals::SshCommand;
|
||||
use project::Project;
|
||||
use remote::SshConnectionOptions;
|
||||
use rpc::proto::DevServerStatus;
|
||||
use settings::update_settings_file;
|
||||
use settings::Settings;
|
||||
use task::HideStrategy;
|
||||
use task::RevealStrategy;
|
||||
use task::SpawnInTerminal;
|
||||
use terminal_view::terminal_panel::TerminalPanel;
|
||||
use ui::{
|
||||
prelude::*, IconButtonShape, List, ListItem, ListSeparator, Modal, ModalHeader, Scrollbar,
|
||||
ScrollbarState, Section, Tooltip,
|
||||
|
@ -43,7 +29,6 @@ use workspace::OpenOptions;
|
|||
use workspace::Toast;
|
||||
use workspace::{notifications::DetachAndPromptErr, ModalView, Workspace};
|
||||
|
||||
use crate::open_dev_server_project;
|
||||
use crate::ssh_connections::connect_over_ssh;
|
||||
use crate::ssh_connections::open_ssh_project;
|
||||
use crate::ssh_connections::RemoteSettingsContent;
|
||||
|
@ -1319,146 +1304,3 @@ impl Render for RemoteServerProjects {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reconnect_to_dev_server_project(
|
||||
workspace: View<Workspace>,
|
||||
dev_server: DevServer,
|
||||
dev_server_project_id: DevServerProjectId,
|
||||
replace_current_window: bool,
|
||||
cx: &mut WindowContext,
|
||||
) -> Task<Result<()>> {
|
||||
let store = dev_server_projects::Store::global(cx);
|
||||
let reconnect = reconnect_to_dev_server(workspace.clone(), dev_server, cx);
|
||||
cx.spawn(|mut cx| async move {
|
||||
reconnect.await?;
|
||||
|
||||
cx.background_executor()
|
||||
.timer(Duration::from_millis(1000))
|
||||
.await;
|
||||
|
||||
if let Some(project_id) = store.update(&mut cx, |store, _| {
|
||||
store
|
||||
.dev_server_project(dev_server_project_id)
|
||||
.and_then(|p| p.project_id)
|
||||
})? {
|
||||
workspace
|
||||
.update(&mut cx, move |_, cx| {
|
||||
open_dev_server_project(
|
||||
replace_current_window,
|
||||
dev_server_project_id,
|
||||
project_id,
|
||||
cx,
|
||||
)
|
||||
})?
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn reconnect_to_dev_server(
|
||||
workspace: View<Workspace>,
|
||||
dev_server: DevServer,
|
||||
cx: &mut WindowContext,
|
||||
) -> Task<Result<()>> {
|
||||
let Some(ssh_connection_string) = dev_server.ssh_connection_string else {
|
||||
return Task::ready(Err(anyhow!("Can't reconnect, no ssh_connection_string")));
|
||||
};
|
||||
let dev_server_store = dev_server_projects::Store::global(cx);
|
||||
let get_access_token = dev_server_store.update(cx, |store, cx| {
|
||||
store.regenerate_dev_server_token(dev_server.id, cx)
|
||||
});
|
||||
|
||||
cx.spawn(|mut cx| async move {
|
||||
let access_token = get_access_token.await?.access_token;
|
||||
|
||||
spawn_ssh_task(
|
||||
workspace,
|
||||
dev_server_store,
|
||||
dev_server.id,
|
||||
ssh_connection_string.to_string(),
|
||||
access_token,
|
||||
&mut cx,
|
||||
)
|
||||
.await
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn spawn_ssh_task(
|
||||
workspace: View<Workspace>,
|
||||
dev_server_store: Model<dev_server_projects::Store>,
|
||||
dev_server_id: DevServerId,
|
||||
ssh_connection_string: String,
|
||||
access_token: String,
|
||||
cx: &mut AsyncWindowContext,
|
||||
) -> Result<()> {
|
||||
let terminal_panel = workspace
|
||||
.update(cx, |workspace, cx| workspace.panel::<TerminalPanel>(cx))
|
||||
.ok()
|
||||
.flatten()
|
||||
.with_context(|| anyhow!("No terminal panel"))?;
|
||||
|
||||
let command = "sh".to_string();
|
||||
let args = vec![
|
||||
"-x".to_string(),
|
||||
"-c".to_string(),
|
||||
format!(
|
||||
r#"~/.local/bin/zed -v >/dev/stderr || (curl -f https://zed.dev/install.sh || wget -qO- https://zed.dev/install.sh) | sh && ZED_HEADLESS=1 ~/.local/bin/zed --dev-server-token {}"#,
|
||||
access_token
|
||||
),
|
||||
];
|
||||
|
||||
let ssh_connection_string = ssh_connection_string.to_string();
|
||||
let (command, args) = wrap_for_ssh(
|
||||
&SshCommand::DevServer(ssh_connection_string.clone()),
|
||||
Some((&command, &args)),
|
||||
None,
|
||||
HashMap::default(),
|
||||
None,
|
||||
);
|
||||
|
||||
let terminal = terminal_panel
|
||||
.update(cx, |terminal_panel, cx| {
|
||||
terminal_panel.spawn_in_new_terminal(
|
||||
SpawnInTerminal {
|
||||
id: task::TaskId("ssh-remote".into()),
|
||||
full_label: "Install zed over ssh".into(),
|
||||
label: "Install zed over ssh".into(),
|
||||
command,
|
||||
args,
|
||||
command_label: ssh_connection_string.clone(),
|
||||
cwd: None,
|
||||
use_new_terminal: true,
|
||||
allow_concurrent_runs: false,
|
||||
reveal: RevealStrategy::Always,
|
||||
hide: HideStrategy::Never,
|
||||
env: Default::default(),
|
||||
shell: Default::default(),
|
||||
},
|
||||
cx,
|
||||
)
|
||||
})?
|
||||
.await?;
|
||||
|
||||
terminal
|
||||
.update(cx, |terminal, cx| terminal.wait_for_completed_task(cx))?
|
||||
.await;
|
||||
|
||||
// There's a race-condition between the task completing successfully, and the server sending us the online status. Make it less likely we'll show the error state.
|
||||
if dev_server_store.update(cx, |this, _| this.dev_server_status(dev_server_id))?
|
||||
== DevServerStatus::Offline
|
||||
{
|
||||
cx.background_executor()
|
||||
.timer(Duration::from_millis(200))
|
||||
.await
|
||||
}
|
||||
|
||||
if dev_server_store.update(cx, |this, _| this.dev_server_status(dev_server_id))?
|
||||
== DevServerStatus::Offline
|
||||
{
|
||||
return Err(anyhow!("couldn't reconnect"))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue