mirror of
https://github.com/dandavison/delta.git
synced 2024-11-23 09:14:43 +03:00
Fix hyperlink absolute paths (#939)
Fix file paths and hyperlinks With this commit the target of a hyperlink should always be an absolute path. This should be true for all file hyperlinks, e.g. - File hyperlink - Hunk header hyperlink - Line number hyperlink Fixes #890
This commit is contained in:
parent
b66d1d3717
commit
3d5b6852a0
@ -25,8 +25,8 @@ use crate::parse_styles;
|
||||
use crate::style;
|
||||
use crate::style::Style;
|
||||
use crate::tests::TESTING;
|
||||
use crate::utils;
|
||||
use crate::utils::bat::output::PagingMode;
|
||||
use crate::utils::cwd::cwd_of_user_shell_process;
|
||||
use crate::utils::regex_replacement::RegexReplacement;
|
||||
use crate::utils::syntect::FromDeltaStyle;
|
||||
use crate::wrapping::WrapConfig;
|
||||
@ -245,9 +245,14 @@ impl From<cli::Opt> for Config {
|
||||
|
||||
let wrap_max_lines_plus1 = adapt_wrap_max_lines_argument(opt.wrap_max_lines);
|
||||
|
||||
#[cfg(not(test))]
|
||||
let cwd_of_delta_process = std::env::current_dir().ok();
|
||||
#[cfg(test)]
|
||||
let cwd_of_delta_process = Some(utils::path::fake_delta_cwd_for_tests());
|
||||
|
||||
let cwd_relative_to_repo_root = std::env::var("GIT_PREFIX").ok();
|
||||
let cwd_of_user_shell_process = cwd_of_user_shell_process(
|
||||
|
||||
let cwd_of_user_shell_process = utils::path::cwd_of_user_shell_process(
|
||||
cwd_of_delta_process.as_ref(),
|
||||
cwd_relative_to_repo_root.as_deref(),
|
||||
);
|
||||
|
@ -1,4 +1,5 @@
|
||||
use std::borrow::Cow;
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
@ -7,6 +8,7 @@ use regex::{Captures, Regex};
|
||||
use crate::config::Config;
|
||||
use crate::features::OptionValueFunction;
|
||||
use crate::git_config::{GitConfig, GitConfigEntry, GitRemoteRepo};
|
||||
|
||||
pub fn make_feature() -> Vec<(String, OptionValueFunction)> {
|
||||
builtin_feature!([
|
||||
(
|
||||
@ -52,27 +54,27 @@ fn get_remote_url(git_config: &GitConfig) -> Option<GitConfigEntry> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a file hyperlink to `path`, displaying `text`.
|
||||
pub fn format_osc8_file_hyperlink<'a>(
|
||||
relative_path: &'a str,
|
||||
/// Create a file hyperlink, displaying `text`.
|
||||
pub fn format_osc8_file_hyperlink<'a, P>(
|
||||
absolute_path: P,
|
||||
line_number: Option<usize>,
|
||||
text: &str,
|
||||
config: &Config,
|
||||
) -> Cow<'a, str> {
|
||||
if let Some(cwd) = &config.cwd_of_user_shell_process {
|
||||
let absolute_path = cwd.join(relative_path);
|
||||
let mut url = config
|
||||
.hyperlinks_file_link_format
|
||||
.replace("{path}", &absolute_path.to_string_lossy());
|
||||
if let Some(n) = line_number {
|
||||
url = url.replace("{line}", &format!("{}", n))
|
||||
} else {
|
||||
url = url.replace("{line}", "")
|
||||
};
|
||||
Cow::from(format_osc8_hyperlink(&url, text))
|
||||
) -> Cow<'a, str>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
P: std::fmt::Debug,
|
||||
{
|
||||
debug_assert!(absolute_path.as_ref().is_absolute());
|
||||
let mut url = config
|
||||
.hyperlinks_file_link_format
|
||||
.replace("{path}", &absolute_path.as_ref().to_string_lossy());
|
||||
if let Some(n) = line_number {
|
||||
url = url.replace("{line}", &format!("{}", n))
|
||||
} else {
|
||||
Cow::from(relative_path)
|
||||
}
|
||||
url = url.replace("{line}", "")
|
||||
};
|
||||
Cow::from(format_osc8_hyperlink(&url, text))
|
||||
}
|
||||
|
||||
fn format_osc8_hyperlink(url: &str, text: &str) -> String {
|
||||
@ -109,57 +111,413 @@ 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 {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub mod unix {
|
||||
use std::path::PathBuf;
|
||||
use std::iter::FromIterator;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::super::*;
|
||||
use crate::tests::integration_test_utils;
|
||||
use super::*;
|
||||
use crate::{
|
||||
tests::integration_test_utils::{self, DeltaTest},
|
||||
utils,
|
||||
};
|
||||
|
||||
fn assert_file_hyperlink_matches(
|
||||
relative_path: &str,
|
||||
expected_hyperlink_path: &str,
|
||||
config: &Config,
|
||||
) {
|
||||
let link_text = "link text";
|
||||
assert_eq!(
|
||||
format_osc8_hyperlink(
|
||||
&PathBuf::from(expected_hyperlink_path).to_string_lossy(),
|
||||
link_text
|
||||
#[test]
|
||||
fn test_paths_and_hyperlinks_user_in_repo_root_dir() {
|
||||
// Expectations are uninfluenced by git's --relative and delta's relative_paths options.
|
||||
let input_type = InputType::GitDiff;
|
||||
let true_location_of_file_relative_to_repo_root = PathBuf::from("a");
|
||||
let git_prefix_env_var = Some("");
|
||||
|
||||
for (delta_relative_paths_option, calling_cmd) in vec![
|
||||
(false, Some("git diff")),
|
||||
(false, Some("git diff --relative")),
|
||||
(true, Some("git diff")),
|
||||
(true, Some("git diff --relative")),
|
||||
] {
|
||||
run_test(FilePathsTestCase {
|
||||
name: &format!(
|
||||
"delta relative_paths={} calling_cmd={:?}",
|
||||
delta_relative_paths_option, calling_cmd
|
||||
),
|
||||
format_osc8_file_hyperlink(relative_path, None, link_text, config)
|
||||
true_location_of_file_relative_to_repo_root:
|
||||
true_location_of_file_relative_to_repo_root.as_path(),
|
||||
git_prefix_env_var,
|
||||
delta_relative_paths_option,
|
||||
input_type,
|
||||
calling_cmd,
|
||||
path_in_delta_input: "a",
|
||||
expected_displayed_path: "a",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_paths_and_hyperlinks_user_in_subdir_file_in_same_subdir() {
|
||||
let input_type = InputType::GitDiff;
|
||||
let true_location_of_file_relative_to_repo_root = PathBuf::from_iter(&["b", "a"]);
|
||||
let git_prefix_env_var = Some("b");
|
||||
|
||||
run_test(FilePathsTestCase {
|
||||
name: "b/a from b",
|
||||
input_type,
|
||||
calling_cmd: Some("git diff"),
|
||||
true_location_of_file_relative_to_repo_root:
|
||||
true_location_of_file_relative_to_repo_root.as_path(),
|
||||
git_prefix_env_var,
|
||||
delta_relative_paths_option: false,
|
||||
path_in_delta_input: "b/a",
|
||||
expected_displayed_path: "b/a",
|
||||
});
|
||||
run_test(FilePathsTestCase {
|
||||
name: "b/a from b",
|
||||
input_type,
|
||||
calling_cmd: Some("git diff --relative"),
|
||||
true_location_of_file_relative_to_repo_root:
|
||||
true_location_of_file_relative_to_repo_root.as_path(),
|
||||
git_prefix_env_var,
|
||||
delta_relative_paths_option: false,
|
||||
path_in_delta_input: "a",
|
||||
// delta saw a and wasn't configured to make any changes
|
||||
expected_displayed_path: "a",
|
||||
});
|
||||
run_test(FilePathsTestCase {
|
||||
name: "b/a from b",
|
||||
input_type,
|
||||
calling_cmd: Some("git diff"),
|
||||
true_location_of_file_relative_to_repo_root:
|
||||
true_location_of_file_relative_to_repo_root.as_path(),
|
||||
git_prefix_env_var,
|
||||
delta_relative_paths_option: true,
|
||||
path_in_delta_input: "b/a",
|
||||
// delta saw b/a and changed it to a
|
||||
expected_displayed_path: "a",
|
||||
});
|
||||
run_test(FilePathsTestCase {
|
||||
name: "b/a from b",
|
||||
input_type,
|
||||
calling_cmd: Some("git diff --relative"),
|
||||
true_location_of_file_relative_to_repo_root:
|
||||
true_location_of_file_relative_to_repo_root.as_path(),
|
||||
git_prefix_env_var,
|
||||
delta_relative_paths_option: true,
|
||||
path_in_delta_input: "a",
|
||||
// delta saw a and didn't change it
|
||||
expected_displayed_path: "a",
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_paths_and_hyperlinks_user_in_subdir_file_in_different_subdir() {
|
||||
let input_type = InputType::GitDiff;
|
||||
let true_location_of_file_relative_to_repo_root = PathBuf::from_iter(&["b", "a"]);
|
||||
let git_prefix_env_var = Some("c");
|
||||
|
||||
run_test(FilePathsTestCase {
|
||||
name: "b/a from c",
|
||||
input_type,
|
||||
calling_cmd: Some("git diff"),
|
||||
delta_relative_paths_option: false,
|
||||
true_location_of_file_relative_to_repo_root:
|
||||
true_location_of_file_relative_to_repo_root.as_path(),
|
||||
git_prefix_env_var,
|
||||
path_in_delta_input: "b/a",
|
||||
expected_displayed_path: "b/a",
|
||||
});
|
||||
run_test(FilePathsTestCase {
|
||||
name: "b/a from c",
|
||||
input_type,
|
||||
calling_cmd: Some("git diff --relative"),
|
||||
delta_relative_paths_option: false,
|
||||
true_location_of_file_relative_to_repo_root:
|
||||
true_location_of_file_relative_to_repo_root.as_path(),
|
||||
git_prefix_env_var,
|
||||
path_in_delta_input: "../b/a",
|
||||
expected_displayed_path: "../b/a",
|
||||
});
|
||||
run_test(FilePathsTestCase {
|
||||
name: "b/a from c",
|
||||
input_type,
|
||||
calling_cmd: Some("git diff"),
|
||||
delta_relative_paths_option: true,
|
||||
true_location_of_file_relative_to_repo_root:
|
||||
true_location_of_file_relative_to_repo_root.as_path(),
|
||||
git_prefix_env_var,
|
||||
path_in_delta_input: "b/a",
|
||||
expected_displayed_path: "../b/a",
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_paths_and_hyperlinks_git_grep_user_in_root() {
|
||||
let input_type = InputType::Grep;
|
||||
let true_location_of_file_relative_to_repo_root = PathBuf::from_iter(&["b", "a.txt"]);
|
||||
|
||||
run_test(FilePathsTestCase {
|
||||
name: "git grep: b/a.txt from root dir",
|
||||
input_type,
|
||||
calling_cmd: Some("git grep foo"),
|
||||
delta_relative_paths_option: false,
|
||||
true_location_of_file_relative_to_repo_root:
|
||||
true_location_of_file_relative_to_repo_root.as_path(),
|
||||
git_prefix_env_var: Some(""),
|
||||
path_in_delta_input: "b/a.txt",
|
||||
expected_displayed_path: "b/a.txt:",
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_paths_and_hyperlinks_grep_user_in_subdir_file_in_same_subdir() {
|
||||
_run_test_grep_user_in_subdir_file_in_same_subdir(Some("git grep foo"));
|
||||
_run_test_grep_user_in_subdir_file_in_same_subdir(Some("rg foo"));
|
||||
}
|
||||
|
||||
fn _run_test_grep_user_in_subdir_file_in_same_subdir(calling_cmd: Option<&str>) {
|
||||
let input_type = InputType::Grep;
|
||||
let true_location_of_file_relative_to_repo_root = PathBuf::from_iter(&["b", "a.txt"]);
|
||||
run_test(FilePathsTestCase {
|
||||
name: "git grep: b/a.txt from b/ dir",
|
||||
input_type,
|
||||
calling_cmd,
|
||||
delta_relative_paths_option: false,
|
||||
true_location_of_file_relative_to_repo_root:
|
||||
true_location_of_file_relative_to_repo_root.as_path(),
|
||||
git_prefix_env_var: Some("b/"),
|
||||
path_in_delta_input: "a.txt",
|
||||
expected_displayed_path: "a.txt:",
|
||||
});
|
||||
}
|
||||
|
||||
const GIT_DIFF_OUTPUT: &str = r#"
|
||||
diff --git a/__path__ b/__path__
|
||||
index 587be6b..975fbec 100644
|
||||
--- a/__path__
|
||||
+++ b/__path__
|
||||
@@ -1 +1 @@
|
||||
-x
|
||||
+y
|
||||
"#;
|
||||
|
||||
const GIT_GREP_OUTPUT: &str = "\
|
||||
__path__: some matching line
|
||||
";
|
||||
|
||||
struct FilePathsTestCase<'a> {
|
||||
// True location of file in repo
|
||||
true_location_of_file_relative_to_repo_root: &'a Path,
|
||||
|
||||
// Git spawns delta from repo root, and stores in this env var the cwd in which the user invoked delta.
|
||||
git_prefix_env_var: Option<&'a str>,
|
||||
|
||||
delta_relative_paths_option: bool,
|
||||
input_type: InputType,
|
||||
calling_cmd: Option<&'a str>,
|
||||
path_in_delta_input: &'a str,
|
||||
expected_displayed_path: &'a str,
|
||||
#[allow(dead_code)]
|
||||
name: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum GitDiffRelative {
|
||||
Yes,
|
||||
No,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum CallingProcess {
|
||||
GitDiff(GitDiffRelative),
|
||||
GitGrep,
|
||||
OtherGrep,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum InputType {
|
||||
GitDiff,
|
||||
Grep,
|
||||
}
|
||||
|
||||
impl<'a> FilePathsTestCase<'a> {
|
||||
pub fn get_args(&self) -> Vec<String> {
|
||||
let mut args = vec![
|
||||
"--navigate", // helps locate the file path in the output
|
||||
"--line-numbers",
|
||||
"--hyperlinks",
|
||||
"--hyperlinks-file-link-format",
|
||||
"{path}",
|
||||
"--grep-file-style",
|
||||
"raw",
|
||||
"--grep-line-number-style",
|
||||
"raw",
|
||||
"--hunk-header-file-style",
|
||||
"raw",
|
||||
"--hunk-header-line-number-style",
|
||||
"raw",
|
||||
"--line-numbers-plus-style",
|
||||
"raw",
|
||||
"--line-numbers-left-style",
|
||||
"raw",
|
||||
"--line-numbers-right-style",
|
||||
"raw",
|
||||
"--line-numbers-left-format",
|
||||
"{nm}અ",
|
||||
"--line-numbers-right-format",
|
||||
"{np}જ",
|
||||
];
|
||||
if self.delta_relative_paths_option {
|
||||
args.push("--relative-paths");
|
||||
}
|
||||
args.iter().map(|s| s.to_string()).collect()
|
||||
}
|
||||
|
||||
pub fn calling_process(&self) -> CallingProcess {
|
||||
match (&self.input_type, self.calling_cmd) {
|
||||
(InputType::GitDiff, Some(s)) if s.starts_with("git diff --relative") => {
|
||||
CallingProcess::GitDiff(GitDiffRelative::Yes)
|
||||
}
|
||||
(InputType::GitDiff, Some(s)) if s.starts_with("git diff") => {
|
||||
CallingProcess::GitDiff(GitDiffRelative::No)
|
||||
}
|
||||
(InputType::Grep, Some(s)) if s.starts_with("git grep") => CallingProcess::GitGrep,
|
||||
(InputType::Grep, Some(s)) if s.starts_with("rg") => CallingProcess::OtherGrep,
|
||||
(InputType::Grep, None) => CallingProcess::GitGrep,
|
||||
_ => panic!(
|
||||
"Unexpected calling spec: {:?} {:?}",
|
||||
self.input_type, self.calling_cmd
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path_in_git_output(&self) -> String {
|
||||
match self.calling_process() {
|
||||
CallingProcess::GitDiff(GitDiffRelative::No) => self
|
||||
.true_location_of_file_relative_to_repo_root
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
CallingProcess::GitDiff(GitDiffRelative::Yes) => pathdiff::diff_paths(
|
||||
self.true_location_of_file_relative_to_repo_root,
|
||||
self.git_prefix_env_var.unwrap(),
|
||||
)
|
||||
.unwrap()
|
||||
.to_string_lossy()
|
||||
.into(),
|
||||
_ => panic!("Unexpected calling process: {:?}", self.calling_process()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the relative path as it would appear in grep output, i.e. accounting for facts
|
||||
/// such as that that the user may have invoked the grep command from a non-root directory
|
||||
/// in the repo.
|
||||
pub fn path_in_grep_output(&self) -> String {
|
||||
use CallingProcess::*;
|
||||
match (self.calling_process(), self.git_prefix_env_var) {
|
||||
(GitGrep, None) => self
|
||||
.true_location_of_file_relative_to_repo_root
|
||||
.to_string_lossy()
|
||||
.into(),
|
||||
(GitGrep, Some(dir)) => {
|
||||
// Delta must have been invoked as core.pager since GIT_PREFIX env var is set.
|
||||
// Note that it is possible that `true_location_of_file_relative_to_repo_root`
|
||||
// is not under `git_prefix_env_var` since one can do things like `git grep foo
|
||||
// ..`
|
||||
pathdiff::diff_paths(self.true_location_of_file_relative_to_repo_root, dir)
|
||||
.unwrap()
|
||||
.to_string_lossy()
|
||||
.into()
|
||||
}
|
||||
(OtherGrep, None) => {
|
||||
// Output from e.g. rg has been piped to delta.
|
||||
// Therefore
|
||||
// (a) the cwd that the delta process reports is the user's shell process cwd
|
||||
// (b) the file in question must be under this cwd
|
||||
// (c) grep output will contain the path relative to this cwd
|
||||
|
||||
// So to compute the path as it would appear in grep output, we could form the
|
||||
// absolute path to the file and strip off the config.cwd_of_delta_process
|
||||
// prefix. The absolute path to the file could be constructed as (absolute path
|
||||
// to repo root) + true_location_of_file_relative_to_repo_root). But I don't
|
||||
// think we know the absolute path to repo root.
|
||||
panic!("Not implemented")
|
||||
}
|
||||
_ => panic!("Not implemented"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expected_hyperlink_path(&self) -> PathBuf {
|
||||
utils::path::fake_delta_cwd_for_tests()
|
||||
.join(self.true_location_of_file_relative_to_repo_root)
|
||||
}
|
||||
}
|
||||
|
||||
fn run_test(test_case: FilePathsTestCase) {
|
||||
let mut config = integration_test_utils::make_config_from_args(
|
||||
&test_case
|
||||
.get_args()
|
||||
.iter()
|
||||
.map(|s| s.as_str())
|
||||
.collect::<Vec<&str>>()
|
||||
.as_slice(),
|
||||
);
|
||||
// The test is simulating delta invoked by git hence these are the same
|
||||
config.cwd_relative_to_repo_root = test_case.git_prefix_env_var.map(|s| s.to_string());
|
||||
config.cwd_of_user_shell_process = utils::path::cwd_of_user_shell_process(
|
||||
config.cwd_of_delta_process.as_ref(),
|
||||
config.cwd_relative_to_repo_root.as_deref(),
|
||||
);
|
||||
let mut delta_test = DeltaTest::with_config(&config);
|
||||
if let Some(cmd) = test_case.calling_cmd {
|
||||
delta_test = delta_test.with_calling_process(cmd)
|
||||
}
|
||||
let delta_test = match test_case.calling_process() {
|
||||
CallingProcess::GitDiff(_) => {
|
||||
assert_eq!(
|
||||
test_case.path_in_delta_input,
|
||||
test_case.path_in_git_output()
|
||||
);
|
||||
delta_test
|
||||
.with_input(&GIT_DIFF_OUTPUT.replace("__path__", test_case.path_in_delta_input))
|
||||
}
|
||||
CallingProcess::GitGrep => {
|
||||
assert_eq!(
|
||||
test_case.path_in_delta_input,
|
||||
test_case.path_in_grep_output()
|
||||
);
|
||||
delta_test.with_input(
|
||||
&GIT_GREP_OUTPUT.replace("__path__", &test_case.path_in_delta_input),
|
||||
)
|
||||
}
|
||||
CallingProcess::OtherGrep => delta_test
|
||||
.with_input(&GIT_GREP_OUTPUT.replace("__path__", &test_case.path_in_delta_input)),
|
||||
};
|
||||
let make_expected_hyperlink = |text| {
|
||||
format_osc8_hyperlink(
|
||||
&PathBuf::from(test_case.expected_hyperlink_path()).to_string_lossy(),
|
||||
text,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_relative_path_file_hyperlink_when_not_child_process_of_git() {
|
||||
// The current process is not a child process of git.
|
||||
// Delta receives a file path 'a'.
|
||||
// The hyperlink should be $cwd/a.
|
||||
let mut config = integration_test_utils::make_config_from_args(&[
|
||||
"--hyperlinks",
|
||||
"--hyperlinks-file-link-format",
|
||||
"{path}",
|
||||
]);
|
||||
config.cwd_of_user_shell_process = Some(PathBuf::from("/some/cwd"));
|
||||
assert_file_hyperlink_matches("a", "/some/cwd/a", &config)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_relative_path_file_hyperlink_when_child_process_of_git() {
|
||||
// The current process is a child process of git.
|
||||
// Delta receives a file path 'a'.
|
||||
// We are in directory b/ relative to the repo root.
|
||||
// The hyperlink should be $repo_root/b/a.
|
||||
let mut config = integration_test_utils::make_config_from_args(&[
|
||||
"--hyperlinks",
|
||||
"--hyperlinks-file-link-format",
|
||||
"{path}",
|
||||
]);
|
||||
config.cwd_of_user_shell_process = Some(PathBuf::from("/some/repo-root/b"));
|
||||
assert_file_hyperlink_matches("a", "/some/repo-root/b/a", &config)
|
||||
};
|
||||
match test_case.calling_process() {
|
||||
CallingProcess::GitDiff(_) => {
|
||||
let line_number = "1";
|
||||
delta_test
|
||||
.inspect_raw()
|
||||
// file hyperlink
|
||||
.expect_raw_contains(&format!(
|
||||
"Δ {}",
|
||||
make_expected_hyperlink(test_case.expected_displayed_path)
|
||||
))
|
||||
// hunk header hyperlink
|
||||
.expect_raw_contains(&format!("• {}", make_expected_hyperlink(line_number)))
|
||||
// line number hyperlink
|
||||
.expect_raw_contains(&format!("અ{}જ", make_expected_hyperlink(line_number)));
|
||||
}
|
||||
CallingProcess::GitGrep | CallingProcess::OtherGrep => {
|
||||
delta_test
|
||||
.inspect_raw()
|
||||
.expect_raw_contains(&make_expected_hyperlink(
|
||||
test_case.expected_displayed_path,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ use crate::features::OptionValueFunction;
|
||||
use crate::format::{self, Align, Placeholder};
|
||||
use crate::minusplus::*;
|
||||
use crate::style::Style;
|
||||
use crate::utils;
|
||||
|
||||
pub fn make_feature() -> Vec<(String, OptionValueFunction)> {
|
||||
builtin_feature!([
|
||||
@ -304,9 +305,13 @@ fn format_line_number(
|
||||
let pad = |n| format::pad(n, width, alignment, precision);
|
||||
match (line_number, config.hyperlinks, plus_file) {
|
||||
(None, _, _) => " ".repeat(width),
|
||||
(Some(n), true, Some(file)) => {
|
||||
hyperlinks::format_osc8_file_hyperlink(file, line_number, &pad(n), config).to_string()
|
||||
}
|
||||
(Some(n), true, Some(file)) => match utils::path::absolute_path(file, config) {
|
||||
Some(absolute_path) => {
|
||||
hyperlinks::format_osc8_file_hyperlink(absolute_path, line_number, &pad(n), config)
|
||||
.to_string()
|
||||
}
|
||||
None => file.to_owned(),
|
||||
},
|
||||
(Some(n), _, _) => pad(n),
|
||||
}
|
||||
}
|
||||
|
@ -6,8 +6,8 @@ use unicode_segmentation::UnicodeSegmentation;
|
||||
use super::draw;
|
||||
use crate::config::Config;
|
||||
use crate::delta::{DiffType, Source, State, StateMachine};
|
||||
use crate::features;
|
||||
use crate::paint::Painter;
|
||||
use crate::{features, utils};
|
||||
|
||||
// https://git-scm.com/docs/git-config#Documentation/git-config.txt-diffmnemonicPrefix
|
||||
const DIFF_PREFIXES: [&str; 6] = ["a/", "b/", "c/", "i/", "o/", "w/"];
|
||||
@ -64,16 +64,12 @@ impl<'a> StateMachine<'a> {
|
||||
}
|
||||
let mut handled_line = false;
|
||||
|
||||
let (path_or_mode, file_event) = parse_diff_header_line(
|
||||
&self.line,
|
||||
self.source == Source::GitDiff,
|
||||
if self.config.relative_paths {
|
||||
self.config.cwd_relative_to_repo_root.as_deref()
|
||||
} else {
|
||||
None
|
||||
},
|
||||
);
|
||||
self.minus_file = path_or_mode;
|
||||
let (path_or_mode, file_event) =
|
||||
parse_diff_header_line(&self.line, self.source == Source::GitDiff);
|
||||
|
||||
self.minus_file = utils::path::relativize_path_maybe(&path_or_mode, self.config)
|
||||
.map(|p| p.to_string_lossy().to_owned().to_string())
|
||||
.unwrap_or(path_or_mode);
|
||||
self.minus_file_event = file_event;
|
||||
|
||||
if self.source == Source::DiffUnified {
|
||||
@ -118,16 +114,12 @@ impl<'a> StateMachine<'a> {
|
||||
return Ok(false);
|
||||
}
|
||||
let mut handled_line = false;
|
||||
let (path_or_mode, file_event) = parse_diff_header_line(
|
||||
&self.line,
|
||||
self.source == Source::GitDiff,
|
||||
if self.config.relative_paths {
|
||||
self.config.cwd_relative_to_repo_root.as_deref()
|
||||
} else {
|
||||
None
|
||||
},
|
||||
);
|
||||
self.plus_file = path_or_mode;
|
||||
let (path_or_mode, file_event) =
|
||||
parse_diff_header_line(&self.line, self.source == Source::GitDiff);
|
||||
|
||||
self.plus_file = utils::path::relativize_path_maybe(&path_or_mode, self.config)
|
||||
.map(|p| p.to_string_lossy().to_owned().to_string())
|
||||
.unwrap_or(path_or_mode);
|
||||
self.plus_file_event = file_event;
|
||||
self.painter
|
||||
.set_syntax(get_file_extension_from_diff_header_line_file_path(
|
||||
@ -187,12 +179,17 @@ impl<'a> StateMachine<'a> {
|
||||
"".to_string()
|
||||
}
|
||||
};
|
||||
let format_file = |file| {
|
||||
if self.config.hyperlinks {
|
||||
features::hyperlinks::format_osc8_file_hyperlink(file, None, file, self.config)
|
||||
} else {
|
||||
Cow::from(file)
|
||||
}
|
||||
let format_file = |file| match (
|
||||
self.config.hyperlinks,
|
||||
utils::path::absolute_path(file, self.config),
|
||||
) {
|
||||
(true, Some(absolute_path)) => features::hyperlinks::format_osc8_file_hyperlink(
|
||||
absolute_path,
|
||||
None,
|
||||
file,
|
||||
self.config,
|
||||
),
|
||||
_ => Cow::from(file),
|
||||
};
|
||||
let label = format_label(&self.config.file_modified_label);
|
||||
let name = get_repeated_file_path_from_diff_line(&self.diff_line)
|
||||
@ -274,12 +271,8 @@ pub fn get_extension(s: &str) -> Option<&str> {
|
||||
.or_else(|| path.file_name().and_then(|s| s.to_str()))
|
||||
}
|
||||
|
||||
fn parse_diff_header_line(
|
||||
line: &str,
|
||||
git_diff_name: bool,
|
||||
relative_path_base: Option<&str>,
|
||||
) -> (String, FileEvent) {
|
||||
let (mut path_or_mode, file_event) = match line {
|
||||
fn parse_diff_header_line(line: &str, git_diff_name: bool) -> (String, FileEvent) {
|
||||
match line {
|
||||
line if line.starts_with("--- ") || line.starts_with("+++ ") => {
|
||||
let offset = 4;
|
||||
let file = _parse_file_path(&line[offset..], git_diff_name);
|
||||
@ -298,17 +291,7 @@ fn parse_diff_header_line(
|
||||
(line[8..].to_string(), FileEvent::Copy) // "copy to ".len()
|
||||
}
|
||||
_ => ("".to_string(), FileEvent::NoEvent),
|
||||
};
|
||||
|
||||
if let Some(base) = relative_path_base {
|
||||
if let Some(relative_path) = pathdiff::diff_paths(&path_or_mode, base) {
|
||||
if let Some(relative_path) = relative_path.to_str() {
|
||||
path_or_mode = relative_path.to_owned();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(path_or_mode, file_event)
|
||||
}
|
||||
|
||||
/// Given input like "diff --git a/src/my file.rs b/src/my file.rs"
|
||||
@ -369,12 +352,12 @@ pub fn get_file_change_description_from_file_paths(
|
||||
plus_file
|
||||
)
|
||||
} else {
|
||||
let format_file = |file| {
|
||||
if config.hyperlinks {
|
||||
features::hyperlinks::format_osc8_file_hyperlink(file, None, file, config)
|
||||
} else {
|
||||
Cow::from(file)
|
||||
let format_file = |file| match (config.hyperlinks, utils::path::absolute_path(file, config))
|
||||
{
|
||||
(true, Some(absolute_path)) => {
|
||||
features::hyperlinks::format_osc8_file_hyperlink(absolute_path, None, file, config)
|
||||
}
|
||||
_ => Cow::from(file),
|
||||
};
|
||||
match (minus_file, plus_file, minus_file_event, plus_file_event) {
|
||||
(minus_file, plus_file, _, _) if minus_file == plus_file => format!(
|
||||
@ -479,21 +462,21 @@ mod tests {
|
||||
#[test]
|
||||
fn test_get_file_path_from_git_diff_header_line() {
|
||||
assert_eq!(
|
||||
parse_diff_header_line("--- /dev/null", true, None),
|
||||
parse_diff_header_line("--- /dev/null", true),
|
||||
("/dev/null".to_string(), FileEvent::Change)
|
||||
);
|
||||
for prefix in &DIFF_PREFIXES {
|
||||
assert_eq!(
|
||||
parse_diff_header_line(&format!("--- {}src/delta.rs", prefix), true, None),
|
||||
parse_diff_header_line(&format!("--- {}src/delta.rs", prefix), true),
|
||||
("src/delta.rs".to_string(), FileEvent::Change)
|
||||
);
|
||||
}
|
||||
assert_eq!(
|
||||
parse_diff_header_line("--- src/delta.rs", true, None),
|
||||
parse_diff_header_line("--- src/delta.rs", true),
|
||||
("src/delta.rs".to_string(), FileEvent::Change)
|
||||
);
|
||||
assert_eq!(
|
||||
parse_diff_header_line("+++ src/delta.rs", true, None),
|
||||
parse_diff_header_line("+++ src/delta.rs", true),
|
||||
("src/delta.rs".to_string(), FileEvent::Change)
|
||||
);
|
||||
}
|
||||
@ -501,23 +484,23 @@ mod tests {
|
||||
#[test]
|
||||
fn test_get_file_path_from_git_diff_header_line_containing_spaces() {
|
||||
assert_eq!(
|
||||
parse_diff_header_line("+++ a/my src/delta.rs", true, None),
|
||||
parse_diff_header_line("+++ a/my src/delta.rs", true),
|
||||
("my src/delta.rs".to_string(), FileEvent::Change)
|
||||
);
|
||||
assert_eq!(
|
||||
parse_diff_header_line("+++ my src/delta.rs", true, None),
|
||||
parse_diff_header_line("+++ my src/delta.rs", true),
|
||||
("my src/delta.rs".to_string(), FileEvent::Change)
|
||||
);
|
||||
assert_eq!(
|
||||
parse_diff_header_line("+++ a/src/my delta.rs", true, None),
|
||||
parse_diff_header_line("+++ a/src/my delta.rs", true),
|
||||
("src/my delta.rs".to_string(), FileEvent::Change)
|
||||
);
|
||||
assert_eq!(
|
||||
parse_diff_header_line("+++ a/my src/my delta.rs", true, None),
|
||||
parse_diff_header_line("+++ a/my src/my delta.rs", true),
|
||||
("my src/my delta.rs".to_string(), FileEvent::Change)
|
||||
);
|
||||
assert_eq!(
|
||||
parse_diff_header_line("+++ b/my src/my enough/my delta.rs", true, None),
|
||||
parse_diff_header_line("+++ b/my src/my enough/my delta.rs", true),
|
||||
(
|
||||
"my src/my enough/my delta.rs".to_string(),
|
||||
FileEvent::Change
|
||||
@ -528,7 +511,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_get_file_path_from_git_diff_header_line_rename() {
|
||||
assert_eq!(
|
||||
parse_diff_header_line("rename from nospace/file2.el", true, None),
|
||||
parse_diff_header_line("rename from nospace/file2.el", true),
|
||||
("nospace/file2.el".to_string(), FileEvent::Rename)
|
||||
);
|
||||
}
|
||||
@ -536,7 +519,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_get_file_path_from_git_diff_header_line_rename_containing_spaces() {
|
||||
assert_eq!(
|
||||
parse_diff_header_line("rename from with space/file1.el", true, None),
|
||||
parse_diff_header_line("rename from with space/file1.el", true),
|
||||
("with space/file1.el".to_string(), FileEvent::Rename)
|
||||
);
|
||||
}
|
||||
@ -544,11 +527,11 @@ mod tests {
|
||||
#[test]
|
||||
fn test_parse_diff_header_line() {
|
||||
assert_eq!(
|
||||
parse_diff_header_line("--- src/delta.rs", false, None),
|
||||
parse_diff_header_line("--- src/delta.rs", false),
|
||||
("src/delta.rs".to_string(), FileEvent::Change)
|
||||
);
|
||||
assert_eq!(
|
||||
parse_diff_header_line("+++ src/delta.rs", false, None),
|
||||
parse_diff_header_line("+++ src/delta.rs", false),
|
||||
("src/delta.rs".to_string(), FileEvent::Change)
|
||||
);
|
||||
}
|
||||
|
@ -399,8 +399,11 @@ pub mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn test_paint_file_path_with_line_number_hyperlinks() {
|
||||
use std::{iter::FromIterator, path::PathBuf};
|
||||
|
||||
use crate::utils;
|
||||
|
||||
// hunk-header-style (by default) includes 'line-number' but not 'file'.
|
||||
// Normally, `paint_file_path_with_line_number` would return a painted line number.
|
||||
// But in this test hyperlinks are activated, and the test ensures that delta.__workdir__ is
|
||||
@ -408,14 +411,21 @@ pub mod tests {
|
||||
// This test confirms that, under those circumstances, `paint_file_path_with_line_number`
|
||||
// returns a hyperlinked file path with line number.
|
||||
|
||||
let mut config =
|
||||
integration_test_utils::make_config_from_args(&["--features", "hyperlinks"]);
|
||||
config.cwd_of_user_shell_process =
|
||||
Some(std::path::PathBuf::from("/some/current/directory"));
|
||||
let config = integration_test_utils::make_config_from_args(&["--features", "hyperlinks"]);
|
||||
let relative_path = PathBuf::from_iter(["some-dir", "some-file"]);
|
||||
|
||||
let result = paint_file_path_with_line_number(Some(3), "some-file", &config);
|
||||
let result =
|
||||
paint_file_path_with_line_number(Some(3), &relative_path.to_string_lossy(), &config);
|
||||
|
||||
assert_eq!(result, "\u{1b}]8;;file:///some/current/directory/some-file\u{1b}\\\u{1b}[34m3\u{1b}[0m\u{1b}]8;;\u{1b}\\");
|
||||
assert_eq!(
|
||||
result,
|
||||
format!(
|
||||
"\u{1b}]8;;file://{}\u{1b}\\\u{1b}[34m3\u{1b}[0m\u{1b}]8;;\u{1b}\\",
|
||||
utils::path::fake_delta_cwd_for_tests()
|
||||
.join(relative_path)
|
||||
.to_string_lossy()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
27
src/paint.rs
27
src/paint.rs
@ -11,7 +11,6 @@ use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
use crate::config::{self, delta_unreachable, Config};
|
||||
use crate::delta::{DiffType, InMergeConflict, MergeParents, State};
|
||||
use crate::edits;
|
||||
use crate::features::hyperlinks;
|
||||
use crate::features::line_numbers::{self, LineNumbersData};
|
||||
use crate::features::side_by_side::ansifill;
|
||||
@ -21,6 +20,7 @@ use crate::minusplus::*;
|
||||
use crate::paint::superimpose_style_sections::superimpose_style_sections;
|
||||
use crate::style::Style;
|
||||
use crate::{ansi, style};
|
||||
use crate::{edits, utils};
|
||||
|
||||
pub type LineSections<'a, S> = Vec<(S, &'a str)>;
|
||||
|
||||
@ -775,7 +775,7 @@ pub fn parse_style_sections<'a>(
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn paint_file_path_with_line_number(
|
||||
line_number: Option<usize>,
|
||||
plus_file: &str,
|
||||
file_path: &str,
|
||||
pad_line_number: bool,
|
||||
separator: &str,
|
||||
terminate_with_separator: bool,
|
||||
@ -785,12 +785,12 @@ pub fn paint_file_path_with_line_number(
|
||||
) -> String {
|
||||
let mut file_with_line_number = Vec::new();
|
||||
if let Some(file_style) = file_style {
|
||||
let plus_file = if let Some(regex_replacement) = &config.file_regex_replacement {
|
||||
regex_replacement.execute(plus_file)
|
||||
let file_path = if let Some(regex_replacement) = &config.file_regex_replacement {
|
||||
regex_replacement.execute(file_path)
|
||||
} else {
|
||||
Cow::from(plus_file)
|
||||
Cow::from(file_path)
|
||||
};
|
||||
file_with_line_number.push(file_style.paint(plus_file))
|
||||
file_with_line_number.push(file_style.paint(file_path))
|
||||
};
|
||||
if let Some(line_number) = line_number {
|
||||
if let Some(line_number_style) = line_number_style {
|
||||
@ -820,16 +820,19 @@ pub fn paint_file_path_with_line_number(
|
||||
}
|
||||
}
|
||||
let file_with_line_number = ansi_term::ANSIStrings(&file_with_line_number).to_string();
|
||||
if config.hyperlinks && !file_with_line_number.is_empty() {
|
||||
hyperlinks::format_osc8_file_hyperlink(
|
||||
plus_file,
|
||||
match if config.hyperlinks && !file_with_line_number.is_empty() {
|
||||
utils::path::absolute_path(file_path, config)
|
||||
} else {
|
||||
None
|
||||
} {
|
||||
Some(absolute_path) => hyperlinks::format_osc8_file_hyperlink(
|
||||
absolute_path,
|
||||
line_number,
|
||||
&file_with_line_number,
|
||||
config,
|
||||
)
|
||||
.into()
|
||||
} else {
|
||||
file_with_line_number
|
||||
.into(),
|
||||
_ => file_with_line_number,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,8 @@ pub fn show_config(config: &config::Config, writer: &mut dyn Write) -> std::io::
|
||||
plus-non-emph-style = {plus_non_emph_style}
|
||||
plus-emph-style = {plus_emph_style}
|
||||
plus-empty-line-marker-style = {plus_empty_line_marker_style}
|
||||
grep-file-style = {grep_file_style}
|
||||
grep-line-number-style = {grep_line_number_style}
|
||||
whitespace-error-style = {whitespace_error_style}
|
||||
blame-palette = {blame_palette}",
|
||||
blame_palette = config
|
||||
@ -44,6 +46,8 @@ pub fn show_config(config: &config::Config, writer: &mut dyn Write) -> std::io::
|
||||
plus_empty_line_marker_style = config.plus_empty_line_marker_style.to_painted_string(),
|
||||
plus_non_emph_style = config.plus_non_emph_style.to_painted_string(),
|
||||
plus_style = config.plus_style.to_painted_string(),
|
||||
grep_file_style = config.grep_file_style.to_painted_string(),
|
||||
grep_line_number_style = config.grep_line_number_style.to_painted_string(),
|
||||
whitespace_error_style = config.whitespace_error_style.to_painted_string(),
|
||||
zero_style = config.zero_style.to_painted_string(),
|
||||
)?;
|
||||
|
@ -244,7 +244,7 @@ impl DeltaTestOutput {
|
||||
pub fn expect_contains(self, expected: &str) -> Self {
|
||||
assert!(
|
||||
self.output.contains(expected),
|
||||
"Output does not contain \"{}\":\n{}",
|
||||
"Output does not contain \"{}\":\n{}\n",
|
||||
expected,
|
||||
delineated_string(&self.output.as_str())
|
||||
);
|
||||
@ -254,7 +254,7 @@ impl DeltaTestOutput {
|
||||
pub fn expect_raw_contains(self, expected: &str) -> Self {
|
||||
assert!(
|
||||
self.raw_output.contains(expected),
|
||||
"Raw output does not contain \"{}\":\n{}",
|
||||
"Raw output does not contain \"{}\":\n{}\n",
|
||||
expected,
|
||||
delineated_string(&self.raw_output.as_str())
|
||||
);
|
||||
@ -264,7 +264,7 @@ impl DeltaTestOutput {
|
||||
pub fn expect_contains_once(self, expected: &str) -> Self {
|
||||
assert!(
|
||||
test_utils::contains_once(&self.output, expected),
|
||||
"Output does not contain \"{}\" exactly once:\n{}",
|
||||
"Output does not contain \"{}\" exactly once:\n{}\n",
|
||||
expected,
|
||||
delineated_string(&self.output.as_str())
|
||||
);
|
||||
|
@ -1,26 +0,0 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Return current working directory of the user's shell process. I.e. the directory which they are
|
||||
/// in when delta exits. This is the directory relative to which the file paths in delta output are
|
||||
/// constructed if they are using either (a) delta's relative-paths option or (b) git's --relative
|
||||
/// flag.
|
||||
pub fn cwd_of_user_shell_process(
|
||||
cwd_of_delta_process: Option<&PathBuf>,
|
||||
cwd_relative_to_repo_root: Option<&str>,
|
||||
) -> Option<PathBuf> {
|
||||
match (cwd_of_delta_process, cwd_relative_to_repo_root) {
|
||||
(Some(cwd), None) => {
|
||||
// We are not a child process of git
|
||||
Some(PathBuf::from(cwd))
|
||||
}
|
||||
(Some(repo_root), Some(cwd_relative_to_repo_root)) => {
|
||||
// We are a child process of git; git spawned us from repo_root and preserved the user's
|
||||
// original cwd in the GIT_PREFIX env var (available as config.cwd_relative_to_repo_root)
|
||||
Some(PathBuf::from(repo_root).join(cwd_relative_to_repo_root))
|
||||
}
|
||||
(None, _) => {
|
||||
// Unexpected
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
pub mod bat;
|
||||
pub mod cwd;
|
||||
pub mod path;
|
||||
pub mod process;
|
||||
pub mod regex_replacement;
|
||||
pub mod syntect;
|
||||
|
109
src/utils/path.rs
Normal file
109
src/utils/path.rs
Normal file
@ -0,0 +1,109 @@
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
|
||||
use crate::config::Config;
|
||||
|
||||
use super::process::calling_process;
|
||||
|
||||
// Infer absolute path to `relative_path`.
|
||||
pub fn absolute_path(relative_path: &str, config: &Config) -> Option<PathBuf> {
|
||||
match (
|
||||
&config.cwd_of_delta_process,
|
||||
&config.cwd_of_user_shell_process,
|
||||
calling_process().paths_in_input_are_relative_to_cwd() || config.relative_paths,
|
||||
) {
|
||||
// Note that if we were invoked by git then cwd_of_delta_process == repo_root
|
||||
(Some(cwd_of_delta_process), _, false) => Some(cwd_of_delta_process.join(relative_path)),
|
||||
(_, Some(cwd_of_user_shell_process), true) => {
|
||||
Some(cwd_of_user_shell_process.join(relative_path))
|
||||
}
|
||||
(Some(cwd_of_delta_process), None, true) => {
|
||||
// This might occur when piping from git to delta?
|
||||
Some(cwd_of_delta_process.join(relative_path))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
.map(normalize_path)
|
||||
}
|
||||
|
||||
/// Relativize path if delta config demands that and paths are not already relativized by git.
|
||||
pub fn relativize_path_maybe(path: &str, config: &Config) -> Option<PathBuf> {
|
||||
if config.relative_paths && !calling_process().paths_in_input_are_relative_to_cwd() {
|
||||
if let Some(base) = config.cwd_relative_to_repo_root.as_deref() {
|
||||
pathdiff::diff_paths(&path, base)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Return current working directory of the user's shell process. I.e. the directory which they are
|
||||
/// in when delta exits. This is the directory relative to which the file paths in delta output are
|
||||
/// constructed if they are using either (a) delta's relative-paths option or (b) git's --relative
|
||||
/// flag.
|
||||
pub fn cwd_of_user_shell_process(
|
||||
cwd_of_delta_process: Option<&PathBuf>,
|
||||
cwd_relative_to_repo_root: Option<&str>,
|
||||
) -> Option<PathBuf> {
|
||||
match (cwd_of_delta_process, cwd_relative_to_repo_root) {
|
||||
(Some(cwd), None) => {
|
||||
// We are not a child process of git
|
||||
Some(PathBuf::from(cwd))
|
||||
}
|
||||
(Some(repo_root), Some(cwd_relative_to_repo_root)) => {
|
||||
// We are a child process of git; git spawned us from repo_root and preserved the user's
|
||||
// original cwd in the GIT_PREFIX env var (available as config.cwd_relative_to_repo_root)
|
||||
Some(PathBuf::from(repo_root).join(cwd_relative_to_repo_root))
|
||||
}
|
||||
(None, _) => {
|
||||
// Unexpected
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copied from
|
||||
// https://github.com/rust-lang/cargo/blob/c6745a3d7fcea3a949c3e13e682b8ddcbd213add/crates/cargo-util/src/paths.rs#L73-L106
|
||||
// as suggested by matklad: https://www.reddit.com/r/rust/comments/hkkquy/comment/fwtw53s/?utm_source=share&utm_medium=web2x&context=3
|
||||
fn normalize_path<P>(path: P) -> PathBuf
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let mut components = path.as_ref().components().peekable();
|
||||
let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
|
||||
components.next();
|
||||
PathBuf::from(c.as_os_str())
|
||||
} else {
|
||||
PathBuf::new()
|
||||
};
|
||||
|
||||
for component in components {
|
||||
match component {
|
||||
Component::Prefix(..) => unreachable!(),
|
||||
Component::RootDir => {
|
||||
ret.push(component.as_os_str());
|
||||
}
|
||||
Component::CurDir => {}
|
||||
Component::ParentDir => {
|
||||
ret.pop();
|
||||
}
|
||||
Component::Normal(c) => {
|
||||
ret.push(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn fake_delta_cwd_for_tests() -> PathBuf {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
PathBuf::from("/fake/delta/cwd")
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
PathBuf::from(r"C:\fake\delta\cwd")
|
||||
}
|
||||
}
|
@ -20,6 +20,18 @@ pub enum CallingProcess {
|
||||
}
|
||||
// TODO: Git blame is currently handled differently
|
||||
|
||||
impl CallingProcess {
|
||||
pub fn paths_in_input_are_relative_to_cwd(&self) -> bool {
|
||||
match self {
|
||||
CallingProcess::GitDiff(cmd) if cmd.long_options.contains("--relative") => true,
|
||||
CallingProcess::GitShow(cmd, _) if cmd.long_options.contains("--relative") => true,
|
||||
CallingProcess::GitLog(cmd) if cmd.long_options.contains("--relative") => true,
|
||||
CallingProcess::GitGrep(_) | CallingProcess::OtherGrep => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct CommandLine {
|
||||
pub long_options: HashSet<String>,
|
||||
|
@ -6,4 +6,4 @@ DELTA="$DELTA_BIN --no-gitconfig --raw --max-line-length 0"
|
||||
|
||||
ANSIFILTER="./etc/bin/ansifilter"
|
||||
GIT_ARGS="log --patch --stat --numstat"
|
||||
diff -u <(git $GIT_ARGS | $ANSIFILTER) <(git $GIT_ARGS | $DELTA | $ANSIFILTER)
|
||||
diff -u <(git $GIT_ARGS | $ANSIFILTER) <(git $GIT_ARGS | DELTA_FEATURES= $DELTA | $ANSIFILTER)
|
||||
|
Loading…
Reference in New Issue
Block a user