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