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