Skip to content

Commit

Permalink
Allow multiple hyperlinks per line
Browse files Browse the repository at this point in the history
Previously only the last commit was linked
  • Loading branch information
th1000s committed Nov 14, 2024
1 parent b5031a7 commit a3a7842
Showing 1 changed file with 106 additions and 35 deletions.
141 changes: 106 additions & 35 deletions src/features/hyperlinks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::borrow::Cow;
use std::path::Path;

use lazy_static::lazy_static;
use regex::{Captures, Regex};
use regex::{Match, Regex};

use crate::config::Config;
use crate::features::OptionValueFunction;
Expand All @@ -19,6 +19,10 @@ pub fn make_feature() -> Vec<(String, OptionValueFunction)> {
])
}

lazy_static! {
static ref COMMIT_LINE_REGEX: Regex = Regex::new(r"\b[0-9a-f]{8,40}\b").unwrap();
}

#[cfg(test)]
pub fn remote_from_config(_: &Option<&GitConfig>) -> Option<GitRemoteRepo> {
Some(GitRemoteRepo::GitHub {
Expand All @@ -35,22 +39,63 @@ pub fn format_commit_line_with_osc8_commit_hyperlink<'a>(
line: &'a str,
config: &Config,
) -> Cow<'a, str> {
// Given a [START..END]-match and pos = 0, store [pos..START] first, then the hyperlink(match),
// then store pos = END. Repeat while matches exist, and at the end store [pos..].
if let Some(commit_link_format) = &config.hyperlinks_commit_link_format {
COMMIT_LINE_REGEX.replace(line, |captures: &Captures| {
let prefix = captures.get(1).map(|m| m.as_str()).unwrap_or("");
let commit = captures.get(2).map(|m| m.as_str()).unwrap();
let suffix = captures.get(3).map(|m| m.as_str()).unwrap_or("");
let formatted_commit =
format_osc8_hyperlink(&commit_link_format.replace("{commit}", commit), commit);
format!("{prefix}{formatted_commit}{suffix}")
})
let mut matches = COMMIT_LINE_REGEX.find_iter(line);
if let Some(capture) = matches.next() {
let mut pos = 0;
let mut result = String::new();

let mut create_commit_hyperlink = |pre_cap_pos, cap: Match| {
result.push_str(&line[pre_cap_pos..cap.start()]);
let commit = &line[cap.start()..cap.end()];
let formatted_commit =
format_osc8_hyperlink(&commit_link_format.replace("{commit}", commit), commit);
result.push_str(&formatted_commit);
cap.end()
};

pos = create_commit_hyperlink(pos, capture);

for capture in matches {
pos = create_commit_hyperlink(pos, capture);
}

result.push_str(&line[pos..]);
return Cow::from(result);
}
} else if let Some(repo) = remote_from_config(&config.git_config()) {
COMMIT_LINE_REGEX.replace(line, |captures: &Captures| {
format_commit_line_captures_with_osc8_commit_hyperlink(captures, &repo)
})
} else {
Cow::from(line)
let mut matches = COMMIT_LINE_REGEX.find_iter(line);
if let Some(capture) = matches.next() {
let mut pos = 0;
let mut result = String::new();

let mut create_repo_hyperlink = |pre_cap_pos, cap: Match| {
result.push_str(&line[pre_cap_pos..cap.start()]);
let commit = &line[cap.start()..cap.end()];
let formatted_commit = format!(
"{osc}8;;{url}{st}{commit}{osc}8;;{st}",
url = repo.format_commit_url(commit),
commit = commit,
osc = "\x1b]",
st = "\x1b\\"
);
result.push_str(&formatted_commit);
cap.end()
};

pos = create_repo_hyperlink(pos, capture);

for capture in matches {
pos = create_repo_hyperlink(pos, capture);
}

result.push_str(&line[pos..]);
return Cow::from(result);
}
}
Cow::from(line)
}

/// Create a file hyperlink, displaying `text`.
Expand Down Expand Up @@ -86,38 +131,64 @@ fn format_osc8_hyperlink(url: &str, text: &str) -> String {
)
}

lazy_static! {
static ref COMMIT_LINE_REGEX: Regex = Regex::new("(.* )?([0-9a-f]{8,40})(.*)").unwrap();
}

fn format_commit_line_captures_with_osc8_commit_hyperlink(
captures: &Captures,
repo: &GitRemoteRepo,
) -> String {
let commit = captures.get(2).unwrap().as_str();
format!(
"{prefix}{osc}8;;{url}{st}{commit}{osc}8;;{st}{suffix}",
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(),
osc = "\x1b]",
st = "\x1b\\"
)
}

#[cfg(not(target_os = "windows"))]
#[cfg(test)]
pub mod tests {
use std::iter::FromIterator;
use std::path::PathBuf;

use pretty_assertions::assert_eq;

use super::*;

use crate::{
tests::integration_test_utils::{self, DeltaTest},
tests::integration_test_utils::{self, make_config_from_args, DeltaTest},
utils,
};

#[test]
fn test_formatted_hyperlinks() {
let config = make_config_from_args(&["--hyperlinks-commit-link-format", "HERE:{commit}"]);

let line = "This 01234abcdf Hash";
let result = format_commit_line_with_osc8_commit_hyperlink(line, &config);
assert_eq!(
result,
"This \u{1b}]8;;HERE:01234abcdf\u{1b}\\01234abcdf\u{1b}]8;;\u{1b}\\ Hash",
);

let line =
"Another 01234abcdf hash but also this one: dc623b084ad2dd14fe5d90189cacad5d49bfbfd3!";
let result = format_commit_line_with_osc8_commit_hyperlink(line, &config);
assert_eq!("Another \u{1b}]8;;HERE:01234abcdf\u{1b}\\01234abcdf\u{1b}]8;;\u{1b}\\ hash but also this one: \u{1b}]8;;\
HERE:dc623b084ad2dd14fe5d90189cacad5d49bfbfd3\u{1b}\\dc623b084ad2dd14fe5d90189cacad5d49bfbfd3\u{1b}]8;;\u{1b}\\!", result);

let line = "01234abcdf 03043baf30 12abcdef0 6789abcdefg";
let result = format_commit_line_with_osc8_commit_hyperlink(line, &config);
assert_eq!("\u{1b}]8;;HERE:01234abcdf\u{1b}\\01234abcdf\u{1b}]8;;\u{1b}\\ \u{1b}]8;;HERE:03043baf30\u{1b}\\03043baf30\u{1b}]8;;\
\u{1b}\\ \u{1b}]8;;HERE:12abcdef0\u{1b}\\12abcdef0\u{1b}]8;;\u{1b}\\ 6789abcdefg", result);
}

#[test]
fn test_hyperlinks_to_repo() {
let mut config = make_config_from_args(&["--hyperlinks"]);
config.git_config = GitConfig::for_testing();

let line = "This a589ff9debaefdd delta commit";
let result = format_commit_line_with_osc8_commit_hyperlink(line, &config);
assert_eq!(
result,
"This \u{1b}]8;;https://github.com/dandavison/delta/commit/a589ff9debaefdd\u{1b}\\a589ff9debaefdd\u{1b}]8;;\u{1b}\\ delta commit",
);

let line =
"Another a589ff9debaefdd hash but also this one: c5696757c0827349a87daa95415656!";
let result = format_commit_line_with_osc8_commit_hyperlink(line, &config);
assert_eq!(result, "Another \u{1b}]8;;https://github.com/dandavison/delta/commit/a589ff9debaefdd\u{1b}\\a589ff9debaefdd\u{1b}]8;;\u{1b}\\ hash \
but also this one: \u{1b}]8;;https://github.com/dandavison/delta/commit/c5696757c0827349a87daa95415656\u{1b}\\c5696757c0827349a87daa95415656\u{1b}]8;;\
\u{1b}\\!");
}

#[test]
fn test_paths_and_hyperlinks_user_in_repo_root_dir() {
// Expectations are uninfluenced by git's --relative and delta's relative_paths options.
Expand Down

0 comments on commit a3a7842

Please sign in to comment.