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