1use imara_diff::sources::{byte_lines_with_terminator, lines_with_terminator};
2
3use crate::{
4 diff::DiffOptions,
5 range::{DiffRange, Range, SliceLike},
6 utils::InternedMergeInput,
7 Algorithm,
8};
9use std::{cmp, fmt};
10
11#[cfg(test)]
12mod tests;
13
14const DEFAULT_CONFLICT_MARKER_LENGTH: usize = 7;
15
16enum Diff3Range<'ancestor, 'ours, 'theirs, T: ?Sized> {
17 Equal(Range<'ancestor, T>, Range<'ours, T>, Range<'theirs, T>),
18 Ancestor(Range<'ancestor, T>),
19 AncestorOurs(Range<'ancestor, T>, Range<'ours, T>),
20 AncestorTheirs(Range<'ancestor, T>, Range<'theirs, T>),
21 Ours(Range<'ours, T>),
22 Theirs(Range<'theirs, T>),
23}
24
25impl<T: ?Sized + fmt::Debug + SliceLike> fmt::Debug for Diff3Range<'_, '_, '_, T> {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 match self {
28 Diff3Range::Equal(range, ..) => write!(f, "Equal: {:?}", range.as_slice()),
29 Diff3Range::Ancestor(range) => write!(f, "Ancestor: {:?}", range.as_slice()),
30 Diff3Range::AncestorOurs(range, ..) => {
31 write!(f, "AncestorOurs: {:?}", range.as_slice())
32 }
33 Diff3Range::AncestorTheirs(range, ..) => {
34 write!(f, "AncestorTheirs: {:?}", range.as_slice())
35 }
36 Diff3Range::Ours(range) => write!(f, "Ours: {:?}", range.as_slice()),
37 Diff3Range::Theirs(range) => write!(f, "Theirs: {:?}", range.as_slice()),
38 }
39 }
40}
41
42impl<T: ?Sized> Copy for Diff3Range<'_, '_, '_, T> {}
43
44impl<T: ?Sized> Clone for Diff3Range<'_, '_, '_, T> {
45 fn clone(&self) -> Self {
46 *self
47 }
48}
49
50enum MergeRange<'ancestor, 'ours, 'theirs, T: ?Sized> {
51 Equal(Range<'ancestor, T>, Range<'ours, T>, Range<'theirs, T>),
52 Conflict(Range<'ancestor, T>, Range<'ours, T>, Range<'theirs, T>),
53 Ours(Range<'ours, T>),
54 Theirs(Range<'theirs, T>),
55 Both(Range<'ours, T>, Range<'theirs, T>),
56}
57
58impl<T: ?Sized + fmt::Debug + SliceLike> fmt::Debug for MergeRange<'_, '_, '_, T> {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 match self {
61 MergeRange::Equal(range, ..) => write!(f, "Equal: {:?}", range.as_slice()),
62 MergeRange::Conflict(ancestor, ours, theirs) => write!(
63 f,
64 "Conflict: ancestor: {:?} ours: {:?} theirs: {:?}",
65 ancestor.as_slice(),
66 ours.as_slice(),
67 theirs.as_slice()
68 ),
69 MergeRange::Ours(range) => write!(f, "Ours: {:?}", range.as_slice()),
70 MergeRange::Theirs(range) => write!(f, "Theirs: {:?}", range.as_slice()),
71 MergeRange::Both(ours, theirs) => write!(
72 f,
73 "Both: ours: {:?} theirs: {:?}",
74 ours.as_slice(),
75 theirs.as_slice()
76 ),
77 }
78 }
79}
80
81impl<T: ?Sized> Copy for MergeRange<'_, '_, '_, T> {}
82
83impl<T: ?Sized> Clone for MergeRange<'_, '_, '_, T> {
84 fn clone(&self) -> Self {
85 *self
86 }
87}
88
89#[derive(Copy, Clone, Debug, Default)]
91pub enum ConflictStyle {
92 Merge,
102
103 #[default]
116 Diff3,
117}
118
119#[derive(Debug)]
121pub struct MergeOptions {
122 pub conflict_marker_length: usize,
124 pub style: ConflictStyle,
126 pub algorithm: Algorithm,
127}
128
129impl MergeOptions {
130 pub fn new() -> Self {
136 Self {
137 conflict_marker_length: DEFAULT_CONFLICT_MARKER_LENGTH,
138 style: ConflictStyle::Diff3,
139 algorithm: Algorithm::Histogram,
140 }
141 }
142
143 pub fn merge<'a>(
147 &self,
148 ancestor: &'a str,
149 ours: &'a str,
150 theirs: &'a str,
151 ) -> Result<String, String> {
152 let input = InternedMergeInput::new(ancestor, ours, theirs);
153 let ancestor_lines: Vec<_> = lines_with_terminator(ancestor).collect();
154 let our_lines: Vec<_> = lines_with_terminator(ours).collect();
155 let their_lines: Vec<_> = lines_with_terminator(theirs).collect();
156
157 let mut opts = DiffOptions::new();
158 opts.set_algorithm(self.algorithm);
159
160 let our_solution = opts.diff_tokens(&input.base, &input.left, input.interner.num_tokens());
161 let their_solution =
162 opts.diff_tokens(&input.base, &input.right, input.interner.num_tokens());
163
164 let merged = merge_solutions(&our_solution, &their_solution);
165 let mut merge = diff3_range_to_merge_range(&merged);
166
167 cleanup_conflicts(&mut merge);
168
169 output_result(
170 &ancestor_lines,
171 &our_lines,
172 &their_lines,
173 &merge,
174 self.conflict_marker_length,
175 self.style,
176 )
177 }
178
179 pub fn merge_bytes<'a>(
181 &self,
182 ancestor: &'a [u8],
183 ours: &'a [u8],
184 theirs: &'a [u8],
185 ) -> Result<Vec<u8>, Vec<u8>> {
186 let input = InternedMergeInput::new(ancestor, ours, theirs);
187 let ancestor_lines: Vec<_> = byte_lines_with_terminator(ancestor).collect();
188 let our_lines: Vec<_> = byte_lines_with_terminator(ours).collect();
189 let their_lines: Vec<_> = byte_lines_with_terminator(theirs).collect();
190
191 let mut opts = DiffOptions::new();
192 opts.set_algorithm(self.algorithm);
193
194 let our_solution = opts.diff_tokens(&input.base, &input.left, input.interner.num_tokens());
195 let their_solution =
196 opts.diff_tokens(&input.base, &input.right, input.interner.num_tokens());
197
198 let merged = merge_solutions(&our_solution, &their_solution);
199 let mut merge = diff3_range_to_merge_range(&merged);
200
201 cleanup_conflicts(&mut merge);
202
203 output_result_bytes(
204 &ancestor_lines,
205 &our_lines,
206 &their_lines,
207 &merge,
208 self.conflict_marker_length,
209 self.style,
210 )
211 }
212}
213
214impl Default for MergeOptions {
215 fn default() -> Self {
216 Self::new()
217 }
218}
219
220pub fn merge<'a>(ancestor: &'a str, ours: &'a str, theirs: &'a str) -> Result<String, String> {
272 MergeOptions::default().merge(ancestor, ours, theirs)
273}
274
275pub fn merge_bytes<'a>(
277 ancestor: &'a [u8],
278 ours: &'a [u8],
279 theirs: &'a [u8],
280) -> Result<Vec<u8>, Vec<u8>> {
281 MergeOptions::default().merge_bytes(ancestor, ours, theirs)
282}
283
284fn merge_solutions<'ancestor, 'ours, 'theirs, T: ?Sized + SliceLike>(
285 our_solution: &[DiffRange<'ancestor, 'ours, T>],
286 their_solution: &[DiffRange<'ancestor, 'theirs, T>],
287) -> Vec<Diff3Range<'ancestor, 'ours, 'theirs, T>> {
288 let mut our_solution = our_solution.iter().copied();
289 let mut their_solution = their_solution.iter().copied();
290 let mut ours = our_solution.next();
291 let mut theirs = their_solution.next();
292
293 let mut solution = Vec::new();
294
295 while ours.is_some() || theirs.is_some() {
296 use DiffRange as DR;
297 let merge_range = match (ours, theirs) {
298 (Some(DR::Insert(range)), _) => {
302 ours.take();
303 Diff3Range::Ours(range)
304 }
305 (_, Some(DR::Insert(range))) => {
306 theirs.take();
307 Diff3Range::Theirs(range)
308 }
309
310 (Some(DR::Equal(ancestor1, our_range)), Some(DR::Equal(ancestor2, their_range))) => {
311 assert_eq!(ancestor1.offset(), ancestor2.offset());
312 let len = cmp::min(ancestor1.len(), ancestor2.len());
313
314 shrink_front(&mut ours, len);
315 shrink_front(&mut theirs, len);
316
317 Diff3Range::Equal(
318 ancestor1.slice(..len),
319 our_range.slice(..len),
320 their_range.slice(..len),
321 )
322 }
323
324 (Some(DR::Equal(ancestor1, our_range)), Some(DR::Delete(ancestor2))) => {
325 assert_eq!(ancestor1.offset(), ancestor2.offset());
326 let len = cmp::min(ancestor1.len(), ancestor2.len());
327
328 shrink_front(&mut ours, len);
329 shrink_front(&mut theirs, len);
330
331 Diff3Range::AncestorOurs(ancestor1.slice(..len), our_range.slice(..len))
332 }
333
334 (Some(DR::Delete(ancestor1)), Some(DR::Equal(ancestor2, their_range))) => {
335 assert_eq!(ancestor1.offset(), ancestor2.offset());
336 let len = cmp::min(ancestor1.len(), ancestor2.len());
337
338 shrink_front(&mut ours, len);
339 shrink_front(&mut theirs, len);
340
341 Diff3Range::AncestorTheirs(ancestor2.slice(..len), their_range.slice(..len))
342 }
343
344 (Some(DR::Delete(ancestor1)), Some(DR::Delete(ancestor2))) => {
345 assert_eq!(ancestor1.offset(), ancestor2.offset());
346 let len = cmp::min(ancestor1.len(), ancestor2.len());
347
348 shrink_front(&mut ours, len);
349 shrink_front(&mut theirs, len);
350
351 Diff3Range::Ancestor(ancestor1.slice(..len))
352 }
353
354 (Some(DR::Equal(..) | DR::Delete(..)), None)
358 | (None, Some(DR::Equal(..) | DR::Delete(_)))
359 | (None, None) => unreachable!("Equal/Delete should match up"),
360 };
361
362 solution.push(merge_range);
363
364 if ours.map_or(true, |range| range.is_empty()) {
365 ours = our_solution.next();
366 }
367 if theirs.map_or(true, |range| range.is_empty()) {
368 theirs = their_solution.next();
369 }
370 }
371
372 solution
373}
374
375fn shrink_front<T: ?Sized + SliceLike>(maybe_range: &mut Option<DiffRange<T>>, len: usize) {
376 if let Some(range) = maybe_range {
377 range.shrink_front(len)
378 }
379}
380
381fn diff3_range_to_merge_range<'ancestor, 'ours, 'theirs, T: ?Sized + SliceLike>(
382 solution: &[Diff3Range<'ancestor, 'ours, 'theirs, T>],
383) -> Vec<MergeRange<'ancestor, 'ours, 'theirs, T>> {
384 let mut ancestor: Option<Range<'ancestor, T>> = None;
385 let mut ours: Option<Range<'ours, T>> = None;
386 let mut theirs: Option<Range<'theirs, T>> = None;
387
388 let mut merge = Vec::new();
389
390 for &diff3 in solution {
391 match diff3 {
392 Diff3Range::Equal(ancestor_range, our_range, their_range) => {
393 if let Some(merge_range) =
394 create_merge_range(ancestor.take(), ours.take(), theirs.take())
395 {
396 merge.push(merge_range);
397 }
398 merge.push(MergeRange::Equal(ancestor_range, our_range, their_range));
399 }
400 Diff3Range::Ancestor(range) => {
401 set_or_merge_range(&mut ancestor, range);
402 set_or_merge_range(&mut ours, Range::empty());
403 set_or_merge_range(&mut theirs, Range::empty());
404 }
405 Diff3Range::AncestorOurs(ancestor_range, our_range) => {
406 set_or_merge_range(&mut ancestor, ancestor_range);
407 set_or_merge_range(&mut ours, our_range);
408 }
409 Diff3Range::AncestorTheirs(ancestor_range, their_range) => {
410 set_or_merge_range(&mut ancestor, ancestor_range);
411 set_or_merge_range(&mut theirs, their_range);
412 }
413 Diff3Range::Ours(range) => set_or_merge_range(&mut ours, range),
414 Diff3Range::Theirs(range) => set_or_merge_range(&mut theirs, range),
415 }
416 }
417
418 if let Some(merge_range) = create_merge_range(ancestor.take(), ours.take(), theirs.take()) {
419 merge.push(merge_range);
420 }
421
422 merge
423}
424
425fn set_or_merge_range<'a, T: ?Sized>(range1: &mut Option<Range<'a, T>>, range2: Range<'a, T>) {
426 if let Some(range1) = range1 {
427 if range1.is_empty() {
428 *range1 = range2;
429 } else if !range2.is_empty() {
430 assert_eq!(range1.offset() + range1.len(), range2.offset());
431 range1.grow_down(range2.len());
432 }
433 } else {
434 *range1 = Some(range2);
435 }
436}
437
438fn create_merge_range<'ancestor, 'ours, 'theirs, T: ?Sized + SliceLike>(
439 ancestor: Option<Range<'ancestor, T>>,
440 ours: Option<Range<'ours, T>>,
441 theirs: Option<Range<'theirs, T>>,
442) -> Option<MergeRange<'ancestor, 'ours, 'theirs, T>> {
443 match (ancestor, ours, theirs) {
444 (_, None, None) => None,
445
446 (None, Some(ours), None) => Some(MergeRange::Ours(ours)),
447 (None, None, Some(theirs)) => Some(MergeRange::Theirs(theirs)),
448
449 (ancestor, ours, theirs) => Some(MergeRange::Conflict(
450 ancestor.unwrap_or_default(),
451 ours.unwrap_or_default(),
452 theirs.unwrap_or_default(),
453 )),
454 }
455}
456
457#[allow(clippy::needless_lifetimes)]
458fn cleanup_conflicts<'ancestor, 'ours, 'theirs, T: ?Sized + SliceLike + PartialEq>(
459 solution: &mut [MergeRange<'ancestor, 'ours, 'theirs, T>],
460) {
461 for merge in solution {
464 if let MergeRange::Conflict(ancestor, ours, theirs) = *merge {
465 if ours.as_slice() == theirs.as_slice() {
468 *merge = MergeRange::Both(ours, theirs);
469 } else if ancestor.as_slice() == ours.as_slice() {
472 *merge = MergeRange::Theirs(theirs);
473 } else if ancestor.as_slice() == theirs.as_slice() {
474 *merge = MergeRange::Ours(ours);
475 }
476 }
477 }
478}
479
480fn output_result<'a, T: ?Sized>(
481 ancestor: &[&'a str],
482 ours: &[&'a str],
483 theirs: &[&'a str],
484 merge: &[MergeRange<T>],
485 marker_len: usize,
486 style: ConflictStyle,
487) -> Result<String, String> {
488 let mut conflicts = 0;
489 let mut output = String::new();
490
491 for (range_idx, merge_range) in merge.iter().enumerate() {
492 match merge_range {
493 MergeRange::Equal(range, ..) => {
494 add_lines(&mut output, &ancestor[range.range()]);
495 }
496 MergeRange::Conflict(ancestor_range, ours_range, theirs_range) => {
497 let ancestor = &ancestor[ancestor_range.range()];
498 let ours = &ours[ours_range.range()];
499 let theirs = &theirs[theirs_range.range()];
500
501 if range_idx == merge.len() - 1 {
502 let ancestor_last_ends_with_newline =
503 ancestor.last().map_or(false, |l| l.ends_with('\n'));
504 let ours_last_ends_with_newline =
505 ours.last().map_or(false, |l| l.ends_with('\n'));
506 let theirs_last_ends_with_newline =
507 theirs.last().map_or(false, |l| l.ends_with('\n'));
508
509 let (add_after_lines, add_after_right_marker) =
510 if ancestor_last_ends_with_newline
511 && ours_last_ends_with_newline
512 && theirs_last_ends_with_newline
513 {
514 (false, true)
515 } else {
516 (true, false)
517 };
518
519 add_conflict_marker(&mut output, '<', marker_len, Some("ours"));
520 add_lines(&mut output, ours);
521 if add_after_lines {
522 output.push('\n');
523 }
524
525 if let ConflictStyle::Diff3 = style {
526 add_conflict_marker(&mut output, '|', marker_len, Some("original"));
527 add_lines(&mut output, ancestor);
528 if add_after_lines {
529 output.push('\n');
530 }
531 }
532
533 add_conflict_marker(&mut output, '=', marker_len, None);
534
535 add_lines(&mut output, theirs);
536 if add_after_lines {
537 output.push('\n');
538 }
539 add_conflict_marker(&mut output, '>', marker_len, Some("theirs"));
540 if !add_after_right_marker {
541 output
542 .pop()
543 .expect("a `\n` is always added by the `add_conflict_marker` above");
544 }
545 } else {
546 add_conflict_marker(&mut output, '<', marker_len, Some("ours"));
547 add_lines(&mut output, ours);
548
549 if let ConflictStyle::Diff3 = style {
550 add_conflict_marker(&mut output, '|', marker_len, Some("original"));
551 add_lines(&mut output, ancestor);
552 }
553
554 add_conflict_marker(&mut output, '=', marker_len, None);
555 add_lines(&mut output, theirs);
556 add_conflict_marker(&mut output, '>', marker_len, Some("theirs"));
557 }
558
559 conflicts += 1;
560 }
561 MergeRange::Ours(range) => {
562 add_lines(&mut output, &ours[range.range()]);
563 }
564 MergeRange::Theirs(range) => {
565 add_lines(&mut output, &theirs[range.range()]);
566 }
567 MergeRange::Both(range, _) => {
568 add_lines(&mut output, &ours[range.range()]);
569 }
570 }
571 }
572
573 if conflicts != 0 {
574 Err(output)
575 } else {
576 Ok(output)
577 }
578}
579
580fn add_lines(dest: &mut String, lines: &[&str]) {
581 dest.extend(lines.iter().copied());
582}
583
584fn add_conflict_marker(
585 output: &mut String,
586 marker: char,
587 marker_len: usize,
588 filename: Option<&str>,
589) {
590 for _ in 0..marker_len {
591 output.push(marker);
592 }
593
594 if let Some(filename) = filename {
595 output.push(' ');
596 output.push_str(filename);
597 }
598 output.push('\n');
599}
600
601fn output_result_bytes<'a, T: ?Sized>(
602 ancestor: &[&'a [u8]],
603 ours: &[&'a [u8]],
604 theirs: &[&'a [u8]],
605 merge: &[MergeRange<T>],
606 marker_len: usize,
607 style: ConflictStyle,
608) -> Result<Vec<u8>, Vec<u8>> {
609 let mut conflicts = 0;
610 let mut output: Vec<u8> = Vec::new();
611
612 for (range_idx, merge_range) in merge.iter().enumerate() {
613 match merge_range {
614 MergeRange::Equal(range, ..) => {
615 add_lines_bytes(&mut output, &ancestor[range.range()]);
616 }
617 MergeRange::Conflict(ancestor_range, ours_range, theirs_range) => {
618 let ancestor = &ancestor[ancestor_range.range()];
619 let ours = &ours[ours_range.range()];
620 let theirs = &theirs[theirs_range.range()];
621
622 if range_idx == merge.len() - 1 {
623 let ancestor_last_ends_with_newline =
624 ancestor.last().map_or(false, |l| l.ends_with(b"\n"));
625 let ours_last_ends_with_newline =
626 ours.last().map_or(false, |l| l.ends_with(b"\n"));
627 let theirs_last_ends_with_newline =
628 theirs.last().map_or(false, |l| l.ends_with(b"\n"));
629
630 let (add_after_lines, add_after_right_marker) =
631 if ancestor_last_ends_with_newline
632 && ours_last_ends_with_newline
633 && theirs_last_ends_with_newline
634 {
635 (false, true)
636 } else {
637 (true, false)
638 };
639
640 add_conflict_marker_bytes(&mut output, b'<', marker_len, Some(b"ours"));
641 add_lines_bytes(&mut output, ours);
642 if add_after_lines {
643 output.push(b'\n');
644 }
645
646 if let ConflictStyle::Diff3 = style {
647 add_conflict_marker_bytes(&mut output, b'|', marker_len, Some(b"original"));
648 add_lines_bytes(&mut output, ancestor);
649 if add_after_lines {
650 output.push(b'\n');
651 }
652 }
653
654 add_conflict_marker_bytes(&mut output, b'=', marker_len, None);
655
656 add_lines_bytes(&mut output, theirs);
657 if add_after_lines {
658 output.push(b'\n');
659 }
660 add_conflict_marker_bytes(&mut output, b'>', marker_len, Some(b"theirs"));
661 if !add_after_right_marker {
662 output.pop().expect(
663 "a `\n` is always added by the `add_conflict_marker_bytes` above",
664 );
665 }
666 } else {
667 add_conflict_marker_bytes(&mut output, b'<', marker_len, Some(b"ours"));
668 add_lines_bytes(&mut output, ours);
669
670 if let ConflictStyle::Diff3 = style {
671 add_conflict_marker_bytes(&mut output, b'|', marker_len, Some(b"original"));
672 add_lines_bytes(&mut output, ancestor);
673 }
674
675 add_conflict_marker_bytes(&mut output, b'=', marker_len, None);
676 add_lines_bytes(&mut output, theirs);
677 add_conflict_marker_bytes(&mut output, b'>', marker_len, Some(b"theirs"));
678 }
679
680 conflicts += 1;
681 }
682 MergeRange::Ours(range) => {
683 add_lines_bytes(&mut output, &ours[range.range()]);
684 }
685 MergeRange::Theirs(range) => {
686 add_lines_bytes(&mut output, &theirs[range.range()]);
687 }
688 MergeRange::Both(range, _) => {
689 add_lines_bytes(&mut output, &ours[range.range()]);
690 }
691 }
692 }
693
694 if conflicts != 0 {
695 Err(output)
696 } else {
697 Ok(output)
698 }
699}
700
701fn add_lines_bytes(output: &mut Vec<u8>, lines: &[&[u8]]) {
702 lines.iter().for_each(|line| output.extend_from_slice(line));
703}
704
705fn add_conflict_marker_bytes(
706 output: &mut Vec<u8>,
707 marker: u8,
708 marker_len: usize,
709 filename: Option<&[u8]>,
710) {
711 for _ in 0..marker_len {
712 output.push(marker);
713 }
714
715 if let Some(filename) = filename {
716 output.push(b' ');
717 output.extend_from_slice(filename);
718 }
719 output.push(b'\n');
720}