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