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