permalink.rs

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