mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-08 07:35:01 +03:00
Open URIs from the CLI, support for the zed://
URI scheme on Linux (#14104)
Allows Zed to open custom `zed://` links (redirects from https://zed.dev/channels) on Linux used XDG MIME types. This PR also allows the CLI to be able to open Zed (`zed://`) URIs directly instead of executing the main executable in `/usr/libexec/zed-editor`. Release Notes: - Linux: Allow `zed.dev/channel` (`zed://`) URIs to open on Linux - CLI: Ability to open URIs from the command line --------- Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
This commit is contained in:
parent
64a796d436
commit
0c6105992c
@ -11,6 +11,7 @@ pub struct IpcHandshake {
|
||||
pub enum CliRequest {
|
||||
Open {
|
||||
paths: Vec<String>,
|
||||
urls: Vec<String>,
|
||||
wait: bool,
|
||||
open_new_workspace: Option<bool>,
|
||||
dev_server_token: Option<String>,
|
||||
|
@ -5,6 +5,7 @@ use clap::Parser;
|
||||
use cli::{ipc::IpcOneShotServer, CliRequest, CliResponse, IpcHandshake};
|
||||
use parking_lot::Mutex;
|
||||
use std::{
|
||||
convert::Infallible,
|
||||
env, fs, io,
|
||||
path::{Path, PathBuf},
|
||||
process::ExitStatus,
|
||||
@ -37,8 +38,7 @@ struct Args {
|
||||
///
|
||||
/// Use `path:line:row` syntax to open a file at a specific location.
|
||||
/// Non-existing paths and directories will ignore `:line:row` suffix.
|
||||
#[arg(value_parser = parse_path_with_position)]
|
||||
paths_with_position: Vec<PathLikeWithPosition<PathBuf>>,
|
||||
paths_with_position: Vec<String>,
|
||||
/// Print Zed's version and the app path.
|
||||
#[arg(short, long)]
|
||||
version: bool,
|
||||
@ -53,12 +53,30 @@ struct Args {
|
||||
dev_server_token: Option<String>,
|
||||
}
|
||||
|
||||
fn parse_path_with_position(
|
||||
argument_str: &str,
|
||||
) -> Result<PathLikeWithPosition<PathBuf>, std::convert::Infallible> {
|
||||
PathLikeWithPosition::parse_str(argument_str, |_, path_str| {
|
||||
fn parse_path_with_position(argument_str: &str) -> Result<String, std::io::Error> {
|
||||
let path_like = PathLikeWithPosition::parse_str::<Infallible>(argument_str, |_, path_str| {
|
||||
Ok(Path::new(path_str).to_path_buf())
|
||||
})
|
||||
.unwrap();
|
||||
let curdir = env::current_dir()?;
|
||||
|
||||
let canonicalized = path_like.map_path_like(|path| match fs::canonicalize(&path) {
|
||||
Ok(path) => Ok(path),
|
||||
Err(e) => {
|
||||
if let Some(mut parent) = path.parent() {
|
||||
if parent == Path::new("") {
|
||||
parent = &curdir
|
||||
}
|
||||
match fs::canonicalize(parent) {
|
||||
Ok(parent) => Ok(parent.join(path.file_name().unwrap())),
|
||||
Err(_) => Err(e),
|
||||
}
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
})?;
|
||||
Ok(canonicalized.to_string(|path| path.display().to_string()))
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
@ -91,28 +109,6 @@ fn main() -> Result<()> {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let curdir = env::current_dir()?;
|
||||
let mut paths = vec![];
|
||||
for path in args.paths_with_position {
|
||||
let canonicalized = path.map_path_like(|path| match fs::canonicalize(&path) {
|
||||
Ok(path) => Ok(path),
|
||||
Err(e) => {
|
||||
if let Some(mut parent) = path.parent() {
|
||||
if parent == Path::new("") {
|
||||
parent = &curdir;
|
||||
}
|
||||
match fs::canonicalize(parent) {
|
||||
Ok(parent) => Ok(parent.join(path.file_name().unwrap())),
|
||||
Err(_) => Err(e),
|
||||
}
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
})?;
|
||||
paths.push(canonicalized.to_string(|path| path.display().to_string()))
|
||||
}
|
||||
|
||||
let (server, server_name) =
|
||||
IpcOneShotServer::<IpcHandshake>::new().context("Handshake before Zed spawn")?;
|
||||
let url = format!("zed-cli://{server_name}");
|
||||
@ -126,6 +122,19 @@ fn main() -> Result<()> {
|
||||
};
|
||||
|
||||
let exit_status = Arc::new(Mutex::new(None));
|
||||
let mut paths = vec![];
|
||||
let mut urls = vec![];
|
||||
for path in args.paths_with_position.iter() {
|
||||
if path.starts_with("zed://")
|
||||
|| path.starts_with("http://")
|
||||
|| path.starts_with("https://")
|
||||
|| path.starts_with("file://")
|
||||
{
|
||||
urls.push(path.to_string());
|
||||
} else {
|
||||
paths.push(parse_path_with_position(path)?)
|
||||
}
|
||||
}
|
||||
|
||||
let sender: JoinHandle<anyhow::Result<()>> = thread::spawn({
|
||||
let exit_status = exit_status.clone();
|
||||
@ -134,6 +143,7 @@ fn main() -> Result<()> {
|
||||
let (tx, rx) = (handshake.requests, handshake.responses);
|
||||
tx.send(CliRequest::Open {
|
||||
paths,
|
||||
urls,
|
||||
wait: args.wait,
|
||||
open_new_workspace,
|
||||
dev_server_token: args.dev_server_token,
|
||||
|
@ -4,13 +4,13 @@ Type=Application
|
||||
Name=$APP_NAME
|
||||
GenericName=Text Editor
|
||||
Comment=A high-performance, multiplayer code editor.
|
||||
TryExec=$APP_CLI
|
||||
TryExec=$APP
|
||||
StartupNotify=$DO_STARTUP_NOTIFY
|
||||
Exec=$APP_CLI $APP_ARGS
|
||||
Icon=$APP_ICON
|
||||
Categories=Utility;TextEditor;Development;IDE;
|
||||
Keywords=zed;
|
||||
MimeType=text/plain;inode/directory;
|
||||
MimeType=text/plain;inode/directory;x-scheme-handler/zed;
|
||||
Actions=NewWorkspace;
|
||||
|
||||
[Desktop Action NewWorkspace]
|
||||
|
@ -22,7 +22,7 @@ use welcome::{show_welcome_view, FIRST_OPEN};
|
||||
use workspace::item::ItemHandle;
|
||||
use workspace::{AppState, Workspace};
|
||||
|
||||
use crate::{init_headless, init_ui};
|
||||
use crate::{handle_open_request, init_headless, init_ui};
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct OpenRequest {
|
||||
@ -223,6 +223,7 @@ pub async fn handle_cli_connection(
|
||||
if let Some(request) = requests.next().await {
|
||||
match request {
|
||||
CliRequest::Open {
|
||||
urls,
|
||||
paths,
|
||||
wait,
|
||||
open_new_workspace,
|
||||
@ -257,6 +258,27 @@ pub async fn handle_cli_connection(
|
||||
return;
|
||||
}
|
||||
|
||||
if !urls.is_empty() {
|
||||
cx.update(|cx| {
|
||||
match OpenRequest::parse(urls, cx) {
|
||||
Ok(open_request) => {
|
||||
handle_open_request(open_request, app_state.clone(), cx);
|
||||
responses.send(CliResponse::Exit { status: 0 }).log_err();
|
||||
}
|
||||
Err(e) => {
|
||||
responses
|
||||
.send(CliResponse::Stderr {
|
||||
message: format!("{e}"),
|
||||
})
|
||||
.log_err();
|
||||
responses.send(CliResponse::Exit { status: 1 }).log_err();
|
||||
}
|
||||
};
|
||||
})
|
||||
.log_err();
|
||||
return;
|
||||
}
|
||||
|
||||
if let Err(e) = cx
|
||||
.update(|cx| init_ui(app_state.clone(), cx))
|
||||
.and_then(|r| r)
|
||||
|
Loading…
Reference in New Issue
Block a user