summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohan Wärlander <johan@snowflake.nu>2022-02-21 18:46:37 +0100
committerGitHub <noreply@github.com>2022-02-21 12:46:37 -0500
commit9572adb744f460ba54fdcdd04d34d951b7b2f205 (patch)
tree53c91ad5eec062d3099a65a405860f053e743fa5
parent6d4a3509cefccafb788c18c029c6b8402f6fd176 (diff)
Enable commit links for GitLab (#972)
* Enable commit links for GitLab
-rw-r--r--src/features/hyperlinks.rs10
-rw-r--r--src/git_config/git_config_entry.rs115
2 files changed, 110 insertions, 15 deletions
diff --git a/src/features/hyperlinks.rs b/src/features/hyperlinks.rs
index b72ff01a..b5f60af9 100644
--- a/src/features/hyperlinks.rs
+++ b/src/features/hyperlinks.rs
@@ -29,7 +29,7 @@ pub fn format_commit_line_with_osc8_commit_hyperlink<'a>(
let commit = captures.get(2).unwrap().as_str();
format_osc8_hyperlink(&commit_link_format.replace("{commit}", commit), commit)
})
- } else if let Some(GitConfigEntry::GitRemote(GitRemoteRepo::GitHubRepo(repo))) =
+ } else if let Some(GitConfigEntry::GitRemote(repo)) =
config.git_config.as_ref().and_then(get_remote_url)
{
COMMIT_LINE_REGEX.replace(line, |captures: &Captures| {
@@ -93,12 +93,12 @@ lazy_static! {
fn format_commit_line_captures_with_osc8_commit_hyperlink(
captures: &Captures,
- github_repo: &str,
+ repo: &GitRemoteRepo,
) -> String {
let commit = captures.get(2).unwrap().as_str();
format!(
"{prefix}{osc}8;;{url}{st}{commit}{osc}8;;{st}{suffix}",
- url = format_github_commit_url(commit, github_repo),
+ url = repo.format_commit_url(commit),
commit = commit,
prefix = captures.get(1).map(|m| m.as_str()).unwrap_or(""),
suffix = captures.get(3).unwrap().as_str(),
@@ -107,10 +107,6 @@ fn format_commit_line_captures_with_osc8_commit_hyperlink(
)
}
-fn format_github_commit_url(commit: &str, github_repo: &str) -> String {
- format!("https://github.com/{}/commit/{}", github_repo, commit)
-}
-
#[cfg(not(target_os = "windows"))]
#[cfg(test)]
pub mod tests {
diff --git a/src/git_config/git_config_entry.rs b/src/git_config/git_config_entry.rs
index 2f026cf2..ff5670a5 100644
--- a/src/git_config/git_config_entry.rs
+++ b/src/git_config/git_config_entry.rs
@@ -14,7 +14,21 @@ pub enum GitConfigEntry {
#[derive(Clone, Debug, PartialEq)]
pub enum GitRemoteRepo {
- GitHubRepo(String),
+ GitHubRepo { repo_slug: String },
+ GitLabRepo { repo_slug: String },
+}
+
+impl GitRemoteRepo {
+ pub fn format_commit_url(&self, commit: &str) -> String {
+ match self {
+ Self::GitHubRepo { repo_slug } => {
+ format!("https://github.com/{}/commit/{}", repo_slug, commit)
+ }
+ Self::GitLabRepo { repo_slug } => {
+ format!("https://gitlab.com/{}/-/commit/{}", repo_slug, commit)
+ }
+ }
+ }
}
lazy_static! {
@@ -32,19 +46,45 @@ lazy_static! {
"
)
.unwrap();
+ static ref GITLAB_REMOTE_URL: Regex = Regex::new(
+ r"(?x)
+ ^
+ (?:https://|git@)? # Support both HTTPS and SSH URLs, SSH URLs optionally omitting the git@
+ gitlab\.com
+ [:/] # This separator differs between SSH and HTTPS URLs
+ ([^/]+) # Capture the user/org name
+ (/.*)? # Capture group(s), if any
+ /
+ (.+?) # Capture the repo name (lazy to avoid consuming '.git' if present)
+ (?:\.git)? # Non-capturing group to consume '.git' if present
+ $
+ "
+ )
+ .unwrap();
}
impl FromStr for GitRemoteRepo {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Some(caps) = GITHUB_REMOTE_URL.captures(s) {
- Ok(Self::GitHubRepo(format!(
- "{user}/{repo}",
- user = caps.get(1).unwrap().as_str(),
- repo = caps.get(2).unwrap().as_str()
- )))
+ Ok(Self::GitHubRepo {
+ repo_slug: format!(
+ "{user}/{repo}",
+ user = caps.get(1).unwrap().as_str(),
+ repo = caps.get(2).unwrap().as_str()
+ ),
+ })
+ } else if let Some(caps) = GITLAB_REMOTE_URL.captures(s) {
+ Ok(Self::GitLabRepo {
+ repo_slug: format!(
+ "{user}{groups}/{repo}",
+ user = caps.get(1).unwrap().as_str(),
+ groups = caps.get(2).map(|x| x.as_str()).unwrap_or_default(),
+ repo = caps.get(3).unwrap().as_str()
+ ),
+ })
} else {
- Err("Not a GitHub repo.".into())
+ Err("Not a GitHub or GitLab repo.".into())
}
}
}
@@ -68,8 +108,67 @@ mod tests {
assert!(parsed.is_ok());
assert_eq!(
parsed.unwrap(),
- GitRemoteRepo::GitHubRepo("dandavison/delta".to_string())
+ GitRemoteRepo::GitHubRepo {
+ repo_slug: "dandavison/delta".to_string()
+ }
+ );
+ }
+ }
+
+ #[test]
+ fn test_format_github_commit_link() {
+ let repo = GitRemoteRepo::GitHubRepo {
+ repo_slug: "dandavison/delta".to_string(),
+ };
+ let commit_hash = "d3b07384d113edec49eaa6238ad5ff00";
+ assert_eq!(
+ repo.format_commit_url(commit_hash),
+ format!("https://github.com/dandavison/delta/commit/{}", commit_hash)
+ )
+ }
+
+ #[test]
+ fn test_parse_gitlab_urls() {
+ let urls = &[
+ (
+ "https://gitlab.com/proj/grp/subgrp/repo.git",
+ "proj/grp/subgrp/repo",
+ ),
+ ("https://gitlab.com/proj/grp/repo.git", "proj/grp/repo"),
+ ("https://gitlab.com/proj/repo.git", "proj/repo"),
+ ("https://gitlab.com/proj/repo", "proj/repo"),
+ (
+ "git@gitlab.com:proj/grp/subgrp/repo.git",
+ "proj/grp/subgrp/repo",
+ ),
+ ("git@gitlab.com:proj/repo.git", "proj/repo"),
+ ("git@gitlab.com:proj/repo", "proj/repo"),
+ ("gitlab.com:proj/grp/repo.git", "proj/grp/repo"),
+ ("gitlab.com:proj/repo.git", "proj/repo"),
+ ("gitlab.com:proj/repo", "proj/repo"),
+ ];
+
+ for (url, expected) in urls {
+ let parsed = GitRemoteRepo::from_str(url);
+ assert!(parsed.is_ok());
+ assert_eq!(
+ parsed.unwrap(),
+ GitRemoteRepo::GitLabRepo {
+ repo_slug: expected.to_string()
+ }
);
}
}
+
+ #[test]
+ fn test_format_gitlab_commit_link() {
+ let repo = GitRemoteRepo::GitLabRepo {
+ repo_slug: "proj/grp/repo".to_string(),
+ };
+ let commit_hash = "d3b07384d113edec49eaa6238ad5ff00";
+ assert_eq!(
+ repo.format_commit_url(commit_hash),
+ format!("https://gitlab.com/proj/grp/repo/-/commit/{}", commit_hash)
+ )
+ }
}