Locate the Zed app from the CLI using NSWorkspace API

This commit is contained in:
Max Brunsfeld 2022-04-19 13:42:33 -07:00 committed by Antonio Scandurra
parent 43763fa2f8
commit a81f7ebbf6
3 changed files with 43 additions and 12 deletions

2
Cargo.lock generated
View File

@ -998,10 +998,12 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap 3.1.8", "clap 3.1.8",
"cocoa",
"core-foundation", "core-foundation",
"core-services", "core-services",
"dirs 3.0.1", "dirs 3.0.1",
"ipc-channel", "ipc-channel",
"objc",
"serde", "serde",
] ]

View File

@ -13,9 +13,13 @@ path = "src/main.rs"
[dependencies] [dependencies]
anyhow = "1.0" anyhow = "1.0"
core-foundation = "0.9"
core-services = "0.2"
clap = { version = "3.1", features = ["derive"] } clap = { version = "3.1", features = ["derive"] }
dirs = "3.0" dirs = "3.0"
ipc-channel = "0.16" ipc-channel = "0.16"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
[target.'cfg(target_os = "macos")'.dependencies]
cocoa = "0.24"
core-foundation = "0.9"
core-services = "0.2"
objc = "0.2"

View File

@ -8,7 +8,8 @@ use core_foundation::{
}; };
use core_services::{kLSLaunchDefaults, LSLaunchURLSpec, LSOpenFromURLSpec, TCFType}; use core_services::{kLSLaunchDefaults, LSLaunchURLSpec, LSOpenFromURLSpec, TCFType};
use ipc_channel::ipc::{IpcOneShotServer, IpcReceiver, IpcSender}; use ipc_channel::ipc::{IpcOneShotServer, IpcReceiver, IpcSender};
use std::{fs, path::PathBuf, ptr}; use objc::{class, msg_send, sel, sel_impl};
use std::{ffi::CStr, fs, path::PathBuf, ptr};
#[derive(Parser)] #[derive(Parser)]
#[clap(name = "zed")] #[clap(name = "zed")]
@ -48,20 +49,46 @@ fn main() -> Result<()> {
} }
fn locate_app() -> Result<PathBuf> { fn locate_app() -> Result<PathBuf> {
Ok(std::env::current_exe()? if cfg!(debug_assertions) {
.parent() Ok(std::env::current_exe()?
.unwrap() .parent()
.join("bundle/osx/Zed.app")) .unwrap()
.join("bundle/osx/Zed.app"))
} else {
Ok(path_to_app_with_bundle_identifier("dev.zed.Zed")
.unwrap_or_else(|| "/Applications/Zed.dev".into()))
}
}
fn path_to_app_with_bundle_identifier(bundle_id: &str) -> Option<PathBuf> {
use cocoa::{
base::{id, nil},
foundation::{NSString, NSURL as _},
};
unsafe {
let workspace: id = msg_send![class!(NSWorkspace), sharedWorkspace];
let bundle_id = NSString::alloc(nil).init_str(bundle_id);
let app_url: id = msg_send![workspace, URLForApplicationWithBundleIdentifier: bundle_id];
if !app_url.is_null() {
Some(PathBuf::from(
CStr::from_ptr(app_url.path().UTF8String())
.to_string_lossy()
.to_string(),
))
} else {
None
}
}
} }
fn launch_app(app_path: PathBuf) -> Result<(IpcSender<CliRequest>, IpcReceiver<CliResponse>)> { fn launch_app(app_path: PathBuf) -> Result<(IpcSender<CliRequest>, IpcReceiver<CliResponse>)> {
let (server, server_name) = IpcOneShotServer::<IpcHandshake>::new()?; let (server, server_name) = IpcOneShotServer::<IpcHandshake>::new()?;
let url = format!("zed-cli://{server_name}");
let status = unsafe { let status = unsafe {
let app_url = let app_url =
CFURL::from_path(&app_path, true).ok_or_else(|| anyhow!("invalid app path"))?; CFURL::from_path(&app_path, true).ok_or_else(|| anyhow!("invalid app path"))?;
let url = format!("zed-cli://{server_name}");
let url_to_open = CFURL::wrap_under_create_rule(CFURLCreateWithBytes( let url_to_open = CFURL::wrap_under_create_rule(CFURLCreateWithBytes(
ptr::null(), ptr::null(),
url.as_ptr(), url.as_ptr(),
@ -69,9 +96,7 @@ fn launch_app(app_path: PathBuf) -> Result<(IpcSender<CliRequest>, IpcReceiver<C
kCFStringEncodingUTF8, kCFStringEncodingUTF8,
ptr::null(), ptr::null(),
)); ));
let urls_to_open = CFArray::from_copyable(&[url_to_open.as_concrete_TypeRef()]); let urls_to_open = CFArray::from_copyable(&[url_to_open.as_concrete_TypeRef()]);
LSOpenFromURLSpec( LSOpenFromURLSpec(
&LSLaunchURLSpec { &LSLaunchURLSpec {
appURL: app_url.as_concrete_TypeRef(), appURL: app_url.as_concrete_TypeRef(),