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