@@ -1730,23 +1730,59 @@ impl ProtoClient for Client {
/// prefix for the zed:// url scheme
pub const ZED_URL_SCHEME: &str = "zed";
+/// A parsed Zed link that can be handled internally by the application.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum ZedLink {
+ /// Join a channel: `zed.dev/channel/channel-name-123` or `zed://channel/channel-name-123`
+ Channel { channel_id: u64 },
+ /// Open channel notes: `zed.dev/channel/channel-name-123/notes` or with heading `notes#heading`
+ ChannelNotes {
+ channel_id: u64,
+ heading: Option<String>,
+ },
+}
+
/// 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: &App) -> Option<&'a str> {
+/// Returns a [`Some`] containing the parsed link if the link is a recognized Zed link
+/// that should be handled internally by the application.
+/// Returns [`None`] for links that should be opened in the browser.
+pub fn parse_zed_link(link: &str, cx: &App) -> Option<ZedLink> {
let server_url = &ClientSettings::get_global(cx).server_url;
- if let Some(stripped) = link
+ let path = link
.strip_prefix(server_url)
.and_then(|result| result.strip_prefix('/'))
- {
- return Some(stripped);
+ .or_else(|| {
+ link.strip_prefix(ZED_URL_SCHEME)
+ .and_then(|result| result.strip_prefix("://"))
+ })?;
+
+ let mut parts = path.split('/');
+
+ if parts.next() != Some("channel") {
+ return None;
}
- if let Some(stripped) = link
- .strip_prefix(ZED_URL_SCHEME)
- .and_then(|result| result.strip_prefix("://"))
- {
- return Some(stripped);
+
+ let slug = parts.next()?;
+ let id_str = slug.split('-').next_back()?;
+ let channel_id = id_str.parse::<u64>().ok()?;
+
+ let Some(next) = parts.next() else {
+ return Some(ZedLink::Channel { channel_id });
+ };
+
+ if let Some(heading) = next.strip_prefix("notes#") {
+ return Some(ZedLink::ChannelNotes {
+ channel_id,
+ heading: Some(heading.to_string()),
+ });
+ }
+
+ if next == "notes" {
+ return Some(ZedLink::ChannelNotes {
+ channel_id,
+ heading: None,
+ });
}
None
@@ -17467,7 +17467,14 @@ impl Editor {
// If there is one url or file, open it directly
match first_url_or_file {
Some(Either::Left(url)) => {
- cx.update(|_, cx| cx.open_url(&url))?;
+ cx.update(|window, cx| {
+ if parse_zed_link(&url, cx).is_some() {
+ window
+ .dispatch_action(Box::new(zed_actions::OpenZedUrl { url }), cx);
+ } else {
+ cx.open_url(&url);
+ }
+ })?;
Ok(Navigated::Yes)
}
Some(Either::Right(path)) => {
@@ -3,7 +3,7 @@ use crate::restorable_workspace_locations;
use anyhow::{Context as _, Result, anyhow};
use cli::{CliRequest, CliResponse, ipc::IpcSender};
use cli::{IpcHandshake, ipc};
-use client::parse_zed_link;
+use client::{ZedLink, parse_zed_link};
use collections::HashMap;
use db::kvp::KEY_VALUE_STORE;
use editor::Editor;
@@ -112,8 +112,18 @@ impl OpenRequest {
});
} else if url.starts_with("ssh://") {
this.parse_ssh_file_path(&url, cx)?
- } else if let Some(request_path) = parse_zed_link(&url, cx) {
- this.parse_request_path(request_path).log_err();
+ } else if let Some(zed_link) = parse_zed_link(&url, cx) {
+ match zed_link {
+ ZedLink::Channel { channel_id } => {
+ this.join_channel = Some(channel_id);
+ }
+ ZedLink::ChannelNotes {
+ channel_id,
+ heading,
+ } => {
+ this.open_channel_notes.push((channel_id, heading));
+ }
+ }
} else {
log::error!("unhandled url: {}", url);
}
@@ -157,31 +167,6 @@ impl OpenRequest {
self.parse_file_path(url.path());
Ok(())
}
-
- fn parse_request_path(&mut self, request_path: &str) -> Result<()> {
- let mut parts = request_path.split('/');
- if parts.next() == Some("channel")
- && let Some(slug) = parts.next()
- && let Some(id_str) = slug.split('-').next_back()
- && let Ok(channel_id) = id_str.parse::<u64>()
- {
- let Some(next) = parts.next() else {
- self.join_channel = Some(channel_id);
- return Ok(());
- };
-
- if let Some(heading) = next.strip_prefix("notes#") {
- self.open_channel_notes
- .push((channel_id, Some(heading.to_string())));
- return Ok(());
- }
- if next == "notes" {
- self.open_channel_notes.push((channel_id, None));
- return Ok(());
- }
- }
- anyhow::bail!("invalid zed url: {request_path}")
- }
}
#[derive(Clone)]