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