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_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_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 eprintln!("-----------------------");
714 // Regression test: even though the parent node of the parentheses (the for loop) does
715 // intersect the given range, the parentheses themselves do not contain the range, so
716 // they should not be returned. Only the curly braces contain the range.
717 assert(
718 indoc! {"
719 for (const a in b) {ˇ
720 // a comment that's longer than the for-loop header
721 }"},
722 vec![indoc! {"
723 for (const a in b) «{»
724 // a comment that's longer than the for-loop header
725 «}»"}],
726 );
727}
728
729#[gpui::test]
730fn test_range_for_syntax_ancestor(cx: &mut MutableAppContext) {
731 cx.add_model(|cx| {
732 let text = "fn a() { b(|c| {}) }";
733 let buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
734 let snapshot = buffer.snapshot();
735
736 assert_eq!(
737 snapshot.range_for_syntax_ancestor(empty_range_at(text, "|")),
738 Some(range_of(text, "|"))
739 );
740 assert_eq!(
741 snapshot.range_for_syntax_ancestor(range_of(text, "|")),
742 Some(range_of(text, "|c|"))
743 );
744 assert_eq!(
745 snapshot.range_for_syntax_ancestor(range_of(text, "|c|")),
746 Some(range_of(text, "|c| {}"))
747 );
748 assert_eq!(
749 snapshot.range_for_syntax_ancestor(range_of(text, "|c| {}")),
750 Some(range_of(text, "(|c| {})"))
751 );
752
753 buffer
754 });
755
756 fn empty_range_at(text: &str, part: &str) -> Range<usize> {
757 let start = text.find(part).unwrap();
758 start..start
759 }
760
761 fn range_of(text: &str, part: &str) -> Range<usize> {
762 let start = text.find(part).unwrap();
763 start..start + part.len()
764 }
765}
766
767#[gpui::test]
768fn test_autoindent_with_soft_tabs(cx: &mut MutableAppContext) {
769 let settings = Settings::test(cx);
770 cx.set_global(settings);
771
772 cx.add_model(|cx| {
773 let text = "fn a() {}";
774 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
775
776 buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
777 assert_eq!(buffer.text(), "fn a() {\n \n}");
778
779 buffer.edit(
780 [(Point::new(1, 4)..Point::new(1, 4), "b()\n")],
781 Some(AutoindentMode::EachLine),
782 cx,
783 );
784 assert_eq!(buffer.text(), "fn a() {\n b()\n \n}");
785
786 // Create a field expression on a new line, causing that line
787 // to be indented.
788 buffer.edit(
789 [(Point::new(2, 4)..Point::new(2, 4), ".c")],
790 Some(AutoindentMode::EachLine),
791 cx,
792 );
793 assert_eq!(buffer.text(), "fn a() {\n b()\n .c\n}");
794
795 // Remove the dot so that the line is no longer a field expression,
796 // causing the line to be outdented.
797 buffer.edit(
798 [(Point::new(2, 8)..Point::new(2, 9), "")],
799 Some(AutoindentMode::EachLine),
800 cx,
801 );
802 assert_eq!(buffer.text(), "fn a() {\n b()\n c\n}");
803
804 buffer
805 });
806}
807
808#[gpui::test]
809fn test_autoindent_with_hard_tabs(cx: &mut MutableAppContext) {
810 let mut settings = Settings::test(cx);
811 settings.editor_overrides.hard_tabs = Some(true);
812 cx.set_global(settings);
813
814 cx.add_model(|cx| {
815 let text = "fn a() {}";
816 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
817
818 buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
819 assert_eq!(buffer.text(), "fn a() {\n\t\n}");
820
821 buffer.edit(
822 [(Point::new(1, 1)..Point::new(1, 1), "b()\n")],
823 Some(AutoindentMode::EachLine),
824 cx,
825 );
826 assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\n}");
827
828 // Create a field expression on a new line, causing that line
829 // to be indented.
830 buffer.edit(
831 [(Point::new(2, 1)..Point::new(2, 1), ".c")],
832 Some(AutoindentMode::EachLine),
833 cx,
834 );
835 assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\t.c\n}");
836
837 // Remove the dot so that the line is no longer a field expression,
838 // causing the line to be outdented.
839 buffer.edit(
840 [(Point::new(2, 2)..Point::new(2, 3), "")],
841 Some(AutoindentMode::EachLine),
842 cx,
843 );
844 assert_eq!(buffer.text(), "fn a() {\n\tb()\n\tc\n}");
845
846 buffer
847 });
848}
849
850#[gpui::test]
851fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut MutableAppContext) {
852 let settings = Settings::test(cx);
853 cx.set_global(settings);
854
855 cx.add_model(|cx| {
856 let mut buffer = Buffer::new(
857 0,
858 "
859 fn a() {
860 c;
861 d;
862 }
863 "
864 .unindent(),
865 cx,
866 )
867 .with_language(Arc::new(rust_lang()), cx);
868
869 // Lines 2 and 3 don't match the indentation suggestion. When editing these lines,
870 // their indentation is not adjusted.
871 buffer.edit_via_marked_text(
872 &"
873 fn a() {
874 c«()»;
875 d«()»;
876 }
877 "
878 .unindent(),
879 Some(AutoindentMode::EachLine),
880 cx,
881 );
882 assert_eq!(
883 buffer.text(),
884 "
885 fn a() {
886 c();
887 d();
888 }
889 "
890 .unindent()
891 );
892
893 // When appending new content after these lines, the indentation is based on the
894 // preceding lines' actual indentation.
895 buffer.edit_via_marked_text(
896 &"
897 fn a() {
898 c«
899 .f
900 .g()»;
901 d«
902 .f
903 .g()»;
904 }
905 "
906 .unindent(),
907 Some(AutoindentMode::EachLine),
908 cx,
909 );
910
911 assert_eq!(
912 buffer.text(),
913 "
914 fn a() {
915 c
916 .f
917 .g();
918 d
919 .f
920 .g();
921 }
922 "
923 .unindent()
924 );
925 buffer
926 });
927
928 cx.add_model(|cx| {
929 let mut buffer = Buffer::new(
930 0,
931 "
932 fn a() {
933 b();
934 |
935 "
936 .replace("|", "") // marker to preserve trailing whitespace
937 .unindent(),
938 cx,
939 )
940 .with_language(Arc::new(rust_lang()), cx);
941
942 // Insert a closing brace. It is outdented.
943 buffer.edit_via_marked_text(
944 &"
945 fn a() {
946 b();
947 «}»
948 "
949 .unindent(),
950 Some(AutoindentMode::EachLine),
951 cx,
952 );
953 assert_eq!(
954 buffer.text(),
955 "
956 fn a() {
957 b();
958 }
959 "
960 .unindent()
961 );
962
963 // Manually edit the leading whitespace. The edit is preserved.
964 buffer.edit_via_marked_text(
965 &"
966 fn a() {
967 b();
968 « »}
969 "
970 .unindent(),
971 Some(AutoindentMode::EachLine),
972 cx,
973 );
974 assert_eq!(
975 buffer.text(),
976 "
977 fn a() {
978 b();
979 }
980 "
981 .unindent()
982 );
983 buffer
984 });
985}
986
987#[gpui::test]
988fn test_autoindent_does_not_adjust_lines_within_newly_created_errors(cx: &mut MutableAppContext) {
989 let settings = Settings::test(cx);
990 cx.set_global(settings);
991
992 cx.add_model(|cx| {
993 let mut buffer = Buffer::new(
994 0,
995 "
996 fn a() {
997 i
998 }
999 "
1000 .unindent(),
1001 cx,
1002 )
1003 .with_language(Arc::new(rust_lang()), cx);
1004
1005 // Regression test: line does not get outdented due to syntax error
1006 buffer.edit_via_marked_text(
1007 &"
1008 fn a() {
1009 i«f let Some(x) = y»
1010 }
1011 "
1012 .unindent(),
1013 Some(AutoindentMode::EachLine),
1014 cx,
1015 );
1016 assert_eq!(
1017 buffer.text(),
1018 "
1019 fn a() {
1020 if let Some(x) = y
1021 }
1022 "
1023 .unindent()
1024 );
1025
1026 buffer.edit_via_marked_text(
1027 &"
1028 fn a() {
1029 if let Some(x) = y« {»
1030 }
1031 "
1032 .unindent(),
1033 Some(AutoindentMode::EachLine),
1034 cx,
1035 );
1036 assert_eq!(
1037 buffer.text(),
1038 "
1039 fn a() {
1040 if let Some(x) = y {
1041 }
1042 "
1043 .unindent()
1044 );
1045
1046 buffer
1047 });
1048}
1049
1050#[gpui::test]
1051fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut MutableAppContext) {
1052 cx.set_global(Settings::test(cx));
1053 cx.add_model(|cx| {
1054 let mut buffer = Buffer::new(
1055 0,
1056 "
1057 fn a() {}
1058 "
1059 .unindent(),
1060 cx,
1061 )
1062 .with_language(Arc::new(rust_lang()), cx);
1063
1064 buffer.edit_via_marked_text(
1065 &"
1066 fn a(«
1067 b») {}
1068 "
1069 .unindent(),
1070 Some(AutoindentMode::EachLine),
1071 cx,
1072 );
1073 assert_eq!(
1074 buffer.text(),
1075 "
1076 fn a(
1077 b) {}
1078 "
1079 .unindent()
1080 );
1081
1082 // The indentation suggestion changed because `@end` node (a close paren)
1083 // is now at the beginning of the line.
1084 buffer.edit_via_marked_text(
1085 &"
1086 fn a(
1087 ˇ) {}
1088 "
1089 .unindent(),
1090 Some(AutoindentMode::EachLine),
1091 cx,
1092 );
1093 assert_eq!(
1094 buffer.text(),
1095 "
1096 fn a(
1097 ) {}
1098 "
1099 .unindent()
1100 );
1101
1102 buffer
1103 });
1104}
1105
1106#[gpui::test]
1107fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut MutableAppContext) {
1108 cx.set_global(Settings::test(cx));
1109 cx.add_model(|cx| {
1110 let text = "a\nb";
1111 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
1112 buffer.edit(
1113 [(0..1, "\n"), (2..3, "\n")],
1114 Some(AutoindentMode::EachLine),
1115 cx,
1116 );
1117 assert_eq!(buffer.text(), "\n\n\n");
1118 buffer
1119 });
1120}
1121
1122#[gpui::test]
1123fn test_autoindent_multi_line_insertion(cx: &mut MutableAppContext) {
1124 cx.set_global(Settings::test(cx));
1125 cx.add_model(|cx| {
1126 let text = "
1127 const a: usize = 1;
1128 fn b() {
1129 if c {
1130 let d = 2;
1131 }
1132 }
1133 "
1134 .unindent();
1135
1136 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
1137 buffer.edit(
1138 [(Point::new(3, 0)..Point::new(3, 0), "e(\n f()\n);\n")],
1139 Some(AutoindentMode::EachLine),
1140 cx,
1141 );
1142 assert_eq!(
1143 buffer.text(),
1144 "
1145 const a: usize = 1;
1146 fn b() {
1147 if c {
1148 e(
1149 f()
1150 );
1151 let d = 2;
1152 }
1153 }
1154 "
1155 .unindent()
1156 );
1157
1158 buffer
1159 });
1160}
1161
1162#[gpui::test]
1163fn test_autoindent_block_mode(cx: &mut MutableAppContext) {
1164 cx.set_global(Settings::test(cx));
1165 cx.add_model(|cx| {
1166 let text = r#"
1167 fn a() {
1168 b();
1169 }
1170 "#
1171 .unindent();
1172 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
1173
1174 // When this text was copied, both of the quotation marks were at the same
1175 // indent level, but the indentation of the first line was not included in
1176 // the copied text. This information is retained in the
1177 // 'original_indent_columns' vector.
1178 let original_indent_columns = vec![4];
1179 let inserted_text = r#"
1180 "
1181 c
1182 d
1183 e
1184 "
1185 "#
1186 .unindent();
1187
1188 // Insert the block at column zero. The entire block is indented
1189 // so that the first line matches the previous line's indentation.
1190 buffer.edit(
1191 [(Point::new(2, 0)..Point::new(2, 0), inserted_text.clone())],
1192 Some(AutoindentMode::Block {
1193 original_indent_columns: original_indent_columns.clone(),
1194 }),
1195 cx,
1196 );
1197 assert_eq!(
1198 buffer.text(),
1199 r#"
1200 fn a() {
1201 b();
1202 "
1203 c
1204 d
1205 e
1206 "
1207 }
1208 "#
1209 .unindent()
1210 );
1211
1212 // Grouping is disabled in tests, so we need 2 undos
1213 buffer.undo(cx); // Undo the auto-indent
1214 buffer.undo(cx); // Undo the original edit
1215
1216 // Insert the block at a deeper indent level. The entire block is outdented.
1217 buffer.edit([(Point::new(2, 0)..Point::new(2, 0), " ")], None, cx);
1218 buffer.edit(
1219 [(Point::new(2, 8)..Point::new(2, 8), inserted_text)],
1220 Some(AutoindentMode::Block {
1221 original_indent_columns: original_indent_columns.clone(),
1222 }),
1223 cx,
1224 );
1225 assert_eq!(
1226 buffer.text(),
1227 r#"
1228 fn a() {
1229 b();
1230 "
1231 c
1232 d
1233 e
1234 "
1235 }
1236 "#
1237 .unindent()
1238 );
1239
1240 buffer
1241 });
1242}
1243
1244#[gpui::test]
1245fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut MutableAppContext) {
1246 cx.set_global(Settings::test(cx));
1247 cx.add_model(|cx| {
1248 let text = r#"
1249 fn a() {
1250 if b() {
1251
1252 }
1253 }
1254 "#
1255 .unindent();
1256 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
1257
1258 // The original indent columns are not known, so this text is
1259 // auto-indented in a block as if the first line was copied in
1260 // its entirety.
1261 let original_indent_columns = Vec::new();
1262 let inserted_text = " c\n .d()\n .e();";
1263
1264 // Insert the block at column zero. The entire block is indented
1265 // so that the first line matches the previous line's indentation.
1266 buffer.edit(
1267 [(Point::new(2, 0)..Point::new(2, 0), inserted_text.clone())],
1268 Some(AutoindentMode::Block {
1269 original_indent_columns: original_indent_columns.clone(),
1270 }),
1271 cx,
1272 );
1273 assert_eq!(
1274 buffer.text(),
1275 r#"
1276 fn a() {
1277 if b() {
1278 c
1279 .d()
1280 .e();
1281 }
1282 }
1283 "#
1284 .unindent()
1285 );
1286
1287 // Grouping is disabled in tests, so we need 2 undos
1288 buffer.undo(cx); // Undo the auto-indent
1289 buffer.undo(cx); // Undo the original edit
1290
1291 // Insert the block at a deeper indent level. The entire block is outdented.
1292 buffer.edit(
1293 [(Point::new(2, 0)..Point::new(2, 0), " ".repeat(12))],
1294 None,
1295 cx,
1296 );
1297 buffer.edit(
1298 [(Point::new(2, 12)..Point::new(2, 12), inserted_text)],
1299 Some(AutoindentMode::Block {
1300 original_indent_columns: Vec::new(),
1301 }),
1302 cx,
1303 );
1304 assert_eq!(
1305 buffer.text(),
1306 r#"
1307 fn a() {
1308 if b() {
1309 c
1310 .d()
1311 .e();
1312 }
1313 }
1314 "#
1315 .unindent()
1316 );
1317
1318 buffer
1319 });
1320}
1321
1322#[gpui::test]
1323fn test_autoindent_language_without_indents_query(cx: &mut MutableAppContext) {
1324 cx.set_global(Settings::test(cx));
1325 cx.add_model(|cx| {
1326 let text = "
1327 * one
1328 - a
1329 - b
1330 * two
1331 "
1332 .unindent();
1333
1334 let mut buffer = Buffer::new(0, text, cx).with_language(
1335 Arc::new(Language::new(
1336 LanguageConfig {
1337 name: "Markdown".into(),
1338 auto_indent_using_last_non_empty_line: false,
1339 ..Default::default()
1340 },
1341 Some(tree_sitter_json::language()),
1342 )),
1343 cx,
1344 );
1345 buffer.edit(
1346 [(Point::new(3, 0)..Point::new(3, 0), "\n")],
1347 Some(AutoindentMode::EachLine),
1348 cx,
1349 );
1350 assert_eq!(
1351 buffer.text(),
1352 "
1353 * one
1354 - a
1355 - b
1356
1357 * two
1358 "
1359 .unindent()
1360 );
1361 buffer
1362 });
1363}
1364
1365#[gpui::test]
1366fn test_autoindent_with_injected_languages(cx: &mut MutableAppContext) {
1367 cx.set_global({
1368 let mut settings = Settings::test(cx);
1369 settings.language_overrides.extend([
1370 (
1371 "HTML".into(),
1372 settings::EditorSettings {
1373 tab_size: Some(2.try_into().unwrap()),
1374 ..Default::default()
1375 },
1376 ),
1377 (
1378 "JavaScript".into(),
1379 settings::EditorSettings {
1380 tab_size: Some(8.try_into().unwrap()),
1381 ..Default::default()
1382 },
1383 ),
1384 ]);
1385 settings
1386 });
1387
1388 let html_language = Arc::new(
1389 Language::new(
1390 LanguageConfig {
1391 name: "HTML".into(),
1392 ..Default::default()
1393 },
1394 Some(tree_sitter_html::language()),
1395 )
1396 .with_indents_query(
1397 "
1398 (element
1399 (start_tag) @start
1400 (end_tag)? @end) @indent
1401 ",
1402 )
1403 .unwrap()
1404 .with_injection_query(
1405 r#"
1406 (script_element
1407 (raw_text) @content
1408 (#set! "language" "javascript"))
1409 "#,
1410 )
1411 .unwrap(),
1412 );
1413
1414 let javascript_language = Arc::new(
1415 Language::new(
1416 LanguageConfig {
1417 name: "JavaScript".into(),
1418 ..Default::default()
1419 },
1420 Some(tree_sitter_javascript::language()),
1421 )
1422 .with_indents_query(
1423 r#"
1424 (object "}" @end) @indent
1425 "#,
1426 )
1427 .unwrap(),
1428 );
1429
1430 let language_registry = Arc::new(LanguageRegistry::test());
1431 language_registry.add(html_language.clone());
1432 language_registry.add(javascript_language.clone());
1433
1434 cx.add_model(|cx| {
1435 let (text, ranges) = marked_text_ranges(
1436 &"
1437 <div>ˇ
1438 </div>
1439 <script>
1440 init({ˇ
1441 })
1442 </script>
1443 <span>ˇ
1444 </span>
1445 "
1446 .unindent(),
1447 false,
1448 );
1449
1450 let mut buffer = Buffer::new(0, text, cx);
1451 buffer.set_language_registry(language_registry);
1452 buffer.set_language(Some(html_language), cx);
1453 buffer.edit(
1454 ranges.into_iter().map(|range| (range, "\na")),
1455 Some(AutoindentMode::EachLine),
1456 cx,
1457 );
1458 assert_eq!(
1459 buffer.text(),
1460 "
1461 <div>
1462 a
1463 </div>
1464 <script>
1465 init({
1466 a
1467 })
1468 </script>
1469 <span>
1470 a
1471 </span>
1472 "
1473 .unindent()
1474 );
1475 buffer
1476 });
1477}
1478
1479#[gpui::test]
1480fn test_autoindent_query_with_outdent_captures(cx: &mut MutableAppContext) {
1481 let mut settings = Settings::test(cx);
1482 settings.editor_defaults.tab_size = Some(2.try_into().unwrap());
1483 cx.set_global(settings);
1484 cx.add_model(|cx| {
1485 let mut buffer = Buffer::new(0, "", cx).with_language(Arc::new(ruby_lang()), cx);
1486
1487 let text = r#"
1488 class C
1489 def a(b, c)
1490 puts b
1491 puts c
1492 rescue
1493 puts "errored"
1494 exit 1
1495 end
1496 end
1497 "#
1498 .unindent();
1499
1500 buffer.edit([(0..0, text)], Some(AutoindentMode::EachLine), cx);
1501
1502 assert_eq!(
1503 buffer.text(),
1504 r#"
1505 class C
1506 def a(b, c)
1507 puts b
1508 puts c
1509 rescue
1510 puts "errored"
1511 exit 1
1512 end
1513 end
1514 "#
1515 .unindent()
1516 );
1517
1518 buffer
1519 });
1520}
1521
1522#[gpui::test]
1523fn test_language_config_at(cx: &mut MutableAppContext) {
1524 cx.set_global(Settings::test(cx));
1525 cx.add_model(|cx| {
1526 let language = Language::new(
1527 LanguageConfig {
1528 name: "JavaScript".into(),
1529 line_comment: Some("// ".into()),
1530 brackets: BracketPairConfig {
1531 pairs: vec![
1532 BracketPair {
1533 start: "{".into(),
1534 end: "}".into(),
1535 close: true,
1536 newline: false,
1537 },
1538 BracketPair {
1539 start: "'".into(),
1540 end: "'".into(),
1541 close: true,
1542 newline: false,
1543 },
1544 ],
1545 disabled_scopes_by_bracket_ix: vec![
1546 Vec::new(), //
1547 vec!["string".into()],
1548 ],
1549 },
1550 overrides: [(
1551 "element".into(),
1552 LanguageConfigOverride {
1553 line_comment: Override::Remove { remove: true },
1554 block_comment: Override::Set(("{/*".into(), "*/}".into())),
1555 ..Default::default()
1556 },
1557 )]
1558 .into_iter()
1559 .collect(),
1560 ..Default::default()
1561 },
1562 Some(tree_sitter_javascript::language()),
1563 )
1564 .with_override_query(
1565 r#"
1566 (jsx_element) @element
1567 (string) @string
1568 "#,
1569 )
1570 .unwrap();
1571
1572 let text = r#"a["b"] = <C d="e"></C>;"#;
1573
1574 let buffer = Buffer::new(0, text, cx).with_language(Arc::new(language), cx);
1575 let snapshot = buffer.snapshot();
1576
1577 let config = snapshot.language_scope_at(0).unwrap();
1578 assert_eq!(config.line_comment_prefix().unwrap().as_ref(), "// ");
1579 // Both bracket pairs are enabled
1580 assert_eq!(
1581 config.brackets().map(|e| e.1).collect::<Vec<_>>(),
1582 &[true, true]
1583 );
1584
1585 let string_config = snapshot.language_scope_at(3).unwrap();
1586 assert_eq!(string_config.line_comment_prefix().unwrap().as_ref(), "// ");
1587 // Second bracket pair is disabled
1588 assert_eq!(
1589 string_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
1590 &[true, false]
1591 );
1592
1593 let element_config = snapshot.language_scope_at(10).unwrap();
1594 assert_eq!(element_config.line_comment_prefix(), None);
1595 assert_eq!(
1596 element_config.block_comment_delimiters(),
1597 Some((&"{/*".into(), &"*/}".into()))
1598 );
1599 // Both bracket pairs are enabled
1600 assert_eq!(
1601 element_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
1602 &[true, true]
1603 );
1604
1605 buffer
1606 });
1607}
1608
1609#[gpui::test]
1610fn test_serialization(cx: &mut gpui::MutableAppContext) {
1611 let mut now = Instant::now();
1612
1613 let buffer1 = cx.add_model(|cx| {
1614 let mut buffer = Buffer::new(0, "abc", cx);
1615 buffer.edit([(3..3, "D")], None, cx);
1616
1617 now += Duration::from_secs(1);
1618 buffer.start_transaction_at(now);
1619 buffer.edit([(4..4, "E")], None, cx);
1620 buffer.end_transaction_at(now, cx);
1621 assert_eq!(buffer.text(), "abcDE");
1622
1623 buffer.undo(cx);
1624 assert_eq!(buffer.text(), "abcD");
1625
1626 buffer.edit([(4..4, "F")], None, cx);
1627 assert_eq!(buffer.text(), "abcDF");
1628 buffer
1629 });
1630 assert_eq!(buffer1.read(cx).text(), "abcDF");
1631
1632 let state = buffer1.read(cx).to_proto();
1633 let ops = cx
1634 .background()
1635 .block(buffer1.read(cx).serialize_ops(None, cx));
1636 let buffer2 = cx.add_model(|cx| {
1637 let mut buffer = Buffer::from_proto(1, state, None).unwrap();
1638 buffer
1639 .apply_ops(
1640 ops.into_iter()
1641 .map(|op| proto::deserialize_operation(op).unwrap()),
1642 cx,
1643 )
1644 .unwrap();
1645 buffer
1646 });
1647 assert_eq!(buffer2.read(cx).text(), "abcDF");
1648}
1649
1650#[gpui::test(iterations = 100)]
1651fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) {
1652 let min_peers = env::var("MIN_PEERS")
1653 .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
1654 .unwrap_or(1);
1655 let max_peers = env::var("MAX_PEERS")
1656 .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
1657 .unwrap_or(5);
1658 let operations = env::var("OPERATIONS")
1659 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1660 .unwrap_or(10);
1661
1662 let base_text_len = rng.gen_range(0..10);
1663 let base_text = RandomCharIter::new(&mut rng)
1664 .take(base_text_len)
1665 .collect::<String>();
1666 let mut replica_ids = Vec::new();
1667 let mut buffers = Vec::new();
1668 let network = Rc::new(RefCell::new(Network::new(rng.clone())));
1669 let base_buffer = cx.add_model(|cx| Buffer::new(0, base_text.as_str(), cx));
1670
1671 for i in 0..rng.gen_range(min_peers..=max_peers) {
1672 let buffer = cx.add_model(|cx| {
1673 let state = base_buffer.read(cx).to_proto();
1674 let ops = cx
1675 .background()
1676 .block(base_buffer.read(cx).serialize_ops(None, cx));
1677 let mut buffer = Buffer::from_proto(i as ReplicaId, state, None).unwrap();
1678 buffer
1679 .apply_ops(
1680 ops.into_iter()
1681 .map(|op| proto::deserialize_operation(op).unwrap()),
1682 cx,
1683 )
1684 .unwrap();
1685 buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
1686 let network = network.clone();
1687 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1688 if let Event::Operation(op) = event {
1689 network
1690 .borrow_mut()
1691 .broadcast(buffer.replica_id(), vec![proto::serialize_operation(op)]);
1692 }
1693 })
1694 .detach();
1695 buffer
1696 });
1697 buffers.push(buffer);
1698 replica_ids.push(i as ReplicaId);
1699 network.borrow_mut().add_peer(i as ReplicaId);
1700 log::info!("Adding initial peer with replica id {}", i);
1701 }
1702
1703 log::info!("initial text: {:?}", base_text);
1704
1705 let mut now = Instant::now();
1706 let mut mutation_count = operations;
1707 let mut next_diagnostic_id = 0;
1708 let mut active_selections = BTreeMap::default();
1709 loop {
1710 let replica_index = rng.gen_range(0..replica_ids.len());
1711 let replica_id = replica_ids[replica_index];
1712 let buffer = &mut buffers[replica_index];
1713 let mut new_buffer = None;
1714 match rng.gen_range(0..100) {
1715 0..=29 if mutation_count != 0 => {
1716 buffer.update(cx, |buffer, cx| {
1717 buffer.start_transaction_at(now);
1718 buffer.randomly_edit(&mut rng, 5, cx);
1719 buffer.end_transaction_at(now, cx);
1720 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1721 });
1722 mutation_count -= 1;
1723 }
1724 30..=39 if mutation_count != 0 => {
1725 buffer.update(cx, |buffer, cx| {
1726 let mut selections = Vec::new();
1727 for id in 0..rng.gen_range(1..=5) {
1728 let range = buffer.random_byte_range(0, &mut rng);
1729 selections.push(Selection {
1730 id,
1731 start: buffer.anchor_before(range.start),
1732 end: buffer.anchor_before(range.end),
1733 reversed: false,
1734 goal: SelectionGoal::None,
1735 });
1736 }
1737 let selections: Arc<[Selection<Anchor>]> = selections.into();
1738 log::info!(
1739 "peer {} setting active selections: {:?}",
1740 replica_id,
1741 selections
1742 );
1743 active_selections.insert(replica_id, selections.clone());
1744 buffer.set_active_selections(selections, false, Default::default(), cx);
1745 });
1746 mutation_count -= 1;
1747 }
1748 40..=49 if mutation_count != 0 && replica_id == 0 => {
1749 let entry_count = rng.gen_range(1..=5);
1750 buffer.update(cx, |buffer, cx| {
1751 let diagnostics = DiagnosticSet::new(
1752 (0..entry_count).map(|_| {
1753 let range = buffer.random_byte_range(0, &mut rng);
1754 let range = range.to_point_utf16(buffer);
1755 let range = range.start..range.end;
1756 DiagnosticEntry {
1757 range,
1758 diagnostic: Diagnostic {
1759 message: post_inc(&mut next_diagnostic_id).to_string(),
1760 ..Default::default()
1761 },
1762 }
1763 }),
1764 buffer,
1765 );
1766 log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
1767 buffer.update_diagnostics(diagnostics, cx);
1768 });
1769 mutation_count -= 1;
1770 }
1771 50..=59 if replica_ids.len() < max_peers => {
1772 let old_buffer_state = buffer.read(cx).to_proto();
1773 let old_buffer_ops = cx
1774 .background()
1775 .block(buffer.read(cx).serialize_ops(None, cx));
1776 let new_replica_id = (0..=replica_ids.len() as ReplicaId)
1777 .filter(|replica_id| *replica_id != buffer.read(cx).replica_id())
1778 .choose(&mut rng)
1779 .unwrap();
1780 log::info!(
1781 "Adding new replica {} (replicating from {})",
1782 new_replica_id,
1783 replica_id
1784 );
1785 new_buffer = Some(cx.add_model(|cx| {
1786 let mut new_buffer =
1787 Buffer::from_proto(new_replica_id, old_buffer_state, None).unwrap();
1788 new_buffer
1789 .apply_ops(
1790 old_buffer_ops
1791 .into_iter()
1792 .map(|op| deserialize_operation(op).unwrap()),
1793 cx,
1794 )
1795 .unwrap();
1796 log::info!(
1797 "New replica {} text: {:?}",
1798 new_buffer.replica_id(),
1799 new_buffer.text()
1800 );
1801 new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
1802 let network = network.clone();
1803 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1804 if let Event::Operation(op) = event {
1805 network.borrow_mut().broadcast(
1806 buffer.replica_id(),
1807 vec![proto::serialize_operation(op)],
1808 );
1809 }
1810 })
1811 .detach();
1812 new_buffer
1813 }));
1814 network.borrow_mut().replicate(replica_id, new_replica_id);
1815
1816 if new_replica_id as usize == replica_ids.len() {
1817 replica_ids.push(new_replica_id);
1818 } else {
1819 let new_buffer = new_buffer.take().unwrap();
1820 while network.borrow().has_unreceived(new_replica_id) {
1821 let ops = network
1822 .borrow_mut()
1823 .receive(new_replica_id)
1824 .into_iter()
1825 .map(|op| proto::deserialize_operation(op).unwrap());
1826 if ops.len() > 0 {
1827 log::info!(
1828 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1829 new_replica_id,
1830 buffer.read(cx).version(),
1831 ops.len(),
1832 ops
1833 );
1834 new_buffer.update(cx, |new_buffer, cx| {
1835 new_buffer.apply_ops(ops, cx).unwrap();
1836 });
1837 }
1838 }
1839 buffers[new_replica_id as usize] = new_buffer;
1840 }
1841 }
1842 60..=69 if mutation_count != 0 => {
1843 buffer.update(cx, |buffer, cx| {
1844 buffer.randomly_undo_redo(&mut rng, cx);
1845 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1846 });
1847 mutation_count -= 1;
1848 }
1849 _ if network.borrow().has_unreceived(replica_id) => {
1850 let ops = network
1851 .borrow_mut()
1852 .receive(replica_id)
1853 .into_iter()
1854 .map(|op| proto::deserialize_operation(op).unwrap());
1855 if ops.len() > 0 {
1856 log::info!(
1857 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1858 replica_id,
1859 buffer.read(cx).version(),
1860 ops.len(),
1861 ops
1862 );
1863 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
1864 }
1865 }
1866 _ => {}
1867 }
1868
1869 now += Duration::from_millis(rng.gen_range(0..=200));
1870 buffers.extend(new_buffer);
1871
1872 for buffer in &buffers {
1873 buffer.read(cx).check_invariants();
1874 }
1875
1876 if mutation_count == 0 && network.borrow().is_idle() {
1877 break;
1878 }
1879 }
1880
1881 let first_buffer = buffers[0].read(cx).snapshot();
1882 for buffer in &buffers[1..] {
1883 let buffer = buffer.read(cx).snapshot();
1884 assert_eq!(
1885 buffer.version(),
1886 first_buffer.version(),
1887 "Replica {} version != Replica 0 version",
1888 buffer.replica_id()
1889 );
1890 assert_eq!(
1891 buffer.text(),
1892 first_buffer.text(),
1893 "Replica {} text != Replica 0 text",
1894 buffer.replica_id()
1895 );
1896 assert_eq!(
1897 buffer
1898 .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
1899 .collect::<Vec<_>>(),
1900 first_buffer
1901 .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
1902 .collect::<Vec<_>>(),
1903 "Replica {} diagnostics != Replica 0 diagnostics",
1904 buffer.replica_id()
1905 );
1906 }
1907
1908 for buffer in &buffers {
1909 let buffer = buffer.read(cx).snapshot();
1910 let actual_remote_selections = buffer
1911 .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
1912 .map(|(replica_id, _, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
1913 .collect::<Vec<_>>();
1914 let expected_remote_selections = active_selections
1915 .iter()
1916 .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
1917 .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
1918 .collect::<Vec<_>>();
1919 assert_eq!(
1920 actual_remote_selections,
1921 expected_remote_selections,
1922 "Replica {} remote selections != expected selections",
1923 buffer.replica_id()
1924 );
1925 }
1926}
1927
1928#[test]
1929fn test_contiguous_ranges() {
1930 assert_eq!(
1931 contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
1932 &[1..4, 5..7, 9..13]
1933 );
1934
1935 // Respects the `max_len` parameter
1936 assert_eq!(
1937 contiguous_ranges(
1938 [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
1939 3
1940 )
1941 .collect::<Vec<_>>(),
1942 &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
1943 );
1944}
1945
1946fn ruby_lang() -> Language {
1947 Language::new(
1948 LanguageConfig {
1949 name: "Ruby".into(),
1950 path_suffixes: vec!["rb".to_string()],
1951 ..Default::default()
1952 },
1953 Some(tree_sitter_ruby::language()),
1954 )
1955 .with_indents_query(
1956 r#"
1957 (class "end" @end) @indent
1958 (method "end" @end) @indent
1959 (rescue) @outdent
1960 (then) @indent
1961 "#,
1962 )
1963 .unwrap()
1964}
1965
1966fn rust_lang() -> Language {
1967 Language::new(
1968 LanguageConfig {
1969 name: "Rust".into(),
1970 path_suffixes: vec!["rs".to_string()],
1971 ..Default::default()
1972 },
1973 Some(tree_sitter_rust::language()),
1974 )
1975 .with_indents_query(
1976 r#"
1977 (call_expression) @indent
1978 (field_expression) @indent
1979 (_ "(" ")" @end) @indent
1980 (_ "{" "}" @end) @indent
1981 "#,
1982 )
1983 .unwrap()
1984 .with_brackets_query(
1985 r#"
1986 ("{" @open "}" @close)
1987 "#,
1988 )
1989 .unwrap()
1990 .with_outline_query(
1991 r#"
1992 (struct_item
1993 "struct" @context
1994 name: (_) @name) @item
1995 (enum_item
1996 "enum" @context
1997 name: (_) @name) @item
1998 (enum_variant
1999 name: (_) @name) @item
2000 (field_declaration
2001 name: (_) @name) @item
2002 (impl_item
2003 "impl" @context
2004 trait: (_)? @name
2005 "for"? @context
2006 type: (_) @name) @item
2007 (function_item
2008 "fn" @context
2009 name: (_) @name) @item
2010 (mod_item
2011 "mod" @context
2012 name: (_) @name) @item
2013 "#,
2014 )
2015 .unwrap()
2016}
2017
2018fn json_lang() -> Language {
2019 Language::new(
2020 LanguageConfig {
2021 name: "Json".into(),
2022 path_suffixes: vec!["js".to_string()],
2023 ..Default::default()
2024 },
2025 Some(tree_sitter_json::language()),
2026 )
2027}
2028
2029fn javascript_lang() -> Language {
2030 Language::new(
2031 LanguageConfig {
2032 name: "JavaScript".into(),
2033 ..Default::default()
2034 },
2035 Some(tree_sitter_javascript::language()),
2036 )
2037 .with_brackets_query(
2038 r#"
2039 ("{" @open "}" @close)
2040 ("(" @open ")" @close)
2041 "#,
2042 )
2043 .unwrap()
2044}
2045
2046fn get_tree_sexp(buffer: &ModelHandle<Buffer>, cx: &gpui::TestAppContext) -> String {
2047 buffer.read_with(cx, |buffer, _| {
2048 let snapshot = buffer.snapshot();
2049 let layers = snapshot.syntax.layers(buffer.as_text_snapshot());
2050 layers[0].node.to_sexp()
2051 })
2052}
2053
2054// Assert that the enclosing bracket ranges around the selection match the pairs indicated by the marked text in `range_markers`
2055fn assert_bracket_pairs(
2056 selection_text: &'static str,
2057 bracket_pair_texts: Vec<&'static str>,
2058 language: Language,
2059 cx: &mut MutableAppContext,
2060) {
2061 cx.set_global(Settings::test(cx));
2062 let (expected_text, selection_ranges) = marked_text_ranges(selection_text, false);
2063 let buffer = cx.add_model(|cx| {
2064 Buffer::new(0, expected_text.clone(), cx).with_language(Arc::new(language), cx)
2065 });
2066 let buffer = buffer.update(cx, |buffer, _cx| buffer.snapshot());
2067
2068 let selection_range = selection_ranges[0].clone();
2069
2070 let bracket_pairs = bracket_pair_texts
2071 .into_iter()
2072 .map(|pair_text| {
2073 let (bracket_text, ranges) = marked_text_ranges(pair_text, false);
2074 assert_eq!(bracket_text, expected_text);
2075 (ranges[0].clone(), ranges[1].clone())
2076 })
2077 .collect::<Vec<_>>();
2078
2079 assert_set_eq!(
2080 buffer.bracket_ranges(selection_range).collect::<Vec<_>>(),
2081 bracket_pairs
2082 );
2083}