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