1use super::*;
2use clock::ReplicaId;
3use collections::BTreeMap;
4use fs::LineEnding;
5use gpui::{ModelHandle, MutableAppContext};
6use indoc::indoc;
7use proto::deserialize_operation;
8use rand::prelude::*;
9use settings::Settings;
10use std::{
11 cell::RefCell,
12 env,
13 ops::Range,
14 rc::Rc,
15 time::{Duration, Instant},
16};
17use text::network::Network;
18use unindent::Unindent as _;
19use util::{assert_set_eq, post_inc, test::marked_text_ranges, RandomCharIter};
20
21#[cfg(test)]
22#[ctor::ctor]
23fn init_logger() {
24 if std::env::var("RUST_LOG").is_ok() {
25 env_logger::init();
26 }
27}
28
29#[gpui::test]
30fn test_line_endings(cx: &mut gpui::MutableAppContext) {
31 cx.set_global(Settings::test(cx));
32 cx.add_model(|cx| {
33 let mut buffer =
34 Buffer::new(0, "one\r\ntwo\rthree", cx).with_language(Arc::new(rust_lang()), cx);
35 assert_eq!(buffer.text(), "one\ntwo\nthree");
36 assert_eq!(buffer.line_ending(), LineEnding::Windows);
37
38 buffer.check_invariants();
39 buffer.edit(
40 [(buffer.len()..buffer.len(), "\r\nfour")],
41 Some(AutoindentMode::EachLine),
42 cx,
43 );
44 buffer.edit([(0..0, "zero\r\n")], None, cx);
45 assert_eq!(buffer.text(), "zero\none\ntwo\nthree\nfour");
46 assert_eq!(buffer.line_ending(), LineEnding::Windows);
47 buffer.check_invariants();
48
49 buffer
50 });
51}
52
53#[gpui::test]
54fn test_select_language() {
55 let registry = Arc::new(LanguageRegistry::test());
56 registry.add(Arc::new(Language::new(
57 LanguageConfig {
58 name: "Rust".into(),
59 path_suffixes: vec!["rs".to_string()],
60 ..Default::default()
61 },
62 Some(tree_sitter_rust::language()),
63 )));
64 registry.add(Arc::new(Language::new(
65 LanguageConfig {
66 name: "Make".into(),
67 path_suffixes: vec!["Makefile".to_string(), "mk".to_string()],
68 ..Default::default()
69 },
70 Some(tree_sitter_rust::language()),
71 )));
72
73 // matching file extension
74 assert_eq!(
75 registry.language_for_path("zed/lib.rs").map(|l| l.name()),
76 Some("Rust".into())
77 );
78 assert_eq!(
79 registry.language_for_path("zed/lib.mk").map(|l| l.name()),
80 Some("Make".into())
81 );
82
83 // matching filename
84 assert_eq!(
85 registry.language_for_path("zed/Makefile").map(|l| l.name()),
86 Some("Make".into())
87 );
88
89 // matching suffix that is not the full file extension or filename
90 assert_eq!(
91 registry.language_for_path("zed/cars").map(|l| l.name()),
92 None
93 );
94 assert_eq!(
95 registry.language_for_path("zed/a.cars").map(|l| l.name()),
96 None
97 );
98 assert_eq!(
99 registry.language_for_path("zed/sumk").map(|l| l.name()),
100 None
101 );
102}
103
104#[gpui::test]
105fn test_edit_events(cx: &mut gpui::MutableAppContext) {
106 let mut now = Instant::now();
107 let buffer_1_events = Rc::new(RefCell::new(Vec::new()));
108 let buffer_2_events = Rc::new(RefCell::new(Vec::new()));
109
110 let buffer1 = cx.add_model(|cx| Buffer::new(0, "abcdef", cx));
111 let buffer2 = cx.add_model(|cx| Buffer::new(1, "abcdef", cx));
112 let buffer1_ops = Rc::new(RefCell::new(Vec::new()));
113 buffer1.update(cx, {
114 let buffer1_ops = buffer1_ops.clone();
115 |buffer, cx| {
116 let buffer_1_events = buffer_1_events.clone();
117 cx.subscribe(&buffer1, move |_, _, event, _| match event.clone() {
118 Event::Operation(op) => buffer1_ops.borrow_mut().push(op),
119 event => buffer_1_events.borrow_mut().push(event),
120 })
121 .detach();
122 let buffer_2_events = buffer_2_events.clone();
123 cx.subscribe(&buffer2, move |_, _, event, _| {
124 buffer_2_events.borrow_mut().push(event.clone())
125 })
126 .detach();
127
128 // An edit emits an edited event, followed by a dirty changed event,
129 // since the buffer was previously in a clean state.
130 buffer.edit([(2..4, "XYZ")], None, cx);
131
132 // An empty transaction does not emit any events.
133 buffer.start_transaction();
134 buffer.end_transaction(cx);
135
136 // A transaction containing two edits emits one edited event.
137 now += Duration::from_secs(1);
138 buffer.start_transaction_at(now);
139 buffer.edit([(5..5, "u")], None, cx);
140 buffer.edit([(6..6, "w")], None, cx);
141 buffer.end_transaction_at(now, cx);
142
143 // Undoing a transaction emits one edited event.
144 buffer.undo(cx);
145 }
146 });
147
148 // Incorporating a set of remote ops emits a single edited event,
149 // followed by a dirty changed event.
150 buffer2.update(cx, |buffer, cx| {
151 buffer
152 .apply_ops(buffer1_ops.borrow_mut().drain(..), cx)
153 .unwrap();
154 });
155 assert_eq!(
156 mem::take(&mut *buffer_1_events.borrow_mut()),
157 vec![
158 Event::Edited,
159 Event::DirtyChanged,
160 Event::Edited,
161 Event::Edited,
162 ]
163 );
164 assert_eq!(
165 mem::take(&mut *buffer_2_events.borrow_mut()),
166 vec![Event::Edited, Event::DirtyChanged]
167 );
168
169 buffer1.update(cx, |buffer, cx| {
170 // Undoing the first transaction emits edited event, followed by a
171 // dirty changed event, since the buffer is again in a clean state.
172 buffer.undo(cx);
173 });
174 // Incorporating the remote ops again emits a single edited event,
175 // followed by a dirty changed event.
176 buffer2.update(cx, |buffer, cx| {
177 buffer
178 .apply_ops(buffer1_ops.borrow_mut().drain(..), cx)
179 .unwrap();
180 });
181 assert_eq!(
182 mem::take(&mut *buffer_1_events.borrow_mut()),
183 vec![Event::Edited, Event::DirtyChanged,]
184 );
185 assert_eq!(
186 mem::take(&mut *buffer_2_events.borrow_mut()),
187 vec![Event::Edited, Event::DirtyChanged]
188 );
189}
190
191#[gpui::test]
192async fn test_apply_diff(cx: &mut gpui::TestAppContext) {
193 let text = "a\nbb\nccc\ndddd\neeeee\nffffff\n";
194 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
195 let anchor = buffer.read_with(cx, |buffer, _| buffer.anchor_before(Point::new(3, 3)));
196
197 let text = "a\nccc\ndddd\nffffff\n";
198 let diff = buffer.read_with(cx, |b, cx| b.diff(text.into(), cx)).await;
199 buffer.update(cx, |buffer, cx| {
200 buffer.apply_diff(diff, cx).unwrap();
201 assert_eq!(buffer.text(), text);
202 assert_eq!(anchor.to_point(buffer), Point::new(2, 3));
203 });
204
205 let text = "a\n1\n\nccc\ndd2dd\nffffff\n";
206 let diff = buffer.read_with(cx, |b, cx| b.diff(text.into(), cx)).await;
207 buffer.update(cx, |buffer, cx| {
208 buffer.apply_diff(diff, cx).unwrap();
209 assert_eq!(buffer.text(), text);
210 assert_eq!(anchor.to_point(buffer), Point::new(4, 4));
211 });
212}
213
214#[gpui::test]
215async fn test_reparse(cx: &mut gpui::TestAppContext) {
216 let text = "fn a() {}";
217 let buffer =
218 cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
219
220 // Wait for the initial text to parse
221 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
222 assert_eq!(
223 get_tree_sexp(&buffer, cx),
224 concat!(
225 "(source_file (function_item name: (identifier) ",
226 "parameters: (parameters) ",
227 "body: (block)))"
228 )
229 );
230
231 buffer.update(cx, |buffer, _| {
232 buffer.set_sync_parse_timeout(Duration::ZERO)
233 });
234
235 // Perform some edits (add parameter and variable reference)
236 // Parsing doesn't begin until the transaction is complete
237 buffer.update(cx, |buf, cx| {
238 buf.start_transaction();
239
240 let offset = buf.text().find(')').unwrap();
241 buf.edit([(offset..offset, "b: C")], None, cx);
242 assert!(!buf.is_parsing());
243
244 let offset = buf.text().find('}').unwrap();
245 buf.edit([(offset..offset, " d; ")], None, cx);
246 assert!(!buf.is_parsing());
247
248 buf.end_transaction(cx);
249 assert_eq!(buf.text(), "fn a(b: C) { d; }");
250 assert!(buf.is_parsing());
251 });
252 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
253 assert_eq!(
254 get_tree_sexp(&buffer, cx),
255 concat!(
256 "(source_file (function_item name: (identifier) ",
257 "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
258 "body: (block (expression_statement (identifier)))))"
259 )
260 );
261
262 // Perform a series of edits without waiting for the current parse to complete:
263 // * turn identifier into a field expression
264 // * turn field expression into a method call
265 // * add a turbofish to the method call
266 buffer.update(cx, |buf, cx| {
267 let offset = buf.text().find(';').unwrap();
268 buf.edit([(offset..offset, ".e")], None, cx);
269 assert_eq!(buf.text(), "fn a(b: C) { d.e; }");
270 assert!(buf.is_parsing());
271 });
272 buffer.update(cx, |buf, cx| {
273 let offset = buf.text().find(';').unwrap();
274 buf.edit([(offset..offset, "(f)")], None, cx);
275 assert_eq!(buf.text(), "fn a(b: C) { d.e(f); }");
276 assert!(buf.is_parsing());
277 });
278 buffer.update(cx, |buf, cx| {
279 let offset = buf.text().find("(f)").unwrap();
280 buf.edit([(offset..offset, "::<G>")], None, cx);
281 assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
282 assert!(buf.is_parsing());
283 });
284 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
285 assert_eq!(
286 get_tree_sexp(&buffer, cx),
287 concat!(
288 "(source_file (function_item name: (identifier) ",
289 "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
290 "body: (block (expression_statement (call_expression ",
291 "function: (generic_function ",
292 "function: (field_expression value: (identifier) field: (field_identifier)) ",
293 "type_arguments: (type_arguments (type_identifier))) ",
294 "arguments: (arguments (identifier)))))))",
295 )
296 );
297
298 buffer.update(cx, |buf, cx| {
299 buf.undo(cx);
300 buf.undo(cx);
301 buf.undo(cx);
302 buf.undo(cx);
303 assert_eq!(buf.text(), "fn a() {}");
304 assert!(buf.is_parsing());
305 });
306 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
307 assert_eq!(
308 get_tree_sexp(&buffer, cx),
309 concat!(
310 "(source_file (function_item name: (identifier) ",
311 "parameters: (parameters) ",
312 "body: (block)))"
313 )
314 );
315
316 buffer.update(cx, |buf, cx| {
317 buf.redo(cx);
318 buf.redo(cx);
319 buf.redo(cx);
320 buf.redo(cx);
321 assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
322 assert!(buf.is_parsing());
323 });
324 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
325 assert_eq!(
326 get_tree_sexp(&buffer, cx),
327 concat!(
328 "(source_file (function_item name: (identifier) ",
329 "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
330 "body: (block (expression_statement (call_expression ",
331 "function: (generic_function ",
332 "function: (field_expression value: (identifier) field: (field_identifier)) ",
333 "type_arguments: (type_arguments (type_identifier))) ",
334 "arguments: (arguments (identifier)))))))",
335 )
336 );
337}
338
339#[gpui::test]
340async fn test_resetting_language(cx: &mut gpui::TestAppContext) {
341 let buffer = cx.add_model(|cx| {
342 let mut buffer = Buffer::new(0, "{}", cx).with_language(Arc::new(rust_lang()), cx);
343 buffer.set_sync_parse_timeout(Duration::ZERO);
344 buffer
345 });
346
347 // Wait for the initial text to parse
348 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
349 assert_eq!(
350 get_tree_sexp(&buffer, cx),
351 "(source_file (expression_statement (block)))"
352 );
353
354 buffer.update(cx, |buffer, cx| {
355 buffer.set_language(Some(Arc::new(json_lang())), cx)
356 });
357 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
358 assert_eq!(get_tree_sexp(&buffer, cx), "(document (object))");
359}
360
361#[gpui::test]
362async fn test_outline(cx: &mut gpui::TestAppContext) {
363 let text = r#"
364 struct Person {
365 name: String,
366 age: usize,
367 }
368
369 mod module {
370 enum LoginState {
371 LoggedOut,
372 LoggingOn,
373 LoggedIn {
374 person: Person,
375 time: Instant,
376 }
377 }
378 }
379
380 impl Eq for Person {}
381
382 impl Drop for Person {
383 fn drop(&mut self) {
384 println!("bye");
385 }
386 }
387 "#
388 .unindent();
389
390 let buffer =
391 cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
392 let outline = buffer
393 .read_with(cx, |buffer, _| buffer.snapshot().outline(None))
394 .unwrap();
395
396 assert_eq!(
397 outline
398 .items
399 .iter()
400 .map(|item| (item.text.as_str(), item.depth))
401 .collect::<Vec<_>>(),
402 &[
403 ("struct Person", 0),
404 ("name", 1),
405 ("age", 1),
406 ("mod module", 0),
407 ("enum LoginState", 1),
408 ("LoggedOut", 2),
409 ("LoggingOn", 2),
410 ("LoggedIn", 2),
411 ("person", 3),
412 ("time", 3),
413 ("impl Eq for Person", 0),
414 ("impl Drop for Person", 0),
415 ("fn drop", 1),
416 ]
417 );
418
419 // Without space, we only match on names
420 assert_eq!(
421 search(&outline, "oon", cx).await,
422 &[
423 ("mod module", vec![]), // included as the parent of a match
424 ("enum LoginState", vec![]), // included as the parent of a match
425 ("LoggingOn", vec![1, 7, 8]), // matches
426 ("impl Drop for Person", vec![7, 18, 19]), // matches in two disjoint names
427 ]
428 );
429
430 assert_eq!(
431 search(&outline, "dp p", cx).await,
432 &[
433 ("impl Drop for Person", vec![5, 8, 9, 14]),
434 ("fn drop", vec![]),
435 ]
436 );
437 assert_eq!(
438 search(&outline, "dpn", cx).await,
439 &[("impl Drop for Person", vec![5, 14, 19])]
440 );
441 assert_eq!(
442 search(&outline, "impl ", cx).await,
443 &[
444 ("impl Eq for Person", vec![0, 1, 2, 3, 4]),
445 ("impl Drop for Person", vec![0, 1, 2, 3, 4]),
446 ("fn drop", vec![]),
447 ]
448 );
449
450 async fn search<'a>(
451 outline: &'a Outline<Anchor>,
452 query: &'a str,
453 cx: &'a gpui::TestAppContext,
454 ) -> Vec<(&'a str, Vec<usize>)> {
455 let matches = cx
456 .read(|cx| outline.search(query, cx.background().clone()))
457 .await;
458 matches
459 .into_iter()
460 .map(|mat| (outline.items[mat.candidate_id].text.as_str(), mat.positions))
461 .collect::<Vec<_>>()
462 }
463}
464
465#[gpui::test]
466async fn test_outline_nodes_with_newlines(cx: &mut gpui::TestAppContext) {
467 let text = r#"
468 impl A for B<
469 C
470 > {
471 };
472 "#
473 .unindent();
474
475 let buffer =
476 cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
477 let outline = buffer
478 .read_with(cx, |buffer, _| buffer.snapshot().outline(None))
479 .unwrap();
480
481 assert_eq!(
482 outline
483 .items
484 .iter()
485 .map(|item| (item.text.as_str(), item.depth))
486 .collect::<Vec<_>>(),
487 &[("impl A for B<", 0)]
488 );
489}
490
491#[gpui::test]
492async fn test_symbols_containing(cx: &mut gpui::TestAppContext) {
493 let text = r#"
494 impl Person {
495 fn one() {
496 1
497 }
498
499 fn two() {
500 2
501 }fn three() {
502 3
503 }
504 }
505 "#
506 .unindent();
507
508 let buffer =
509 cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
510 let snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot());
511
512 // point is at the start of an item
513 assert_eq!(
514 symbols_containing(Point::new(1, 4), &snapshot),
515 vec![
516 (
517 "impl Person".to_string(),
518 Point::new(0, 0)..Point::new(10, 1)
519 ),
520 ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
521 ]
522 );
523
524 // point is in the middle of an item
525 assert_eq!(
526 symbols_containing(Point::new(2, 8), &snapshot),
527 vec![
528 (
529 "impl Person".to_string(),
530 Point::new(0, 0)..Point::new(10, 1)
531 ),
532 ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
533 ]
534 );
535
536 // point is at the end of an item
537 assert_eq!(
538 symbols_containing(Point::new(3, 5), &snapshot),
539 vec![
540 (
541 "impl Person".to_string(),
542 Point::new(0, 0)..Point::new(10, 1)
543 ),
544 ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
545 ]
546 );
547
548 // point is in between two adjacent items
549 assert_eq!(
550 symbols_containing(Point::new(7, 5), &snapshot),
551 vec![
552 (
553 "impl Person".to_string(),
554 Point::new(0, 0)..Point::new(10, 1)
555 ),
556 ("fn two".to_string(), Point::new(5, 4)..Point::new(7, 5))
557 ]
558 );
559
560 fn symbols_containing(
561 position: Point,
562 snapshot: &BufferSnapshot,
563 ) -> Vec<(String, Range<Point>)> {
564 snapshot
565 .symbols_containing(position, None)
566 .unwrap()
567 .into_iter()
568 .map(|item| {
569 (
570 item.text,
571 item.range.start.to_point(snapshot)..item.range.end.to_point(snapshot),
572 )
573 })
574 .collect()
575 }
576}
577
578#[gpui::test]
579fn test_enclosing_bracket_ranges(cx: &mut MutableAppContext) {
580 let mut assert = |selection_text, range_markers| {
581 assert_enclosing_bracket_pairs(selection_text, range_markers, rust_lang(), cx)
582 };
583
584 assert(
585 indoc! {"
586 mod x {
587 moˇd y {
588
589 }
590 }
591 let foo = 1;"},
592 vec![indoc! {"
593 mod x «{»
594 mod y {
595
596 }
597 «}»
598 let foo = 1;"}],
599 );
600
601 assert(
602 indoc! {"
603 mod x {
604 mod y ˇ{
605
606 }
607 }
608 let foo = 1;"},
609 vec![
610 indoc! {"
611 mod x «{»
612 mod y {
613
614 }
615 «}»
616 let foo = 1;"},
617 indoc! {"
618 mod x {
619 mod y «{»
620
621 «}»
622 }
623 let foo = 1;"},
624 ],
625 );
626
627 assert(
628 indoc! {"
629 mod x {
630 mod y {
631
632 }ˇ
633 }
634 let foo = 1;"},
635 vec![
636 indoc! {"
637 mod x «{»
638 mod y {
639
640 }
641 «}»
642 let foo = 1;"},
643 indoc! {"
644 mod x {
645 mod y «{»
646
647 «}»
648 }
649 let foo = 1;"},
650 ],
651 );
652
653 assert(
654 indoc! {"
655 mod x {
656 mod y {
657
658 }
659 ˇ}
660 let foo = 1;"},
661 vec![indoc! {"
662 mod x «{»
663 mod y {
664
665 }
666 «}»
667 let foo = 1;"}],
668 );
669
670 assert(
671 indoc! {"
672 mod x {
673 mod y {
674
675 }
676 }
677 let fˇoo = 1;"},
678 vec![],
679 );
680
681 // Regression test: avoid crash when querying at the end of the buffer.
682 assert(
683 indoc! {"
684 mod x {
685 mod y {
686
687 }
688 }
689 let foo = 1;ˇ"},
690 vec![],
691 );
692}
693
694#[gpui::test]
695fn test_enclosing_bracket_ranges_where_brackets_are_not_outermost_children(
696 cx: &mut MutableAppContext,
697) {
698 let mut assert = |selection_text, bracket_pair_texts| {
699 assert_enclosing_bracket_pairs(selection_text, bracket_pair_texts, javascript_lang(), cx)
700 };
701
702 assert(
703 indoc! {"
704 for (const a in b)ˇ {
705 // a comment that's longer than the for-loop header
706 }"},
707 vec![indoc! {"
708 for «(»const a in b«)» {
709 // a comment that's longer than the for-loop header
710 }"}],
711 );
712
713 // Regression test: even though the parent node of the parentheses (the for loop) does
714 // intersect the given range, the parentheses themselves do not contain the range, so
715 // they should not be returned. Only the curly braces contain the range.
716 assert(
717 indoc! {"
718 for (const a in b) {ˇ
719 // a comment that's longer than the for-loop header
720 }"},
721 vec![indoc! {"
722 for (const a in b) «{»
723 // a comment that's longer than the for-loop header
724 «}»"}],
725 );
726}
727
728#[gpui::test]
729fn test_range_for_syntax_ancestor(cx: &mut MutableAppContext) {
730 cx.add_model(|cx| {
731 let text = "fn a() { b(|c| {}) }";
732 let buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
733 let snapshot = buffer.snapshot();
734
735 assert_eq!(
736 snapshot.range_for_syntax_ancestor(empty_range_at(text, "|")),
737 Some(range_of(text, "|"))
738 );
739 assert_eq!(
740 snapshot.range_for_syntax_ancestor(range_of(text, "|")),
741 Some(range_of(text, "|c|"))
742 );
743 assert_eq!(
744 snapshot.range_for_syntax_ancestor(range_of(text, "|c|")),
745 Some(range_of(text, "|c| {}"))
746 );
747 assert_eq!(
748 snapshot.range_for_syntax_ancestor(range_of(text, "|c| {}")),
749 Some(range_of(text, "(|c| {})"))
750 );
751
752 buffer
753 });
754
755 fn empty_range_at(text: &str, part: &str) -> Range<usize> {
756 let start = text.find(part).unwrap();
757 start..start
758 }
759
760 fn range_of(text: &str, part: &str) -> Range<usize> {
761 let start = text.find(part).unwrap();
762 start..start + part.len()
763 }
764}
765
766#[gpui::test]
767fn test_autoindent_with_soft_tabs(cx: &mut MutableAppContext) {
768 let settings = Settings::test(cx);
769 cx.set_global(settings);
770
771 cx.add_model(|cx| {
772 let text = "fn a() {}";
773 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
774
775 buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
776 assert_eq!(buffer.text(), "fn a() {\n \n}");
777
778 buffer.edit(
779 [(Point::new(1, 4)..Point::new(1, 4), "b()\n")],
780 Some(AutoindentMode::EachLine),
781 cx,
782 );
783 assert_eq!(buffer.text(), "fn a() {\n b()\n \n}");
784
785 // Create a field expression on a new line, causing that line
786 // to be indented.
787 buffer.edit(
788 [(Point::new(2, 4)..Point::new(2, 4), ".c")],
789 Some(AutoindentMode::EachLine),
790 cx,
791 );
792 assert_eq!(buffer.text(), "fn a() {\n b()\n .c\n}");
793
794 // Remove the dot so that the line is no longer a field expression,
795 // causing the line to be outdented.
796 buffer.edit(
797 [(Point::new(2, 8)..Point::new(2, 9), "")],
798 Some(AutoindentMode::EachLine),
799 cx,
800 );
801 assert_eq!(buffer.text(), "fn a() {\n b()\n c\n}");
802
803 buffer
804 });
805}
806
807#[gpui::test]
808fn test_autoindent_with_hard_tabs(cx: &mut MutableAppContext) {
809 let mut settings = Settings::test(cx);
810 settings.editor_overrides.hard_tabs = Some(true);
811 cx.set_global(settings);
812
813 cx.add_model(|cx| {
814 let text = "fn a() {}";
815 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
816
817 buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
818 assert_eq!(buffer.text(), "fn a() {\n\t\n}");
819
820 buffer.edit(
821 [(Point::new(1, 1)..Point::new(1, 1), "b()\n")],
822 Some(AutoindentMode::EachLine),
823 cx,
824 );
825 assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\n}");
826
827 // Create a field expression on a new line, causing that line
828 // to be indented.
829 buffer.edit(
830 [(Point::new(2, 1)..Point::new(2, 1), ".c")],
831 Some(AutoindentMode::EachLine),
832 cx,
833 );
834 assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\t.c\n}");
835
836 // Remove the dot so that the line is no longer a field expression,
837 // causing the line to be outdented.
838 buffer.edit(
839 [(Point::new(2, 2)..Point::new(2, 3), "")],
840 Some(AutoindentMode::EachLine),
841 cx,
842 );
843 assert_eq!(buffer.text(), "fn a() {\n\tb()\n\tc\n}");
844
845 buffer
846 });
847}
848
849#[gpui::test]
850fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut MutableAppContext) {
851 let settings = Settings::test(cx);
852 cx.set_global(settings);
853
854 cx.add_model(|cx| {
855 let mut buffer = Buffer::new(
856 0,
857 "
858 fn a() {
859 c;
860 d;
861 }
862 "
863 .unindent(),
864 cx,
865 )
866 .with_language(Arc::new(rust_lang()), cx);
867
868 // Lines 2 and 3 don't match the indentation suggestion. When editing these lines,
869 // their indentation is not adjusted.
870 buffer.edit_via_marked_text(
871 &"
872 fn a() {
873 c«()»;
874 d«()»;
875 }
876 "
877 .unindent(),
878 Some(AutoindentMode::EachLine),
879 cx,
880 );
881 assert_eq!(
882 buffer.text(),
883 "
884 fn a() {
885 c();
886 d();
887 }
888 "
889 .unindent()
890 );
891
892 // When appending new content after these lines, the indentation is based on the
893 // preceding lines' actual indentation.
894 buffer.edit_via_marked_text(
895 &"
896 fn a() {
897 c«
898 .f
899 .g()»;
900 d«
901 .f
902 .g()»;
903 }
904 "
905 .unindent(),
906 Some(AutoindentMode::EachLine),
907 cx,
908 );
909
910 assert_eq!(
911 buffer.text(),
912 "
913 fn a() {
914 c
915 .f
916 .g();
917 d
918 .f
919 .g();
920 }
921 "
922 .unindent()
923 );
924 buffer
925 });
926
927 cx.add_model(|cx| {
928 let mut buffer = Buffer::new(
929 0,
930 "
931 fn a() {
932 b();
933 |
934 "
935 .replace("|", "") // marker to preserve trailing whitespace
936 .unindent(),
937 cx,
938 )
939 .with_language(Arc::new(rust_lang()), cx);
940
941 // Insert a closing brace. It is outdented.
942 buffer.edit_via_marked_text(
943 &"
944 fn a() {
945 b();
946 «}»
947 "
948 .unindent(),
949 Some(AutoindentMode::EachLine),
950 cx,
951 );
952 assert_eq!(
953 buffer.text(),
954 "
955 fn a() {
956 b();
957 }
958 "
959 .unindent()
960 );
961
962 // Manually edit the leading whitespace. The edit is preserved.
963 buffer.edit_via_marked_text(
964 &"
965 fn a() {
966 b();
967 « »}
968 "
969 .unindent(),
970 Some(AutoindentMode::EachLine),
971 cx,
972 );
973 assert_eq!(
974 buffer.text(),
975 "
976 fn a() {
977 b();
978 }
979 "
980 .unindent()
981 );
982 buffer
983 });
984}
985
986#[gpui::test]
987fn test_autoindent_does_not_adjust_lines_within_newly_created_errors(cx: &mut MutableAppContext) {
988 let settings = Settings::test(cx);
989 cx.set_global(settings);
990
991 cx.add_model(|cx| {
992 let mut buffer = Buffer::new(
993 0,
994 "
995 fn a() {
996 i
997 }
998 "
999 .unindent(),
1000 cx,
1001 )
1002 .with_language(Arc::new(rust_lang()), cx);
1003
1004 // Regression test: line does not get outdented due to syntax error
1005 buffer.edit_via_marked_text(
1006 &"
1007 fn a() {
1008 i«f let Some(x) = y»
1009 }
1010 "
1011 .unindent(),
1012 Some(AutoindentMode::EachLine),
1013 cx,
1014 );
1015 assert_eq!(
1016 buffer.text(),
1017 "
1018 fn a() {
1019 if let Some(x) = y
1020 }
1021 "
1022 .unindent()
1023 );
1024
1025 buffer.edit_via_marked_text(
1026 &"
1027 fn a() {
1028 if let Some(x) = y« {»
1029 }
1030 "
1031 .unindent(),
1032 Some(AutoindentMode::EachLine),
1033 cx,
1034 );
1035 assert_eq!(
1036 buffer.text(),
1037 "
1038 fn a() {
1039 if let Some(x) = y {
1040 }
1041 "
1042 .unindent()
1043 );
1044
1045 buffer
1046 });
1047}
1048
1049#[gpui::test]
1050fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut MutableAppContext) {
1051 cx.set_global(Settings::test(cx));
1052 cx.add_model(|cx| {
1053 let mut buffer = Buffer::new(
1054 0,
1055 "
1056 fn a() {}
1057 "
1058 .unindent(),
1059 cx,
1060 )
1061 .with_language(Arc::new(rust_lang()), cx);
1062
1063 buffer.edit_via_marked_text(
1064 &"
1065 fn a(«
1066 b») {}
1067 "
1068 .unindent(),
1069 Some(AutoindentMode::EachLine),
1070 cx,
1071 );
1072 assert_eq!(
1073 buffer.text(),
1074 "
1075 fn a(
1076 b) {}
1077 "
1078 .unindent()
1079 );
1080
1081 // The indentation suggestion changed because `@end` node (a close paren)
1082 // is now at the beginning of the line.
1083 buffer.edit_via_marked_text(
1084 &"
1085 fn a(
1086 ˇ) {}
1087 "
1088 .unindent(),
1089 Some(AutoindentMode::EachLine),
1090 cx,
1091 );
1092 assert_eq!(
1093 buffer.text(),
1094 "
1095 fn a(
1096 ) {}
1097 "
1098 .unindent()
1099 );
1100
1101 buffer
1102 });
1103}
1104
1105#[gpui::test]
1106fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut MutableAppContext) {
1107 cx.set_global(Settings::test(cx));
1108 cx.add_model(|cx| {
1109 let text = "a\nb";
1110 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
1111 buffer.edit(
1112 [(0..1, "\n"), (2..3, "\n")],
1113 Some(AutoindentMode::EachLine),
1114 cx,
1115 );
1116 assert_eq!(buffer.text(), "\n\n\n");
1117 buffer
1118 });
1119}
1120
1121#[gpui::test]
1122fn test_autoindent_multi_line_insertion(cx: &mut MutableAppContext) {
1123 cx.set_global(Settings::test(cx));
1124 cx.add_model(|cx| {
1125 let text = "
1126 const a: usize = 1;
1127 fn b() {
1128 if c {
1129 let d = 2;
1130 }
1131 }
1132 "
1133 .unindent();
1134
1135 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
1136 buffer.edit(
1137 [(Point::new(3, 0)..Point::new(3, 0), "e(\n f()\n);\n")],
1138 Some(AutoindentMode::EachLine),
1139 cx,
1140 );
1141 assert_eq!(
1142 buffer.text(),
1143 "
1144 const a: usize = 1;
1145 fn b() {
1146 if c {
1147 e(
1148 f()
1149 );
1150 let d = 2;
1151 }
1152 }
1153 "
1154 .unindent()
1155 );
1156
1157 buffer
1158 });
1159}
1160
1161#[gpui::test]
1162fn test_autoindent_block_mode(cx: &mut MutableAppContext) {
1163 cx.set_global(Settings::test(cx));
1164 cx.add_model(|cx| {
1165 let text = r#"
1166 fn a() {
1167 b();
1168 }
1169 "#
1170 .unindent();
1171 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
1172
1173 // When this text was copied, both of the quotation marks were at the same
1174 // indent level, but the indentation of the first line was not included in
1175 // the copied text. This information is retained in the
1176 // 'original_indent_columns' vector.
1177 let original_indent_columns = vec![4];
1178 let inserted_text = r#"
1179 "
1180 c
1181 d
1182 e
1183 "
1184 "#
1185 .unindent();
1186
1187 // Insert the block at column zero. The entire block is indented
1188 // so that the first line matches the previous line's indentation.
1189 buffer.edit(
1190 [(Point::new(2, 0)..Point::new(2, 0), inserted_text.clone())],
1191 Some(AutoindentMode::Block {
1192 original_indent_columns: original_indent_columns.clone(),
1193 }),
1194 cx,
1195 );
1196 assert_eq!(
1197 buffer.text(),
1198 r#"
1199 fn a() {
1200 b();
1201 "
1202 c
1203 d
1204 e
1205 "
1206 }
1207 "#
1208 .unindent()
1209 );
1210
1211 // Grouping is disabled in tests, so we need 2 undos
1212 buffer.undo(cx); // Undo the auto-indent
1213 buffer.undo(cx); // Undo the original edit
1214
1215 // Insert the block at a deeper indent level. The entire block is outdented.
1216 buffer.edit([(Point::new(2, 0)..Point::new(2, 0), " ")], None, cx);
1217 buffer.edit(
1218 [(Point::new(2, 8)..Point::new(2, 8), inserted_text)],
1219 Some(AutoindentMode::Block {
1220 original_indent_columns: original_indent_columns.clone(),
1221 }),
1222 cx,
1223 );
1224 assert_eq!(
1225 buffer.text(),
1226 r#"
1227 fn a() {
1228 b();
1229 "
1230 c
1231 d
1232 e
1233 "
1234 }
1235 "#
1236 .unindent()
1237 );
1238
1239 buffer
1240 });
1241}
1242
1243#[gpui::test]
1244fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut MutableAppContext) {
1245 cx.set_global(Settings::test(cx));
1246 cx.add_model(|cx| {
1247 let text = r#"
1248 fn a() {
1249 if b() {
1250
1251 }
1252 }
1253 "#
1254 .unindent();
1255 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
1256
1257 // The original indent columns are not known, so this text is
1258 // auto-indented in a block as if the first line was copied in
1259 // its entirety.
1260 let original_indent_columns = Vec::new();
1261 let inserted_text = " c\n .d()\n .e();";
1262
1263 // Insert the block at column zero. The entire block is indented
1264 // so that the first line matches the previous line's indentation.
1265 buffer.edit(
1266 [(Point::new(2, 0)..Point::new(2, 0), inserted_text.clone())],
1267 Some(AutoindentMode::Block {
1268 original_indent_columns: original_indent_columns.clone(),
1269 }),
1270 cx,
1271 );
1272 assert_eq!(
1273 buffer.text(),
1274 r#"
1275 fn a() {
1276 if b() {
1277 c
1278 .d()
1279 .e();
1280 }
1281 }
1282 "#
1283 .unindent()
1284 );
1285
1286 // Grouping is disabled in tests, so we need 2 undos
1287 buffer.undo(cx); // Undo the auto-indent
1288 buffer.undo(cx); // Undo the original edit
1289
1290 // Insert the block at a deeper indent level. The entire block is outdented.
1291 buffer.edit(
1292 [(Point::new(2, 0)..Point::new(2, 0), " ".repeat(12))],
1293 None,
1294 cx,
1295 );
1296 buffer.edit(
1297 [(Point::new(2, 12)..Point::new(2, 12), inserted_text)],
1298 Some(AutoindentMode::Block {
1299 original_indent_columns: Vec::new(),
1300 }),
1301 cx,
1302 );
1303 assert_eq!(
1304 buffer.text(),
1305 r#"
1306 fn a() {
1307 if b() {
1308 c
1309 .d()
1310 .e();
1311 }
1312 }
1313 "#
1314 .unindent()
1315 );
1316
1317 buffer
1318 });
1319}
1320
1321#[gpui::test]
1322fn test_autoindent_language_without_indents_query(cx: &mut MutableAppContext) {
1323 cx.set_global(Settings::test(cx));
1324 cx.add_model(|cx| {
1325 let text = "
1326 * one
1327 - a
1328 - b
1329 * two
1330 "
1331 .unindent();
1332
1333 let mut buffer = Buffer::new(0, text, cx).with_language(
1334 Arc::new(Language::new(
1335 LanguageConfig {
1336 name: "Markdown".into(),
1337 auto_indent_using_last_non_empty_line: false,
1338 ..Default::default()
1339 },
1340 Some(tree_sitter_json::language()),
1341 )),
1342 cx,
1343 );
1344 buffer.edit(
1345 [(Point::new(3, 0)..Point::new(3, 0), "\n")],
1346 Some(AutoindentMode::EachLine),
1347 cx,
1348 );
1349 assert_eq!(
1350 buffer.text(),
1351 "
1352 * one
1353 - a
1354 - b
1355
1356 * two
1357 "
1358 .unindent()
1359 );
1360 buffer
1361 });
1362}
1363
1364#[gpui::test]
1365fn test_autoindent_with_injected_languages(cx: &mut MutableAppContext) {
1366 cx.set_global({
1367 let mut settings = Settings::test(cx);
1368 settings.language_overrides.extend([
1369 (
1370 "HTML".into(),
1371 settings::EditorSettings {
1372 tab_size: Some(2.try_into().unwrap()),
1373 ..Default::default()
1374 },
1375 ),
1376 (
1377 "JavaScript".into(),
1378 settings::EditorSettings {
1379 tab_size: Some(8.try_into().unwrap()),
1380 ..Default::default()
1381 },
1382 ),
1383 ]);
1384 settings
1385 });
1386
1387 let html_language = Arc::new(
1388 Language::new(
1389 LanguageConfig {
1390 name: "HTML".into(),
1391 ..Default::default()
1392 },
1393 Some(tree_sitter_html::language()),
1394 )
1395 .with_indents_query(
1396 "
1397 (element
1398 (start_tag) @start
1399 (end_tag)? @end) @indent
1400 ",
1401 )
1402 .unwrap()
1403 .with_injection_query(
1404 r#"
1405 (script_element
1406 (raw_text) @content
1407 (#set! "language" "javascript"))
1408 "#,
1409 )
1410 .unwrap(),
1411 );
1412
1413 let javascript_language = Arc::new(
1414 Language::new(
1415 LanguageConfig {
1416 name: "JavaScript".into(),
1417 ..Default::default()
1418 },
1419 Some(tree_sitter_javascript::language()),
1420 )
1421 .with_indents_query(
1422 r#"
1423 (object "}" @end) @indent
1424 "#,
1425 )
1426 .unwrap(),
1427 );
1428
1429 let language_registry = Arc::new(LanguageRegistry::test());
1430 language_registry.add(html_language.clone());
1431 language_registry.add(javascript_language.clone());
1432
1433 cx.add_model(|cx| {
1434 let (text, ranges) = marked_text_ranges(
1435 &"
1436 <div>ˇ
1437 </div>
1438 <script>
1439 init({ˇ
1440 })
1441 </script>
1442 <span>ˇ
1443 </span>
1444 "
1445 .unindent(),
1446 false,
1447 );
1448
1449 let mut buffer = Buffer::new(0, text, cx);
1450 buffer.set_language_registry(language_registry);
1451 buffer.set_language(Some(html_language), cx);
1452 buffer.edit(
1453 ranges.into_iter().map(|range| (range, "\na")),
1454 Some(AutoindentMode::EachLine),
1455 cx,
1456 );
1457 assert_eq!(
1458 buffer.text(),
1459 "
1460 <div>
1461 a
1462 </div>
1463 <script>
1464 init({
1465 a
1466 })
1467 </script>
1468 <span>
1469 a
1470 </span>
1471 "
1472 .unindent()
1473 );
1474 buffer
1475 });
1476}
1477
1478#[gpui::test]
1479fn test_autoindent_query_with_outdent_captures(cx: &mut MutableAppContext) {
1480 let mut settings = Settings::test(cx);
1481 settings.editor_defaults.tab_size = Some(2.try_into().unwrap());
1482 cx.set_global(settings);
1483 cx.add_model(|cx| {
1484 let mut buffer = Buffer::new(0, "", cx).with_language(Arc::new(ruby_lang()), cx);
1485
1486 let text = r#"
1487 class C
1488 def a(b, c)
1489 puts b
1490 puts c
1491 rescue
1492 puts "errored"
1493 exit 1
1494 end
1495 end
1496 "#
1497 .unindent();
1498
1499 buffer.edit([(0..0, text)], Some(AutoindentMode::EachLine), cx);
1500
1501 assert_eq!(
1502 buffer.text(),
1503 r#"
1504 class C
1505 def a(b, c)
1506 puts b
1507 puts c
1508 rescue
1509 puts "errored"
1510 exit 1
1511 end
1512 end
1513 "#
1514 .unindent()
1515 );
1516
1517 buffer
1518 });
1519}
1520
1521#[gpui::test]
1522fn test_language_config_at(cx: &mut MutableAppContext) {
1523 cx.set_global(Settings::test(cx));
1524 cx.add_model(|cx| {
1525 let language = Language::new(
1526 LanguageConfig {
1527 name: "JavaScript".into(),
1528 line_comment: Some("// ".into()),
1529 brackets: vec![
1530 BracketPair {
1531 start: "{".into(),
1532 end: "}".into(),
1533 close: true,
1534 newline: false,
1535 },
1536 BracketPair {
1537 start: "'".into(),
1538 end: "'".into(),
1539 close: true,
1540 newline: false,
1541 },
1542 ],
1543 overrides: [
1544 (
1545 "element".into(),
1546 LanguageConfigOverride {
1547 line_comment: Override::Remove { remove: true },
1548 block_comment: Override::Set(("{/*".into(), "*/}".into())),
1549 ..Default::default()
1550 },
1551 ),
1552 (
1553 "string".into(),
1554 LanguageConfigOverride {
1555 brackets: Override::Set(vec![BracketPair {
1556 start: "{".into(),
1557 end: "}".into(),
1558 close: true,
1559 newline: false,
1560 }]),
1561 ..Default::default()
1562 },
1563 ),
1564 ]
1565 .into_iter()
1566 .collect(),
1567 ..Default::default()
1568 },
1569 Some(tree_sitter_javascript::language()),
1570 )
1571 .with_override_query(
1572 r#"
1573 (jsx_element) @element
1574 (string) @string
1575 "#,
1576 )
1577 .unwrap();
1578
1579 let text = r#"a["b"] = <C d="e"></C>;"#;
1580
1581 let buffer = Buffer::new(0, text, cx).with_language(Arc::new(language), cx);
1582 let snapshot = buffer.snapshot();
1583
1584 let config = snapshot.language_scope_at(0).unwrap();
1585 assert_eq!(config.line_comment_prefix().unwrap().as_ref(), "// ");
1586 assert_eq!(config.brackets().len(), 2);
1587
1588 let string_config = snapshot.language_scope_at(3).unwrap();
1589 assert_eq!(config.line_comment_prefix().unwrap().as_ref(), "// ");
1590 assert_eq!(string_config.brackets().len(), 1);
1591
1592 let element_config = snapshot.language_scope_at(10).unwrap();
1593 assert_eq!(element_config.line_comment_prefix(), None);
1594 assert_eq!(
1595 element_config.block_comment_delimiters(),
1596 Some((&"{/*".into(), &"*/}".into()))
1597 );
1598 assert_eq!(element_config.brackets().len(), 2);
1599
1600 buffer
1601 });
1602}
1603
1604#[gpui::test]
1605fn test_serialization(cx: &mut gpui::MutableAppContext) {
1606 let mut now = Instant::now();
1607
1608 let buffer1 = cx.add_model(|cx| {
1609 let mut buffer = Buffer::new(0, "abc", cx);
1610 buffer.edit([(3..3, "D")], None, cx);
1611
1612 now += Duration::from_secs(1);
1613 buffer.start_transaction_at(now);
1614 buffer.edit([(4..4, "E")], None, cx);
1615 buffer.end_transaction_at(now, cx);
1616 assert_eq!(buffer.text(), "abcDE");
1617
1618 buffer.undo(cx);
1619 assert_eq!(buffer.text(), "abcD");
1620
1621 buffer.edit([(4..4, "F")], None, cx);
1622 assert_eq!(buffer.text(), "abcDF");
1623 buffer
1624 });
1625 assert_eq!(buffer1.read(cx).text(), "abcDF");
1626
1627 let state = buffer1.read(cx).to_proto();
1628 let ops = cx
1629 .background()
1630 .block(buffer1.read(cx).serialize_ops(None, cx));
1631 let buffer2 = cx.add_model(|cx| {
1632 let mut buffer = Buffer::from_proto(1, state, None).unwrap();
1633 buffer
1634 .apply_ops(
1635 ops.into_iter()
1636 .map(|op| proto::deserialize_operation(op).unwrap()),
1637 cx,
1638 )
1639 .unwrap();
1640 buffer
1641 });
1642 assert_eq!(buffer2.read(cx).text(), "abcDF");
1643}
1644
1645#[gpui::test(iterations = 100)]
1646fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) {
1647 let min_peers = env::var("MIN_PEERS")
1648 .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
1649 .unwrap_or(1);
1650 let max_peers = env::var("MAX_PEERS")
1651 .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
1652 .unwrap_or(5);
1653 let operations = env::var("OPERATIONS")
1654 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1655 .unwrap_or(10);
1656
1657 let base_text_len = rng.gen_range(0..10);
1658 let base_text = RandomCharIter::new(&mut rng)
1659 .take(base_text_len)
1660 .collect::<String>();
1661 let mut replica_ids = Vec::new();
1662 let mut buffers = Vec::new();
1663 let network = Rc::new(RefCell::new(Network::new(rng.clone())));
1664 let base_buffer = cx.add_model(|cx| Buffer::new(0, base_text.as_str(), cx));
1665
1666 for i in 0..rng.gen_range(min_peers..=max_peers) {
1667 let buffer = cx.add_model(|cx| {
1668 let state = base_buffer.read(cx).to_proto();
1669 let ops = cx
1670 .background()
1671 .block(base_buffer.read(cx).serialize_ops(None, cx));
1672 let mut buffer = Buffer::from_proto(i as ReplicaId, state, None).unwrap();
1673 buffer
1674 .apply_ops(
1675 ops.into_iter()
1676 .map(|op| proto::deserialize_operation(op).unwrap()),
1677 cx,
1678 )
1679 .unwrap();
1680 buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
1681 let network = network.clone();
1682 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1683 if let Event::Operation(op) = event {
1684 network
1685 .borrow_mut()
1686 .broadcast(buffer.replica_id(), vec![proto::serialize_operation(op)]);
1687 }
1688 })
1689 .detach();
1690 buffer
1691 });
1692 buffers.push(buffer);
1693 replica_ids.push(i as ReplicaId);
1694 network.borrow_mut().add_peer(i as ReplicaId);
1695 log::info!("Adding initial peer with replica id {}", i);
1696 }
1697
1698 log::info!("initial text: {:?}", base_text);
1699
1700 let mut now = Instant::now();
1701 let mut mutation_count = operations;
1702 let mut next_diagnostic_id = 0;
1703 let mut active_selections = BTreeMap::default();
1704 loop {
1705 let replica_index = rng.gen_range(0..replica_ids.len());
1706 let replica_id = replica_ids[replica_index];
1707 let buffer = &mut buffers[replica_index];
1708 let mut new_buffer = None;
1709 match rng.gen_range(0..100) {
1710 0..=29 if mutation_count != 0 => {
1711 buffer.update(cx, |buffer, cx| {
1712 buffer.start_transaction_at(now);
1713 buffer.randomly_edit(&mut rng, 5, cx);
1714 buffer.end_transaction_at(now, cx);
1715 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1716 });
1717 mutation_count -= 1;
1718 }
1719 30..=39 if mutation_count != 0 => {
1720 buffer.update(cx, |buffer, cx| {
1721 let mut selections = Vec::new();
1722 for id in 0..rng.gen_range(1..=5) {
1723 let range = buffer.random_byte_range(0, &mut rng);
1724 selections.push(Selection {
1725 id,
1726 start: buffer.anchor_before(range.start),
1727 end: buffer.anchor_before(range.end),
1728 reversed: false,
1729 goal: SelectionGoal::None,
1730 });
1731 }
1732 let selections: Arc<[Selection<Anchor>]> = selections.into();
1733 log::info!(
1734 "peer {} setting active selections: {:?}",
1735 replica_id,
1736 selections
1737 );
1738 active_selections.insert(replica_id, selections.clone());
1739 buffer.set_active_selections(selections, false, Default::default(), cx);
1740 });
1741 mutation_count -= 1;
1742 }
1743 40..=49 if mutation_count != 0 && replica_id == 0 => {
1744 let entry_count = rng.gen_range(1..=5);
1745 buffer.update(cx, |buffer, cx| {
1746 let diagnostics = DiagnosticSet::new(
1747 (0..entry_count).map(|_| {
1748 let range = buffer.random_byte_range(0, &mut rng);
1749 let range = range.to_point_utf16(buffer);
1750 let range = range.start..range.end;
1751 DiagnosticEntry {
1752 range,
1753 diagnostic: Diagnostic {
1754 message: post_inc(&mut next_diagnostic_id).to_string(),
1755 ..Default::default()
1756 },
1757 }
1758 }),
1759 buffer,
1760 );
1761 log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
1762 buffer.update_diagnostics(diagnostics, cx);
1763 });
1764 mutation_count -= 1;
1765 }
1766 50..=59 if replica_ids.len() < max_peers => {
1767 let old_buffer_state = buffer.read(cx).to_proto();
1768 let old_buffer_ops = cx
1769 .background()
1770 .block(buffer.read(cx).serialize_ops(None, cx));
1771 let new_replica_id = (0..=replica_ids.len() as ReplicaId)
1772 .filter(|replica_id| *replica_id != buffer.read(cx).replica_id())
1773 .choose(&mut rng)
1774 .unwrap();
1775 log::info!(
1776 "Adding new replica {} (replicating from {})",
1777 new_replica_id,
1778 replica_id
1779 );
1780 new_buffer = Some(cx.add_model(|cx| {
1781 let mut new_buffer =
1782 Buffer::from_proto(new_replica_id, old_buffer_state, None).unwrap();
1783 new_buffer
1784 .apply_ops(
1785 old_buffer_ops
1786 .into_iter()
1787 .map(|op| deserialize_operation(op).unwrap()),
1788 cx,
1789 )
1790 .unwrap();
1791 log::info!(
1792 "New replica {} text: {:?}",
1793 new_buffer.replica_id(),
1794 new_buffer.text()
1795 );
1796 new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
1797 let network = network.clone();
1798 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1799 if let Event::Operation(op) = event {
1800 network.borrow_mut().broadcast(
1801 buffer.replica_id(),
1802 vec![proto::serialize_operation(op)],
1803 );
1804 }
1805 })
1806 .detach();
1807 new_buffer
1808 }));
1809 network.borrow_mut().replicate(replica_id, new_replica_id);
1810
1811 if new_replica_id as usize == replica_ids.len() {
1812 replica_ids.push(new_replica_id);
1813 } else {
1814 let new_buffer = new_buffer.take().unwrap();
1815 while network.borrow().has_unreceived(new_replica_id) {
1816 let ops = network
1817 .borrow_mut()
1818 .receive(new_replica_id)
1819 .into_iter()
1820 .map(|op| proto::deserialize_operation(op).unwrap());
1821 if ops.len() > 0 {
1822 log::info!(
1823 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1824 new_replica_id,
1825 buffer.read(cx).version(),
1826 ops.len(),
1827 ops
1828 );
1829 new_buffer.update(cx, |new_buffer, cx| {
1830 new_buffer.apply_ops(ops, cx).unwrap();
1831 });
1832 }
1833 }
1834 buffers[new_replica_id as usize] = new_buffer;
1835 }
1836 }
1837 60..=69 if mutation_count != 0 => {
1838 buffer.update(cx, |buffer, cx| {
1839 buffer.randomly_undo_redo(&mut rng, cx);
1840 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1841 });
1842 mutation_count -= 1;
1843 }
1844 _ if network.borrow().has_unreceived(replica_id) => {
1845 let ops = network
1846 .borrow_mut()
1847 .receive(replica_id)
1848 .into_iter()
1849 .map(|op| proto::deserialize_operation(op).unwrap());
1850 if ops.len() > 0 {
1851 log::info!(
1852 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1853 replica_id,
1854 buffer.read(cx).version(),
1855 ops.len(),
1856 ops
1857 );
1858 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
1859 }
1860 }
1861 _ => {}
1862 }
1863
1864 now += Duration::from_millis(rng.gen_range(0..=200));
1865 buffers.extend(new_buffer);
1866
1867 for buffer in &buffers {
1868 buffer.read(cx).check_invariants();
1869 }
1870
1871 if mutation_count == 0 && network.borrow().is_idle() {
1872 break;
1873 }
1874 }
1875
1876 let first_buffer = buffers[0].read(cx).snapshot();
1877 for buffer in &buffers[1..] {
1878 let buffer = buffer.read(cx).snapshot();
1879 assert_eq!(
1880 buffer.version(),
1881 first_buffer.version(),
1882 "Replica {} version != Replica 0 version",
1883 buffer.replica_id()
1884 );
1885 assert_eq!(
1886 buffer.text(),
1887 first_buffer.text(),
1888 "Replica {} text != Replica 0 text",
1889 buffer.replica_id()
1890 );
1891 assert_eq!(
1892 buffer
1893 .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
1894 .collect::<Vec<_>>(),
1895 first_buffer
1896 .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
1897 .collect::<Vec<_>>(),
1898 "Replica {} diagnostics != Replica 0 diagnostics",
1899 buffer.replica_id()
1900 );
1901 }
1902
1903 for buffer in &buffers {
1904 let buffer = buffer.read(cx).snapshot();
1905 let actual_remote_selections = buffer
1906 .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
1907 .map(|(replica_id, _, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
1908 .collect::<Vec<_>>();
1909 let expected_remote_selections = active_selections
1910 .iter()
1911 .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
1912 .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
1913 .collect::<Vec<_>>();
1914 assert_eq!(
1915 actual_remote_selections,
1916 expected_remote_selections,
1917 "Replica {} remote selections != expected selections",
1918 buffer.replica_id()
1919 );
1920 }
1921}
1922
1923#[test]
1924fn test_contiguous_ranges() {
1925 assert_eq!(
1926 contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
1927 &[1..4, 5..7, 9..13]
1928 );
1929
1930 // Respects the `max_len` parameter
1931 assert_eq!(
1932 contiguous_ranges(
1933 [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
1934 3
1935 )
1936 .collect::<Vec<_>>(),
1937 &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
1938 );
1939}
1940
1941fn ruby_lang() -> Language {
1942 Language::new(
1943 LanguageConfig {
1944 name: "Ruby".into(),
1945 path_suffixes: vec!["rb".to_string()],
1946 ..Default::default()
1947 },
1948 Some(tree_sitter_ruby::language()),
1949 )
1950 .with_indents_query(
1951 r#"
1952 (class "end" @end) @indent
1953 (method "end" @end) @indent
1954 (rescue) @outdent
1955 (then) @indent
1956 "#,
1957 )
1958 .unwrap()
1959}
1960
1961fn rust_lang() -> Language {
1962 Language::new(
1963 LanguageConfig {
1964 name: "Rust".into(),
1965 path_suffixes: vec!["rs".to_string()],
1966 ..Default::default()
1967 },
1968 Some(tree_sitter_rust::language()),
1969 )
1970 .with_indents_query(
1971 r#"
1972 (call_expression) @indent
1973 (field_expression) @indent
1974 (_ "(" ")" @end) @indent
1975 (_ "{" "}" @end) @indent
1976 "#,
1977 )
1978 .unwrap()
1979 .with_brackets_query(
1980 r#"
1981 ("{" @open "}" @close)
1982 "#,
1983 )
1984 .unwrap()
1985 .with_outline_query(
1986 r#"
1987 (struct_item
1988 "struct" @context
1989 name: (_) @name) @item
1990 (enum_item
1991 "enum" @context
1992 name: (_) @name) @item
1993 (enum_variant
1994 name: (_) @name) @item
1995 (field_declaration
1996 name: (_) @name) @item
1997 (impl_item
1998 "impl" @context
1999 trait: (_)? @name
2000 "for"? @context
2001 type: (_) @name) @item
2002 (function_item
2003 "fn" @context
2004 name: (_) @name) @item
2005 (mod_item
2006 "mod" @context
2007 name: (_) @name) @item
2008 "#,
2009 )
2010 .unwrap()
2011}
2012
2013fn json_lang() -> Language {
2014 Language::new(
2015 LanguageConfig {
2016 name: "Json".into(),
2017 path_suffixes: vec!["js".to_string()],
2018 ..Default::default()
2019 },
2020 Some(tree_sitter_json::language()),
2021 )
2022}
2023
2024fn javascript_lang() -> Language {
2025 Language::new(
2026 LanguageConfig {
2027 name: "JavaScript".into(),
2028 ..Default::default()
2029 },
2030 Some(tree_sitter_javascript::language()),
2031 )
2032 .with_brackets_query(
2033 r#"
2034 ("{" @open "}" @close)
2035 ("(" @open ")" @close)
2036 "#,
2037 )
2038 .unwrap()
2039}
2040
2041fn get_tree_sexp(buffer: &ModelHandle<Buffer>, cx: &gpui::TestAppContext) -> String {
2042 buffer.read_with(cx, |buffer, _| {
2043 let snapshot = buffer.snapshot();
2044 let layers = snapshot.syntax.layers(buffer.as_text_snapshot());
2045 layers[0].node.to_sexp()
2046 })
2047}
2048
2049// Assert that the enclosing bracket ranges around the selection match the pairs indicated by the marked text in `range_markers`
2050fn assert_enclosing_bracket_pairs(
2051 selection_text: &'static str,
2052 bracket_pair_texts: Vec<&'static str>,
2053 language: Language,
2054 cx: &mut MutableAppContext,
2055) {
2056 cx.set_global(Settings::test(cx));
2057 let (expected_text, selection_ranges) = marked_text_ranges(selection_text, false);
2058 let buffer = cx.add_model(|cx| {
2059 Buffer::new(0, expected_text.clone(), cx).with_language(Arc::new(language), cx)
2060 });
2061 let buffer = buffer.update(cx, |buffer, _cx| buffer.snapshot());
2062
2063 let selection_range = selection_ranges[0].clone();
2064
2065 let bracket_pairs = bracket_pair_texts
2066 .into_iter()
2067 .map(|pair_text| {
2068 let (bracket_text, ranges) = marked_text_ranges(pair_text, false);
2069 assert_eq!(bracket_text, expected_text);
2070 (ranges[0].clone(), ranges[1].clone())
2071 })
2072 .collect::<Vec<_>>();
2073
2074 assert_set_eq!(
2075 buffer.bracket_ranges(selection_range).collect::<Vec<_>>(),
2076 bracket_pairs
2077 );
2078}