Compare commits
9 Commits
fix-git-ht
...
v0.137.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
951898034c | ||
|
|
6bf53a1fcd | ||
|
|
719680b5a7 | ||
|
|
944356ec10 | ||
|
|
e5646fb28d | ||
|
|
44a5fdc425 | ||
|
|
21ea30122a | ||
|
|
416fa58850 | ||
|
|
cd2ec1e08e |
3
Cargo.lock
generated
3
Cargo.lock
generated
@@ -8043,6 +8043,7 @@ name = "recent_projects"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"client",
|
||||
"dev_server_projects",
|
||||
"editor",
|
||||
"feature_flags",
|
||||
@@ -13000,7 +13001,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed"
|
||||
version = "0.137.0"
|
||||
version = "0.137.2"
|
||||
dependencies = [
|
||||
"activity_indicator",
|
||||
"anyhow",
|
||||
|
||||
@@ -84,7 +84,8 @@ lazy_static! {
|
||||
std::env::var("ZED_ALWAYS_ACTIVE").map_or(false, |e| !e.is_empty());
|
||||
}
|
||||
|
||||
pub const INITIAL_RECONNECTION_DELAY: Duration = Duration::from_millis(100);
|
||||
pub const INITIAL_RECONNECTION_DELAY: Duration = Duration::from_millis(500);
|
||||
pub const MAX_RECONNECTION_DELAY: Duration = Duration::from_secs(10);
|
||||
pub const CONNECTION_TIMEOUT: Duration = Duration::from_secs(20);
|
||||
|
||||
actions!(client, [SignIn, SignOut, Reconnect]);
|
||||
@@ -287,7 +288,6 @@ struct ClientState {
|
||||
status: (watch::Sender<Status>, watch::Receiver<Status>),
|
||||
entity_id_extractors: HashMap<TypeId, fn(&dyn AnyTypedEnvelope) -> u64>,
|
||||
_reconnect_task: Option<Task<()>>,
|
||||
reconnect_interval: Duration,
|
||||
entities_by_type_and_remote_id: HashMap<(TypeId, u64), WeakSubscriber>,
|
||||
models_by_message_type: HashMap<TypeId, AnyWeakModel>,
|
||||
entity_types_by_message_type: HashMap<TypeId, TypeId>,
|
||||
@@ -363,7 +363,6 @@ impl Default for ClientState {
|
||||
status: watch::channel_with(Status::SignedOut),
|
||||
entity_id_extractors: Default::default(),
|
||||
_reconnect_task: None,
|
||||
reconnect_interval: Duration::from_secs(5),
|
||||
models_by_message_type: Default::default(),
|
||||
entities_by_type_and_remote_id: Default::default(),
|
||||
entity_types_by_message_type: Default::default(),
|
||||
@@ -623,7 +622,6 @@ impl Client {
|
||||
}
|
||||
Status::ConnectionLost => {
|
||||
let this = self.clone();
|
||||
let reconnect_interval = state.reconnect_interval;
|
||||
state._reconnect_task = Some(cx.spawn(move |cx| async move {
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
let mut rng = StdRng::seed_from_u64(0);
|
||||
@@ -642,8 +640,9 @@ impl Client {
|
||||
);
|
||||
cx.background_executor().timer(delay).await;
|
||||
delay = delay
|
||||
.mul_f32(rng.gen_range(1.0..=2.0))
|
||||
.min(reconnect_interval);
|
||||
.mul_f32(rng.gen_range(0.5..=2.5))
|
||||
.max(INITIAL_RECONNECTION_DELAY)
|
||||
.min(MAX_RECONNECTION_DELAY);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1140,8 +1140,17 @@ impl Server {
|
||||
Principal::DevServer(dev_server) => {
|
||||
{
|
||||
let mut pool = self.connection_pool.lock();
|
||||
if pool.dev_server_connection_id(dev_server.id).is_some() {
|
||||
return Err(anyhow!(ErrorCode::DevServerAlreadyOnline))?;
|
||||
if let Some(stale_connection_id) = pool.dev_server_connection_id(dev_server.id)
|
||||
{
|
||||
self.peer.send(
|
||||
stale_connection_id,
|
||||
proto::ShutdownDevServer {
|
||||
reason: Some(
|
||||
"another dev server connected with the same token".to_string(),
|
||||
),
|
||||
},
|
||||
)?;
|
||||
pool.remove_connection(stale_connection_id)?;
|
||||
};
|
||||
pool.add_dev_server(connection_id, dev_server.id, zed_version);
|
||||
}
|
||||
@@ -2398,9 +2407,12 @@ async fn regenerate_dev_server_token(
|
||||
.dev_server_connection_id(dev_server_id);
|
||||
if let Some(connection_id) = connection_id {
|
||||
shutdown_dev_server_internal(dev_server_id, connection_id, &session).await?;
|
||||
session
|
||||
.peer
|
||||
.send(connection_id, proto::ShutdownDevServer {})?;
|
||||
session.peer.send(
|
||||
connection_id,
|
||||
proto::ShutdownDevServer {
|
||||
reason: Some("dev server token was regenerated".to_string()),
|
||||
},
|
||||
)?;
|
||||
let _ = remove_dev_server_connection(dev_server_id, &session).await;
|
||||
}
|
||||
|
||||
@@ -2470,9 +2482,12 @@ async fn delete_dev_server(
|
||||
.dev_server_connection_id(dev_server_id);
|
||||
if let Some(connection_id) = connection_id {
|
||||
shutdown_dev_server_internal(dev_server_id, connection_id, &session).await?;
|
||||
session
|
||||
.peer
|
||||
.send(connection_id, proto::ShutdownDevServer {})?;
|
||||
session.peer.send(
|
||||
connection_id,
|
||||
proto::ShutdownDevServer {
|
||||
reason: Some("dev server was deleted".to_string()),
|
||||
},
|
||||
)?;
|
||||
let _ = remove_dev_server_connection(dev_server_id, &session).await;
|
||||
}
|
||||
|
||||
|
||||
@@ -104,7 +104,10 @@ impl DevServer {
|
||||
let request = if self.remote_shutdown {
|
||||
None
|
||||
} else {
|
||||
Some(self.client.request(proto::ShutdownDevServer {}))
|
||||
Some(
|
||||
self.client
|
||||
.request(proto::ShutdownDevServer { reason: None }),
|
||||
)
|
||||
};
|
||||
async move {
|
||||
if let Some(request) = request {
|
||||
|
||||
@@ -14,6 +14,7 @@ doctest = false
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
client.workspace = true
|
||||
editor.workspace = true
|
||||
feature_flags.workspace = true
|
||||
fuzzy.workspace = true
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use anyhow::Context;
|
||||
use dev_server_projects::{DevServer, DevServerId, DevServerProject, DevServerProjectId};
|
||||
use editor::Editor;
|
||||
use feature_flags::FeatureFlagAppExt;
|
||||
use feature_flags::FeatureFlagViewExt;
|
||||
use gpui::AsyncWindowContext;
|
||||
use gpui::Subscription;
|
||||
use gpui::Task;
|
||||
use gpui::WeakView;
|
||||
@@ -47,9 +49,9 @@ pub struct DevServerProjects {
|
||||
_dev_server_subscription: Subscription,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
#[derive(Default)]
|
||||
struct CreateDevServer {
|
||||
creating: bool,
|
||||
creating: Option<Task<()>>,
|
||||
dev_server_id: Option<DevServerId>,
|
||||
access_token: Option<String>,
|
||||
manual_setup: bool,
|
||||
@@ -132,12 +134,7 @@ impl DevServerProjects {
|
||||
let markdown = cx.new_view(|cx| Markdown::new("".to_string(), markdown_style, None, cx));
|
||||
|
||||
Self {
|
||||
mode: Mode::CreateDevServer(CreateDevServer {
|
||||
creating: false,
|
||||
dev_server_id: None,
|
||||
access_token: None,
|
||||
manual_setup: false,
|
||||
}),
|
||||
mode: Mode::Default(None),
|
||||
focus_handle,
|
||||
scroll_handle: ScrollHandle::new(),
|
||||
dev_server_store,
|
||||
@@ -154,12 +151,16 @@ impl DevServerProjects {
|
||||
dev_server_id: DevServerId,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
let path = self.project_path_input.read(cx).text(cx).trim().to_string();
|
||||
let mut path = self.project_path_input.read(cx).text(cx).trim().to_string();
|
||||
|
||||
if path == "" {
|
||||
return;
|
||||
}
|
||||
|
||||
if !path.starts_with('/') && !path.starts_with('~') {
|
||||
path = format!("~/{}", path);
|
||||
}
|
||||
|
||||
if self
|
||||
.dev_server_store
|
||||
.read(cx)
|
||||
@@ -313,95 +314,77 @@ impl DevServerProjects {
|
||||
});
|
||||
|
||||
let workspace = self.workspace.clone();
|
||||
let store = dev_server_projects::Store::global(cx);
|
||||
|
||||
cx.spawn({
|
||||
let access_token = access_token.clone();
|
||||
|this, mut cx| async move {
|
||||
let result = dev_server.await;
|
||||
let task = cx
|
||||
.spawn({
|
||||
|this, mut cx| async move {
|
||||
let result = dev_server.await;
|
||||
|
||||
match result {
|
||||
Ok(dev_server) => {
|
||||
if let Some(ssh_connection_string) = ssh_connection_string {
|
||||
match result {
|
||||
Ok(dev_server) => {
|
||||
if let Some(ssh_connection_string) = ssh_connection_string {
|
||||
this.update(&mut cx, |this, cx| {
|
||||
if let Mode::CreateDevServer(CreateDevServer {
|
||||
access_token,
|
||||
dev_server_id,
|
||||
..
|
||||
}) = &mut this.mode
|
||||
{
|
||||
access_token.replace(dev_server.access_token.clone());
|
||||
dev_server_id
|
||||
.replace(DevServerId(dev_server.dev_server_id));
|
||||
}
|
||||
cx.notify();
|
||||
})?;
|
||||
|
||||
let access_token = access_token.clone();
|
||||
this.update(&mut cx, |this, cx| {
|
||||
spawn_ssh_task(
|
||||
workspace
|
||||
.upgrade()
|
||||
.ok_or_else(|| anyhow!("workspace dropped"))?,
|
||||
store,
|
||||
DevServerId(dev_server.dev_server_id),
|
||||
ssh_connection_string,
|
||||
dev_server.access_token.clone(),
|
||||
&mut cx,
|
||||
)
|
||||
.await
|
||||
.log_err();
|
||||
}
|
||||
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.focus_handle.focus(cx);
|
||||
this.mode = Mode::CreateDevServer(CreateDevServer {
|
||||
creating: true,
|
||||
creating: None,
|
||||
dev_server_id: Some(DevServerId(dev_server.dev_server_id)),
|
||||
access_token: Some(access_token.unwrap_or(dev_server.access_token.clone())),
|
||||
manual_setup: false,
|
||||
});
|
||||
access_token: Some(dev_server.access_token),
|
||||
manual_setup,
|
||||
});
|
||||
cx.notify();
|
||||
})?;
|
||||
let terminal_panel = workspace
|
||||
.update(&mut cx, |workspace, cx| workspace.panel::<TerminalPanel>(cx))
|
||||
.ok()
|
||||
.flatten()
|
||||
.with_context(|| anyhow::anyhow!("No terminal panel"))?;
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.mode = Mode::CreateDevServer(CreateDevServer {
|
||||
creating: None,
|
||||
dev_server_id: existing_id,
|
||||
access_token: None,
|
||||
manual_setup,
|
||||
});
|
||||
cx.notify()
|
||||
})
|
||||
.log_err();
|
||||
|
||||
let command = "sh".to_string();
|
||||
let args = vec!["-x".to_string(),"-c".to_string(),
|
||||
format!(r#"~/.local/bin/zed -v >/dev/stderr || (curl -sSL https://zed.dev/install.sh || wget -qO- https://zed.dev/install.sh) | bash && ~/.local/bin/zed --dev-server-token {}"#, dev_server.access_token)];
|
||||
|
||||
let terminal = terminal_panel.update(&mut 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: Some(TerminalWorkDir::Ssh { ssh_command: ssh_connection_string, path: None }),
|
||||
env: Default::default(),
|
||||
use_new_terminal: true,
|
||||
allow_concurrent_runs: false,
|
||||
reveal: RevealStrategy::Always,
|
||||
},
|
||||
cx,
|
||||
)
|
||||
})?.await?;
|
||||
|
||||
terminal.update(&mut 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 this.update(&mut cx, |this, cx| {
|
||||
this.dev_server_store.read(cx).dev_server_status(DevServerId(dev_server.dev_server_id))
|
||||
})? == DevServerStatus::Offline {
|
||||
cx.background_executor().timer(Duration::from_millis(200)).await
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.focus_handle.focus(cx);
|
||||
this.mode = Mode::CreateDevServer(CreateDevServer {
|
||||
creating: false,
|
||||
dev_server_id: Some(DevServerId(dev_server.dev_server_id)),
|
||||
access_token: Some(dev_server.access_token),
|
||||
manual_setup: false,
|
||||
});
|
||||
cx.notify();
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.mode = Mode::CreateDevServer(CreateDevServer { creating:false, dev_server_id: existing_id, access_token: None, manual_setup });
|
||||
cx.notify()
|
||||
})
|
||||
.log_err();
|
||||
|
||||
return Err(e)
|
||||
}
|
||||
}
|
||||
}})
|
||||
.detach_and_prompt_err("Failed to create server", cx, |_, _| None);
|
||||
}
|
||||
})
|
||||
.prompt_err("Failed to create server", cx, |_, _| None);
|
||||
|
||||
self.mode = Mode::CreateDevServer(CreateDevServer {
|
||||
creating: true,
|
||||
creating: Some(task),
|
||||
dev_server_id: existing_id,
|
||||
access_token,
|
||||
manual_setup,
|
||||
@@ -503,7 +486,7 @@ impl DevServerProjects {
|
||||
self.create_dev_server_project(create_project.dev_server_id, cx);
|
||||
}
|
||||
Mode::CreateDevServer(state) => {
|
||||
if !state.creating {
|
||||
if state.creating.is_none() || state.dev_server_id.is_some() {
|
||||
self.create_or_update_dev_server(
|
||||
state.manual_setup,
|
||||
state.dev_server_id,
|
||||
@@ -580,7 +563,7 @@ impl DevServerProjects {
|
||||
.on_click(cx.listener(move |this, _, cx| {
|
||||
this.mode = Mode::CreateDevServer(CreateDevServer {
|
||||
dev_server_id: Some(dev_server_id),
|
||||
creating: false,
|
||||
creating: None,
|
||||
access_token: None,
|
||||
manual_setup,
|
||||
});
|
||||
@@ -715,16 +698,14 @@ impl DevServerProjects {
|
||||
}
|
||||
|
||||
fn render_create_dev_server(
|
||||
&mut self,
|
||||
state: CreateDevServer,
|
||||
&self,
|
||||
state: &CreateDevServer,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> impl IntoElement {
|
||||
let CreateDevServer {
|
||||
creating,
|
||||
dev_server_id,
|
||||
access_token,
|
||||
manual_setup,
|
||||
} = state.clone();
|
||||
let creating = state.creating.is_some();
|
||||
let dev_server_id = state.dev_server_id;
|
||||
let access_token = state.access_token.clone();
|
||||
let manual_setup = state.manual_setup;
|
||||
|
||||
let status = dev_server_id
|
||||
.map(|id| self.dev_server_store.read(cx).dev_server_status(id))
|
||||
@@ -770,13 +751,11 @@ impl DevServerProjects {
|
||||
Label::new("Connect via SSH (default)"),
|
||||
!manual_setup,
|
||||
cx.listener({
|
||||
let state = state.clone();
|
||||
move |this, _, cx| {
|
||||
this.mode = Mode::CreateDevServer(CreateDevServer {
|
||||
manual_setup: false,
|
||||
..state.clone()
|
||||
});
|
||||
cx.notify()
|
||||
if let Mode::CreateDevServer(CreateDevServer{ manual_setup, .. }) = &mut this.mode {
|
||||
*manual_setup = false;
|
||||
}
|
||||
cx.notify()
|
||||
}
|
||||
}),
|
||||
))
|
||||
@@ -785,13 +764,11 @@ impl DevServerProjects {
|
||||
Label::new("Manual Setup"),
|
||||
manual_setup,
|
||||
cx.listener({
|
||||
let state = state.clone();
|
||||
move |this, _, cx| {
|
||||
this.mode = Mode::CreateDevServer(CreateDevServer {
|
||||
manual_setup: true,
|
||||
..state.clone()
|
||||
});
|
||||
cx.notify()
|
||||
if let Mode::CreateDevServer(CreateDevServer{ manual_setup, .. }) = &mut this.mode {
|
||||
*manual_setup = true;
|
||||
}
|
||||
cx.notify()
|
||||
}}),
|
||||
)))
|
||||
.when(dev_server_id.is_none(), |el| {
|
||||
@@ -839,10 +816,10 @@ impl DevServerProjects {
|
||||
cx.notify();
|
||||
}))
|
||||
} else {
|
||||
Button::new("create-dev-server", if manual_setup { "Create"} else { "Connect"})
|
||||
Button::new("create-dev-server", if manual_setup { if dev_server_id.is_some() { "Update" } else { "Create"} } else { if dev_server_id.is_some() { "Reconnect" } else { "Connect"} })
|
||||
.style(ButtonStyle::Filled)
|
||||
.layer(ElevationIndex::ModalSurface)
|
||||
.disabled(creating)
|
||||
.disabled(creating && dev_server_id.is_none())
|
||||
.on_click(cx.listener({
|
||||
let access_token = access_token.clone();
|
||||
move |this, _, cx| {
|
||||
@@ -1007,18 +984,115 @@ impl Render for DevServerProjects {
|
||||
.on_mouse_down_out(cx.listener(|this, _, cx| {
|
||||
if matches!(this.mode, Mode::Default(None)) {
|
||||
cx.emit(DismissEvent)
|
||||
} else {
|
||||
this.focus_handle(cx).focus(cx);
|
||||
cx.stop_propagation()
|
||||
}
|
||||
}))
|
||||
.w(rems(34.))
|
||||
.max_h(rems(40.))
|
||||
.child(match &self.mode {
|
||||
Mode::Default(_) => self.render_default(cx).into_any_element(),
|
||||
Mode::CreateDevServer(state) => self
|
||||
.render_create_dev_server(state.clone(), cx)
|
||||
.into_any_element(),
|
||||
Mode::CreateDevServer(state) => {
|
||||
self.render_create_dev_server(state, cx).into_any_element()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reconnect_to_dev_server(
|
||||
workspace: View<Workspace>,
|
||||
dev_server: DevServer,
|
||||
cx: &mut WindowContext,
|
||||
) -> Task<anyhow::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,
|
||||
) -> anyhow::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 -sSL https://zed.dev/install.sh || wget -qO- https://zed.dev/install.sh) | bash && ~/.local/bin/zed --dev-server-token {}"#,
|
||||
access_token
|
||||
),
|
||||
];
|
||||
|
||||
let ssh_connection_string = ssh_connection_string.to_string();
|
||||
|
||||
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: Some(TerminalWorkDir::Ssh {
|
||||
ssh_command: ssh_connection_string,
|
||||
path: None,
|
||||
}),
|
||||
env: Default::default(),
|
||||
use_new_terminal: true,
|
||||
allow_concurrent_runs: false,
|
||||
reveal: RevealStrategy::Always,
|
||||
},
|
||||
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(())
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
mod dev_servers;
|
||||
|
||||
use client::ProjectId;
|
||||
use dev_servers::reconnect_to_dev_server;
|
||||
pub use dev_servers::DevServerProjects;
|
||||
use feature_flags::FeatureFlagAppExt;
|
||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||
@@ -17,6 +19,7 @@ use serde::Deserialize;
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
use ui::{
|
||||
prelude::*, tooltip_container, ButtonLike, IconWithIndicator, Indicator, KeyBinding, ListItem,
|
||||
@@ -313,73 +316,59 @@ impl PickerDelegate for RecentProjectsDelegate {
|
||||
}
|
||||
}
|
||||
SerializedWorkspaceLocation::DevServer(dev_server_project) => {
|
||||
let store = dev_server_projects::Store::global(cx).read(cx);
|
||||
let Some(project_id) = store
|
||||
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 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| DevServerProjects::new(cx, handle))
|
||||
})?;
|
||||
} else {
|
||||
workspace.update(&mut cx, |workspace, cx| {
|
||||
RecentProjects::open(workspace, true, cx);
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
};
|
||||
if let Some(app_state) = AppState::global(cx).upgrade() {
|
||||
let handle = if replace_current_window {
|
||||
cx.window_handle().downcast::<Workspace>()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
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()) {
|
||||
let reconnect = reconnect_to_dev_server(cx.view().clone(), server.unwrap().clone(), cx);
|
||||
let id = dev_server_project.id;
|
||||
return cx.spawn(|workspace, mut cx| async move {
|
||||
reconnect.await?;
|
||||
|
||||
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(true, cx)
|
||||
})?
|
||||
.await?;
|
||||
if continue_replacing {
|
||||
workspace
|
||||
.update(&mut cx, |_workspace, cx| {
|
||||
workspace::join_dev_server_project(project_id, app_state, Some(handle), cx)
|
||||
})?
|
||||
.await?;
|
||||
cx.background_executor().timer(Duration::from_millis(1000)).await;
|
||||
|
||||
if let Some(project_id) = store.update(&mut cx, |store, _| {
|
||||
store.dev_server_project(id)
|
||||
.and_then(|p| p.project_id)
|
||||
})? {
|
||||
workspace.update(&mut cx, move |_, cx| {
|
||||
open_dev_server_project(replace_current_window, project_id, cx)
|
||||
})?.await?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
else {
|
||||
let task =
|
||||
workspace::join_dev_server_project(project_id, app_state, None, cx);
|
||||
cx.spawn(|_, _| async move {
|
||||
task.await?;
|
||||
Ok(())
|
||||
})
|
||||
} 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| DevServerProjects::new(cx, handle))
|
||||
})?;
|
||||
} else {
|
||||
workspace.update(&mut cx, |workspace, cx| {
|
||||
RecentProjects::open(workspace, true, cx);
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
} else {
|
||||
Task::ready(Err(anyhow::anyhow!("App state not found")))
|
||||
}
|
||||
}
|
||||
};
|
||||
open_dev_server_project(replace_current_window, project_id, cx)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
.detach_and_log_err(cx);
|
||||
cx.emit(DismissEvent);
|
||||
}
|
||||
}
|
||||
@@ -546,7 +535,7 @@ impl PickerDelegate for RecentProjectsDelegate {
|
||||
.when_some(KeyBinding::for_action(&OpenRemote, cx), |button, key| {
|
||||
button.child(key)
|
||||
})
|
||||
.child(Label::new("Connect…").color(Color::Muted))
|
||||
.child(Label::new("New remote project…").color(Color::Muted))
|
||||
.on_click(|_, cx| cx.dispatch_action(OpenRemote.boxed_clone())),
|
||||
)
|
||||
.child(
|
||||
@@ -555,7 +544,7 @@ impl PickerDelegate for RecentProjectsDelegate {
|
||||
KeyBinding::for_action(&workspace::Open, cx),
|
||||
|button, key| button.child(key),
|
||||
)
|
||||
.child(Label::new("Open folder…").color(Color::Muted))
|
||||
.child(Label::new("Open local folder…").color(Color::Muted))
|
||||
.on_click(|_, cx| cx.dispatch_action(workspace::Open.boxed_clone())),
|
||||
)
|
||||
.into_any(),
|
||||
@@ -563,6 +552,51 @@ impl PickerDelegate for RecentProjectsDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
fn open_dev_server_project(
|
||||
replace_current_window: bool,
|
||||
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(true, cx)
|
||||
})?
|
||||
.await?;
|
||||
if continue_replacing {
|
||||
workspace
|
||||
.update(&mut cx, |_workspace, cx| {
|
||||
workspace::join_dev_server_project(
|
||||
project_id,
|
||||
app_state,
|
||||
Some(handle),
|
||||
cx,
|
||||
)
|
||||
})?
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
} else {
|
||||
let task = workspace::join_dev_server_project(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,
|
||||
|
||||
@@ -510,6 +510,7 @@ message CreateDevServerResponse {
|
||||
}
|
||||
|
||||
message ShutdownDevServer {
|
||||
optional string reason = 1;
|
||||
}
|
||||
|
||||
message RenameDevServer {
|
||||
|
||||
@@ -283,7 +283,7 @@ impl TerminalView {
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
Timer::after(CURSOR_BLINK_INTERVAL).await;
|
||||
this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx))
|
||||
.log_err();
|
||||
.ok();
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
@@ -512,6 +512,13 @@ where
|
||||
}
|
||||
|
||||
pub trait DetachAndPromptErr {
|
||||
fn prompt_err(
|
||||
self,
|
||||
msg: &str,
|
||||
cx: &mut WindowContext,
|
||||
f: impl FnOnce(&anyhow::Error, &mut WindowContext) -> Option<String> + 'static,
|
||||
) -> Task<()>;
|
||||
|
||||
fn detach_and_prompt_err(
|
||||
self,
|
||||
msg: &str,
|
||||
@@ -524,12 +531,12 @@ impl<R> DetachAndPromptErr for Task<anyhow::Result<R>>
|
||||
where
|
||||
R: 'static,
|
||||
{
|
||||
fn detach_and_prompt_err(
|
||||
fn prompt_err(
|
||||
self,
|
||||
msg: &str,
|
||||
cx: &mut WindowContext,
|
||||
f: impl FnOnce(&anyhow::Error, &mut WindowContext) -> Option<String> + 'static,
|
||||
) {
|
||||
) -> Task<()> {
|
||||
let msg = msg.to_owned();
|
||||
cx.spawn(|mut cx| async move {
|
||||
if let Err(err) = self.await {
|
||||
@@ -543,6 +550,14 @@ where
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
fn detach_and_prompt_err(
|
||||
self,
|
||||
msg: &str,
|
||||
cx: &mut WindowContext,
|
||||
f: impl FnOnce(&anyhow::Error, &mut WindowContext) -> Option<String> + 'static,
|
||||
) {
|
||||
self.prompt_err(msg, cx, f).detach();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
description = "The fast, collaborative code editor."
|
||||
edition = "2021"
|
||||
name = "zed"
|
||||
version = "0.137.0"
|
||||
version = "0.137.2"
|
||||
publish = false
|
||||
license = "GPL-3.0-or-later"
|
||||
authors = ["Zed Team <hi@zed.dev>"]
|
||||
|
||||
@@ -1 +1 @@
|
||||
dev
|
||||
stable
|
||||
Reference in New Issue
Block a user