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 if rng.gen_bool(0.2) {
1808 log::info!("peer {} clearing active selections", replica_id);
1809 active_selections.remove(&replica_id);
1810 buffer.remove_active_selections(cx);
1811 } else {
1812 let mut selections = Vec::new();
1813 for id in 0..rng.gen_range(1..=5) {
1814 let range = buffer.random_byte_range(0, &mut rng);
1815 selections.push(Selection {
1816 id,
1817 start: buffer.anchor_before(range.start),
1818 end: buffer.anchor_before(range.end),
1819 reversed: false,
1820 goal: SelectionGoal::None,
1821 });
1822 }
1823 let selections: Arc<[Selection<Anchor>]> = selections.into();
1824 log::info!(
1825 "peer {} setting active selections: {:?}",
1826 replica_id,
1827 selections
1828 );
1829 active_selections.insert(replica_id, selections.clone());
1830 buffer.set_active_selections(selections, false, Default::default(), cx);
1831 }
1832 });
1833 mutation_count -= 1;
1834 }
1835 40..=49 if mutation_count != 0 && replica_id == 0 => {
1836 let entry_count = rng.gen_range(1..=5);
1837 buffer.update(cx, |buffer, cx| {
1838 let diagnostics = DiagnosticSet::new(
1839 (0..entry_count).map(|_| {
1840 let range = buffer.random_byte_range(0, &mut rng);
1841 let range = range.to_point_utf16(buffer);
1842 let range = range.start..range.end;
1843 DiagnosticEntry {
1844 range,
1845 diagnostic: Diagnostic {
1846 message: post_inc(&mut next_diagnostic_id).to_string(),
1847 ..Default::default()
1848 },
1849 }
1850 }),
1851 buffer,
1852 );
1853 log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
1854 buffer.update_diagnostics(diagnostics, cx);
1855 });
1856 mutation_count -= 1;
1857 }
1858 50..=59 if replica_ids.len() < max_peers => {
1859 let old_buffer_state = buffer.read(cx).to_proto();
1860 let old_buffer_ops = cx
1861 .background()
1862 .block(buffer.read(cx).serialize_ops(None, cx));
1863 let new_replica_id = (0..=replica_ids.len() as ReplicaId)
1864 .filter(|replica_id| *replica_id != buffer.read(cx).replica_id())
1865 .choose(&mut rng)
1866 .unwrap();
1867 log::info!(
1868 "Adding new replica {} (replicating from {})",
1869 new_replica_id,
1870 replica_id
1871 );
1872 new_buffer = Some(cx.add_model(|cx| {
1873 let mut new_buffer =
1874 Buffer::from_proto(new_replica_id, old_buffer_state, None).unwrap();
1875 new_buffer
1876 .apply_ops(
1877 old_buffer_ops
1878 .into_iter()
1879 .map(|op| deserialize_operation(op).unwrap()),
1880 cx,
1881 )
1882 .unwrap();
1883 log::info!(
1884 "New replica {} text: {:?}",
1885 new_buffer.replica_id(),
1886 new_buffer.text()
1887 );
1888 new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
1889 let network = network.clone();
1890 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1891 if let Event::Operation(op) = event {
1892 network.borrow_mut().broadcast(
1893 buffer.replica_id(),
1894 vec![proto::serialize_operation(op)],
1895 );
1896 }
1897 })
1898 .detach();
1899 new_buffer
1900 }));
1901 network.borrow_mut().replicate(replica_id, new_replica_id);
1902
1903 if new_replica_id as usize == replica_ids.len() {
1904 replica_ids.push(new_replica_id);
1905 } else {
1906 let new_buffer = new_buffer.take().unwrap();
1907 while network.borrow().has_unreceived(new_replica_id) {
1908 let ops = network
1909 .borrow_mut()
1910 .receive(new_replica_id)
1911 .into_iter()
1912 .map(|op| proto::deserialize_operation(op).unwrap());
1913 if ops.len() > 0 {
1914 log::info!(
1915 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1916 new_replica_id,
1917 buffer.read(cx).version(),
1918 ops.len(),
1919 ops
1920 );
1921 new_buffer.update(cx, |new_buffer, cx| {
1922 new_buffer.apply_ops(ops, cx).unwrap();
1923 });
1924 }
1925 }
1926 buffers[new_replica_id as usize] = new_buffer;
1927 }
1928 }
1929 60..=69 if mutation_count != 0 => {
1930 buffer.update(cx, |buffer, cx| {
1931 buffer.randomly_undo_redo(&mut rng, cx);
1932 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1933 });
1934 mutation_count -= 1;
1935 }
1936 _ if network.borrow().has_unreceived(replica_id) => {
1937 let ops = network
1938 .borrow_mut()
1939 .receive(replica_id)
1940 .into_iter()
1941 .map(|op| proto::deserialize_operation(op).unwrap());
1942 if ops.len() > 0 {
1943 log::info!(
1944 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1945 replica_id,
1946 buffer.read(cx).version(),
1947 ops.len(),
1948 ops
1949 );
1950 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
1951 }
1952 }
1953 _ => {}
1954 }
1955
1956 now += Duration::from_millis(rng.gen_range(0..=200));
1957 buffers.extend(new_buffer);
1958
1959 for buffer in &buffers {
1960 buffer.read(cx).check_invariants();
1961 }
1962
1963 if mutation_count == 0 && network.borrow().is_idle() {
1964 break;
1965 }
1966 }
1967
1968 let first_buffer = buffers[0].read(cx).snapshot();
1969 for buffer in &buffers[1..] {
1970 let buffer = buffer.read(cx).snapshot();
1971 assert_eq!(
1972 buffer.version(),
1973 first_buffer.version(),
1974 "Replica {} version != Replica 0 version",
1975 buffer.replica_id()
1976 );
1977 assert_eq!(
1978 buffer.text(),
1979 first_buffer.text(),
1980 "Replica {} text != Replica 0 text",
1981 buffer.replica_id()
1982 );
1983 assert_eq!(
1984 buffer
1985 .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
1986 .collect::<Vec<_>>(),
1987 first_buffer
1988 .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
1989 .collect::<Vec<_>>(),
1990 "Replica {} diagnostics != Replica 0 diagnostics",
1991 buffer.replica_id()
1992 );
1993 }
1994
1995 for buffer in &buffers {
1996 let buffer = buffer.read(cx).snapshot();
1997 let actual_remote_selections = buffer
1998 .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
1999 .map(|(replica_id, _, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
2000 .collect::<Vec<_>>();
2001 let expected_remote_selections = active_selections
2002 .iter()
2003 .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
2004 .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
2005 .collect::<Vec<_>>();
2006 assert_eq!(
2007 actual_remote_selections,
2008 expected_remote_selections,
2009 "Replica {} remote selections != expected selections",
2010 buffer.replica_id()
2011 );
2012 }
2013}
2014
2015#[test]
2016fn test_contiguous_ranges() {
2017 assert_eq!(
2018 contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
2019 &[1..4, 5..7, 9..13]
2020 );
2021
2022 // Respects the `max_len` parameter
2023 assert_eq!(
2024 contiguous_ranges(
2025 [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
2026 3
2027 )
2028 .collect::<Vec<_>>(),
2029 &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
2030 );
2031}
2032
2033#[gpui::test(iterations = 500)]
2034fn test_trailing_whitespace_ranges(mut rng: StdRng) {
2035 // Generate a random multi-line string containing
2036 // some lines with trailing whitespace.
2037 let mut text = String::new();
2038 for _ in 0..rng.gen_range(0..16) {
2039 for _ in 0..rng.gen_range(0..36) {
2040 text.push(match rng.gen_range(0..10) {
2041 0..=1 => ' ',
2042 3 => '\t',
2043 _ => rng.gen_range('a'..'z'),
2044 });
2045 }
2046 text.push('\n');
2047 }
2048
2049 match rng.gen_range(0..10) {
2050 // sometimes remove the last newline
2051 0..=1 => drop(text.pop()), //
2052
2053 // sometimes add extra newlines
2054 2..=3 => text.push_str(&"\n".repeat(rng.gen_range(1..5))),
2055 _ => {}
2056 }
2057
2058 let rope = Rope::from(text.as_str());
2059 let actual_ranges = trailing_whitespace_ranges(&rope);
2060 let expected_ranges = TRAILING_WHITESPACE_REGEX
2061 .find_iter(&text)
2062 .map(|m| m.range())
2063 .collect::<Vec<_>>();
2064 assert_eq!(
2065 actual_ranges,
2066 expected_ranges,
2067 "wrong ranges for text lines:\n{:?}",
2068 text.split("\n").collect::<Vec<_>>()
2069 );
2070}
2071
2072fn ruby_lang() -> Language {
2073 Language::new(
2074 LanguageConfig {
2075 name: "Ruby".into(),
2076 path_suffixes: vec!["rb".to_string()],
2077 ..Default::default()
2078 },
2079 Some(tree_sitter_ruby::language()),
2080 )
2081 .with_indents_query(
2082 r#"
2083 (class "end" @end) @indent
2084 (method "end" @end) @indent
2085 (rescue) @outdent
2086 (then) @indent
2087 "#,
2088 )
2089 .unwrap()
2090}
2091
2092fn rust_lang() -> Language {
2093 Language::new(
2094 LanguageConfig {
2095 name: "Rust".into(),
2096 path_suffixes: vec!["rs".to_string()],
2097 ..Default::default()
2098 },
2099 Some(tree_sitter_rust::language()),
2100 )
2101 .with_indents_query(
2102 r#"
2103 (call_expression) @indent
2104 (field_expression) @indent
2105 (_ "(" ")" @end) @indent
2106 (_ "{" "}" @end) @indent
2107 "#,
2108 )
2109 .unwrap()
2110 .with_brackets_query(
2111 r#"
2112 ("{" @open "}" @close)
2113 "#,
2114 )
2115 .unwrap()
2116 .with_outline_query(
2117 r#"
2118 (struct_item
2119 "struct" @context
2120 name: (_) @name) @item
2121 (enum_item
2122 "enum" @context
2123 name: (_) @name) @item
2124 (enum_variant
2125 name: (_) @name) @item
2126 (field_declaration
2127 name: (_) @name) @item
2128 (impl_item
2129 "impl" @context
2130 trait: (_)? @name
2131 "for"? @context
2132 type: (_) @name) @item
2133 (function_item
2134 "fn" @context
2135 name: (_) @name) @item
2136 (mod_item
2137 "mod" @context
2138 name: (_) @name) @item
2139 "#,
2140 )
2141 .unwrap()
2142}
2143
2144fn json_lang() -> Language {
2145 Language::new(
2146 LanguageConfig {
2147 name: "Json".into(),
2148 path_suffixes: vec!["js".to_string()],
2149 ..Default::default()
2150 },
2151 Some(tree_sitter_json::language()),
2152 )
2153}
2154
2155fn javascript_lang() -> Language {
2156 Language::new(
2157 LanguageConfig {
2158 name: "JavaScript".into(),
2159 ..Default::default()
2160 },
2161 Some(tree_sitter_javascript::language()),
2162 )
2163 .with_brackets_query(
2164 r#"
2165 ("{" @open "}" @close)
2166 ("(" @open ")" @close)
2167 "#,
2168 )
2169 .unwrap()
2170}
2171
2172fn get_tree_sexp(buffer: &ModelHandle<Buffer>, cx: &gpui::TestAppContext) -> String {
2173 buffer.read_with(cx, |buffer, _| {
2174 let snapshot = buffer.snapshot();
2175 let layers = snapshot.syntax.layers(buffer.as_text_snapshot());
2176 layers[0].node.to_sexp()
2177 })
2178}
2179
2180// Assert that the enclosing bracket ranges around the selection match the pairs indicated by the marked text in `range_markers`
2181fn assert_bracket_pairs(
2182 selection_text: &'static str,
2183 bracket_pair_texts: Vec<&'static str>,
2184 language: Language,
2185 cx: &mut MutableAppContext,
2186) {
2187 cx.set_global(Settings::test(cx));
2188 let (expected_text, selection_ranges) = marked_text_ranges(selection_text, false);
2189 let buffer = cx.add_model(|cx| {
2190 Buffer::new(0, expected_text.clone(), cx).with_language(Arc::new(language), cx)
2191 });
2192 let buffer = buffer.update(cx, |buffer, _cx| buffer.snapshot());
2193
2194 let selection_range = selection_ranges[0].clone();
2195
2196 let bracket_pairs = bracket_pair_texts
2197 .into_iter()
2198 .map(|pair_text| {
2199 let (bracket_text, ranges) = marked_text_ranges(pair_text, false);
2200 assert_eq!(bracket_text, expected_text);
2201 (ranges[0].clone(), ranges[1].clone())
2202 })
2203 .collect::<Vec<_>>();
2204
2205 assert_set_eq!(
2206 buffer.bracket_ranges(selection_range).collect::<Vec<_>>(),
2207 bracket_pairs
2208 );
2209}