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