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().0.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]
2631async fn test_preview_edits(cx: &mut TestAppContext) {
2632 cx.update(|cx| {
2633 init_settings(cx, |_| {});
2634 theme::init(theme::LoadThemes::JustBase, cx);
2635 });
2636
2637 let text = indoc! {r#"
2638 struct Person {
2639 first_name: String,
2640 }
2641
2642 impl Person {
2643 fn last_name(&self) -> &String {
2644 &self.last_name
2645 }
2646 }"#
2647 };
2648
2649 let language = Arc::new(Language::new(
2650 LanguageConfig::default(),
2651 Some(tree_sitter_rust::LANGUAGE.into()),
2652 ));
2653 let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
2654 let highlighted_edits = preview_edits(
2655 &buffer,
2656 cx,
2657 [
2658 (Point::new(5, 7)..Point::new(5, 11), "first"),
2659 (Point::new(6, 14)..Point::new(6, 18), "first"),
2660 ],
2661 )
2662 .await;
2663
2664 assert_eq!(
2665 highlighted_edits.text,
2666 " fn lastfirst_name(&self) -> &String {\n &self.lastfirst_name"
2667 );
2668
2669 async fn preview_edits(
2670 buffer: &Entity<Buffer>,
2671 cx: &mut TestAppContext,
2672 edits: impl IntoIterator<Item = (Range<Point>, &'static str)>,
2673 ) -> HighlightedEdits {
2674 let edits = buffer.read_with(cx, |buffer, _| {
2675 edits
2676 .into_iter()
2677 .map(|(range, text)| {
2678 (
2679 buffer.anchor_before(range.start)..buffer.anchor_after(range.end),
2680 text.to_string(),
2681 )
2682 })
2683 .collect::<Vec<_>>()
2684 });
2685 let edit_preview = buffer
2686 .read_with(cx, |buffer, cx| {
2687 buffer.preview_edits(edits.clone().into(), cx)
2688 })
2689 .await;
2690 cx.read(|cx| edit_preview.highlight_edits(&buffer.read(cx).snapshot(), &edits, true, cx))
2691 }
2692}
2693
2694#[gpui::test]
2695async fn test_preview_edits_interpolate(cx: &mut TestAppContext) {
2696 use theme::ActiveTheme;
2697 cx.update(|cx| {
2698 init_settings(cx, |_| {});
2699 theme::init(theme::LoadThemes::JustBase, cx);
2700 });
2701
2702 let text = indoc! {r#"
2703 struct Person {
2704 _name: String
2705 }"#
2706 };
2707
2708 let language = Arc::new(Language::new(
2709 LanguageConfig::default(),
2710 Some(tree_sitter_rust::LANGUAGE.into()),
2711 ));
2712 let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
2713
2714 let edits = construct_edits(&buffer, [(Point::new(1, 4)..Point::new(1, 4), "first")], cx);
2715 let edit_preview = buffer
2716 .read_with(cx, |buffer, cx| buffer.preview_edits(edits.clone(), cx))
2717 .await;
2718
2719 let highlighted_edits =
2720 cx.read(|cx| edit_preview.highlight_edits(&buffer.read(cx).snapshot(), &edits, false, cx));
2721
2722 let created_background = cx.read(|cx| cx.theme().status().created_background);
2723
2724 assert_eq!(highlighted_edits.text, " first_name: String");
2725 assert_eq!(highlighted_edits.highlights.len(), 1);
2726 assert_eq!(highlighted_edits.highlights[0].0, 4..9);
2727 assert_eq!(
2728 highlighted_edits.highlights[0].1.background_color,
2729 Some(created_background)
2730 );
2731
2732 let edits = construct_edits(&buffer, [(Point::new(1, 4)..Point::new(1, 4), "f")], cx);
2733 cx.update(|cx| {
2734 buffer.update(cx, |buffer, cx| {
2735 buffer.edit(edits.iter().cloned(), None, cx);
2736 })
2737 });
2738
2739 let edits = construct_edits(&buffer, [(Point::new(1, 5)..Point::new(1, 5), "irst")], cx);
2740 let highlighted_edits =
2741 cx.read(|cx| edit_preview.highlight_edits(&buffer.read(cx).snapshot(), &edits, false, cx));
2742
2743 assert_eq!(highlighted_edits.text, " first_name: String");
2744 assert_eq!(highlighted_edits.highlights.len(), 1);
2745 assert_eq!(highlighted_edits.highlights[0].0, (5..9));
2746 assert_eq!(
2747 highlighted_edits.highlights[0].1.background_color,
2748 Some(created_background)
2749 );
2750
2751 fn construct_edits(
2752 buffer: &Entity<Buffer>,
2753 edits: impl IntoIterator<Item = (Range<Point>, &'static str)>,
2754 cx: &mut TestAppContext,
2755 ) -> Arc<[(Range<Anchor>, String)]> {
2756 buffer
2757 .read_with(cx, |buffer, _| {
2758 edits
2759 .into_iter()
2760 .map(|(range, text)| {
2761 (
2762 buffer.anchor_after(range.start)..buffer.anchor_before(range.end),
2763 text.to_string(),
2764 )
2765 })
2766 .collect::<Vec<_>>()
2767 })
2768 .into()
2769 }
2770}
2771
2772#[gpui::test(iterations = 100)]
2773fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
2774 let min_peers = env::var("MIN_PEERS")
2775 .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
2776 .unwrap_or(1);
2777 let max_peers = env::var("MAX_PEERS")
2778 .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
2779 .unwrap_or(5);
2780 let operations = env::var("OPERATIONS")
2781 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
2782 .unwrap_or(10);
2783
2784 let base_text_len = rng.gen_range(0..10);
2785 let base_text = RandomCharIter::new(&mut rng)
2786 .take(base_text_len)
2787 .collect::<String>();
2788 let mut replica_ids = Vec::new();
2789 let mut buffers = Vec::new();
2790 let network = Arc::new(Mutex::new(Network::new(rng.clone())));
2791 let base_buffer = cx.new(|cx| Buffer::local(base_text.as_str(), cx));
2792
2793 for i in 0..rng.gen_range(min_peers..=max_peers) {
2794 let buffer = cx.new(|cx| {
2795 let state = base_buffer.read(cx).to_proto(cx);
2796 let ops = cx
2797 .background_executor()
2798 .block(base_buffer.read(cx).serialize_ops(None, cx));
2799 let mut buffer =
2800 Buffer::from_proto(i as ReplicaId, Capability::ReadWrite, state, None).unwrap();
2801 buffer.apply_ops(
2802 ops.into_iter()
2803 .map(|op| proto::deserialize_operation(op).unwrap()),
2804 cx,
2805 );
2806 buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
2807 let network = network.clone();
2808 cx.subscribe(&cx.entity(), move |buffer, _, event, _| {
2809 if let BufferEvent::Operation {
2810 operation,
2811 is_local: true,
2812 } = event
2813 {
2814 network.lock().broadcast(
2815 buffer.replica_id(),
2816 vec![proto::serialize_operation(operation)],
2817 );
2818 }
2819 })
2820 .detach();
2821 buffer
2822 });
2823
2824 buffers.push(buffer);
2825 replica_ids.push(i as ReplicaId);
2826 network.lock().add_peer(i as ReplicaId);
2827 log::info!("Adding initial peer with replica id {}", i);
2828 }
2829
2830 log::info!("initial text: {:?}", base_text);
2831
2832 let mut now = Instant::now();
2833 let mut mutation_count = operations;
2834 let mut next_diagnostic_id = 0;
2835 let mut active_selections = BTreeMap::default();
2836 loop {
2837 let replica_index = rng.gen_range(0..replica_ids.len());
2838 let replica_id = replica_ids[replica_index];
2839 let buffer = &mut buffers[replica_index];
2840 let mut new_buffer = None;
2841 match rng.gen_range(0..100) {
2842 0..=29 if mutation_count != 0 => {
2843 buffer.update(cx, |buffer, cx| {
2844 buffer.start_transaction_at(now);
2845 buffer.randomly_edit(&mut rng, 5, cx);
2846 buffer.end_transaction_at(now, cx);
2847 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
2848 });
2849 mutation_count -= 1;
2850 }
2851 30..=39 if mutation_count != 0 => {
2852 buffer.update(cx, |buffer, cx| {
2853 if rng.gen_bool(0.2) {
2854 log::info!("peer {} clearing active selections", replica_id);
2855 active_selections.remove(&replica_id);
2856 buffer.remove_active_selections(cx);
2857 } else {
2858 let mut selections = Vec::new();
2859 for id in 0..rng.gen_range(1..=5) {
2860 let range = buffer.random_byte_range(0, &mut rng);
2861 selections.push(Selection {
2862 id,
2863 start: buffer.anchor_before(range.start),
2864 end: buffer.anchor_before(range.end),
2865 reversed: false,
2866 goal: SelectionGoal::None,
2867 });
2868 }
2869 let selections: Arc<[Selection<Anchor>]> = selections.into();
2870 log::info!(
2871 "peer {} setting active selections: {:?}",
2872 replica_id,
2873 selections
2874 );
2875 active_selections.insert(replica_id, selections.clone());
2876 buffer.set_active_selections(selections, false, Default::default(), cx);
2877 }
2878 });
2879 mutation_count -= 1;
2880 }
2881 40..=49 if mutation_count != 0 && replica_id == 0 => {
2882 let entry_count = rng.gen_range(1..=5);
2883 buffer.update(cx, |buffer, cx| {
2884 let diagnostics = DiagnosticSet::new(
2885 (0..entry_count).map(|_| {
2886 let range = buffer.random_byte_range(0, &mut rng);
2887 let range = range.to_point_utf16(buffer);
2888 let range = range.start..range.end;
2889 DiagnosticEntry {
2890 range,
2891 diagnostic: Diagnostic {
2892 message: post_inc(&mut next_diagnostic_id).to_string(),
2893 ..Default::default()
2894 },
2895 }
2896 }),
2897 buffer,
2898 );
2899 log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
2900 buffer.update_diagnostics(LanguageServerId(0), diagnostics, cx);
2901 });
2902 mutation_count -= 1;
2903 }
2904 50..=59 if replica_ids.len() < max_peers => {
2905 let old_buffer_state = buffer.read(cx).to_proto(cx);
2906 let old_buffer_ops = cx
2907 .background_executor()
2908 .block(buffer.read(cx).serialize_ops(None, cx));
2909 let new_replica_id = (0..=replica_ids.len() as ReplicaId)
2910 .filter(|replica_id| *replica_id != buffer.read(cx).replica_id())
2911 .choose(&mut rng)
2912 .unwrap();
2913 log::info!(
2914 "Adding new replica {} (replicating from {})",
2915 new_replica_id,
2916 replica_id
2917 );
2918 new_buffer = Some(cx.new(|cx| {
2919 let mut new_buffer = Buffer::from_proto(
2920 new_replica_id,
2921 Capability::ReadWrite,
2922 old_buffer_state,
2923 None,
2924 )
2925 .unwrap();
2926 new_buffer.apply_ops(
2927 old_buffer_ops
2928 .into_iter()
2929 .map(|op| deserialize_operation(op).unwrap()),
2930 cx,
2931 );
2932 log::info!(
2933 "New replica {} text: {:?}",
2934 new_buffer.replica_id(),
2935 new_buffer.text()
2936 );
2937 new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
2938 let network = network.clone();
2939 cx.subscribe(&cx.entity(), move |buffer, _, event, _| {
2940 if let BufferEvent::Operation {
2941 operation,
2942 is_local: true,
2943 } = event
2944 {
2945 network.lock().broadcast(
2946 buffer.replica_id(),
2947 vec![proto::serialize_operation(operation)],
2948 );
2949 }
2950 })
2951 .detach();
2952 new_buffer
2953 }));
2954 network.lock().replicate(replica_id, new_replica_id);
2955
2956 if new_replica_id as usize == replica_ids.len() {
2957 replica_ids.push(new_replica_id);
2958 } else {
2959 let new_buffer = new_buffer.take().unwrap();
2960 while network.lock().has_unreceived(new_replica_id) {
2961 let ops = network
2962 .lock()
2963 .receive(new_replica_id)
2964 .into_iter()
2965 .map(|op| proto::deserialize_operation(op).unwrap());
2966 if ops.len() > 0 {
2967 log::info!(
2968 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
2969 new_replica_id,
2970 buffer.read(cx).version(),
2971 ops.len(),
2972 ops
2973 );
2974 new_buffer.update(cx, |new_buffer, cx| {
2975 new_buffer.apply_ops(ops, cx);
2976 });
2977 }
2978 }
2979 buffers[new_replica_id as usize] = new_buffer;
2980 }
2981 }
2982 60..=69 if mutation_count != 0 => {
2983 buffer.update(cx, |buffer, cx| {
2984 buffer.randomly_undo_redo(&mut rng, cx);
2985 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
2986 });
2987 mutation_count -= 1;
2988 }
2989 _ if network.lock().has_unreceived(replica_id) => {
2990 let ops = network
2991 .lock()
2992 .receive(replica_id)
2993 .into_iter()
2994 .map(|op| proto::deserialize_operation(op).unwrap());
2995 if ops.len() > 0 {
2996 log::info!(
2997 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
2998 replica_id,
2999 buffer.read(cx).version(),
3000 ops.len(),
3001 ops
3002 );
3003 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx));
3004 }
3005 }
3006 _ => {}
3007 }
3008
3009 now += Duration::from_millis(rng.gen_range(0..=200));
3010 buffers.extend(new_buffer);
3011
3012 for buffer in &buffers {
3013 buffer.read(cx).check_invariants();
3014 }
3015
3016 if mutation_count == 0 && network.lock().is_idle() {
3017 break;
3018 }
3019 }
3020
3021 let first_buffer = buffers[0].read(cx).snapshot();
3022 for buffer in &buffers[1..] {
3023 let buffer = buffer.read(cx).snapshot();
3024 assert_eq!(
3025 buffer.version(),
3026 first_buffer.version(),
3027 "Replica {} version != Replica 0 version",
3028 buffer.replica_id()
3029 );
3030 assert_eq!(
3031 buffer.text(),
3032 first_buffer.text(),
3033 "Replica {} text != Replica 0 text",
3034 buffer.replica_id()
3035 );
3036 assert_eq!(
3037 buffer
3038 .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
3039 .collect::<Vec<_>>(),
3040 first_buffer
3041 .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
3042 .collect::<Vec<_>>(),
3043 "Replica {} diagnostics != Replica 0 diagnostics",
3044 buffer.replica_id()
3045 );
3046 }
3047
3048 for buffer in &buffers {
3049 let buffer = buffer.read(cx).snapshot();
3050 let actual_remote_selections = buffer
3051 .selections_in_range(Anchor::MIN..Anchor::MAX, false)
3052 .map(|(replica_id, _, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
3053 .collect::<Vec<_>>();
3054 let expected_remote_selections = active_selections
3055 .iter()
3056 .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
3057 .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
3058 .collect::<Vec<_>>();
3059 assert_eq!(
3060 actual_remote_selections,
3061 expected_remote_selections,
3062 "Replica {} remote selections != expected selections",
3063 buffer.replica_id()
3064 );
3065 }
3066}
3067
3068#[test]
3069fn test_contiguous_ranges() {
3070 assert_eq!(
3071 contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
3072 &[1..4, 5..7, 9..13]
3073 );
3074
3075 // Respects the `max_len` parameter
3076 assert_eq!(
3077 contiguous_ranges(
3078 [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
3079 3
3080 )
3081 .collect::<Vec<_>>(),
3082 &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
3083 );
3084}
3085
3086#[gpui::test(iterations = 500)]
3087fn test_trailing_whitespace_ranges(mut rng: StdRng) {
3088 // Generate a random multi-line string containing
3089 // some lines with trailing whitespace.
3090 let mut text = String::new();
3091 for _ in 0..rng.gen_range(0..16) {
3092 for _ in 0..rng.gen_range(0..36) {
3093 text.push(match rng.gen_range(0..10) {
3094 0..=1 => ' ',
3095 3 => '\t',
3096 _ => rng.gen_range('a'..='z'),
3097 });
3098 }
3099 text.push('\n');
3100 }
3101
3102 match rng.gen_range(0..10) {
3103 // sometimes remove the last newline
3104 0..=1 => drop(text.pop()), //
3105
3106 // sometimes add extra newlines
3107 2..=3 => text.push_str(&"\n".repeat(rng.gen_range(1..5))),
3108 _ => {}
3109 }
3110
3111 let rope = Rope::from(text.as_str());
3112 let actual_ranges = trailing_whitespace_ranges(&rope);
3113 let expected_ranges = TRAILING_WHITESPACE_REGEX
3114 .find_iter(&text)
3115 .map(|m| m.range())
3116 .collect::<Vec<_>>();
3117 assert_eq!(
3118 actual_ranges,
3119 expected_ranges,
3120 "wrong ranges for text lines:\n{:?}",
3121 text.split('\n').collect::<Vec<_>>()
3122 );
3123}
3124
3125fn ruby_lang() -> Language {
3126 Language::new(
3127 LanguageConfig {
3128 name: "Ruby".into(),
3129 matcher: LanguageMatcher {
3130 path_suffixes: vec!["rb".to_string()],
3131 ..Default::default()
3132 },
3133 line_comments: vec!["# ".into()],
3134 ..Default::default()
3135 },
3136 Some(tree_sitter_ruby::LANGUAGE.into()),
3137 )
3138 .with_indents_query(
3139 r#"
3140 (class "end" @end) @indent
3141 (method "end" @end) @indent
3142 (rescue) @outdent
3143 (then) @indent
3144 "#,
3145 )
3146 .unwrap()
3147}
3148
3149fn html_lang() -> Language {
3150 Language::new(
3151 LanguageConfig {
3152 name: LanguageName::new("HTML"),
3153 block_comment: Some(("<!--".into(), "-->".into())),
3154 ..Default::default()
3155 },
3156 Some(tree_sitter_html::language()),
3157 )
3158 .with_indents_query(
3159 "
3160 (element
3161 (start_tag) @start
3162 (end_tag)? @end) @indent
3163 ",
3164 )
3165 .unwrap()
3166 .with_injection_query(
3167 r#"
3168 (script_element
3169 (raw_text) @injection.content
3170 (#set! injection.language "javascript"))
3171 "#,
3172 )
3173 .unwrap()
3174}
3175
3176fn erb_lang() -> Language {
3177 Language::new(
3178 LanguageConfig {
3179 name: "ERB".into(),
3180 matcher: LanguageMatcher {
3181 path_suffixes: vec!["erb".to_string()],
3182 ..Default::default()
3183 },
3184 block_comment: Some(("<%#".into(), "%>".into())),
3185 ..Default::default()
3186 },
3187 Some(tree_sitter_embedded_template::LANGUAGE.into()),
3188 )
3189 .with_injection_query(
3190 r#"
3191 (
3192 (code) @injection.content
3193 (#set! injection.language "ruby")
3194 (#set! injection.combined)
3195 )
3196
3197 (
3198 (content) @injection.content
3199 (#set! injection.language "html")
3200 (#set! injection.combined)
3201 )
3202 "#,
3203 )
3204 .unwrap()
3205}
3206
3207fn rust_lang() -> Language {
3208 Language::new(
3209 LanguageConfig {
3210 name: "Rust".into(),
3211 matcher: LanguageMatcher {
3212 path_suffixes: vec!["rs".to_string()],
3213 ..Default::default()
3214 },
3215 ..Default::default()
3216 },
3217 Some(tree_sitter_rust::LANGUAGE.into()),
3218 )
3219 .with_indents_query(
3220 r#"
3221 (call_expression) @indent
3222 (field_expression) @indent
3223 (_ "(" ")" @end) @indent
3224 (_ "{" "}" @end) @indent
3225 "#,
3226 )
3227 .unwrap()
3228 .with_brackets_query(
3229 r#"
3230 ("{" @open "}" @close)
3231 "#,
3232 )
3233 .unwrap()
3234 .with_text_object_query(
3235 r#"
3236 (function_item
3237 body: (_
3238 "{"
3239 (_)* @function.inside
3240 "}" )) @function.around
3241
3242 (line_comment)+ @comment.around
3243
3244 (block_comment) @comment.around
3245 "#,
3246 )
3247 .unwrap()
3248 .with_outline_query(
3249 r#"
3250 (line_comment) @annotation
3251
3252 (struct_item
3253 "struct" @context
3254 name: (_) @name) @item
3255 (enum_item
3256 "enum" @context
3257 name: (_) @name) @item
3258 (enum_variant
3259 name: (_) @name) @item
3260 (field_declaration
3261 name: (_) @name) @item
3262 (impl_item
3263 "impl" @context
3264 trait: (_)? @name
3265 "for"? @context
3266 type: (_) @name
3267 body: (_ "{" (_)* "}")) @item
3268 (function_item
3269 "fn" @context
3270 name: (_) @name) @item
3271 (mod_item
3272 "mod" @context
3273 name: (_) @name) @item
3274 "#,
3275 )
3276 .unwrap()
3277}
3278
3279fn json_lang() -> Language {
3280 Language::new(
3281 LanguageConfig {
3282 name: "Json".into(),
3283 matcher: LanguageMatcher {
3284 path_suffixes: vec!["js".to_string()],
3285 ..Default::default()
3286 },
3287 ..Default::default()
3288 },
3289 Some(tree_sitter_json::LANGUAGE.into()),
3290 )
3291}
3292
3293fn javascript_lang() -> Language {
3294 Language::new(
3295 LanguageConfig {
3296 name: "JavaScript".into(),
3297 ..Default::default()
3298 },
3299 Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
3300 )
3301 .with_brackets_query(
3302 r#"
3303 ("{" @open "}" @close)
3304 ("(" @open ")" @close)
3305 "#,
3306 )
3307 .unwrap()
3308 .with_indents_query(
3309 r#"
3310 (object "}" @end) @indent
3311 "#,
3312 )
3313 .unwrap()
3314}
3315
3316pub fn markdown_lang() -> Language {
3317 Language::new(
3318 LanguageConfig {
3319 name: "Markdown".into(),
3320 matcher: LanguageMatcher {
3321 path_suffixes: vec!["md".into()],
3322 ..Default::default()
3323 },
3324 ..Default::default()
3325 },
3326 Some(tree_sitter_md::LANGUAGE.into()),
3327 )
3328 .with_injection_query(
3329 r#"
3330 (fenced_code_block
3331 (info_string
3332 (language) @injection.language)
3333 (code_fence_content) @injection.content)
3334
3335 ((inline) @injection.content
3336 (#set! injection.language "markdown-inline"))
3337 "#,
3338 )
3339 .unwrap()
3340}
3341
3342pub fn markdown_inline_lang() -> Language {
3343 Language::new(
3344 LanguageConfig {
3345 name: "Markdown-Inline".into(),
3346 hidden: true,
3347 ..LanguageConfig::default()
3348 },
3349 Some(tree_sitter_md::INLINE_LANGUAGE.into()),
3350 )
3351 .with_highlights_query("(emphasis) @emphasis")
3352 .unwrap()
3353}
3354
3355fn get_tree_sexp(buffer: &Entity<Buffer>, cx: &mut gpui::TestAppContext) -> String {
3356 buffer.update(cx, |buffer, _| {
3357 let snapshot = buffer.snapshot();
3358 let layers = snapshot.syntax.layers(buffer.as_text_snapshot());
3359 layers[0].node().to_sexp()
3360 })
3361}
3362
3363// Assert that the enclosing bracket ranges around the selection match the pairs indicated by the marked text in `range_markers`
3364fn assert_bracket_pairs(
3365 selection_text: &'static str,
3366 bracket_pair_texts: Vec<&'static str>,
3367 language: Language,
3368 cx: &mut App,
3369) {
3370 let (expected_text, selection_ranges) = marked_text_ranges(selection_text, false);
3371 let buffer =
3372 cx.new(|cx| Buffer::local(expected_text.clone(), cx).with_language(Arc::new(language), cx));
3373 let buffer = buffer.update(cx, |buffer, _cx| buffer.snapshot());
3374
3375 let selection_range = selection_ranges[0].clone();
3376
3377 let bracket_pairs = bracket_pair_texts
3378 .into_iter()
3379 .map(|pair_text| {
3380 let (bracket_text, ranges) = marked_text_ranges(pair_text, false);
3381 assert_eq!(bracket_text, expected_text);
3382 (ranges[0].clone(), ranges[1].clone())
3383 })
3384 .collect::<Vec<_>>();
3385
3386 assert_set_eq!(
3387 buffer.bracket_ranges(selection_range).collect::<Vec<_>>(),
3388 bracket_pairs
3389 );
3390}
3391
3392fn init_settings(cx: &mut App, f: fn(&mut AllLanguageSettingsContent)) {
3393 let settings_store = SettingsStore::test(cx);
3394 cx.set_global(settings_store);
3395 crate::init(cx);
3396 cx.update_global::<SettingsStore, _>(|settings, cx| {
3397 settings.update_user_settings::<AllLanguageSettings>(cx, f);
3398 });
3399}