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