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