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().unwrap(), &[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!(
1832 string_config.line_comment_prefixes().unwrap(),
1833 &[Arc::from("// ")]
1834 );
1835 // Second bracket pair is disabled
1836 assert_eq!(
1837 string_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
1838 &[true, false]
1839 );
1840
1841 // In between JSX tags: use the `element` override.
1842 let element_config = snapshot
1843 .language_scope_at(text.find("<F>").unwrap())
1844 .unwrap();
1845 assert_eq!(element_config.line_comment_prefixes(), None);
1846 assert_eq!(
1847 element_config.block_comment_delimiters(),
1848 Some((&"{/*".into(), &"*/}".into()))
1849 );
1850 assert_eq!(
1851 element_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
1852 &[true, true]
1853 );
1854
1855 // Within a JSX tag: use the default config.
1856 let tag_config = snapshot
1857 .language_scope_at(text.find(" d=").unwrap() + 1)
1858 .unwrap();
1859 assert_eq!(
1860 tag_config.line_comment_prefixes().unwrap(),
1861 &[Arc::from("// ")]
1862 );
1863 assert_eq!(
1864 tag_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
1865 &[true, true]
1866 );
1867
1868 // In a JSX expression: use the default config.
1869 let expression_in_element_config = snapshot
1870 .language_scope_at(text.find('{').unwrap() + 1)
1871 .unwrap();
1872 assert_eq!(
1873 expression_in_element_config
1874 .line_comment_prefixes()
1875 .unwrap(),
1876 &[Arc::from("// ")]
1877 );
1878 assert_eq!(
1879 expression_in_element_config
1880 .brackets()
1881 .map(|e| e.1)
1882 .collect::<Vec<_>>(),
1883 &[true, true]
1884 );
1885
1886 buffer
1887 });
1888}
1889
1890#[gpui::test]
1891fn test_language_scope_at_with_rust(cx: &mut AppContext) {
1892 init_settings(cx, |_| {});
1893
1894 cx.new_model(|cx| {
1895 let language = Language::new(
1896 LanguageConfig {
1897 name: "Rust".into(),
1898 brackets: BracketPairConfig {
1899 pairs: vec![
1900 BracketPair {
1901 start: "{".into(),
1902 end: "}".into(),
1903 close: true,
1904 newline: false,
1905 },
1906 BracketPair {
1907 start: "'".into(),
1908 end: "'".into(),
1909 close: true,
1910 newline: false,
1911 },
1912 ],
1913 disabled_scopes_by_bracket_ix: vec![
1914 Vec::new(), //
1915 vec!["string".into()],
1916 ],
1917 },
1918 ..Default::default()
1919 },
1920 Some(tree_sitter_rust::language()),
1921 )
1922 .with_override_query(
1923 r#"
1924 (string_literal) @string
1925 "#,
1926 )
1927 .unwrap();
1928
1929 let text = r#"
1930 const S: &'static str = "hello";
1931 "#
1932 .unindent();
1933
1934 let buffer = Buffer::local(text.clone(), cx).with_language(Arc::new(language), cx);
1935 let snapshot = buffer.snapshot();
1936
1937 // By default, all brackets are enabled
1938 let config = snapshot.language_scope_at(0).unwrap();
1939 assert_eq!(
1940 config.brackets().map(|e| e.1).collect::<Vec<_>>(),
1941 &[true, true]
1942 );
1943
1944 // Within a string, the quotation brackets are disabled.
1945 let string_config = snapshot
1946 .language_scope_at(text.find("ello").unwrap())
1947 .unwrap();
1948 assert_eq!(
1949 string_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
1950 &[true, false]
1951 );
1952
1953 buffer
1954 });
1955}
1956
1957#[gpui::test]
1958fn test_language_scope_at_with_combined_injections(cx: &mut AppContext) {
1959 init_settings(cx, |_| {});
1960
1961 cx.new_model(|cx| {
1962 let text = r#"
1963 <ol>
1964 <% people.each do |person| %>
1965 <li>
1966 <%= person.name %>
1967 </li>
1968 <% end %>
1969 </ol>
1970 "#
1971 .unindent();
1972
1973 let language_registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
1974 language_registry.add(Arc::new(ruby_lang()));
1975 language_registry.add(Arc::new(html_lang()));
1976 language_registry.add(Arc::new(erb_lang()));
1977
1978 let mut buffer = Buffer::local(text, cx);
1979 buffer.set_language_registry(language_registry.clone());
1980 buffer.set_language(
1981 language_registry
1982 .language_for_name("ERB")
1983 .now_or_never()
1984 .unwrap()
1985 .ok(),
1986 cx,
1987 );
1988
1989 let snapshot = buffer.snapshot();
1990 let html_config = snapshot.language_scope_at(Point::new(2, 4)).unwrap();
1991 assert_eq!(html_config.line_comment_prefixes(), Some(&vec![]));
1992 assert_eq!(
1993 html_config.block_comment_delimiters(),
1994 Some((&"<!--".into(), &"-->".into()))
1995 );
1996
1997 let ruby_config = snapshot.language_scope_at(Point::new(3, 12)).unwrap();
1998 assert_eq!(
1999 ruby_config.line_comment_prefixes().unwrap(),
2000 &[Arc::from("# ")]
2001 );
2002 assert_eq!(ruby_config.block_comment_delimiters(), None);
2003
2004 buffer
2005 });
2006}
2007
2008#[gpui::test]
2009fn test_serialization(cx: &mut gpui::AppContext) {
2010 let mut now = Instant::now();
2011
2012 let buffer1 = cx.new_model(|cx| {
2013 let mut buffer = Buffer::local("abc", cx);
2014 buffer.edit([(3..3, "D")], None, cx);
2015
2016 now += Duration::from_secs(1);
2017 buffer.start_transaction_at(now);
2018 buffer.edit([(4..4, "E")], None, cx);
2019 buffer.end_transaction_at(now, cx);
2020 assert_eq!(buffer.text(), "abcDE");
2021
2022 buffer.undo(cx);
2023 assert_eq!(buffer.text(), "abcD");
2024
2025 buffer.edit([(4..4, "F")], None, cx);
2026 assert_eq!(buffer.text(), "abcDF");
2027 buffer
2028 });
2029 assert_eq!(buffer1.read(cx).text(), "abcDF");
2030
2031 let state = buffer1.read(cx).to_proto();
2032 let ops = cx
2033 .background_executor()
2034 .block(buffer1.read(cx).serialize_ops(None, cx));
2035 let buffer2 = cx.new_model(|cx| {
2036 let mut buffer = Buffer::from_proto(1, Capability::ReadWrite, state, None).unwrap();
2037 buffer
2038 .apply_ops(
2039 ops.into_iter()
2040 .map(|op| proto::deserialize_operation(op).unwrap()),
2041 cx,
2042 )
2043 .unwrap();
2044 buffer
2045 });
2046 assert_eq!(buffer2.read(cx).text(), "abcDF");
2047}
2048
2049#[gpui::test(iterations = 100)]
2050fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
2051 let min_peers = env::var("MIN_PEERS")
2052 .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
2053 .unwrap_or(1);
2054 let max_peers = env::var("MAX_PEERS")
2055 .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
2056 .unwrap_or(5);
2057 let operations = env::var("OPERATIONS")
2058 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
2059 .unwrap_or(10);
2060
2061 let base_text_len = rng.gen_range(0..10);
2062 let base_text = RandomCharIter::new(&mut rng)
2063 .take(base_text_len)
2064 .collect::<String>();
2065 let mut replica_ids = Vec::new();
2066 let mut buffers = Vec::new();
2067 let network = Arc::new(Mutex::new(Network::new(rng.clone())));
2068 let base_buffer = cx.new_model(|cx| Buffer::local(base_text.as_str(), cx));
2069
2070 for i in 0..rng.gen_range(min_peers..=max_peers) {
2071 let buffer = cx.new_model(|cx| {
2072 let state = base_buffer.read(cx).to_proto();
2073 let ops = cx
2074 .background_executor()
2075 .block(base_buffer.read(cx).serialize_ops(None, cx));
2076 let mut buffer =
2077 Buffer::from_proto(i as ReplicaId, Capability::ReadWrite, state, None).unwrap();
2078 buffer
2079 .apply_ops(
2080 ops.into_iter()
2081 .map(|op| proto::deserialize_operation(op).unwrap()),
2082 cx,
2083 )
2084 .unwrap();
2085 buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
2086 let network = network.clone();
2087 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
2088 if let Event::Operation(op) = event {
2089 network
2090 .lock()
2091 .broadcast(buffer.replica_id(), vec![proto::serialize_operation(op)]);
2092 }
2093 })
2094 .detach();
2095 buffer
2096 });
2097
2098 buffers.push(buffer);
2099 replica_ids.push(i as ReplicaId);
2100 network.lock().add_peer(i as ReplicaId);
2101 log::info!("Adding initial peer with replica id {}", i);
2102 }
2103
2104 log::info!("initial text: {:?}", base_text);
2105
2106 let mut now = Instant::now();
2107 let mut mutation_count = operations;
2108 let mut next_diagnostic_id = 0;
2109 let mut active_selections = BTreeMap::default();
2110 loop {
2111 let replica_index = rng.gen_range(0..replica_ids.len());
2112 let replica_id = replica_ids[replica_index];
2113 let buffer = &mut buffers[replica_index];
2114 let mut new_buffer = None;
2115 match rng.gen_range(0..100) {
2116 0..=29 if mutation_count != 0 => {
2117 buffer.update(cx, |buffer, cx| {
2118 buffer.start_transaction_at(now);
2119 buffer.randomly_edit(&mut rng, 5, cx);
2120 buffer.end_transaction_at(now, cx);
2121 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
2122 });
2123 mutation_count -= 1;
2124 }
2125 30..=39 if mutation_count != 0 => {
2126 buffer.update(cx, |buffer, cx| {
2127 if rng.gen_bool(0.2) {
2128 log::info!("peer {} clearing active selections", replica_id);
2129 active_selections.remove(&replica_id);
2130 buffer.remove_active_selections(cx);
2131 } else {
2132 let mut selections = Vec::new();
2133 for id in 0..rng.gen_range(1..=5) {
2134 let range = buffer.random_byte_range(0, &mut rng);
2135 selections.push(Selection {
2136 id,
2137 start: buffer.anchor_before(range.start),
2138 end: buffer.anchor_before(range.end),
2139 reversed: false,
2140 goal: SelectionGoal::None,
2141 });
2142 }
2143 let selections: Arc<[Selection<Anchor>]> = selections.into();
2144 log::info!(
2145 "peer {} setting active selections: {:?}",
2146 replica_id,
2147 selections
2148 );
2149 active_selections.insert(replica_id, selections.clone());
2150 buffer.set_active_selections(selections, false, Default::default(), cx);
2151 }
2152 });
2153 mutation_count -= 1;
2154 }
2155 40..=49 if mutation_count != 0 && replica_id == 0 => {
2156 let entry_count = rng.gen_range(1..=5);
2157 buffer.update(cx, |buffer, cx| {
2158 let diagnostics = DiagnosticSet::new(
2159 (0..entry_count).map(|_| {
2160 let range = buffer.random_byte_range(0, &mut rng);
2161 let range = range.to_point_utf16(buffer);
2162 let range = range.start..range.end;
2163 DiagnosticEntry {
2164 range,
2165 diagnostic: Diagnostic {
2166 message: post_inc(&mut next_diagnostic_id).to_string(),
2167 ..Default::default()
2168 },
2169 }
2170 }),
2171 buffer,
2172 );
2173 log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
2174 buffer.update_diagnostics(LanguageServerId(0), diagnostics, cx);
2175 });
2176 mutation_count -= 1;
2177 }
2178 50..=59 if replica_ids.len() < max_peers => {
2179 let old_buffer_state = buffer.read(cx).to_proto();
2180 let old_buffer_ops = cx
2181 .background_executor()
2182 .block(buffer.read(cx).serialize_ops(None, cx));
2183 let new_replica_id = (0..=replica_ids.len() as ReplicaId)
2184 .filter(|replica_id| *replica_id != buffer.read(cx).replica_id())
2185 .choose(&mut rng)
2186 .unwrap();
2187 log::info!(
2188 "Adding new replica {} (replicating from {})",
2189 new_replica_id,
2190 replica_id
2191 );
2192 new_buffer = Some(cx.new_model(|cx| {
2193 let mut new_buffer = Buffer::from_proto(
2194 new_replica_id,
2195 Capability::ReadWrite,
2196 old_buffer_state,
2197 None,
2198 )
2199 .unwrap();
2200 new_buffer
2201 .apply_ops(
2202 old_buffer_ops
2203 .into_iter()
2204 .map(|op| deserialize_operation(op).unwrap()),
2205 cx,
2206 )
2207 .unwrap();
2208 log::info!(
2209 "New replica {} text: {:?}",
2210 new_buffer.replica_id(),
2211 new_buffer.text()
2212 );
2213 new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
2214 let network = network.clone();
2215 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
2216 if let Event::Operation(op) = event {
2217 network.lock().broadcast(
2218 buffer.replica_id(),
2219 vec![proto::serialize_operation(op)],
2220 );
2221 }
2222 })
2223 .detach();
2224 new_buffer
2225 }));
2226 network.lock().replicate(replica_id, new_replica_id);
2227
2228 if new_replica_id as usize == replica_ids.len() {
2229 replica_ids.push(new_replica_id);
2230 } else {
2231 let new_buffer = new_buffer.take().unwrap();
2232 while network.lock().has_unreceived(new_replica_id) {
2233 let ops = network
2234 .lock()
2235 .receive(new_replica_id)
2236 .into_iter()
2237 .map(|op| proto::deserialize_operation(op).unwrap());
2238 if ops.len() > 0 {
2239 log::info!(
2240 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
2241 new_replica_id,
2242 buffer.read(cx).version(),
2243 ops.len(),
2244 ops
2245 );
2246 new_buffer.update(cx, |new_buffer, cx| {
2247 new_buffer.apply_ops(ops, cx).unwrap();
2248 });
2249 }
2250 }
2251 buffers[new_replica_id as usize] = new_buffer;
2252 }
2253 }
2254 60..=69 if mutation_count != 0 => {
2255 buffer.update(cx, |buffer, cx| {
2256 buffer.randomly_undo_redo(&mut rng, cx);
2257 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
2258 });
2259 mutation_count -= 1;
2260 }
2261 _ if network.lock().has_unreceived(replica_id) => {
2262 let ops = network
2263 .lock()
2264 .receive(replica_id)
2265 .into_iter()
2266 .map(|op| proto::deserialize_operation(op).unwrap());
2267 if ops.len() > 0 {
2268 log::info!(
2269 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
2270 replica_id,
2271 buffer.read(cx).version(),
2272 ops.len(),
2273 ops
2274 );
2275 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
2276 }
2277 }
2278 _ => {}
2279 }
2280
2281 now += Duration::from_millis(rng.gen_range(0..=200));
2282 buffers.extend(new_buffer);
2283
2284 for buffer in &buffers {
2285 buffer.read(cx).check_invariants();
2286 }
2287
2288 if mutation_count == 0 && network.lock().is_idle() {
2289 break;
2290 }
2291 }
2292
2293 let first_buffer = buffers[0].read(cx).snapshot();
2294 for buffer in &buffers[1..] {
2295 let buffer = buffer.read(cx).snapshot();
2296 assert_eq!(
2297 buffer.version(),
2298 first_buffer.version(),
2299 "Replica {} version != Replica 0 version",
2300 buffer.replica_id()
2301 );
2302 assert_eq!(
2303 buffer.text(),
2304 first_buffer.text(),
2305 "Replica {} text != Replica 0 text",
2306 buffer.replica_id()
2307 );
2308 assert_eq!(
2309 buffer
2310 .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
2311 .collect::<Vec<_>>(),
2312 first_buffer
2313 .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
2314 .collect::<Vec<_>>(),
2315 "Replica {} diagnostics != Replica 0 diagnostics",
2316 buffer.replica_id()
2317 );
2318 }
2319
2320 for buffer in &buffers {
2321 let buffer = buffer.read(cx).snapshot();
2322 let actual_remote_selections = buffer
2323 .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
2324 .map(|(replica_id, _, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
2325 .collect::<Vec<_>>();
2326 let expected_remote_selections = active_selections
2327 .iter()
2328 .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
2329 .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
2330 .collect::<Vec<_>>();
2331 assert_eq!(
2332 actual_remote_selections,
2333 expected_remote_selections,
2334 "Replica {} remote selections != expected selections",
2335 buffer.replica_id()
2336 );
2337 }
2338}
2339
2340#[test]
2341fn test_contiguous_ranges() {
2342 assert_eq!(
2343 contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
2344 &[1..4, 5..7, 9..13]
2345 );
2346
2347 // Respects the `max_len` parameter
2348 assert_eq!(
2349 contiguous_ranges(
2350 [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
2351 3
2352 )
2353 .collect::<Vec<_>>(),
2354 &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
2355 );
2356}
2357
2358#[gpui::test(iterations = 500)]
2359fn test_trailing_whitespace_ranges(mut rng: StdRng) {
2360 // Generate a random multi-line string containing
2361 // some lines with trailing whitespace.
2362 let mut text = String::new();
2363 for _ in 0..rng.gen_range(0..16) {
2364 for _ in 0..rng.gen_range(0..36) {
2365 text.push(match rng.gen_range(0..10) {
2366 0..=1 => ' ',
2367 3 => '\t',
2368 _ => rng.gen_range('a'..'z'),
2369 });
2370 }
2371 text.push('\n');
2372 }
2373
2374 match rng.gen_range(0..10) {
2375 // sometimes remove the last newline
2376 0..=1 => drop(text.pop()), //
2377
2378 // sometimes add extra newlines
2379 2..=3 => text.push_str(&"\n".repeat(rng.gen_range(1..5))),
2380 _ => {}
2381 }
2382
2383 let rope = Rope::from(text.as_str());
2384 let actual_ranges = trailing_whitespace_ranges(&rope);
2385 let expected_ranges = TRAILING_WHITESPACE_REGEX
2386 .find_iter(&text)
2387 .map(|m| m.range())
2388 .collect::<Vec<_>>();
2389 assert_eq!(
2390 actual_ranges,
2391 expected_ranges,
2392 "wrong ranges for text lines:\n{:?}",
2393 text.split('\n').collect::<Vec<_>>()
2394 );
2395}
2396
2397fn ruby_lang() -> Language {
2398 Language::new(
2399 LanguageConfig {
2400 name: "Ruby".into(),
2401 matcher: LanguageMatcher {
2402 path_suffixes: vec!["rb".to_string()],
2403 ..Default::default()
2404 },
2405 line_comments: vec!["# ".into()],
2406 ..Default::default()
2407 },
2408 Some(tree_sitter_ruby::language()),
2409 )
2410 .with_indents_query(
2411 r#"
2412 (class "end" @end) @indent
2413 (method "end" @end) @indent
2414 (rescue) @outdent
2415 (then) @indent
2416 "#,
2417 )
2418 .unwrap()
2419}
2420
2421fn html_lang() -> Language {
2422 Language::new(
2423 LanguageConfig {
2424 name: "HTML".into(),
2425 block_comment: Some(("<!--".into(), "-->".into())),
2426 ..Default::default()
2427 },
2428 Some(tree_sitter_html::language()),
2429 )
2430 .with_indents_query(
2431 "
2432 (element
2433 (start_tag) @start
2434 (end_tag)? @end) @indent
2435 ",
2436 )
2437 .unwrap()
2438 .with_injection_query(
2439 r#"
2440 (script_element
2441 (raw_text) @content
2442 (#set! "language" "javascript"))
2443 "#,
2444 )
2445 .unwrap()
2446}
2447
2448fn erb_lang() -> Language {
2449 Language::new(
2450 LanguageConfig {
2451 name: "ERB".into(),
2452 matcher: LanguageMatcher {
2453 path_suffixes: vec!["erb".to_string()],
2454 ..Default::default()
2455 },
2456 block_comment: Some(("<%#".into(), "%>".into())),
2457 ..Default::default()
2458 },
2459 Some(tree_sitter_embedded_template::language()),
2460 )
2461 .with_injection_query(
2462 r#"
2463 (
2464 (code) @content
2465 (#set! "language" "ruby")
2466 (#set! "combined")
2467 )
2468
2469 (
2470 (content) @content
2471 (#set! "language" "html")
2472 (#set! "combined")
2473 )
2474 "#,
2475 )
2476 .unwrap()
2477}
2478
2479fn rust_lang() -> Language {
2480 Language::new(
2481 LanguageConfig {
2482 name: "Rust".into(),
2483 matcher: LanguageMatcher {
2484 path_suffixes: vec!["rs".to_string()],
2485 ..Default::default()
2486 },
2487 ..Default::default()
2488 },
2489 Some(tree_sitter_rust::language()),
2490 )
2491 .with_indents_query(
2492 r#"
2493 (call_expression) @indent
2494 (field_expression) @indent
2495 (_ "(" ")" @end) @indent
2496 (_ "{" "}" @end) @indent
2497 "#,
2498 )
2499 .unwrap()
2500 .with_brackets_query(
2501 r#"
2502 ("{" @open "}" @close)
2503 "#,
2504 )
2505 .unwrap()
2506 .with_outline_query(
2507 r#"
2508 (struct_item
2509 "struct" @context
2510 name: (_) @name) @item
2511 (enum_item
2512 "enum" @context
2513 name: (_) @name) @item
2514 (enum_variant
2515 name: (_) @name) @item
2516 (field_declaration
2517 name: (_) @name) @item
2518 (impl_item
2519 "impl" @context
2520 trait: (_)? @name
2521 "for"? @context
2522 type: (_) @name) @item
2523 (function_item
2524 "fn" @context
2525 name: (_) @name) @item
2526 (mod_item
2527 "mod" @context
2528 name: (_) @name) @item
2529 "#,
2530 )
2531 .unwrap()
2532}
2533
2534fn json_lang() -> Language {
2535 Language::new(
2536 LanguageConfig {
2537 name: "Json".into(),
2538 matcher: LanguageMatcher {
2539 path_suffixes: vec!["js".to_string()],
2540 ..Default::default()
2541 },
2542 ..Default::default()
2543 },
2544 Some(tree_sitter_json::language()),
2545 )
2546}
2547
2548fn javascript_lang() -> Language {
2549 Language::new(
2550 LanguageConfig {
2551 name: "JavaScript".into(),
2552 ..Default::default()
2553 },
2554 Some(tree_sitter_typescript::language_tsx()),
2555 )
2556 .with_brackets_query(
2557 r#"
2558 ("{" @open "}" @close)
2559 ("(" @open ")" @close)
2560 "#,
2561 )
2562 .unwrap()
2563 .with_indents_query(
2564 r#"
2565 (object "}" @end) @indent
2566 "#,
2567 )
2568 .unwrap()
2569}
2570
2571fn get_tree_sexp(buffer: &Model<Buffer>, cx: &mut gpui::TestAppContext) -> String {
2572 buffer.update(cx, |buffer, _| {
2573 let snapshot = buffer.snapshot();
2574 let layers = snapshot.syntax.layers(buffer.as_text_snapshot());
2575 layers[0].node().to_sexp()
2576 })
2577}
2578
2579// Assert that the enclosing bracket ranges around the selection match the pairs indicated by the marked text in `range_markers`
2580fn assert_bracket_pairs(
2581 selection_text: &'static str,
2582 bracket_pair_texts: Vec<&'static str>,
2583 language: Language,
2584 cx: &mut AppContext,
2585) {
2586 let (expected_text, selection_ranges) = marked_text_ranges(selection_text, false);
2587 let buffer = cx.new_model(|cx| {
2588 Buffer::local(expected_text.clone(), cx).with_language(Arc::new(language), cx)
2589 });
2590 let buffer = buffer.update(cx, |buffer, _cx| buffer.snapshot());
2591
2592 let selection_range = selection_ranges[0].clone();
2593
2594 let bracket_pairs = bracket_pair_texts
2595 .into_iter()
2596 .map(|pair_text| {
2597 let (bracket_text, ranges) = marked_text_ranges(pair_text, false);
2598 assert_eq!(bracket_text, expected_text);
2599 (ranges[0].clone(), ranges[1].clone())
2600 })
2601 .collect::<Vec<_>>();
2602
2603 assert_set_eq!(
2604 buffer.bracket_ranges(selection_range).collect::<Vec<_>>(),
2605 bracket_pairs
2606 );
2607}
2608
2609fn init_settings(cx: &mut AppContext, f: fn(&mut AllLanguageSettingsContent)) {
2610 let settings_store = SettingsStore::test(cx);
2611 cx.set_global(settings_store);
2612 crate::init(cx);
2613 cx.update_global::<SettingsStore, _>(|settings, cx| {
2614 settings.update_user_settings::<AllLanguageSettings>(cx, f);
2615 });
2616}