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