1use super::*;
2use clock::ReplicaId;
3use collections::BTreeMap;
4use gpui::{ModelHandle, MutableAppContext};
5use rand::prelude::*;
6use std::{
7 cell::RefCell,
8 env,
9 ops::Range,
10 rc::Rc,
11 time::{Duration, Instant},
12};
13use text::network::Network;
14use unindent::Unindent as _;
15use util::post_inc;
16
17#[cfg(test)]
18#[ctor::ctor]
19fn init_logger() {
20 if std::env::var("RUST_LOG").is_ok() {
21 env_logger::init();
22 }
23}
24
25#[gpui::test]
26fn test_select_language() {
27 let registry = LanguageRegistry::test();
28 registry.add(Arc::new(Language::new(
29 LanguageConfig {
30 name: "Rust".into(),
31 path_suffixes: vec!["rs".to_string()],
32 ..Default::default()
33 },
34 Some(tree_sitter_rust::language()),
35 )));
36 registry.add(Arc::new(Language::new(
37 LanguageConfig {
38 name: "Make".into(),
39 path_suffixes: vec!["Makefile".to_string(), "mk".to_string()],
40 ..Default::default()
41 },
42 Some(tree_sitter_rust::language()),
43 )));
44
45 // matching file extension
46 assert_eq!(
47 registry.select_language("zed/lib.rs").map(|l| l.name()),
48 Some("Rust".into())
49 );
50 assert_eq!(
51 registry.select_language("zed/lib.mk").map(|l| l.name()),
52 Some("Make".into())
53 );
54
55 // matching filename
56 assert_eq!(
57 registry.select_language("zed/Makefile").map(|l| l.name()),
58 Some("Make".into())
59 );
60
61 // matching suffix that is not the full file extension or filename
62 assert_eq!(registry.select_language("zed/cars").map(|l| l.name()), None);
63 assert_eq!(
64 registry.select_language("zed/a.cars").map(|l| l.name()),
65 None
66 );
67 assert_eq!(registry.select_language("zed/sumk").map(|l| l.name()), None);
68}
69
70#[gpui::test]
71fn test_edit_events(cx: &mut gpui::MutableAppContext) {
72 let mut now = Instant::now();
73 let buffer_1_events = Rc::new(RefCell::new(Vec::new()));
74 let buffer_2_events = Rc::new(RefCell::new(Vec::new()));
75
76 let buffer1 = cx.add_model(|cx| Buffer::new(0, "abcdef", cx));
77 let buffer2 = cx.add_model(|cx| Buffer::new(1, "abcdef", cx));
78 let buffer1_ops = Rc::new(RefCell::new(Vec::new()));
79 buffer1.update(cx, {
80 let buffer1_ops = buffer1_ops.clone();
81 |buffer, cx| {
82 let buffer_1_events = buffer_1_events.clone();
83 cx.subscribe(&buffer1, move |_, _, event, _| match event.clone() {
84 Event::Operation(op) => buffer1_ops.borrow_mut().push(op),
85 event @ _ => buffer_1_events.borrow_mut().push(event),
86 })
87 .detach();
88 let buffer_2_events = buffer_2_events.clone();
89 cx.subscribe(&buffer2, move |_, _, event, _| {
90 buffer_2_events.borrow_mut().push(event.clone())
91 })
92 .detach();
93
94 // An edit emits an edited event, followed by a dirtied event,
95 // since the buffer was previously in a clean state.
96 buffer.edit(Some(2..4), "XYZ", cx);
97
98 // An empty transaction does not emit any events.
99 buffer.start_transaction();
100 buffer.end_transaction(cx);
101
102 // A transaction containing two edits emits one edited event.
103 now += Duration::from_secs(1);
104 buffer.start_transaction_at(now);
105 buffer.edit(Some(5..5), "u", cx);
106 buffer.edit(Some(6..6), "w", cx);
107 buffer.end_transaction_at(now, cx);
108
109 // Undoing a transaction emits one edited event.
110 buffer.undo(cx);
111 }
112 });
113
114 // Incorporating a set of remote ops emits a single edited event,
115 // followed by a dirtied event.
116 buffer2.update(cx, |buffer, cx| {
117 buffer
118 .apply_ops(buffer1_ops.borrow_mut().drain(..), cx)
119 .unwrap();
120 });
121
122 let buffer_1_events = buffer_1_events.borrow();
123 assert_eq!(
124 *buffer_1_events,
125 vec![Event::Edited, Event::Dirtied, Event::Edited, Event::Edited]
126 );
127
128 let buffer_2_events = buffer_2_events.borrow();
129 assert_eq!(*buffer_2_events, vec![Event::Edited, Event::Dirtied]);
130}
131
132#[gpui::test]
133async fn test_apply_diff(cx: &mut gpui::TestAppContext) {
134 let text = "a\nbb\nccc\ndddd\neeeee\nffffff\n";
135 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
136
137 let text = "a\nccc\ndddd\nffffff\n";
138 let diff = buffer.read_with(cx, |b, cx| b.diff(text.into(), cx)).await;
139 buffer.update(cx, |b, cx| b.apply_diff(diff, cx));
140 cx.read(|cx| assert_eq!(buffer.read(cx).text(), text));
141
142 let text = "a\n1\n\nccc\ndd2dd\nffffff\n";
143 let diff = buffer.read_with(cx, |b, cx| b.diff(text.into(), cx)).await;
144 buffer.update(cx, |b, cx| b.apply_diff(diff, cx));
145 cx.read(|cx| assert_eq!(buffer.read(cx).text(), text));
146}
147
148#[gpui::test]
149async fn test_reparse(cx: &mut gpui::TestAppContext) {
150 let text = "fn a() {}";
151 let buffer =
152 cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
153
154 // Wait for the initial text to parse
155 buffer
156 .condition(&cx, |buffer, _| !buffer.is_parsing())
157 .await;
158 assert_eq!(
159 get_tree_sexp(&buffer, &cx),
160 concat!(
161 "(source_file (function_item name: (identifier) ",
162 "parameters: (parameters) ",
163 "body: (block)))"
164 )
165 );
166
167 buffer.update(cx, |buffer, _| {
168 buffer.set_sync_parse_timeout(Duration::ZERO)
169 });
170
171 // Perform some edits (add parameter and variable reference)
172 // Parsing doesn't begin until the transaction is complete
173 buffer.update(cx, |buf, cx| {
174 buf.start_transaction();
175
176 let offset = buf.text().find(")").unwrap();
177 buf.edit(vec![offset..offset], "b: C", cx);
178 assert!(!buf.is_parsing());
179
180 let offset = buf.text().find("}").unwrap();
181 buf.edit(vec![offset..offset], " d; ", cx);
182 assert!(!buf.is_parsing());
183
184 buf.end_transaction(cx);
185 assert_eq!(buf.text(), "fn a(b: C) { d; }");
186 assert!(buf.is_parsing());
187 });
188 buffer
189 .condition(&cx, |buffer, _| !buffer.is_parsing())
190 .await;
191 assert_eq!(
192 get_tree_sexp(&buffer, &cx),
193 concat!(
194 "(source_file (function_item name: (identifier) ",
195 "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
196 "body: (block (expression_statement (identifier)))))"
197 )
198 );
199
200 // Perform a series of edits without waiting for the current parse to complete:
201 // * turn identifier into a field expression
202 // * turn field expression into a method call
203 // * add a turbofish to the method call
204 buffer.update(cx, |buf, cx| {
205 let offset = buf.text().find(";").unwrap();
206 buf.edit(vec![offset..offset], ".e", cx);
207 assert_eq!(buf.text(), "fn a(b: C) { d.e; }");
208 assert!(buf.is_parsing());
209 });
210 buffer.update(cx, |buf, cx| {
211 let offset = buf.text().find(";").unwrap();
212 buf.edit(vec![offset..offset], "(f)", cx);
213 assert_eq!(buf.text(), "fn a(b: C) { d.e(f); }");
214 assert!(buf.is_parsing());
215 });
216 buffer.update(cx, |buf, cx| {
217 let offset = buf.text().find("(f)").unwrap();
218 buf.edit(vec![offset..offset], "::<G>", cx);
219 assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
220 assert!(buf.is_parsing());
221 });
222 buffer
223 .condition(&cx, |buffer, _| !buffer.is_parsing())
224 .await;
225 assert_eq!(
226 get_tree_sexp(&buffer, &cx),
227 concat!(
228 "(source_file (function_item name: (identifier) ",
229 "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
230 "body: (block (expression_statement (call_expression ",
231 "function: (generic_function ",
232 "function: (field_expression value: (identifier) field: (field_identifier)) ",
233 "type_arguments: (type_arguments (type_identifier))) ",
234 "arguments: (arguments (identifier)))))))",
235 )
236 );
237
238 buffer.update(cx, |buf, cx| {
239 buf.undo(cx);
240 assert_eq!(buf.text(), "fn a() {}");
241 assert!(buf.is_parsing());
242 });
243 buffer
244 .condition(&cx, |buffer, _| !buffer.is_parsing())
245 .await;
246 assert_eq!(
247 get_tree_sexp(&buffer, &cx),
248 concat!(
249 "(source_file (function_item name: (identifier) ",
250 "parameters: (parameters) ",
251 "body: (block)))"
252 )
253 );
254
255 buffer.update(cx, |buf, cx| {
256 buf.redo(cx);
257 assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
258 assert!(buf.is_parsing());
259 });
260 buffer
261 .condition(&cx, |buffer, _| !buffer.is_parsing())
262 .await;
263 assert_eq!(
264 get_tree_sexp(&buffer, &cx),
265 concat!(
266 "(source_file (function_item name: (identifier) ",
267 "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
268 "body: (block (expression_statement (call_expression ",
269 "function: (generic_function ",
270 "function: (field_expression value: (identifier) field: (field_identifier)) ",
271 "type_arguments: (type_arguments (type_identifier))) ",
272 "arguments: (arguments (identifier)))))))",
273 )
274 );
275
276 fn get_tree_sexp(buffer: &ModelHandle<Buffer>, cx: &gpui::TestAppContext) -> String {
277 buffer.read_with(cx, |buffer, _| {
278 buffer.syntax_tree().unwrap().root_node().to_sexp()
279 })
280 }
281}
282
283#[gpui::test]
284async fn test_outline(cx: &mut gpui::TestAppContext) {
285 let text = r#"
286 struct Person {
287 name: String,
288 age: usize,
289 }
290
291 mod module {
292 enum LoginState {
293 LoggedOut,
294 LoggingOn,
295 LoggedIn {
296 person: Person,
297 time: Instant,
298 }
299 }
300 }
301
302 impl Eq for Person {}
303
304 impl Drop for Person {
305 fn drop(&mut self) {
306 println!("bye");
307 }
308 }
309 "#
310 .unindent();
311
312 let buffer =
313 cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
314 let outline = buffer
315 .read_with(cx, |buffer, _| buffer.snapshot().outline(None))
316 .unwrap();
317
318 assert_eq!(
319 outline
320 .items
321 .iter()
322 .map(|item| (item.text.as_str(), item.depth))
323 .collect::<Vec<_>>(),
324 &[
325 ("struct Person", 0),
326 ("name", 1),
327 ("age", 1),
328 ("mod module", 0),
329 ("enum LoginState", 1),
330 ("LoggedOut", 2),
331 ("LoggingOn", 2),
332 ("LoggedIn", 2),
333 ("person", 3),
334 ("time", 3),
335 ("impl Eq for Person", 0),
336 ("impl Drop for Person", 0),
337 ("fn drop", 1),
338 ]
339 );
340
341 // Without space, we only match on names
342 assert_eq!(
343 search(&outline, "oon", &cx).await,
344 &[
345 ("mod module", vec![]), // included as the parent of a match
346 ("enum LoginState", vec![]), // included as the parent of a match
347 ("LoggingOn", vec![1, 7, 8]), // matches
348 ("impl Drop for Person", vec![7, 18, 19]), // matches in two disjoint names
349 ]
350 );
351
352 assert_eq!(
353 search(&outline, "dp p", &cx).await,
354 &[
355 ("impl Drop for Person", vec![5, 8, 9, 14]),
356 ("fn drop", vec![]),
357 ]
358 );
359 assert_eq!(
360 search(&outline, "dpn", &cx).await,
361 &[("impl Drop for Person", vec![5, 14, 19])]
362 );
363 assert_eq!(
364 search(&outline, "impl ", &cx).await,
365 &[
366 ("impl Eq for Person", vec![0, 1, 2, 3, 4]),
367 ("impl Drop for Person", vec![0, 1, 2, 3, 4]),
368 ("fn drop", vec![]),
369 ]
370 );
371
372 async fn search<'a>(
373 outline: &'a Outline<Anchor>,
374 query: &str,
375 cx: &gpui::TestAppContext,
376 ) -> Vec<(&'a str, Vec<usize>)> {
377 let matches = cx
378 .read(|cx| outline.search(query, cx.background().clone()))
379 .await;
380 matches
381 .into_iter()
382 .map(|mat| (outline.items[mat.candidate_id].text.as_str(), mat.positions))
383 .collect::<Vec<_>>()
384 }
385}
386
387#[gpui::test]
388async fn test_symbols_containing(cx: &mut gpui::TestAppContext) {
389 let text = r#"
390 impl Person {
391 fn one() {
392 1
393 }
394
395 fn two() {
396 2
397 }fn three() {
398 3
399 }
400 }
401 "#
402 .unindent();
403
404 let buffer =
405 cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
406 let snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot());
407
408 // point is at the start of an item
409 assert_eq!(
410 symbols_containing(Point::new(1, 4), &snapshot),
411 vec![
412 (
413 "impl Person".to_string(),
414 Point::new(0, 0)..Point::new(10, 1)
415 ),
416 ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
417 ]
418 );
419
420 // point is in the middle of an item
421 assert_eq!(
422 symbols_containing(Point::new(2, 8), &snapshot),
423 vec![
424 (
425 "impl Person".to_string(),
426 Point::new(0, 0)..Point::new(10, 1)
427 ),
428 ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
429 ]
430 );
431
432 // point is at the end of an item
433 assert_eq!(
434 symbols_containing(Point::new(3, 5), &snapshot),
435 vec![
436 (
437 "impl Person".to_string(),
438 Point::new(0, 0)..Point::new(10, 1)
439 ),
440 ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
441 ]
442 );
443
444 // point is in between two adjacent items
445 assert_eq!(
446 symbols_containing(Point::new(7, 5), &snapshot),
447 vec![
448 (
449 "impl Person".to_string(),
450 Point::new(0, 0)..Point::new(10, 1)
451 ),
452 ("fn two".to_string(), Point::new(5, 4)..Point::new(7, 5))
453 ]
454 );
455
456 fn symbols_containing<'a>(
457 position: Point,
458 snapshot: &'a BufferSnapshot,
459 ) -> Vec<(String, Range<Point>)> {
460 snapshot
461 .symbols_containing(position, None)
462 .unwrap()
463 .into_iter()
464 .map(|item| {
465 (
466 item.text,
467 item.range.start.to_point(snapshot)..item.range.end.to_point(snapshot),
468 )
469 })
470 .collect()
471 }
472}
473
474#[gpui::test]
475fn test_enclosing_bracket_ranges(cx: &mut MutableAppContext) {
476 let buffer = cx.add_model(|cx| {
477 let text = "
478 mod x {
479 mod y {
480
481 }
482 }
483 "
484 .unindent();
485 Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx)
486 });
487 let buffer = buffer.read(cx);
488 assert_eq!(
489 buffer.enclosing_bracket_point_ranges(Point::new(1, 6)..Point::new(1, 6)),
490 Some((
491 Point::new(0, 6)..Point::new(0, 7),
492 Point::new(4, 0)..Point::new(4, 1)
493 ))
494 );
495 assert_eq!(
496 buffer.enclosing_bracket_point_ranges(Point::new(1, 10)..Point::new(1, 10)),
497 Some((
498 Point::new(1, 10)..Point::new(1, 11),
499 Point::new(3, 4)..Point::new(3, 5)
500 ))
501 );
502 assert_eq!(
503 buffer.enclosing_bracket_point_ranges(Point::new(3, 5)..Point::new(3, 5)),
504 Some((
505 Point::new(1, 10)..Point::new(1, 11),
506 Point::new(3, 4)..Point::new(3, 5)
507 ))
508 );
509}
510
511#[gpui::test]
512fn test_range_for_syntax_ancestor(cx: &mut MutableAppContext) {
513 cx.add_model(|cx| {
514 let text = "fn a() { b(|c| {}) }";
515 let buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
516 let snapshot = buffer.snapshot();
517
518 assert_eq!(
519 snapshot.range_for_syntax_ancestor(empty_range_at(text, "|")),
520 Some(range_of(text, "|"))
521 );
522 assert_eq!(
523 snapshot.range_for_syntax_ancestor(range_of(text, "|")),
524 Some(range_of(text, "|c|"))
525 );
526 assert_eq!(
527 snapshot.range_for_syntax_ancestor(range_of(text, "|c|")),
528 Some(range_of(text, "|c| {}"))
529 );
530 assert_eq!(
531 snapshot.range_for_syntax_ancestor(range_of(text, "|c| {}")),
532 Some(range_of(text, "(|c| {})"))
533 );
534
535 buffer
536 });
537
538 fn empty_range_at(text: &str, part: &str) -> Range<usize> {
539 let start = text.find(part).unwrap();
540 start..start
541 }
542
543 fn range_of(text: &str, part: &str) -> Range<usize> {
544 let start = text.find(part).unwrap();
545 start..start + part.len()
546 }
547}
548
549#[gpui::test]
550fn test_edit_with_autoindent(cx: &mut MutableAppContext) {
551 cx.add_model(|cx| {
552 let text = "fn a() {}";
553 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
554
555 buffer.edit_with_autoindent([8..8], "\n\n", cx);
556 assert_eq!(buffer.text(), "fn a() {\n \n}");
557
558 buffer.edit_with_autoindent([Point::new(1, 4)..Point::new(1, 4)], "b()\n", cx);
559 assert_eq!(buffer.text(), "fn a() {\n b()\n \n}");
560
561 buffer.edit_with_autoindent([Point::new(2, 4)..Point::new(2, 4)], ".c", cx);
562 assert_eq!(buffer.text(), "fn a() {\n b()\n .c\n}");
563
564 buffer
565 });
566}
567
568#[gpui::test]
569fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut MutableAppContext) {
570 cx.add_model(|cx| {
571 let text = "
572 fn a() {
573 c;
574 d;
575 }
576 "
577 .unindent();
578
579 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
580
581 // Lines 2 and 3 don't match the indentation suggestion. When editing these lines,
582 // their indentation is not adjusted.
583 buffer.edit_with_autoindent([empty(Point::new(1, 1)), empty(Point::new(2, 1))], "()", cx);
584 assert_eq!(
585 buffer.text(),
586 "
587 fn a() {
588 c();
589 d();
590 }
591 "
592 .unindent()
593 );
594
595 // When appending new content after these lines, the indentation is based on the
596 // preceding lines' actual indentation.
597 buffer.edit_with_autoindent(
598 [empty(Point::new(1, 1)), empty(Point::new(2, 1))],
599 "\n.f\n.g",
600 cx,
601 );
602 assert_eq!(
603 buffer.text(),
604 "
605 fn a() {
606 c
607 .f
608 .g();
609 d
610 .f
611 .g();
612 }
613 "
614 .unindent()
615 );
616 buffer
617 });
618}
619
620#[gpui::test]
621fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut MutableAppContext) {
622 cx.add_model(|cx| {
623 let text = "
624 fn a() {}
625 "
626 .unindent();
627
628 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
629
630 buffer.edit_with_autoindent([5..5], "\nb", cx);
631 assert_eq!(
632 buffer.text(),
633 "
634 fn a(
635 b) {}
636 "
637 .unindent()
638 );
639
640 // The indentation suggestion changed because `@end` node (a close paren)
641 // is now at the beginning of the line.
642 buffer.edit_with_autoindent([Point::new(1, 4)..Point::new(1, 5)], "", cx);
643 assert_eq!(
644 buffer.text(),
645 "
646 fn a(
647 ) {}
648 "
649 .unindent()
650 );
651
652 buffer
653 });
654}
655
656#[gpui::test]
657fn test_serialization(cx: &mut gpui::MutableAppContext) {
658 let mut now = Instant::now();
659
660 let buffer1 = cx.add_model(|cx| {
661 let mut buffer = Buffer::new(0, "abc", cx);
662 buffer.edit([3..3], "D", cx);
663
664 now += Duration::from_secs(1);
665 buffer.start_transaction_at(now);
666 buffer.edit([4..4], "E", cx);
667 buffer.end_transaction_at(now, cx);
668 assert_eq!(buffer.text(), "abcDE");
669
670 buffer.undo(cx);
671 assert_eq!(buffer.text(), "abcD");
672
673 buffer.edit([4..4], "F", cx);
674 assert_eq!(buffer.text(), "abcDF");
675 buffer
676 });
677 assert_eq!(buffer1.read(cx).text(), "abcDF");
678
679 let message = buffer1.read(cx).to_proto();
680 let buffer2 = cx.add_model(|cx| Buffer::from_proto(1, message, None, cx).unwrap());
681 assert_eq!(buffer2.read(cx).text(), "abcDF");
682}
683
684#[gpui::test(iterations = 100)]
685fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) {
686 let min_peers = env::var("MIN_PEERS")
687 .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
688 .unwrap_or(1);
689 let max_peers = env::var("MAX_PEERS")
690 .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
691 .unwrap_or(5);
692 let operations = env::var("OPERATIONS")
693 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
694 .unwrap_or(10);
695
696 let base_text_len = rng.gen_range(0..10);
697 let base_text = RandomCharIter::new(&mut rng)
698 .take(base_text_len)
699 .collect::<String>();
700 let mut replica_ids = Vec::new();
701 let mut buffers = Vec::new();
702 let network = Rc::new(RefCell::new(Network::new(rng.clone())));
703
704 for i in 0..rng.gen_range(min_peers..=max_peers) {
705 let buffer = cx.add_model(|cx| {
706 let mut buffer = Buffer::new(i as ReplicaId, base_text.as_str(), cx);
707 buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
708 let network = network.clone();
709 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
710 if let Event::Operation(op) = event {
711 network
712 .borrow_mut()
713 .broadcast(buffer.replica_id(), vec![proto::serialize_operation(&op)]);
714 }
715 })
716 .detach();
717 buffer
718 });
719 buffers.push(buffer);
720 replica_ids.push(i as ReplicaId);
721 network.borrow_mut().add_peer(i as ReplicaId);
722 log::info!("Adding initial peer with replica id {}", i);
723 }
724
725 log::info!("initial text: {:?}", base_text);
726
727 let mut now = Instant::now();
728 let mut mutation_count = operations;
729 let mut next_diagnostic_id = 0;
730 let mut active_selections = BTreeMap::default();
731 loop {
732 let replica_index = rng.gen_range(0..replica_ids.len());
733 let replica_id = replica_ids[replica_index];
734 let buffer = &mut buffers[replica_index];
735 let mut new_buffer = None;
736 match rng.gen_range(0..100) {
737 0..=29 if mutation_count != 0 => {
738 buffer.update(cx, |buffer, cx| {
739 buffer.start_transaction_at(now);
740 buffer.randomly_edit(&mut rng, 5, cx);
741 buffer.end_transaction_at(now, cx);
742 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
743 });
744 mutation_count -= 1;
745 }
746 30..=39 if mutation_count != 0 => {
747 buffer.update(cx, |buffer, cx| {
748 let mut selections = Vec::new();
749 for id in 0..rng.gen_range(1..=5) {
750 let range = buffer.random_byte_range(0, &mut rng);
751 selections.push(Selection {
752 id,
753 start: buffer.anchor_before(range.start),
754 end: buffer.anchor_before(range.end),
755 reversed: false,
756 goal: SelectionGoal::None,
757 });
758 }
759 let selections: Arc<[Selection<Anchor>]> = selections.into();
760 log::info!(
761 "peer {} setting active selections: {:?}",
762 replica_id,
763 selections
764 );
765 active_selections.insert(replica_id, selections.clone());
766 buffer.set_active_selections(selections, cx);
767 });
768 mutation_count -= 1;
769 }
770 40..=49 if mutation_count != 0 && replica_id == 0 => {
771 let entry_count = rng.gen_range(1..=5);
772 buffer.update(cx, |buffer, cx| {
773 let diagnostics = DiagnosticSet::new(
774 (0..entry_count).map(|_| {
775 let range = buffer.random_byte_range(0, &mut rng);
776 let range = range.to_point_utf16(buffer);
777 DiagnosticEntry {
778 range,
779 diagnostic: Diagnostic {
780 message: post_inc(&mut next_diagnostic_id).to_string(),
781 ..Default::default()
782 },
783 }
784 }),
785 buffer,
786 );
787 log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
788 buffer.update_diagnostics(diagnostics, cx);
789 });
790 mutation_count -= 1;
791 }
792 50..=59 if replica_ids.len() < max_peers => {
793 let old_buffer = buffer.read(cx).to_proto();
794 let new_replica_id = replica_ids.len() as ReplicaId;
795 log::info!(
796 "Adding new replica {} (replicating from {})",
797 new_replica_id,
798 replica_id
799 );
800 new_buffer = Some(cx.add_model(|cx| {
801 let mut new_buffer =
802 Buffer::from_proto(new_replica_id, old_buffer, None, cx).unwrap();
803 new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
804 let network = network.clone();
805 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
806 if let Event::Operation(op) = event {
807 network.borrow_mut().broadcast(
808 buffer.replica_id(),
809 vec![proto::serialize_operation(&op)],
810 );
811 }
812 })
813 .detach();
814 new_buffer
815 }));
816 replica_ids.push(new_replica_id);
817 network.borrow_mut().replicate(replica_id, new_replica_id);
818 }
819 60..=69 if mutation_count != 0 => {
820 buffer.update(cx, |buffer, cx| {
821 buffer.randomly_undo_redo(&mut rng, cx);
822 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
823 });
824 mutation_count -= 1;
825 }
826 _ if network.borrow().has_unreceived(replica_id) => {
827 let ops = network
828 .borrow_mut()
829 .receive(replica_id)
830 .into_iter()
831 .map(|op| proto::deserialize_operation(op).unwrap());
832 if ops.len() > 0 {
833 log::info!(
834 "peer {} applying {} ops from the network.",
835 replica_id,
836 ops.len()
837 );
838 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
839 }
840 }
841 _ => {}
842 }
843
844 now += Duration::from_millis(rng.gen_range(0..=200));
845 buffers.extend(new_buffer);
846
847 for buffer in &buffers {
848 buffer.read(cx).check_invariants();
849 }
850
851 if mutation_count == 0 && network.borrow().is_idle() {
852 break;
853 }
854 }
855
856 let first_buffer = buffers[0].read(cx).snapshot();
857 for buffer in &buffers[1..] {
858 let buffer = buffer.read(cx).snapshot();
859 assert_eq!(
860 buffer.text(),
861 first_buffer.text(),
862 "Replica {} text != Replica 0 text",
863 buffer.replica_id()
864 );
865 assert_eq!(
866 buffer
867 .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
868 .collect::<Vec<_>>(),
869 first_buffer
870 .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
871 .collect::<Vec<_>>(),
872 "Replica {} diagnostics != Replica 0 diagnostics",
873 buffer.replica_id()
874 );
875 }
876
877 for buffer in &buffers {
878 let buffer = buffer.read(cx).snapshot();
879 let actual_remote_selections = buffer
880 .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
881 .map(|(replica_id, selections)| (replica_id, selections.collect::<Vec<_>>()))
882 .collect::<Vec<_>>();
883 let expected_remote_selections = active_selections
884 .iter()
885 .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
886 .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
887 .collect::<Vec<_>>();
888 assert_eq!(actual_remote_selections, expected_remote_selections);
889 }
890}
891
892#[test]
893fn test_contiguous_ranges() {
894 assert_eq!(
895 contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
896 &[1..4, 5..7, 9..13]
897 );
898
899 // Respects the `max_len` parameter
900 assert_eq!(
901 contiguous_ranges(
902 [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
903 3
904 )
905 .collect::<Vec<_>>(),
906 &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
907 );
908}
909
910impl Buffer {
911 pub fn enclosing_bracket_point_ranges<T: ToOffset>(
912 &self,
913 range: Range<T>,
914 ) -> Option<(Range<Point>, Range<Point>)> {
915 self.snapshot()
916 .enclosing_bracket_ranges(range)
917 .map(|(start, end)| {
918 let point_start = start.start.to_point(self)..start.end.to_point(self);
919 let point_end = end.start.to_point(self)..end.end.to_point(self);
920 (point_start, point_end)
921 })
922 }
923}
924
925fn rust_lang() -> Language {
926 Language::new(
927 LanguageConfig {
928 name: "Rust".into(),
929 path_suffixes: vec!["rs".to_string()],
930 language_server: None,
931 ..Default::default()
932 },
933 Some(tree_sitter_rust::language()),
934 )
935 .with_indents_query(
936 r#"
937 (call_expression) @indent
938 (field_expression) @indent
939 (_ "(" ")" @end) @indent
940 (_ "{" "}" @end) @indent
941 "#,
942 )
943 .unwrap()
944 .with_brackets_query(
945 r#"
946 ("{" @open "}" @close)
947 "#,
948 )
949 .unwrap()
950 .with_outline_query(
951 r#"
952 (struct_item
953 "struct" @context
954 name: (_) @name) @item
955 (enum_item
956 "enum" @context
957 name: (_) @name) @item
958 (enum_variant
959 name: (_) @name) @item
960 (field_declaration
961 name: (_) @name) @item
962 (impl_item
963 "impl" @context
964 trait: (_)? @name
965 "for"? @context
966 type: (_) @name) @item
967 (function_item
968 "fn" @context
969 name: (_) @name) @item
970 (mod_item
971 "mod" @context
972 name: (_) @name) @item
973 "#,
974 )
975 .unwrap()
976}
977
978fn empty(point: Point) -> Range<Point> {
979 point..point
980}