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_edit_with_autoindent(cx: &mut MutableAppContext) {
463 cx.add_model(|cx| {
464 let text = "fn a() {}";
465 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
466
467 buffer.edit_with_autoindent([8..8], "\n\n", cx);
468 assert_eq!(buffer.text(), "fn a() {\n \n}");
469
470 buffer.edit_with_autoindent([Point::new(1, 4)..Point::new(1, 4)], "b()\n", cx);
471 assert_eq!(buffer.text(), "fn a() {\n b()\n \n}");
472
473 buffer.edit_with_autoindent([Point::new(2, 4)..Point::new(2, 4)], ".c", cx);
474 assert_eq!(buffer.text(), "fn a() {\n b()\n .c\n}");
475
476 buffer
477 });
478}
479
480#[gpui::test]
481fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut MutableAppContext) {
482 cx.add_model(|cx| {
483 let text = "
484 fn a() {
485 c;
486 d;
487 }
488 "
489 .unindent();
490
491 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
492
493 // Lines 2 and 3 don't match the indentation suggestion. When editing these lines,
494 // their indentation is not adjusted.
495 buffer.edit_with_autoindent([empty(Point::new(1, 1)), empty(Point::new(2, 1))], "()", cx);
496 assert_eq!(
497 buffer.text(),
498 "
499 fn a() {
500 c();
501 d();
502 }
503 "
504 .unindent()
505 );
506
507 // When appending new content after these lines, the indentation is based on the
508 // preceding lines' actual indentation.
509 buffer.edit_with_autoindent(
510 [empty(Point::new(1, 1)), empty(Point::new(2, 1))],
511 "\n.f\n.g",
512 cx,
513 );
514 assert_eq!(
515 buffer.text(),
516 "
517 fn a() {
518 c
519 .f
520 .g();
521 d
522 .f
523 .g();
524 }
525 "
526 .unindent()
527 );
528 buffer
529 });
530}
531
532#[gpui::test]
533fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut MutableAppContext) {
534 cx.add_model(|cx| {
535 let text = "
536 fn a() {}
537 "
538 .unindent();
539
540 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
541
542 buffer.edit_with_autoindent([5..5], "\nb", cx);
543 assert_eq!(
544 buffer.text(),
545 "
546 fn a(
547 b) {}
548 "
549 .unindent()
550 );
551
552 // The indentation suggestion changed because `@end` node (a close paren)
553 // is now at the beginning of the line.
554 buffer.edit_with_autoindent([Point::new(1, 4)..Point::new(1, 5)], "", cx);
555 assert_eq!(
556 buffer.text(),
557 "
558 fn a(
559 ) {}
560 "
561 .unindent()
562 );
563
564 buffer
565 });
566}
567
568#[gpui::test]
569fn test_serialization(cx: &mut gpui::MutableAppContext) {
570 let mut now = Instant::now();
571
572 let buffer1 = cx.add_model(|cx| {
573 let mut buffer = Buffer::new(0, "abc", cx);
574 buffer.edit([3..3], "D", cx);
575
576 now += Duration::from_secs(1);
577 buffer.start_transaction_at(now);
578 buffer.edit([4..4], "E", cx);
579 buffer.end_transaction_at(now, cx);
580 assert_eq!(buffer.text(), "abcDE");
581
582 buffer.undo(cx);
583 assert_eq!(buffer.text(), "abcD");
584
585 buffer.edit([4..4], "F", cx);
586 assert_eq!(buffer.text(), "abcDF");
587 buffer
588 });
589 assert_eq!(buffer1.read(cx).text(), "abcDF");
590
591 let message = buffer1.read(cx).to_proto();
592 let buffer2 = cx.add_model(|cx| Buffer::from_proto(1, message, None, cx).unwrap());
593 assert_eq!(buffer2.read(cx).text(), "abcDF");
594}
595
596#[gpui::test(iterations = 100)]
597fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) {
598 let min_peers = env::var("MIN_PEERS")
599 .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
600 .unwrap_or(1);
601 let max_peers = env::var("MAX_PEERS")
602 .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
603 .unwrap_or(5);
604 let operations = env::var("OPERATIONS")
605 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
606 .unwrap_or(10);
607
608 let base_text_len = rng.gen_range(0..10);
609 let base_text = RandomCharIter::new(&mut rng)
610 .take(base_text_len)
611 .collect::<String>();
612 let mut replica_ids = Vec::new();
613 let mut buffers = Vec::new();
614 let network = Rc::new(RefCell::new(Network::new(rng.clone())));
615
616 for i in 0..rng.gen_range(min_peers..=max_peers) {
617 let buffer = cx.add_model(|cx| {
618 let mut buffer = Buffer::new(i as ReplicaId, base_text.as_str(), cx);
619 buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
620 let network = network.clone();
621 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
622 if let Event::Operation(op) = event {
623 network
624 .borrow_mut()
625 .broadcast(buffer.replica_id(), vec![proto::serialize_operation(&op)]);
626 }
627 })
628 .detach();
629 buffer
630 });
631 buffers.push(buffer);
632 replica_ids.push(i as ReplicaId);
633 network.borrow_mut().add_peer(i as ReplicaId);
634 log::info!("Adding initial peer with replica id {}", i);
635 }
636
637 log::info!("initial text: {:?}", base_text);
638
639 let mut now = Instant::now();
640 let mut mutation_count = operations;
641 let mut next_diagnostic_id = 0;
642 let mut active_selections = BTreeMap::default();
643 loop {
644 let replica_index = rng.gen_range(0..replica_ids.len());
645 let replica_id = replica_ids[replica_index];
646 let buffer = &mut buffers[replica_index];
647 let mut new_buffer = None;
648 match rng.gen_range(0..100) {
649 0..=29 if mutation_count != 0 => {
650 buffer.update(cx, |buffer, cx| {
651 buffer.start_transaction_at(now);
652 buffer.randomly_edit(&mut rng, 5, cx);
653 buffer.end_transaction_at(now, cx);
654 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
655 });
656 mutation_count -= 1;
657 }
658 30..=39 if mutation_count != 0 => {
659 buffer.update(cx, |buffer, cx| {
660 let mut selections = Vec::new();
661 for id in 0..rng.gen_range(1..=5) {
662 let range = buffer.random_byte_range(0, &mut rng);
663 selections.push(Selection {
664 id,
665 start: buffer.anchor_before(range.start),
666 end: buffer.anchor_before(range.end),
667 reversed: false,
668 goal: SelectionGoal::None,
669 });
670 }
671 let selections: Arc<[Selection<Anchor>]> = selections.into();
672 log::info!(
673 "peer {} setting active selections: {:?}",
674 replica_id,
675 selections
676 );
677 active_selections.insert(replica_id, selections.clone());
678 buffer.set_active_selections(selections, cx);
679 });
680 mutation_count -= 1;
681 }
682 40..=49 if mutation_count != 0 && replica_id == 0 => {
683 let entry_count = rng.gen_range(1..=5);
684 buffer.update(cx, |buffer, cx| {
685 let diagnostics = DiagnosticSet::new(
686 (0..entry_count).map(|_| {
687 let range = buffer.random_byte_range(0, &mut rng);
688 let range = range.to_point_utf16(buffer);
689 DiagnosticEntry {
690 range,
691 diagnostic: Diagnostic {
692 message: post_inc(&mut next_diagnostic_id).to_string(),
693 ..Default::default()
694 },
695 }
696 }),
697 buffer,
698 );
699 log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
700 buffer.update_diagnostics(diagnostics, cx);
701 });
702 mutation_count -= 1;
703 }
704 50..=59 if replica_ids.len() < max_peers => {
705 let old_buffer = buffer.read(cx).to_proto();
706 let new_replica_id = replica_ids.len() as ReplicaId;
707 log::info!(
708 "Adding new replica {} (replicating from {})",
709 new_replica_id,
710 replica_id
711 );
712 new_buffer = Some(cx.add_model(|cx| {
713 let mut new_buffer =
714 Buffer::from_proto(new_replica_id, old_buffer, None, cx).unwrap();
715 new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
716 let network = network.clone();
717 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
718 if let Event::Operation(op) = event {
719 network.borrow_mut().broadcast(
720 buffer.replica_id(),
721 vec![proto::serialize_operation(&op)],
722 );
723 }
724 })
725 .detach();
726 new_buffer
727 }));
728 replica_ids.push(new_replica_id);
729 network.borrow_mut().replicate(replica_id, new_replica_id);
730 }
731 60..=69 if mutation_count != 0 => {
732 buffer.update(cx, |buffer, cx| {
733 buffer.randomly_undo_redo(&mut rng, cx);
734 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
735 });
736 mutation_count -= 1;
737 }
738 _ if network.borrow().has_unreceived(replica_id) => {
739 let ops = network
740 .borrow_mut()
741 .receive(replica_id)
742 .into_iter()
743 .map(|op| proto::deserialize_operation(op).unwrap());
744 if ops.len() > 0 {
745 log::info!(
746 "peer {} applying {} ops from the network.",
747 replica_id,
748 ops.len()
749 );
750 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
751 }
752 }
753 _ => {}
754 }
755
756 now += Duration::from_millis(rng.gen_range(0..=200));
757 buffers.extend(new_buffer);
758
759 for buffer in &buffers {
760 buffer.read(cx).check_invariants();
761 }
762
763 if mutation_count == 0 && network.borrow().is_idle() {
764 break;
765 }
766 }
767
768 let first_buffer = buffers[0].read(cx).snapshot();
769 for buffer in &buffers[1..] {
770 let buffer = buffer.read(cx).snapshot();
771 assert_eq!(
772 buffer.text(),
773 first_buffer.text(),
774 "Replica {} text != Replica 0 text",
775 buffer.replica_id()
776 );
777 assert_eq!(
778 buffer
779 .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
780 .collect::<Vec<_>>(),
781 first_buffer
782 .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
783 .collect::<Vec<_>>(),
784 "Replica {} diagnostics != Replica 0 diagnostics",
785 buffer.replica_id()
786 );
787 }
788
789 for buffer in &buffers {
790 let buffer = buffer.read(cx).snapshot();
791 let actual_remote_selections = buffer
792 .remote_selections_in_range(Anchor::min()..Anchor::max())
793 .map(|(replica_id, selections)| (replica_id, selections.collect::<Vec<_>>()))
794 .collect::<Vec<_>>();
795 let expected_remote_selections = active_selections
796 .iter()
797 .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
798 .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
799 .collect::<Vec<_>>();
800 assert_eq!(actual_remote_selections, expected_remote_selections);
801 }
802}
803
804#[test]
805fn test_contiguous_ranges() {
806 assert_eq!(
807 contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
808 &[1..4, 5..7, 9..13]
809 );
810
811 // Respects the `max_len` parameter
812 assert_eq!(
813 contiguous_ranges(
814 [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
815 3
816 )
817 .collect::<Vec<_>>(),
818 &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
819 );
820}
821
822impl Buffer {
823 pub fn enclosing_bracket_point_ranges<T: ToOffset>(
824 &self,
825 range: Range<T>,
826 ) -> Option<(Range<Point>, Range<Point>)> {
827 self.snapshot()
828 .enclosing_bracket_ranges(range)
829 .map(|(start, end)| {
830 let point_start = start.start.to_point(self)..start.end.to_point(self);
831 let point_end = end.start.to_point(self)..end.end.to_point(self);
832 (point_start, point_end)
833 })
834 }
835}
836
837fn rust_lang() -> Language {
838 Language::new(
839 LanguageConfig {
840 name: "Rust".into(),
841 path_suffixes: vec!["rs".to_string()],
842 language_server: None,
843 ..Default::default()
844 },
845 Some(tree_sitter_rust::language()),
846 )
847 .with_indents_query(
848 r#"
849 (call_expression) @indent
850 (field_expression) @indent
851 (_ "(" ")" @end) @indent
852 (_ "{" "}" @end) @indent
853 "#,
854 )
855 .unwrap()
856 .with_brackets_query(
857 r#"
858 ("{" @open "}" @close)
859 "#,
860 )
861 .unwrap()
862}
863
864fn empty(point: Point) -> Range<Point> {
865 point..point
866}