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