1use std::ops::Range;
2
3use crate::blob::{
4 builtin_driver::text::{
5 utils::{
6 assure_ends_with_nl, contains_lines, detect_line_ending, detect_line_ending_or_nl, fill_ancestor,
7 hunks_differ_in_diff3, take_intersecting, tokens, write_ancestor, write_conflict_marker, write_hunks,
8 zealously_contract_hunks, CollectHunks, Hunk, Side,
9 },
10 Conflict, ConflictStyle, Labels, Options,
11 },
12 Resolution,
13};
14
15#[allow(clippy::too_many_arguments)]
28pub fn merge<'a>(
29 out: &mut Vec<u8>,
30 input: &mut imara_diff::intern::InternedInput<&'a [u8]>,
31 Labels {
32 ancestor: ancestor_label,
33 current: current_label,
34 other: other_label,
35 }: Labels<'_>,
36 current: &'a [u8],
37 ancestor: &'a [u8],
38 other: &'a [u8],
39 Options {
40 diff_algorithm,
41 conflict,
42 }: Options,
43) -> Resolution {
44 out.clear();
45 input.update_before(tokens(ancestor));
46 input.update_after(tokens(current));
47
48 let hunks = imara_diff::diff(
49 diff_algorithm,
50 input,
51 CollectHunks {
52 side: Side::Current,
53 hunks: Vec::new(),
54 },
55 );
56
57 let current_tokens = std::mem::take(&mut input.after);
58 input.update_after(tokens(other));
59
60 let mut hunks = imara_diff::diff(
61 diff_algorithm,
62 input,
63 CollectHunks {
64 side: Side::Other,
65 hunks,
66 },
67 );
68
69 if hunks.is_empty() {
70 write_ancestor(input, 0, input.before.len(), out);
71 return Resolution::Complete;
72 }
73
74 hunks.sort_by(|a, b| a.before.start.cmp(&b.before.start));
75 let mut hunks = hunks.into_iter().peekable();
76 let mut intersecting = Vec::new();
77 let mut ancestor_integrated_until = 0;
78 let mut resolution = Resolution::Complete;
79 let mut current_hunks = Vec::with_capacity(2);
80 while take_intersecting(&mut hunks, &mut current_hunks, &mut intersecting).is_some() {
81 if intersecting.is_empty() {
82 let hunk = current_hunks.pop().expect("always pushed during intersection check");
83 write_ancestor(input, ancestor_integrated_until, hunk.before.start as usize, out);
84 ancestor_integrated_until = hunk.before.end;
85 write_hunks(std::slice::from_ref(&hunk), input, ¤t_tokens, out);
86 continue;
87 }
88
89 let filled_hunks_side = current_hunks.first().expect("at least one hunk").side;
90 {
91 let filled_hunks_range = before_range_from_hunks(¤t_hunks);
92 let intersecting_range = before_range_from_hunks(&intersecting);
93 let extended_range = filled_hunks_range.start..intersecting_range.end.max(filled_hunks_range.end);
94 fill_ancestor(&extended_range, &mut current_hunks);
95 fill_ancestor(&extended_range, &mut intersecting);
96 }
97 match conflict {
98 Conflict::Keep { style, marker_size } => {
99 let marker_size = marker_size.get();
100 let (hunks_front_and_back, num_hunks_front) = match style {
101 ConflictStyle::Merge | ConflictStyle::ZealousDiff3 => {
102 zealously_contract_hunks(&mut current_hunks, &mut intersecting, input, ¤t_tokens)
103 }
104 ConflictStyle::Diff3 => (Vec::new(), 0),
105 };
106 let (our_hunks, their_hunks) = match filled_hunks_side {
107 Side::Current => (¤t_hunks, &intersecting),
108 Side::Other => (&intersecting, ¤t_hunks),
109 Side::Ancestor => {
110 unreachable!("initial hunks are never ancestors")
111 }
112 };
113 let (front_hunks, back_hunks) = hunks_front_and_back.split_at(num_hunks_front);
114 let first_hunk = first_hunk(front_hunks, our_hunks, their_hunks, back_hunks);
115 let last_hunk = last_hunk(front_hunks, our_hunks, their_hunks, back_hunks);
116 write_ancestor(input, ancestor_integrated_until, first_hunk.before.start as usize, out);
117 write_hunks(front_hunks, input, ¤t_tokens, out);
118 let hunk_storage;
120 let nl = detect_line_ending(
121 if front_hunks.is_empty() {
122 hunk_storage = Hunk {
123 before: ancestor_integrated_until..first_hunk.before.start,
124 after: Default::default(),
125 side: Side::Ancestor,
126 };
127 std::slice::from_ref(&hunk_storage)
128 } else {
129 front_hunks
130 },
131 input,
132 ¤t_tokens,
133 )
134 .or_else(|| detect_line_ending(our_hunks, input, ¤t_tokens))
135 .unwrap_or(b"\n".into());
136 match style {
137 ConflictStyle::Merge => {
138 if contains_lines(our_hunks) || contains_lines(their_hunks) {
139 resolution = Resolution::Conflict;
140 write_conflict_marker(out, b'<', current_label, marker_size, nl);
141 write_hunks(our_hunks, input, ¤t_tokens, out);
142 write_conflict_marker(out, b'=', None, marker_size, nl);
143 write_hunks(their_hunks, input, ¤t_tokens, out);
144 write_conflict_marker(out, b'>', other_label, marker_size, nl);
145 }
146 }
147 ConflictStyle::Diff3 | ConflictStyle::ZealousDiff3 => {
148 if contains_lines(our_hunks) || contains_lines(their_hunks) {
149 if hunks_differ_in_diff3(style, our_hunks, their_hunks, input, ¤t_tokens) {
150 resolution = Resolution::Conflict;
151 write_conflict_marker(out, b'<', current_label, marker_size, nl);
152 write_hunks(our_hunks, input, ¤t_tokens, out);
153 let ancestor_hunk = Hunk {
154 before: first_hunk.before.start..last_hunk.before.end,
155 after: Default::default(),
156 side: Side::Ancestor,
157 };
158 let ancestor_hunk = std::slice::from_ref(&ancestor_hunk);
159 let ancestor_nl = detect_line_ending_or_nl(ancestor_hunk, input, ¤t_tokens);
160 write_conflict_marker(out, b'|', ancestor_label, marker_size, ancestor_nl);
161 write_hunks(ancestor_hunk, input, ¤t_tokens, out);
162 write_conflict_marker(out, b'=', None, marker_size, nl);
163 write_hunks(their_hunks, input, ¤t_tokens, out);
164 write_conflict_marker(out, b'>', other_label, marker_size, nl);
165 } else {
166 write_hunks(our_hunks, input, ¤t_tokens, out);
167 }
168 }
169 }
170 }
171
172 write_hunks(back_hunks, input, ¤t_tokens, out);
173 ancestor_integrated_until = last_hunk.before.end;
174 }
175 Conflict::ResolveWithOurs | Conflict::ResolveWithTheirs => {
176 let (our_hunks, their_hunks) = match filled_hunks_side {
177 Side::Current => (¤t_hunks, &intersecting),
178 Side::Other => (&intersecting, ¤t_hunks),
179 Side::Ancestor => {
180 unreachable!("initial hunks are never ancestors")
181 }
182 };
183 if hunks_differ_in_diff3(ConflictStyle::Diff3, our_hunks, their_hunks, input, ¤t_tokens) {
184 resolution = Resolution::CompleteWithAutoResolvedConflict;
185 }
186 let hunks_to_write = if conflict == Conflict::ResolveWithOurs {
187 our_hunks
188 } else {
189 their_hunks
190 };
191 if let Some(first_hunk) = hunks_to_write.first() {
192 write_ancestor(input, ancestor_integrated_until, first_hunk.before.start as usize, out);
193 }
194 write_hunks(hunks_to_write, input, ¤t_tokens, out);
195 if let Some(last_hunk) = hunks_to_write.last() {
196 ancestor_integrated_until = last_hunk.before.end;
197 }
198 }
199 Conflict::ResolveWithUnion => {
200 let (hunks_front_and_back, num_hunks_front) =
201 zealously_contract_hunks(&mut current_hunks, &mut intersecting, input, ¤t_tokens);
202
203 let (our_hunks, their_hunks) = match filled_hunks_side {
204 Side::Current => (¤t_hunks, &intersecting),
205 Side::Other => (&intersecting, ¤t_hunks),
206 Side::Ancestor => {
207 unreachable!("initial hunks are never ancestors")
208 }
209 };
210 if hunks_differ_in_diff3(ConflictStyle::Diff3, our_hunks, their_hunks, input, ¤t_tokens) {
211 resolution = Resolution::CompleteWithAutoResolvedConflict;
212 }
213 let (front_hunks, back_hunks) = hunks_front_and_back.split_at(num_hunks_front);
214 let first_hunk = first_hunk(front_hunks, our_hunks, their_hunks, back_hunks);
215 write_ancestor(input, ancestor_integrated_until, first_hunk.before.start as usize, out);
216 write_hunks(front_hunks, input, ¤t_tokens, out);
217 assure_ends_with_nl(out, detect_line_ending_or_nl(front_hunks, input, ¤t_tokens));
218 write_hunks(our_hunks, input, ¤t_tokens, out);
219 assure_ends_with_nl(out, detect_line_ending_or_nl(our_hunks, input, ¤t_tokens));
220 write_hunks(their_hunks, input, ¤t_tokens, out);
221 if !back_hunks.is_empty() {
222 assure_ends_with_nl(out, detect_line_ending_or_nl(their_hunks, input, ¤t_tokens));
223 }
224 write_hunks(back_hunks, input, ¤t_tokens, out);
225 let last_hunk = last_hunk(front_hunks, our_hunks, their_hunks, back_hunks);
226 ancestor_integrated_until = last_hunk.before.end;
227 }
228 }
229 }
230 write_ancestor(input, ancestor_integrated_until, input.before.len(), out);
231
232 resolution
233}
234
235fn first_hunk<'a>(front: &'a [Hunk], ours: &'a [Hunk], theirs: &'a [Hunk], back: &'a [Hunk]) -> &'a Hunk {
236 front
237 .first()
238 .or(ours.first())
239 .or(theirs.first())
240 .or(back.first())
241 .expect("at least one hunk - we aborted if there are none anywhere")
242}
243
244fn last_hunk<'a>(front: &'a [Hunk], ours: &'a [Hunk], theirs: &'a [Hunk], back: &'a [Hunk]) -> &'a Hunk {
246 back.last()
247 .or(theirs.last())
248 .or(ours.last())
249 .or(front.last())
250 .expect("at least one hunk - we aborted if there are none anywhere")
251}
252
253fn before_range_from_hunks(hunks: &[Hunk]) -> Range<u32> {
254 hunks
255 .first()
256 .zip(hunks.last())
257 .map(|(f, l)| f.before.start..l.before.end)
258 .expect("at least one entry")
259}