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