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