mirror of
https://github.com/martinvonz/jj.git
synced 2025-01-20 03:20:08 +00:00
diff: return matching hunk contents from all inputs
We're likely to use the right (or new) context lines in rendered diffs, but it's odd that the hunks iterator choose which context hunk to return. We'll also need both contents to calculate left/right line numbers. Since the hunk content types are the same, I also split enum DiffHunk into { kind, contents } pair.
This commit is contained in:
parent
cacd6a3fd0
commit
383cca4c4d
5 changed files with 154 additions and 96 deletions
|
@ -41,6 +41,7 @@ use jj_lib::copies::CopyOperation;
|
|||
use jj_lib::copies::CopyRecords;
|
||||
use jj_lib::diff::Diff;
|
||||
use jj_lib::diff::DiffHunk;
|
||||
use jj_lib::diff::DiffHunkKind;
|
||||
use jj_lib::files::DiffLineHunkSide;
|
||||
use jj_lib::files::DiffLineIterator;
|
||||
use jj_lib::files::DiffLineNumber;
|
||||
|
@ -469,9 +470,13 @@ fn show_color_words_diff_hunks(
|
|||
let mut emitted = false;
|
||||
|
||||
for hunk in line_diff.hunks() {
|
||||
match hunk {
|
||||
DiffHunk::Matching(content) => contexts.push(content),
|
||||
DiffHunk::Different(contents) => {
|
||||
match hunk.kind {
|
||||
DiffHunkKind::Matching => {
|
||||
// TODO: add support for unmatched contexts
|
||||
debug_assert!(hunk.contents.iter().all_equal());
|
||||
contexts.push(hunk.contents[0]);
|
||||
}
|
||||
DiffHunkKind::Different => {
|
||||
let num_after = if emitted { options.context } else { 0 };
|
||||
line_number = show_color_words_context_lines(
|
||||
formatter,
|
||||
|
@ -483,7 +488,7 @@ fn show_color_words_diff_hunks(
|
|||
contexts.clear();
|
||||
emitted = true;
|
||||
line_number =
|
||||
show_color_words_diff_lines(formatter, &contents, line_number, options)?;
|
||||
show_color_words_diff_lines(formatter, &hunk.contents, line_number, options)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -656,9 +661,9 @@ fn show_color_words_single_sided_line(
|
|||
fn count_diff_alternation(diff_hunks: &[DiffHunk]) -> usize {
|
||||
diff_hunks
|
||||
.iter()
|
||||
.filter_map(|hunk| match hunk {
|
||||
DiffHunk::Matching(_) => None,
|
||||
DiffHunk::Different(contents) => Some(contents),
|
||||
.filter_map(|hunk| match hunk.kind {
|
||||
DiffHunkKind::Matching => None,
|
||||
DiffHunkKind::Different => Some(&hunk.contents),
|
||||
})
|
||||
// Map non-empty diff side to index (0: left, 1: right)
|
||||
.flat_map(|contents| contents.iter().positions(|content| !content.is_empty()))
|
||||
|
@ -671,9 +676,9 @@ fn count_diff_alternation(diff_hunks: &[DiffHunk]) -> usize {
|
|||
fn split_diff_hunks_by_matching_newline<'a, 'b>(
|
||||
diff_hunks: &'a [DiffHunk<'b>],
|
||||
) -> impl Iterator<Item = &'a [DiffHunk<'b>]> {
|
||||
diff_hunks.split_inclusive(|hunk| match hunk {
|
||||
DiffHunk::Matching(content) => content.contains(&b'\n'),
|
||||
DiffHunk::Different(_) => false,
|
||||
diff_hunks.split_inclusive(|hunk| match hunk.kind {
|
||||
DiffHunkKind::Matching => hunk.contents.iter().all(|content| content.contains(&b'\n')),
|
||||
DiffHunkKind::Different => false,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1144,9 +1149,11 @@ fn unified_diff_hunks<'content>(
|
|||
let diff = Diff::by_line([left_content, right_content]);
|
||||
let mut diff_hunks = diff.hunks().peekable();
|
||||
while let Some(hunk) = diff_hunks.next() {
|
||||
match hunk {
|
||||
DiffHunk::Matching(content) => {
|
||||
let mut lines = content.split_inclusive(|b| *b == b'\n').fuse();
|
||||
match hunk.kind {
|
||||
DiffHunkKind::Matching => {
|
||||
// TODO: add support for unmatched contexts
|
||||
debug_assert!(hunk.contents.iter().all_equal());
|
||||
let mut lines = hunk.contents[0].split_inclusive(|b| *b == b'\n').fuse();
|
||||
if !current_hunk.lines.is_empty() {
|
||||
// The previous hunk line should be either removed/added.
|
||||
current_hunk.extend_context_lines(lines.by_ref().take(num_context_lines));
|
||||
|
@ -1172,9 +1179,9 @@ fn unified_diff_hunks<'content>(
|
|||
// The next hunk should be of DiffHunk::Different type if any.
|
||||
current_hunk.extend_context_lines(before_lines.into_iter().rev());
|
||||
}
|
||||
DiffHunk::Different(contents) => {
|
||||
DiffHunkKind::Different => {
|
||||
let (left_lines, right_lines) =
|
||||
unzip_diff_hunks_to_lines(Diff::by_word(contents).hunks());
|
||||
unzip_diff_hunks_to_lines(Diff::by_word(hunk.contents).hunks());
|
||||
current_hunk.extend_removed_lines(left_lines);
|
||||
current_hunk.extend_added_lines(right_lines);
|
||||
}
|
||||
|
@ -1200,9 +1207,12 @@ where
|
|||
let mut right_tokens: DiffTokenVec<'content> = vec![];
|
||||
|
||||
for hunk in diff_hunks {
|
||||
match hunk.borrow() {
|
||||
DiffHunk::Matching(content) => {
|
||||
for token in content.split_inclusive(|b| *b == b'\n') {
|
||||
let hunk = hunk.borrow();
|
||||
match hunk.kind {
|
||||
DiffHunkKind::Matching => {
|
||||
// TODO: add support for unmatched contexts
|
||||
debug_assert!(hunk.contents.iter().all_equal());
|
||||
for token in hunk.contents[0].split_inclusive(|b| *b == b'\n') {
|
||||
left_tokens.push((DiffTokenType::Matching, token));
|
||||
right_tokens.push((DiffTokenType::Matching, token));
|
||||
if token.ends_with(b"\n") {
|
||||
|
@ -1211,8 +1221,8 @@ where
|
|||
}
|
||||
}
|
||||
}
|
||||
DiffHunk::Different(contents) => {
|
||||
let [left, right] = contents[..]
|
||||
DiffHunkKind::Different => {
|
||||
let [left, right] = hunk.contents[..]
|
||||
.try_into()
|
||||
.expect("hunk should have exactly two inputs");
|
||||
for token in left.split_inclusive(|b| *b == b'\n') {
|
||||
|
@ -1437,10 +1447,10 @@ fn get_diff_stat(
|
|||
let mut added = 0;
|
||||
let mut removed = 0;
|
||||
for hunk in diff.hunks() {
|
||||
match hunk {
|
||||
DiffHunk::Matching(_) => {}
|
||||
DiffHunk::Different(contents) => {
|
||||
let [left, right] = contents.try_into().unwrap();
|
||||
match hunk.kind {
|
||||
DiffHunkKind::Matching => {}
|
||||
DiffHunkKind::Different => {
|
||||
let [left, right] = hunk.contents.try_into().unwrap();
|
||||
removed += left.split_inclusive(|b| *b == b'\n').count();
|
||||
added += right.split_inclusive(|b| *b == b'\n').count();
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ use jj_lib::conflicts::materialize_merge_result;
|
|||
use jj_lib::conflicts::materialize_tree_value;
|
||||
use jj_lib::conflicts::MaterializedTreeValue;
|
||||
use jj_lib::diff::Diff;
|
||||
use jj_lib::diff::DiffHunk;
|
||||
use jj_lib::diff::DiffHunkKind;
|
||||
use jj_lib::files;
|
||||
use jj_lib::files::MergeResult;
|
||||
use jj_lib::matchers::Matcher;
|
||||
|
@ -253,8 +253,10 @@ fn make_diff_sections(
|
|||
let diff = Diff::by_line([left_contents.as_bytes(), right_contents.as_bytes()]);
|
||||
let mut sections = Vec::new();
|
||||
for hunk in diff.hunks() {
|
||||
match hunk {
|
||||
DiffHunk::Matching(text) => {
|
||||
match hunk.kind {
|
||||
DiffHunkKind::Matching => {
|
||||
debug_assert!(hunk.contents.iter().all_equal());
|
||||
let text = hunk.contents[0];
|
||||
let text =
|
||||
std::str::from_utf8(text).map_err(|err| BuiltinToolError::DecodeUtf8 {
|
||||
source: err,
|
||||
|
@ -267,7 +269,8 @@ fn make_diff_sections(
|
|||
.collect(),
|
||||
});
|
||||
}
|
||||
DiffHunk::Different(sides) => {
|
||||
DiffHunkKind::Different => {
|
||||
let sides = &hunk.contents;
|
||||
assert_eq!(sides.len(), 2, "only two inputs were provided to the diff");
|
||||
let left_side =
|
||||
std::str::from_utf8(sides[0]).map_err(|err| BuiltinToolError::DecodeUtf8 {
|
||||
|
|
|
@ -40,6 +40,7 @@ use crate::copies::CopiesTreeDiffEntry;
|
|||
use crate::copies::CopiesTreeDiffEntryPath;
|
||||
use crate::diff::Diff;
|
||||
use crate::diff::DiffHunk;
|
||||
use crate::diff::DiffHunkKind;
|
||||
use crate::files;
|
||||
use crate::files::MergeResult;
|
||||
use crate::merge::Merge;
|
||||
|
@ -73,19 +74,20 @@ static CONFLICT_MARKER_REGEX: once_cell::sync::Lazy<Regex> = once_cell::sync::La
|
|||
|
||||
fn write_diff_hunks(hunks: &[DiffHunk], file: &mut dyn Write) -> std::io::Result<()> {
|
||||
for hunk in hunks {
|
||||
match hunk {
|
||||
DiffHunk::Matching(content) => {
|
||||
for line in content.split_inclusive(|b| *b == b'\n') {
|
||||
match hunk.kind {
|
||||
DiffHunkKind::Matching => {
|
||||
debug_assert!(hunk.contents.iter().all_equal());
|
||||
for line in hunk.contents[0].split_inclusive(|b| *b == b'\n') {
|
||||
file.write_all(b" ")?;
|
||||
file.write_all(line)?;
|
||||
}
|
||||
}
|
||||
DiffHunk::Different(content) => {
|
||||
for line in content[0].split_inclusive(|b| *b == b'\n') {
|
||||
DiffHunkKind::Different => {
|
||||
for line in hunk.contents[0].split_inclusive(|b| *b == b'\n') {
|
||||
file.write_all(b"-")?;
|
||||
file.write_all(line)?;
|
||||
}
|
||||
for line in content[1].split_inclusive(|b| *b == b'\n') {
|
||||
for line in hunk.contents[1].split_inclusive(|b| *b == b'\n') {
|
||||
file.write_all(b"+")?;
|
||||
file.write_all(line)?;
|
||||
}
|
||||
|
@ -333,9 +335,9 @@ pub fn materialize_merge_result(
|
|||
fn diff_size(hunks: &[DiffHunk]) -> usize {
|
||||
hunks
|
||||
.iter()
|
||||
.map(|hunk| match hunk {
|
||||
DiffHunk::Matching(_) => 0,
|
||||
DiffHunk::Different(slices) => slices.iter().map(|slice| slice.len()).sum(),
|
||||
.map(|hunk| match hunk.kind {
|
||||
DiffHunkKind::Matching => 0,
|
||||
DiffHunkKind::Different => hunk.contents.iter().map(|content| content.len()).sum(),
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
|
|
137
lib/src/diff.rs
137
lib/src/diff.rs
|
@ -721,6 +721,14 @@ impl<'input> Diff<'input> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns contents at the unchanged `range`.
|
||||
fn hunk_at<'a>(&'a self, range: &'a UnchangedRange) -> impl Iterator<Item = &'input BStr> + 'a {
|
||||
itertools::chain(
|
||||
iter::once(&self.base_input[range.base.clone()]),
|
||||
iter::zip(&self.other_inputs, &range.others).map(|(input, r)| &input[r.clone()]),
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns contents between the `previous` ends and the `current` starts.
|
||||
fn hunk_between<'a>(
|
||||
&'a self,
|
||||
|
@ -803,24 +811,38 @@ impl<'input> Diff<'input> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub enum DiffHunk<'input> {
|
||||
Matching(&'input BStr),
|
||||
Different(Vec<&'input BStr>),
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct DiffHunk<'input> {
|
||||
pub kind: DiffHunkKind,
|
||||
pub contents: Vec<&'input BStr>,
|
||||
}
|
||||
|
||||
impl<'input> DiffHunk<'input> {
|
||||
pub fn matching<T: AsRef<[u8]> + ?Sized>(content: &'input T) -> Self {
|
||||
DiffHunk::Matching(BStr::new(content))
|
||||
pub fn matching<T: AsRef<[u8]> + ?Sized + 'input>(
|
||||
contents: impl IntoIterator<Item = &'input T>,
|
||||
) -> Self {
|
||||
DiffHunk {
|
||||
kind: DiffHunkKind::Matching,
|
||||
contents: contents.into_iter().map(BStr::new).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn different<T: AsRef<[u8]> + ?Sized + 'input>(
|
||||
contents: impl IntoIterator<Item = &'input T>,
|
||||
) -> Self {
|
||||
DiffHunk::Different(contents.into_iter().map(BStr::new).collect())
|
||||
DiffHunk {
|
||||
kind: DiffHunkKind::Different,
|
||||
contents: contents.into_iter().map(BStr::new).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum DiffHunkKind {
|
||||
Matching,
|
||||
Different,
|
||||
}
|
||||
|
||||
pub struct DiffHunkIterator<'diff, 'input> {
|
||||
diff: &'diff Diff<'input>,
|
||||
previous: UnchangedRange,
|
||||
|
@ -835,21 +857,22 @@ impl<'diff, 'input> Iterator for DiffHunkIterator<'diff, 'input> {
|
|||
loop {
|
||||
if !self.unchanged_emitted {
|
||||
self.unchanged_emitted = true;
|
||||
if !self.previous.base.is_empty() {
|
||||
return Some(DiffHunk::Matching(
|
||||
&self.diff.base_input[self.previous.base.clone()],
|
||||
));
|
||||
let contents = self.diff.hunk_at(&self.previous).collect_vec();
|
||||
if contents.iter().any(|content| !content.is_empty()) {
|
||||
let kind = DiffHunkKind::Matching;
|
||||
return Some(DiffHunk { kind, contents });
|
||||
}
|
||||
}
|
||||
if let Some(current) = self.unchanged_iter.next() {
|
||||
let slices = self
|
||||
let contents = self
|
||||
.diff
|
||||
.hunk_between(&self.previous, current)
|
||||
.collect_vec();
|
||||
self.previous = current.clone();
|
||||
self.unchanged_emitted = false;
|
||||
if slices.iter().any(|slice| !slice.is_empty()) {
|
||||
return Some(DiffHunk::Different(slices));
|
||||
if contents.iter().any(|content| !content.is_empty()) {
|
||||
let kind = DiffHunkKind::Different;
|
||||
return Some(DiffHunk { kind, contents });
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
|
@ -1188,7 +1211,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_diff_single_input() {
|
||||
assert_eq!(diff(["abc"]), vec![DiffHunk::matching("abc")]);
|
||||
assert_eq!(diff(["abc"]), vec![DiffHunk::matching(["abc"])]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1228,9 +1251,9 @@ mod tests {
|
|||
assert_eq!(
|
||||
diff(["a b c", "a X c"]),
|
||||
vec![
|
||||
DiffHunk::matching("a "),
|
||||
DiffHunk::matching(["a "].repeat(2)),
|
||||
DiffHunk::different(["b", "X"]),
|
||||
DiffHunk::matching(" c"),
|
||||
DiffHunk::matching([" c"].repeat(2)),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
@ -1240,9 +1263,9 @@ mod tests {
|
|||
assert_eq!(
|
||||
diff(["a b c", "a X c", "a b c"]),
|
||||
vec![
|
||||
DiffHunk::matching("a "),
|
||||
DiffHunk::matching(["a "].repeat(3)),
|
||||
DiffHunk::different(["b", "X", "b"]),
|
||||
DiffHunk::matching(" c"),
|
||||
DiffHunk::matching([" c"].repeat(3)),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
@ -1252,9 +1275,9 @@ mod tests {
|
|||
assert_eq!(
|
||||
diff(["a b c", "a X c", "a c X"]),
|
||||
vec![
|
||||
DiffHunk::matching("a "),
|
||||
DiffHunk::matching(["a "].repeat(3)),
|
||||
DiffHunk::different(["b ", "X ", ""]),
|
||||
DiffHunk::matching("c"),
|
||||
DiffHunk::matching(["c"].repeat(3)),
|
||||
DiffHunk::different(["", "", " X"]),
|
||||
]
|
||||
);
|
||||
|
@ -1271,9 +1294,9 @@ mod tests {
|
|||
assert_eq!(
|
||||
diff.hunks().collect_vec(),
|
||||
vec![
|
||||
DiffHunk::matching("a\nb\nc\n"),
|
||||
DiffHunk::matching(["a\nb\nc\n"].repeat(2)),
|
||||
DiffHunk::different(["d\n", "X\n"]),
|
||||
DiffHunk::matching("e\nf\ng"),
|
||||
DiffHunk::matching(["e\nf\ng"].repeat(2)),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
@ -1291,9 +1314,9 @@ mod tests {
|
|||
assert_eq!(
|
||||
diff(["a z", "a S z"]),
|
||||
vec![
|
||||
DiffHunk::matching("a "),
|
||||
DiffHunk::matching(["a "].repeat(2)),
|
||||
DiffHunk::different(["", "S "]),
|
||||
DiffHunk::matching("z"),
|
||||
DiffHunk::matching(["z"].repeat(2)),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
@ -1303,11 +1326,11 @@ mod tests {
|
|||
assert_eq!(
|
||||
diff(["a R R S S z", "a S S R R z"]),
|
||||
vec![
|
||||
DiffHunk::matching("a "),
|
||||
DiffHunk::matching(["a "].repeat(2)),
|
||||
DiffHunk::different(["R R ", ""]),
|
||||
DiffHunk::matching("S S "),
|
||||
DiffHunk::matching(["S S "].repeat(2)),
|
||||
DiffHunk::different(["", "R R "]),
|
||||
DiffHunk::matching("z")
|
||||
DiffHunk::matching(["z"].repeat(2))
|
||||
],
|
||||
);
|
||||
}
|
||||
|
@ -1320,19 +1343,19 @@ mod tests {
|
|||
"a r r x q y z q b y q x r r c",
|
||||
]),
|
||||
vec![
|
||||
DiffHunk::matching("a "),
|
||||
DiffHunk::matching(["a "].repeat(2)),
|
||||
DiffHunk::different(["q", "r"]),
|
||||
DiffHunk::matching(" "),
|
||||
DiffHunk::matching([" "].repeat(2)),
|
||||
DiffHunk::different(["", "r "]),
|
||||
DiffHunk::matching("x q y "),
|
||||
DiffHunk::matching(["x q y "].repeat(2)),
|
||||
DiffHunk::different(["q ", ""]),
|
||||
DiffHunk::matching("z q b "),
|
||||
DiffHunk::matching(["z q b "].repeat(2)),
|
||||
DiffHunk::different(["q ", ""]),
|
||||
DiffHunk::matching("y q x "),
|
||||
DiffHunk::matching(["y q x "].repeat(2)),
|
||||
DiffHunk::different(["q", "r"]),
|
||||
DiffHunk::matching(" "),
|
||||
DiffHunk::matching([" "].repeat(2)),
|
||||
DiffHunk::different(["", "r "]),
|
||||
DiffHunk::matching("c"),
|
||||
DiffHunk::matching(["c"].repeat(2)),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
@ -1346,17 +1369,23 @@ mod tests {
|
|||
}
|
||||
|
||||
assert_eq!(diff(["", "\n"]), vec![DiffHunk::different(["", "\n"])]);
|
||||
assert_eq!(diff(["a\n", " a\r\n"]), vec![DiffHunk::matching("a\n")]);
|
||||
assert_eq!(
|
||||
diff(["a\n", " a\r\n"]),
|
||||
vec![DiffHunk::matching(["a\n", " a\r\n"])]
|
||||
);
|
||||
assert_eq!(
|
||||
diff(["a\n", " a\nb"]),
|
||||
vec![DiffHunk::matching("a\n"), DiffHunk::different(["", "b"])]
|
||||
vec![
|
||||
DiffHunk::matching(["a\n", " a\n"]),
|
||||
DiffHunk::different(["", "b"]),
|
||||
]
|
||||
);
|
||||
|
||||
// No LCS matches, so trim leading/trailing common lines
|
||||
assert_eq!(
|
||||
diff(["a\nc\n", " a\n a\n"]),
|
||||
vec![
|
||||
DiffHunk::matching("a\n"),
|
||||
DiffHunk::matching(["a\n", " a\n"]),
|
||||
DiffHunk::different(["c\n", " a\n"]),
|
||||
]
|
||||
);
|
||||
|
@ -1364,7 +1393,7 @@ mod tests {
|
|||
diff(["c\na\n", " a\n a\n"]),
|
||||
vec![
|
||||
DiffHunk::different(["c\n", " a\n"]),
|
||||
DiffHunk::matching("a\n"),
|
||||
DiffHunk::matching(["a\n", " a\n"]),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
@ -1379,7 +1408,10 @@ mod tests {
|
|||
|
||||
assert_eq!(diff(["", "\n"]), vec![DiffHunk::different(["", "\n"])]);
|
||||
// whitespace at line end is ignored
|
||||
assert_eq!(diff(["a\n", "a\r\n"]), vec![DiffHunk::matching("a\n")]);
|
||||
assert_eq!(
|
||||
diff(["a\n", "a\r\n"]),
|
||||
vec![DiffHunk::matching(["a\n", "a\r\n"])]
|
||||
);
|
||||
// but whitespace at line start isn't
|
||||
assert_eq!(
|
||||
diff(["a\n", " a\n"]),
|
||||
|
@ -1387,7 +1419,10 @@ mod tests {
|
|||
);
|
||||
assert_eq!(
|
||||
diff(["a\n", "a \nb"]),
|
||||
vec![DiffHunk::matching("a\n"), DiffHunk::different(["", "b"])]
|
||||
vec![
|
||||
DiffHunk::matching(["a\n", "a \n"]),
|
||||
DiffHunk::different(["", "b"]),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1405,11 +1440,11 @@ mod tests {
|
|||
" pub fn write_fmt(&mut self, fmt: fmt::Arguments<\'_>) -> io::Result<()> {\n self.styler().write_fmt(fmt)\n"
|
||||
]),
|
||||
vec![
|
||||
DiffHunk::matching(" pub fn write_fmt(&mut self, fmt: fmt::Arguments<\'_>) "),
|
||||
DiffHunk::matching([" pub fn write_fmt(&mut self, fmt: fmt::Arguments<\'_>) "].repeat(2)),
|
||||
DiffHunk::different(["", "-> io::Result<()> "]),
|
||||
DiffHunk::matching("{\n self.styler().write_fmt(fmt)"),
|
||||
DiffHunk::matching(["{\n self.styler().write_fmt(fmt)"].repeat(2)),
|
||||
DiffHunk::different([".unwrap()", ""]),
|
||||
DiffHunk::matching("\n")
|
||||
DiffHunk::matching(["\n"].repeat(2))
|
||||
]
|
||||
);
|
||||
}
|
||||
|
@ -1560,19 +1595,19 @@ int main(int argc, char **argv)
|
|||
"##,
|
||||
]),
|
||||
vec![
|
||||
DiffHunk::matching("/*\n * GIT - The information manager from hell\n *\n * Copyright (C) Linus Torvalds, 2005\n */\n#include \"#cache.h\"\n\n"),
|
||||
DiffHunk::matching(["/*\n * GIT - The information manager from hell\n *\n * Copyright (C) Linus Torvalds, 2005\n */\n#include \"#cache.h\"\n\n"].repeat(2)),
|
||||
DiffHunk::different(["", "static void create_directories(const char *path)\n{\n\tint len = strlen(path);\n\tchar *buf = malloc(len + 1);\n\tconst char *slash = path;\n\n\twhile ((slash = strchr(slash+1, \'/\')) != NULL) {\n\t\tlen = slash - path;\n\t\tmemcpy(buf, path, len);\n\t\tbuf[len] = 0;\n\t\tmkdir(buf, 0700);\n\t}\n}\n\nstatic int create_file(const char *path)\n{\n\tint fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, 0600);\n\tif (fd < 0) {\n\t\tif (errno == ENOENT) {\n\t\t\tcreate_directories(path);\n\t\t\tfd = open(path, O_WRONLY | O_TRUNC | O_CREAT, 0600);\n\t\t}\n\t}\n\treturn fd;\n}\n\n"]),
|
||||
DiffHunk::matching("static int unpack(unsigned char *sha1)\n{\n\tvoid *buffer;\n\tunsigned long size;\n\tchar type[20];\n\n\tbuffer = read_sha1_file(sha1, type, &size);\n\tif (!buffer)\n\t\tusage(\"unable to read sha1 file\");\n\tif (strcmp(type, \"tree\"))\n\t\tusage(\"expected a \'tree\' node\");\n\twhile (size) {\n\t\tint len = strlen(buffer)+1;\n\t\tunsigned char *sha1 = buffer + len;\n\t\tchar *path = strchr(buffer, \' \')+1;\n"),
|
||||
DiffHunk::matching(["static int unpack(unsigned char *sha1)\n{\n\tvoid *buffer;\n\tunsigned long size;\n\tchar type[20];\n\n\tbuffer = read_sha1_file(sha1, type, &size);\n\tif (!buffer)\n\t\tusage(\"unable to read sha1 file\");\n\tif (strcmp(type, \"tree\"))\n\t\tusage(\"expected a \'tree\' node\");\n\twhile (size) {\n\t\tint len = strlen(buffer)+1;\n\t\tunsigned char *sha1 = buffer + len;\n\t\tchar *path = strchr(buffer, \' \')+1;\n"].repeat(2)),
|
||||
DiffHunk::different(["", "\t\tchar *data;\n\t\tunsigned long filesize;\n"]),
|
||||
DiffHunk::matching("\t\tunsigned int mode;\n"),
|
||||
DiffHunk::matching(["\t\tunsigned int mode;\n"].repeat(2)),
|
||||
DiffHunk::different(["", "\t\tint fd;\n\n"]),
|
||||
DiffHunk::matching("\t\tif (size < len + 20 || sscanf(buffer, \"%o\", &mode) != 1)\n\t\t\tusage(\"corrupt \'tree\' file\");\n\t\tbuffer = sha1 + 20;\n\t\tsize -= len + 20;\n\t\t"),
|
||||
DiffHunk::matching(["\t\tif (size < len + 20 || sscanf(buffer, \"%o\", &mode) != 1)\n\t\t\tusage(\"corrupt \'tree\' file\");\n\t\tbuffer = sha1 + 20;\n\t\tsize -= len + 20;\n\t\t"].repeat(2)),
|
||||
DiffHunk::different(["printf(\"%o %s (%s)\\n\", mode, path,", "data ="]),
|
||||
DiffHunk::matching(" "),
|
||||
DiffHunk::matching([" "].repeat(2)),
|
||||
DiffHunk::different(["sha1_to_hex", "read_sha1_file"]),
|
||||
DiffHunk::matching("(sha1"),
|
||||
DiffHunk::matching(["(sha1"].repeat(2)),
|
||||
DiffHunk::different([")", ", type, &filesize);\n\t\tif (!data || strcmp(type, \"blob\"))\n\t\t\tusage(\"tree file refers to bad file data\");\n\t\tfd = create_file(path);\n\t\tif (fd < 0)\n\t\t\tusage(\"unable to create file\");\n\t\tif (write(fd, data, filesize) != filesize)\n\t\t\tusage(\"unable to write file\");\n\t\tfchmod(fd, mode);\n\t\tclose(fd);\n\t\tfree(data"]),
|
||||
DiffHunk::matching(");\n\t}\n\treturn 0;\n}\n\nint main(int argc, char **argv)\n{\n\tint fd;\n\tunsigned char sha1[20];\n\n\tif (argc != 2)\n\t\tusage(\"read-tree <key>\");\n\tif (get_sha1_hex(argv[1], sha1) < 0)\n\t\tusage(\"read-tree <key>\");\n\tsha1_file_directory = getenv(DB_ENVIRONMENT);\n\tif (!sha1_file_directory)\n\t\tsha1_file_directory = DEFAULT_DB_ENVIRONMENT;\n\tif (unpack(sha1) < 0)\n\t\tusage(\"unpack failed\");\n\treturn 0;\n}\n"),
|
||||
DiffHunk::matching([");\n\t}\n\treturn 0;\n}\n\nint main(int argc, char **argv)\n{\n\tint fd;\n\tunsigned char sha1[20];\n\n\tif (argc != 2)\n\t\tusage(\"read-tree <key>\");\n\tif (get_sha1_hex(argv[1], sha1) < 0)\n\t\tusage(\"read-tree <key>\");\n\tsha1_file_directory = getenv(DB_ENVIRONMENT);\n\tif (!sha1_file_directory)\n\t\tsha1_file_directory = DEFAULT_DB_ENVIRONMENT;\n\tif (unpack(sha1) < 0)\n\t\tusage(\"unpack failed\");\n\treturn 0;\n}\n"].repeat(2)),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
|
|
@ -21,9 +21,11 @@ use std::mem;
|
|||
|
||||
use bstr::BStr;
|
||||
use bstr::BString;
|
||||
use itertools::Itertools as _;
|
||||
|
||||
use crate::diff::Diff;
|
||||
use crate::diff::DiffHunk;
|
||||
use crate::diff::DiffHunkKind;
|
||||
use crate::merge::trivial_merge;
|
||||
use crate::merge::Merge;
|
||||
|
||||
|
@ -130,8 +132,12 @@ where
|
|||
let Some(hunk) = self.diff_hunks.next() else {
|
||||
break;
|
||||
};
|
||||
match hunk.borrow() {
|
||||
DiffHunk::Matching(text) => {
|
||||
let hunk = hunk.borrow();
|
||||
match hunk.kind {
|
||||
DiffHunkKind::Matching => {
|
||||
// TODO: add support for unmatched contexts?
|
||||
debug_assert!(hunk.contents.iter().all_equal());
|
||||
let text = hunk.contents[0];
|
||||
let lines = text.split_inclusive(|b| *b == b'\n').map(BStr::new);
|
||||
for line in lines {
|
||||
self.current_line.hunks.push((DiffLineHunkSide::Both, line));
|
||||
|
@ -142,8 +148,8 @@ where
|
|||
}
|
||||
}
|
||||
}
|
||||
DiffHunk::Different(contents) => {
|
||||
let [left_text, right_text] = contents[..]
|
||||
DiffHunkKind::Different => {
|
||||
let [left_text, right_text] = hunk.contents[..]
|
||||
.try_into()
|
||||
.expect("hunk should have exactly two inputs");
|
||||
let left_lines = left_text.split_inclusive(|b| *b == b'\n').map(BStr::new);
|
||||
|
@ -201,11 +207,13 @@ fn merge_hunks(diff: &Diff, num_diffs: usize) -> MergeResult {
|
|||
let mut resolved_hunk = BString::new(vec![]);
|
||||
let mut merge_hunks: Vec<Merge<BString>> = vec![];
|
||||
for diff_hunk in diff.hunks() {
|
||||
match diff_hunk {
|
||||
DiffHunk::Matching(content) => {
|
||||
resolved_hunk.extend_from_slice(content);
|
||||
match diff_hunk.kind {
|
||||
DiffHunkKind::Matching => {
|
||||
debug_assert!(diff_hunk.contents.iter().all_equal());
|
||||
resolved_hunk.extend_from_slice(diff_hunk.contents[0]);
|
||||
}
|
||||
DiffHunk::Different(parts) => {
|
||||
DiffHunkKind::Different => {
|
||||
let parts = &diff_hunk.contents;
|
||||
if let Some(resolved) = trivial_merge(&parts[..num_diffs], &parts[num_diffs..]) {
|
||||
resolved_hunk.extend_from_slice(resolved);
|
||||
} else {
|
||||
|
|
Loading…
Reference in a new issue