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