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]
779fn test_outline_annotations(cx: &mut AppContext) {
780 // Add this new test case
781 let text = r#"
782 /// This is a doc comment
783 /// that spans multiple lines
784 fn annotated_function() {
785 // This is not an annotation
786 }
787
788 // This is a single-line annotation
789 fn another_function() {}
790
791 fn unannotated_function() {}
792
793 // This comment is not an annotation
794
795 fn function_after_blank_line() {}
796 "#
797 .unindent();
798
799 let buffer =
800 cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
801 let outline = buffer
802 .update(cx, |buffer, _| buffer.snapshot().outline(None))
803 .unwrap();
804
805 assert_eq!(
806 outline
807 .items
808 .into_iter()
809 .map(|item| (
810 item.text,
811 item.depth,
812 item.annotation_range
813 .map(|range| { buffer.read(cx).text_for_range(range).collect::<String>() })
814 ))
815 .collect::<Vec<_>>(),
816 &[
817 (
818 "fn annotated_function".to_string(),
819 0,
820 Some("/// This is a doc comment\n/// that spans multiple lines".to_string())
821 ),
822 (
823 "fn another_function".to_string(),
824 0,
825 Some("// This is a single-line annotation".to_string())
826 ),
827 ("fn unannotated_function".to_string(), 0, None),
828 ("fn function_after_blank_line".to_string(), 0, None),
829 ]
830 );
831}
832
833#[gpui::test]
834async fn test_symbols_containing(cx: &mut gpui::TestAppContext) {
835 let text = r#"
836 impl Person {
837 fn one() {
838 1
839 }
840
841 fn two() {
842 2
843 }fn three() {
844 3
845 }
846 }
847 "#
848 .unindent();
849
850 let buffer =
851 cx.new_model(|cx| Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx));
852 let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
853
854 // point is at the start of an item
855 assert_eq!(
856 symbols_containing(Point::new(1, 4), &snapshot),
857 vec![
858 (
859 "impl Person".to_string(),
860 Point::new(0, 0)..Point::new(10, 1)
861 ),
862 ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
863 ]
864 );
865
866 // point is in the middle of an item
867 assert_eq!(
868 symbols_containing(Point::new(2, 8), &snapshot),
869 vec![
870 (
871 "impl Person".to_string(),
872 Point::new(0, 0)..Point::new(10, 1)
873 ),
874 ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
875 ]
876 );
877
878 // point is at the end of an item
879 assert_eq!(
880 symbols_containing(Point::new(3, 5), &snapshot),
881 vec![
882 (
883 "impl Person".to_string(),
884 Point::new(0, 0)..Point::new(10, 1)
885 ),
886 ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
887 ]
888 );
889
890 // point is in between two adjacent items
891 assert_eq!(
892 symbols_containing(Point::new(7, 5), &snapshot),
893 vec![
894 (
895 "impl Person".to_string(),
896 Point::new(0, 0)..Point::new(10, 1)
897 ),
898 ("fn two".to_string(), Point::new(5, 4)..Point::new(7, 5))
899 ]
900 );
901
902 fn symbols_containing(
903 position: Point,
904 snapshot: &BufferSnapshot,
905 ) -> Vec<(String, Range<Point>)> {
906 snapshot
907 .symbols_containing(position, None)
908 .unwrap()
909 .into_iter()
910 .map(|item| {
911 (
912 item.text,
913 item.range.start.to_point(snapshot)..item.range.end.to_point(snapshot),
914 )
915 })
916 .collect()
917 }
918}
919
920#[gpui::test]
921fn test_enclosing_bracket_ranges(cx: &mut AppContext) {
922 let mut assert = |selection_text, range_markers| {
923 assert_bracket_pairs(selection_text, range_markers, rust_lang(), cx)
924 };
925
926 assert(
927 indoc! {"
928 mod x {
929 moˇd y {
930
931 }
932 }
933 let foo = 1;"},
934 vec![indoc! {"
935 mod x «{»
936 mod y {
937
938 }
939 «}»
940 let foo = 1;"}],
941 );
942
943 assert(
944 indoc! {"
945 mod x {
946 mod y ˇ{
947
948 }
949 }
950 let foo = 1;"},
951 vec![
952 indoc! {"
953 mod x «{»
954 mod y {
955
956 }
957 «}»
958 let foo = 1;"},
959 indoc! {"
960 mod x {
961 mod y «{»
962
963 «}»
964 }
965 let foo = 1;"},
966 ],
967 );
968
969 assert(
970 indoc! {"
971 mod x {
972 mod y {
973
974 }ˇ
975 }
976 let foo = 1;"},
977 vec![
978 indoc! {"
979 mod x «{»
980 mod y {
981
982 }
983 «}»
984 let foo = 1;"},
985 indoc! {"
986 mod x {
987 mod y «{»
988
989 «}»
990 }
991 let foo = 1;"},
992 ],
993 );
994
995 assert(
996 indoc! {"
997 mod x {
998 mod y {
999
1000 }
1001 ˇ}
1002 let foo = 1;"},
1003 vec![indoc! {"
1004 mod x «{»
1005 mod y {
1006
1007 }
1008 «}»
1009 let foo = 1;"}],
1010 );
1011
1012 assert(
1013 indoc! {"
1014 mod x {
1015 mod y {
1016
1017 }
1018 }
1019 let fˇoo = 1;"},
1020 vec![],
1021 );
1022
1023 // Regression test: avoid crash when querying at the end of the buffer.
1024 assert(
1025 indoc! {"
1026 mod x {
1027 mod y {
1028
1029 }
1030 }
1031 let foo = 1;ˇ"},
1032 vec![],
1033 );
1034}
1035
1036#[gpui::test]
1037fn test_enclosing_bracket_ranges_where_brackets_are_not_outermost_children(cx: &mut AppContext) {
1038 let mut assert = |selection_text, bracket_pair_texts| {
1039 assert_bracket_pairs(selection_text, bracket_pair_texts, javascript_lang(), cx)
1040 };
1041
1042 assert(
1043 indoc! {"
1044 for (const a in b)ˇ {
1045 // a comment that's longer than the for-loop header
1046 }"},
1047 vec![indoc! {"
1048 for «(»const a in b«)» {
1049 // a comment that's longer than the for-loop header
1050 }"}],
1051 );
1052
1053 // Regression test: even though the parent node of the parentheses (the for loop) does
1054 // intersect the given range, the parentheses themselves do not contain the range, so
1055 // they should not be returned. Only the curly braces contain the range.
1056 assert(
1057 indoc! {"
1058 for (const a in b) {ˇ
1059 // a comment that's longer than the for-loop header
1060 }"},
1061 vec![indoc! {"
1062 for (const a in b) «{»
1063 // a comment that's longer than the for-loop header
1064 «}»"}],
1065 );
1066}
1067
1068#[gpui::test]
1069fn test_range_for_syntax_ancestor(cx: &mut AppContext) {
1070 cx.new_model(|cx| {
1071 let text = "fn a() { b(|c| {}) }";
1072 let buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
1073 let snapshot = buffer.snapshot();
1074
1075 assert_eq!(
1076 snapshot.range_for_syntax_ancestor(empty_range_at(text, "|")),
1077 Some(range_of(text, "|"))
1078 );
1079 assert_eq!(
1080 snapshot.range_for_syntax_ancestor(range_of(text, "|")),
1081 Some(range_of(text, "|c|"))
1082 );
1083 assert_eq!(
1084 snapshot.range_for_syntax_ancestor(range_of(text, "|c|")),
1085 Some(range_of(text, "|c| {}"))
1086 );
1087 assert_eq!(
1088 snapshot.range_for_syntax_ancestor(range_of(text, "|c| {}")),
1089 Some(range_of(text, "(|c| {})"))
1090 );
1091
1092 buffer
1093 });
1094
1095 fn empty_range_at(text: &str, part: &str) -> Range<usize> {
1096 let start = text.find(part).unwrap();
1097 start..start
1098 }
1099
1100 fn range_of(text: &str, part: &str) -> Range<usize> {
1101 let start = text.find(part).unwrap();
1102 start..start + part.len()
1103 }
1104}
1105
1106#[gpui::test]
1107fn test_autoindent_with_soft_tabs(cx: &mut AppContext) {
1108 init_settings(cx, |_| {});
1109
1110 cx.new_model(|cx| {
1111 let text = "fn a() {}";
1112 let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
1113
1114 buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
1115 assert_eq!(buffer.text(), "fn a() {\n \n}");
1116
1117 buffer.edit(
1118 [(Point::new(1, 4)..Point::new(1, 4), "b()\n")],
1119 Some(AutoindentMode::EachLine),
1120 cx,
1121 );
1122 assert_eq!(buffer.text(), "fn a() {\n b()\n \n}");
1123
1124 // Create a field expression on a new line, causing that line
1125 // to be indented.
1126 buffer.edit(
1127 [(Point::new(2, 4)..Point::new(2, 4), ".c")],
1128 Some(AutoindentMode::EachLine),
1129 cx,
1130 );
1131 assert_eq!(buffer.text(), "fn a() {\n b()\n .c\n}");
1132
1133 // Remove the dot so that the line is no longer a field expression,
1134 // causing the line to be outdented.
1135 buffer.edit(
1136 [(Point::new(2, 8)..Point::new(2, 9), "")],
1137 Some(AutoindentMode::EachLine),
1138 cx,
1139 );
1140 assert_eq!(buffer.text(), "fn a() {\n b()\n c\n}");
1141
1142 buffer
1143 });
1144}
1145
1146#[gpui::test]
1147fn test_autoindent_with_hard_tabs(cx: &mut AppContext) {
1148 init_settings(cx, |settings| {
1149 settings.defaults.hard_tabs = Some(true);
1150 });
1151
1152 cx.new_model(|cx| {
1153 let text = "fn a() {}";
1154 let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
1155
1156 buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
1157 assert_eq!(buffer.text(), "fn a() {\n\t\n}");
1158
1159 buffer.edit(
1160 [(Point::new(1, 1)..Point::new(1, 1), "b()\n")],
1161 Some(AutoindentMode::EachLine),
1162 cx,
1163 );
1164 assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\n}");
1165
1166 // Create a field expression on a new line, causing that line
1167 // to be indented.
1168 buffer.edit(
1169 [(Point::new(2, 1)..Point::new(2, 1), ".c")],
1170 Some(AutoindentMode::EachLine),
1171 cx,
1172 );
1173 assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\t.c\n}");
1174
1175 // Remove the dot so that the line is no longer a field expression,
1176 // causing the line to be outdented.
1177 buffer.edit(
1178 [(Point::new(2, 2)..Point::new(2, 3), "")],
1179 Some(AutoindentMode::EachLine),
1180 cx,
1181 );
1182 assert_eq!(buffer.text(), "fn a() {\n\tb()\n\tc\n}");
1183
1184 buffer
1185 });
1186}
1187
1188#[gpui::test]
1189fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut AppContext) {
1190 init_settings(cx, |_| {});
1191
1192 cx.new_model(|cx| {
1193 let mut buffer = Buffer::local(
1194 "
1195 fn a() {
1196 c;
1197 d;
1198 }
1199 "
1200 .unindent(),
1201 cx,
1202 )
1203 .with_language(Arc::new(rust_lang()), cx);
1204
1205 // Lines 2 and 3 don't match the indentation suggestion. When editing these lines,
1206 // their indentation is not adjusted.
1207 buffer.edit_via_marked_text(
1208 &"
1209 fn a() {
1210 c«()»;
1211 d«()»;
1212 }
1213 "
1214 .unindent(),
1215 Some(AutoindentMode::EachLine),
1216 cx,
1217 );
1218 assert_eq!(
1219 buffer.text(),
1220 "
1221 fn a() {
1222 c();
1223 d();
1224 }
1225 "
1226 .unindent()
1227 );
1228
1229 // When appending new content after these lines, the indentation is based on the
1230 // preceding lines' actual indentation.
1231 buffer.edit_via_marked_text(
1232 &"
1233 fn a() {
1234 c«
1235 .f
1236 .g()»;
1237 d«
1238 .f
1239 .g()»;
1240 }
1241 "
1242 .unindent(),
1243 Some(AutoindentMode::EachLine),
1244 cx,
1245 );
1246
1247 assert_eq!(
1248 buffer.text(),
1249 "
1250 fn a() {
1251 c
1252 .f
1253 .g();
1254 d
1255 .f
1256 .g();
1257 }
1258 "
1259 .unindent()
1260 );
1261 buffer
1262 });
1263
1264 cx.new_model(|cx| {
1265 eprintln!("second buffer: {:?}", cx.entity_id());
1266
1267 let mut buffer = Buffer::local(
1268 "
1269 fn a() {
1270 b();
1271 |
1272 "
1273 .replace('|', "") // marker to preserve trailing whitespace
1274 .unindent(),
1275 cx,
1276 )
1277 .with_language(Arc::new(rust_lang()), cx);
1278
1279 // Insert a closing brace. It is outdented.
1280 buffer.edit_via_marked_text(
1281 &"
1282 fn a() {
1283 b();
1284 «}»
1285 "
1286 .unindent(),
1287 Some(AutoindentMode::EachLine),
1288 cx,
1289 );
1290 assert_eq!(
1291 buffer.text(),
1292 "
1293 fn a() {
1294 b();
1295 }
1296 "
1297 .unindent()
1298 );
1299
1300 // Manually edit the leading whitespace. The edit is preserved.
1301 buffer.edit_via_marked_text(
1302 &"
1303 fn a() {
1304 b();
1305 « »}
1306 "
1307 .unindent(),
1308 Some(AutoindentMode::EachLine),
1309 cx,
1310 );
1311 assert_eq!(
1312 buffer.text(),
1313 "
1314 fn a() {
1315 b();
1316 }
1317 "
1318 .unindent()
1319 );
1320 buffer
1321 });
1322
1323 eprintln!("DONE");
1324}
1325
1326#[gpui::test]
1327fn test_autoindent_does_not_adjust_lines_within_newly_created_errors(cx: &mut AppContext) {
1328 init_settings(cx, |_| {});
1329
1330 cx.new_model(|cx| {
1331 let mut buffer = Buffer::local(
1332 "
1333 fn a() {
1334 i
1335 }
1336 "
1337 .unindent(),
1338 cx,
1339 )
1340 .with_language(Arc::new(rust_lang()), cx);
1341
1342 // Regression test: line does not get outdented due to syntax error
1343 buffer.edit_via_marked_text(
1344 &"
1345 fn a() {
1346 i«f let Some(x) = y»
1347 }
1348 "
1349 .unindent(),
1350 Some(AutoindentMode::EachLine),
1351 cx,
1352 );
1353 assert_eq!(
1354 buffer.text(),
1355 "
1356 fn a() {
1357 if let Some(x) = y
1358 }
1359 "
1360 .unindent()
1361 );
1362
1363 buffer.edit_via_marked_text(
1364 &"
1365 fn a() {
1366 if let Some(x) = y« {»
1367 }
1368 "
1369 .unindent(),
1370 Some(AutoindentMode::EachLine),
1371 cx,
1372 );
1373 assert_eq!(
1374 buffer.text(),
1375 "
1376 fn a() {
1377 if let Some(x) = y {
1378 }
1379 "
1380 .unindent()
1381 );
1382
1383 buffer
1384 });
1385}
1386
1387#[gpui::test]
1388fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut AppContext) {
1389 init_settings(cx, |_| {});
1390
1391 cx.new_model(|cx| {
1392 let mut buffer = Buffer::local(
1393 "
1394 fn a() {}
1395 "
1396 .unindent(),
1397 cx,
1398 )
1399 .with_language(Arc::new(rust_lang()), cx);
1400
1401 buffer.edit_via_marked_text(
1402 &"
1403 fn a(«
1404 b») {}
1405 "
1406 .unindent(),
1407 Some(AutoindentMode::EachLine),
1408 cx,
1409 );
1410 assert_eq!(
1411 buffer.text(),
1412 "
1413 fn a(
1414 b) {}
1415 "
1416 .unindent()
1417 );
1418
1419 // The indentation suggestion changed because `@end` node (a close paren)
1420 // is now at the beginning of the line.
1421 buffer.edit_via_marked_text(
1422 &"
1423 fn a(
1424 ˇ) {}
1425 "
1426 .unindent(),
1427 Some(AutoindentMode::EachLine),
1428 cx,
1429 );
1430 assert_eq!(
1431 buffer.text(),
1432 "
1433 fn a(
1434 ) {}
1435 "
1436 .unindent()
1437 );
1438
1439 buffer
1440 });
1441}
1442
1443#[gpui::test]
1444fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut AppContext) {
1445 init_settings(cx, |_| {});
1446
1447 cx.new_model(|cx| {
1448 let text = "a\nb";
1449 let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
1450 buffer.edit(
1451 [(0..1, "\n"), (2..3, "\n")],
1452 Some(AutoindentMode::EachLine),
1453 cx,
1454 );
1455 assert_eq!(buffer.text(), "\n\n\n");
1456 buffer
1457 });
1458}
1459
1460#[gpui::test]
1461fn test_autoindent_multi_line_insertion(cx: &mut AppContext) {
1462 init_settings(cx, |_| {});
1463
1464 cx.new_model(|cx| {
1465 let text = "
1466 const a: usize = 1;
1467 fn b() {
1468 if c {
1469 let d = 2;
1470 }
1471 }
1472 "
1473 .unindent();
1474
1475 let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
1476 buffer.edit(
1477 [(Point::new(3, 0)..Point::new(3, 0), "e(\n f()\n);\n")],
1478 Some(AutoindentMode::EachLine),
1479 cx,
1480 );
1481 assert_eq!(
1482 buffer.text(),
1483 "
1484 const a: usize = 1;
1485 fn b() {
1486 if c {
1487 e(
1488 f()
1489 );
1490 let d = 2;
1491 }
1492 }
1493 "
1494 .unindent()
1495 );
1496
1497 buffer
1498 });
1499}
1500
1501#[gpui::test]
1502fn test_autoindent_block_mode(cx: &mut AppContext) {
1503 init_settings(cx, |_| {});
1504
1505 cx.new_model(|cx| {
1506 let text = r#"
1507 fn a() {
1508 b();
1509 }
1510 "#
1511 .unindent();
1512 let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
1513
1514 // When this text was copied, both of the quotation marks were at the same
1515 // indent level, but the indentation of the first line was not included in
1516 // the copied text. This information is retained in the
1517 // 'original_indent_columns' vector.
1518 let original_indent_columns = vec![4];
1519 let inserted_text = r#"
1520 "
1521 c
1522 d
1523 e
1524 "
1525 "#
1526 .unindent();
1527
1528 // Insert the block at column zero. The entire block is indented
1529 // so that the first line matches the previous line's indentation.
1530 buffer.edit(
1531 [(Point::new(2, 0)..Point::new(2, 0), inserted_text.clone())],
1532 Some(AutoindentMode::Block {
1533 original_indent_columns: original_indent_columns.clone(),
1534 }),
1535 cx,
1536 );
1537 assert_eq!(
1538 buffer.text(),
1539 r#"
1540 fn a() {
1541 b();
1542 "
1543 c
1544 d
1545 e
1546 "
1547 }
1548 "#
1549 .unindent()
1550 );
1551
1552 // Grouping is disabled in tests, so we need 2 undos
1553 buffer.undo(cx); // Undo the auto-indent
1554 buffer.undo(cx); // Undo the original edit
1555
1556 // Insert the block at a deeper indent level. The entire block is outdented.
1557 buffer.edit([(Point::new(2, 0)..Point::new(2, 0), " ")], None, cx);
1558 buffer.edit(
1559 [(Point::new(2, 8)..Point::new(2, 8), inserted_text)],
1560 Some(AutoindentMode::Block {
1561 original_indent_columns: original_indent_columns.clone(),
1562 }),
1563 cx,
1564 );
1565 assert_eq!(
1566 buffer.text(),
1567 r#"
1568 fn a() {
1569 b();
1570 "
1571 c
1572 d
1573 e
1574 "
1575 }
1576 "#
1577 .unindent()
1578 );
1579
1580 buffer
1581 });
1582}
1583
1584#[gpui::test]
1585fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut AppContext) {
1586 init_settings(cx, |_| {});
1587
1588 cx.new_model(|cx| {
1589 let text = r#"
1590 fn a() {
1591 if b() {
1592
1593 }
1594 }
1595 "#
1596 .unindent();
1597 let mut buffer = Buffer::local(text, cx).with_language(Arc::new(rust_lang()), cx);
1598
1599 // The original indent columns are not known, so this text is
1600 // auto-indented in a block as if the first line was copied in
1601 // its entirety.
1602 let original_indent_columns = Vec::new();
1603 let inserted_text = " c\n .d()\n .e();";
1604
1605 // Insert the block at column zero. The entire block is indented
1606 // so that the first line matches the previous line's indentation.
1607 buffer.edit(
1608 [(Point::new(2, 0)..Point::new(2, 0), inserted_text)],
1609 Some(AutoindentMode::Block {
1610 original_indent_columns: original_indent_columns.clone(),
1611 }),
1612 cx,
1613 );
1614 assert_eq!(
1615 buffer.text(),
1616 r#"
1617 fn a() {
1618 if b() {
1619 c
1620 .d()
1621 .e();
1622 }
1623 }
1624 "#
1625 .unindent()
1626 );
1627
1628 // Grouping is disabled in tests, so we need 2 undos
1629 buffer.undo(cx); // Undo the auto-indent
1630 buffer.undo(cx); // Undo the original edit
1631
1632 // Insert the block at a deeper indent level. The entire block is outdented.
1633 buffer.edit(
1634 [(Point::new(2, 0)..Point::new(2, 0), " ".repeat(12))],
1635 None,
1636 cx,
1637 );
1638 buffer.edit(
1639 [(Point::new(2, 12)..Point::new(2, 12), inserted_text)],
1640 Some(AutoindentMode::Block {
1641 original_indent_columns: Vec::new(),
1642 }),
1643 cx,
1644 );
1645 assert_eq!(
1646 buffer.text(),
1647 r#"
1648 fn a() {
1649 if b() {
1650 c
1651 .d()
1652 .e();
1653 }
1654 }
1655 "#
1656 .unindent()
1657 );
1658
1659 buffer
1660 });
1661}
1662
1663#[gpui::test]
1664fn test_autoindent_language_without_indents_query(cx: &mut AppContext) {
1665 init_settings(cx, |_| {});
1666
1667 cx.new_model(|cx| {
1668 let text = "
1669 * one
1670 - a
1671 - b
1672 * two
1673 "
1674 .unindent();
1675
1676 let mut buffer = Buffer::local(text, cx).with_language(
1677 Arc::new(Language::new(
1678 LanguageConfig {
1679 name: "Markdown".into(),
1680 auto_indent_using_last_non_empty_line: false,
1681 ..Default::default()
1682 },
1683 Some(tree_sitter_json::language()),
1684 )),
1685 cx,
1686 );
1687 buffer.edit(
1688 [(Point::new(3, 0)..Point::new(3, 0), "\n")],
1689 Some(AutoindentMode::EachLine),
1690 cx,
1691 );
1692 assert_eq!(
1693 buffer.text(),
1694 "
1695 * one
1696 - a
1697 - b
1698
1699 * two
1700 "
1701 .unindent()
1702 );
1703 buffer
1704 });
1705}
1706
1707#[gpui::test]
1708fn test_autoindent_with_injected_languages(cx: &mut AppContext) {
1709 init_settings(cx, |settings| {
1710 settings.languages.extend([
1711 (
1712 "HTML".into(),
1713 LanguageSettingsContent {
1714 tab_size: Some(2.try_into().unwrap()),
1715 ..Default::default()
1716 },
1717 ),
1718 (
1719 "JavaScript".into(),
1720 LanguageSettingsContent {
1721 tab_size: Some(8.try_into().unwrap()),
1722 ..Default::default()
1723 },
1724 ),
1725 ])
1726 });
1727
1728 let html_language = Arc::new(html_lang());
1729
1730 let javascript_language = Arc::new(javascript_lang());
1731
1732 let language_registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
1733 language_registry.add(html_language.clone());
1734 language_registry.add(javascript_language.clone());
1735
1736 cx.new_model(|cx| {
1737 let (text, ranges) = marked_text_ranges(
1738 &"
1739 <div>ˇ
1740 </div>
1741 <script>
1742 init({ˇ
1743 })
1744 </script>
1745 <span>ˇ
1746 </span>
1747 "
1748 .unindent(),
1749 false,
1750 );
1751
1752 let mut buffer = Buffer::local(text, cx);
1753 buffer.set_language_registry(language_registry);
1754 buffer.set_language(Some(html_language), cx);
1755 buffer.edit(
1756 ranges.into_iter().map(|range| (range, "\na")),
1757 Some(AutoindentMode::EachLine),
1758 cx,
1759 );
1760 assert_eq!(
1761 buffer.text(),
1762 "
1763 <div>
1764 a
1765 </div>
1766 <script>
1767 init({
1768 a
1769 })
1770 </script>
1771 <span>
1772 a
1773 </span>
1774 "
1775 .unindent()
1776 );
1777 buffer
1778 });
1779}
1780
1781#[gpui::test]
1782fn test_autoindent_query_with_outdent_captures(cx: &mut AppContext) {
1783 init_settings(cx, |settings| {
1784 settings.defaults.tab_size = Some(2.try_into().unwrap());
1785 });
1786
1787 cx.new_model(|cx| {
1788 let mut buffer = Buffer::local("", cx).with_language(Arc::new(ruby_lang()), cx);
1789
1790 let text = r#"
1791 class C
1792 def a(b, c)
1793 puts b
1794 puts c
1795 rescue
1796 puts "errored"
1797 exit 1
1798 end
1799 end
1800 "#
1801 .unindent();
1802
1803 buffer.edit([(0..0, text)], Some(AutoindentMode::EachLine), cx);
1804
1805 assert_eq!(
1806 buffer.text(),
1807 r#"
1808 class C
1809 def a(b, c)
1810 puts b
1811 puts c
1812 rescue
1813 puts "errored"
1814 exit 1
1815 end
1816 end
1817 "#
1818 .unindent()
1819 );
1820
1821 buffer
1822 });
1823}
1824
1825#[gpui::test]
1826fn test_language_scope_at_with_javascript(cx: &mut AppContext) {
1827 init_settings(cx, |_| {});
1828
1829 cx.new_model(|cx| {
1830 let language = Language::new(
1831 LanguageConfig {
1832 name: "JavaScript".into(),
1833 line_comments: vec!["// ".into()],
1834 brackets: BracketPairConfig {
1835 pairs: vec![
1836 BracketPair {
1837 start: "{".into(),
1838 end: "}".into(),
1839 close: true,
1840 surround: true,
1841 newline: false,
1842 },
1843 BracketPair {
1844 start: "'".into(),
1845 end: "'".into(),
1846 close: true,
1847 surround: true,
1848 newline: false,
1849 },
1850 ],
1851 disabled_scopes_by_bracket_ix: vec![
1852 Vec::new(), //
1853 vec!["string".into()],
1854 ],
1855 },
1856 overrides: [(
1857 "element".into(),
1858 LanguageConfigOverride {
1859 line_comments: Override::Remove { remove: true },
1860 block_comment: Override::Set(("{/*".into(), "*/}".into())),
1861 ..Default::default()
1862 },
1863 )]
1864 .into_iter()
1865 .collect(),
1866 ..Default::default()
1867 },
1868 Some(tree_sitter_typescript::language_tsx()),
1869 )
1870 .with_override_query(
1871 r#"
1872 (jsx_element) @element
1873 (string) @string
1874 [
1875 (jsx_opening_element)
1876 (jsx_closing_element)
1877 (jsx_expression)
1878 ] @default
1879 "#,
1880 )
1881 .unwrap();
1882
1883 let text = r#"
1884 a["b"] = <C d="e">
1885 <F></F>
1886 { g() }
1887 </C>;
1888 "#
1889 .unindent();
1890
1891 let buffer = Buffer::local(&text, cx).with_language(Arc::new(language), cx);
1892 let snapshot = buffer.snapshot();
1893
1894 let config = snapshot.language_scope_at(0).unwrap();
1895 assert_eq!(config.line_comment_prefixes(), &[Arc::from("// ")]);
1896 // Both bracket pairs are enabled
1897 assert_eq!(
1898 config.brackets().map(|e| e.1).collect::<Vec<_>>(),
1899 &[true, true]
1900 );
1901
1902 let string_config = snapshot
1903 .language_scope_at(text.find("b\"").unwrap())
1904 .unwrap();
1905 assert_eq!(string_config.line_comment_prefixes(), &[Arc::from("// ")]);
1906 // Second bracket pair is disabled
1907 assert_eq!(
1908 string_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
1909 &[true, false]
1910 );
1911
1912 // In between JSX tags: use the `element` override.
1913 let element_config = snapshot
1914 .language_scope_at(text.find("<F>").unwrap())
1915 .unwrap();
1916 // TODO nested blocks after newlines are captured with all whitespaces
1917 // https://github.com/tree-sitter/tree-sitter-typescript/issues/306
1918 // assert_eq!(element_config.line_comment_prefixes(), &[]);
1919 // assert_eq!(
1920 // element_config.block_comment_delimiters(),
1921 // Some((&"{/*".into(), &"*/}".into()))
1922 // );
1923 assert_eq!(
1924 element_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
1925 &[true, true]
1926 );
1927
1928 // Within a JSX tag: use the default config.
1929 let tag_config = snapshot
1930 .language_scope_at(text.find(" d=").unwrap() + 1)
1931 .unwrap();
1932 assert_eq!(tag_config.line_comment_prefixes(), &[Arc::from("// ")]);
1933 assert_eq!(
1934 tag_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
1935 &[true, true]
1936 );
1937
1938 // In a JSX expression: use the default config.
1939 let expression_in_element_config = snapshot
1940 .language_scope_at(text.find('{').unwrap() + 1)
1941 .unwrap();
1942 assert_eq!(
1943 expression_in_element_config.line_comment_prefixes(),
1944 &[Arc::from("// ")]
1945 );
1946 assert_eq!(
1947 expression_in_element_config
1948 .brackets()
1949 .map(|e| e.1)
1950 .collect::<Vec<_>>(),
1951 &[true, true]
1952 );
1953
1954 buffer
1955 });
1956}
1957
1958#[gpui::test]
1959fn test_language_scope_at_with_rust(cx: &mut AppContext) {
1960 init_settings(cx, |_| {});
1961
1962 cx.new_model(|cx| {
1963 let language = Language::new(
1964 LanguageConfig {
1965 name: "Rust".into(),
1966 brackets: BracketPairConfig {
1967 pairs: vec![
1968 BracketPair {
1969 start: "{".into(),
1970 end: "}".into(),
1971 close: true,
1972 surround: true,
1973 newline: false,
1974 },
1975 BracketPair {
1976 start: "'".into(),
1977 end: "'".into(),
1978 close: true,
1979 surround: true,
1980 newline: false,
1981 },
1982 ],
1983 disabled_scopes_by_bracket_ix: vec![
1984 Vec::new(), //
1985 vec!["string".into()],
1986 ],
1987 },
1988 ..Default::default()
1989 },
1990 Some(tree_sitter_rust::language()),
1991 )
1992 .with_override_query(
1993 r#"
1994 (string_literal) @string
1995 "#,
1996 )
1997 .unwrap();
1998
1999 let text = r#"
2000 const S: &'static str = "hello";
2001 "#
2002 .unindent();
2003
2004 let buffer = Buffer::local(text.clone(), cx).with_language(Arc::new(language), cx);
2005 let snapshot = buffer.snapshot();
2006
2007 // By default, all brackets are enabled
2008 let config = snapshot.language_scope_at(0).unwrap();
2009 assert_eq!(
2010 config.brackets().map(|e| e.1).collect::<Vec<_>>(),
2011 &[true, true]
2012 );
2013
2014 // Within a string, the quotation brackets are disabled.
2015 let string_config = snapshot
2016 .language_scope_at(text.find("ello").unwrap())
2017 .unwrap();
2018 assert_eq!(
2019 string_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
2020 &[true, false]
2021 );
2022
2023 buffer
2024 });
2025}
2026
2027#[gpui::test]
2028fn test_language_scope_at_with_combined_injections(cx: &mut AppContext) {
2029 init_settings(cx, |_| {});
2030
2031 cx.new_model(|cx| {
2032 let text = r#"
2033 <ol>
2034 <% people.each do |person| %>
2035 <li>
2036 <%= person.name %>
2037 </li>
2038 <% end %>
2039 </ol>
2040 "#
2041 .unindent();
2042
2043 let language_registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
2044 language_registry.add(Arc::new(ruby_lang()));
2045 language_registry.add(Arc::new(html_lang()));
2046 language_registry.add(Arc::new(erb_lang()));
2047
2048 let mut buffer = Buffer::local(text, cx);
2049 buffer.set_language_registry(language_registry.clone());
2050 buffer.set_language(
2051 language_registry
2052 .language_for_name("ERB")
2053 .now_or_never()
2054 .unwrap()
2055 .ok(),
2056 cx,
2057 );
2058
2059 let snapshot = buffer.snapshot();
2060 let html_config = snapshot.language_scope_at(Point::new(2, 4)).unwrap();
2061 assert_eq!(html_config.line_comment_prefixes(), &[]);
2062 assert_eq!(
2063 html_config.block_comment_delimiters(),
2064 Some((&"<!--".into(), &"-->".into()))
2065 );
2066
2067 let ruby_config = snapshot.language_scope_at(Point::new(3, 12)).unwrap();
2068 assert_eq!(ruby_config.line_comment_prefixes(), &[Arc::from("# ")]);
2069 assert_eq!(ruby_config.block_comment_delimiters(), None);
2070
2071 buffer
2072 });
2073}
2074
2075#[gpui::test]
2076fn test_serialization(cx: &mut gpui::AppContext) {
2077 let mut now = Instant::now();
2078
2079 let buffer1 = cx.new_model(|cx| {
2080 let mut buffer = Buffer::local("abc", cx);
2081 buffer.edit([(3..3, "D")], None, cx);
2082
2083 now += Duration::from_secs(1);
2084 buffer.start_transaction_at(now);
2085 buffer.edit([(4..4, "E")], None, cx);
2086 buffer.end_transaction_at(now, cx);
2087 assert_eq!(buffer.text(), "abcDE");
2088
2089 buffer.undo(cx);
2090 assert_eq!(buffer.text(), "abcD");
2091
2092 buffer.edit([(4..4, "F")], None, cx);
2093 assert_eq!(buffer.text(), "abcDF");
2094 buffer
2095 });
2096 assert_eq!(buffer1.read(cx).text(), "abcDF");
2097
2098 let state = buffer1.read(cx).to_proto(cx);
2099 let ops = cx
2100 .background_executor()
2101 .block(buffer1.read(cx).serialize_ops(None, cx));
2102 let buffer2 = cx.new_model(|cx| {
2103 let mut buffer = Buffer::from_proto(1, Capability::ReadWrite, state, None).unwrap();
2104 buffer
2105 .apply_ops(
2106 ops.into_iter()
2107 .map(|op| proto::deserialize_operation(op).unwrap()),
2108 cx,
2109 )
2110 .unwrap();
2111 buffer
2112 });
2113 assert_eq!(buffer2.read(cx).text(), "abcDF");
2114}
2115
2116#[gpui::test]
2117async fn test_find_matching_indent(cx: &mut TestAppContext) {
2118 cx.update(|cx| init_settings(cx, |_| {}));
2119
2120 async fn enclosing_indent(
2121 text: impl Into<String>,
2122 buffer_row: u32,
2123 cx: &mut TestAppContext,
2124 ) -> Option<(Range<u32>, LineIndent)> {
2125 let buffer = cx.new_model(|cx| Buffer::local(text, cx));
2126 let snapshot = cx.read(|cx| buffer.read(cx).snapshot());
2127 snapshot.enclosing_indent(buffer_row).await
2128 }
2129
2130 assert_eq!(
2131 enclosing_indent(
2132 "
2133 fn b() {
2134 if c {
2135 let d = 2;
2136 }
2137 }"
2138 .unindent(),
2139 1,
2140 cx,
2141 )
2142 .await,
2143 Some((
2144 1..2,
2145 LineIndent {
2146 tabs: 0,
2147 spaces: 4,
2148 line_blank: false,
2149 }
2150 ))
2151 );
2152
2153 assert_eq!(
2154 enclosing_indent(
2155 "
2156 fn b() {
2157 if c {
2158 let d = 2;
2159 }
2160 }"
2161 .unindent(),
2162 2,
2163 cx,
2164 )
2165 .await,
2166 Some((
2167 1..2,
2168 LineIndent {
2169 tabs: 0,
2170 spaces: 4,
2171 line_blank: false,
2172 }
2173 ))
2174 );
2175
2176 assert_eq!(
2177 enclosing_indent(
2178 "
2179 fn b() {
2180 if c {
2181 let d = 2;
2182
2183 let e = 5;
2184 }
2185 }"
2186 .unindent(),
2187 3,
2188 cx,
2189 )
2190 .await,
2191 Some((
2192 1..4,
2193 LineIndent {
2194 tabs: 0,
2195 spaces: 4,
2196 line_blank: false,
2197 }
2198 ))
2199 );
2200}
2201
2202#[gpui::test(iterations = 100)]
2203fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
2204 let min_peers = env::var("MIN_PEERS")
2205 .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
2206 .unwrap_or(1);
2207 let max_peers = env::var("MAX_PEERS")
2208 .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
2209 .unwrap_or(5);
2210 let operations = env::var("OPERATIONS")
2211 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
2212 .unwrap_or(10);
2213
2214 let base_text_len = rng.gen_range(0..10);
2215 let base_text = RandomCharIter::new(&mut rng)
2216 .take(base_text_len)
2217 .collect::<String>();
2218 let mut replica_ids = Vec::new();
2219 let mut buffers = Vec::new();
2220 let network = Arc::new(Mutex::new(Network::new(rng.clone())));
2221 let base_buffer = cx.new_model(|cx| Buffer::local(base_text.as_str(), cx));
2222
2223 for i in 0..rng.gen_range(min_peers..=max_peers) {
2224 let buffer = cx.new_model(|cx| {
2225 let state = base_buffer.read(cx).to_proto(cx);
2226 let ops = cx
2227 .background_executor()
2228 .block(base_buffer.read(cx).serialize_ops(None, cx));
2229 let mut buffer =
2230 Buffer::from_proto(i as ReplicaId, Capability::ReadWrite, state, None).unwrap();
2231 buffer
2232 .apply_ops(
2233 ops.into_iter()
2234 .map(|op| proto::deserialize_operation(op).unwrap()),
2235 cx,
2236 )
2237 .unwrap();
2238 buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
2239 let network = network.clone();
2240 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
2241 if let Event::Operation(op) = event {
2242 network
2243 .lock()
2244 .broadcast(buffer.replica_id(), vec![proto::serialize_operation(op)]);
2245 }
2246 })
2247 .detach();
2248 buffer
2249 });
2250
2251 buffers.push(buffer);
2252 replica_ids.push(i as ReplicaId);
2253 network.lock().add_peer(i as ReplicaId);
2254 log::info!("Adding initial peer with replica id {}", i);
2255 }
2256
2257 log::info!("initial text: {:?}", base_text);
2258
2259 let mut now = Instant::now();
2260 let mut mutation_count = operations;
2261 let mut next_diagnostic_id = 0;
2262 let mut active_selections = BTreeMap::default();
2263 loop {
2264 let replica_index = rng.gen_range(0..replica_ids.len());
2265 let replica_id = replica_ids[replica_index];
2266 let buffer = &mut buffers[replica_index];
2267 let mut new_buffer = None;
2268 match rng.gen_range(0..100) {
2269 0..=29 if mutation_count != 0 => {
2270 buffer.update(cx, |buffer, cx| {
2271 buffer.start_transaction_at(now);
2272 buffer.randomly_edit(&mut rng, 5, cx);
2273 buffer.end_transaction_at(now, cx);
2274 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
2275 });
2276 mutation_count -= 1;
2277 }
2278 30..=39 if mutation_count != 0 => {
2279 buffer.update(cx, |buffer, cx| {
2280 if rng.gen_bool(0.2) {
2281 log::info!("peer {} clearing active selections", replica_id);
2282 active_selections.remove(&replica_id);
2283 buffer.remove_active_selections(cx);
2284 } else {
2285 let mut selections = Vec::new();
2286 for id in 0..rng.gen_range(1..=5) {
2287 let range = buffer.random_byte_range(0, &mut rng);
2288 selections.push(Selection {
2289 id,
2290 start: buffer.anchor_before(range.start),
2291 end: buffer.anchor_before(range.end),
2292 reversed: false,
2293 goal: SelectionGoal::None,
2294 });
2295 }
2296 let selections: Arc<[Selection<Anchor>]> = selections.into();
2297 log::info!(
2298 "peer {} setting active selections: {:?}",
2299 replica_id,
2300 selections
2301 );
2302 active_selections.insert(replica_id, selections.clone());
2303 buffer.set_active_selections(selections, false, Default::default(), cx);
2304 }
2305 });
2306 mutation_count -= 1;
2307 }
2308 40..=49 if mutation_count != 0 && replica_id == 0 => {
2309 let entry_count = rng.gen_range(1..=5);
2310 buffer.update(cx, |buffer, cx| {
2311 let diagnostics = DiagnosticSet::new(
2312 (0..entry_count).map(|_| {
2313 let range = buffer.random_byte_range(0, &mut rng);
2314 let range = range.to_point_utf16(buffer);
2315 let range = range.start..range.end;
2316 DiagnosticEntry {
2317 range,
2318 diagnostic: Diagnostic {
2319 message: post_inc(&mut next_diagnostic_id).to_string(),
2320 ..Default::default()
2321 },
2322 }
2323 }),
2324 buffer,
2325 );
2326 log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
2327 buffer.update_diagnostics(LanguageServerId(0), diagnostics, cx);
2328 });
2329 mutation_count -= 1;
2330 }
2331 50..=59 if replica_ids.len() < max_peers => {
2332 let old_buffer_state = buffer.read(cx).to_proto(cx);
2333 let old_buffer_ops = cx
2334 .background_executor()
2335 .block(buffer.read(cx).serialize_ops(None, cx));
2336 let new_replica_id = (0..=replica_ids.len() as ReplicaId)
2337 .filter(|replica_id| *replica_id != buffer.read(cx).replica_id())
2338 .choose(&mut rng)
2339 .unwrap();
2340 log::info!(
2341 "Adding new replica {} (replicating from {})",
2342 new_replica_id,
2343 replica_id
2344 );
2345 new_buffer = Some(cx.new_model(|cx| {
2346 let mut new_buffer = Buffer::from_proto(
2347 new_replica_id,
2348 Capability::ReadWrite,
2349 old_buffer_state,
2350 None,
2351 )
2352 .unwrap();
2353 new_buffer
2354 .apply_ops(
2355 old_buffer_ops
2356 .into_iter()
2357 .map(|op| deserialize_operation(op).unwrap()),
2358 cx,
2359 )
2360 .unwrap();
2361 log::info!(
2362 "New replica {} text: {:?}",
2363 new_buffer.replica_id(),
2364 new_buffer.text()
2365 );
2366 new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
2367 let network = network.clone();
2368 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
2369 if let Event::Operation(op) = event {
2370 network.lock().broadcast(
2371 buffer.replica_id(),
2372 vec![proto::serialize_operation(op)],
2373 );
2374 }
2375 })
2376 .detach();
2377 new_buffer
2378 }));
2379 network.lock().replicate(replica_id, new_replica_id);
2380
2381 if new_replica_id as usize == replica_ids.len() {
2382 replica_ids.push(new_replica_id);
2383 } else {
2384 let new_buffer = new_buffer.take().unwrap();
2385 while network.lock().has_unreceived(new_replica_id) {
2386 let ops = network
2387 .lock()
2388 .receive(new_replica_id)
2389 .into_iter()
2390 .map(|op| proto::deserialize_operation(op).unwrap());
2391 if ops.len() > 0 {
2392 log::info!(
2393 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
2394 new_replica_id,
2395 buffer.read(cx).version(),
2396 ops.len(),
2397 ops
2398 );
2399 new_buffer.update(cx, |new_buffer, cx| {
2400 new_buffer.apply_ops(ops, cx).unwrap();
2401 });
2402 }
2403 }
2404 buffers[new_replica_id as usize] = new_buffer;
2405 }
2406 }
2407 60..=69 if mutation_count != 0 => {
2408 buffer.update(cx, |buffer, cx| {
2409 buffer.randomly_undo_redo(&mut rng, cx);
2410 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
2411 });
2412 mutation_count -= 1;
2413 }
2414 _ if network.lock().has_unreceived(replica_id) => {
2415 let ops = network
2416 .lock()
2417 .receive(replica_id)
2418 .into_iter()
2419 .map(|op| proto::deserialize_operation(op).unwrap());
2420 if ops.len() > 0 {
2421 log::info!(
2422 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
2423 replica_id,
2424 buffer.read(cx).version(),
2425 ops.len(),
2426 ops
2427 );
2428 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
2429 }
2430 }
2431 _ => {}
2432 }
2433
2434 now += Duration::from_millis(rng.gen_range(0..=200));
2435 buffers.extend(new_buffer);
2436
2437 for buffer in &buffers {
2438 buffer.read(cx).check_invariants();
2439 }
2440
2441 if mutation_count == 0 && network.lock().is_idle() {
2442 break;
2443 }
2444 }
2445
2446 let first_buffer = buffers[0].read(cx).snapshot();
2447 for buffer in &buffers[1..] {
2448 let buffer = buffer.read(cx).snapshot();
2449 assert_eq!(
2450 buffer.version(),
2451 first_buffer.version(),
2452 "Replica {} version != Replica 0 version",
2453 buffer.replica_id()
2454 );
2455 assert_eq!(
2456 buffer.text(),
2457 first_buffer.text(),
2458 "Replica {} text != Replica 0 text",
2459 buffer.replica_id()
2460 );
2461 assert_eq!(
2462 buffer
2463 .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
2464 .collect::<Vec<_>>(),
2465 first_buffer
2466 .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
2467 .collect::<Vec<_>>(),
2468 "Replica {} diagnostics != Replica 0 diagnostics",
2469 buffer.replica_id()
2470 );
2471 }
2472
2473 for buffer in &buffers {
2474 let buffer = buffer.read(cx).snapshot();
2475 let actual_remote_selections = buffer
2476 .selections_in_range(Anchor::MIN..Anchor::MAX, false)
2477 .map(|(replica_id, _, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
2478 .collect::<Vec<_>>();
2479 let expected_remote_selections = active_selections
2480 .iter()
2481 .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
2482 .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
2483 .collect::<Vec<_>>();
2484 assert_eq!(
2485 actual_remote_selections,
2486 expected_remote_selections,
2487 "Replica {} remote selections != expected selections",
2488 buffer.replica_id()
2489 );
2490 }
2491}
2492
2493#[test]
2494fn test_contiguous_ranges() {
2495 assert_eq!(
2496 contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
2497 &[1..4, 5..7, 9..13]
2498 );
2499
2500 // Respects the `max_len` parameter
2501 assert_eq!(
2502 contiguous_ranges(
2503 [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
2504 3
2505 )
2506 .collect::<Vec<_>>(),
2507 &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
2508 );
2509}
2510
2511#[gpui::test(iterations = 500)]
2512fn test_trailing_whitespace_ranges(mut rng: StdRng) {
2513 // Generate a random multi-line string containing
2514 // some lines with trailing whitespace.
2515 let mut text = String::new();
2516 for _ in 0..rng.gen_range(0..16) {
2517 for _ in 0..rng.gen_range(0..36) {
2518 text.push(match rng.gen_range(0..10) {
2519 0..=1 => ' ',
2520 3 => '\t',
2521 _ => rng.gen_range('a'..='z'),
2522 });
2523 }
2524 text.push('\n');
2525 }
2526
2527 match rng.gen_range(0..10) {
2528 // sometimes remove the last newline
2529 0..=1 => drop(text.pop()), //
2530
2531 // sometimes add extra newlines
2532 2..=3 => text.push_str(&"\n".repeat(rng.gen_range(1..5))),
2533 _ => {}
2534 }
2535
2536 let rope = Rope::from(text.as_str());
2537 let actual_ranges = trailing_whitespace_ranges(&rope);
2538 let expected_ranges = TRAILING_WHITESPACE_REGEX
2539 .find_iter(&text)
2540 .map(|m| m.range())
2541 .collect::<Vec<_>>();
2542 assert_eq!(
2543 actual_ranges,
2544 expected_ranges,
2545 "wrong ranges for text lines:\n{:?}",
2546 text.split('\n').collect::<Vec<_>>()
2547 );
2548}
2549
2550fn ruby_lang() -> Language {
2551 Language::new(
2552 LanguageConfig {
2553 name: "Ruby".into(),
2554 matcher: LanguageMatcher {
2555 path_suffixes: vec!["rb".to_string()],
2556 ..Default::default()
2557 },
2558 line_comments: vec!["# ".into()],
2559 ..Default::default()
2560 },
2561 Some(tree_sitter_ruby::language()),
2562 )
2563 .with_indents_query(
2564 r#"
2565 (class "end" @end) @indent
2566 (method "end" @end) @indent
2567 (rescue) @outdent
2568 (then) @indent
2569 "#,
2570 )
2571 .unwrap()
2572}
2573
2574fn html_lang() -> Language {
2575 Language::new(
2576 LanguageConfig {
2577 name: "HTML".into(),
2578 block_comment: Some(("<!--".into(), "-->".into())),
2579 ..Default::default()
2580 },
2581 Some(tree_sitter_html::language()),
2582 )
2583 .with_indents_query(
2584 "
2585 (element
2586 (start_tag) @start
2587 (end_tag)? @end) @indent
2588 ",
2589 )
2590 .unwrap()
2591 .with_injection_query(
2592 r#"
2593 (script_element
2594 (raw_text) @content
2595 (#set! "language" "javascript"))
2596 "#,
2597 )
2598 .unwrap()
2599}
2600
2601fn erb_lang() -> Language {
2602 Language::new(
2603 LanguageConfig {
2604 name: "ERB".into(),
2605 matcher: LanguageMatcher {
2606 path_suffixes: vec!["erb".to_string()],
2607 ..Default::default()
2608 },
2609 block_comment: Some(("<%#".into(), "%>".into())),
2610 ..Default::default()
2611 },
2612 Some(tree_sitter_embedded_template::language()),
2613 )
2614 .with_injection_query(
2615 r#"
2616 (
2617 (code) @content
2618 (#set! "language" "ruby")
2619 (#set! "combined")
2620 )
2621
2622 (
2623 (content) @content
2624 (#set! "language" "html")
2625 (#set! "combined")
2626 )
2627 "#,
2628 )
2629 .unwrap()
2630}
2631
2632fn rust_lang() -> Language {
2633 Language::new(
2634 LanguageConfig {
2635 name: "Rust".into(),
2636 matcher: LanguageMatcher {
2637 path_suffixes: vec!["rs".to_string()],
2638 ..Default::default()
2639 },
2640 ..Default::default()
2641 },
2642 Some(tree_sitter_rust::language()),
2643 )
2644 .with_indents_query(
2645 r#"
2646 (call_expression) @indent
2647 (field_expression) @indent
2648 (_ "(" ")" @end) @indent
2649 (_ "{" "}" @end) @indent
2650 "#,
2651 )
2652 .unwrap()
2653 .with_brackets_query(
2654 r#"
2655 ("{" @open "}" @close)
2656 "#,
2657 )
2658 .unwrap()
2659 .with_outline_query(
2660 r#"
2661 (line_comment) @annotation
2662
2663 (struct_item
2664 "struct" @context
2665 name: (_) @name) @item
2666 (enum_item
2667 "enum" @context
2668 name: (_) @name) @item
2669 (enum_variant
2670 name: (_) @name) @item
2671 (field_declaration
2672 name: (_) @name) @item
2673 (impl_item
2674 "impl" @context
2675 trait: (_)? @name
2676 "for"? @context
2677 type: (_) @name
2678 body: (_ "{" (_)* "}")) @item
2679 (function_item
2680 "fn" @context
2681 name: (_) @name) @item
2682 (mod_item
2683 "mod" @context
2684 name: (_) @name) @item
2685 "#,
2686 )
2687 .unwrap()
2688}
2689
2690fn json_lang() -> Language {
2691 Language::new(
2692 LanguageConfig {
2693 name: "Json".into(),
2694 matcher: LanguageMatcher {
2695 path_suffixes: vec!["js".to_string()],
2696 ..Default::default()
2697 },
2698 ..Default::default()
2699 },
2700 Some(tree_sitter_json::language()),
2701 )
2702}
2703
2704fn javascript_lang() -> Language {
2705 Language::new(
2706 LanguageConfig {
2707 name: "JavaScript".into(),
2708 ..Default::default()
2709 },
2710 Some(tree_sitter_typescript::language_tsx()),
2711 )
2712 .with_brackets_query(
2713 r#"
2714 ("{" @open "}" @close)
2715 ("(" @open ")" @close)
2716 "#,
2717 )
2718 .unwrap()
2719 .with_indents_query(
2720 r#"
2721 (object "}" @end) @indent
2722 "#,
2723 )
2724 .unwrap()
2725}
2726
2727fn get_tree_sexp(buffer: &Model<Buffer>, cx: &mut gpui::TestAppContext) -> String {
2728 buffer.update(cx, |buffer, _| {
2729 let snapshot = buffer.snapshot();
2730 let layers = snapshot.syntax.layers(buffer.as_text_snapshot());
2731 layers[0].node().to_sexp()
2732 })
2733}
2734
2735// Assert that the enclosing bracket ranges around the selection match the pairs indicated by the marked text in `range_markers`
2736fn assert_bracket_pairs(
2737 selection_text: &'static str,
2738 bracket_pair_texts: Vec<&'static str>,
2739 language: Language,
2740 cx: &mut AppContext,
2741) {
2742 let (expected_text, selection_ranges) = marked_text_ranges(selection_text, false);
2743 let buffer = cx.new_model(|cx| {
2744 Buffer::local(expected_text.clone(), cx).with_language(Arc::new(language), cx)
2745 });
2746 let buffer = buffer.update(cx, |buffer, _cx| buffer.snapshot());
2747
2748 let selection_range = selection_ranges[0].clone();
2749
2750 let bracket_pairs = bracket_pair_texts
2751 .into_iter()
2752 .map(|pair_text| {
2753 let (bracket_text, ranges) = marked_text_ranges(pair_text, false);
2754 assert_eq!(bracket_text, expected_text);
2755 (ranges[0].clone(), ranges[1].clone())
2756 })
2757 .collect::<Vec<_>>();
2758
2759 assert_set_eq!(
2760 buffer.bracket_ranges(selection_range).collect::<Vec<_>>(),
2761 bracket_pairs
2762 );
2763}
2764
2765fn init_settings(cx: &mut AppContext, f: fn(&mut AllLanguageSettingsContent)) {
2766 let settings_store = SettingsStore::test(cx);
2767 cx.set_global(settings_store);
2768 crate::init(cx);
2769 cx.update_global::<SettingsStore, _>(|settings, cx| {
2770 settings.update_user_settings::<AllLanguageSettings>(cx, f);
2771 });
2772}