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: vec![
1531 BracketPair {
1532 start: "{".into(),
1533 end: "}".into(),
1534 close: true,
1535 newline: false,
1536 },
1537 BracketPair {
1538 start: "'".into(),
1539 end: "'".into(),
1540 close: true,
1541 newline: false,
1542 },
1543 ],
1544 overrides: [
1545 (
1546 "element".into(),
1547 LanguageConfigOverride {
1548 line_comment: Override::Remove { remove: true },
1549 block_comment: Override::Set(("{/*".into(), "*/}".into())),
1550 ..Default::default()
1551 },
1552 ),
1553 (
1554 "string".into(),
1555 LanguageConfigOverride {
1556 brackets: Override::Set(vec![BracketPair {
1557 start: "{".into(),
1558 end: "}".into(),
1559 close: true,
1560 newline: false,
1561 }]),
1562 ..Default::default()
1563 },
1564 ),
1565 ]
1566 .into_iter()
1567 .collect(),
1568 ..Default::default()
1569 },
1570 Some(tree_sitter_javascript::language()),
1571 )
1572 .with_override_query(
1573 r#"
1574 (jsx_element) @element
1575 (string) @string
1576 "#,
1577 )
1578 .unwrap();
1579
1580 let text = r#"a["b"] = <C d="e"></C>;"#;
1581
1582 let buffer = Buffer::new(0, text, cx).with_language(Arc::new(language), cx);
1583 let snapshot = buffer.snapshot();
1584
1585 let config = snapshot.language_scope_at(0).unwrap();
1586 assert_eq!(config.line_comment_prefix().unwrap().as_ref(), "// ");
1587 assert_eq!(config.brackets().len(), 2);
1588
1589 let string_config = snapshot.language_scope_at(3).unwrap();
1590 assert_eq!(config.line_comment_prefix().unwrap().as_ref(), "// ");
1591 assert_eq!(string_config.brackets().len(), 1);
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 assert_eq!(element_config.brackets().len(), 2);
1600
1601 buffer
1602 });
1603}
1604
1605#[gpui::test]
1606fn test_serialization(cx: &mut gpui::MutableAppContext) {
1607 let mut now = Instant::now();
1608
1609 let buffer1 = cx.add_model(|cx| {
1610 let mut buffer = Buffer::new(0, "abc", cx);
1611 buffer.edit([(3..3, "D")], None, cx);
1612
1613 now += Duration::from_secs(1);
1614 buffer.start_transaction_at(now);
1615 buffer.edit([(4..4, "E")], None, cx);
1616 buffer.end_transaction_at(now, cx);
1617 assert_eq!(buffer.text(), "abcDE");
1618
1619 buffer.undo(cx);
1620 assert_eq!(buffer.text(), "abcD");
1621
1622 buffer.edit([(4..4, "F")], None, cx);
1623 assert_eq!(buffer.text(), "abcDF");
1624 buffer
1625 });
1626 assert_eq!(buffer1.read(cx).text(), "abcDF");
1627
1628 let state = buffer1.read(cx).to_proto();
1629 let ops = cx
1630 .background()
1631 .block(buffer1.read(cx).serialize_ops(None, cx));
1632 let buffer2 = cx.add_model(|cx| {
1633 let mut buffer = Buffer::from_proto(1, state, None).unwrap();
1634 buffer
1635 .apply_ops(
1636 ops.into_iter()
1637 .map(|op| proto::deserialize_operation(op).unwrap()),
1638 cx,
1639 )
1640 .unwrap();
1641 buffer
1642 });
1643 assert_eq!(buffer2.read(cx).text(), "abcDF");
1644}
1645
1646#[gpui::test(iterations = 100)]
1647fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) {
1648 let min_peers = env::var("MIN_PEERS")
1649 .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
1650 .unwrap_or(1);
1651 let max_peers = env::var("MAX_PEERS")
1652 .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
1653 .unwrap_or(5);
1654 let operations = env::var("OPERATIONS")
1655 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1656 .unwrap_or(10);
1657
1658 let base_text_len = rng.gen_range(0..10);
1659 let base_text = RandomCharIter::new(&mut rng)
1660 .take(base_text_len)
1661 .collect::<String>();
1662 let mut replica_ids = Vec::new();
1663 let mut buffers = Vec::new();
1664 let network = Rc::new(RefCell::new(Network::new(rng.clone())));
1665 let base_buffer = cx.add_model(|cx| Buffer::new(0, base_text.as_str(), cx));
1666
1667 for i in 0..rng.gen_range(min_peers..=max_peers) {
1668 let buffer = cx.add_model(|cx| {
1669 let state = base_buffer.read(cx).to_proto();
1670 let ops = cx
1671 .background()
1672 .block(base_buffer.read(cx).serialize_ops(None, cx));
1673 let mut buffer = Buffer::from_proto(i as ReplicaId, state, None).unwrap();
1674 buffer
1675 .apply_ops(
1676 ops.into_iter()
1677 .map(|op| proto::deserialize_operation(op).unwrap()),
1678 cx,
1679 )
1680 .unwrap();
1681 buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
1682 let network = network.clone();
1683 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1684 if let Event::Operation(op) = event {
1685 network
1686 .borrow_mut()
1687 .broadcast(buffer.replica_id(), vec![proto::serialize_operation(op)]);
1688 }
1689 })
1690 .detach();
1691 buffer
1692 });
1693 buffers.push(buffer);
1694 replica_ids.push(i as ReplicaId);
1695 network.borrow_mut().add_peer(i as ReplicaId);
1696 log::info!("Adding initial peer with replica id {}", i);
1697 }
1698
1699 log::info!("initial text: {:?}", base_text);
1700
1701 let mut now = Instant::now();
1702 let mut mutation_count = operations;
1703 let mut next_diagnostic_id = 0;
1704 let mut active_selections = BTreeMap::default();
1705 loop {
1706 let replica_index = rng.gen_range(0..replica_ids.len());
1707 let replica_id = replica_ids[replica_index];
1708 let buffer = &mut buffers[replica_index];
1709 let mut new_buffer = None;
1710 match rng.gen_range(0..100) {
1711 0..=29 if mutation_count != 0 => {
1712 buffer.update(cx, |buffer, cx| {
1713 buffer.start_transaction_at(now);
1714 buffer.randomly_edit(&mut rng, 5, cx);
1715 buffer.end_transaction_at(now, cx);
1716 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1717 });
1718 mutation_count -= 1;
1719 }
1720 30..=39 if mutation_count != 0 => {
1721 buffer.update(cx, |buffer, cx| {
1722 let mut selections = Vec::new();
1723 for id in 0..rng.gen_range(1..=5) {
1724 let range = buffer.random_byte_range(0, &mut rng);
1725 selections.push(Selection {
1726 id,
1727 start: buffer.anchor_before(range.start),
1728 end: buffer.anchor_before(range.end),
1729 reversed: false,
1730 goal: SelectionGoal::None,
1731 });
1732 }
1733 let selections: Arc<[Selection<Anchor>]> = selections.into();
1734 log::info!(
1735 "peer {} setting active selections: {:?}",
1736 replica_id,
1737 selections
1738 );
1739 active_selections.insert(replica_id, selections.clone());
1740 buffer.set_active_selections(selections, false, Default::default(), cx);
1741 });
1742 mutation_count -= 1;
1743 }
1744 40..=49 if mutation_count != 0 && replica_id == 0 => {
1745 let entry_count = rng.gen_range(1..=5);
1746 buffer.update(cx, |buffer, cx| {
1747 let diagnostics = DiagnosticSet::new(
1748 (0..entry_count).map(|_| {
1749 let range = buffer.random_byte_range(0, &mut rng);
1750 let range = range.to_point_utf16(buffer);
1751 let range = range.start..range.end;
1752 DiagnosticEntry {
1753 range,
1754 diagnostic: Diagnostic {
1755 message: post_inc(&mut next_diagnostic_id).to_string(),
1756 ..Default::default()
1757 },
1758 }
1759 }),
1760 buffer,
1761 );
1762 log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
1763 buffer.update_diagnostics(diagnostics, cx);
1764 });
1765 mutation_count -= 1;
1766 }
1767 50..=59 if replica_ids.len() < max_peers => {
1768 let old_buffer_state = buffer.read(cx).to_proto();
1769 let old_buffer_ops = cx
1770 .background()
1771 .block(buffer.read(cx).serialize_ops(None, cx));
1772 let new_replica_id = (0..=replica_ids.len() as ReplicaId)
1773 .filter(|replica_id| *replica_id != buffer.read(cx).replica_id())
1774 .choose(&mut rng)
1775 .unwrap();
1776 log::info!(
1777 "Adding new replica {} (replicating from {})",
1778 new_replica_id,
1779 replica_id
1780 );
1781 new_buffer = Some(cx.add_model(|cx| {
1782 let mut new_buffer =
1783 Buffer::from_proto(new_replica_id, old_buffer_state, None).unwrap();
1784 new_buffer
1785 .apply_ops(
1786 old_buffer_ops
1787 .into_iter()
1788 .map(|op| deserialize_operation(op).unwrap()),
1789 cx,
1790 )
1791 .unwrap();
1792 log::info!(
1793 "New replica {} text: {:?}",
1794 new_buffer.replica_id(),
1795 new_buffer.text()
1796 );
1797 new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
1798 let network = network.clone();
1799 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1800 if let Event::Operation(op) = event {
1801 network.borrow_mut().broadcast(
1802 buffer.replica_id(),
1803 vec![proto::serialize_operation(op)],
1804 );
1805 }
1806 })
1807 .detach();
1808 new_buffer
1809 }));
1810 network.borrow_mut().replicate(replica_id, new_replica_id);
1811
1812 if new_replica_id as usize == replica_ids.len() {
1813 replica_ids.push(new_replica_id);
1814 } else {
1815 let new_buffer = new_buffer.take().unwrap();
1816 while network.borrow().has_unreceived(new_replica_id) {
1817 let ops = network
1818 .borrow_mut()
1819 .receive(new_replica_id)
1820 .into_iter()
1821 .map(|op| proto::deserialize_operation(op).unwrap());
1822 if ops.len() > 0 {
1823 log::info!(
1824 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1825 new_replica_id,
1826 buffer.read(cx).version(),
1827 ops.len(),
1828 ops
1829 );
1830 new_buffer.update(cx, |new_buffer, cx| {
1831 new_buffer.apply_ops(ops, cx).unwrap();
1832 });
1833 }
1834 }
1835 buffers[new_replica_id as usize] = new_buffer;
1836 }
1837 }
1838 60..=69 if mutation_count != 0 => {
1839 buffer.update(cx, |buffer, cx| {
1840 buffer.randomly_undo_redo(&mut rng, cx);
1841 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1842 });
1843 mutation_count -= 1;
1844 }
1845 _ if network.borrow().has_unreceived(replica_id) => {
1846 let ops = network
1847 .borrow_mut()
1848 .receive(replica_id)
1849 .into_iter()
1850 .map(|op| proto::deserialize_operation(op).unwrap());
1851 if ops.len() > 0 {
1852 log::info!(
1853 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1854 replica_id,
1855 buffer.read(cx).version(),
1856 ops.len(),
1857 ops
1858 );
1859 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
1860 }
1861 }
1862 _ => {}
1863 }
1864
1865 now += Duration::from_millis(rng.gen_range(0..=200));
1866 buffers.extend(new_buffer);
1867
1868 for buffer in &buffers {
1869 buffer.read(cx).check_invariants();
1870 }
1871
1872 if mutation_count == 0 && network.borrow().is_idle() {
1873 break;
1874 }
1875 }
1876
1877 let first_buffer = buffers[0].read(cx).snapshot();
1878 for buffer in &buffers[1..] {
1879 let buffer = buffer.read(cx).snapshot();
1880 assert_eq!(
1881 buffer.version(),
1882 first_buffer.version(),
1883 "Replica {} version != Replica 0 version",
1884 buffer.replica_id()
1885 );
1886 assert_eq!(
1887 buffer.text(),
1888 first_buffer.text(),
1889 "Replica {} text != Replica 0 text",
1890 buffer.replica_id()
1891 );
1892 assert_eq!(
1893 buffer
1894 .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
1895 .collect::<Vec<_>>(),
1896 first_buffer
1897 .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
1898 .collect::<Vec<_>>(),
1899 "Replica {} diagnostics != Replica 0 diagnostics",
1900 buffer.replica_id()
1901 );
1902 }
1903
1904 for buffer in &buffers {
1905 let buffer = buffer.read(cx).snapshot();
1906 let actual_remote_selections = buffer
1907 .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
1908 .map(|(replica_id, _, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
1909 .collect::<Vec<_>>();
1910 let expected_remote_selections = active_selections
1911 .iter()
1912 .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
1913 .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
1914 .collect::<Vec<_>>();
1915 assert_eq!(
1916 actual_remote_selections,
1917 expected_remote_selections,
1918 "Replica {} remote selections != expected selections",
1919 buffer.replica_id()
1920 );
1921 }
1922}
1923
1924#[test]
1925fn test_contiguous_ranges() {
1926 assert_eq!(
1927 contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
1928 &[1..4, 5..7, 9..13]
1929 );
1930
1931 // Respects the `max_len` parameter
1932 assert_eq!(
1933 contiguous_ranges(
1934 [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
1935 3
1936 )
1937 .collect::<Vec<_>>(),
1938 &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
1939 );
1940}
1941
1942fn ruby_lang() -> Language {
1943 Language::new(
1944 LanguageConfig {
1945 name: "Ruby".into(),
1946 path_suffixes: vec!["rb".to_string()],
1947 ..Default::default()
1948 },
1949 Some(tree_sitter_ruby::language()),
1950 )
1951 .with_indents_query(
1952 r#"
1953 (class "end" @end) @indent
1954 (method "end" @end) @indent
1955 (rescue) @outdent
1956 (then) @indent
1957 "#,
1958 )
1959 .unwrap()
1960}
1961
1962fn rust_lang() -> Language {
1963 Language::new(
1964 LanguageConfig {
1965 name: "Rust".into(),
1966 path_suffixes: vec!["rs".to_string()],
1967 ..Default::default()
1968 },
1969 Some(tree_sitter_rust::language()),
1970 )
1971 .with_indents_query(
1972 r#"
1973 (call_expression) @indent
1974 (field_expression) @indent
1975 (_ "(" ")" @end) @indent
1976 (_ "{" "}" @end) @indent
1977 "#,
1978 )
1979 .unwrap()
1980 .with_brackets_query(
1981 r#"
1982 ("{" @open "}" @close)
1983 "#,
1984 )
1985 .unwrap()
1986 .with_outline_query(
1987 r#"
1988 (struct_item
1989 "struct" @context
1990 name: (_) @name) @item
1991 (enum_item
1992 "enum" @context
1993 name: (_) @name) @item
1994 (enum_variant
1995 name: (_) @name) @item
1996 (field_declaration
1997 name: (_) @name) @item
1998 (impl_item
1999 "impl" @context
2000 trait: (_)? @name
2001 "for"? @context
2002 type: (_) @name) @item
2003 (function_item
2004 "fn" @context
2005 name: (_) @name) @item
2006 (mod_item
2007 "mod" @context
2008 name: (_) @name) @item
2009 "#,
2010 )
2011 .unwrap()
2012}
2013
2014fn json_lang() -> Language {
2015 Language::new(
2016 LanguageConfig {
2017 name: "Json".into(),
2018 path_suffixes: vec!["js".to_string()],
2019 ..Default::default()
2020 },
2021 Some(tree_sitter_json::language()),
2022 )
2023}
2024
2025fn javascript_lang() -> Language {
2026 Language::new(
2027 LanguageConfig {
2028 name: "JavaScript".into(),
2029 ..Default::default()
2030 },
2031 Some(tree_sitter_javascript::language()),
2032 )
2033 .with_brackets_query(
2034 r#"
2035 ("{" @open "}" @close)
2036 ("(" @open ")" @close)
2037 "#,
2038 )
2039 .unwrap()
2040}
2041
2042fn get_tree_sexp(buffer: &ModelHandle<Buffer>, cx: &gpui::TestAppContext) -> String {
2043 buffer.read_with(cx, |buffer, _| {
2044 let snapshot = buffer.snapshot();
2045 let layers = snapshot.syntax.layers(buffer.as_text_snapshot());
2046 layers[0].node.to_sexp()
2047 })
2048}
2049
2050// Assert that the enclosing bracket ranges around the selection match the pairs indicated by the marked text in `range_markers`
2051fn assert_bracket_pairs(
2052 selection_text: &'static str,
2053 bracket_pair_texts: Vec<&'static str>,
2054 language: Language,
2055 cx: &mut MutableAppContext,
2056) {
2057 cx.set_global(Settings::test(cx));
2058 let (expected_text, selection_ranges) = marked_text_ranges(selection_text, false);
2059 let buffer = cx.add_model(|cx| {
2060 Buffer::new(0, expected_text.clone(), cx).with_language(Arc::new(language), cx)
2061 });
2062 let buffer = buffer.update(cx, |buffer, _cx| buffer.snapshot());
2063
2064 let selection_range = selection_ranges[0].clone();
2065
2066 let bracket_pairs = bracket_pair_texts
2067 .into_iter()
2068 .map(|pair_text| {
2069 let (bracket_text, ranges) = marked_text_ranges(pair_text, false);
2070 assert_eq!(bracket_text, expected_text);
2071 (ranges[0].clone(), ranges[1].clone())
2072 })
2073 .collect::<Vec<_>>();
2074
2075 assert_set_eq!(
2076 buffer.bracket_ranges(selection_range).collect::<Vec<_>>(),
2077 bracket_pairs
2078 );
2079}