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