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 conflict_marker_length: usize,
124 style: ConflictStyle,
126 algorithm: Algorithm,
127}
128
129impl MergeOptions {
130 pub fn new() -> Self {
137 Self {
138 conflict_marker_length: DEFAULT_CONFLICT_MARKER_LENGTH,
139 style: ConflictStyle::Diff3,
140 algorithm: Algorithm::Histogram,
141 }
142 }
143
144 pub fn set_conflict_marker_length(&mut self, conflict_marker_length: usize) -> &mut Self {
146 self.conflict_marker_length = conflict_marker_length;
147 self
148 }
149
150 pub fn set_conflict_style(&mut self, style: ConflictStyle) -> &mut Self {
152 self.style = style;
153 self
154 }
155
156 pub fn set_algorithm(&mut self, algorithm: Algorithm) -> &mut Self {
159 self.algorithm = algorithm;
160 self
161 }
162
163 pub fn merge<'a>(
167 &self,
168 ancestor: &'a str,
169 ours: &'a str,
170 theirs: &'a str,
171 ) -> Result<String, String> {
172 let input = InternedMergeInput::new(ancestor, ours, theirs);
173 let ancestor_lines: Vec<_> = lines_with_terminator(ancestor).collect();
174 let our_lines: Vec<_> = lines_with_terminator(ours).collect();
175 let their_lines: Vec<_> = lines_with_terminator(theirs).collect();
176
177 let mut opts = DiffOptions::new();
178 opts.set_algorithm(self.algorithm);
179
180 let our_solution = opts.diff_tokens(&input.base, &input.left, input.interner.num_tokens());
181 let their_solution =
182 opts.diff_tokens(&input.base, &input.right, input.interner.num_tokens());
183
184 let merged = merge_solutions(&our_solution, &their_solution);
185 let mut merge = diff3_range_to_merge_range(&merged);
186
187 cleanup_conflicts(&mut merge);
188
189 output_result(
190 &ancestor_lines,
191 &our_lines,
192 &their_lines,
193 &merge,
194 self.conflict_marker_length,
195 self.style,
196 )
197 }
198
199 pub fn merge_bytes<'a>(
201 &self,
202 ancestor: &'a [u8],
203 ours: &'a [u8],
204 theirs: &'a [u8],
205 ) -> Result<Vec<u8>, Vec<u8>> {
206 let input = InternedMergeInput::new(ancestor, ours, theirs);
207 let ancestor_lines: Vec<_> = byte_lines_with_terminator(ancestor).collect();
208 let our_lines: Vec<_> = byte_lines_with_terminator(ours).collect();
209 let their_lines: Vec<_> = byte_lines_with_terminator(theirs).collect();
210
211 let mut opts = DiffOptions::new();
212 opts.set_algorithm(self.algorithm);
213
214 let our_solution = opts.diff_tokens(&input.base, &input.left, input.interner.num_tokens());
215 let their_solution =
216 opts.diff_tokens(&input.base, &input.right, input.interner.num_tokens());
217
218 let merged = merge_solutions(&our_solution, &their_solution);
219 let mut merge = diff3_range_to_merge_range(&merged);
220
221 cleanup_conflicts(&mut merge);
222
223 output_result_bytes(
224 &ancestor_lines,
225 &our_lines,
226 &their_lines,
227 &merge,
228 self.conflict_marker_length,
229 self.style,
230 )
231 }
232}
233
234impl Default for MergeOptions {
235 fn default() -> Self {
236 Self::new()
237 }
238}
239
240pub fn merge<'a>(ancestor: &'a str, ours: &'a str, theirs: &'a str) -> Result<String, String> {
292 MergeOptions::default().merge(ancestor, ours, theirs)
293}
294
295pub fn merge_bytes<'a>(
297 ancestor: &'a [u8],
298 ours: &'a [u8],
299 theirs: &'a [u8],
300) -> Result<Vec<u8>, Vec<u8>> {
301 MergeOptions::default().merge_bytes(ancestor, ours, theirs)
302}
303
304fn merge_solutions<'ancestor, 'ours, 'theirs, T: ?Sized + SliceLike>(
305 our_solution: &[DiffRange<'ancestor, 'ours, T>],
306 their_solution: &[DiffRange<'ancestor, 'theirs, T>],
307) -> Vec<Diff3Range<'ancestor, 'ours, 'theirs, T>> {
308 let mut our_solution = our_solution.iter().copied();
309 let mut their_solution = their_solution.iter().copied();
310 let mut ours = our_solution.next();
311 let mut theirs = their_solution.next();
312
313 let mut solution = Vec::new();
314
315 while ours.is_some() || theirs.is_some() {
316 use DiffRange as DR;
317 let merge_range = match (ours, theirs) {
318 (Some(DR::Insert(range)), _) => {
322 ours.take();
323 Diff3Range::Ours(range)
324 }
325 (_, Some(DR::Insert(range))) => {
326 theirs.take();
327 Diff3Range::Theirs(range)
328 }
329
330 (Some(DR::Equal(ancestor1, our_range)), Some(DR::Equal(ancestor2, their_range))) => {
331 assert_eq!(ancestor1.offset(), ancestor2.offset());
332 let len = cmp::min(ancestor1.len(), ancestor2.len());
333
334 shrink_front(&mut ours, len);
335 shrink_front(&mut theirs, len);
336
337 Diff3Range::Equal(
338 ancestor1.slice(..len),
339 our_range.slice(..len),
340 their_range.slice(..len),
341 )
342 }
343
344 (Some(DR::Equal(ancestor1, our_range)), 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::AncestorOurs(ancestor1.slice(..len), our_range.slice(..len))
352 }
353
354 (Some(DR::Delete(ancestor1)), Some(DR::Equal(ancestor2, their_range))) => {
355 assert_eq!(ancestor1.offset(), ancestor2.offset());
356 let len = cmp::min(ancestor1.len(), ancestor2.len());
357
358 shrink_front(&mut ours, len);
359 shrink_front(&mut theirs, len);
360
361 Diff3Range::AncestorTheirs(ancestor2.slice(..len), their_range.slice(..len))
362 }
363
364 (Some(DR::Delete(ancestor1)), Some(DR::Delete(ancestor2))) => {
365 assert_eq!(ancestor1.offset(), ancestor2.offset());
366 let len = cmp::min(ancestor1.len(), ancestor2.len());
367
368 shrink_front(&mut ours, len);
369 shrink_front(&mut theirs, len);
370
371 Diff3Range::Ancestor(ancestor1.slice(..len))
372 }
373
374 (Some(DR::Equal(..) | DR::Delete(..)), None)
378 | (None, Some(DR::Equal(..) | DR::Delete(_)))
379 | (None, None) => unreachable!("Equal/Delete should match up"),
380 };
381
382 solution.push(merge_range);
383
384 if ours.map_or(true, |range| range.is_empty()) {
385 ours = our_solution.next();
386 }
387 if theirs.map_or(true, |range| range.is_empty()) {
388 theirs = their_solution.next();
389 }
390 }
391
392 solution
393}
394
395fn shrink_front<T: ?Sized + SliceLike>(maybe_range: &mut Option<DiffRange<T>>, len: usize) {
396 if let Some(range) = maybe_range {
397 range.shrink_front(len)
398 }
399}
400
401fn diff3_range_to_merge_range<'ancestor, 'ours, 'theirs, T: ?Sized + SliceLike>(
402 solution: &[Diff3Range<'ancestor, 'ours, 'theirs, T>],
403) -> Vec<MergeRange<'ancestor, 'ours, 'theirs, T>> {
404 let mut ancestor: Option<Range<'ancestor, T>> = None;
405 let mut ours: Option<Range<'ours, T>> = None;
406 let mut theirs: Option<Range<'theirs, T>> = None;
407
408 let mut merge = Vec::new();
409
410 for &diff3 in solution {
411 match diff3 {
412 Diff3Range::Equal(ancestor_range, our_range, their_range) => {
413 if let Some(merge_range) =
414 create_merge_range(ancestor.take(), ours.take(), theirs.take())
415 {
416 merge.push(merge_range);
417 }
418 merge.push(MergeRange::Equal(ancestor_range, our_range, their_range));
419 }
420 Diff3Range::Ancestor(range) => {
421 set_or_merge_range(&mut ancestor, range);
422 set_or_merge_range(&mut ours, Range::empty());
423 set_or_merge_range(&mut theirs, Range::empty());
424 }
425 Diff3Range::AncestorOurs(ancestor_range, our_range) => {
426 set_or_merge_range(&mut ancestor, ancestor_range);
427 set_or_merge_range(&mut ours, our_range);
428 }
429 Diff3Range::AncestorTheirs(ancestor_range, their_range) => {
430 set_or_merge_range(&mut ancestor, ancestor_range);
431 set_or_merge_range(&mut theirs, their_range);
432 }
433 Diff3Range::Ours(range) => set_or_merge_range(&mut ours, range),
434 Diff3Range::Theirs(range) => set_or_merge_range(&mut theirs, range),
435 }
436 }
437
438 if let Some(merge_range) = create_merge_range(ancestor.take(), ours.take(), theirs.take()) {
439 merge.push(merge_range);
440 }
441
442 merge
443}
444
445fn set_or_merge_range<'a, T: ?Sized>(range1: &mut Option<Range<'a, T>>, range2: Range<'a, T>) {
446 if let Some(range1) = range1 {
447 if range1.is_empty() {
448 *range1 = range2;
449 } else if !range2.is_empty() {
450 assert_eq!(range1.offset() + range1.len(), range2.offset());
451 range1.grow_down(range2.len());
452 }
453 } else {
454 *range1 = Some(range2);
455 }
456}
457
458fn create_merge_range<'ancestor, 'ours, 'theirs, T: ?Sized + SliceLike>(
459 ancestor: Option<Range<'ancestor, T>>,
460 ours: Option<Range<'ours, T>>,
461 theirs: Option<Range<'theirs, T>>,
462) -> Option<MergeRange<'ancestor, 'ours, 'theirs, T>> {
463 match (ancestor, ours, theirs) {
464 (_, None, None) => None,
465
466 (None, Some(ours), None) => Some(MergeRange::Ours(ours)),
467 (None, None, Some(theirs)) => Some(MergeRange::Theirs(theirs)),
468
469 (ancestor, ours, theirs) => Some(MergeRange::Conflict(
470 ancestor.unwrap_or_default(),
471 ours.unwrap_or_default(),
472 theirs.unwrap_or_default(),
473 )),
474 }
475}
476
477#[allow(clippy::needless_lifetimes)]
478fn cleanup_conflicts<'ancestor, 'ours, 'theirs, T: ?Sized + SliceLike + PartialEq>(
479 solution: &mut [MergeRange<'ancestor, 'ours, 'theirs, T>],
480) {
481 for merge in solution {
484 if let MergeRange::Conflict(ancestor, ours, theirs) = *merge {
485 if ours.as_slice() == theirs.as_slice() {
488 *merge = MergeRange::Both(ours, theirs);
489 } else if ancestor.as_slice() == ours.as_slice() {
492 *merge = MergeRange::Theirs(theirs);
493 } else if ancestor.as_slice() == theirs.as_slice() {
494 *merge = MergeRange::Ours(ours);
495 }
496 }
497 }
498}
499
500fn output_result<'a, T: ?Sized>(
501 ancestor: &[&'a str],
502 ours: &[&'a str],
503 theirs: &[&'a str],
504 merge: &[MergeRange<T>],
505 marker_len: usize,
506 style: ConflictStyle,
507) -> Result<String, String> {
508 let mut conflicts = 0;
509 let mut output = String::new();
510
511 for (range_idx, merge_range) in merge.iter().enumerate() {
512 match merge_range {
513 MergeRange::Equal(range, ..) => {
514 add_lines(&mut output, &ancestor[range.range()]);
515 }
516 MergeRange::Conflict(ancestor_range, ours_range, theirs_range) => {
517 let ancestor = &ancestor[ancestor_range.range()];
518 let ours = &ours[ours_range.range()];
519 let theirs = &theirs[theirs_range.range()];
520
521 if range_idx == merge.len() - 1 {
522 let ancestor_last_ends_with_newline =
523 ancestor.last().map_or(false, |l| l.ends_with('\n'));
524 let ours_last_ends_with_newline =
525 ours.last().map_or(false, |l| l.ends_with('\n'));
526 let theirs_last_ends_with_newline =
527 theirs.last().map_or(false, |l| l.ends_with('\n'));
528
529 let (add_after_lines, add_after_right_marker) =
530 if ancestor_last_ends_with_newline
531 && ours_last_ends_with_newline
532 && theirs_last_ends_with_newline
533 {
534 (false, true)
535 } else {
536 (true, false)
537 };
538
539 add_conflict_marker(&mut output, '<', marker_len, Some("ours"));
540 add_lines(&mut output, ours);
541 if add_after_lines {
542 output.push('\n');
543 }
544
545 if let ConflictStyle::Diff3 = style {
546 add_conflict_marker(&mut output, '|', marker_len, Some("original"));
547 add_lines(&mut output, ancestor);
548 if add_after_lines {
549 output.push('\n');
550 }
551 }
552
553 add_conflict_marker(&mut output, '=', marker_len, None);
554
555 add_lines(&mut output, theirs);
556 if add_after_lines {
557 output.push('\n');
558 }
559 add_conflict_marker(&mut output, '>', marker_len, Some("theirs"));
560 if !add_after_right_marker {
561 output
562 .pop()
563 .expect("a `\n` is always added by the `add_conflict_marker` above");
564 }
565 } else {
566 add_conflict_marker(&mut output, '<', marker_len, Some("ours"));
567 add_lines(&mut output, ours);
568
569 if let ConflictStyle::Diff3 = style {
570 add_conflict_marker(&mut output, '|', marker_len, Some("original"));
571 add_lines(&mut output, ancestor);
572 }
573
574 add_conflict_marker(&mut output, '=', marker_len, None);
575 add_lines(&mut output, theirs);
576 add_conflict_marker(&mut output, '>', marker_len, Some("theirs"));
577 }
578
579 conflicts += 1;
580 }
581 MergeRange::Ours(range) => {
582 add_lines(&mut output, &ours[range.range()]);
583 }
584 MergeRange::Theirs(range) => {
585 add_lines(&mut output, &theirs[range.range()]);
586 }
587 MergeRange::Both(range, _) => {
588 add_lines(&mut output, &ours[range.range()]);
589 }
590 }
591 }
592
593 if conflicts != 0 {
594 Err(output)
595 } else {
596 Ok(output)
597 }
598}
599
600fn add_lines(dest: &mut String, lines: &[&str]) {
601 dest.extend(lines.iter().copied());
602}
603
604fn add_conflict_marker(
605 output: &mut String,
606 marker: char,
607 marker_len: usize,
608 filename: Option<&str>,
609) {
610 for _ in 0..marker_len {
611 output.push(marker);
612 }
613
614 if let Some(filename) = filename {
615 output.push(' ');
616 output.push_str(filename);
617 }
618 output.push('\n');
619}
620
621fn output_result_bytes<'a, T: ?Sized>(
622 ancestor: &[&'a [u8]],
623 ours: &[&'a [u8]],
624 theirs: &[&'a [u8]],
625 merge: &[MergeRange<T>],
626 marker_len: usize,
627 style: ConflictStyle,
628) -> Result<Vec<u8>, Vec<u8>> {
629 let mut conflicts = 0;
630 let mut output: Vec<u8> = Vec::new();
631
632 for (range_idx, merge_range) in merge.iter().enumerate() {
633 match merge_range {
634 MergeRange::Equal(range, ..) => {
635 add_lines_bytes(&mut output, &ancestor[range.range()]);
636 }
637 MergeRange::Conflict(ancestor_range, ours_range, theirs_range) => {
638 let ancestor = &ancestor[ancestor_range.range()];
639 let ours = &ours[ours_range.range()];
640 let theirs = &theirs[theirs_range.range()];
641
642 if range_idx == merge.len() - 1 {
643 let ancestor_last_ends_with_newline =
644 ancestor.last().map_or(false, |l| l.ends_with(b"\n"));
645 let ours_last_ends_with_newline =
646 ours.last().map_or(false, |l| l.ends_with(b"\n"));
647 let theirs_last_ends_with_newline =
648 theirs.last().map_or(false, |l| l.ends_with(b"\n"));
649
650 let (add_after_lines, add_after_right_marker) =
651 if ancestor_last_ends_with_newline
652 && ours_last_ends_with_newline
653 && theirs_last_ends_with_newline
654 {
655 (false, true)
656 } else {
657 (true, false)
658 };
659
660 add_conflict_marker_bytes(&mut output, b'<', marker_len, Some(b"ours"));
661 add_lines_bytes(&mut output, ours);
662 if add_after_lines {
663 output.push(b'\n');
664 }
665
666 if let ConflictStyle::Diff3 = style {
667 add_conflict_marker_bytes(&mut output, b'|', marker_len, Some(b"original"));
668 add_lines_bytes(&mut output, ancestor);
669 if add_after_lines {
670 output.push(b'\n');
671 }
672 }
673
674 add_conflict_marker_bytes(&mut output, b'=', marker_len, None);
675
676 add_lines_bytes(&mut output, theirs);
677 if add_after_lines {
678 output.push(b'\n');
679 }
680 add_conflict_marker_bytes(&mut output, b'>', marker_len, Some(b"theirs"));
681 if !add_after_right_marker {
682 output.pop().expect(
683 "a `\n` is always added by the `add_conflict_marker_bytes` above",
684 );
685 }
686 } else {
687 add_conflict_marker_bytes(&mut output, b'<', marker_len, Some(b"ours"));
688 add_lines_bytes(&mut output, ours);
689
690 if let ConflictStyle::Diff3 = style {
691 add_conflict_marker_bytes(&mut output, b'|', marker_len, Some(b"original"));
692 add_lines_bytes(&mut output, ancestor);
693 }
694
695 add_conflict_marker_bytes(&mut output, b'=', marker_len, None);
696 add_lines_bytes(&mut output, theirs);
697 add_conflict_marker_bytes(&mut output, b'>', marker_len, Some(b"theirs"));
698 }
699
700 conflicts += 1;
701 }
702 MergeRange::Ours(range) => {
703 add_lines_bytes(&mut output, &ours[range.range()]);
704 }
705 MergeRange::Theirs(range) => {
706 add_lines_bytes(&mut output, &theirs[range.range()]);
707 }
708 MergeRange::Both(range, _) => {
709 add_lines_bytes(&mut output, &ours[range.range()]);
710 }
711 }
712 }
713
714 if conflicts != 0 {
715 Err(output)
716 } else {
717 Ok(output)
718 }
719}
720
721fn add_lines_bytes(output: &mut Vec<u8>, lines: &[&[u8]]) {
722 lines.iter().for_each(|line| output.extend_from_slice(line));
723}
724
725fn add_conflict_marker_bytes(
726 output: &mut Vec<u8>,
727 marker: u8,
728 marker_len: usize,
729 filename: Option<&[u8]>,
730) {
731 for _ in 0..marker_len {
732 output.push(marker);
733 }
734
735 if let Some(filename) = filename {
736 output.push(b' ');
737 output.extend_from_slice(filename);
738 }
739 output.push(b'\n');
740}