diff --git a/crates/zed/src/zed/open_listener.rs b/crates/zed/src/zed/open_listener.rs index abe18ff85e69f29c8c13dd6d860c19fb9e721105..a9f7b8208cf54874ce15fdcdf5a54a96731b3ef6 100644 --- a/crates/zed/src/zed/open_listener.rs +++ b/crates/zed/src/zed/open_listener.rs @@ -116,12 +116,8 @@ impl OpenRequest { this.kind = Some(OpenRequestKind::Setting { setting_path: Some(setting_path.to_string()), }); - } else if let Some(repo_url) = url.strip_prefix("zed://git/clone/") { - this.kind = Some(OpenRequestKind::GitClone { - repo_url: urlencoding::decode(repo_url) - .unwrap_or_else(|_| repo_url.into()) - .to_string(), - }); + } else if let Some(clone_path) = url.strip_prefix("zed://git/clone") { + this.parse_git_clone_url(clone_path)? } else if let Some(commit_path) = url.strip_prefix("zed://git/commit/") { this.parse_git_commit_url(commit_path)? } else if url.starts_with("ssh://") { @@ -152,6 +148,25 @@ impl OpenRequest { } } + fn parse_git_clone_url(&mut self, clone_path: &str) -> Result<()> { + // Format: /?repo= or ?repo= + let clone_path = clone_path.strip_prefix('/').unwrap_or(clone_path); + + let query = clone_path + .strip_prefix('?') + .context("invalid git clone url: missing query string")?; + + let repo_url = url::form_urlencoded::parse(query.as_bytes()) + .find_map(|(key, value)| (key == "repo").then_some(value)) + .filter(|s| !s.is_empty()) + .context("invalid git clone url: missing repo query parameter")? + .to_string(); + + self.kind = Some(OpenRequestKind::GitClone { repo_url }); + + Ok(()) + } + fn parse_git_commit_url(&mut self, commit_path: &str) -> Result<()> { // Format: ?repo= let (sha, query) = commit_path @@ -1104,7 +1119,34 @@ mod tests { let request = cx.update(|cx| { OpenRequest::parse( RawOpenRequest { - urls: vec!["zed://git/clone/https://github.com/zed-industries/zed.git".into()], + urls: vec![ + "zed://git/clone/?repo=https://github.com/zed-industries/zed.git".into(), + ], + ..Default::default() + }, + cx, + ) + .unwrap() + }); + + match request.kind { + Some(OpenRequestKind::GitClone { repo_url }) => { + assert_eq!(repo_url, "https://github.com/zed-industries/zed.git"); + } + _ => panic!("Expected GitClone kind"), + } + } + + #[gpui::test] + fn test_parse_git_clone_url_without_slash(cx: &mut TestAppContext) { + let _app_state = init_test(cx); + + let request = cx.update(|cx| { + OpenRequest::parse( + RawOpenRequest { + urls: vec![ + "zed://git/clone?repo=https://github.com/zed-industries/zed.git".into(), + ], ..Default::default() }, cx, @@ -1128,7 +1170,7 @@ mod tests { OpenRequest::parse( RawOpenRequest { urls: vec![ - "zed://git/clone/https%3A%2F%2Fgithub.com%2Fzed-industries%2Fzed.git" + "zed://git/clone/?repo=https%3A%2F%2Fgithub.com%2Fzed-industries%2Fzed.git" .into(), ], ..Default::default()