mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-08 07:35:01 +03:00
linux cli (#11585)
- [x] Build out cli on linux - [x] Add support for --dev-server-token sent by the CLI - [x] Package cli into the .tar.gz - [x] Link the cli to ~/.local/bin in install.sh Release Notes: - linux: Add cli support for managing zed
This commit is contained in:
parent
0c2d71f1ac
commit
4f9ba28a25
54
Cargo.lock
generated
54
Cargo.lock
generated
@ -2119,7 +2119,11 @@ dependencies = [
|
|||||||
"clap 4.4.4",
|
"clap 4.4.4",
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
"core-services",
|
"core-services",
|
||||||
|
"exec",
|
||||||
|
"fork",
|
||||||
"ipc-channel",
|
"ipc-channel",
|
||||||
|
"libc",
|
||||||
|
"once_cell",
|
||||||
"plist",
|
"plist",
|
||||||
"release_channel",
|
"release_channel",
|
||||||
"serde",
|
"serde",
|
||||||
@ -3575,6 +3579,17 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno"
|
||||||
|
version = "0.2.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
|
||||||
|
dependencies = [
|
||||||
|
"errno-dragonfly",
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "errno"
|
name = "errno"
|
||||||
version = "0.3.8"
|
version = "0.3.8"
|
||||||
@ -3585,6 +3600,16 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno-dragonfly"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "etagere"
|
name = "etagere"
|
||||||
version = "0.2.8"
|
version = "0.2.8"
|
||||||
@ -3663,6 +3688,16 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "exec"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "886b70328cba8871bfc025858e1de4be16b1d5088f2ba50b57816f4210672615"
|
||||||
|
dependencies = [
|
||||||
|
"errno 0.2.8",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "extension"
|
name = "extension"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -4063,6 +4098,15 @@ version = "0.3.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
|
checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fork"
|
||||||
|
version = "0.1.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "60e74d3423998a57e9d906e49252fb79eb4a04d5cdfe188fb1b7ff9fc076a8ed"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "form_urlencoded"
|
name = "form_urlencoded"
|
||||||
version = "1.2.1"
|
version = "1.2.1"
|
||||||
@ -4788,7 +4832,6 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"client",
|
"client",
|
||||||
"ctrlc",
|
|
||||||
"fs",
|
"fs",
|
||||||
"futures 0.3.28",
|
"futures 0.3.28",
|
||||||
"gpui",
|
"gpui",
|
||||||
@ -4800,6 +4843,7 @@ dependencies = [
|
|||||||
"rpc",
|
"rpc",
|
||||||
"settings",
|
"settings",
|
||||||
"shellexpand",
|
"shellexpand",
|
||||||
|
"signal-hook",
|
||||||
"util",
|
"util",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -8400,7 +8444,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06"
|
checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"errno",
|
"errno 0.3.8",
|
||||||
"io-lifetimes 1.0.11",
|
"io-lifetimes 1.0.11",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys 0.3.8",
|
"linux-raw-sys 0.3.8",
|
||||||
@ -8414,7 +8458,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89"
|
checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.4.2",
|
"bitflags 2.4.2",
|
||||||
"errno",
|
"errno 0.3.8",
|
||||||
"itoa",
|
"itoa",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys 0.4.12",
|
"linux-raw-sys 0.4.12",
|
||||||
@ -8428,7 +8472,7 @@ version = "0.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a25c3aad9fc1424eb82c88087789a7d938e1829724f3e4043163baf0d13cfc12"
|
checksum = "a25c3aad9fc1424eb82c88087789a7d938e1829724f3e4043163baf0d13cfc12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"errno",
|
"errno 0.3.8",
|
||||||
"libc",
|
"libc",
|
||||||
"rustix 0.38.32",
|
"rustix 0.38.32",
|
||||||
]
|
]
|
||||||
@ -12832,7 +12876,6 @@ dependencies = [
|
|||||||
"clap 4.4.4",
|
"clap 4.4.4",
|
||||||
"cli",
|
"cli",
|
||||||
"client",
|
"client",
|
||||||
"clock",
|
|
||||||
"collab_ui",
|
"collab_ui",
|
||||||
"collections",
|
"collections",
|
||||||
"command_palette",
|
"command_palette",
|
||||||
@ -12863,6 +12906,7 @@ dependencies = [
|
|||||||
"language_selector",
|
"language_selector",
|
||||||
"language_tools",
|
"language_tools",
|
||||||
"languages",
|
"languages",
|
||||||
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"markdown_preview",
|
"markdown_preview",
|
||||||
"menu",
|
"menu",
|
||||||
|
@ -266,12 +266,14 @@ chrono = { version = "0.4", features = ["serde"] }
|
|||||||
clap = { version = "4.4", features = ["derive"] }
|
clap = { version = "4.4", features = ["derive"] }
|
||||||
clickhouse = { version = "0.11.6" }
|
clickhouse = { version = "0.11.6" }
|
||||||
ctor = "0.2.6"
|
ctor = "0.2.6"
|
||||||
ctrlc = "3.4.4"
|
signal-hook = "0.3.17"
|
||||||
core-foundation = { version = "0.9.3" }
|
core-foundation = { version = "0.9.3" }
|
||||||
core-foundation-sys = "0.8.6"
|
core-foundation-sys = "0.8.6"
|
||||||
derive_more = "0.99.17"
|
derive_more = "0.99.17"
|
||||||
emojis = "0.6.1"
|
emojis = "0.6.1"
|
||||||
env_logger = "0.9"
|
env_logger = "0.9"
|
||||||
|
exec = "0.3.1"
|
||||||
|
fork = "0.1.23"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
futures-batch = "0.6.1"
|
futures-batch = "0.6.1"
|
||||||
futures-lite = "1.13"
|
futures-lite = "1.13"
|
||||||
@ -290,10 +292,12 @@ isahc = { version = "1.7.2", default-features = false, features = [
|
|||||||
] }
|
] }
|
||||||
itertools = "0.11.0"
|
itertools = "0.11.0"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
libc = "0.2"
|
||||||
linkify = "0.10.0"
|
linkify = "0.10.0"
|
||||||
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
|
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
|
||||||
nanoid = "0.4"
|
nanoid = "0.4"
|
||||||
nix = "0.28"
|
nix = "0.28"
|
||||||
|
once_cell = "1.19.0"
|
||||||
ordered-float = "2.1.1"
|
ordered-float = "2.1.1"
|
||||||
palette = { version = "0.7.5", default-features = false, features = ["std"] }
|
palette = { version = "0.7.5", default-features = false, features = ["std"] }
|
||||||
parking_lot = "0.12.1"
|
parking_lot = "0.12.1"
|
||||||
|
@ -19,11 +19,17 @@ path = "src/main.rs"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
clap.workspace = true
|
clap.workspace = true
|
||||||
|
libc.workspace = true
|
||||||
ipc-channel = "0.18"
|
ipc-channel = "0.18"
|
||||||
|
once_cell.workspace = true
|
||||||
release_channel.workspace = true
|
release_channel.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
util.workspace = true
|
util.workspace = true
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
|
exec.workspace = true
|
||||||
|
fork.workspace = true
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
core-foundation.workspace = true
|
core-foundation.workspace = true
|
||||||
core-services = "0.2"
|
core-services = "0.2"
|
||||||
|
@ -13,6 +13,7 @@ pub enum CliRequest {
|
|||||||
paths: Vec<String>,
|
paths: Vec<String>,
|
||||||
wait: bool,
|
wait: bool,
|
||||||
open_new_workspace: Option<bool>,
|
open_new_workspace: Option<bool>,
|
||||||
|
dev_server_token: Option<String>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,17 +1,21 @@
|
|||||||
#![cfg_attr(any(target_os = "linux", target_os = "windows"), allow(dead_code))]
|
#![cfg_attr(any(target_os = "linux", target_os = "windows"), allow(dead_code))]
|
||||||
|
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use cli::{CliRequest, CliResponse};
|
use cli::{ipc::IpcOneShotServer, CliRequest, CliResponse, IpcHandshake};
|
||||||
use serde::Deserialize;
|
|
||||||
use std::{
|
use std::{
|
||||||
env,
|
env, fs,
|
||||||
ffi::OsStr,
|
|
||||||
fs,
|
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
use util::paths::PathLikeWithPosition;
|
use util::paths::PathLikeWithPosition;
|
||||||
|
|
||||||
|
struct Detect;
|
||||||
|
|
||||||
|
trait InstalledApp {
|
||||||
|
fn zed_version_string(&self) -> String;
|
||||||
|
fn launch(&self, ipc_url: String) -> anyhow::Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(name = "zed", disable_version_flag = true)]
|
#[command(name = "zed", disable_version_flag = true)]
|
||||||
struct Args {
|
struct Args {
|
||||||
@ -33,9 +37,9 @@ struct Args {
|
|||||||
/// Print Zed's version and the app path.
|
/// Print Zed's version and the app path.
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
version: bool,
|
version: bool,
|
||||||
/// Custom Zed.app path
|
/// Custom path to Zed.app or the zed binary
|
||||||
#[arg(short, long)]
|
#[arg(long)]
|
||||||
bundle_path: Option<PathBuf>,
|
zed: Option<PathBuf>,
|
||||||
/// Run zed in dev-server mode
|
/// Run zed in dev-server mode
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
dev_server_token: Option<String>,
|
dev_server_token: Option<String>,
|
||||||
@ -49,12 +53,6 @@ fn parse_path_with_position(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
struct InfoPlist {
|
|
||||||
#[serde(rename = "CFBundleShortVersionString")]
|
|
||||||
bundle_short_version_string: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
// Intercept version designators
|
// Intercept version designators
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
@ -68,14 +66,10 @@ fn main() -> Result<()> {
|
|||||||
}
|
}
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
|
|
||||||
let bundle = Bundle::detect(args.bundle_path.as_deref()).context("Bundle detection")?;
|
let app = Detect::detect(args.zed.as_deref()).context("Bundle detection")?;
|
||||||
|
|
||||||
if let Some(dev_server_token) = args.dev_server_token {
|
|
||||||
return bundle.spawn(vec!["--dev-server-token".into(), dev_server_token]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.version {
|
if args.version {
|
||||||
println!("{}", bundle.zed_version_string());
|
println!("{}", app.zed_version_string());
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +95,14 @@ fn main() -> Result<()> {
|
|||||||
paths.push(canonicalized.to_string(|path| path.display().to_string()))
|
paths.push(canonicalized.to_string(|path| path.display().to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
let (tx, rx) = bundle.launch()?;
|
let (server, server_name) =
|
||||||
|
IpcOneShotServer::<IpcHandshake>::new().context("Handshake before Zed spawn")?;
|
||||||
|
let url = format!("zed-cli://{server_name}");
|
||||||
|
|
||||||
|
app.launch(url)?;
|
||||||
|
let (_, handshake) = server.accept().context("Handshake after Zed spawn")?;
|
||||||
|
let (tx, rx) = (handshake.requests, handshake.responses);
|
||||||
|
|
||||||
let open_new_workspace = if args.new {
|
let open_new_workspace = if args.new {
|
||||||
Some(true)
|
Some(true)
|
||||||
} else if args.add {
|
} else if args.add {
|
||||||
@ -114,6 +115,7 @@ fn main() -> Result<()> {
|
|||||||
paths,
|
paths,
|
||||||
wait: args.wait,
|
wait: args.wait,
|
||||||
open_new_workspace,
|
open_new_workspace,
|
||||||
|
dev_server_token: args.dev_server_token,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
while let Ok(response) = rx.recv() {
|
while let Ok(response) = rx.recv() {
|
||||||
@ -128,7 +130,181 @@ fn main() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Bundle {
|
#[cfg(target_os = "linux")]
|
||||||
|
mod linux {
|
||||||
|
use std::{
|
||||||
|
env,
|
||||||
|
ffi::OsString,
|
||||||
|
io,
|
||||||
|
os::{
|
||||||
|
linux::net::SocketAddrExt,
|
||||||
|
unix::net::{SocketAddr, UnixDatagram},
|
||||||
|
},
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
process, thread,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use anyhow::anyhow;
|
||||||
|
use cli::FORCE_CLI_MODE_ENV_VAR_NAME;
|
||||||
|
use fork::Fork;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
use crate::{Detect, InstalledApp};
|
||||||
|
|
||||||
|
static RELEASE_CHANNEL: Lazy<String> =
|
||||||
|
Lazy::new(|| include_str!("../../zed/RELEASE_CHANNEL").trim().to_string());
|
||||||
|
|
||||||
|
struct App(PathBuf);
|
||||||
|
|
||||||
|
impl Detect {
|
||||||
|
pub fn detect(path: Option<&Path>) -> anyhow::Result<impl InstalledApp> {
|
||||||
|
let path = if let Some(path) = path {
|
||||||
|
path.to_path_buf().canonicalize()
|
||||||
|
} else {
|
||||||
|
let cli = env::current_exe()?;
|
||||||
|
let dir = cli
|
||||||
|
.parent()
|
||||||
|
.ok_or_else(|| anyhow!("no parent path for cli"))?;
|
||||||
|
|
||||||
|
match dir.join("zed").canonicalize() {
|
||||||
|
Ok(path) => Ok(path),
|
||||||
|
// development builds have Zed capitalized
|
||||||
|
Err(e) => match dir.join("Zed").canonicalize() {
|
||||||
|
Ok(path) => Ok(path),
|
||||||
|
Err(_) => Err(e),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}?;
|
||||||
|
|
||||||
|
Ok(App(path))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InstalledApp for App {
|
||||||
|
fn zed_version_string(&self) -> String {
|
||||||
|
format!(
|
||||||
|
"Zed {}{} – {}",
|
||||||
|
if *RELEASE_CHANNEL == "stable" {
|
||||||
|
"".to_string()
|
||||||
|
} else {
|
||||||
|
format!(" {} ", *RELEASE_CHANNEL)
|
||||||
|
},
|
||||||
|
option_env!("RELEASE_VERSION").unwrap_or_default(),
|
||||||
|
self.0.display(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn launch(&self, ipc_url: String) -> anyhow::Result<()> {
|
||||||
|
let uid: u32 = unsafe { libc::getuid() };
|
||||||
|
let sock_addr =
|
||||||
|
SocketAddr::from_abstract_name(format!("zed-{}-{}", *RELEASE_CHANNEL, uid))?;
|
||||||
|
|
||||||
|
let sock = UnixDatagram::unbound()?;
|
||||||
|
if sock.connect_addr(&sock_addr).is_err() {
|
||||||
|
self.boot_background(ipc_url)?;
|
||||||
|
} else {
|
||||||
|
sock.send(ipc_url.as_bytes())?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl App {
|
||||||
|
fn boot_background(&self, ipc_url: String) -> anyhow::Result<()> {
|
||||||
|
let path = &self.0;
|
||||||
|
|
||||||
|
match fork::fork() {
|
||||||
|
Ok(Fork::Parent(_)) => Ok(()),
|
||||||
|
Ok(Fork::Child) => {
|
||||||
|
std::env::set_var(FORCE_CLI_MODE_ENV_VAR_NAME, "");
|
||||||
|
if let Err(_) = fork::setsid() {
|
||||||
|
eprintln!("failed to setsid: {}", std::io::Error::last_os_error());
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
if std::env::var("ZED_KEEP_FD").is_err() {
|
||||||
|
if let Err(_) = fork::close_fd() {
|
||||||
|
eprintln!("failed to close_fd: {}", std::io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let error =
|
||||||
|
exec::execvp(path.clone(), &[path.as_os_str(), &OsString::from(ipc_url)]);
|
||||||
|
// if exec succeeded, we never get here.
|
||||||
|
eprintln!("failed to exec {:?}: {}", path, error);
|
||||||
|
process::exit(1)
|
||||||
|
}
|
||||||
|
Err(_) => Err(anyhow!(io::Error::last_os_error())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wait_for_socket(
|
||||||
|
&self,
|
||||||
|
sock_addr: &SocketAddr,
|
||||||
|
sock: &mut UnixDatagram,
|
||||||
|
) -> Result<(), std::io::Error> {
|
||||||
|
for _ in 0..100 {
|
||||||
|
thread::sleep(Duration::from_millis(10));
|
||||||
|
if sock.connect_addr(&sock_addr).is_ok() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sock.connect_addr(&sock_addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo("windows")
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
mod windows {
|
||||||
|
use crate::{Detect, InstalledApp};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
struct App;
|
||||||
|
impl InstalledApp for App {
|
||||||
|
fn zed_version_string(&self) -> String {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
fn launch(&self, _ipc_url: String) -> anyhow::Result<()> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Detect {
|
||||||
|
pub fn detect(_path: Option<&Path>) -> anyhow::Result<impl InstalledApp> {
|
||||||
|
Ok(App)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
mod mac_os {
|
||||||
|
use anyhow::{anyhow, Context, Result};
|
||||||
|
use core_foundation::{
|
||||||
|
array::{CFArray, CFIndex},
|
||||||
|
string::kCFStringEncodingUTF8,
|
||||||
|
url::{CFURLCreateWithBytes, CFURL},
|
||||||
|
};
|
||||||
|
use core_services::{kLSLaunchDefaults, LSLaunchURLSpec, LSOpenFromURLSpec, TCFType};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::{
|
||||||
|
ffi::OsStr,
|
||||||
|
fs,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
process::Command,
|
||||||
|
ptr,
|
||||||
|
};
|
||||||
|
|
||||||
|
use cli::FORCE_CLI_MODE_ENV_VAR_NAME;
|
||||||
|
|
||||||
|
use crate::{Detect, InstalledApp};
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
struct InfoPlist {
|
||||||
|
#[serde(rename = "CFBundleShortVersionString")]
|
||||||
|
bundle_short_version_string: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Bundle {
|
||||||
App {
|
App {
|
||||||
app_bundle: PathBuf,
|
app_bundle: PathBuf,
|
||||||
plist: InfoPlist,
|
plist: InfoPlist,
|
||||||
@ -137,9 +313,9 @@ enum Bundle {
|
|||||||
executable: PathBuf,
|
executable: PathBuf,
|
||||||
plist: InfoPlist,
|
plist: InfoPlist,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
fn locate_bundle() -> Result<PathBuf> {
|
fn locate_bundle() -> Result<PathBuf> {
|
||||||
let cli_path = std::env::current_exe()?.canonicalize()?;
|
let cli_path = std::env::current_exe()?.canonicalize()?;
|
||||||
let mut app_path = cli_path.clone();
|
let mut app_path = cli_path.clone();
|
||||||
while app_path.extension() != Some(OsStr::new("app")) {
|
while app_path.extension() != Some(OsStr::new("app")) {
|
||||||
@ -148,100 +324,11 @@ fn locate_bundle() -> Result<PathBuf> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(app_path)
|
Ok(app_path)
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
mod linux {
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use cli::{CliRequest, CliResponse};
|
|
||||||
use ipc_channel::ipc::{IpcReceiver, IpcSender};
|
|
||||||
|
|
||||||
use crate::{Bundle, InfoPlist};
|
|
||||||
|
|
||||||
impl Bundle {
|
|
||||||
pub fn detect(_args_bundle_path: Option<&Path>) -> anyhow::Result<Self> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn plist(&self) -> &InfoPlist {
|
impl Detect {
|
||||||
unimplemented!()
|
pub fn detect(path: Option<&Path>) -> anyhow::Result<impl InstalledApp> {
|
||||||
}
|
let bundle_path = if let Some(bundle_path) = path {
|
||||||
|
|
||||||
pub fn path(&self) -> &Path {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn launch(&self) -> anyhow::Result<(IpcSender<CliRequest>, IpcReceiver<CliResponse>)> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn spawn(&self, _args: Vec<String>) -> anyhow::Result<()> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn zed_version_string(&self) -> String {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo("windows")
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
mod windows {
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use cli::{CliRequest, CliResponse};
|
|
||||||
use ipc_channel::ipc::{IpcReceiver, IpcSender};
|
|
||||||
|
|
||||||
use crate::{Bundle, InfoPlist};
|
|
||||||
|
|
||||||
impl Bundle {
|
|
||||||
pub fn detect(_args_bundle_path: Option<&Path>) -> anyhow::Result<Self> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn plist(&self) -> &InfoPlist {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn path(&self) -> &Path {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn launch(&self) -> anyhow::Result<(IpcSender<CliRequest>, IpcReceiver<CliResponse>)> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn spawn(&self, _args: Vec<String>) -> anyhow::Result<()> {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn zed_version_string(&self) -> String {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
mod mac_os {
|
|
||||||
use anyhow::{Context, Result};
|
|
||||||
use core_foundation::{
|
|
||||||
array::{CFArray, CFIndex},
|
|
||||||
string::kCFStringEncodingUTF8,
|
|
||||||
url::{CFURLCreateWithBytes, CFURL},
|
|
||||||
};
|
|
||||||
use core_services::{kLSLaunchDefaults, LSLaunchURLSpec, LSOpenFromURLSpec, TCFType};
|
|
||||||
use std::{fs, path::Path, process::Command, ptr};
|
|
||||||
|
|
||||||
use cli::{CliRequest, CliResponse, IpcHandshake, FORCE_CLI_MODE_ENV_VAR_NAME};
|
|
||||||
use ipc_channel::ipc::{IpcOneShotServer, IpcReceiver, IpcSender};
|
|
||||||
|
|
||||||
use crate::{locate_bundle, Bundle, InfoPlist};
|
|
||||||
|
|
||||||
impl Bundle {
|
|
||||||
pub fn detect(args_bundle_path: Option<&Path>) -> anyhow::Result<Self> {
|
|
||||||
let bundle_path = if let Some(bundle_path) = args_bundle_path {
|
|
||||||
bundle_path
|
bundle_path
|
||||||
.canonicalize()
|
.canonicalize()
|
||||||
.with_context(|| format!("Args bundle path {bundle_path:?} canonicalization"))?
|
.with_context(|| format!("Args bundle path {bundle_path:?} canonicalization"))?
|
||||||
@ -256,7 +343,7 @@ mod mac_os {
|
|||||||
plist::from_file::<_, InfoPlist>(&plist_path).with_context(|| {
|
plist::from_file::<_, InfoPlist>(&plist_path).with_context(|| {
|
||||||
format!("Reading *.app bundle plist file at {plist_path:?}")
|
format!("Reading *.app bundle plist file at {plist_path:?}")
|
||||||
})?;
|
})?;
|
||||||
Ok(Self::App {
|
Ok(Bundle::App {
|
||||||
app_bundle: bundle_path,
|
app_bundle: bundle_path,
|
||||||
plist,
|
plist,
|
||||||
})
|
})
|
||||||
@ -271,42 +358,27 @@ mod mac_os {
|
|||||||
plist::from_file::<_, InfoPlist>(&plist_path).with_context(|| {
|
plist::from_file::<_, InfoPlist>(&plist_path).with_context(|| {
|
||||||
format!("Reading dev bundle plist file at {plist_path:?}")
|
format!("Reading dev bundle plist file at {plist_path:?}")
|
||||||
})?;
|
})?;
|
||||||
Ok(Self::LocalPath {
|
Ok(Bundle::LocalPath {
|
||||||
executable: bundle_path,
|
executable: bundle_path,
|
||||||
plist,
|
plist,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn plist(&self) -> &InfoPlist {
|
|
||||||
match self {
|
|
||||||
Self::App { plist, .. } => plist,
|
|
||||||
Self::LocalPath { plist, .. } => plist,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path(&self) -> &Path {
|
impl InstalledApp for Bundle {
|
||||||
match self {
|
fn zed_version_string(&self) -> String {
|
||||||
Self::App { app_bundle, .. } => app_bundle,
|
let is_dev = matches!(self, Self::LocalPath { .. });
|
||||||
Self::LocalPath { executable, .. } => executable,
|
format!(
|
||||||
}
|
"Zed {}{} – {}",
|
||||||
|
self.plist().bundle_short_version_string,
|
||||||
|
if is_dev { " (dev)" } else { "" },
|
||||||
|
self.path().display(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spawn(&self, args: Vec<String>) -> Result<()> {
|
fn launch(&self, url: String) -> anyhow::Result<()> {
|
||||||
let path = match self {
|
|
||||||
Self::App { app_bundle, .. } => app_bundle.join("Contents/MacOS/zed"),
|
|
||||||
Self::LocalPath { executable, .. } => executable.clone(),
|
|
||||||
};
|
|
||||||
Command::new(path).args(args).status()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn launch(&self) -> anyhow::Result<(IpcSender<CliRequest>, IpcReceiver<CliResponse>)> {
|
|
||||||
let (server, server_name) =
|
|
||||||
IpcOneShotServer::<IpcHandshake>::new().context("Handshake before Zed spawn")?;
|
|
||||||
let url = format!("zed-cli://{server_name}");
|
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Self::App { app_bundle, .. } => {
|
Self::App { app_bundle, .. } => {
|
||||||
let app_path = app_bundle;
|
let app_path = app_bundle;
|
||||||
@ -368,18 +440,23 @@ mod mac_os {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (_, handshake) = server.accept().context("Handshake after Zed spawn")?;
|
Ok(())
|
||||||
Ok((handshake.requests, handshake.responses))
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn zed_version_string(&self) -> String {
|
impl Bundle {
|
||||||
let is_dev = matches!(self, Self::LocalPath { .. });
|
fn plist(&self) -> &InfoPlist {
|
||||||
format!(
|
match self {
|
||||||
"Zed {}{} – {}",
|
Self::App { plist, .. } => plist,
|
||||||
self.plist().bundle_short_version_string,
|
Self::LocalPath { plist, .. } => plist,
|
||||||
if is_dev { " (dev)" } else { "" },
|
}
|
||||||
self.path().display(),
|
}
|
||||||
)
|
|
||||||
|
fn path(&self) -> &Path {
|
||||||
|
match self {
|
||||||
|
Self::App { app_bundle, .. } => app_bundle,
|
||||||
|
Self::LocalPath { executable, .. } => executable,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ futures.workspace = true
|
|||||||
gpui.workspace = true
|
gpui.workspace = true
|
||||||
lazy_static.workspace = true
|
lazy_static.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
once_cell = "1.19.0"
|
once_cell.workspace = true
|
||||||
parking_lot.workspace = true
|
parking_lot.workspace = true
|
||||||
postage.workspace = true
|
postage.workspace = true
|
||||||
rand.workspace = true
|
rand.workspace = true
|
||||||
|
@ -428,8 +428,10 @@ impl TestServer {
|
|||||||
node_runtime: app_state.node_runtime.clone(),
|
node_runtime: app_state.node_runtime.clone(),
|
||||||
},
|
},
|
||||||
cx,
|
cx,
|
||||||
);
|
)
|
||||||
});
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
TestClient {
|
TestClient {
|
||||||
app_state,
|
app_state,
|
||||||
|
@ -164,7 +164,7 @@ pub struct ExtensionIndexLanguageEntry {
|
|||||||
actions!(zed, [ReloadExtensions]);
|
actions!(zed, [ReloadExtensions]);
|
||||||
|
|
||||||
pub fn init(
|
pub fn init(
|
||||||
fs: Arc<fs::RealFs>,
|
fs: Arc<dyn Fs>,
|
||||||
client: Arc<Client>,
|
client: Arc<Client>,
|
||||||
node_runtime: Arc<dyn NodeRuntime>,
|
node_runtime: Arc<dyn NodeRuntime>,
|
||||||
language_registry: Arc<LanguageRegistry>,
|
language_registry: Arc<LanguageRegistry>,
|
||||||
|
@ -29,7 +29,7 @@ git.workspace = true
|
|||||||
git2.workspace = true
|
git2.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
libc = "0.2"
|
libc.workspace = true
|
||||||
time.workspace = true
|
time.workspace = true
|
||||||
|
|
||||||
gpui = { workspace = true, optional = true }
|
gpui = { workspace = true, optional = true }
|
||||||
|
@ -15,7 +15,7 @@ doctest = false
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
client.workspace = true
|
client.workspace = true
|
||||||
ctrlc.workspace = true
|
signal-hook.workspace = true
|
||||||
gpui.workspace = true
|
gpui.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
rpc.workspace = true
|
rpc.workspace = true
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use anyhow::Result;
|
use anyhow::{anyhow, Result};
|
||||||
use client::DevServerProjectId;
|
use client::DevServerProjectId;
|
||||||
use client::{user::UserStore, Client, ClientSettings};
|
use client::{user::UserStore, Client, ClientSettings};
|
||||||
use fs::Fs;
|
use fs::Fs;
|
||||||
@ -36,7 +36,7 @@ struct GlobalDevServer(Model<DevServer>);
|
|||||||
|
|
||||||
impl Global for GlobalDevServer {}
|
impl Global for GlobalDevServer {}
|
||||||
|
|
||||||
pub fn init(client: Arc<Client>, app_state: AppState, cx: &mut AppContext) {
|
pub fn init(client: Arc<Client>, app_state: AppState, cx: &mut AppContext) -> Task<Result<()>> {
|
||||||
let dev_server = cx.new_model(|cx| DevServer::new(client.clone(), app_state, cx));
|
let dev_server = cx.new_model(|cx| DevServer::new(client.clone(), app_state, cx));
|
||||||
cx.set_global(GlobalDevServer(dev_server.clone()));
|
cx.set_global(GlobalDevServer(dev_server.clone()));
|
||||||
|
|
||||||
@ -49,42 +49,36 @@ pub fn init(client: Arc<Client>, app_state: AppState, cx: &mut AppContext) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set up a handler when the dev server is shut down by the user pressing Ctrl-C
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
{
|
||||||
|
use signal_hook::consts::{SIGINT, SIGTERM};
|
||||||
|
use signal_hook::iterator::Signals;
|
||||||
|
// Set up a handler when the dev server is shut down
|
||||||
|
// with ctrl-c or kill
|
||||||
let (tx, rx) = futures::channel::oneshot::channel();
|
let (tx, rx) = futures::channel::oneshot::channel();
|
||||||
set_ctrlc_handler(move || tx.send(()).log_err().unwrap()).log_err();
|
let mut signals = Signals::new(&[SIGTERM, SIGINT]).unwrap();
|
||||||
|
std::thread::spawn({
|
||||||
|
move || {
|
||||||
|
if let Some(sig) = signals.forever().next() {
|
||||||
|
tx.send(sig).log_err();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
cx.spawn(|cx| async move {
|
cx.spawn(|cx| async move {
|
||||||
rx.await.log_err();
|
if let Ok(sig) = rx.await {
|
||||||
log::info!("Received interrupt signal");
|
log::info!("received signal {sig:?}");
|
||||||
cx.update(|cx| cx.quit()).log_err();
|
cx.update(|cx| cx.quit()).log_err();
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
}
|
||||||
|
|
||||||
let server_url = ClientSettings::get_global(&cx).server_url.clone();
|
let server_url = ClientSettings::get_global(&cx).server_url.clone();
|
||||||
cx.spawn(|cx| async move {
|
cx.spawn(|cx| async move {
|
||||||
match client.authenticate_and_connect(false, &cx).await {
|
client
|
||||||
Ok(_) => {
|
.authenticate_and_connect(false, &cx)
|
||||||
log::info!("Connected to {}", server_url);
|
.await
|
||||||
}
|
.map_err(|e| anyhow!("Error connecting to '{}': {}", server_url, e))
|
||||||
Err(e) => {
|
|
||||||
log::error!("Error connecting to '{}': {}", server_url, e);
|
|
||||||
cx.update(|cx| cx.quit()).log_err();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_ctrlc_handler<F>(f: F) -> Result<(), ctrlc::Error>
|
|
||||||
where
|
|
||||||
F: FnOnce() + 'static + Send,
|
|
||||||
{
|
|
||||||
let f = std::sync::Mutex::new(Some(f));
|
|
||||||
ctrlc::set_handler(move || {
|
|
||||||
if let Ok(mut guard) = f.lock() {
|
|
||||||
let f = guard.take().expect("f can only be taken once");
|
|
||||||
f();
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,7 +180,7 @@ impl DevServer {
|
|||||||
|
|
||||||
let path_exists = fs.is_dir(path).await;
|
let path_exists = fs.is_dir(path).await;
|
||||||
if !path_exists {
|
if !path_exists {
|
||||||
return Err(anyhow::anyhow!(ErrorCode::DevServerProjectPathDoesNotExist))?;
|
return Err(anyhow!(ErrorCode::DevServerProjectPathDoesNotExist))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(proto::Ack {})
|
Ok(proto::Ack {})
|
||||||
|
@ -10,4 +10,4 @@ workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gpui.workspace = true
|
gpui.workspace = true
|
||||||
once_cell = "1.19.0"
|
once_cell.workspace = true
|
||||||
|
@ -7,7 +7,8 @@ use std::{env, str::FromStr};
|
|||||||
use gpui::{AppContext, Global, SemanticVersion};
|
use gpui::{AppContext, Global, SemanticVersion};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
static RELEASE_CHANNEL_NAME: Lazy<String> = if cfg!(debug_assertions) {
|
/// stable | dev | nightly | preview
|
||||||
|
pub static RELEASE_CHANNEL_NAME: Lazy<String> = if cfg!(debug_assertions) {
|
||||||
Lazy::new(|| {
|
Lazy::new(|| {
|
||||||
env::var("ZED_RELEASE_CHANNEL")
|
env::var("ZED_RELEASE_CHANNEL")
|
||||||
.unwrap_or_else(|_| include_str!("../../zed/RELEASE_CHANNEL").trim().to_string())
|
.unwrap_or_else(|_| include_str!("../../zed/RELEASE_CHANNEL").trim().to_string())
|
||||||
|
@ -20,7 +20,7 @@ collections.workspace = true
|
|||||||
dirs = "4.0.0"
|
dirs = "4.0.0"
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
gpui.workspace = true
|
gpui.workspace = true
|
||||||
libc = "0.2"
|
libc.workspace = true
|
||||||
task.workspace = true
|
task.workspace = true
|
||||||
schemars.workspace = true
|
schemars.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
|
@ -30,7 +30,6 @@ chrono.workspace = true
|
|||||||
clap.workspace = true
|
clap.workspace = true
|
||||||
cli.workspace = true
|
cli.workspace = true
|
||||||
client.workspace = true
|
client.workspace = true
|
||||||
clock.workspace = true
|
|
||||||
collab_ui.workspace = true
|
collab_ui.workspace = true
|
||||||
collections.workspace = true
|
collections.workspace = true
|
||||||
command_palette.workspace = true
|
command_palette.workspace = true
|
||||||
@ -60,11 +59,12 @@ language.workspace = true
|
|||||||
language_selector.workspace = true
|
language_selector.workspace = true
|
||||||
language_tools.workspace = true
|
language_tools.workspace = true
|
||||||
languages.workspace = true
|
languages.workspace = true
|
||||||
|
libc.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
markdown_preview.workspace = true
|
markdown_preview.workspace = true
|
||||||
menu.workspace = true
|
menu.workspace = true
|
||||||
mimalloc = { version = "0.1", optional = true }
|
mimalloc = { version = "0.1", optional = true }
|
||||||
nix = {workspace = true, features = ["pthread"] }
|
nix = {workspace = true, features = ["pthread", "signal"] }
|
||||||
node_runtime.workspace = true
|
node_runtime.workspace = true
|
||||||
notifications.workspace = true
|
notifications.workspace = true
|
||||||
outline.workspace = true
|
outline.workspace = true
|
||||||
|
@ -17,7 +17,7 @@ use env_logger::Builder;
|
|||||||
use fs::RealFs;
|
use fs::RealFs;
|
||||||
use futures::{future, StreamExt};
|
use futures::{future, StreamExt};
|
||||||
use git::GitHostingProviderRegistry;
|
use git::GitHostingProviderRegistry;
|
||||||
use gpui::{App, AppContext, AsyncAppContext, Context, Task, VisualContext};
|
use gpui::{App, AppContext, AsyncAppContext, Context, Global, Task, VisualContext};
|
||||||
use image_viewer;
|
use image_viewer;
|
||||||
use language::LanguageRegistry;
|
use language::LanguageRegistry;
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
@ -26,9 +26,7 @@ use assets::Assets;
|
|||||||
use node_runtime::RealNodeRuntime;
|
use node_runtime::RealNodeRuntime;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use release_channel::AppCommitSha;
|
use release_channel::AppCommitSha;
|
||||||
use settings::{
|
use settings::{handle_settings_file_changes, watch_config_file, Settings, SettingsStore};
|
||||||
default_settings, handle_settings_file_changes, watch_config_file, Settings, SettingsStore,
|
|
||||||
};
|
|
||||||
use simplelog::ConfigBuilder;
|
use simplelog::ConfigBuilder;
|
||||||
use smol::process::Command;
|
use smol::process::Command;
|
||||||
use std::{
|
use std::{
|
||||||
@ -36,11 +34,11 @@ use std::{
|
|||||||
fs::OpenOptions,
|
fs::OpenOptions,
|
||||||
io::{IsTerminal, Write},
|
io::{IsTerminal, Write},
|
||||||
path::Path,
|
path::Path,
|
||||||
|
process,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
use theme::{ActiveTheme, SystemAppearance, ThemeRegistry, ThemeSettings};
|
use theme::{ActiveTheme, SystemAppearance, ThemeRegistry, ThemeSettings};
|
||||||
use util::{
|
use util::{
|
||||||
http::HttpClientWithUrl,
|
|
||||||
maybe, parse_env_output,
|
maybe, parse_env_output,
|
||||||
paths::{self},
|
paths::{self},
|
||||||
ResultExt, TryFutureExt,
|
ResultExt, TryFutureExt,
|
||||||
@ -49,9 +47,8 @@ use uuid::Uuid;
|
|||||||
use welcome::{show_welcome_view, BaseKeymap, FIRST_OPEN};
|
use welcome::{show_welcome_view, BaseKeymap, FIRST_OPEN};
|
||||||
use workspace::{AppState, WorkspaceSettings, WorkspaceStore};
|
use workspace::{AppState, WorkspaceSettings, WorkspaceStore};
|
||||||
use zed::{
|
use zed::{
|
||||||
app_menus, build_window_options, ensure_only_instance, handle_cli_connection,
|
app_menus, build_window_options, handle_cli_connection, handle_keymap_file_changes,
|
||||||
handle_keymap_file_changes, initialize_workspace, open_paths_with_positions, IsOnlyInstance,
|
initialize_workspace, open_paths_with_positions, OpenListener, OpenRequest,
|
||||||
OpenListener, OpenRequest,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::zed::inline_completion_registry;
|
use crate::zed::inline_completion_registry;
|
||||||
@ -76,95 +73,169 @@ fn fail_to_launch(e: anyhow::Error) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_headless(dev_server_token: DevServerToken) {
|
enum AppMode {
|
||||||
if let Err(e) = init_paths() {
|
Headless(DevServerToken),
|
||||||
log::error!("Failed to launch: {}", e);
|
Ui,
|
||||||
return;
|
}
|
||||||
|
impl Global for AppMode {}
|
||||||
|
|
||||||
|
fn init_headless(
|
||||||
|
dev_server_token: DevServerToken,
|
||||||
|
app_state: Arc<AppState>,
|
||||||
|
cx: &mut AppContext,
|
||||||
|
) -> Task<Result<()>> {
|
||||||
|
match cx.try_global::<AppMode>() {
|
||||||
|
Some(AppMode::Headless(token)) if token == &dev_server_token => return Task::ready(Ok(())),
|
||||||
|
Some(_) => {
|
||||||
|
return Task::ready(Err(anyhow!(
|
||||||
|
"zed is already running. Use `kill {}` to stop it",
|
||||||
|
process::id()
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
init_logger();
|
None => {
|
||||||
|
cx.set_global(AppMode::Headless(dev_server_token.clone()));
|
||||||
let app = App::new();
|
|
||||||
|
|
||||||
let session_id = Uuid::new_v4().to_string();
|
|
||||||
let (installation_id, _) = app
|
|
||||||
.background_executor()
|
|
||||||
.block(installation_id())
|
|
||||||
.ok()
|
|
||||||
.unzip();
|
|
||||||
|
|
||||||
reliability::init_panic_hook(&app, installation_id.clone(), session_id.clone());
|
|
||||||
|
|
||||||
app.run(|cx| {
|
|
||||||
release_channel::init(env!("CARGO_PKG_VERSION"), cx);
|
|
||||||
if let Some(build_sha) = option_env!("ZED_COMMIT_SHA") {
|
|
||||||
AppCommitSha::set_global(AppCommitSha(build_sha.into()), cx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut store = SettingsStore::default();
|
|
||||||
store
|
|
||||||
.set_default_settings(default_settings().as_ref(), cx)
|
|
||||||
.unwrap();
|
|
||||||
cx.set_global(store);
|
|
||||||
|
|
||||||
client::init_settings(cx);
|
|
||||||
|
|
||||||
let clock = Arc::new(clock::RealSystemClock);
|
|
||||||
let http = Arc::new(HttpClientWithUrl::new(
|
|
||||||
&client::ClientSettings::get_global(cx).server_url,
|
|
||||||
));
|
|
||||||
|
|
||||||
let client = client::Client::new(clock, http.clone(), cx);
|
|
||||||
let client = client.clone();
|
|
||||||
client.set_dev_server_token(dev_server_token);
|
|
||||||
|
|
||||||
project::Project::init(&client, cx);
|
|
||||||
client::init(&client, cx);
|
|
||||||
|
|
||||||
let git_hosting_provider_registry = GitHostingProviderRegistry::default_global(cx);
|
|
||||||
let git_binary_path = if option_env!("ZED_BUNDLE").as_deref() == Some("true") {
|
|
||||||
cx.path_for_auxiliary_executable("git")
|
|
||||||
.context("could not find git binary path")
|
|
||||||
.log_err()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
};
|
||||||
let fs = Arc::new(RealFs::new(git_hosting_provider_registry, git_binary_path));
|
let client = app_state.client.clone();
|
||||||
|
client.set_dev_server_token(dev_server_token);
|
||||||
git_hosting_providers::init(cx);
|
|
||||||
|
|
||||||
let mut languages =
|
|
||||||
LanguageRegistry::new(Task::ready(()), cx.background_executor().clone());
|
|
||||||
languages.set_language_server_download_dir(paths::LANGUAGES_DIR.clone());
|
|
||||||
let languages = Arc::new(languages);
|
|
||||||
let node_runtime = RealNodeRuntime::new(http.clone());
|
|
||||||
|
|
||||||
language::init(cx);
|
|
||||||
languages::init(languages.clone(), node_runtime.clone(), cx);
|
|
||||||
let user_store = cx.new_model(|cx| UserStore::new(client.clone(), cx));
|
|
||||||
|
|
||||||
let user_settings_file_rx = watch_config_file(
|
|
||||||
&cx.background_executor(),
|
|
||||||
fs.clone(),
|
|
||||||
paths::SETTINGS.clone(),
|
|
||||||
);
|
|
||||||
handle_settings_file_changes(user_settings_file_rx, cx);
|
|
||||||
|
|
||||||
reliability::init(client.http_client(), installation_id, cx);
|
|
||||||
|
|
||||||
headless::init(
|
headless::init(
|
||||||
client.clone(),
|
client.clone(),
|
||||||
headless::AppState {
|
headless::AppState {
|
||||||
languages: languages.clone(),
|
languages: app_state.languages.clone(),
|
||||||
user_store: user_store.clone(),
|
user_store: app_state.user_store.clone(),
|
||||||
fs: fs.clone(),
|
fs: app_state.fs.clone(),
|
||||||
node_runtime: node_runtime.clone(),
|
node_runtime: app_state.node_runtime.clone(),
|
||||||
},
|
},
|
||||||
cx,
|
cx,
|
||||||
);
|
)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_ui(args: Args) {
|
fn init_ui(app_state: Arc<AppState>, cx: &mut AppContext) -> Result<()> {
|
||||||
|
match cx.try_global::<AppMode>() {
|
||||||
|
Some(AppMode::Headless(_)) => {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"zed is already running in headless mode. Use `kill {}` to stop it",
|
||||||
|
process::id()
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Some(AppMode::Ui) => return Ok(()),
|
||||||
|
None => {
|
||||||
|
cx.set_global(AppMode::Ui);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SystemAppearance::init(cx);
|
||||||
|
load_embedded_fonts(cx);
|
||||||
|
|
||||||
|
theme::init(theme::LoadThemes::All(Box::new(Assets)), cx);
|
||||||
|
app_state.languages.set_theme(cx.theme().clone());
|
||||||
|
command_palette::init(cx);
|
||||||
|
editor::init(cx);
|
||||||
|
image_viewer::init(cx);
|
||||||
|
diagnostics::init(cx);
|
||||||
|
|
||||||
|
audio::init(Assets, cx);
|
||||||
|
workspace::init(app_state.clone(), cx);
|
||||||
|
recent_projects::init(cx);
|
||||||
|
|
||||||
|
go_to_line::init(cx);
|
||||||
|
file_finder::init(cx);
|
||||||
|
tab_switcher::init(cx);
|
||||||
|
outline::init(cx);
|
||||||
|
project_symbols::init(cx);
|
||||||
|
project_panel::init(Assets, cx);
|
||||||
|
tasks_ui::init(cx);
|
||||||
|
channel::init(&app_state.client.clone(), app_state.user_store.clone(), cx);
|
||||||
|
search::init(cx);
|
||||||
|
vim::init(cx);
|
||||||
|
terminal_view::init(cx);
|
||||||
|
|
||||||
|
journal::init(app_state.clone(), cx);
|
||||||
|
language_selector::init(cx);
|
||||||
|
theme_selector::init(cx);
|
||||||
|
language_tools::init(cx);
|
||||||
|
call::init(app_state.client.clone(), app_state.user_store.clone(), cx);
|
||||||
|
notifications::init(app_state.client.clone(), app_state.user_store.clone(), cx);
|
||||||
|
collab_ui::init(&app_state, cx);
|
||||||
|
feedback::init(cx);
|
||||||
|
markdown_preview::init(cx);
|
||||||
|
welcome::init(cx);
|
||||||
|
extensions_ui::init(cx);
|
||||||
|
|
||||||
|
// Initialize each completion provider. Settings are used for toggling between them.
|
||||||
|
let copilot_language_server_id = app_state.languages.next_language_server_id();
|
||||||
|
copilot::init(
|
||||||
|
copilot_language_server_id,
|
||||||
|
app_state.client.http_client(),
|
||||||
|
app_state.node_runtime.clone(),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
supermaven::init(app_state.client.clone(), cx);
|
||||||
|
|
||||||
|
inline_completion_registry::init(app_state.client.telemetry().clone(), cx);
|
||||||
|
|
||||||
|
assistant::init(app_state.client.clone(), cx);
|
||||||
|
assistant2::init(app_state.client.clone(), cx);
|
||||||
|
|
||||||
|
cx.observe_global::<SettingsStore>({
|
||||||
|
let languages = app_state.languages.clone();
|
||||||
|
let http = app_state.client.http_client();
|
||||||
|
let client = app_state.client.clone();
|
||||||
|
|
||||||
|
move |cx| {
|
||||||
|
for &mut window in cx.windows().iter_mut() {
|
||||||
|
let background_appearance = cx.theme().window_background_appearance();
|
||||||
|
window
|
||||||
|
.update(cx, |_, cx| {
|
||||||
|
cx.set_background_appearance(background_appearance)
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
languages.set_theme(cx.theme().clone());
|
||||||
|
let new_host = &client::ClientSettings::get_global(cx).server_url;
|
||||||
|
if &http.base_url() != new_host {
|
||||||
|
http.set_base_url(new_host);
|
||||||
|
if client.status().borrow().is_connected() {
|
||||||
|
client.reconnect(&cx.to_async());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
let telemetry = app_state.client.telemetry();
|
||||||
|
telemetry.report_setting_event("theme", cx.theme().name.to_string());
|
||||||
|
telemetry.report_setting_event("keymap", BaseKeymap::get_global(cx).to_string());
|
||||||
|
telemetry.flush_events();
|
||||||
|
|
||||||
|
extension::init(
|
||||||
|
app_state.fs.clone(),
|
||||||
|
app_state.client.clone(),
|
||||||
|
app_state.node_runtime.clone(),
|
||||||
|
app_state.languages.clone(),
|
||||||
|
ThemeRegistry::global(cx),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
|
||||||
|
dev_server_projects::init(app_state.client.clone(), cx);
|
||||||
|
|
||||||
|
let fs = app_state.fs.clone();
|
||||||
|
load_user_themes_in_background(fs.clone(), cx);
|
||||||
|
watch_themes(fs.clone(), cx);
|
||||||
|
watch_languages(fs.clone(), app_state.languages.clone(), cx);
|
||||||
|
watch_file_types(fs.clone(), cx);
|
||||||
|
|
||||||
|
cx.set_menus(app_menus());
|
||||||
|
initialize_workspace(app_state.clone(), cx);
|
||||||
|
|
||||||
|
cx.activate(true);
|
||||||
|
|
||||||
|
cx.spawn(|cx| async move { authenticate(app_state.client.clone(), &cx).await })
|
||||||
|
.detach_and_log_err(cx);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
menu::init();
|
menu::init();
|
||||||
zed_actions::init();
|
zed_actions::init();
|
||||||
|
|
||||||
@ -175,10 +246,6 @@ fn init_ui(args: Args) {
|
|||||||
|
|
||||||
init_logger();
|
init_logger();
|
||||||
|
|
||||||
if ensure_only_instance() != IsOnlyInstance::Yes {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
log::info!("========== starting zed ==========");
|
log::info!("========== starting zed ==========");
|
||||||
let app = App::new().with_assets(Assets);
|
let app = App::new().with_assets(Assets);
|
||||||
|
|
||||||
@ -190,6 +257,26 @@ fn init_ui(args: Args) {
|
|||||||
let session_id = Uuid::new_v4().to_string();
|
let session_id = Uuid::new_v4().to_string();
|
||||||
reliability::init_panic_hook(&app, installation_id.clone(), session_id.clone());
|
reliability::init_panic_hook(&app, installation_id.clone(), session_id.clone());
|
||||||
|
|
||||||
|
let (listener, mut open_rx) = OpenListener::new();
|
||||||
|
let listener = Arc::new(listener);
|
||||||
|
let open_listener = listener.clone();
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
{
|
||||||
|
if crate::zed::listen_for_cli_connections(listener.clone()).is_err() {
|
||||||
|
println!("zed is already running");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
{
|
||||||
|
use zed::only_instance::*;
|
||||||
|
if ensure_only_instance() != IsOnlyInstance::Yes {
|
||||||
|
println!("zed is already running");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let git_hosting_provider_registry = Arc::new(GitHostingProviderRegistry::new());
|
let git_hosting_provider_registry = Arc::new(GitHostingProviderRegistry::new());
|
||||||
let git_binary_path = if option_env!("ZED_BUNDLE").as_deref() == Some("true") {
|
let git_binary_path = if option_env!("ZED_BUNDLE").as_deref() == Some("true") {
|
||||||
app.path_for_auxiliary_executable("git")
|
app.path_for_auxiliary_executable("git")
|
||||||
@ -223,9 +310,6 @@ fn init_ui(args: Args) {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
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| open_listener.open_urls(urls));
|
||||||
app.on_reopen(move |cx| {
|
app.on_reopen(move |cx| {
|
||||||
if let Some(app_state) = AppState::try_global(cx).and_then(|app_state| app_state.upgrade())
|
if let Some(app_state) = AppState::try_global(cx).and_then(|app_state| app_state.upgrade())
|
||||||
@ -247,11 +331,8 @@ fn init_ui(args: Args) {
|
|||||||
GitHostingProviderRegistry::set_global(git_hosting_provider_registry, cx);
|
GitHostingProviderRegistry::set_global(git_hosting_provider_registry, cx);
|
||||||
git_hosting_providers::init(cx);
|
git_hosting_providers::init(cx);
|
||||||
|
|
||||||
SystemAppearance::init(cx);
|
|
||||||
OpenListener::set_global(listener.clone(), cx);
|
OpenListener::set_global(listener.clone(), cx);
|
||||||
|
|
||||||
load_embedded_fonts(cx);
|
|
||||||
|
|
||||||
settings::init(cx);
|
settings::init(cx);
|
||||||
handle_settings_file_changes(user_settings_file_rx, cx);
|
handle_settings_file_changes(user_settings_file_rx, cx);
|
||||||
handle_keymap_file_changes(user_keymap_file_rx, cx);
|
handle_keymap_file_changes(user_keymap_file_rx, cx);
|
||||||
@ -260,7 +341,6 @@ fn init_ui(args: Args) {
|
|||||||
let client = Client::production(cx);
|
let client = Client::production(cx);
|
||||||
let mut languages =
|
let mut languages =
|
||||||
LanguageRegistry::new(login_shell_env_loaded, cx.background_executor().clone());
|
LanguageRegistry::new(login_shell_env_loaded, cx.background_executor().clone());
|
||||||
let copilot_language_server_id = languages.next_language_server_id();
|
|
||||||
languages.set_language_server_download_dir(paths::LANGUAGES_DIR.clone());
|
languages.set_language_server_download_dir(paths::LANGUAGES_DIR.clone());
|
||||||
let languages = Arc::new(languages);
|
let languages = Arc::new(languages);
|
||||||
let node_runtime = RealNodeRuntime::new(client.http_client());
|
let node_runtime = RealNodeRuntime::new(client.http_client());
|
||||||
@ -273,76 +353,11 @@ fn init_ui(args: Args) {
|
|||||||
Client::set_global(client.clone(), cx);
|
Client::set_global(client.clone(), cx);
|
||||||
|
|
||||||
zed::init(cx);
|
zed::init(cx);
|
||||||
theme::init(theme::LoadThemes::All(Box::new(Assets)), cx);
|
|
||||||
project::Project::init(&client, cx);
|
project::Project::init(&client, cx);
|
||||||
client::init(&client, cx);
|
client::init(&client, cx);
|
||||||
command_palette::init(cx);
|
|
||||||
language::init(cx);
|
language::init(cx);
|
||||||
editor::init(cx);
|
|
||||||
image_viewer::init(cx);
|
|
||||||
diagnostics::init(cx);
|
|
||||||
|
|
||||||
// Initialize each completion provider. Settings are used for toggling between them.
|
|
||||||
copilot::init(
|
|
||||||
copilot_language_server_id,
|
|
||||||
client.http_client(),
|
|
||||||
node_runtime.clone(),
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
supermaven::init(client.clone(), cx);
|
|
||||||
|
|
||||||
assistant::init(client.clone(), cx);
|
|
||||||
assistant2::init(client.clone(), cx);
|
|
||||||
|
|
||||||
inline_completion_registry::init(client.telemetry().clone(), cx);
|
|
||||||
|
|
||||||
extension::init(
|
|
||||||
fs.clone(),
|
|
||||||
client.clone(),
|
|
||||||
node_runtime.clone(),
|
|
||||||
languages.clone(),
|
|
||||||
ThemeRegistry::global(cx),
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
dev_server_projects::init(client.clone(), cx);
|
|
||||||
|
|
||||||
load_user_themes_in_background(fs.clone(), cx);
|
|
||||||
watch_themes(fs.clone(), cx);
|
|
||||||
watch_languages(fs.clone(), languages.clone(), cx);
|
|
||||||
watch_file_types(fs.clone(), cx);
|
|
||||||
|
|
||||||
languages.set_theme(cx.theme().clone());
|
|
||||||
|
|
||||||
cx.observe_global::<SettingsStore>({
|
|
||||||
let languages = languages.clone();
|
|
||||||
let http = client.http_client();
|
|
||||||
let client = client.clone();
|
|
||||||
|
|
||||||
move |cx| {
|
|
||||||
for &mut window in cx.windows().iter_mut() {
|
|
||||||
let background_appearance = cx.theme().window_background_appearance();
|
|
||||||
window
|
|
||||||
.update(cx, |_, cx| {
|
|
||||||
cx.set_background_appearance(background_appearance)
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
languages.set_theme(cx.theme().clone());
|
|
||||||
let new_host = &client::ClientSettings::get_global(cx).server_url;
|
|
||||||
if &http.base_url() != new_host {
|
|
||||||
http.set_base_url(new_host);
|
|
||||||
if client.status().borrow().is_connected() {
|
|
||||||
client.reconnect(&cx.to_async());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
|
|
||||||
let telemetry = client.telemetry();
|
let telemetry = client.telemetry();
|
||||||
telemetry.start(installation_id.clone(), session_id, cx);
|
telemetry.start(installation_id.clone(), session_id, cx);
|
||||||
telemetry.report_setting_event("theme", cx.theme().name.to_string());
|
|
||||||
telemetry.report_setting_event("keymap", BaseKeymap::get_global(cx).to_string());
|
|
||||||
telemetry.report_app_event(
|
telemetry.report_app_event(
|
||||||
match existing_installation_id_found {
|
match existing_installation_id_found {
|
||||||
Some(false) => "first open",
|
Some(false) => "first open",
|
||||||
@ -350,7 +365,6 @@ fn init_ui(args: Args) {
|
|||||||
}
|
}
|
||||||
.to_string(),
|
.to_string(),
|
||||||
);
|
);
|
||||||
telemetry.flush_events();
|
|
||||||
let app_state = Arc::new(AppState {
|
let app_state = Arc::new(AppState {
|
||||||
languages: languages.clone(),
|
languages: languages.clone(),
|
||||||
client: client.clone(),
|
client: client.clone(),
|
||||||
@ -362,44 +376,11 @@ fn init_ui(args: Args) {
|
|||||||
});
|
});
|
||||||
AppState::set_global(Arc::downgrade(&app_state), cx);
|
AppState::set_global(Arc::downgrade(&app_state), cx);
|
||||||
|
|
||||||
audio::init(Assets, cx);
|
|
||||||
auto_update::init(client.http_client(), cx);
|
auto_update::init(client.http_client(), cx);
|
||||||
|
|
||||||
workspace::init(app_state.clone(), cx);
|
|
||||||
recent_projects::init(cx);
|
|
||||||
|
|
||||||
go_to_line::init(cx);
|
|
||||||
file_finder::init(cx);
|
|
||||||
tab_switcher::init(cx);
|
|
||||||
outline::init(cx);
|
|
||||||
project_symbols::init(cx);
|
|
||||||
project_panel::init(Assets, cx);
|
|
||||||
tasks_ui::init(cx);
|
|
||||||
channel::init(&client, user_store.clone(), cx);
|
|
||||||
search::init(cx);
|
|
||||||
vim::init(cx);
|
|
||||||
terminal_view::init(cx);
|
|
||||||
|
|
||||||
journal::init(app_state.clone(), cx);
|
|
||||||
language_selector::init(cx);
|
|
||||||
theme_selector::init(cx);
|
|
||||||
language_tools::init(cx);
|
|
||||||
call::init(app_state.client.clone(), app_state.user_store.clone(), cx);
|
|
||||||
notifications::init(app_state.client.clone(), app_state.user_store.clone(), cx);
|
|
||||||
collab_ui::init(&app_state, cx);
|
|
||||||
feedback::init(cx);
|
|
||||||
markdown_preview::init(cx);
|
|
||||||
welcome::init(cx);
|
|
||||||
extensions_ui::init(cx);
|
|
||||||
|
|
||||||
cx.set_menus(app_menus());
|
|
||||||
initialize_workspace(app_state.clone(), cx);
|
|
||||||
|
|
||||||
reliability::init(client.http_client(), installation_id, cx);
|
reliability::init(client.http_client(), installation_id, cx);
|
||||||
|
|
||||||
cx.activate(true);
|
let args = Args::parse();
|
||||||
|
|
||||||
let mut triggered_authentication = false;
|
|
||||||
let urls: Vec<_> = args
|
let urls: Vec<_> = args
|
||||||
.paths_or_urls
|
.paths_or_urls
|
||||||
.iter()
|
.iter()
|
||||||
@ -417,14 +398,30 @@ fn init_ui(args: Args) {
|
|||||||
.and_then(|urls| OpenRequest::parse(urls, cx).log_err())
|
.and_then(|urls| OpenRequest::parse(urls, cx).log_err())
|
||||||
{
|
{
|
||||||
Some(request) => {
|
Some(request) => {
|
||||||
triggered_authentication = handle_open_request(request, app_state.clone(), cx)
|
handle_open_request(request, app_state.clone(), cx);
|
||||||
}
|
}
|
||||||
None => cx
|
None => {
|
||||||
.spawn({
|
if let Some(dev_server_token) = args.dev_server_token {
|
||||||
|
let task =
|
||||||
|
init_headless(DevServerToken(dev_server_token), app_state.clone(), cx);
|
||||||
|
cx.spawn(|cx| async move {
|
||||||
|
if let Err(e) = task.await {
|
||||||
|
log::error!("{}", e);
|
||||||
|
cx.update(|cx| cx.quit()).log_err();
|
||||||
|
} else {
|
||||||
|
log::info!("connected!");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
} else {
|
||||||
|
init_ui(app_state.clone(), cx).unwrap();
|
||||||
|
cx.spawn({
|
||||||
let app_state = app_state.clone();
|
let app_state = app_state.clone();
|
||||||
|cx| async move { restore_or_create_workspace(app_state, cx).await }
|
|cx| async move { restore_or_create_workspace(app_state, cx).await }
|
||||||
})
|
})
|
||||||
.detach(),
|
.detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let app_state = app_state.clone();
|
let app_state = app_state.clone();
|
||||||
@ -439,34 +436,20 @@ fn init_ui(args: Args) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
if !triggered_authentication {
|
|
||||||
cx.spawn(|cx| async move { authenticate(client, &cx).await })
|
|
||||||
.detach_and_log_err(cx);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn handle_open_request(request: OpenRequest, app_state: Arc<AppState>, cx: &mut AppContext) {
|
||||||
let mut args = Args::parse();
|
|
||||||
if let Some(dev_server_token) = args.dev_server_token.take() {
|
|
||||||
let dev_server_token = DevServerToken(dev_server_token);
|
|
||||||
init_headless(dev_server_token)
|
|
||||||
} else {
|
|
||||||
init_ui(args)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_open_request(
|
|
||||||
request: OpenRequest,
|
|
||||||
app_state: Arc<AppState>,
|
|
||||||
cx: &mut AppContext,
|
|
||||||
) -> bool {
|
|
||||||
if let Some(connection) = request.cli_connection {
|
if let Some(connection) = request.cli_connection {
|
||||||
let app_state = app_state.clone();
|
let app_state = app_state.clone();
|
||||||
cx.spawn(move |cx| handle_cli_connection(connection, app_state, cx))
|
cx.spawn(move |cx| handle_cli_connection(connection, app_state, cx))
|
||||||
.detach();
|
.detach();
|
||||||
return false;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = init_ui(app_state.clone(), cx) {
|
||||||
|
log::error!("{}", e);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut task = None;
|
let mut task = None;
|
||||||
@ -531,13 +514,9 @@ fn handle_open_request(
|
|||||||
anyhow::Ok(())
|
anyhow::Ok(())
|
||||||
})
|
})
|
||||||
.detach_and_log_err(cx);
|
.detach_and_log_err(cx);
|
||||||
true
|
} else if let Some(task) = task {
|
||||||
} else {
|
|
||||||
if let Some(task) = task {
|
|
||||||
task.detach_and_log_err(cx)
|
task.detach_and_log_err(cx)
|
||||||
}
|
}
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn authenticate(client: Arc<Client>, cx: &AsyncAppContext) -> Result<()> {
|
async fn authenticate(client: Arc<Client>, cx: &AsyncAppContext) -> Result<()> {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
mod app_menus;
|
mod app_menus;
|
||||||
pub mod inline_completion_registry;
|
pub mod inline_completion_registry;
|
||||||
mod only_instance;
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
pub(crate) mod only_instance;
|
||||||
mod open_listener;
|
mod open_listener;
|
||||||
|
|
||||||
pub use app_menus::*;
|
pub use app_menus::*;
|
||||||
@ -12,7 +13,6 @@ use gpui::{
|
|||||||
actions, point, px, AppContext, AsyncAppContext, Context, FocusableView, PromptLevel,
|
actions, point, px, AppContext, AsyncAppContext, Context, FocusableView, PromptLevel,
|
||||||
TitlebarOptions, View, ViewContext, VisualContext, WindowKind, WindowOptions,
|
TitlebarOptions, View, ViewContext, VisualContext, WindowKind, WindowOptions,
|
||||||
};
|
};
|
||||||
pub use only_instance::*;
|
|
||||||
pub use open_listener::*;
|
pub use open_listener::*;
|
||||||
|
|
||||||
use anyhow::Context as _;
|
use anyhow::Context as _;
|
||||||
|
@ -13,13 +13,15 @@ use language::{Bias, Point};
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::thread;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use std::{process, thread};
|
||||||
use util::paths::PathLikeWithPosition;
|
use util::paths::PathLikeWithPosition;
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use workspace::item::ItemHandle;
|
use workspace::item::ItemHandle;
|
||||||
use workspace::{AppState, Workspace};
|
use workspace::{AppState, Workspace};
|
||||||
|
|
||||||
|
use crate::{init_headless, init_ui};
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct OpenRequest {
|
pub struct OpenRequest {
|
||||||
pub cli_connection: Option<(mpsc::Receiver<CliRequest>, IpcSender<CliResponse>)>,
|
pub cli_connection: Option<(mpsc::Receiver<CliRequest>, IpcSender<CliResponse>)>,
|
||||||
@ -116,6 +118,24 @@ impl OpenListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub fn listen_for_cli_connections(opener: Arc<OpenListener>) -> Result<()> {
|
||||||
|
use release_channel::RELEASE_CHANNEL_NAME;
|
||||||
|
use std::os::{linux::net::SocketAddrExt, unix::net::SocketAddr, unix::net::UnixDatagram};
|
||||||
|
|
||||||
|
let uid: u32 = unsafe { libc::getuid() };
|
||||||
|
let sock_addr =
|
||||||
|
SocketAddr::from_abstract_name(format!("zed-{}-{}", *RELEASE_CHANNEL_NAME, uid))?;
|
||||||
|
let listener = UnixDatagram::bind_addr(&sock_addr)?;
|
||||||
|
thread::spawn(move || {
|
||||||
|
let mut buf = [0u8; 1024];
|
||||||
|
while let Ok(len) = listener.recv(&mut buf) {
|
||||||
|
opener.open_urls(vec![String::from_utf8_lossy(&buf[..len]).to_string()]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn connect_to_cli(
|
fn connect_to_cli(
|
||||||
server_name: &str,
|
server_name: &str,
|
||||||
) -> Result<(mpsc::Receiver<CliRequest>, IpcSender<CliResponse>)> {
|
) -> Result<(mpsc::Receiver<CliRequest>, IpcSender<CliResponse>)> {
|
||||||
@ -211,7 +231,50 @@ pub async fn handle_cli_connection(
|
|||||||
paths,
|
paths,
|
||||||
wait,
|
wait,
|
||||||
open_new_workspace,
|
open_new_workspace,
|
||||||
|
dev_server_token,
|
||||||
} => {
|
} => {
|
||||||
|
if let Some(dev_server_token) = dev_server_token {
|
||||||
|
match cx
|
||||||
|
.update(|cx| {
|
||||||
|
init_headless(client::DevServerToken(dev_server_token), app_state, cx)
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => {
|
||||||
|
responses
|
||||||
|
.send(CliResponse::Stdout {
|
||||||
|
message: format!("zed (pid {}) connected!", process::id()),
|
||||||
|
})
|
||||||
|
.log_err();
|
||||||
|
responses.send(CliResponse::Exit { status: 0 }).log_err();
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
responses
|
||||||
|
.send(CliResponse::Stderr {
|
||||||
|
message: format!("{}", error),
|
||||||
|
})
|
||||||
|
.log_err();
|
||||||
|
responses.send(CliResponse::Exit { status: 1 }).log_err();
|
||||||
|
cx.update(|cx| cx.quit()).log_err();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = cx
|
||||||
|
.update(|cx| init_ui(app_state.clone(), cx))
|
||||||
|
.and_then(|r| r)
|
||||||
|
{
|
||||||
|
responses
|
||||||
|
.send(CliResponse::Stderr {
|
||||||
|
message: format!("{}", e),
|
||||||
|
})
|
||||||
|
.log_err();
|
||||||
|
responses.send(CliResponse::Exit { status: 1 }).log_err();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let paths = if paths.is_empty() {
|
let paths = if paths.is_empty() {
|
||||||
if open_new_workspace == Some(true) {
|
if open_new_workspace == Some(true) {
|
||||||
vec![]
|
vec![]
|
||||||
|
@ -38,11 +38,12 @@ host_line=$(echo "$version_info" | grep host)
|
|||||||
target_triple=${host_line#*: }
|
target_triple=${host_line#*: }
|
||||||
|
|
||||||
# Build binary in release mode
|
# Build binary in release mode
|
||||||
cargo build --release --target "${target_triple}" --package zed
|
cargo build --release --target "${target_triple}" --package zed --package cli
|
||||||
|
|
||||||
# Strip the binary of all debug symbols
|
# Strip the binary of all debug symbols
|
||||||
# Later, we probably want to do something like this: https://github.com/GabrielMajeri/separate-symbols
|
# Later, we probably want to do something like this: https://github.com/GabrielMajeri/separate-symbols
|
||||||
strip "target/${target_triple}/release/Zed"
|
strip "target/${target_triple}/release/Zed"
|
||||||
|
strip "target/${target_triple}/release/cli"
|
||||||
|
|
||||||
suffix=""
|
suffix=""
|
||||||
if [ "$channel" != "stable" ]; then
|
if [ "$channel" != "stable" ]; then
|
||||||
@ -57,6 +58,7 @@ zed_dir="${temp_dir}/zed$suffix.app"
|
|||||||
# Binary
|
# Binary
|
||||||
mkdir -p "${zed_dir}/bin"
|
mkdir -p "${zed_dir}/bin"
|
||||||
cp "target/${target_triple}/release/Zed" "${zed_dir}/bin/zed"
|
cp "target/${target_triple}/release/Zed" "${zed_dir}/bin/zed"
|
||||||
|
cp "target/${target_triple}/release/cli" "${zed_dir}/bin/cli"
|
||||||
|
|
||||||
# Icons
|
# Icons
|
||||||
mkdir -p "${zed_dir}/share/icons/hicolor/512x512/apps"
|
mkdir -p "${zed_dir}/share/icons/hicolor/512x512/apps"
|
||||||
|
@ -80,7 +80,7 @@ linux() {
|
|||||||
mkdir -p "$HOME/.local/bin" "$HOME/.local/share/applications"
|
mkdir -p "$HOME/.local/bin" "$HOME/.local/share/applications"
|
||||||
|
|
||||||
# Link the binary
|
# Link the binary
|
||||||
ln -sf ~/.local/zed$suffix.app/bin/zed "$HOME/.local/bin/zed"
|
ln -sf ~/.local/zed$suffix.app/bin/cli "$HOME/.local/bin/zed"
|
||||||
|
|
||||||
# Copy .desktop file
|
# Copy .desktop file
|
||||||
desktop_file_path="$HOME/.local/share/applications/${appid}.desktop"
|
desktop_file_path="$HOME/.local/share/applications/${appid}.desktop"
|
||||||
|
Loading…
Reference in New Issue
Block a user