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