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