Add initial support for WSL (#37035)
Closes #36188 ## Todo * [x] CLI * [x] terminals * [x] tasks ## For future PRs * debugging * UI for opening WSL projects * fixing workspace state restoration Release Notes: - Windows alpha: Zed now supports editing folders in WSL. --------- Co-authored-by: Junkui Zhang <364772080@qq.com>
This commit is contained in:
co-authored by
Junkui Zhang
parent
1c2e2a00fe
commit
f78f3e7729
@@ -1,6 +1,6 @@
|
||||
use gpui::{ClickEvent, DismissEvent, EventEmitter, FocusHandle, Focusable, Render, WeakEntity};
|
||||
use project::project_settings::ProjectSettings;
|
||||
use remote::SshConnectionOptions;
|
||||
use remote::RemoteConnectionOptions;
|
||||
use settings::Settings;
|
||||
use ui::{
|
||||
Button, ButtonCommon, ButtonStyle, Clickable, Context, ElevationIndex, FluentBuilder, Headline,
|
||||
@@ -9,11 +9,11 @@ use ui::{
|
||||
};
|
||||
use workspace::{ModalView, OpenOptions, Workspace, notifications::DetachAndPromptErr};
|
||||
|
||||
use crate::open_ssh_project;
|
||||
use crate::open_remote_project;
|
||||
|
||||
enum Host {
|
||||
RemoteProject,
|
||||
SshRemoteProject(SshConnectionOptions),
|
||||
CollabGuestProject,
|
||||
RemoteServerProject(RemoteConnectionOptions),
|
||||
}
|
||||
|
||||
pub struct DisconnectedOverlay {
|
||||
@@ -66,9 +66,9 @@ impl DisconnectedOverlay {
|
||||
|
||||
let remote_connection_options = project.read(cx).remote_connection_options(cx);
|
||||
let host = if let Some(ssh_connection_options) = remote_connection_options {
|
||||
Host::SshRemoteProject(ssh_connection_options)
|
||||
Host::RemoteServerProject(ssh_connection_options)
|
||||
} else {
|
||||
Host::RemoteProject
|
||||
Host::CollabGuestProject
|
||||
};
|
||||
|
||||
workspace.toggle_modal(window, cx, |_, cx| DisconnectedOverlay {
|
||||
@@ -86,14 +86,14 @@ impl DisconnectedOverlay {
|
||||
self.finished = true;
|
||||
cx.emit(DismissEvent);
|
||||
|
||||
if let Host::SshRemoteProject(ssh_connection_options) = &self.host {
|
||||
self.reconnect_to_ssh_remote(ssh_connection_options.clone(), window, cx);
|
||||
if let Host::RemoteServerProject(ssh_connection_options) = &self.host {
|
||||
self.reconnect_to_remote_project(ssh_connection_options.clone(), window, cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn reconnect_to_ssh_remote(
|
||||
fn reconnect_to_remote_project(
|
||||
&self,
|
||||
connection_options: SshConnectionOptions,
|
||||
connection_options: RemoteConnectionOptions,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
@@ -114,7 +114,7 @@ impl DisconnectedOverlay {
|
||||
.collect();
|
||||
|
||||
cx.spawn_in(window, async move |_, cx| {
|
||||
open_ssh_project(
|
||||
open_remote_project(
|
||||
connection_options,
|
||||
paths,
|
||||
app_state,
|
||||
@@ -138,13 +138,13 @@ impl DisconnectedOverlay {
|
||||
|
||||
impl Render for DisconnectedOverlay {
|
||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let can_reconnect = matches!(self.host, Host::SshRemoteProject(_));
|
||||
let can_reconnect = matches!(self.host, Host::RemoteServerProject(_));
|
||||
|
||||
let message = match &self.host {
|
||||
Host::RemoteProject => {
|
||||
Host::CollabGuestProject => {
|
||||
"Your connection to the remote project has been lost.".to_string()
|
||||
}
|
||||
Host::SshRemoteProject(options) => {
|
||||
Host::RemoteServerProject(options) => {
|
||||
let autosave = if ProjectSettings::get_global(cx)
|
||||
.session
|
||||
.restore_unsaved_buffers
|
||||
@@ -155,7 +155,8 @@ impl Render for DisconnectedOverlay {
|
||||
};
|
||||
format!(
|
||||
"Your connection to {} has been lost.{}",
|
||||
options.host, autosave
|
||||
options.display_name(),
|
||||
autosave
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
pub mod disconnected_overlay;
|
||||
mod remote_connections;
|
||||
mod remote_servers;
|
||||
mod ssh_config;
|
||||
mod ssh_connections;
|
||||
|
||||
pub use ssh_connections::{is_connecting_over_ssh, open_ssh_project};
|
||||
use remote::RemoteConnectionOptions;
|
||||
pub use remote_connections::open_remote_project;
|
||||
|
||||
use disconnected_overlay::DisconnectedOverlay;
|
||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||
@@ -16,9 +17,9 @@ use picker::{
|
||||
Picker, PickerDelegate,
|
||||
highlighted_match_with_paths::{HighlightedMatch, HighlightedMatchWithPaths},
|
||||
};
|
||||
pub use remote_connections::SshSettings;
|
||||
pub use remote_servers::RemoteServerProjects;
|
||||
use settings::Settings;
|
||||
pub use ssh_connections::SshSettings;
|
||||
use std::{path::Path, sync::Arc};
|
||||
use ui::{KeyBinding, ListItem, ListItemSpacing, Tooltip, prelude::*, tooltip_container};
|
||||
use util::{ResultExt, paths::PathExt};
|
||||
@@ -290,7 +291,7 @@ impl PickerDelegate for RecentProjectsDelegate {
|
||||
if workspace.database_id() == Some(*candidate_workspace_id) {
|
||||
Task::ready(Ok(()))
|
||||
} else {
|
||||
match candidate_workspace_location {
|
||||
match candidate_workspace_location.clone() {
|
||||
SerializedWorkspaceLocation::Local => {
|
||||
let paths = candidate_workspace_paths.paths().to_vec();
|
||||
if replace_current_window {
|
||||
@@ -320,7 +321,7 @@ impl PickerDelegate for RecentProjectsDelegate {
|
||||
workspace.open_workspace_for_paths(false, paths, window, cx)
|
||||
}
|
||||
}
|
||||
SerializedWorkspaceLocation::Ssh(connection) => {
|
||||
SerializedWorkspaceLocation::Remote(mut connection) => {
|
||||
let app_state = workspace.app_state().clone();
|
||||
|
||||
let replace_window = if replace_current_window {
|
||||
@@ -334,18 +335,16 @@ impl PickerDelegate for RecentProjectsDelegate {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let connection_options = SshSettings::get_global(cx)
|
||||
.connection_options_for(
|
||||
connection.host.clone(),
|
||||
connection.port,
|
||||
connection.user.clone(),
|
||||
);
|
||||
if let RemoteConnectionOptions::Ssh(connection) = &mut connection {
|
||||
SshSettings::get_global(cx)
|
||||
.fill_connection_options_from_settings(connection);
|
||||
};
|
||||
|
||||
let paths = candidate_workspace_paths.paths().to_vec();
|
||||
|
||||
cx.spawn_in(window, async move |_, cx| {
|
||||
open_ssh_project(
|
||||
connection_options,
|
||||
open_remote_project(
|
||||
connection.clone(),
|
||||
paths,
|
||||
app_state,
|
||||
open_options,
|
||||
@@ -418,9 +417,11 @@ impl PickerDelegate for RecentProjectsDelegate {
|
||||
SerializedWorkspaceLocation::Local => Icon::new(IconName::Screen)
|
||||
.color(Color::Muted)
|
||||
.into_any_element(),
|
||||
SerializedWorkspaceLocation::Ssh(_) => Icon::new(IconName::Server)
|
||||
.color(Color::Muted)
|
||||
.into_any_element(),
|
||||
SerializedWorkspaceLocation::Remote(_) => {
|
||||
Icon::new(IconName::Server)
|
||||
.color(Color::Muted)
|
||||
.into_any_element()
|
||||
}
|
||||
})
|
||||
})
|
||||
.child({
|
||||
|
||||
+73
-65
@@ -16,7 +16,8 @@ use language::CursorShape;
|
||||
use markdown::{Markdown, MarkdownElement, MarkdownStyle};
|
||||
use release_channel::ReleaseChannel;
|
||||
use remote::{
|
||||
ConnectionIdentifier, RemoteClient, RemotePlatform, SshConnectionOptions, SshPortForwardOption,
|
||||
ConnectionIdentifier, RemoteClient, RemoteConnectionOptions, RemotePlatform,
|
||||
SshConnectionOptions, SshPortForwardOption,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -42,32 +43,35 @@ impl SshSettings {
|
||||
self.ssh_connections.clone().into_iter().flatten()
|
||||
}
|
||||
|
||||
pub fn fill_connection_options_from_settings(&self, options: &mut SshConnectionOptions) {
|
||||
for conn in self.ssh_connections() {
|
||||
if conn.host == options.host
|
||||
&& conn.username == options.username
|
||||
&& conn.port == options.port
|
||||
{
|
||||
options.nickname = conn.nickname;
|
||||
options.upload_binary_over_ssh = conn.upload_binary_over_ssh.unwrap_or_default();
|
||||
options.args = Some(conn.args);
|
||||
options.port_forwards = conn.port_forwards;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn connection_options_for(
|
||||
&self,
|
||||
host: String,
|
||||
port: Option<u16>,
|
||||
username: Option<String>,
|
||||
) -> SshConnectionOptions {
|
||||
for conn in self.ssh_connections() {
|
||||
if conn.host == host && conn.username == username && conn.port == port {
|
||||
return SshConnectionOptions {
|
||||
nickname: conn.nickname,
|
||||
upload_binary_over_ssh: conn.upload_binary_over_ssh.unwrap_or_default(),
|
||||
args: Some(conn.args),
|
||||
host,
|
||||
port,
|
||||
username,
|
||||
port_forwards: conn.port_forwards,
|
||||
password: None,
|
||||
};
|
||||
}
|
||||
}
|
||||
SshConnectionOptions {
|
||||
let mut options = SshConnectionOptions {
|
||||
host,
|
||||
port,
|
||||
username,
|
||||
..Default::default()
|
||||
}
|
||||
};
|
||||
self.fill_connection_options_from_settings(&mut options);
|
||||
options
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +139,7 @@ impl Settings for SshSettings {
|
||||
fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {}
|
||||
}
|
||||
|
||||
pub struct SshPrompt {
|
||||
pub struct RemoteConnectionPrompt {
|
||||
connection_string: SharedString,
|
||||
nickname: Option<SharedString>,
|
||||
status_message: Option<SharedString>,
|
||||
@@ -144,7 +148,7 @@ pub struct SshPrompt {
|
||||
editor: Entity<Editor>,
|
||||
}
|
||||
|
||||
impl Drop for SshPrompt {
|
||||
impl Drop for RemoteConnectionPrompt {
|
||||
fn drop(&mut self) {
|
||||
if let Some(cancel) = self.cancellation.take() {
|
||||
cancel.send(()).ok();
|
||||
@@ -152,24 +156,22 @@ impl Drop for SshPrompt {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SshConnectionModal {
|
||||
pub(crate) prompt: Entity<SshPrompt>,
|
||||
pub struct RemoteConnectionModal {
|
||||
pub(crate) prompt: Entity<RemoteConnectionPrompt>,
|
||||
paths: Vec<PathBuf>,
|
||||
finished: bool,
|
||||
}
|
||||
|
||||
impl SshPrompt {
|
||||
impl RemoteConnectionPrompt {
|
||||
pub(crate) fn new(
|
||||
connection_options: &SshConnectionOptions,
|
||||
connection_string: String,
|
||||
nickname: Option<String>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let connection_string = connection_options.connection_string().into();
|
||||
let nickname = connection_options.nickname.clone().map(|s| s.into());
|
||||
|
||||
Self {
|
||||
connection_string,
|
||||
nickname,
|
||||
connection_string: connection_string.into(),
|
||||
nickname: nickname.map(|nickname| nickname.into()),
|
||||
editor: cx.new(|cx| Editor::single_line(window, cx)),
|
||||
status_message: None,
|
||||
cancellation: None,
|
||||
@@ -232,7 +234,7 @@ impl SshPrompt {
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for SshPrompt {
|
||||
impl Render for RemoteConnectionPrompt {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let theme = ThemeSettings::get_global(cx);
|
||||
|
||||
@@ -297,15 +299,22 @@ impl Render for SshPrompt {
|
||||
}
|
||||
}
|
||||
|
||||
impl SshConnectionModal {
|
||||
impl RemoteConnectionModal {
|
||||
pub(crate) fn new(
|
||||
connection_options: &SshConnectionOptions,
|
||||
connection_options: &RemoteConnectionOptions,
|
||||
paths: Vec<PathBuf>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let (connection_string, nickname) = match connection_options {
|
||||
RemoteConnectionOptions::Ssh(options) => {
|
||||
(options.connection_string(), options.nickname.clone())
|
||||
}
|
||||
RemoteConnectionOptions::Wsl(options) => (options.distro_name.clone(), None),
|
||||
};
|
||||
Self {
|
||||
prompt: cx.new(|cx| SshPrompt::new(connection_options, window, cx)),
|
||||
prompt: cx
|
||||
.new(|cx| RemoteConnectionPrompt::new(connection_string, nickname, window, cx)),
|
||||
finished: false,
|
||||
paths,
|
||||
}
|
||||
@@ -386,7 +395,7 @@ impl RenderOnce for SshConnectionHeader {
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for SshConnectionModal {
|
||||
impl Render for RemoteConnectionModal {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl ui::IntoElement {
|
||||
let nickname = self.prompt.read(cx).nickname.clone();
|
||||
let connection_string = self.prompt.read(cx).connection_string.clone();
|
||||
@@ -423,15 +432,15 @@ impl Render for SshConnectionModal {
|
||||
}
|
||||
}
|
||||
|
||||
impl Focusable for SshConnectionModal {
|
||||
impl Focusable for RemoteConnectionModal {
|
||||
fn focus_handle(&self, cx: &gpui::App) -> gpui::FocusHandle {
|
||||
self.prompt.read(cx).editor.focus_handle(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<DismissEvent> for SshConnectionModal {}
|
||||
impl EventEmitter<DismissEvent> for RemoteConnectionModal {}
|
||||
|
||||
impl ModalView for SshConnectionModal {
|
||||
impl ModalView for RemoteConnectionModal {
|
||||
fn on_before_dismiss(
|
||||
&mut self,
|
||||
_window: &mut Window,
|
||||
@@ -446,13 +455,13 @@ impl ModalView for SshConnectionModal {
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SshClientDelegate {
|
||||
pub struct RemoteClientDelegate {
|
||||
window: AnyWindowHandle,
|
||||
ui: WeakEntity<SshPrompt>,
|
||||
ui: WeakEntity<RemoteConnectionPrompt>,
|
||||
known_password: Option<String>,
|
||||
}
|
||||
|
||||
impl remote::RemoteClientDelegate for SshClientDelegate {
|
||||
impl remote::RemoteClientDelegate for RemoteClientDelegate {
|
||||
fn ask_password(&self, prompt: String, tx: oneshot::Sender<String>, cx: &mut AsyncApp) {
|
||||
let mut known_password = self.known_password.clone();
|
||||
if let Some(password) = known_password.take() {
|
||||
@@ -522,7 +531,7 @@ impl remote::RemoteClientDelegate for SshClientDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
impl SshClientDelegate {
|
||||
impl RemoteClientDelegate {
|
||||
fn update_status(&self, status: Option<&str>, cx: &mut AsyncApp) {
|
||||
self.window
|
||||
.update(cx, |_, _, cx| {
|
||||
@@ -534,14 +543,10 @@ impl SshClientDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_connecting_over_ssh(workspace: &Workspace, cx: &App) -> bool {
|
||||
workspace.active_modal::<SshConnectionModal>(cx).is_some()
|
||||
}
|
||||
|
||||
pub fn connect_over_ssh(
|
||||
unique_identifier: ConnectionIdentifier,
|
||||
connection_options: SshConnectionOptions,
|
||||
ui: Entity<SshPrompt>,
|
||||
ui: Entity<RemoteConnectionPrompt>,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> Task<Result<Option<Entity<RemoteClient>>>> {
|
||||
@@ -554,7 +559,7 @@ pub fn connect_over_ssh(
|
||||
unique_identifier,
|
||||
connection_options,
|
||||
rx,
|
||||
Arc::new(SshClientDelegate {
|
||||
Arc::new(RemoteClientDelegate {
|
||||
window,
|
||||
ui: ui.downgrade(),
|
||||
known_password,
|
||||
@@ -563,8 +568,8 @@ pub fn connect_over_ssh(
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn open_ssh_project(
|
||||
connection_options: SshConnectionOptions,
|
||||
pub async fn open_remote_project(
|
||||
connection_options: RemoteConnectionOptions,
|
||||
paths: Vec<PathBuf>,
|
||||
app_state: Arc<AppState>,
|
||||
open_options: workspace::OpenOptions,
|
||||
@@ -575,13 +580,7 @@ pub async fn open_ssh_project(
|
||||
} else {
|
||||
let workspace_position = cx
|
||||
.update(|cx| {
|
||||
workspace::ssh_workspace_position_from_db(
|
||||
connection_options.host.clone(),
|
||||
connection_options.port,
|
||||
connection_options.username.clone(),
|
||||
&paths,
|
||||
cx,
|
||||
)
|
||||
workspace::remote_workspace_position_from_db(connection_options.clone(), &paths, cx)
|
||||
})?
|
||||
.await
|
||||
.context("fetching ssh workspace position from db")?;
|
||||
@@ -611,16 +610,16 @@ pub async fn open_ssh_project(
|
||||
loop {
|
||||
let (cancel_tx, cancel_rx) = oneshot::channel();
|
||||
let delegate = window.update(cx, {
|
||||
let connection_options = connection_options.clone();
|
||||
let paths = paths.clone();
|
||||
let connection_options = connection_options.clone();
|
||||
move |workspace, window, cx| {
|
||||
window.activate_window();
|
||||
workspace.toggle_modal(window, cx, |window, cx| {
|
||||
SshConnectionModal::new(&connection_options, paths, window, cx)
|
||||
RemoteConnectionModal::new(&connection_options, paths, window, cx)
|
||||
});
|
||||
|
||||
let ui = workspace
|
||||
.active_modal::<SshConnectionModal>(cx)?
|
||||
.active_modal::<RemoteConnectionModal>(cx)?
|
||||
.read(cx)
|
||||
.prompt
|
||||
.clone();
|
||||
@@ -629,19 +628,25 @@ pub async fn open_ssh_project(
|
||||
ui.set_cancellation_tx(cancel_tx);
|
||||
});
|
||||
|
||||
Some(Arc::new(SshClientDelegate {
|
||||
Some(Arc::new(RemoteClientDelegate {
|
||||
window: window.window_handle(),
|
||||
ui: ui.downgrade(),
|
||||
known_password: connection_options.password.clone(),
|
||||
known_password: if let RemoteConnectionOptions::Ssh(options) =
|
||||
&connection_options
|
||||
{
|
||||
options.password.clone()
|
||||
} else {
|
||||
None
|
||||
},
|
||||
}))
|
||||
}
|
||||
})?;
|
||||
|
||||
let Some(delegate) = delegate else { break };
|
||||
|
||||
let did_open_ssh_project = cx
|
||||
let did_open_project = cx
|
||||
.update(|cx| {
|
||||
workspace::open_ssh_project_with_new_connection(
|
||||
workspace::open_remote_project_with_new_connection(
|
||||
window,
|
||||
connection_options.clone(),
|
||||
cancel_rx,
|
||||
@@ -655,19 +660,22 @@ pub async fn open_ssh_project(
|
||||
|
||||
window
|
||||
.update(cx, |workspace, _, cx| {
|
||||
if let Some(ui) = workspace.active_modal::<SshConnectionModal>(cx) {
|
||||
if let Some(ui) = workspace.active_modal::<RemoteConnectionModal>(cx) {
|
||||
ui.update(cx, |modal, cx| modal.finished(cx))
|
||||
}
|
||||
})
|
||||
.ok();
|
||||
|
||||
if let Err(e) = did_open_ssh_project {
|
||||
if let Err(e) = did_open_project {
|
||||
log::error!("Failed to open project: {e:?}");
|
||||
let response = window
|
||||
.update(cx, |_, window, cx| {
|
||||
window.prompt(
|
||||
PromptLevel::Critical,
|
||||
"Failed to connect over SSH",
|
||||
match connection_options {
|
||||
RemoteConnectionOptions::Ssh(_) => "Failed to connect over SSH",
|
||||
RemoteConnectionOptions::Wsl(_) => "Failed to connect to WSL",
|
||||
},
|
||||
Some(&e.to_string()),
|
||||
&["Retry", "Ok"],
|
||||
cx,
|
||||
@@ -1,70 +1,52 @@
|
||||
use std::any::Any;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::BTreeSet;
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
|
||||
use crate::{
|
||||
remote_connections::{
|
||||
RemoteConnectionModal, RemoteConnectionPrompt, RemoteSettingsContent, SshConnection,
|
||||
SshConnectionHeader, SshProject, SshSettings, connect_over_ssh, open_remote_project,
|
||||
},
|
||||
ssh_config::parse_ssh_config_hosts,
|
||||
};
|
||||
use editor::Editor;
|
||||
use file_finder::OpenPathDelegate;
|
||||
use futures::FutureExt;
|
||||
use futures::channel::oneshot;
|
||||
use futures::future::Shared;
|
||||
use futures::select;
|
||||
use gpui::ClickEvent;
|
||||
use gpui::ClipboardItem;
|
||||
use gpui::Subscription;
|
||||
use gpui::Task;
|
||||
use gpui::WeakEntity;
|
||||
use gpui::canvas;
|
||||
use futures::{FutureExt, channel::oneshot, future::Shared, select};
|
||||
use gpui::{
|
||||
AnyElement, App, Context, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable,
|
||||
PromptLevel, ScrollHandle, Window,
|
||||
AnyElement, App, ClickEvent, ClipboardItem, Context, DismissEvent, Entity, EventEmitter,
|
||||
FocusHandle, Focusable, PromptLevel, ScrollHandle, Subscription, Task, WeakEntity, Window,
|
||||
canvas,
|
||||
};
|
||||
use paths::global_ssh_config_file;
|
||||
use paths::user_ssh_config_file;
|
||||
use paths::{global_ssh_config_file, user_ssh_config_file};
|
||||
use picker::Picker;
|
||||
use project::Fs;
|
||||
use project::Project;
|
||||
use remote::remote_client::ConnectionIdentifier;
|
||||
use remote::{RemoteClient, SshConnectionOptions};
|
||||
use settings::Settings;
|
||||
use settings::SettingsStore;
|
||||
use settings::update_settings_file;
|
||||
use settings::watch_config_file;
|
||||
use project::{Fs, Project};
|
||||
use remote::{
|
||||
RemoteClient, RemoteConnectionOptions, SshConnectionOptions,
|
||||
remote_client::ConnectionIdentifier,
|
||||
};
|
||||
use settings::{Settings, SettingsStore, update_settings_file, watch_config_file};
|
||||
use smol::stream::StreamExt as _;
|
||||
use ui::Navigable;
|
||||
use ui::NavigableEntry;
|
||||
use std::{
|
||||
any::Any,
|
||||
borrow::Cow,
|
||||
collections::BTreeSet,
|
||||
path::PathBuf,
|
||||
rc::Rc,
|
||||
sync::{
|
||||
Arc,
|
||||
atomic::{self, AtomicUsize},
|
||||
},
|
||||
};
|
||||
use ui::{
|
||||
IconButtonShape, List, ListItem, ListSeparator, Modal, ModalHeader, Scrollbar, ScrollbarState,
|
||||
Section, Tooltip, prelude::*,
|
||||
IconButtonShape, List, ListItem, ListSeparator, Modal, ModalHeader, Navigable, NavigableEntry,
|
||||
Scrollbar, ScrollbarState, Section, Tooltip, prelude::*,
|
||||
};
|
||||
use util::{
|
||||
ResultExt,
|
||||
paths::{PathStyle, RemotePathBuf},
|
||||
};
|
||||
use workspace::OpenOptions;
|
||||
use workspace::Toast;
|
||||
use workspace::notifications::NotificationId;
|
||||
use workspace::{
|
||||
ModalView, Workspace, notifications::DetachAndPromptErr,
|
||||
open_ssh_project_with_existing_connection,
|
||||
ModalView, OpenOptions, Toast, Workspace,
|
||||
notifications::{DetachAndPromptErr, NotificationId},
|
||||
open_remote_project_with_existing_connection,
|
||||
};
|
||||
|
||||
use crate::ssh_config::parse_ssh_config_hosts;
|
||||
use crate::ssh_connections::RemoteSettingsContent;
|
||||
use crate::ssh_connections::SshConnection;
|
||||
use crate::ssh_connections::SshConnectionHeader;
|
||||
use crate::ssh_connections::SshConnectionModal;
|
||||
use crate::ssh_connections::SshProject;
|
||||
use crate::ssh_connections::SshPrompt;
|
||||
use crate::ssh_connections::SshSettings;
|
||||
use crate::ssh_connections::connect_over_ssh;
|
||||
use crate::ssh_connections::open_ssh_project;
|
||||
|
||||
mod navigation_base {}
|
||||
pub struct RemoteServerProjects {
|
||||
mode: Mode,
|
||||
focus_handle: FocusHandle,
|
||||
@@ -79,7 +61,7 @@ pub struct RemoteServerProjects {
|
||||
struct CreateRemoteServer {
|
||||
address_editor: Entity<Editor>,
|
||||
address_error: Option<SharedString>,
|
||||
ssh_prompt: Option<Entity<SshPrompt>>,
|
||||
ssh_prompt: Option<Entity<RemoteConnectionPrompt>>,
|
||||
_creating: Option<Task<Option<()>>>,
|
||||
}
|
||||
|
||||
@@ -222,8 +204,13 @@ impl ProjectPicker {
|
||||
})
|
||||
.log_err()?;
|
||||
|
||||
open_ssh_project_with_existing_connection(
|
||||
connection, project, paths, app_state, window, cx,
|
||||
open_remote_project_with_existing_connection(
|
||||
RemoteConnectionOptions::Ssh(connection),
|
||||
project,
|
||||
paths,
|
||||
app_state,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
.await
|
||||
.log_err();
|
||||
@@ -472,7 +459,14 @@ impl RemoteServerProjects {
|
||||
return;
|
||||
}
|
||||
};
|
||||
let ssh_prompt = cx.new(|cx| SshPrompt::new(&connection_options, window, cx));
|
||||
let ssh_prompt = cx.new(|cx| {
|
||||
RemoteConnectionPrompt::new(
|
||||
connection_options.connection_string(),
|
||||
connection_options.nickname.clone(),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
let connection = connect_over_ssh(
|
||||
ConnectionIdentifier::setup(),
|
||||
@@ -552,15 +546,20 @@ impl RemoteServerProjects {
|
||||
};
|
||||
|
||||
let create_new_window = self.create_new_window;
|
||||
let connection_options = ssh_connection.into();
|
||||
let connection_options: SshConnectionOptions = ssh_connection.into();
|
||||
workspace.update(cx, |_, cx| {
|
||||
cx.defer_in(window, move |workspace, window, cx| {
|
||||
let app_state = workspace.app_state().clone();
|
||||
workspace.toggle_modal(window, cx, |window, cx| {
|
||||
SshConnectionModal::new(&connection_options, Vec::new(), window, cx)
|
||||
RemoteConnectionModal::new(
|
||||
&RemoteConnectionOptions::Ssh(connection_options.clone()),
|
||||
Vec::new(),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
let prompt = workspace
|
||||
.active_modal::<SshConnectionModal>(cx)
|
||||
.active_modal::<RemoteConnectionModal>(cx)
|
||||
.unwrap()
|
||||
.read(cx)
|
||||
.prompt
|
||||
@@ -579,7 +578,7 @@ impl RemoteServerProjects {
|
||||
let session = connect.await;
|
||||
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
if let Some(prompt) = workspace.active_modal::<SshConnectionModal>(cx) {
|
||||
if let Some(prompt) = workspace.active_modal::<RemoteConnectionModal>(cx) {
|
||||
prompt.update(cx, |prompt, cx| prompt.finished(cx))
|
||||
}
|
||||
})?;
|
||||
@@ -898,8 +897,8 @@ impl RemoteServerProjects {
|
||||
};
|
||||
|
||||
cx.spawn_in(window, async move |_, cx| {
|
||||
let result = open_ssh_project(
|
||||
server.into(),
|
||||
let result = open_remote_project(
|
||||
RemoteConnectionOptions::Ssh(server.into()),
|
||||
project.paths.into_iter().map(PathBuf::from).collect(),
|
||||
app_state,
|
||||
OpenOptions {
|
||||
|
||||
Reference in New Issue
Block a user