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