Implement `zed --version`

Antonio Scandurra and Nathan Sobo created

Co-Authored-By: Nathan Sobo <nathan@zed.dev>

Change summary

Cargo.lock             | 53 +++++++++++++++++++++++++++++++++++++++----
crates/cli/Cargo.toml  |  3 +
crates/cli/src/main.rs | 23 ++++++++++++++++++
3 files changed, 72 insertions(+), 7 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -1004,6 +1004,7 @@ dependencies = [
  "dirs 3.0.1",
  "ipc-channel",
  "objc",
+ "plist",
  "serde",
 ]
 
@@ -2595,7 +2596,7 @@ checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11"
 dependencies = [
  "bytes 1.0.1",
  "fnv",
- "itoa",
+ "itoa 0.4.7",
 ]
 
 [[package]]
@@ -2804,6 +2805,12 @@ version = "0.4.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
 
+[[package]]
+name = "itoa"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
+
 [[package]]
 name = "jobserver"
 version = "0.1.24"
@@ -3027,6 +3034,15 @@ dependencies = [
  "vcpkg",
 ]
 
+[[package]]
+name = "line-wrap"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9"
+dependencies = [
+ "safemem",
+]
+
 [[package]]
 name = "lipsum"
 version = "0.8.0"
@@ -3779,6 +3795,20 @@ version = "0.3.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
 
+[[package]]
+name = "plist"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd39bc6cdc9355ad1dc5eeedefee696bb35c34caf21768741e81826c0bbd7225"
+dependencies = [
+ "base64 0.13.0",
+ "indexmap",
+ "line-wrap",
+ "serde",
+ "time 0.3.7",
+ "xml-rs",
+]
+
 [[package]]
 name = "png"
 version = "0.16.8"
@@ -4440,6 +4470,12 @@ dependencies = [
  "bytemuck",
 ]
 
+[[package]]
+name = "safemem"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
+
 [[package]]
 name = "salsa20"
 version = "0.8.0"
@@ -4642,7 +4678,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79"
 dependencies = [
  "indexmap",
- "itoa",
+ "itoa 0.4.7",
  "ryu",
  "serde",
 ]
@@ -4686,7 +4722,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9"
 dependencies = [
  "form_urlencoded",
- "itoa",
+ "itoa 0.4.7",
  "ryu",
  "serde",
 ]
@@ -5039,7 +5075,7 @@ dependencies = [
  "hashlink 0.6.0",
  "hex",
  "hmac 0.10.1",
- "itoa",
+ "itoa 0.4.7",
  "libc",
  "log",
  "md-5",
@@ -5088,7 +5124,7 @@ dependencies = [
  "hashlink 0.7.0",
  "hex",
  "hmac 0.10.1",
- "itoa",
+ "itoa 0.4.7",
  "libc",
  "log",
  "md-5",
@@ -5585,6 +5621,7 @@ version = "0.3.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "004cbc98f30fa233c61a38bc77e96a9106e65c88f2d3bef182ae952027e5753d"
 dependencies = [
+ "itoa 1.0.1",
  "libc",
  "num_threads",
 ]
@@ -6324,6 +6361,12 @@ version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57"
 
+[[package]]
+name = "xml-rs"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3"
+
 [[package]]
 name = "xmlparser"
 version = "0.13.3"

crates/cli/Cargo.toml 🔗

@@ -22,4 +22,5 @@ serde = { version = "1.0", features = ["derive"] }
 cocoa = "0.24"
 core-foundation = "0.9"
 core-services = "0.2"
-objc = "0.2"
+objc = "0.2"
+plist = "1.3"

crates/cli/src/main.rs 🔗

@@ -9,10 +9,11 @@ use core_foundation::{
 use core_services::{kLSLaunchDefaults, LSLaunchURLSpec, LSOpenFromURLSpec, TCFType};
 use ipc_channel::ipc::{IpcOneShotServer, IpcReceiver, IpcSender};
 use objc::{class, msg_send, sel, sel_impl};
+use serde::Deserialize;
 use std::{ffi::CStr, fs, path::PathBuf, ptr};
 
 #[derive(Parser)]
-#[clap(name = "zed")]
+#[clap(name = "zed", global_setting(clap::AppSettings::NoAutoVersion))]
 struct Args {
     /// Wait for all of the given paths to be closed before exiting.
     #[clap(short, long)]
@@ -20,12 +21,32 @@ struct Args {
     /// A sequence of space-separated paths that you want to open.
     #[clap()]
     paths: Vec<PathBuf>,
+    /// Print Zed's version and the app path.
+    #[clap(short, long)]
+    version: bool,
+}
+
+#[derive(Debug, Deserialize)]
+struct InfoPlist {
+    #[serde(rename = "CFBundleShortVersionString")]
+    bundle_short_version_string: String,
 }
 
 fn main() -> Result<()> {
     let args = Args::parse();
 
     let app_path = locate_app()?;
+    if args.version {
+        let plist_path = app_path.join("Contents/Info.plist");
+        let plist = plist::from_file::<_, InfoPlist>(plist_path)?;
+        println!(
+            "Zed {} – {}",
+            plist.bundle_short_version_string,
+            app_path.to_string_lossy()
+        );
+        return Ok(());
+    }
+
     let (tx, rx) = launch_app(app_path)?;
 
     tx.send(CliRequest::Open {