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