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