tests.rs

  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}