mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-08 07:35:01 +03:00
Remove release channel from Zed URLs (#8863)
Also adds a new command `cli: Register Zed Scheme` that will cause URLs to be opened in the current zed version, and we call this implicitly if you install the CLI Also add some status reporting to install cli Fixes: #8857 Release Notes: - Added success/error reporting to `cli: Install Cli` ([#8857](https://github.com/zed-industries/zed/issues/8857)). - Removed `zed-{preview,nightly,dev}:` url schemes (used by channel links) - Added `cli: Register Zed Scheme` to control which zed handles the `zed://` scheme (defaults to the most recently installed, or the version that you last used `cli: Install Cli` with)
This commit is contained in:
parent
2201b9b116
commit
f53823c840
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2390,7 +2390,6 @@ dependencies = [
|
||||
"picker",
|
||||
"postage",
|
||||
"project",
|
||||
"release_channel",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"settings",
|
||||
|
@ -3,7 +3,7 @@ mod channel_index;
|
||||
use crate::{channel_buffer::ChannelBuffer, channel_chat::ChannelChat, ChannelMessage};
|
||||
use anyhow::{anyhow, Result};
|
||||
use channel_index::ChannelIndex;
|
||||
use client::{ChannelId, Client, Subscription, User, UserId, UserStore};
|
||||
use client::{ChannelId, Client, ClientSettings, Subscription, User, UserId, UserStore};
|
||||
use collections::{hash_map, HashMap, HashSet};
|
||||
use futures::{channel::mpsc, future::Shared, Future, FutureExt, StreamExt};
|
||||
use gpui::{
|
||||
@ -11,11 +11,11 @@ use gpui::{
|
||||
Task, WeakModel,
|
||||
};
|
||||
use language::Capability;
|
||||
use release_channel::RELEASE_CHANNEL;
|
||||
use rpc::{
|
||||
proto::{self, ChannelRole, ChannelVisibility},
|
||||
TypedEnvelope,
|
||||
};
|
||||
use settings::Settings;
|
||||
use std::{mem, sync::Arc, time::Duration};
|
||||
use util::{async_maybe, maybe, ResultExt};
|
||||
|
||||
@ -93,16 +93,17 @@ pub struct ChannelState {
|
||||
}
|
||||
|
||||
impl Channel {
|
||||
pub fn link(&self) -> String {
|
||||
RELEASE_CHANNEL.link_prefix().to_owned()
|
||||
+ "channel/"
|
||||
+ &Self::slug(&self.name)
|
||||
+ "-"
|
||||
+ &self.id.to_string()
|
||||
pub fn link(&self, cx: &AppContext) -> String {
|
||||
format!(
|
||||
"{}/channel/{}-{}",
|
||||
ClientSettings::get_global(cx).server_url,
|
||||
Self::slug(&self.name),
|
||||
self.id
|
||||
)
|
||||
}
|
||||
|
||||
pub fn notes_link(&self, heading: Option<String>) -> String {
|
||||
self.link()
|
||||
pub fn notes_link(&self, heading: Option<String>, cx: &AppContext) -> String {
|
||||
self.link(cx)
|
||||
+ "/notes"
|
||||
+ &heading
|
||||
.map(|h| format!("#{}", Self::slug(&h)))
|
||||
|
@ -1437,21 +1437,29 @@ async fn delete_credentials_from_keychain(cx: &AsyncAppContext) -> Result<()> {
|
||||
.await
|
||||
}
|
||||
|
||||
const WORKTREE_URL_PREFIX: &str = "zed://worktrees/";
|
||||
/// prefix for the zed:// url scheme
|
||||
pub static ZED_URL_SCHEME: &str = "zed";
|
||||
|
||||
pub fn encode_worktree_url(id: u64, access_token: &str) -> String {
|
||||
format!("{}{}/{}", WORKTREE_URL_PREFIX, id, access_token)
|
||||
}
|
||||
|
||||
pub fn decode_worktree_url(url: &str) -> Option<(u64, String)> {
|
||||
let path = url.trim().strip_prefix(WORKTREE_URL_PREFIX)?;
|
||||
let mut parts = path.split('/');
|
||||
let id = parts.next()?.parse::<u64>().ok()?;
|
||||
let access_token = parts.next()?;
|
||||
if access_token.is_empty() {
|
||||
return None;
|
||||
/// Parses the given link into a Zed link.
|
||||
///
|
||||
/// Returns a [`Some`] containing the unprefixed link if the link is a Zed link.
|
||||
/// Returns [`None`] otherwise.
|
||||
pub fn parse_zed_link<'a>(link: &'a str, cx: &AppContext) -> Option<&'a str> {
|
||||
let server_url = &ClientSettings::get_global(cx).server_url;
|
||||
if let Some(stripped) = link
|
||||
.strip_prefix(server_url)
|
||||
.and_then(|result| result.strip_prefix('/'))
|
||||
{
|
||||
return Some(stripped);
|
||||
}
|
||||
Some((id, access_token.to_string()))
|
||||
if let Some(stripped) = link
|
||||
.strip_prefix(ZED_URL_SCHEME)
|
||||
.and_then(|result| result.strip_prefix("://"))
|
||||
{
|
||||
return Some(stripped);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -1629,17 +1637,6 @@ mod tests {
|
||||
assert_eq!(*dropped_auth_count.lock(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_and_decode_worktree_url() {
|
||||
let url = encode_worktree_url(5, "deadbeef");
|
||||
assert_eq!(decode_worktree_url(&url), Some((5, "deadbeef".to_string())));
|
||||
assert_eq!(
|
||||
decode_worktree_url(&format!("\n {}\t", url)),
|
||||
Some((5, "deadbeef".to_string()))
|
||||
);
|
||||
assert_eq!(decode_worktree_url("not://the-right-format"), None);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_subscribing_to_entity(cx: &mut TestAppContext) {
|
||||
init_test(cx);
|
||||
|
@ -265,7 +265,7 @@ impl ChannelView {
|
||||
return;
|
||||
};
|
||||
|
||||
let link = channel.notes_link(closest_heading.map(|heading| heading.text));
|
||||
let link = channel.notes_link(closest_heading.map(|heading| heading.text), cx);
|
||||
cx.write_to_clipboard(ClipboardItem::new(link));
|
||||
self.workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
|
@ -2023,7 +2023,7 @@ impl CollabPanel {
|
||||
let Some(channel) = channel_store.channel_for_id(channel_id) else {
|
||||
return;
|
||||
};
|
||||
let item = ClipboardItem::new(channel.link());
|
||||
let item = ClipboardItem::new(channel.link(cx));
|
||||
cx.write_to_clipboard(item)
|
||||
}
|
||||
|
||||
@ -2206,7 +2206,7 @@ impl CollabPanel {
|
||||
|
||||
let channel = self.channel_store.read(cx).channel_for_id(channel_id)?;
|
||||
|
||||
channel_link = Some(channel.link());
|
||||
channel_link = Some(channel.link(cx));
|
||||
(channel_icon, channel_tooltip_text) = match channel.visibility {
|
||||
proto::ChannelVisibility::Public => {
|
||||
(Some("icons/public.svg"), Some("Copy public channel link."))
|
||||
|
@ -197,7 +197,7 @@ impl Render for ChannelModal {
|
||||
.read(cx)
|
||||
.channel_for_id(channel_id)
|
||||
{
|
||||
let item = ClipboardItem::new(channel.link());
|
||||
let item = ClipboardItem::new(channel.link(cx));
|
||||
cx.write_to_clipboard(item);
|
||||
}
|
||||
})),
|
||||
|
@ -18,7 +18,6 @@ gpui.workspace = true
|
||||
picker.workspace = true
|
||||
postage.workspace = true
|
||||
project.workspace = true
|
||||
release_channel.workspace = true
|
||||
serde.workspace = true
|
||||
settings.workspace = true
|
||||
theme.workspace = true
|
||||
|
@ -4,7 +4,7 @@ use std::{
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use client::telemetry::Telemetry;
|
||||
use client::{parse_zed_link, telemetry::Telemetry};
|
||||
use collections::HashMap;
|
||||
use command_palette_hooks::{
|
||||
CommandInterceptResult, CommandPaletteFilter, CommandPaletteInterceptor,
|
||||
@ -17,7 +17,6 @@ use gpui::{
|
||||
use picker::{Picker, PickerDelegate};
|
||||
|
||||
use postage::{sink::Sink, stream::Stream};
|
||||
use release_channel::parse_zed_link;
|
||||
use ui::{h_flex, prelude::*, v_flex, HighlightedLabel, KeyBinding, ListItem, ListItemSpacing};
|
||||
use util::ResultExt;
|
||||
use workspace::{ModalView, Workspace};
|
||||
@ -26,6 +25,7 @@ use zed_actions::OpenZedUrl;
|
||||
actions!(command_palette, [Toggle]);
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
client::init_settings(cx);
|
||||
cx.set_global(HitCounts::default());
|
||||
cx.set_global(CommandPaletteFilter::default());
|
||||
cx.observe_new_views(CommandPalette::register).detach();
|
||||
@ -192,7 +192,7 @@ impl CommandPaletteDelegate {
|
||||
None
|
||||
};
|
||||
|
||||
if parse_zed_link(&query).is_some() {
|
||||
if parse_zed_link(&query, cx).is_some() {
|
||||
intercept_result = Some(CommandInterceptResult {
|
||||
action: OpenZedUrl { url: query.clone() }.boxed_clone(),
|
||||
string: query.clone(),
|
||||
|
@ -566,6 +566,14 @@ impl AppContext {
|
||||
self.platform.open_url(url);
|
||||
}
|
||||
|
||||
/// register_url_scheme requests that the given scheme (e.g. `zed` for `zed://` urls)
|
||||
/// is opened by the current app.
|
||||
/// On some platforms (e.g. macOS) you may be able to register URL schemes as part of app
|
||||
/// distribution, but this method exists to let you register schemes at runtime.
|
||||
pub fn register_url_scheme(&self, scheme: &str) -> Task<Result<()>> {
|
||||
self.platform.register_url_scheme(scheme)
|
||||
}
|
||||
|
||||
/// Returns the full pathname of the current app bundle.
|
||||
/// If the app is not being run from a bundle, returns an error.
|
||||
pub fn app_path(&self) -> Result<PathBuf> {
|
||||
|
@ -101,6 +101,8 @@ pub(crate) trait Platform: 'static {
|
||||
|
||||
fn open_url(&self, url: &str);
|
||||
fn on_open_urls(&self, callback: Box<dyn FnMut(Vec<String>)>);
|
||||
fn register_url_scheme(&self, url: &str) -> Task<Result<()>>;
|
||||
|
||||
fn prompt_for_paths(
|
||||
&self,
|
||||
options: PathPromptOptions,
|
||||
|
@ -441,6 +441,10 @@ impl Platform for LinuxPlatform {
|
||||
fn window_appearance(&self) -> crate::WindowAppearance {
|
||||
crate::WindowAppearance::Light
|
||||
}
|
||||
|
||||
fn register_url_scheme(&self, _: &str) -> Task<anyhow::Result<()>> {
|
||||
Task::ready(Err(anyhow!("register_url_scheme unimplemented")))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -525,6 +525,49 @@ impl Platform for MacPlatform {
|
||||
}
|
||||
}
|
||||
|
||||
fn register_url_scheme(&self, scheme: &str) -> Task<anyhow::Result<()>> {
|
||||
// API only available post Monterey
|
||||
// https://developer.apple.com/documentation/appkit/nsworkspace/3753004-setdefaultapplicationaturl
|
||||
let (done_tx, done_rx) = oneshot::channel();
|
||||
if self.os_version().ok() < Some(SemanticVersion::new(12, 0, 0)) {
|
||||
return Task::ready(Err(anyhow!(
|
||||
"macOS 12.0 or later is required to register URL schemes"
|
||||
)));
|
||||
}
|
||||
|
||||
let bundle_id = unsafe {
|
||||
let bundle: id = msg_send![class!(NSBundle), mainBundle];
|
||||
let bundle_id: id = msg_send![bundle, bundleIdentifier];
|
||||
if bundle_id == nil {
|
||||
return Task::ready(Err(anyhow!("Can only register URL scheme in bundled apps")));
|
||||
}
|
||||
bundle_id
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let workspace: id = msg_send![class!(NSWorkspace), sharedWorkspace];
|
||||
let scheme: id = ns_string(scheme);
|
||||
let app: id = msg_send![workspace, URLForApplicationWithBundleIdentifier: bundle_id];
|
||||
let done_tx = Cell::new(Some(done_tx));
|
||||
let block = ConcreteBlock::new(move |error: id| {
|
||||
let result = if error == nil {
|
||||
Ok(())
|
||||
} else {
|
||||
let msg: id = msg_send![error, localizedDescription];
|
||||
Err(anyhow!("Failed to register: {:?}", msg))
|
||||
};
|
||||
|
||||
if let Some(done_tx) = done_tx.take() {
|
||||
let _ = done_tx.send(result);
|
||||
}
|
||||
});
|
||||
let _: () = msg_send![workspace, setDefaultApplicationAtURL: app toOpenURLsWithScheme: scheme completionHandler: block];
|
||||
}
|
||||
|
||||
self.background_executor()
|
||||
.spawn(async { crate::Flatten::flatten(done_rx.await.map_err(|e| anyhow!(e))) })
|
||||
}
|
||||
|
||||
fn on_open_urls(&self, callback: Box<dyn FnMut(Vec<String>)>) {
|
||||
self.0.lock().open_urls = Some(callback);
|
||||
}
|
||||
|
@ -298,4 +298,8 @@ impl Platform for TestPlatform {
|
||||
fn double_click_interval(&self) -> std::time::Duration {
|
||||
Duration::from_millis(500)
|
||||
}
|
||||
|
||||
fn register_url_scheme(&self, _: &str) -> Task<anyhow::Result<()>> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
@ -314,4 +314,8 @@ impl Platform for WindowsPlatform {
|
||||
fn delete_credentials(&self, url: &str) -> Task<Result<()>> {
|
||||
Task::Ready(Some(Err(anyhow!("not implemented yet."))))
|
||||
}
|
||||
|
||||
fn register_url_scheme(&self, _: &str) -> Task<anyhow::Result<()>> {
|
||||
Task::ready(Err(anyhow!("register_url_scheme unimplemented")))
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,18 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use gpui::{actions, AsyncAppContext};
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
use util::ResultExt;
|
||||
|
||||
actions!(cli, [Install]);
|
||||
actions!(cli, [Install, RegisterZedScheme]);
|
||||
|
||||
pub async fn install_cli(cx: &AsyncAppContext) -> Result<()> {
|
||||
pub async fn install_cli(cx: &AsyncAppContext) -> Result<PathBuf> {
|
||||
let cli_path = cx.update(|cx| cx.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(());
|
||||
return Ok(link_path.into());
|
||||
}
|
||||
|
||||
// If the symlink is not there or is outdated, first try replacing it
|
||||
@ -26,7 +26,7 @@ pub async fn install_cli(cx: &AsyncAppContext) -> Result<()> {
|
||||
.log_err()
|
||||
.is_some()
|
||||
{
|
||||
return Ok(());
|
||||
return Ok(link_path.into());
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,7 +51,7 @@ pub async fn install_cli(cx: &AsyncAppContext) -> Result<()> {
|
||||
.await?
|
||||
.status;
|
||||
if status.success() {
|
||||
Ok(())
|
||||
Ok(link_path.into())
|
||||
} else {
|
||||
Err(anyhow!("error running osascript"))
|
||||
}
|
||||
|
@ -139,26 +139,6 @@ impl ReleaseChannel {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the URL scheme for this [`ReleaseChannel`].
|
||||
pub fn url_scheme(&self) -> &'static str {
|
||||
match self {
|
||||
ReleaseChannel::Dev => "zed-dev://",
|
||||
ReleaseChannel::Nightly => "zed-nightly://",
|
||||
ReleaseChannel::Preview => "zed-preview://",
|
||||
ReleaseChannel::Stable => "zed://",
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the link prefix for this [`ReleaseChannel`].
|
||||
pub fn link_prefix(&self) -> &'static str {
|
||||
match self {
|
||||
ReleaseChannel::Dev => "https://zed.dev/dev/",
|
||||
ReleaseChannel::Nightly => "https://zed.dev/nightly/",
|
||||
ReleaseChannel::Preview => "https://zed.dev/preview/",
|
||||
ReleaseChannel::Stable => "https://zed.dev/",
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the query parameter for this [`ReleaseChannel`].
|
||||
pub fn release_query_param(&self) -> Option<&'static str> {
|
||||
match self {
|
||||
@ -169,24 +149,3 @@ impl ReleaseChannel {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses the given link into a Zed link.
|
||||
///
|
||||
/// Returns a [`Some`] containing the unprefixed link if the link is a Zed link.
|
||||
/// Returns [`None`] otherwise.
|
||||
pub fn parse_zed_link(link: &str) -> Option<&str> {
|
||||
for release in [
|
||||
ReleaseChannel::Dev,
|
||||
ReleaseChannel::Nightly,
|
||||
ReleaseChannel::Preview,
|
||||
ReleaseChannel::Stable,
|
||||
] {
|
||||
if let Some(stripped) = link.strip_prefix(release.link_prefix()) {
|
||||
return Some(stripped);
|
||||
}
|
||||
if let Some(stripped) = link.strip_prefix(release.url_scheme()) {
|
||||
return Some(stripped);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
@ -107,7 +107,7 @@ identifier = "dev.zed.Zed-Dev"
|
||||
name = "Zed Dev"
|
||||
osx_minimum_system_version = "10.15.7"
|
||||
osx_info_plist_exts = ["resources/info/*"]
|
||||
osx_url_schemes = ["zed-dev"]
|
||||
osx_url_schemes = ["zed"]
|
||||
|
||||
[package.metadata.bundle-nightly]
|
||||
icon = ["resources/app-icon-nightly@2x.png", "resources/app-icon-nightly.png"]
|
||||
@ -115,7 +115,7 @@ identifier = "dev.zed.Zed-Nightly"
|
||||
name = "Zed Nightly"
|
||||
osx_minimum_system_version = "10.15.7"
|
||||
osx_info_plist_exts = ["resources/info/*"]
|
||||
osx_url_schemes = ["zed-nightly"]
|
||||
osx_url_schemes = ["zed"]
|
||||
|
||||
[package.metadata.bundle-preview]
|
||||
icon = ["resources/app-icon-preview@2x.png", "resources/app-icon-preview.png"]
|
||||
@ -123,7 +123,7 @@ identifier = "dev.zed.Zed-Preview"
|
||||
name = "Zed Preview"
|
||||
osx_minimum_system_version = "10.15.7"
|
||||
osx_info_plist_exts = ["resources/info/*"]
|
||||
osx_url_schemes = ["zed-preview"]
|
||||
osx_url_schemes = ["zed"]
|
||||
|
||||
[package.metadata.bundle-stable]
|
||||
icon = ["resources/app-icon@2x.png", "resources/app-icon.png"]
|
||||
|
@ -5,7 +5,7 @@ use anyhow::{anyhow, Context as _, Result};
|
||||
use backtrace::Backtrace;
|
||||
use chrono::Utc;
|
||||
use cli::FORCE_CLI_MODE_ENV_VAR_NAME;
|
||||
use client::{Client, UserStore};
|
||||
use client::{parse_zed_link, Client, UserStore};
|
||||
use collab_ui::channel_view::ChannelView;
|
||||
use db::kvp::KEY_VALUE_STORE;
|
||||
use editor::Editor;
|
||||
@ -23,7 +23,7 @@ use assets::Assets;
|
||||
use mimalloc::MiMalloc;
|
||||
use node_runtime::RealNodeRuntime;
|
||||
use parking_lot::Mutex;
|
||||
use release_channel::{parse_zed_link, AppCommitSha, ReleaseChannel, RELEASE_CHANNEL};
|
||||
use release_channel::{AppCommitSha, ReleaseChannel, RELEASE_CHANNEL};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::{
|
||||
default_settings, handle_settings_file_changes, watch_config_file, Settings, SettingsStore,
|
||||
@ -106,7 +106,7 @@ fn main() {
|
||||
let (listener, mut open_rx) = OpenListener::new();
|
||||
let listener = Arc::new(listener);
|
||||
let open_listener = listener.clone();
|
||||
app.on_open_urls(move |urls, _| open_listener.open_urls(&urls));
|
||||
app.on_open_urls(move |urls, cx| open_listener.open_urls(&urls, cx));
|
||||
app.on_reopen(move |cx| {
|
||||
if let Some(app_state) = AppState::try_global(cx).and_then(|app_state| app_state.upgrade())
|
||||
{
|
||||
@ -271,9 +271,9 @@ fn main() {
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
upload_panics_and_crashes(http.clone(), cx);
|
||||
cx.activate(true);
|
||||
let urls = collect_url_args();
|
||||
let urls = collect_url_args(cx);
|
||||
if !urls.is_empty() {
|
||||
listener.open_urls(&urls)
|
||||
listener.open_urls(&urls, cx)
|
||||
}
|
||||
} else {
|
||||
upload_panics_and_crashes(http.clone(), cx);
|
||||
@ -282,7 +282,7 @@ fn main() {
|
||||
if std::env::var(FORCE_CLI_MODE_ENV_VAR_NAME).ok().is_some()
|
||||
&& !listener.triggered.load(Ordering::Acquire)
|
||||
{
|
||||
listener.open_urls(&collect_url_args())
|
||||
listener.open_urls(&collect_url_args(cx), cx)
|
||||
}
|
||||
}
|
||||
|
||||
@ -921,13 +921,13 @@ fn stdout_is_a_pty() -> bool {
|
||||
std::env::var(FORCE_CLI_MODE_ENV_VAR_NAME).ok().is_none() && std::io::stdout().is_terminal()
|
||||
}
|
||||
|
||||
fn collect_url_args() -> Vec<String> {
|
||||
fn collect_url_args(cx: &AppContext) -> Vec<String> {
|
||||
env::args()
|
||||
.skip(1)
|
||||
.filter_map(|arg| match std::fs::canonicalize(Path::new(&arg)) {
|
||||
Ok(path) => Some(format!("file://{}", path.to_string_lossy())),
|
||||
Err(error) => {
|
||||
if let Some(_) = parse_zed_link(&arg) {
|
||||
if let Some(_) = parse_zed_link(&arg, cx) {
|
||||
Some(arg)
|
||||
} else {
|
||||
log::error!("error parsing path argument: {}", error);
|
||||
|
@ -1,6 +1,7 @@
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use cli::{ipc, IpcHandshake};
|
||||
use cli::{ipc::IpcSender, CliRequest, CliResponse};
|
||||
use client::parse_zed_link;
|
||||
use collections::HashMap;
|
||||
use editor::scroll::Autoscroll;
|
||||
use editor::Editor;
|
||||
@ -10,7 +11,6 @@ use futures::{FutureExt, SinkExt, StreamExt};
|
||||
use gpui::{AppContext, AsyncAppContext, Global};
|
||||
use itertools::Itertools;
|
||||
use language::{Bias, Point};
|
||||
use release_channel::parse_zed_link;
|
||||
use std::path::Path;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
@ -66,13 +66,13 @@ impl OpenListener {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn open_urls(&self, urls: &[String]) {
|
||||
pub fn open_urls(&self, urls: &[String], cx: &AppContext) {
|
||||
self.triggered.store(true, Ordering::Release);
|
||||
let request = if let Some(server_name) =
|
||||
urls.first().and_then(|url| url.strip_prefix("zed-cli://"))
|
||||
{
|
||||
self.handle_cli_connection(server_name)
|
||||
} else if let Some(request_path) = urls.first().and_then(|url| parse_zed_link(url)) {
|
||||
} else if let Some(request_path) = urls.first().and_then(|url| parse_zed_link(url, cx)) {
|
||||
self.handle_zed_url_scheme(request_path)
|
||||
} else {
|
||||
self.handle_file_urls(urls)
|
||||
|
@ -5,11 +5,12 @@ mod open_listener;
|
||||
pub use app_menus::*;
|
||||
use assistant::AssistantPanel;
|
||||
use breadcrumbs::Breadcrumbs;
|
||||
use client::ZED_URL_SCHEME;
|
||||
use collections::VecDeque;
|
||||
use editor::{Editor, MultiBuffer};
|
||||
use gpui::{
|
||||
actions, point, px, AppContext, Context, FocusableView, PromptLevel, TitlebarOptions, View,
|
||||
ViewContext, VisualContext, WindowBounds, WindowKind, WindowOptions,
|
||||
actions, point, px, AppContext, AsyncAppContext, Context, FocusableView, PromptLevel,
|
||||
TitlebarOptions, View, ViewContext, VisualContext, WindowBounds, WindowKind, WindowOptions,
|
||||
};
|
||||
pub use only_instance::*;
|
||||
pub use open_listener::*;
|
||||
@ -38,11 +39,11 @@ use util::{
|
||||
use uuid::Uuid;
|
||||
use vim::VimModeSetting;
|
||||
use welcome::BaseKeymap;
|
||||
use workspace::Pane;
|
||||
use workspace::{
|
||||
create_and_open_local_file, notifications::simple_message_notification::MessageNotification,
|
||||
open_new, AppState, NewFile, NewWindow, Workspace, WorkspaceSettings,
|
||||
open_new, AppState, NewFile, NewWindow, Toast, Workspace, WorkspaceSettings,
|
||||
};
|
||||
use workspace::{notifications::DetachAndPromptErr, Pane};
|
||||
use zed_actions::{OpenBrowser, OpenSettings, OpenZedUrl, Quit};
|
||||
|
||||
actions!(
|
||||
@ -232,7 +233,7 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
|
||||
cx.toggle_full_screen();
|
||||
})
|
||||
.register_action(|_, action: &OpenZedUrl, cx| {
|
||||
OpenListener::global(cx).open_urls(&[action.url.clone()])
|
||||
OpenListener::global(cx).open_urls(&[action.url.clone()], cx)
|
||||
})
|
||||
.register_action(|_, action: &OpenBrowser, cx| cx.open_url(&action.url))
|
||||
.register_action(move |_, _: &IncreaseBufferFontSize, cx| {
|
||||
@ -243,12 +244,50 @@ pub fn initialize_workspace(app_state: Arc<AppState>, cx: &mut AppContext) {
|
||||
})
|
||||
.register_action(move |_, _: &ResetBufferFontSize, cx| theme::reset_font_size(cx))
|
||||
.register_action(|_, _: &install_cli::Install, cx| {
|
||||
cx.spawn(|_, cx| async move {
|
||||
install_cli::install_cli(cx.deref())
|
||||
cx.spawn(|workspace, mut cx| async move {
|
||||
let path = install_cli::install_cli(cx.deref())
|
||||
.await
|
||||
.context("error creating CLI symlink")
|
||||
.context("error creating CLI symlink")?;
|
||||
workspace.update(&mut cx, |workspace, cx| {
|
||||
workspace.show_toast(
|
||||
Toast::new(
|
||||
0,
|
||||
format!(
|
||||
"Installed `zed` to {}. You can launch {} from your terminal.",
|
||||
path.to_string_lossy(),
|
||||
ReleaseChannel::global(cx).display_name()
|
||||
),
|
||||
),
|
||||
cx,
|
||||
)
|
||||
})?;
|
||||
register_zed_scheme(&cx).await.log_err();
|
||||
Ok(())
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
.detach_and_prompt_err("Error installing zed cli", cx, |_, _| None);
|
||||
})
|
||||
.register_action(|_, _: &install_cli::RegisterZedScheme, cx| {
|
||||
cx.spawn(|workspace, mut cx| async move {
|
||||
register_zed_scheme(&cx).await?;
|
||||
workspace.update(&mut cx, |workspace, cx| {
|
||||
workspace.show_toast(
|
||||
Toast::new(
|
||||
0,
|
||||
format!(
|
||||
"zed:// links will now open in {}.",
|
||||
ReleaseChannel::global(cx).display_name()
|
||||
),
|
||||
),
|
||||
cx,
|
||||
)
|
||||
})?;
|
||||
Ok(())
|
||||
})
|
||||
.detach_and_prompt_err(
|
||||
"Error registering zed:// scheme",
|
||||
cx,
|
||||
|_, _| None,
|
||||
);
|
||||
})
|
||||
.register_action(|workspace, _: &OpenLog, cx| {
|
||||
open_log_file(workspace, cx);
|
||||
@ -2881,3 +2920,8 @@ mod tests {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn register_zed_scheme(cx: &AsyncAppContext) -> anyhow::Result<()> {
|
||||
cx.update(|cx| cx.register_url_scheme(ZED_URL_SCHEME))?
|
||||
.await
|
||||
}
|
||||
|
@ -173,6 +173,7 @@ if [ "$local_arch" = false ]; then
|
||||
cp -R target/${local_target_triple}/${target_dir}/WebRTC.framework "${app_path}/Contents/Frameworks/"
|
||||
else
|
||||
cp -R target/${target_dir}/WebRTC.framework "${app_path}/Contents/Frameworks/"
|
||||
cp -R target/${target_dir}/cli "${app_path}/Contents/MacOS/"
|
||||
fi
|
||||
|
||||
# Note: The app identifier for our development builds is the same as the app identifier for nightly.
|
||||
|
Loading…
Reference in New Issue
Block a user