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: Default::default(),
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 if their_hunks.is_empty() {
119 write_hunks(our_hunks, input, ¤t_tokens, out);
120 } else if our_hunks.is_empty() {
121 write_hunks(their_hunks, input, ¤t_tokens, out);
122 } else {
123 let hunk_storage;
125 let nl = detect_line_ending(
126 if front_hunks.is_empty() {
127 hunk_storage = Hunk {
128 before: ancestor_integrated_until..first_hunk.before.start,
129 after: Default::default(),
130 side: Side::Ancestor,
131 };
132 std::slice::from_ref(&hunk_storage)
133 } else {
134 front_hunks
135 },
136 input,
137 ¤t_tokens,
138 )
139 .or_else(|| detect_line_ending(our_hunks, input, ¤t_tokens))
140 .unwrap_or(b"\n".into());
141 match style {
142 ConflictStyle::Merge => {
143 if contains_lines(our_hunks) || contains_lines(their_hunks) {
144 resolution = Resolution::Conflict;
145 write_conflict_marker(out, b'<', current_label, marker_size, nl);
146 write_hunks(our_hunks, input, ¤t_tokens, out);
147 write_conflict_marker(out, b'=', None, marker_size, nl);
148 write_hunks(their_hunks, input, ¤t_tokens, out);
149 write_conflict_marker(out, b'>', other_label, marker_size, nl);
150 }
151 }
152 ConflictStyle::Diff3 | ConflictStyle::ZealousDiff3 => {
153 if contains_lines(our_hunks) || contains_lines(their_hunks) {
154 if hunks_differ_in_diff3(style, our_hunks, their_hunks, input, ¤t_tokens) {
155 resolution = Resolution::Conflict;
156 write_conflict_marker(out, b'<', current_label, marker_size, nl);
157 write_hunks(our_hunks, input, ¤t_tokens, out);
158 let ancestor_hunk = Hunk {
159 before: first_hunk.before.start..last_hunk.before.end,
160 after: Default::default(),
161 side: Side::Ancestor,
162 };
163 let ancestor_hunk = std::slice::from_ref(&ancestor_hunk);
164 let ancestor_nl = detect_line_ending_or_nl(ancestor_hunk, input, ¤t_tokens);
165 write_conflict_marker(out, b'|', ancestor_label, marker_size, ancestor_nl);
166 write_hunks(ancestor_hunk, input, ¤t_tokens, out);
167 write_conflict_marker(out, b'=', None, marker_size, nl);
168 write_hunks(their_hunks, input, ¤t_tokens, out);
169 write_conflict_marker(out, b'>', other_label, marker_size, nl);
170 } else {
171 write_hunks(our_hunks, input, ¤t_tokens, out);
172 }
173 }
174 }
175 }
176 }
177 write_hunks(back_hunks, input, ¤t_tokens, out);
178 ancestor_integrated_until = last_hunk.before.end;
179 }
180 Conflict::ResolveWithOurs | Conflict::ResolveWithTheirs => {
181 let (our_hunks, their_hunks) = match filled_hunks_side {
182 Side::Current => (¤t_hunks, &intersecting),
183 Side::Other => (&intersecting, ¤t_hunks),
184 Side::Ancestor => {
185 unreachable!("initial hunks are never ancestors")
186 }
187 };
188 if hunks_differ_in_diff3(ConflictStyle::Diff3, our_hunks, their_hunks, input, ¤t_tokens) {
189 resolution = Resolution::CompleteWithAutoResolvedConflict;
190 }
191 let hunks_to_write = if conflict == Conflict::ResolveWithOurs {
192 our_hunks
193 } else {
194 their_hunks
195 };
196 if let Some(first_hunk) = hunks_to_write.first() {
197 write_ancestor(input, ancestor_integrated_until, first_hunk.before.start as usize, out);
198 }
199 write_hunks(hunks_to_write, input, ¤t_tokens, out);
200 if let Some(last_hunk) = hunks_to_write.last() {
201 ancestor_integrated_until = last_hunk.before.end;
202 }
203 }
204 Conflict::ResolveWithUnion => {
205 let (hunks_front_and_back, num_hunks_front) =
206 zealously_contract_hunks(&mut current_hunks, &mut intersecting, input, ¤t_tokens);
207
208 let (our_hunks, their_hunks) = match filled_hunks_side {
209 Side::Current => (¤t_hunks, &intersecting),
210 Side::Other => (&intersecting, ¤t_hunks),
211 Side::Ancestor => {
212 unreachable!("initial hunks are never ancestors")
213 }
214 };
215 if hunks_differ_in_diff3(ConflictStyle::Diff3, our_hunks, their_hunks, input, ¤t_tokens) {
216 resolution = Resolution::CompleteWithAutoResolvedConflict;
217 }
218 let (front_hunks, back_hunks) = hunks_front_and_back.split_at(num_hunks_front);
219 let first_hunk = first_hunk(front_hunks, our_hunks, their_hunks, back_hunks);
220 write_ancestor(input, ancestor_integrated_until, first_hunk.before.start as usize, out);
221 write_hunks(front_hunks, input, ¤t_tokens, out);
222 assure_ends_with_nl(out, detect_line_ending_or_nl(front_hunks, input, ¤t_tokens));
223 write_hunks(our_hunks, input, ¤t_tokens, out);
224 assure_ends_with_nl(out, detect_line_ending_or_nl(our_hunks, input, ¤t_tokens));
225 write_hunks(their_hunks, input, ¤t_tokens, out);
226 if !back_hunks.is_empty() {
227 assure_ends_with_nl(out, detect_line_ending_or_nl(their_hunks, input, ¤t_tokens));
228 }
229 write_hunks(back_hunks, input, ¤t_tokens, out);
230 let last_hunk = last_hunk(front_hunks, our_hunks, their_hunks, back_hunks);
231 ancestor_integrated_until = last_hunk.before.end;
232 }
233 }
234 }
235 write_ancestor(input, ancestor_integrated_until, input.before.len(), out);
236
237 resolution
238}
239
240fn first_hunk<'a>(front: &'a [Hunk], ours: &'a [Hunk], theirs: &'a [Hunk], back: &'a [Hunk]) -> &'a Hunk {
241 front
242 .first()
243 .or(ours.first())
244 .or(theirs.first())
245 .or(back.first())
246 .expect("at least one hunk - we aborted if there are none anywhere")
247}
248
249fn last_hunk<'a>(front: &'a [Hunk], ours: &'a [Hunk], theirs: &'a [Hunk], back: &'a [Hunk]) -> &'a Hunk {
251 back.last()
252 .or(theirs.last())
253 .or(ours.last())
254 .or(front.last())
255 .expect("at least one hunk - we aborted if there are none anywhere")
256}
257
258fn before_range_from_hunks(hunks: &[Hunk]) -> Range<u32> {
259 hunks
260 .first()
261 .zip(hunks.last())
262 .map(|(f, l)| f.before.start..l.before.end)
263 .expect("at least one entry")
264}