permalink.rs

  1use std::ops::Range;
  2
  3use anyhow::{anyhow, Result};
  4use language::Point;
  5use url::Url;
  6
  7enum GitHostingProvider {
  8    Github,
  9    Gitlab,
 10    Gitee,
 11    Bitbucket,
 12    Sourcehut,
 13    Codeberg,
 14}
 15
 16impl GitHostingProvider {
 17    fn base_url(&self) -> Url {
 18        let base_url = match self {
 19            Self::Github => "https://github.com",
 20            Self::Gitlab => "https://gitlab.com",
 21            Self::Gitee => "https://gitee.com",
 22            Self::Bitbucket => "https://bitbucket.org",
 23            Self::Sourcehut => "https://git.sr.ht",
 24            Self::Codeberg => "https://codeberg.org",
 25        };
 26
 27        Url::parse(&base_url).unwrap()
 28    }
 29
 30    /// Returns the fragment portion of the URL for the selected lines in
 31    /// the representation the [`GitHostingProvider`] expects.
 32    fn line_fragment(&self, selection: &Range<Point>) -> String {
 33        if selection.start.row == selection.end.row {
 34            let line = selection.start.row + 1;
 35
 36            match self {
 37                Self::Github | Self::Gitlab | Self::Gitee | Self::Sourcehut | Self::Codeberg => {
 38                    format!("L{}", line)
 39                }
 40                Self::Bitbucket => format!("lines-{}", line),
 41            }
 42        } else {
 43            let start_line = selection.start.row + 1;
 44            let end_line = selection.end.row + 1;
 45
 46            match self {
 47                Self::Github | Self::Codeberg => format!("L{}-L{}", start_line, end_line),
 48                Self::Gitlab | Self::Gitee | Self::Sourcehut => {
 49                    format!("L{}-{}", start_line, end_line)
 50                }
 51                Self::Bitbucket => format!("lines-{}:{}", start_line, end_line),
 52            }
 53        }
 54    }
 55}
 56
 57pub struct BuildPermalinkParams<'a> {
 58    pub remote_url: &'a str,
 59    pub sha: &'a str,
 60    pub path: &'a str,
 61    pub selection: Option<Range<Point>>,
 62}
 63
 64pub fn build_permalink(params: BuildPermalinkParams) -> Result<Url> {
 65    let BuildPermalinkParams {
 66        remote_url,
 67        sha,
 68        path,
 69        selection,
 70    } = params;
 71
 72    let ParsedGitRemote {
 73        provider,
 74        owner,
 75        repo,
 76    } = parse_git_remote_url(remote_url)
 77        .ok_or_else(|| anyhow!("failed to parse Git remote URL"))?;
 78
 79    let path = match provider {
 80        GitHostingProvider::Github => format!("{owner}/{repo}/blob/{sha}/{path}"),
 81        GitHostingProvider::Gitlab => format!("{owner}/{repo}/-/blob/{sha}/{path}"),
 82        GitHostingProvider::Gitee => format!("{owner}/{repo}/blob/{sha}/{path}"),
 83        GitHostingProvider::Bitbucket => format!("{owner}/{repo}/src/{sha}/{path}"),
 84        GitHostingProvider::Sourcehut => format!("~{owner}/{repo}/tree/{sha}/item/{path}"),
 85        GitHostingProvider::Codeberg => format!("{owner}/{repo}/src/commit/{sha}/{path}"),
 86    };
 87    let line_fragment = selection.map(|selection| provider.line_fragment(&selection));
 88
 89    let mut permalink = provider.base_url().join(&path).unwrap();
 90    permalink.set_fragment(line_fragment.as_deref());
 91
 92    Ok(permalink)
 93}
 94
 95struct ParsedGitRemote<'a> {
 96    pub provider: GitHostingProvider,
 97    pub owner: &'a str,
 98    pub repo: &'a str,
 99}
100
101fn parse_git_remote_url(url: &str) -> Option<ParsedGitRemote> {
102    if url.starts_with("git@github.com:") || url.starts_with("https://github.com/") {
103        let repo_with_owner = url
104            .trim_start_matches("git@github.com:")
105            .trim_start_matches("https://github.com/")
106            .trim_end_matches(".git");
107
108        let (owner, repo) = repo_with_owner.split_once('/')?;
109
110        return Some(ParsedGitRemote {
111            provider: GitHostingProvider::Github,
112            owner,
113            repo,
114        });
115    }
116
117    if url.starts_with("git@gitlab.com:") || url.starts_with("https://gitlab.com/") {
118        let repo_with_owner = url
119            .trim_start_matches("git@gitlab.com:")
120            .trim_start_matches("https://gitlab.com/")
121            .trim_end_matches(".git");
122
123        let (owner, repo) = repo_with_owner.split_once('/')?;
124
125        return Some(ParsedGitRemote {
126            provider: GitHostingProvider::Gitlab,
127            owner,
128            repo,
129        });
130    }
131
132    if url.starts_with("git@gitee.com:") || url.starts_with("https://gitee.com/") {
133        let repo_with_owner = url
134            .trim_start_matches("git@gitee.com:")
135            .trim_start_matches("https://gitee.com/")
136            .trim_end_matches(".git");
137
138        let (owner, repo) = repo_with_owner.split_once('/')?;
139
140        return Some(ParsedGitRemote {
141            provider: GitHostingProvider::Gitee,
142            owner,
143            repo,
144        });
145    }
146
147    if url.contains("bitbucket.org") {
148        let (_, repo_with_owner) = url.trim_end_matches(".git").split_once("bitbucket.org")?;
149        let (owner, repo) = repo_with_owner
150            .trim_start_matches('/')
151            .trim_start_matches(':')
152            .split_once('/')?;
153
154        return Some(ParsedGitRemote {
155            provider: GitHostingProvider::Bitbucket,
156            owner,
157            repo,
158        });
159    }
160
161    if url.starts_with("git@git.sr.ht:") || url.starts_with("https://git.sr.ht/") {
162        // sourcehut indicates a repo with '.git' suffix as a separate repo.
163        // For example, "git@git.sr.ht:~username/repo" and "git@git.sr.ht:~username/repo.git"
164        // are two distinct repositories.
165        let repo_with_owner = url
166            .trim_start_matches("git@git.sr.ht:~")
167            .trim_start_matches("https://git.sr.ht/~");
168
169        let (owner, repo) = repo_with_owner.split_once('/')?;
170
171        return Some(ParsedGitRemote {
172            provider: GitHostingProvider::Sourcehut,
173            owner,
174            repo,
175        });
176    }
177
178    if url.starts_with("git@codeberg.org:") || url.starts_with("https://codeberg.org/") {
179        let repo_with_owner = url
180            .trim_start_matches("git@codeberg.org:")
181            .trim_start_matches("https://codeberg.org/")
182            .trim_end_matches(".git");
183
184        let (owner, repo) = repo_with_owner.split_once('/')?;
185
186        return Some(ParsedGitRemote {
187            provider: GitHostingProvider::Codeberg,
188            owner,
189            repo,
190        });
191    }
192
193    None
194}
195
196#[cfg(test)]
197mod tests {
198    use super::*;
199
200    #[test]
201    fn test_build_github_permalink_from_ssh_url() {
202        let permalink = build_permalink(BuildPermalinkParams {
203            remote_url: "git@github.com:zed-industries/zed.git",
204            sha: "e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7",
205            path: "crates/editor/src/git/permalink.rs",
206            selection: None,
207        })
208        .unwrap();
209
210        let expected_url = "https://github.com/zed-industries/zed/blob/e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7/crates/editor/src/git/permalink.rs";
211        assert_eq!(permalink.to_string(), expected_url.to_string())
212    }
213
214    #[test]
215    fn test_build_github_permalink_from_ssh_url_single_line_selection() {
216        let permalink = build_permalink(BuildPermalinkParams {
217            remote_url: "git@github.com:zed-industries/zed.git",
218            sha: "e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7",
219            path: "crates/editor/src/git/permalink.rs",
220            selection: Some(Point::new(6, 1)..Point::new(6, 10)),
221        })
222        .unwrap();
223
224        let expected_url = "https://github.com/zed-industries/zed/blob/e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7/crates/editor/src/git/permalink.rs#L7";
225        assert_eq!(permalink.to_string(), expected_url.to_string())
226    }
227
228    #[test]
229    fn test_build_github_permalink_from_ssh_url_multi_line_selection() {
230        let permalink = build_permalink(BuildPermalinkParams {
231            remote_url: "git@github.com:zed-industries/zed.git",
232            sha: "e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7",
233            path: "crates/editor/src/git/permalink.rs",
234            selection: Some(Point::new(23, 1)..Point::new(47, 10)),
235        })
236        .unwrap();
237
238        let expected_url = "https://github.com/zed-industries/zed/blob/e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7/crates/editor/src/git/permalink.rs#L24-L48";
239        assert_eq!(permalink.to_string(), expected_url.to_string())
240    }
241
242    #[test]
243    fn test_build_github_permalink_from_https_url() {
244        let permalink = build_permalink(BuildPermalinkParams {
245            remote_url: "https://github.com/zed-industries/zed.git",
246            sha: "b2efec9824c45fcc90c9a7eb107a50d1772a60aa",
247            path: "crates/zed/src/main.rs",
248            selection: None,
249        })
250        .unwrap();
251
252        let expected_url = "https://github.com/zed-industries/zed/blob/b2efec9824c45fcc90c9a7eb107a50d1772a60aa/crates/zed/src/main.rs";
253        assert_eq!(permalink.to_string(), expected_url.to_string())
254    }
255
256    #[test]
257    fn test_build_github_permalink_from_https_url_single_line_selection() {
258        let permalink = build_permalink(BuildPermalinkParams {
259            remote_url: "https://github.com/zed-industries/zed.git",
260            sha: "b2efec9824c45fcc90c9a7eb107a50d1772a60aa",
261            path: "crates/zed/src/main.rs",
262            selection: Some(Point::new(6, 1)..Point::new(6, 10)),
263        })
264        .unwrap();
265
266        let expected_url = "https://github.com/zed-industries/zed/blob/b2efec9824c45fcc90c9a7eb107a50d1772a60aa/crates/zed/src/main.rs#L7";
267        assert_eq!(permalink.to_string(), expected_url.to_string())
268    }
269
270    #[test]
271    fn test_build_github_permalink_from_https_url_multi_line_selection() {
272        let permalink = build_permalink(BuildPermalinkParams {
273            remote_url: "https://github.com/zed-industries/zed.git",
274            sha: "b2efec9824c45fcc90c9a7eb107a50d1772a60aa",
275            path: "crates/zed/src/main.rs",
276            selection: Some(Point::new(23, 1)..Point::new(47, 10)),
277        })
278        .unwrap();
279
280        let expected_url = "https://github.com/zed-industries/zed/blob/b2efec9824c45fcc90c9a7eb107a50d1772a60aa/crates/zed/src/main.rs#L24-L48";
281        assert_eq!(permalink.to_string(), expected_url.to_string())
282    }
283
284    #[test]
285    fn test_build_gitlab_permalink_from_ssh_url() {
286        let permalink = build_permalink(BuildPermalinkParams {
287            remote_url: "git@gitlab.com:zed-industries/zed.git",
288            sha: "e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7",
289            path: "crates/editor/src/git/permalink.rs",
290            selection: None,
291        })
292        .unwrap();
293
294        let expected_url = "https://gitlab.com/zed-industries/zed/-/blob/e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7/crates/editor/src/git/permalink.rs";
295        assert_eq!(permalink.to_string(), expected_url.to_string())
296    }
297
298    #[test]
299    fn test_build_gitlab_permalink_from_ssh_url_single_line_selection() {
300        let permalink = build_permalink(BuildPermalinkParams {
301            remote_url: "git@gitlab.com:zed-industries/zed.git",
302            sha: "e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7",
303            path: "crates/editor/src/git/permalink.rs",
304            selection: Some(Point::new(6, 1)..Point::new(6, 10)),
305        })
306        .unwrap();
307
308        let expected_url = "https://gitlab.com/zed-industries/zed/-/blob/e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7/crates/editor/src/git/permalink.rs#L7";
309        assert_eq!(permalink.to_string(), expected_url.to_string())
310    }
311
312    #[test]
313    fn test_build_gitlab_permalink_from_ssh_url_multi_line_selection() {
314        let permalink = build_permalink(BuildPermalinkParams {
315            remote_url: "git@gitlab.com:zed-industries/zed.git",
316            sha: "e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7",
317            path: "crates/editor/src/git/permalink.rs",
318            selection: Some(Point::new(23, 1)..Point::new(47, 10)),
319        })
320        .unwrap();
321
322        let expected_url = "https://gitlab.com/zed-industries/zed/-/blob/e6ebe7974deb6bb6cc0e2595c8ec31f0c71084b7/crates/editor/src/git/permalink.rs#L24-48";
323        assert_eq!(permalink.to_string(), expected_url.to_string())
324    }
325
326    #[test]
327    fn test_build_gitlab_permalink_from_https_url() {
328        let permalink = build_permalink(BuildPermalinkParams {
329            remote_url: "https://gitlab.com/zed-industries/zed.git",
330            sha: "b2efec9824c45fcc90c9a7eb107a50d1772a60aa",
331            path: "crates/zed/src/main.rs",
332            selection: None,
333        })
334        .unwrap();
335
336        let expected_url = "https://gitlab.com/zed-industries/zed/-/blob/b2efec9824c45fcc90c9a7eb107a50d1772a60aa/crates/zed/src/main.rs";
337        assert_eq!(permalink.to_string(), expected_url.to_string())
338    }
339
340    #[test]
341    fn test_build_gitlab_permalink_from_https_url_single_line_selection() {
342        let permalink = build_permalink(BuildPermalinkParams {
343            remote_url: "https://gitlab.com/zed-industries/zed.git",
344            sha: "b2efec9824c45fcc90c9a7eb107a50d1772a60aa",
345            path: "crates/zed/src/main.rs",
346            selection: Some(Point::new(6, 1)..Point::new(6, 10)),
347        })
348        .unwrap();
349
350        let expected_url = "https://gitlab.com/zed-industries/zed/-/blob/b2efec9824c45fcc90c9a7eb107a50d1772a60aa/crates/zed/src/main.rs#L7";
351        assert_eq!(permalink.to_string(), expected_url.to_string())
352    }
353
354    #[test]
355    fn test_build_gitlab_permalink_from_https_url_multi_line_selection() {
356        let permalink = build_permalink(BuildPermalinkParams {
357            remote_url: "https://gitlab.com/zed-industries/zed.git",
358            sha: "b2efec9824c45fcc90c9a7eb107a50d1772a60aa",
359            path: "crates/zed/src/main.rs",
360            selection: Some(Point::new(23, 1)..Point::new(47, 10)),
361        })
362        .unwrap();
363
364        let expected_url = "https://gitlab.com/zed-industries/zed/-/blob/b2efec9824c45fcc90c9a7eb107a50d1772a60aa/crates/zed/src/main.rs#L24-48";
365        assert_eq!(permalink.to_string(), expected_url.to_string())
366    }
367
368    #[test]
369    fn test_build_gitee_permalink_from_ssh_url() {
370        let permalink = build_permalink(BuildPermalinkParams {
371            remote_url: "git@gitee.com:libkitten/zed.git",
372            sha: "e5fe811d7ad0fc26934edd76f891d20bdc3bb194",
373            path: "crates/editor/src/git/permalink.rs",
374            selection: None,
375        })
376        .unwrap();
377
378        let expected_url = "https://gitee.com/libkitten/zed/blob/e5fe811d7ad0fc26934edd76f891d20bdc3bb194/crates/editor/src/git/permalink.rs";
379        assert_eq!(permalink.to_string(), expected_url.to_string())
380    }
381
382    #[test]
383    fn test_build_gitee_permalink_from_ssh_url_single_line_selection() {
384        let permalink = build_permalink(BuildPermalinkParams {
385            remote_url: "git@gitee.com:libkitten/zed.git",
386            sha: "e5fe811d7ad0fc26934edd76f891d20bdc3bb194",
387            path: "crates/editor/src/git/permalink.rs",
388            selection: Some(Point::new(6, 1)..Point::new(6, 10)),
389        })
390        .unwrap();
391
392        let expected_url = "https://gitee.com/libkitten/zed/blob/e5fe811d7ad0fc26934edd76f891d20bdc3bb194/crates/editor/src/git/permalink.rs#L7";
393        assert_eq!(permalink.to_string(), expected_url.to_string())
394    }
395
396    #[test]
397    fn test_build_gitee_permalink_from_ssh_url_multi_line_selection() {
398        let permalink = build_permalink(BuildPermalinkParams {
399            remote_url: "git@gitee.com:libkitten/zed.git",
400            sha: "e5fe811d7ad0fc26934edd76f891d20bdc3bb194",
401            path: "crates/editor/src/git/permalink.rs",
402            selection: Some(Point::new(23, 1)..Point::new(47, 10)),
403        })
404        .unwrap();
405
406        let expected_url = "https://gitee.com/libkitten/zed/blob/e5fe811d7ad0fc26934edd76f891d20bdc3bb194/crates/editor/src/git/permalink.rs#L24-48";
407        assert_eq!(permalink.to_string(), expected_url.to_string())
408    }
409
410    #[test]
411    fn test_build_gitee_permalink_from_https_url() {
412        let permalink = build_permalink(BuildPermalinkParams {
413            remote_url: "https://gitee.com/libkitten/zed.git",
414            sha: "e5fe811d7ad0fc26934edd76f891d20bdc3bb194",
415            path: "crates/zed/src/main.rs",
416            selection: None,
417        })
418        .unwrap();
419
420        let expected_url = "https://gitee.com/libkitten/zed/blob/e5fe811d7ad0fc26934edd76f891d20bdc3bb194/crates/zed/src/main.rs";
421        assert_eq!(permalink.to_string(), expected_url.to_string())
422    }
423
424    #[test]
425    fn test_build_gitee_permalink_from_https_url_single_line_selection() {
426        let permalink = build_permalink(BuildPermalinkParams {
427            remote_url: "https://gitee.com/libkitten/zed.git",
428            sha: "e5fe811d7ad0fc26934edd76f891d20bdc3bb194",
429            path: "crates/zed/src/main.rs",
430            selection: Some(Point::new(6, 1)..Point::new(6, 10)),
431        })
432        .unwrap();
433
434        let expected_url = "https://gitee.com/libkitten/zed/blob/e5fe811d7ad0fc26934edd76f891d20bdc3bb194/crates/zed/src/main.rs#L7";
435        assert_eq!(permalink.to_string(), expected_url.to_string())
436    }
437
438    #[test]
439    fn test_build_gitee_permalink_from_https_url_multi_line_selection() {
440        let permalink = build_permalink(BuildPermalinkParams {
441            remote_url: "https://gitee.com/libkitten/zed.git",
442            sha: "e5fe811d7ad0fc26934edd76f891d20bdc3bb194",
443            path: "crates/zed/src/main.rs",
444            selection: Some(Point::new(23, 1)..Point::new(47, 10)),
445        })
446        .unwrap();
447        let expected_url = "https://gitee.com/libkitten/zed/blob/e5fe811d7ad0fc26934edd76f891d20bdc3bb194/crates/zed/src/main.rs#L24-48";
448        assert_eq!(permalink.to_string(), expected_url.to_string())
449    }
450
451    #[test]
452    fn test_parse_git_remote_url_bitbucket_https_with_username() {
453        let url = "https://thorstenballzed@bitbucket.org/thorstenzed/testingrepo.git";
454        let parsed = parse_git_remote_url(url).unwrap();
455        assert!(matches!(parsed.provider, GitHostingProvider::Bitbucket));
456        assert_eq!(parsed.owner, "thorstenzed");
457        assert_eq!(parsed.repo, "testingrepo");
458    }
459
460    #[test]
461    fn test_parse_git_remote_url_bitbucket_https_without_username() {
462        let url = "https://bitbucket.org/thorstenzed/testingrepo.git";
463        let parsed = parse_git_remote_url(url).unwrap();
464        assert!(matches!(parsed.provider, GitHostingProvider::Bitbucket));
465        assert_eq!(parsed.owner, "thorstenzed");
466        assert_eq!(parsed.repo, "testingrepo");
467    }
468
469    #[test]
470    fn test_parse_git_remote_url_bitbucket_git() {
471        let url = "git@bitbucket.org:thorstenzed/testingrepo.git";
472        let parsed = parse_git_remote_url(url).unwrap();
473        assert!(matches!(parsed.provider, GitHostingProvider::Bitbucket));
474        assert_eq!(parsed.owner, "thorstenzed");
475        assert_eq!(parsed.repo, "testingrepo");
476    }
477
478    #[test]
479    fn test_build_bitbucket_permalink_from_ssh_url() {
480        let permalink = build_permalink(BuildPermalinkParams {
481            remote_url: "git@bitbucket.org:thorstenzed/testingrepo.git",
482            sha: "f00b4r",
483            path: "main.rs",
484            selection: None,
485        })
486        .unwrap();
487
488        let expected_url = "https://bitbucket.org/thorstenzed/testingrepo/src/f00b4r/main.rs";
489        assert_eq!(permalink.to_string(), expected_url.to_string())
490    }
491
492    #[test]
493    fn test_build_bitbucket_permalink_from_ssh_url_single_line_selection() {
494        let permalink = build_permalink(BuildPermalinkParams {
495            remote_url: "git@bitbucket.org:thorstenzed/testingrepo.git",
496            sha: "f00b4r",
497            path: "main.rs",
498            selection: Some(Point::new(6, 1)..Point::new(6, 10)),
499        })
500        .unwrap();
501
502        let expected_url =
503            "https://bitbucket.org/thorstenzed/testingrepo/src/f00b4r/main.rs#lines-7";
504        assert_eq!(permalink.to_string(), expected_url.to_string())
505    }
506
507    #[test]
508    fn test_build_bitbucket_permalink_from_ssh_url_multi_line_selection() {
509        let permalink = build_permalink(BuildPermalinkParams {
510            remote_url: "git@bitbucket.org:thorstenzed/testingrepo.git",
511            sha: "f00b4r",
512            path: "main.rs",
513            selection: Some(Point::new(23, 1)..Point::new(47, 10)),
514        })
515        .unwrap();
516
517        let expected_url =
518            "https://bitbucket.org/thorstenzed/testingrepo/src/f00b4r/main.rs#lines-24:48";
519        assert_eq!(permalink.to_string(), expected_url.to_string())
520    }
521
522    #[test]
523    fn test_build_sourcehut_permalink_from_ssh_url() {
524        let permalink = build_permalink(BuildPermalinkParams {
525            remote_url: "git@git.sr.ht:~rajveermalviya/zed",
526            sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
527            path: "crates/editor/src/git/permalink.rs",
528            selection: None,
529        })
530        .unwrap();
531
532        let expected_url = "https://git.sr.ht/~rajveermalviya/zed/tree/faa6f979be417239b2e070dbbf6392b909224e0b/item/crates/editor/src/git/permalink.rs";
533        assert_eq!(permalink.to_string(), expected_url.to_string())
534    }
535
536    #[test]
537    fn test_build_sourcehut_permalink_from_ssh_url_with_git_prefix() {
538        let permalink = build_permalink(BuildPermalinkParams {
539            remote_url: "git@git.sr.ht:~rajveermalviya/zed.git",
540            sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
541            path: "crates/editor/src/git/permalink.rs",
542            selection: None,
543        })
544        .unwrap();
545
546        let expected_url = "https://git.sr.ht/~rajveermalviya/zed.git/tree/faa6f979be417239b2e070dbbf6392b909224e0b/item/crates/editor/src/git/permalink.rs";
547        assert_eq!(permalink.to_string(), expected_url.to_string())
548    }
549
550    #[test]
551    fn test_build_sourcehut_permalink_from_ssh_url_single_line_selection() {
552        let permalink = build_permalink(BuildPermalinkParams {
553            remote_url: "git@git.sr.ht:~rajveermalviya/zed",
554            sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
555            path: "crates/editor/src/git/permalink.rs",
556            selection: Some(Point::new(6, 1)..Point::new(6, 10)),
557        })
558        .unwrap();
559
560        let expected_url = "https://git.sr.ht/~rajveermalviya/zed/tree/faa6f979be417239b2e070dbbf6392b909224e0b/item/crates/editor/src/git/permalink.rs#L7";
561        assert_eq!(permalink.to_string(), expected_url.to_string())
562    }
563
564    #[test]
565    fn test_build_sourcehut_permalink_from_ssh_url_multi_line_selection() {
566        let permalink = build_permalink(BuildPermalinkParams {
567            remote_url: "git@git.sr.ht:~rajveermalviya/zed",
568            sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
569            path: "crates/editor/src/git/permalink.rs",
570            selection: Some(Point::new(23, 1)..Point::new(47, 10)),
571        })
572        .unwrap();
573
574        let expected_url = "https://git.sr.ht/~rajveermalviya/zed/tree/faa6f979be417239b2e070dbbf6392b909224e0b/item/crates/editor/src/git/permalink.rs#L24-48";
575        assert_eq!(permalink.to_string(), expected_url.to_string())
576    }
577
578    #[test]
579    fn test_build_sourcehut_permalink_from_https_url() {
580        let permalink = build_permalink(BuildPermalinkParams {
581            remote_url: "https://git.sr.ht/~rajveermalviya/zed",
582            sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
583            path: "crates/zed/src/main.rs",
584            selection: None,
585        })
586        .unwrap();
587
588        let expected_url = "https://git.sr.ht/~rajveermalviya/zed/tree/faa6f979be417239b2e070dbbf6392b909224e0b/item/crates/zed/src/main.rs";
589        assert_eq!(permalink.to_string(), expected_url.to_string())
590    }
591
592    #[test]
593    fn test_build_sourcehut_permalink_from_https_url_single_line_selection() {
594        let permalink = build_permalink(BuildPermalinkParams {
595            remote_url: "https://git.sr.ht/~rajveermalviya/zed",
596            sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
597            path: "crates/zed/src/main.rs",
598            selection: Some(Point::new(6, 1)..Point::new(6, 10)),
599        })
600        .unwrap();
601
602        let expected_url = "https://git.sr.ht/~rajveermalviya/zed/tree/faa6f979be417239b2e070dbbf6392b909224e0b/item/crates/zed/src/main.rs#L7";
603        assert_eq!(permalink.to_string(), expected_url.to_string())
604    }
605
606    #[test]
607    fn test_build_sourcehut_permalink_from_https_url_multi_line_selection() {
608        let permalink = build_permalink(BuildPermalinkParams {
609            remote_url: "https://git.sr.ht/~rajveermalviya/zed",
610            sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
611            path: "crates/zed/src/main.rs",
612            selection: Some(Point::new(23, 1)..Point::new(47, 10)),
613        })
614        .unwrap();
615
616        let expected_url = "https://git.sr.ht/~rajveermalviya/zed/tree/faa6f979be417239b2e070dbbf6392b909224e0b/item/crates/zed/src/main.rs#L24-48";
617        assert_eq!(permalink.to_string(), expected_url.to_string())
618    }
619
620    #[test]
621    fn test_build_codeberg_permalink_from_ssh_url() {
622        let permalink = build_permalink(BuildPermalinkParams {
623            remote_url: "git@codeberg.org:rajveermalviya/zed.git",
624            sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
625            path: "crates/editor/src/git/permalink.rs",
626            selection: None,
627        })
628        .unwrap();
629
630        let expected_url = "https://codeberg.org/rajveermalviya/zed/src/commit/faa6f979be417239b2e070dbbf6392b909224e0b/crates/editor/src/git/permalink.rs";
631        assert_eq!(permalink.to_string(), expected_url.to_string())
632    }
633
634    #[test]
635    fn test_build_codeberg_permalink_from_ssh_url_single_line_selection() {
636        let permalink = build_permalink(BuildPermalinkParams {
637            remote_url: "git@codeberg.org:rajveermalviya/zed.git",
638            sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
639            path: "crates/editor/src/git/permalink.rs",
640            selection: Some(Point::new(6, 1)..Point::new(6, 10)),
641        })
642        .unwrap();
643
644        let expected_url = "https://codeberg.org/rajveermalviya/zed/src/commit/faa6f979be417239b2e070dbbf6392b909224e0b/crates/editor/src/git/permalink.rs#L7";
645        assert_eq!(permalink.to_string(), expected_url.to_string())
646    }
647
648    #[test]
649    fn test_build_codeberg_permalink_from_ssh_url_multi_line_selection() {
650        let permalink = build_permalink(BuildPermalinkParams {
651            remote_url: "git@codeberg.org:rajveermalviya/zed.git",
652            sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
653            path: "crates/editor/src/git/permalink.rs",
654            selection: Some(Point::new(23, 1)..Point::new(47, 10)),
655        })
656        .unwrap();
657
658        let expected_url = "https://codeberg.org/rajveermalviya/zed/src/commit/faa6f979be417239b2e070dbbf6392b909224e0b/crates/editor/src/git/permalink.rs#L24-L48";
659        assert_eq!(permalink.to_string(), expected_url.to_string())
660    }
661
662    #[test]
663    fn test_build_codeberg_permalink_from_https_url() {
664        let permalink = build_permalink(BuildPermalinkParams {
665            remote_url: "https://codeberg.org/rajveermalviya/zed.git",
666            sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
667            path: "crates/zed/src/main.rs",
668            selection: None,
669        })
670        .unwrap();
671
672        let expected_url = "https://codeberg.org/rajveermalviya/zed/src/commit/faa6f979be417239b2e070dbbf6392b909224e0b/crates/zed/src/main.rs";
673        assert_eq!(permalink.to_string(), expected_url.to_string())
674    }
675
676    #[test]
677    fn test_build_codeberg_permalink_from_https_url_single_line_selection() {
678        let permalink = build_permalink(BuildPermalinkParams {
679            remote_url: "https://codeberg.org/rajveermalviya/zed.git",
680            sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
681            path: "crates/zed/src/main.rs",
682            selection: Some(Point::new(6, 1)..Point::new(6, 10)),
683        })
684        .unwrap();
685
686        let expected_url = "https://codeberg.org/rajveermalviya/zed/src/commit/faa6f979be417239b2e070dbbf6392b909224e0b/crates/zed/src/main.rs#L7";
687        assert_eq!(permalink.to_string(), expected_url.to_string())
688    }
689
690    #[test]
691    fn test_build_codeberg_permalink_from_https_url_multi_line_selection() {
692        let permalink = build_permalink(BuildPermalinkParams {
693            remote_url: "https://codeberg.org/rajveermalviya/zed.git",
694            sha: "faa6f979be417239b2e070dbbf6392b909224e0b",
695            path: "crates/zed/src/main.rs",
696            selection: Some(Point::new(23, 1)..Point::new(47, 10)),
697        })
698        .unwrap();
699
700        let expected_url = "https://codeberg.org/rajveermalviya/zed/src/commit/faa6f979be417239b2e070dbbf6392b909224e0b/crates/zed/src/main.rs#L24-L48";
701        assert_eq!(permalink.to_string(), expected_url.to_string())
702    }
703}