Skip to content

Commit 8911d53

Browse files
committed
feat: diff output for --preview
1 parent efb8198 commit 8911d53

File tree

3 files changed

+75
-21
lines changed

3 files changed

+75
-21
lines changed

Cargo.lock

Lines changed: 21 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ ignore = "0.4.20"
3030
ansi_term = "0.12.1"
3131
is-terminal = "0.4.9"
3232
clap = { version = "4.4.3", features = ["derive", "deprecated", "wrap_help"] }
33+
similar = { version = "2.3.0", features = ["inline", "bytes"] }
3334

3435
[dev-dependencies]
3536
assert_cmd = "2.0.12"

src/replacer.rs

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::{utils, Error, Result};
22
use regex::bytes::Regex;
3+
use similar::{TextDiff, ChangeTag};
34
use std::{fs, fs::File, io::prelude::*, path::Path};
45

56
pub(crate) struct Replacer {
@@ -94,28 +95,61 @@ impl Replacer {
9495
&'a self,
9596
content: &[u8],
9697
) -> std::borrow::Cow<'a, [u8]> {
98+
use ansi_term::{Color, Style};
99+
100+
let replaced = self.replace(content);
101+
let diff = TextDiff::from_lines(&*content, &*replaced);
102+
97103
let mut v = Vec::<u8>::new();
98-
let mut captures = self.regex.captures_iter(content);
99-
100-
self.regex.split(content).for_each(|sur_text| {
101-
use regex::bytes::Replacer;
102-
103-
v.extend(sur_text);
104-
if let Some(capture) = captures.next() {
105-
v.extend_from_slice(
106-
ansi_term::Color::Green.prefix().to_string().as_bytes(),
107-
);
108-
if self.is_literal {
109-
regex::bytes::NoExpand(&self.replace_with)
110-
.replace_append(&capture, &mut v);
111-
} else {
112-
(&*self.replace_with).replace_append(&capture, &mut v);
104+
105+
for group in diff.grouped_ops(3).iter() {
106+
for op in group {
107+
for change in diff.iter_inline_changes(op) {
108+
match change.tag() {
109+
ChangeTag::Delete => {
110+
v.extend(Color::Red.prefix().to_string().as_bytes());
111+
let idx = match change.old_index() {
112+
Some(old_idx) => format!("{:>3} ", old_idx).into_bytes(),
113+
None => b" ".to_vec(),
114+
};
115+
v.extend(idx);
116+
v.push(b'-');
117+
},
118+
ChangeTag::Insert => {
119+
v.extend(Color::Green.prefix().to_string().as_bytes());
120+
let idx = match change.new_index() {
121+
Some(new_idx) => format!("{:>3} ", new_idx).into_bytes(),
122+
None => b" ".to_vec(),
123+
};
124+
v.extend(idx);
125+
v.push(b'+');
126+
},
127+
ChangeTag::Equal => {
128+
v.extend(b" ");
129+
},
130+
}
131+
v.push(b' ');
132+
133+
for (emphasized, value) in change.iter_strings_lossy() {
134+
if emphasized {
135+
v.extend(Style::new().bold().paint(value).as_bytes());
136+
} else {
137+
v.extend(value.as_bytes());
138+
}
139+
}
140+
141+
match change.tag() {
142+
ChangeTag::Delete => v.extend(Color::Red.suffix().to_string().as_bytes()),
143+
ChangeTag::Insert => v.extend(Color::Green.suffix().to_string().as_bytes()),
144+
ChangeTag::Equal => (),
145+
}
146+
147+
if change.missing_newline() {
148+
v.push(b'\n');
149+
}
113150
}
114-
v.extend_from_slice(
115-
ansi_term::Color::Green.suffix().to_string().as_bytes(),
116-
);
117151
}
118-
});
152+
}
119153

120154
return std::borrow::Cow::Owned(v);
121155
}

0 commit comments

Comments
 (0)