summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Davison <dandavison7@gmail.com>2021-04-20 19:12:54 -0400
committerDan Davison <dandavison7@gmail.com>2021-04-24 16:43:24 -0400
commit6630202259cf984a8b20d7cb76086986a107390c (patch)
treea33d182815ad51e675c130a9efebe0f7b4ad7b3f
parenta15b1221ba29ec062590069ea629a0464f81e0a6 (diff)
Output paths relative to current working directory
Fixes #552
-rw-r--r--Cargo.lock7
-rw-r--r--Cargo.toml1
-rw-r--r--src/config.rs2
-rw-r--r--src/delta.rs30
-rw-r--r--src/parse.rs105
5 files changed, 126 insertions, 19 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 96844e15..0db780e7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -272,6 +272,7 @@ dependencies = [
"git2",
"itertools",
"lazy_static",
+ "pathdiff",
"regex",
"shell-words",
"structopt",
@@ -497,6 +498,12 @@ dependencies = [
]
[[package]]
+name = "pathdiff"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877630b3de15c0b64cc52f659345724fbf6bdad9bd9566699fc53688f3c34a34"
+
+[[package]]
name = "percent-encoding"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index b4e003e0..55ec3b0b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -25,6 +25,7 @@ console = "0.14.1"
dirs-next = "2.0.0"
itertools = "0.10.0"
lazy_static = "1.4"
+pathdiff = "0.2.0"
regex = "1.4.6"
shell-words = "1.0.0"
structopt = "0.3.21"
diff --git a/src/config.rs b/src/config.rs
index f822efc2..c6719839 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -23,6 +23,7 @@ pub struct Config {
pub background_color_extends_to_terminal_width: bool,
pub commit_style: Style,
pub color_only: bool,
+ pub cwd_relative_to_repo_root: Option<String>,
pub decorations_width: cli::Width,
pub error_exit_code: i32,
pub file_added_label: String,
@@ -182,6 +183,7 @@ impl From<cli::Opt> for Config {
.background_color_extends_to_terminal_width,
commit_style,
color_only: opt.color_only,
+ cwd_relative_to_repo_root: std::env::var("GIT_PREFIX").ok(),
decorations_width: opt.computed.decorations_width,
error_exit_code: 2, // Use 2 for error because diff uses 0 and 1 for non-error.
file_added_label,
diff --git a/src/delta.rs b/src/delta.rs
index 7d52eae1..869bd4a4 100644
--- a/src/delta.rs
+++ b/src/delta.rs
@@ -113,6 +113,8 @@ impl<'a> StateMachine<'a> {
let mut handled_line = if line.starts_with("commit ") {
self.handle_commit_meta_header_line()?
+ } else if self.state == State::CommitMeta && line.starts_with(" ") {
+ self.handle_diff_stat_line()?
} else if line.starts_with("diff ") {
self.handle_file_meta_diff_line()?
} else if (self.state == State::FileMeta || self.source == Source::DiffUnified)
@@ -226,6 +228,20 @@ impl<'a> StateMachine<'a> {
Ok(())
}
+ fn handle_diff_stat_line(&mut self) -> std::io::Result<bool> {
+ let mut handled_line = false;
+ if let Some(cwd) = self.config.cwd_relative_to_repo_root.as_deref() {
+ if let Some(replacement_line) =
+ parse::relativize_path_in_diff_stat_line(&self.raw_line, cwd)
+ {
+ self.painter.emit()?;
+ writeln!(self.painter.writer, "{}", replacement_line)?;
+ handled_line = true
+ }
+ }
+ Ok(handled_line)
+ }
+
#[allow(clippy::unnecessary_wraps)]
fn handle_file_meta_diff_line(&mut self) -> std::io::Result<bool> {
self.painter.paint_buffered_minus_and_plus_lines();
@@ -237,8 +253,11 @@ impl<'a> StateMachine<'a> {
fn handle_file_meta_minus_line(&mut self) -> std::io::Result<bool> {
let mut handled_line = false;
- let parsed_file_meta_line =
- parse::parse_file_meta_line(&self.line, self.source == Source::GitDiff);
+ let parsed_file_meta_line = parse::parse_file_meta_line(
+ &self.line,
+ self.source == Source::GitDiff,
+ self.config.cwd_relative_to_repo_root.as_deref(),
+ );
self.minus_file = parsed_file_meta_line.0;
self.file_event = parsed_file_meta_line.1;
@@ -271,8 +290,11 @@ impl<'a> StateMachine<'a> {
fn handle_file_meta_plus_line(&mut self) -> std::io::Result<bool> {
let mut handled_line = false;
- let parsed_file_meta_line =
- parse::parse_file_meta_line(&self.line, self.source == Source::GitDiff);
+ let parsed_file_meta_line = parse::parse_file_meta_line(
+ &self.line,
+ self.source == Source::GitDiff,
+ self.config.cwd_relative_to_repo_root.as_deref(),
+ );
self.plus_file = parsed_file_meta_line.0;
self.painter
.set_syntax(parse::get_file_extension_from_file_meta_line_file_path(
diff --git a/src/parse.rs b/src/parse.rs
index 4c2cf233..65ad72f1 100644
--- a/src/parse.rs
+++ b/src/parse.rs
@@ -28,8 +28,12 @@ pub enum FileEvent {
NoEvent,
}
-pub fn parse_file_meta_line(line: &str, git_diff_name: bool) -> (String, FileEvent) {
- match line {
+pub fn parse_file_meta_line(
+ line: &str,
+ git_diff_name: bool,
+ cwd_relative_to_repo_root: Option<&str>,
+) -> (String, FileEvent) {
+ let (mut path, file_event) = match line {
line if line.starts_with("--- ") || line.starts_with("+++ ") => {
let offset = 4;
let file = match &line[offset..] {
@@ -56,7 +60,44 @@ pub fn parse_file_meta_line(line: &str, git_diff_name: bool) -> (String, FileEve
(line[8..].to_string(), FileEvent::Copy) // "copy to ".len()
}
_ => ("".to_string(), FileEvent::NoEvent),
+ };
+
+ if let Some(cwd) = cwd_relative_to_repo_root {
+ if let Some(relative_path) = pathdiff::diff_paths(&path, cwd) {
+ if let Some(relative_path) = relative_path.to_str() {
+ path = relative_path.to_owned();
+ }
+ }
+ }
+
+ (path, file_event)
+}
+
+// A regex to capture the path, and the content from the pipe onwards, in lines
+// like these:
+// " src/delta.rs | 14 ++++++++++----"
+// " src/config.rs | 2 ++"
+lazy_static! {
+ static ref DIFF_STAT_LINE_REGEX: Regex =
+ Regex::new(r" ([^\| ][^\|]+[^\| ]) +(\| +[0-9]+ .+)").unwrap();
+}
+
+pub fn relativize_path_in_diff_stat_line(
+ line: &str,
+ cwd_relative_to_repo_root: &str,
+) -> Option<String> {
+ if let Some(caps) = DIFF_STAT_LINE_REGEX.captures(line) {
+ let path_relative_to_repo_root = caps.get(1).unwrap().as_str();
+ if let Some(relative_path) =
+ pathdiff::diff_paths(path_relative_to_repo_root, cwd_relative_to_repo_root)
+ {
+ if let Some(relative_path) = relative_path.to_str() {
+ let suffix = caps.get(2).unwrap().as_str();
+ return Some(format!(" {:<30}{}", relative_path, suffix,));
+ }
+ }
}
+ return None;
}
pub fn get_file_extension_from_file_meta_line_file_path(path: &str) -> Option<&str> {
@@ -245,21 +286,21 @@ mod tests {
#[test]
fn test_get_file_path_from_git_file_meta_line() {
assert_eq!(
- parse_file_meta_line("--- /dev/null", true),
+ parse_file_meta_line("--- /dev/null", true, None),
("/dev/null".to_string(), FileEvent::Change)
);
for prefix in &DIFF_PREFIXES {
assert_eq!(
- parse_file_meta_line(&format!("--- {}src/delta.rs", prefix), true),
+ parse_file_meta_line(&format!("--- {}src/delta.rs", prefix), true, None),
("src/delta.rs".to_string(), FileEvent::Change)
);
}
assert_eq!(
- parse_file_meta_line("--- src/delta.rs", true),
+ parse_file_meta_line("--- src/delta.rs", true, None),
("src/delta.rs".to_string(), FileEvent::Change)
);
assert_eq!(
- parse_file_meta_line("+++ src/delta.rs", true),
+ parse_file_meta_line("+++ src/delta.rs", true, None),
("src/delta.rs".to_string(), FileEvent::Change)
);
}
@@ -267,23 +308,23 @@ mod tests {
#[test]
fn test_get_file_path_from_git_file_meta_line_containing_spaces() {
assert_eq!(
- parse_file_meta_line("+++ a/my src/delta.rs", true),
+ parse_file_meta_line("+++ a/my src/delta.rs", true, None),
("my src/delta.rs".to_string(), FileEvent::Change)
);
assert_eq!(
- parse_file_meta_line("+++ my src/delta.rs", true),
+ parse_file_meta_line("+++ my src/delta.rs", true, None),
("my src/delta.rs".to_string(), FileEvent::Change)
);
assert_eq!(
- parse_file_meta_line("+++ a/src/my delta.rs", true),
+ parse_file_meta_line("+++ a/src/my delta.rs", true, None),
("src/my delta.rs".to_string(), FileEvent::Change)
);
assert_eq!(
- parse_file_meta_line("+++ a/my src/my delta.rs", true),
+ parse_file_meta_line("+++ a/my src/my delta.rs", true, None),
("my src/my delta.rs".to_string(), FileEvent::Change)
);
assert_eq!(
- parse_file_meta_line("+++ b/my src/my enough/my delta.rs", true),
+ parse_file_meta_line("+++ b/my src/my enough/my delta.rs", true, None),
(
"my src/my enough/my delta.rs".to_string(),
FileEvent::Change
@@ -294,7 +335,7 @@ mod tests {
#[test]
fn test_get_file_path_from_git_file_meta_line_rename() {
assert_eq!(
- parse_file_meta_line("rename from nospace/file2.el", true),
+ parse_file_meta_line("rename from nospace/file2.el", true, None),
("nospace/file2.el".to_string(), FileEvent::Rename)
);
}
@@ -302,7 +343,7 @@ mod tests {
#[test]
fn test_get_file_path_from_git_file_meta_line_rename_containing_spaces() {
assert_eq!(
- parse_file_meta_line("rename from with space/file1.el", true),
+ parse_file_meta_line("rename from with space/file1.el", true, None),
("with space/file1.el".to_string(), FileEvent::Rename)
);
}
@@ -310,11 +351,11 @@ mod tests {
#[test]
fn test_parse_file_meta_line() {
assert_eq!(
- parse_file_meta_line("--- src/delta.rs", false),
+ parse_file_meta_line("--- src/delta.rs", false, None),
("src/delta.rs".to_string(), FileEvent::Change)
);
assert_eq!(
- parse_file_meta_line("+++ src/delta.rs", false),
+ parse_file_meta_line("+++ src/delta.rs", false, None),
("src/delta.rs".to_string(), FileEvent::Change)
);
}
@@ -369,4 +410,38 @@ mod tests {
assert_eq!(line_numbers_and_hunk_lengths[1], (358, 15),);
assert_eq!(line_numbers_and_hunk_lengths[2], (358, 16),);
}
+
+ #[test]
+ fn test_relative_path() {
+ for (path, cwd_relative_to_repo_root, expected) in &[
+ ("file.rs", "", "file.rs"),
+ ("file.rs", "a/", "../file.rs"),
+ ("a/file.rs", "a/", "file.rs"),
+ ("a/b/file.rs", "a", "b/file.rs"),
+ ("c/d/file.rs", "a/b/", "../../c/d/file.rs"),
+ ] {
+ assert_eq!(
+ pathdiff::diff_paths(path, cwd_relative_to_repo_root),
+ Some(expected.into())
+ )
+ }
+ }
+
+ #[test]
+ fn test_diff_stat_line_regex_1() {
+ let caps = DIFF_STAT_LINE_REGEX.captures(" src/delta.rs | 14 ++++++++++----");
+ assert!(caps.is_some());
+ let caps = caps.unwrap();
+ assert_eq!(caps.get(1).unwrap().as_str(), "src/delta.rs");
+ assert_eq!(caps.get(2).unwrap().as_str(), "| 14 ++++++++++----");
+ }
+
+ #[test]
+ fn test_diff_stat_line_regex_2() {
+ let caps = DIFF_STAT_LINE_REGEX.captures(" src/config.rs | 2 ++");
+ assert!(caps.is_some());
+ let caps = caps.unwrap();
+ assert_eq!(caps.get(1).unwrap().as_str(), "src/config.rs");
+ assert_eq!(caps.get(2).unwrap().as_str(), "| 2 ++");
+ }
}