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