1use gpui::{App, Context, Entity, EventEmitter, SharedString};
2use std::{cmp::Ordering, ops::Range, sync::Arc};
3use text::{Anchor, BufferId, OffsetRangeExt as _};
4
5pub struct ConflictSet {
6 pub has_conflict: bool,
7 pub snapshot: ConflictSetSnapshot,
8}
9
10#[derive(Clone, Debug, PartialEq, Eq)]
11pub struct ConflictSetUpdate {
12 pub buffer_range: Option<Range<Anchor>>,
13 pub old_range: Range<usize>,
14 pub new_range: Range<usize>,
15}
16
17#[derive(Debug, Clone)]
18pub struct ConflictSetSnapshot {
19 pub buffer_id: BufferId,
20 pub conflicts: Arc<[ConflictRegion]>,
21}
22
23impl ConflictSetSnapshot {
24 pub fn conflicts_in_range(
25 &self,
26 range: Range<Anchor>,
27 buffer: &text::BufferSnapshot,
28 ) -> &[ConflictRegion] {
29 let start_ix = self
30 .conflicts
31 .binary_search_by(|conflict| {
32 conflict
33 .range
34 .end
35 .cmp(&range.start, buffer)
36 .then(Ordering::Greater)
37 })
38 .unwrap_err();
39 let end_ix = start_ix
40 + self.conflicts[start_ix..]
41 .binary_search_by(|conflict| {
42 conflict
43 .range
44 .start
45 .cmp(&range.end, buffer)
46 .then(Ordering::Less)
47 })
48 .unwrap_err();
49 &self.conflicts[start_ix..end_ix]
50 }
51
52 pub fn compare(&self, other: &Self, buffer: &text::BufferSnapshot) -> ConflictSetUpdate {
53 let common_prefix_len = self
54 .conflicts
55 .iter()
56 .zip(other.conflicts.iter())
57 .take_while(|(old, new)| old == new)
58 .count();
59 let common_suffix_len = self.conflicts[common_prefix_len..]
60 .iter()
61 .rev()
62 .zip(other.conflicts[common_prefix_len..].iter().rev())
63 .take_while(|(old, new)| old == new)
64 .count();
65 let old_conflicts =
66 &self.conflicts[common_prefix_len..(self.conflicts.len() - common_suffix_len)];
67 let new_conflicts =
68 &other.conflicts[common_prefix_len..(other.conflicts.len() - common_suffix_len)];
69 let old_range = common_prefix_len..(common_prefix_len + old_conflicts.len());
70 let new_range = common_prefix_len..(common_prefix_len + new_conflicts.len());
71 let start = match (old_conflicts.first(), new_conflicts.first()) {
72 (None, None) => None,
73 (None, Some(conflict)) => Some(conflict.range.start),
74 (Some(conflict), None) => Some(conflict.range.start),
75 (Some(first), Some(second)) => {
76 Some(*first.range.start.min(&second.range.start, buffer))
77 }
78 };
79 let end = match (old_conflicts.last(), new_conflicts.last()) {
80 (None, None) => None,
81 (None, Some(conflict)) => Some(conflict.range.end),
82 (Some(first), None) => Some(first.range.end),
83 (Some(first), Some(second)) => Some(*first.range.end.max(&second.range.end, buffer)),
84 };
85 ConflictSetUpdate {
86 buffer_range: start.zip(end).map(|(start, end)| start..end),
87 old_range,
88 new_range,
89 }
90 }
91}
92
93#[derive(Debug, Clone, PartialEq, Eq)]
94pub struct ConflictRegion {
95 pub ours_branch_name: SharedString,
96 pub theirs_branch_name: SharedString,
97 pub range: Range<Anchor>,
98 pub ours: Range<Anchor>,
99 pub theirs: Range<Anchor>,
100 pub base: Option<Range<Anchor>>,
101}
102
103impl ConflictRegion {
104 pub fn resolve(
105 &self,
106 buffer: Entity<language::Buffer>,
107 ranges: &[Range<Anchor>],
108 cx: &mut App,
109 ) {
110 let buffer_snapshot = buffer.read(cx).snapshot();
111 let mut deletions = Vec::new();
112 let empty = "";
113 let outer_range = self.range.to_offset(&buffer_snapshot);
114 let mut offset = outer_range.start;
115 for kept_range in ranges {
116 let kept_range = kept_range.to_offset(&buffer_snapshot);
117 if kept_range.start > offset {
118 deletions.push((offset..kept_range.start, empty));
119 }
120 offset = kept_range.end;
121 }
122 if outer_range.end > offset {
123 deletions.push((offset..outer_range.end, empty));
124 }
125
126 buffer.update(cx, |buffer, cx| {
127 buffer.edit(deletions, None, cx);
128 });
129 }
130}
131
132impl ConflictSet {
133 pub fn new(buffer_id: BufferId, has_conflict: bool, _: &mut Context<Self>) -> Self {
134 Self {
135 has_conflict,
136 snapshot: ConflictSetSnapshot {
137 buffer_id,
138 conflicts: Default::default(),
139 },
140 }
141 }
142
143 pub fn set_has_conflict(&mut self, has_conflict: bool, cx: &mut Context<Self>) -> bool {
144 if has_conflict != self.has_conflict {
145 self.has_conflict = has_conflict;
146 if !self.has_conflict {
147 cx.emit(ConflictSetUpdate {
148 buffer_range: None,
149 old_range: 0..self.snapshot.conflicts.len(),
150 new_range: 0..0,
151 });
152 self.snapshot.conflicts = Default::default();
153 }
154 true
155 } else {
156 false
157 }
158 }
159
160 pub fn snapshot(&self) -> ConflictSetSnapshot {
161 self.snapshot.clone()
162 }
163
164 pub fn set_snapshot(
165 &mut self,
166 snapshot: ConflictSetSnapshot,
167 update: ConflictSetUpdate,
168 cx: &mut Context<Self>,
169 ) {
170 self.snapshot = snapshot;
171 cx.emit(update);
172 }
173
174 pub fn parse(buffer: &text::BufferSnapshot) -> ConflictSetSnapshot {
175 let mut conflicts = Vec::new();
176
177 let mut line_pos = 0;
178 let buffer_len = buffer.len();
179 let mut lines = buffer.text_for_range(0..buffer_len).lines();
180
181 let mut conflict_start: Option<usize> = None;
182 let mut ours_start: Option<usize> = None;
183 let mut ours_end: Option<usize> = None;
184 let mut ours_branch_name: Option<SharedString> = None;
185 let mut base_start: Option<usize> = None;
186 let mut base_end: Option<usize> = None;
187 let mut theirs_start: Option<usize> = None;
188 let mut theirs_branch_name: Option<SharedString> = None;
189
190 while let Some(line) = lines.next() {
191 let line_end = line_pos + line.len();
192
193 if let Some(branch_name) = line.strip_prefix("<<<<<<< ") {
194 // If we see a new conflict marker while already parsing one,
195 // abandon the previous one and start a new one
196 conflict_start = Some(line_pos);
197 ours_start = Some(line_end + 1);
198
199 let branch_name = branch_name.trim();
200 if !branch_name.is_empty() {
201 ours_branch_name = Some(SharedString::new(branch_name));
202 }
203 } else if line.starts_with("||||||| ")
204 && conflict_start.is_some()
205 && ours_start.is_some()
206 {
207 ours_end = Some(line_pos);
208 base_start = Some(line_end + 1);
209 } else if line.starts_with("=======")
210 && conflict_start.is_some()
211 && ours_start.is_some()
212 {
213 // Set ours_end if not already set (would be set if we have base markers)
214 if ours_end.is_none() {
215 ours_end = Some(line_pos);
216 } else if base_start.is_some() {
217 base_end = Some(line_pos);
218 }
219 theirs_start = Some(line_end + 1);
220 } else if let Some(branch_name) = line.strip_prefix(">>>>>>> ")
221 && conflict_start.is_some()
222 && ours_start.is_some()
223 && ours_end.is_some()
224 && theirs_start.is_some()
225 {
226 let branch_name = branch_name.trim();
227 if !branch_name.is_empty() {
228 theirs_branch_name = Some(SharedString::new(branch_name));
229 }
230
231 let theirs_end = line_pos;
232 let conflict_end = (line_end + 1).min(buffer_len);
233
234 let range = buffer.anchor_after(conflict_start.unwrap())
235 ..buffer.anchor_before(conflict_end);
236 let ours = buffer.anchor_after(ours_start.unwrap())
237 ..buffer.anchor_before(ours_end.unwrap());
238 let theirs =
239 buffer.anchor_after(theirs_start.unwrap())..buffer.anchor_before(theirs_end);
240
241 let base = base_start
242 .zip(base_end)
243 .map(|(start, end)| buffer.anchor_after(start)..buffer.anchor_before(end));
244
245 conflicts.push(ConflictRegion {
246 ours_branch_name: ours_branch_name
247 .take()
248 .unwrap_or_else(|| SharedString::new_static("HEAD")),
249 theirs_branch_name: theirs_branch_name
250 .take()
251 .unwrap_or_else(|| SharedString::new_static("Origin")),
252 range,
253 ours,
254 theirs,
255 base,
256 });
257
258 conflict_start = None;
259 ours_start = None;
260 ours_end = None;
261 base_start = None;
262 base_end = None;
263 theirs_start = None;
264 }
265
266 line_pos = line_end + 1;
267 }
268
269 ConflictSetSnapshot {
270 conflicts: conflicts.into(),
271 buffer_id: buffer.remote_id(),
272 }
273 }
274}
275
276impl EventEmitter<ConflictSetUpdate> for ConflictSet {}