1use super::{network::Network, *};
2use clock::ReplicaId;
3use rand::prelude::*;
4use std::{
5 cmp::Ordering,
6 env,
7 iter::Iterator,
8 time::{Duration, Instant},
9};
10
11#[cfg(test)]
12#[ctor::ctor]
13fn init_logger() {
14 zlog::init_test();
15}
16
17#[test]
18fn test_edit() {
19 let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "abc");
20 assert_eq!(buffer.text(), "abc");
21 buffer.edit([(3..3, "def")]);
22 assert_eq!(buffer.text(), "abcdef");
23 buffer.edit([(0..0, "ghi")]);
24 assert_eq!(buffer.text(), "ghiabcdef");
25 buffer.edit([(5..5, "jkl")]);
26 assert_eq!(buffer.text(), "ghiabjklcdef");
27 buffer.edit([(6..7, "")]);
28 assert_eq!(buffer.text(), "ghiabjlcdef");
29 buffer.edit([(4..9, "mno")]);
30 assert_eq!(buffer.text(), "ghiamnoef");
31}
32
33#[test]
34fn test_point_for_row_and_column_from_external_source() {
35 let buffer = Buffer::new(
36 ReplicaId::LOCAL,
37 BufferId::new(1).unwrap(),
38 "aéøbcdef\nsecond",
39 );
40 let snapshot = buffer.snapshot();
41
42 assert_eq!(snapshot.point_from_external_input(0, 0), Point::new(0, 0));
43 assert_eq!(snapshot.point_from_external_input(0, 4), Point::new(0, 6));
44 assert_eq!(
45 snapshot.point_from_external_input(0, 100),
46 Point::new(0, 10)
47 );
48 assert_eq!(snapshot.point_from_external_input(1, 3), Point::new(1, 3));
49}
50
51#[gpui::test(iterations = 100)]
52fn test_random_edits(mut rng: StdRng) {
53 let operations = env::var("OPERATIONS")
54 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
55 .unwrap_or(10);
56
57 let reference_string_len = rng.random_range(0..3);
58 let mut reference_string = RandomCharIter::new(&mut rng)
59 .take(reference_string_len)
60 .collect::<String>();
61 let mut buffer = Buffer::new(
62 ReplicaId::LOCAL,
63 BufferId::new(1).unwrap(),
64 reference_string.clone(),
65 );
66 LineEnding::normalize(&mut reference_string);
67
68 buffer.set_group_interval(Duration::from_millis(rng.random_range(0..=200)));
69 let mut buffer_versions = Vec::new();
70 log::info!(
71 "buffer text {:?}, version: {:?}",
72 buffer.text(),
73 buffer.version()
74 );
75
76 for _i in 0..operations {
77 let (edits, _) = buffer.randomly_edit(&mut rng, 5);
78 for (old_range, new_text) in edits.iter().rev() {
79 reference_string.replace_range(old_range.clone(), new_text);
80 }
81
82 assert_eq!(buffer.text(), reference_string);
83 log::info!(
84 "buffer text {:?}, version: {:?}",
85 buffer.text(),
86 buffer.version()
87 );
88
89 if rng.random_bool(0.25) {
90 buffer.randomly_undo_redo(&mut rng);
91 reference_string = buffer.text();
92 log::info!(
93 "buffer text {:?}, version: {:?}",
94 buffer.text(),
95 buffer.version()
96 );
97 }
98
99 let range = buffer.random_byte_range(0, &mut rng);
100 assert_eq!(
101 buffer.text_summary_for_range::<TextSummary, _>(range.clone()),
102 TextSummary::from(&reference_string[range])
103 );
104
105 buffer.check_invariants();
106
107 if rng.random_bool(0.3) {
108 buffer_versions.push((buffer.clone(), buffer.subscribe()));
109 }
110 }
111
112 for (old_buffer, subscription) in buffer_versions {
113 let edits = buffer
114 .edits_since::<usize>(&old_buffer.version)
115 .collect::<Vec<_>>();
116
117 log::info!(
118 "applying edits since version {:?} to old text: {:?}: {:?}",
119 old_buffer.version(),
120 old_buffer.text(),
121 edits,
122 );
123
124 let mut text = old_buffer.visible_text.clone();
125 for edit in edits {
126 let new_text: String = buffer.text_for_range(edit.new.clone()).collect();
127 text.replace(edit.new.start..edit.new.start + edit.old.len(), &new_text);
128 }
129 assert_eq!(text.to_string(), buffer.text());
130
131 assert_eq!(
132 buffer.rope_for_version(old_buffer.version()).to_string(),
133 old_buffer.text()
134 );
135
136 for _ in 0..5 {
137 let end_ix =
138 old_buffer.clip_offset(rng.random_range(0..=old_buffer.len()), Bias::Right);
139 let start_ix = old_buffer.clip_offset(rng.random_range(0..=end_ix), Bias::Left);
140 let range = old_buffer.anchor_before(start_ix)..old_buffer.anchor_after(end_ix);
141 let mut old_text = old_buffer.text_for_range(range.clone()).collect::<String>();
142 let edits = buffer
143 .edits_since_in_range::<usize>(&old_buffer.version, range.clone())
144 .collect::<Vec<_>>();
145 log::info!(
146 "applying edits since version {:?} to old text in range {:?}: {:?}: {:?}",
147 old_buffer.version(),
148 start_ix..end_ix,
149 old_text,
150 edits,
151 );
152
153 let new_text = buffer.text_for_range(range).collect::<String>();
154 for edit in edits {
155 old_text.replace_range(
156 edit.new.start..edit.new.start + edit.old_len(),
157 &new_text[edit.new],
158 );
159 }
160 assert_eq!(old_text, new_text);
161 }
162
163 assert_eq!(
164 buffer.has_edits_since(&old_buffer.version),
165 buffer
166 .edits_since::<usize>(&old_buffer.version)
167 .next()
168 .is_some(),
169 );
170
171 let subscription_edits = subscription.consume();
172 log::info!(
173 "applying subscription edits since version {:?} to old text: {:?}: {:?}",
174 old_buffer.version(),
175 old_buffer.text(),
176 subscription_edits,
177 );
178
179 let mut text = old_buffer.visible_text.clone();
180 for edit in subscription_edits.into_inner() {
181 let new_text: String = buffer.text_for_range(edit.new.clone()).collect();
182 text.replace(edit.new.start..edit.new.start + edit.old.len(), &new_text);
183 }
184 assert_eq!(text.to_string(), buffer.text());
185 }
186}
187
188#[test]
189fn test_line_endings() {
190 assert_eq!(LineEnding::detect(&"🍐✅\n".repeat(1000)), LineEnding::Unix);
191 assert_eq!(LineEnding::detect(&"abcd\n".repeat(1000)), LineEnding::Unix);
192 assert_eq!(
193 LineEnding::detect(&"🍐✅\r\n".repeat(1000)),
194 LineEnding::Windows
195 );
196 assert_eq!(
197 LineEnding::detect(&"abcd\r\n".repeat(1000)),
198 LineEnding::Windows
199 );
200
201 let mut buffer = Buffer::new(
202 ReplicaId::LOCAL,
203 BufferId::new(1).unwrap(),
204 "one\r\ntwo\rthree",
205 );
206 assert_eq!(buffer.text(), "one\ntwo\nthree");
207 assert_eq!(buffer.line_ending(), LineEnding::Windows);
208 buffer.check_invariants();
209
210 buffer.edit([(buffer.len()..buffer.len(), "\r\nfour")]);
211 buffer.edit([(0..0, "zero\r\n")]);
212 assert_eq!(buffer.text(), "zero\none\ntwo\nthree\nfour");
213 assert_eq!(buffer.line_ending(), LineEnding::Windows);
214 buffer.check_invariants();
215}
216
217#[test]
218fn test_line_len() {
219 let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "");
220 buffer.edit([(0..0, "abcd\nefg\nhij")]);
221 buffer.edit([(12..12, "kl\nmno")]);
222 buffer.edit([(18..18, "\npqrs\n")]);
223 buffer.edit([(18..21, "\nPQ")]);
224
225 assert_eq!(buffer.line_len(0), 4);
226 assert_eq!(buffer.line_len(1), 3);
227 assert_eq!(buffer.line_len(2), 5);
228 assert_eq!(buffer.line_len(3), 3);
229 assert_eq!(buffer.line_len(4), 4);
230 assert_eq!(buffer.line_len(5), 0);
231}
232
233#[test]
234fn test_common_prefix_at_position() {
235 let text = "a = str; b = δα";
236 let buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), text);
237
238 let offset1 = offset_after(text, "str");
239 let offset2 = offset_after(text, "δα");
240
241 // the preceding word is a prefix of the suggestion
242 assert_eq!(
243 buffer.common_prefix_at(offset1, "string"),
244 range_of(text, "str"),
245 );
246 // a suffix of the preceding word is a prefix of the suggestion
247 assert_eq!(
248 buffer.common_prefix_at(offset1, "tree"),
249 range_of(text, "tr"),
250 );
251 // the preceding word is a substring of the suggestion, but not a prefix
252 assert_eq!(
253 buffer.common_prefix_at(offset1, "astro"),
254 empty_range_after(text, "str"),
255 );
256
257 // prefix matching is case insensitive.
258 assert_eq!(
259 buffer.common_prefix_at(offset1, "Strαngε"),
260 range_of(text, "str"),
261 );
262 assert_eq!(
263 buffer.common_prefix_at(offset2, "ΔΑΜΝ"),
264 range_of(text, "δα"),
265 );
266
267 fn offset_after(text: &str, part: &str) -> usize {
268 text.find(part).unwrap() + part.len()
269 }
270
271 fn empty_range_after(text: &str, part: &str) -> Range<usize> {
272 let offset = offset_after(text, part);
273 offset..offset
274 }
275
276 fn range_of(text: &str, part: &str) -> Range<usize> {
277 let start = text.find(part).unwrap();
278 start..start + part.len()
279 }
280}
281
282#[test]
283fn test_text_summary_for_range() {
284 let buffer = Buffer::new(
285 ReplicaId::LOCAL,
286 BufferId::new(1).unwrap(),
287 "ab\nefg\nhklm\nnopqrs\ntuvwxyz",
288 );
289 assert_eq!(
290 buffer.text_summary_for_range::<TextSummary, _>(0..2),
291 TextSummary {
292 len: 2,
293 chars: 2,
294 len_utf16: OffsetUtf16(2),
295 lines: Point::new(0, 2),
296 first_line_chars: 2,
297 last_line_chars: 2,
298 last_line_len_utf16: 2,
299 longest_row: 0,
300 longest_row_chars: 2,
301 }
302 );
303 assert_eq!(
304 buffer.text_summary_for_range::<TextSummary, _>(1..3),
305 TextSummary {
306 len: 2,
307 chars: 2,
308 len_utf16: OffsetUtf16(2),
309 lines: Point::new(1, 0),
310 first_line_chars: 1,
311 last_line_chars: 0,
312 last_line_len_utf16: 0,
313 longest_row: 0,
314 longest_row_chars: 1,
315 }
316 );
317 assert_eq!(
318 buffer.text_summary_for_range::<TextSummary, _>(1..12),
319 TextSummary {
320 len: 11,
321 chars: 11,
322 len_utf16: OffsetUtf16(11),
323 lines: Point::new(3, 0),
324 first_line_chars: 1,
325 last_line_chars: 0,
326 last_line_len_utf16: 0,
327 longest_row: 2,
328 longest_row_chars: 4,
329 }
330 );
331 assert_eq!(
332 buffer.text_summary_for_range::<TextSummary, _>(0..20),
333 TextSummary {
334 len: 20,
335 chars: 20,
336 len_utf16: OffsetUtf16(20),
337 lines: Point::new(4, 1),
338 first_line_chars: 2,
339 last_line_chars: 1,
340 last_line_len_utf16: 1,
341 longest_row: 3,
342 longest_row_chars: 6,
343 }
344 );
345 assert_eq!(
346 buffer.text_summary_for_range::<TextSummary, _>(0..22),
347 TextSummary {
348 len: 22,
349 chars: 22,
350 len_utf16: OffsetUtf16(22),
351 lines: Point::new(4, 3),
352 first_line_chars: 2,
353 last_line_chars: 3,
354 last_line_len_utf16: 3,
355 longest_row: 3,
356 longest_row_chars: 6,
357 }
358 );
359 assert_eq!(
360 buffer.text_summary_for_range::<TextSummary, _>(7..22),
361 TextSummary {
362 len: 15,
363 chars: 15,
364 len_utf16: OffsetUtf16(15),
365 lines: Point::new(2, 3),
366 first_line_chars: 4,
367 last_line_chars: 3,
368 last_line_len_utf16: 3,
369 longest_row: 1,
370 longest_row_chars: 6,
371 }
372 );
373}
374
375#[test]
376fn test_chars_at() {
377 let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "");
378 buffer.edit([(0..0, "abcd\nefgh\nij")]);
379 buffer.edit([(12..12, "kl\nmno")]);
380 buffer.edit([(18..18, "\npqrs")]);
381 buffer.edit([(18..21, "\nPQ")]);
382
383 let chars = buffer.chars_at(Point::new(0, 0));
384 assert_eq!(chars.collect::<String>(), "abcd\nefgh\nijkl\nmno\nPQrs");
385
386 let chars = buffer.chars_at(Point::new(1, 0));
387 assert_eq!(chars.collect::<String>(), "efgh\nijkl\nmno\nPQrs");
388
389 let chars = buffer.chars_at(Point::new(2, 0));
390 assert_eq!(chars.collect::<String>(), "ijkl\nmno\nPQrs");
391
392 let chars = buffer.chars_at(Point::new(3, 0));
393 assert_eq!(chars.collect::<String>(), "mno\nPQrs");
394
395 let chars = buffer.chars_at(Point::new(4, 0));
396 assert_eq!(chars.collect::<String>(), "PQrs");
397
398 // Regression test:
399 let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "");
400 buffer.edit([(0..0, "[workspace]\nmembers = [\n \"xray_core\",\n \"xray_server\",\n \"xray_cli\",\n \"xray_wasm\",\n]\n")]);
401 buffer.edit([(60..60, "\n")]);
402
403 let chars = buffer.chars_at(Point::new(6, 0));
404 assert_eq!(chars.collect::<String>(), " \"xray_wasm\",\n]\n");
405}
406
407#[test]
408fn test_anchors() {
409 let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "");
410 buffer.edit([(0..0, "abc")]);
411 let left_anchor = buffer.anchor_before(2);
412 let right_anchor = buffer.anchor_after(2);
413
414 buffer.edit([(1..1, "def\n")]);
415 assert_eq!(buffer.text(), "adef\nbc");
416 assert_eq!(left_anchor.to_offset(&buffer), 6);
417 assert_eq!(right_anchor.to_offset(&buffer), 6);
418 assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 });
419 assert_eq!(right_anchor.to_point(&buffer), Point { row: 1, column: 1 });
420
421 buffer.edit([(2..3, "")]);
422 assert_eq!(buffer.text(), "adf\nbc");
423 assert_eq!(left_anchor.to_offset(&buffer), 5);
424 assert_eq!(right_anchor.to_offset(&buffer), 5);
425 assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 });
426 assert_eq!(right_anchor.to_point(&buffer), Point { row: 1, column: 1 });
427
428 buffer.edit([(5..5, "ghi\n")]);
429 assert_eq!(buffer.text(), "adf\nbghi\nc");
430 assert_eq!(left_anchor.to_offset(&buffer), 5);
431 assert_eq!(right_anchor.to_offset(&buffer), 9);
432 assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 });
433 assert_eq!(right_anchor.to_point(&buffer), Point { row: 2, column: 0 });
434
435 buffer.edit([(7..9, "")]);
436 assert_eq!(buffer.text(), "adf\nbghc");
437 assert_eq!(left_anchor.to_offset(&buffer), 5);
438 assert_eq!(right_anchor.to_offset(&buffer), 7);
439 assert_eq!(left_anchor.to_point(&buffer), Point { row: 1, column: 1 },);
440 assert_eq!(right_anchor.to_point(&buffer), Point { row: 1, column: 3 });
441
442 // Ensure anchoring to a point is equivalent to anchoring to an offset.
443 assert_eq!(
444 buffer.anchor_before(Point { row: 0, column: 0 }),
445 buffer.anchor_before(0)
446 );
447 assert_eq!(
448 buffer.anchor_before(Point { row: 0, column: 1 }),
449 buffer.anchor_before(1)
450 );
451 assert_eq!(
452 buffer.anchor_before(Point { row: 0, column: 2 }),
453 buffer.anchor_before(2)
454 );
455 assert_eq!(
456 buffer.anchor_before(Point { row: 0, column: 3 }),
457 buffer.anchor_before(3)
458 );
459 assert_eq!(
460 buffer.anchor_before(Point { row: 1, column: 0 }),
461 buffer.anchor_before(4)
462 );
463 assert_eq!(
464 buffer.anchor_before(Point { row: 1, column: 1 }),
465 buffer.anchor_before(5)
466 );
467 assert_eq!(
468 buffer.anchor_before(Point { row: 1, column: 2 }),
469 buffer.anchor_before(6)
470 );
471 assert_eq!(
472 buffer.anchor_before(Point { row: 1, column: 3 }),
473 buffer.anchor_before(7)
474 );
475 assert_eq!(
476 buffer.anchor_before(Point { row: 1, column: 4 }),
477 buffer.anchor_before(8)
478 );
479
480 // Comparison between anchors.
481 let anchor_at_offset_0 = buffer.anchor_before(0);
482 let anchor_at_offset_1 = buffer.anchor_before(1);
483 let anchor_at_offset_2 = buffer.anchor_before(2);
484
485 assert_eq!(
486 anchor_at_offset_0.cmp(&anchor_at_offset_0, &buffer),
487 Ordering::Equal
488 );
489 assert_eq!(
490 anchor_at_offset_1.cmp(&anchor_at_offset_1, &buffer),
491 Ordering::Equal
492 );
493 assert_eq!(
494 anchor_at_offset_2.cmp(&anchor_at_offset_2, &buffer),
495 Ordering::Equal
496 );
497
498 assert_eq!(
499 anchor_at_offset_0.cmp(&anchor_at_offset_1, &buffer),
500 Ordering::Less
501 );
502 assert_eq!(
503 anchor_at_offset_1.cmp(&anchor_at_offset_2, &buffer),
504 Ordering::Less
505 );
506 assert_eq!(
507 anchor_at_offset_0.cmp(&anchor_at_offset_2, &buffer),
508 Ordering::Less
509 );
510
511 assert_eq!(
512 anchor_at_offset_1.cmp(&anchor_at_offset_0, &buffer),
513 Ordering::Greater
514 );
515 assert_eq!(
516 anchor_at_offset_2.cmp(&anchor_at_offset_1, &buffer),
517 Ordering::Greater
518 );
519 assert_eq!(
520 anchor_at_offset_2.cmp(&anchor_at_offset_0, &buffer),
521 Ordering::Greater
522 );
523}
524
525#[test]
526fn test_anchors_at_start_and_end() {
527 let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "");
528 let before_start_anchor = buffer.anchor_before(0);
529 let after_end_anchor = buffer.anchor_after(0);
530
531 buffer.edit([(0..0, "abc")]);
532 assert_eq!(buffer.text(), "abc");
533 assert_eq!(before_start_anchor.to_offset(&buffer), 0);
534 assert_eq!(after_end_anchor.to_offset(&buffer), 3);
535
536 let after_start_anchor = buffer.anchor_after(0);
537 let before_end_anchor = buffer.anchor_before(3);
538
539 buffer.edit([(3..3, "def")]);
540 buffer.edit([(0..0, "ghi")]);
541 assert_eq!(buffer.text(), "ghiabcdef");
542 assert_eq!(before_start_anchor.to_offset(&buffer), 0);
543 assert_eq!(after_start_anchor.to_offset(&buffer), 3);
544 assert_eq!(before_end_anchor.to_offset(&buffer), 6);
545 assert_eq!(after_end_anchor.to_offset(&buffer), 9);
546}
547
548#[test]
549fn test_undo_redo() {
550 let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "1234");
551 // Set group interval to zero so as to not group edits in the undo stack.
552 buffer.set_group_interval(Duration::from_secs(0));
553
554 buffer.edit([(1..1, "abx")]);
555 buffer.edit([(3..4, "yzef")]);
556 buffer.edit([(3..5, "cd")]);
557 assert_eq!(buffer.text(), "1abcdef234");
558
559 let entries = buffer.history.undo_stack.clone();
560 assert_eq!(entries.len(), 3);
561
562 buffer.undo_or_redo(entries[0].transaction.clone());
563 assert_eq!(buffer.text(), "1cdef234");
564 buffer.undo_or_redo(entries[0].transaction.clone());
565 assert_eq!(buffer.text(), "1abcdef234");
566
567 buffer.undo_or_redo(entries[1].transaction.clone());
568 assert_eq!(buffer.text(), "1abcdx234");
569 buffer.undo_or_redo(entries[2].transaction.clone());
570 assert_eq!(buffer.text(), "1abx234");
571 buffer.undo_or_redo(entries[1].transaction.clone());
572 assert_eq!(buffer.text(), "1abyzef234");
573 buffer.undo_or_redo(entries[2].transaction.clone());
574 assert_eq!(buffer.text(), "1abcdef234");
575
576 buffer.undo_or_redo(entries[2].transaction.clone());
577 assert_eq!(buffer.text(), "1abyzef234");
578 buffer.undo_or_redo(entries[0].transaction.clone());
579 assert_eq!(buffer.text(), "1yzef234");
580 buffer.undo_or_redo(entries[1].transaction.clone());
581 assert_eq!(buffer.text(), "1234");
582}
583
584#[test]
585fn test_history() {
586 let mut now = Instant::now();
587 let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "123456");
588 buffer.set_group_interval(Duration::from_millis(300));
589
590 let transaction_1 = buffer.start_transaction_at(now).unwrap();
591 buffer.edit([(2..4, "cd")]);
592 buffer.end_transaction_at(now);
593 assert_eq!(buffer.text(), "12cd56");
594
595 buffer.start_transaction_at(now);
596 buffer.edit([(4..5, "e")]);
597 buffer.end_transaction_at(now).unwrap();
598 assert_eq!(buffer.text(), "12cde6");
599
600 now += buffer.transaction_group_interval() + Duration::from_millis(1);
601 buffer.start_transaction_at(now);
602 buffer.edit([(0..1, "a")]);
603 buffer.edit([(1..1, "b")]);
604 buffer.end_transaction_at(now).unwrap();
605 assert_eq!(buffer.text(), "ab2cde6");
606
607 // Last transaction happened past the group interval, undo it on its own.
608 buffer.undo();
609 assert_eq!(buffer.text(), "12cde6");
610
611 // First two transactions happened within the group interval, undo them together.
612 buffer.undo();
613 assert_eq!(buffer.text(), "123456");
614
615 // Redo the first two transactions together.
616 buffer.redo();
617 assert_eq!(buffer.text(), "12cde6");
618
619 // Redo the last transaction on its own.
620 buffer.redo();
621 assert_eq!(buffer.text(), "ab2cde6");
622
623 buffer.start_transaction_at(now);
624 assert!(buffer.end_transaction_at(now).is_none());
625 buffer.undo();
626 assert_eq!(buffer.text(), "12cde6");
627
628 // Redo stack gets cleared after performing an edit.
629 buffer.start_transaction_at(now);
630 buffer.edit([(0..0, "X")]);
631 buffer.end_transaction_at(now);
632 assert_eq!(buffer.text(), "X12cde6");
633 buffer.redo();
634 assert_eq!(buffer.text(), "X12cde6");
635 buffer.undo();
636 assert_eq!(buffer.text(), "12cde6");
637 buffer.undo();
638 assert_eq!(buffer.text(), "123456");
639
640 // Transactions can be grouped manually.
641 buffer.redo();
642 buffer.redo();
643 assert_eq!(buffer.text(), "X12cde6");
644 buffer.group_until_transaction(transaction_1);
645 buffer.undo();
646 assert_eq!(buffer.text(), "123456");
647 buffer.redo();
648 assert_eq!(buffer.text(), "X12cde6");
649}
650
651#[test]
652fn test_finalize_last_transaction() {
653 let now = Instant::now();
654 let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "123456");
655 buffer.history.group_interval = Duration::from_millis(1);
656
657 buffer.start_transaction_at(now);
658 buffer.edit([(2..4, "cd")]);
659 buffer.end_transaction_at(now);
660 assert_eq!(buffer.text(), "12cd56");
661
662 buffer.finalize_last_transaction();
663 buffer.start_transaction_at(now);
664 buffer.edit([(4..5, "e")]);
665 buffer.end_transaction_at(now).unwrap();
666 assert_eq!(buffer.text(), "12cde6");
667
668 buffer.start_transaction_at(now);
669 buffer.edit([(0..1, "a")]);
670 buffer.edit([(1..1, "b")]);
671 buffer.end_transaction_at(now).unwrap();
672 assert_eq!(buffer.text(), "ab2cde6");
673
674 buffer.undo();
675 assert_eq!(buffer.text(), "12cd56");
676
677 buffer.undo();
678 assert_eq!(buffer.text(), "123456");
679
680 buffer.redo();
681 assert_eq!(buffer.text(), "12cd56");
682
683 buffer.redo();
684 assert_eq!(buffer.text(), "ab2cde6");
685}
686
687#[test]
688fn test_edited_ranges_for_transaction() {
689 let now = Instant::now();
690 let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "1234567");
691
692 buffer.start_transaction_at(now);
693 buffer.edit([(2..4, "cd")]);
694 buffer.edit([(6..6, "efg")]);
695 buffer.end_transaction_at(now);
696 assert_eq!(buffer.text(), "12cd56efg7");
697
698 let tx = buffer.finalize_last_transaction().unwrap().clone();
699 assert_eq!(
700 buffer
701 .edited_ranges_for_transaction::<usize>(&tx)
702 .collect::<Vec<_>>(),
703 [2..4, 6..9]
704 );
705
706 buffer.edit([(5..5, "hijk")]);
707 assert_eq!(buffer.text(), "12cd5hijk6efg7");
708 assert_eq!(
709 buffer
710 .edited_ranges_for_transaction::<usize>(&tx)
711 .collect::<Vec<_>>(),
712 [2..4, 10..13]
713 );
714
715 buffer.edit([(4..4, "l")]);
716 assert_eq!(buffer.text(), "12cdl5hijk6efg7");
717 assert_eq!(
718 buffer
719 .edited_ranges_for_transaction::<usize>(&tx)
720 .collect::<Vec<_>>(),
721 [2..4, 11..14]
722 );
723}
724
725#[test]
726fn test_concurrent_edits() {
727 let text = "abcdef";
728
729 let mut buffer1 = Buffer::new(ReplicaId::new(1), BufferId::new(1).unwrap(), text);
730 let mut buffer2 = Buffer::new(ReplicaId::new(2), BufferId::new(1).unwrap(), text);
731 let mut buffer3 = Buffer::new(ReplicaId::new(3), BufferId::new(1).unwrap(), text);
732
733 let buf1_op = buffer1.edit([(1..2, "12")]);
734 assert_eq!(buffer1.text(), "a12cdef");
735 let buf2_op = buffer2.edit([(3..4, "34")]);
736 assert_eq!(buffer2.text(), "abc34ef");
737 let buf3_op = buffer3.edit([(5..6, "56")]);
738 assert_eq!(buffer3.text(), "abcde56");
739
740 buffer1.apply_op(buf2_op.clone());
741 buffer1.apply_op(buf3_op.clone());
742 buffer2.apply_op(buf1_op.clone());
743 buffer2.apply_op(buf3_op);
744 buffer3.apply_op(buf1_op);
745 buffer3.apply_op(buf2_op);
746
747 assert_eq!(buffer1.text(), "a12c34e56");
748 assert_eq!(buffer2.text(), "a12c34e56");
749 assert_eq!(buffer3.text(), "a12c34e56");
750}
751
752// Regression test: applying a remote edit whose FullOffset range partially
753// overlaps a fragment that was already deleted (observed but not visible)
754// used to leave the fragment unsplit, causing the rope builder to read past
755// the end of the rope.
756#[test]
757fn test_edit_partially_intersecting_a_deleted_fragment() {
758 let mut buffer = Buffer::new(ReplicaId::new(1), BufferId::new(1).unwrap(), "abcdefgh");
759
760 // Delete "cde", creating a single deleted fragment at FullOffset 2..5.
761 // After this the fragment layout is:
762 // "ab"(vis, FullOffset 0..2) "cde"(del, 2..5) "fgh"(vis, 5..8)
763 buffer.edit([(2..5, "")]);
764 assert_eq!(buffer.text(), "abfgh");
765
766 // Construct a synthetic remote edit whose version includes the deletion (so
767 // the "cde" fragment is observed + deleted → !was_visible) but whose
768 // FullOffset range only partially overlaps it. This state arises in
769 // production when concurrent edits cause different fragment splits on
770 // different replicas.
771 let synthetic_timestamp = clock::Lamport {
772 replica_id: ReplicaId::new(2),
773 value: 10,
774 };
775 let synthetic_edit = Operation::Edit(EditOperation {
776 timestamp: synthetic_timestamp,
777 version: buffer.version(),
778 // Range 1..4 partially overlaps the deleted "cde" (FullOffset 2..5):
779 // it covers "b" (1..2) and only "cd" (2..4), leaving "e" (4..5) out.
780 ranges: vec![FullOffset(1)..FullOffset(4)],
781 new_text: vec!["".into()],
782 });
783
784 // Without the fix this panics with "cannot summarize past end of rope"
785 // because the full 3-byte "cde" fragment is consumed from the deleted
786 // rope instead of only the 2-byte intersection.
787 buffer.apply_ops([synthetic_edit]);
788 assert_eq!(buffer.text(), "afgh");
789
790 buffer.undo_operations([(synthetic_timestamp, u32::MAX)].into_iter().collect());
791 assert_eq!(buffer.text(), "abfgh");
792}
793
794#[gpui::test(iterations = 100)]
795fn test_random_concurrent_edits(mut rng: StdRng) {
796 let peers = env::var("PEERS")
797 .map(|i| i.parse().expect("invalid `PEERS` variable"))
798 .unwrap_or(5);
799 let operations = env::var("OPERATIONS")
800 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
801 .unwrap_or(10);
802
803 let base_text_len = rng.random_range(0..10);
804 let base_text = RandomCharIter::new(&mut rng)
805 .take(base_text_len)
806 .collect::<String>();
807 let mut replica_ids = Vec::new();
808 let mut buffers = Vec::new();
809 let mut network = Network::new(rng.clone());
810
811 for i in 0..peers {
812 let mut buffer = Buffer::new(
813 ReplicaId::new(i as u16),
814 BufferId::new(1).unwrap(),
815 base_text.clone(),
816 );
817 buffer.history.group_interval = Duration::from_millis(rng.random_range(0..=200));
818 buffers.push(buffer);
819 replica_ids.push(ReplicaId::new(i as u16));
820 network.add_peer(ReplicaId::new(i as u16));
821 }
822
823 log::info!("initial text: {:?}", base_text);
824
825 let mut mutation_count = operations;
826 loop {
827 let replica_index = rng.random_range(0..peers);
828 let replica_id = replica_ids[replica_index];
829 let buffer = &mut buffers[replica_index];
830 match rng.random_range(0..=100) {
831 0..=50 if mutation_count != 0 => {
832 let op = buffer.randomly_edit(&mut rng, 5).1;
833 network.broadcast(buffer.replica_id, vec![op]);
834 log::info!("buffer {:?} text: {:?}", buffer.replica_id, buffer.text());
835 mutation_count -= 1;
836 }
837 51..=70 if mutation_count != 0 => {
838 let ops = buffer.randomly_undo_redo(&mut rng);
839 network.broadcast(buffer.replica_id, ops);
840 mutation_count -= 1;
841 }
842 71..=100 if network.has_unreceived(replica_id) => {
843 let ops = network.receive(replica_id);
844 if !ops.is_empty() {
845 log::info!(
846 "peer {:?} applying {} ops from the network.",
847 replica_id,
848 ops.len()
849 );
850 buffer.apply_ops(ops);
851 }
852 }
853 _ => {}
854 }
855 buffer.check_invariants();
856
857 if mutation_count == 0 && network.is_idle() {
858 break;
859 }
860 }
861
862 let first_buffer = &buffers[0];
863 for buffer in &buffers[1..] {
864 assert_eq!(
865 buffer.text(),
866 first_buffer.text(),
867 "Replica {:?} text != Replica 0 text",
868 buffer.replica_id
869 );
870 buffer.check_invariants();
871 }
872}
873
874#[test]
875fn test_new_normalized_splits_large_base_text() {
876 // ASCII text that exceeds max_insertion_len
877 let text = "abcdefghij".repeat(10); // 100 bytes
878 let rope = Rope::from(text.as_str());
879 let buffer = Buffer::new_normalized(
880 ReplicaId::LOCAL,
881 BufferId::new(1).unwrap(),
882 LineEnding::Unix,
883 rope,
884 );
885 assert_eq!(buffer.text(), text);
886 buffer.check_invariants();
887
888 // Verify anchors at various positions, including across chunk boundaries
889 for offset in [0, 1, 15, 16, 17, 50, 99] {
890 let anchor = buffer.anchor_before(offset);
891 assert_eq!(
892 anchor.to_offset(&buffer),
893 offset,
894 "anchor_before({offset}) round-tripped incorrectly"
895 );
896 let anchor = buffer.anchor_after(offset);
897 assert_eq!(
898 anchor.to_offset(&buffer),
899 offset,
900 "anchor_after({offset}) round-tripped incorrectly"
901 );
902 }
903
904 // Verify editing works after a split initialization
905 let mut buffer = buffer;
906 buffer.edit([(50..60, "XYZ")]);
907 let mut expected = text;
908 expected.replace_range(50..60, "XYZ");
909 assert_eq!(buffer.text(), expected);
910 buffer.check_invariants();
911}
912
913#[test]
914fn test_new_normalized_splits_large_base_text_with_multibyte_chars() {
915 // Use multi-byte chars (é is 2 bytes in UTF-8) so that a naive byte-level
916 // split would land in the middle of a character.
917 let unit = "ééééééééé"; // 9 chars × 2 bytes = 18 bytes
918 let text = unit.repeat(6); // 108 bytes
919 let rope = Rope::from(text.as_str());
920 let buffer = Buffer::new_normalized(
921 ReplicaId::LOCAL,
922 BufferId::new(1).unwrap(),
923 LineEnding::Unix,
924 rope,
925 );
926 assert_eq!(buffer.text(), text);
927 buffer.check_invariants();
928
929 // Every anchor should resolve correctly even though chunks had to be
930 // rounded down to a char boundary.
931 let snapshot = buffer.snapshot();
932 for offset in (0..text.len()).filter(|o| text.is_char_boundary(*o)) {
933 let anchor = snapshot.anchor_before(offset);
934 assert_eq!(
935 anchor.to_offset(snapshot),
936 offset,
937 "anchor round-trip failed at byte offset {offset}"
938 );
939 }
940}
941
942#[test]
943fn test_new_normalized_small_text_unchanged() {
944 // Text that fits in a single chunk should produce exactly one fragment,
945 // matching the original single-fragment behaviour.
946 let text = "hello world";
947 let rope = Rope::from(text);
948 let buffer = Buffer::new_normalized(
949 ReplicaId::LOCAL,
950 BufferId::new(1).unwrap(),
951 LineEnding::Unix,
952 rope,
953 );
954 assert_eq!(buffer.text(), text);
955 buffer.check_invariants();
956 assert_eq!(buffer.snapshot().fragments.items(&None).len(), 1);
957}
958
959#[test]
960fn test_edit_splits_large_insertion() {
961 let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "abcdefghij");
962
963 let large_text: Arc<str> = "X".repeat(100).into();
964 let edits = vec![(3..7, large_text.clone())];
965
966 buffer.edit(edits);
967
968 let expected = format!("abc{}hij", large_text);
969 assert_eq!(buffer.text(), expected);
970 buffer.check_invariants();
971
972 // Anchors should resolve correctly throughout the buffer.
973 for offset in [0, 3, 50, 103, expected.len()] {
974 let anchor = buffer.anchor_before(offset);
975 assert_eq!(
976 anchor.to_offset(&buffer),
977 offset,
978 "anchor_before({offset}) round-tripped incorrectly"
979 );
980 }
981}
982
983#[test]
984fn test_edit_splits_large_insertion_with_multibyte_chars() {
985 let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "abcdefghij");
986
987 // 4-byte chars so that naive byte splits would land mid-character.
988 let large_text: Arc<str> = "😀".repeat(30).into(); // 30 × 4 = 120 bytes
989 let edits = vec![(5..5, large_text.clone())];
990
991 buffer.edit(edits);
992
993 let expected = format!("abcde{}fghij", large_text);
994 assert_eq!(buffer.text(), expected);
995 buffer.check_invariants();
996}
997
998#[test]
999fn test_edit_splits_large_insertion_among_multiple_edits() {
1000 let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "ABCDEFGHIJ");
1001
1002 let large_text: Arc<str> = "x".repeat(60).into();
1003 // Three edits: small, large, small. The large one must be split while
1004 // preserving the correct positions of the surrounding edits.
1005 let edits = vec![
1006 (1..2, Arc::from("y")), // replace "B" with "y"
1007 (4..6, large_text.clone()), // replace "EF" with 60 x's
1008 (9..9, Arc::from("z")), // insert "z" before "J"
1009 ];
1010
1011 buffer.edit(edits);
1012
1013 // Original: A B C D E F G H I J
1014 // After (1..2, "y"): A y C D E F G H I J
1015 // After (4..6, large): A y C D <60 x's> G H I J
1016 // After (9..9, "z"): A y C D <60 x's> G H I z J
1017 let expected = format!("AyCD{}GHIzJ", large_text);
1018 assert_eq!(buffer.text(), expected);
1019 buffer.check_invariants();
1020}
1021
1022#[test]
1023fn test_edit_splits_multiple_large_insertions() {
1024 let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "ABCDE");
1025
1026 let text1: Arc<str> = "a".repeat(40).into();
1027 let text2: Arc<str> = "b".repeat(40).into();
1028 let edits = vec![
1029 (1..2, text1.clone()), // replace "B" with 40 a's
1030 (3..4, text2.clone()), // replace "D" with 40 b's
1031 ];
1032
1033 buffer.edit(edits);
1034
1035 let expected = format!("A{}C{}E", text1, text2);
1036 assert_eq!(buffer.text(), expected);
1037 buffer.check_invariants();
1038}
1039
1040#[test]
1041fn test_edit_undo_after_split() {
1042 let mut buffer = Buffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), "hello world");
1043 buffer.set_group_interval(Duration::from_secs(0));
1044 let original = buffer.text();
1045
1046 let large_text: Arc<str> = "Z".repeat(50).into();
1047 let edits = vec![(5..6, large_text)];
1048 buffer.edit(edits);
1049 assert_ne!(buffer.text(), original);
1050 buffer.check_invariants();
1051
1052 // Undo should restore the original text even though the edit was split
1053 // into multiple internal operations grouped in one transaction.
1054 buffer.undo();
1055 assert_eq!(buffer.text(), original);
1056 buffer.check_invariants();
1057}