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