Detailed changes
@@ -3018,6 +3018,17 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adab1eaa3408fb7f0c777a73e7465fd5656136fc93b670eb6df3c88c2c1344e3"
+[[package]]
+name = "install_cli"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "gpui",
+ "log",
+ "smol",
+ "util",
+]
+
[[package]]
name = "instant"
version = "0.1.12"
@@ -8020,6 +8031,7 @@ dependencies = [
"anyhow",
"editor",
"gpui",
+ "install_cli",
"log",
"project",
"settings",
@@ -8304,6 +8316,7 @@ dependencies = [
"futures 0.3.25",
"gpui",
"indoc",
+ "install_cli",
"language",
"lazy_static",
"log",
@@ -8412,6 +8425,7 @@ dependencies = [
"ignore",
"image",
"indexmap",
+ "install_cli",
"isahc",
"journal",
"language",
@@ -26,6 +26,7 @@ members = [
"crates/go_to_line",
"crates/gpui",
"crates/gpui_macros",
+ "crates/install_cli",
"crates/journal",
"crates/language",
"crates/live_kit_client",
@@ -0,0 +1,18 @@
+[package]
+name = "install_cli"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[lib]
+path = "src/install_cli.rs"
+
+[features]
+test-support = []
+
+[dependencies]
+smol = "1.2.5"
+anyhow = "1.0.38"
+log = "0.4"
+gpui = { path = "../gpui" }
+util = { path = "../util" }
@@ -0,0 +1,56 @@
+use std::path::Path;
+
+use anyhow::{Result, anyhow};
+use gpui::{AsyncAppContext, actions};
+use util::ResultExt;
+
+actions!(cli, [ Install ]);
+
+
+pub async fn install_cli(cx: &AsyncAppContext) -> Result<()> {
+ let cli_path = cx.platform().path_for_auxiliary_executable("cli")?;
+ let link_path = Path::new("/usr/local/bin/zed");
+ let bin_dir_path = link_path.parent().unwrap();
+
+ // Don't re-create symlink if it points to the same CLI binary.
+ if smol::fs::read_link(link_path).await.ok().as_ref() == Some(&cli_path) {
+ return Ok(());
+ }
+
+ // If the symlink is not there or is outdated, first try replacing it
+ // without escalating.
+ smol::fs::remove_file(link_path).await.log_err();
+ if smol::fs::unix::symlink(&cli_path, link_path)
+ .await
+ .log_err()
+ .is_some()
+ {
+ return Ok(());
+ }
+
+ // The symlink could not be created, so use osascript with admin privileges
+ // to create it.
+ let status = smol::process::Command::new("osascript")
+ .args([
+ "-e",
+ &format!(
+ "do shell script \" \
+ mkdir -p \'{}\' && \
+ ln -sf \'{}\' \'{}\' \
+ \" with administrator privileges",
+ bin_dir_path.to_string_lossy(),
+ cli_path.to_string_lossy(),
+ link_path.to_string_lossy(),
+ ),
+ ])
+ .stdout(smol::process::Stdio::inherit())
+ .stderr(smol::process::Stdio::inherit())
+ .output()
+ .await?
+ .status;
+ if status.success() {
+ Ok(())
+ } else {
+ Err(anyhow!("error running osascript"))
+ }
+}
@@ -15,6 +15,7 @@ anyhow = "1.0.38"
log = "0.4"
editor = { path = "../editor" }
gpui = { path = "../gpui" }
+install_cli = { path = "../install_cli" }
project = { path = "../project" }
settings = { path = "../settings" }
theme = { path = "../theme" }
@@ -66,6 +66,7 @@ impl View for WelcomePage {
.boxed(),
self.render_cta_button(2, "Choose a theme", theme_selector::Toggle, width, cx),
self.render_cta_button(3, "Choose a keymap", theme_selector::Toggle, width, cx),
+ self.render_cta_button(4, "Install the CLI", install_cli::Install, width, cx),
self.render_settings_checkbox::<Metrics>(
"Do you want to send telemetry?",
&theme.welcome.checkbox,
@@ -27,6 +27,7 @@ context_menu = { path = "../context_menu" }
drag_and_drop = { path = "../drag_and_drop" }
fs = { path = "../fs" }
gpui = { path = "../gpui" }
+install_cli = { path = "../install_cli" }
language = { path = "../language" }
menu = { path = "../menu" }
project = { path = "../project" }
@@ -122,6 +122,8 @@ impl Workspace {
pub mod simple_message_notification {
+ use std::borrow::Cow;
+
use gpui::{
actions,
elements::{Flex, MouseEventHandler, Padding, ParentElement, Svg, Text},
@@ -153,9 +155,9 @@ pub mod simple_message_notification {
}
pub struct MessageNotification {
- message: String,
+ message: Cow<'static, str>,
click_action: Option<Box<dyn Action>>,
- click_message: Option<String>,
+ click_message: Option<Cow<'static, str>>,
}
pub enum MessageNotificationEvent {
@@ -167,23 +169,23 @@ pub mod simple_message_notification {
}
impl MessageNotification {
- pub fn new_message<S: AsRef<str>>(message: S) -> MessageNotification {
+ pub fn new_message<S: Into<Cow<'static, str>>>(message: S) -> MessageNotification {
Self {
- message: message.as_ref().to_string(),
+ message: message.into(),
click_action: None,
click_message: None,
}
}
- pub fn new<S1: AsRef<str>, A: Action, S2: AsRef<str>>(
+ pub fn new<S1: Into<Cow<'static, str>>, A: Action, S2: Into<Cow<'static, str>>>(
message: S1,
click_action: A,
click_message: S2,
) -> Self {
Self {
- message: message.as_ref().to_string(),
+ message: message.into(),
click_action: Some(Box::new(click_action) as Box<dyn Action>),
- click_message: Some(click_message.as_ref().to_string()),
+ click_message: Some(click_message.into()),
}
}
@@ -210,6 +212,8 @@ pub mod simple_message_notification {
let click_message = self.click_message.as_ref().map(|message| message.clone());
let message = self.message.clone();
+ let has_click_action = click_action.is_some();
+
MouseEventHandler::<MessageNotificationTag>::new(0, cx, |state, cx| {
Flex::column()
.with_child(
@@ -243,6 +247,7 @@ pub mod simple_message_notification {
.on_click(MouseButton::Left, move |_, cx| {
cx.dispatch_action(CancelMessageNotification)
})
+ .with_cursor_style(CursorStyle::PointingHand)
.aligned()
.constrained()
.with_height(
@@ -272,12 +277,19 @@ pub mod simple_message_notification {
.contained()
.boxed()
})
- .with_cursor_style(CursorStyle::PointingHand)
+ // Since we're not using a proper overlay, we have to capture these extra events
+ .on_down(MouseButton::Left, |_, _| {})
+ .on_up(MouseButton::Left, |_, _| {})
.on_click(MouseButton::Left, move |_, cx| {
if let Some(click_action) = click_action.as_ref() {
cx.dispatch_any_action(click_action.boxed_clone())
}
})
+ .with_cursor_style(if has_click_action {
+ CursorStyle::PointingHand
+ } else {
+ CursorStyle::Arrow
+ })
.boxed()
}
}
@@ -17,7 +17,7 @@ mod toolbar;
pub use smallvec;
-use anyhow::{anyhow, Result};
+use anyhow::{anyhow, Result, Context};
use call::ActiveCall;
use client::{
proto::{self, PeerId},
@@ -65,7 +65,7 @@ use crate::{
};
use lazy_static::lazy_static;
use log::{error, warn};
-use notifications::NotificationHandle;
+use notifications::{NotificationHandle, NotifyResultExt};
pub use pane::*;
pub use pane_group::*;
use persistence::{model::SerializedItem, DB};
@@ -267,6 +267,28 @@ pub fn init(app_state: Arc<AppState>, cx: &mut MutableAppContext) {
})
},
);
+
+ cx.add_action(|_: &mut Workspace, _: &install_cli::Install, cx| {
+ cx.spawn(|workspace, mut cx| async move {
+ let err = install_cli::install_cli(&cx).await.context("Failed to create CLI symlink");
+
+ cx.update(|cx| {
+ workspace.update(cx, |workspace, cx| {
+ if matches!(err, Err(_)) {
+ err.notify_err(workspace, cx);
+ } else {
+ workspace.show_notification(1, cx, |cx| {
+ cx.add_view(|_| MessageNotification::new_message("Successfully installed the 'zed' binary"))
+ });
+ }
+ })
+ })
+
+ }).detach();
+
+
+
+ });
let client = &app_state.client;
client.add_view_request_handler(Workspace::handle_follow);
@@ -39,6 +39,7 @@ fsevent = { path = "../fsevent" }
fuzzy = { path = "../fuzzy" }
go_to_line = { path = "../go_to_line" }
gpui = { path = "../gpui" }
+install_cli = { path = "../install_cli" }
journal = { path = "../journal" }
language = { path = "../language" }
lsp = { path = "../lsp" }
@@ -19,7 +19,7 @@ pub fn menus() -> Vec<Menu<'static>> {
MenuItem::action("Select Theme", theme_selector::Toggle),
],
}),
- MenuItem::action("Install CLI", super::InstallCommandLineInterface),
+ MenuItem::action("Install CLI", install_cli::Install),
MenuItem::separator(),
MenuItem::action("Hide Zed", super::Hide),
MenuItem::action("Hide Others", super::HideOthers),
@@ -2,7 +2,7 @@ pub mod languages;
pub mod menus;
#[cfg(any(test, feature = "test-support"))]
pub mod test;
-use anyhow::{anyhow, Context, Result};
+use anyhow::Context;
use assets::Assets;
use breadcrumbs::Breadcrumbs;
pub use client;
@@ -21,7 +21,7 @@ use gpui::{
geometry::vector::vec2f,
impl_actions,
platform::{WindowBounds, WindowOptions},
- AssetSource, AsyncAppContext, Platform, PromptLevel, TitlebarOptions, ViewContext, WindowKind,
+ AssetSource, Platform, PromptLevel, TitlebarOptions, ViewContext, WindowKind,
};
use language::Rope;
pub use lsp;
@@ -68,7 +68,6 @@ actions!(
IncreaseBufferFontSize,
DecreaseBufferFontSize,
ResetBufferFontSize,
- InstallCommandLineInterface,
ResetDatabase,
WelcomeExperience
]
@@ -144,8 +143,8 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::MutableAppContext) {
cx.refresh_windows();
});
});
- cx.add_global_action(move |_: &InstallCommandLineInterface, cx| {
- cx.spawn(|cx| async move { install_cli(&cx).await.context("error creating CLI symlink") })
+ cx.add_global_action(move |_: &install_cli::Install, cx| {
+ cx.spawn(|cx| async move { install_cli::install_cli(&cx).await.context("error creating CLI symlink") })
.detach_and_log_err(cx);
});
cx.add_action({
@@ -505,54 +504,6 @@ fn about(_: &mut Workspace, _: &About, cx: &mut gpui::ViewContext<Workspace>) {
);
}
-async fn install_cli(cx: &AsyncAppContext) -> Result<()> {
- let cli_path = cx.platform().path_for_auxiliary_executable("cli")?;
- let link_path = Path::new("/usr/local/bin/zed");
- let bin_dir_path = link_path.parent().unwrap();
-
- // Don't re-create symlink if it points to the same CLI binary.
- if smol::fs::read_link(link_path).await.ok().as_ref() == Some(&cli_path) {
- return Ok(());
- }
-
- // If the symlink is not there or is outdated, first try replacing it
- // without escalating.
- smol::fs::remove_file(link_path).await.log_err();
- if smol::fs::unix::symlink(&cli_path, link_path)
- .await
- .log_err()
- .is_some()
- {
- return Ok(());
- }
-
- // The symlink could not be created, so use osascript with admin privileges
- // to create it.
- let status = smol::process::Command::new("osascript")
- .args([
- "-e",
- &format!(
- "do shell script \" \
- mkdir -p \'{}\' && \
- ln -sf \'{}\' \'{}\' \
- \" with administrator privileges",
- bin_dir_path.to_string_lossy(),
- cli_path.to_string_lossy(),
- link_path.to_string_lossy(),
- ),
- ])
- .stdout(smol::process::Stdio::inherit())
- .stderr(smol::process::Stdio::inherit())
- .output()
- .await?
- .status;
- if status.success() {
- Ok(())
- } else {
- Err(anyhow!("error running osascript"))
- }
-}
-
fn open_config_file(
path: &'static Path,
app_state: Arc<AppState>,