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 language = Arc::new(
286 rust_lang()
287 .with_outline_query(
288 r#"
289 (struct_item
290 "struct" @context
291 name: (_) @name) @item
292 (enum_item
293 "enum" @context
294 name: (_) @name) @item
295 (enum_variant
296 name: (_) @name) @item
297 (field_declaration
298 name: (_) @name) @item
299 (impl_item
300 "impl" @context
301 trait: (_) @name
302 "for" @context
303 type: (_) @name) @item
304 (function_item
305 "fn" @context
306 name: (_) @name) @item
307 (mod_item
308 "mod" @context
309 name: (_) @name) @item
310 "#,
311 )
312 .unwrap(),
313 );
314
315 let text = r#"
316 struct Person {
317 name: String,
318 age: usize,
319 }
320
321 mod module {
322 enum LoginState {
323 LoggedOut,
324 LoggingOn,
325 LoggedIn {
326 person: Person,
327 time: Instant,
328 }
329 }
330 }
331
332 impl Eq for Person {}
333
334 impl Drop for Person {
335 fn drop(&mut self) {
336 println!("bye");
337 }
338 }
339 "#
340 .unindent();
341
342 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
343 let outline = buffer
344 .read_with(cx, |buffer, _| buffer.snapshot().outline(None))
345 .unwrap();
346
347 assert_eq!(
348 outline
349 .items
350 .iter()
351 .map(|item| (item.text.as_str(), item.depth))
352 .collect::<Vec<_>>(),
353 &[
354 ("struct Person", 0),
355 ("name", 1),
356 ("age", 1),
357 ("mod module", 0),
358 ("enum LoginState", 1),
359 ("LoggedOut", 2),
360 ("LoggingOn", 2),
361 ("LoggedIn", 2),
362 ("person", 3),
363 ("time", 3),
364 ("impl Eq for Person", 0),
365 ("impl Drop for Person", 0),
366 ("fn drop", 1),
367 ]
368 );
369
370 // Without space, we only match on names
371 assert_eq!(
372 search(&outline, "oon", &cx).await,
373 &[
374 ("mod module", vec![]), // included as the parent of a match
375 ("enum LoginState", vec![]), // included as the parent of a match
376 ("LoggingOn", vec![1, 7, 8]), // matches
377 ("impl Drop for Person", vec![7, 18, 19]), // matches in two disjoint names
378 ]
379 );
380
381 assert_eq!(
382 search(&outline, "dp p", &cx).await,
383 &[
384 ("impl Drop for Person", vec![5, 8, 9, 14]),
385 ("fn drop", vec![]),
386 ]
387 );
388 assert_eq!(
389 search(&outline, "dpn", &cx).await,
390 &[("impl Drop for Person", vec![5, 14, 19])]
391 );
392 assert_eq!(
393 search(&outline, "impl ", &cx).await,
394 &[
395 ("impl Eq for Person", vec![0, 1, 2, 3, 4]),
396 ("impl Drop for Person", vec![0, 1, 2, 3, 4]),
397 ("fn drop", vec![]),
398 ]
399 );
400
401 async fn search<'a>(
402 outline: &'a Outline<Anchor>,
403 query: &str,
404 cx: &gpui::TestAppContext,
405 ) -> Vec<(&'a str, Vec<usize>)> {
406 let matches = cx
407 .read(|cx| outline.search(query, cx.background().clone()))
408 .await;
409 matches
410 .into_iter()
411 .map(|mat| (outline.items[mat.candidate_id].text.as_str(), mat.positions))
412 .collect::<Vec<_>>()
413 }
414}
415
416#[gpui::test]
417fn test_enclosing_bracket_ranges(cx: &mut MutableAppContext) {
418 let buffer = cx.add_model(|cx| {
419 let text = "
420 mod x {
421 mod y {
422
423 }
424 }
425 "
426 .unindent();
427 Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx)
428 });
429 let buffer = buffer.read(cx);
430 assert_eq!(
431 buffer.enclosing_bracket_point_ranges(Point::new(1, 6)..Point::new(1, 6)),
432 Some((
433 Point::new(0, 6)..Point::new(0, 7),
434 Point::new(4, 0)..Point::new(4, 1)
435 ))
436 );
437 assert_eq!(
438 buffer.enclosing_bracket_point_ranges(Point::new(1, 10)..Point::new(1, 10)),
439 Some((
440 Point::new(1, 10)..Point::new(1, 11),
441 Point::new(3, 4)..Point::new(3, 5)
442 ))
443 );
444 assert_eq!(
445 buffer.enclosing_bracket_point_ranges(Point::new(3, 5)..Point::new(3, 5)),
446 Some((
447 Point::new(1, 10)..Point::new(1, 11),
448 Point::new(3, 4)..Point::new(3, 5)
449 ))
450 );
451}
452
453#[gpui::test]
454fn test_edit_with_autoindent(cx: &mut MutableAppContext) {
455 cx.add_model(|cx| {
456 let text = "fn a() {}";
457 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
458
459 buffer.edit_with_autoindent([8..8], "\n\n", cx);
460 assert_eq!(buffer.text(), "fn a() {\n \n}");
461
462 buffer.edit_with_autoindent([Point::new(1, 4)..Point::new(1, 4)], "b()\n", cx);
463 assert_eq!(buffer.text(), "fn a() {\n b()\n \n}");
464
465 buffer.edit_with_autoindent([Point::new(2, 4)..Point::new(2, 4)], ".c", cx);
466 assert_eq!(buffer.text(), "fn a() {\n b()\n .c\n}");
467
468 buffer
469 });
470}
471
472#[gpui::test]
473fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut MutableAppContext) {
474 cx.add_model(|cx| {
475 let text = "
476 fn a() {
477 c;
478 d;
479 }
480 "
481 .unindent();
482
483 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
484
485 // Lines 2 and 3 don't match the indentation suggestion. When editing these lines,
486 // their indentation is not adjusted.
487 buffer.edit_with_autoindent([empty(Point::new(1, 1)), empty(Point::new(2, 1))], "()", cx);
488 assert_eq!(
489 buffer.text(),
490 "
491 fn a() {
492 c();
493 d();
494 }
495 "
496 .unindent()
497 );
498
499 // When appending new content after these lines, the indentation is based on the
500 // preceding lines' actual indentation.
501 buffer.edit_with_autoindent(
502 [empty(Point::new(1, 1)), empty(Point::new(2, 1))],
503 "\n.f\n.g",
504 cx,
505 );
506 assert_eq!(
507 buffer.text(),
508 "
509 fn a() {
510 c
511 .f
512 .g();
513 d
514 .f
515 .g();
516 }
517 "
518 .unindent()
519 );
520 buffer
521 });
522}
523
524#[gpui::test]
525fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut MutableAppContext) {
526 cx.add_model(|cx| {
527 let text = "
528 fn a() {}
529 "
530 .unindent();
531
532 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
533
534 buffer.edit_with_autoindent([5..5], "\nb", cx);
535 assert_eq!(
536 buffer.text(),
537 "
538 fn a(
539 b) {}
540 "
541 .unindent()
542 );
543
544 // The indentation suggestion changed because `@end` node (a close paren)
545 // is now at the beginning of the line.
546 buffer.edit_with_autoindent([Point::new(1, 4)..Point::new(1, 5)], "", cx);
547 assert_eq!(
548 buffer.text(),
549 "
550 fn a(
551 ) {}
552 "
553 .unindent()
554 );
555
556 buffer
557 });
558}
559
560#[gpui::test]
561fn test_serialization(cx: &mut gpui::MutableAppContext) {
562 let mut now = Instant::now();
563
564 let buffer1 = cx.add_model(|cx| {
565 let mut buffer = Buffer::new(0, "abc", cx);
566 buffer.edit([3..3], "D", cx);
567
568 now += Duration::from_secs(1);
569 buffer.start_transaction_at(now);
570 buffer.edit([4..4], "E", cx);
571 buffer.end_transaction_at(now, cx);
572 assert_eq!(buffer.text(), "abcDE");
573
574 buffer.undo(cx);
575 assert_eq!(buffer.text(), "abcD");
576
577 buffer.edit([4..4], "F", cx);
578 assert_eq!(buffer.text(), "abcDF");
579 buffer
580 });
581 assert_eq!(buffer1.read(cx).text(), "abcDF");
582
583 let message = buffer1.read(cx).to_proto();
584 let buffer2 = cx.add_model(|cx| Buffer::from_proto(1, message, None, cx).unwrap());
585 assert_eq!(buffer2.read(cx).text(), "abcDF");
586}
587
588#[gpui::test(iterations = 100)]
589fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) {
590 let min_peers = env::var("MIN_PEERS")
591 .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
592 .unwrap_or(1);
593 let max_peers = env::var("MAX_PEERS")
594 .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
595 .unwrap_or(5);
596 let operations = env::var("OPERATIONS")
597 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
598 .unwrap_or(10);
599
600 let base_text_len = rng.gen_range(0..10);
601 let base_text = RandomCharIter::new(&mut rng)
602 .take(base_text_len)
603 .collect::<String>();
604 let mut replica_ids = Vec::new();
605 let mut buffers = Vec::new();
606 let network = Rc::new(RefCell::new(Network::new(rng.clone())));
607
608 for i in 0..rng.gen_range(min_peers..=max_peers) {
609 let buffer = cx.add_model(|cx| {
610 let mut buffer = Buffer::new(i as ReplicaId, base_text.as_str(), cx);
611 buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
612 let network = network.clone();
613 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
614 if let Event::Operation(op) = event {
615 network
616 .borrow_mut()
617 .broadcast(buffer.replica_id(), vec![proto::serialize_operation(&op)]);
618 }
619 })
620 .detach();
621 buffer
622 });
623 buffers.push(buffer);
624 replica_ids.push(i as ReplicaId);
625 network.borrow_mut().add_peer(i as ReplicaId);
626 log::info!("Adding initial peer with replica id {}", i);
627 }
628
629 log::info!("initial text: {:?}", base_text);
630
631 let mut now = Instant::now();
632 let mut mutation_count = operations;
633 let mut next_diagnostic_id = 0;
634 let mut active_selections = BTreeMap::default();
635 loop {
636 let replica_index = rng.gen_range(0..replica_ids.len());
637 let replica_id = replica_ids[replica_index];
638 let buffer = &mut buffers[replica_index];
639 let mut new_buffer = None;
640 match rng.gen_range(0..100) {
641 0..=29 if mutation_count != 0 => {
642 buffer.update(cx, |buffer, cx| {
643 buffer.start_transaction_at(now);
644 buffer.randomly_edit(&mut rng, 5, cx);
645 buffer.end_transaction_at(now, cx);
646 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
647 });
648 mutation_count -= 1;
649 }
650 30..=39 if mutation_count != 0 => {
651 buffer.update(cx, |buffer, cx| {
652 let mut selections = Vec::new();
653 for id in 0..rng.gen_range(1..=5) {
654 let range = buffer.random_byte_range(0, &mut rng);
655 selections.push(Selection {
656 id,
657 start: buffer.anchor_before(range.start),
658 end: buffer.anchor_before(range.end),
659 reversed: false,
660 goal: SelectionGoal::None,
661 });
662 }
663 let selections: Arc<[Selection<Anchor>]> = selections.into();
664 log::info!(
665 "peer {} setting active selections: {:?}",
666 replica_id,
667 selections
668 );
669 active_selections.insert(replica_id, selections.clone());
670 buffer.set_active_selections(selections, cx);
671 });
672 mutation_count -= 1;
673 }
674 40..=49 if mutation_count != 0 && replica_id == 0 => {
675 let entry_count = rng.gen_range(1..=5);
676 buffer.update(cx, |buffer, cx| {
677 let diagnostics = DiagnosticSet::new(
678 (0..entry_count).map(|_| {
679 let range = buffer.random_byte_range(0, &mut rng);
680 let range = range.to_point_utf16(buffer);
681 DiagnosticEntry {
682 range,
683 diagnostic: Diagnostic {
684 message: post_inc(&mut next_diagnostic_id).to_string(),
685 ..Default::default()
686 },
687 }
688 }),
689 buffer,
690 );
691 log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
692 buffer.update_diagnostics(diagnostics, cx);
693 });
694 mutation_count -= 1;
695 }
696 50..=59 if replica_ids.len() < max_peers => {
697 let old_buffer = buffer.read(cx).to_proto();
698 let new_replica_id = replica_ids.len() as ReplicaId;
699 log::info!(
700 "Adding new replica {} (replicating from {})",
701 new_replica_id,
702 replica_id
703 );
704 new_buffer = Some(cx.add_model(|cx| {
705 let mut new_buffer =
706 Buffer::from_proto(new_replica_id, old_buffer, None, cx).unwrap();
707 new_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.borrow_mut().broadcast(
712 buffer.replica_id(),
713 vec![proto::serialize_operation(&op)],
714 );
715 }
716 })
717 .detach();
718 new_buffer
719 }));
720 replica_ids.push(new_replica_id);
721 network.borrow_mut().replicate(replica_id, new_replica_id);
722 }
723 60..=69 if mutation_count != 0 => {
724 buffer.update(cx, |buffer, cx| {
725 buffer.randomly_undo_redo(&mut rng, cx);
726 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
727 });
728 mutation_count -= 1;
729 }
730 _ if network.borrow().has_unreceived(replica_id) => {
731 let ops = network
732 .borrow_mut()
733 .receive(replica_id)
734 .into_iter()
735 .map(|op| proto::deserialize_operation(op).unwrap());
736 if ops.len() > 0 {
737 log::info!(
738 "peer {} applying {} ops from the network.",
739 replica_id,
740 ops.len()
741 );
742 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
743 }
744 }
745 _ => {}
746 }
747
748 now += Duration::from_millis(rng.gen_range(0..=200));
749 buffers.extend(new_buffer);
750
751 for buffer in &buffers {
752 buffer.read(cx).check_invariants();
753 }
754
755 if mutation_count == 0 && network.borrow().is_idle() {
756 break;
757 }
758 }
759
760 let first_buffer = buffers[0].read(cx).snapshot();
761 for buffer in &buffers[1..] {
762 let buffer = buffer.read(cx).snapshot();
763 assert_eq!(
764 buffer.text(),
765 first_buffer.text(),
766 "Replica {} text != Replica 0 text",
767 buffer.replica_id()
768 );
769 assert_eq!(
770 buffer
771 .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
772 .collect::<Vec<_>>(),
773 first_buffer
774 .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
775 .collect::<Vec<_>>(),
776 "Replica {} diagnostics != Replica 0 diagnostics",
777 buffer.replica_id()
778 );
779 }
780
781 for buffer in &buffers {
782 let buffer = buffer.read(cx).snapshot();
783 let actual_remote_selections = buffer
784 .remote_selections_in_range(Anchor::min()..Anchor::max())
785 .map(|(replica_id, selections)| (replica_id, selections.collect::<Vec<_>>()))
786 .collect::<Vec<_>>();
787 let expected_remote_selections = active_selections
788 .iter()
789 .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
790 .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
791 .collect::<Vec<_>>();
792 assert_eq!(actual_remote_selections, expected_remote_selections);
793 }
794}
795
796#[test]
797fn test_contiguous_ranges() {
798 assert_eq!(
799 contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
800 &[1..4, 5..7, 9..13]
801 );
802
803 // Respects the `max_len` parameter
804 assert_eq!(
805 contiguous_ranges(
806 [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
807 3
808 )
809 .collect::<Vec<_>>(),
810 &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
811 );
812}
813
814impl Buffer {
815 pub fn enclosing_bracket_point_ranges<T: ToOffset>(
816 &self,
817 range: Range<T>,
818 ) -> Option<(Range<Point>, Range<Point>)> {
819 self.snapshot()
820 .enclosing_bracket_ranges(range)
821 .map(|(start, end)| {
822 let point_start = start.start.to_point(self)..start.end.to_point(self);
823 let point_end = end.start.to_point(self)..end.end.to_point(self);
824 (point_start, point_end)
825 })
826 }
827}
828
829fn rust_lang() -> Language {
830 Language::new(
831 LanguageConfig {
832 name: "Rust".into(),
833 path_suffixes: vec!["rs".to_string()],
834 language_server: None,
835 ..Default::default()
836 },
837 Some(tree_sitter_rust::language()),
838 )
839 .with_indents_query(
840 r#"
841 (call_expression) @indent
842 (field_expression) @indent
843 (_ "(" ")" @end) @indent
844 (_ "{" "}" @end) @indent
845 "#,
846 )
847 .unwrap()
848 .with_brackets_query(
849 r#"
850 ("{" @open "}" @close)
851 "#,
852 )
853 .unwrap()
854}
855
856fn empty(point: Point) -> Range<Point> {
857 point..point
858}