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