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