1use crate::language_settings::{
2 AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent,
3};
4
5use super::*;
6use clock::ReplicaId;
7use collections::BTreeMap;
8use fs::LineEnding;
9use gpui::{AppContext, ModelHandle};
10use indoc::indoc;
11use proto::deserialize_operation;
12use rand::prelude::*;
13use regex::RegexBuilder;
14use settings::SettingsStore;
15use std::{
16 cell::RefCell,
17 env,
18 ops::Range,
19 rc::Rc,
20 time::{Duration, Instant},
21};
22use text::network::Network;
23use unindent::Unindent as _;
24use util::{assert_set_eq, post_inc, test::marked_text_ranges, RandomCharIter};
25
26lazy_static! {
27 static ref TRAILING_WHITESPACE_REGEX: Regex = RegexBuilder::new("[ \t]+$")
28 .multi_line(true)
29 .build()
30 .unwrap();
31}
32
33#[cfg(test)]
34#[ctor::ctor]
35fn init_logger() {
36 if std::env::var("RUST_LOG").is_ok() {
37 env_logger::init();
38 }
39}
40
41#[gpui::test]
42fn test_line_endings(cx: &mut gpui::AppContext) {
43 init_settings(cx, |_| {});
44
45 cx.add_model(|cx| {
46 let mut buffer =
47 Buffer::new(0, "one\r\ntwo\rthree", cx).with_language(Arc::new(rust_lang()), cx);
48 assert_eq!(buffer.text(), "one\ntwo\nthree");
49 assert_eq!(buffer.line_ending(), LineEnding::Windows);
50
51 buffer.check_invariants();
52 buffer.edit(
53 [(buffer.len()..buffer.len(), "\r\nfour")],
54 Some(AutoindentMode::EachLine),
55 cx,
56 );
57 buffer.edit([(0..0, "zero\r\n")], None, cx);
58 assert_eq!(buffer.text(), "zero\none\ntwo\nthree\nfour");
59 assert_eq!(buffer.line_ending(), LineEnding::Windows);
60 buffer.check_invariants();
61
62 buffer
63 });
64}
65
66#[gpui::test]
67fn test_select_language() {
68 let registry = Arc::new(LanguageRegistry::test());
69 registry.add(Arc::new(Language::new(
70 LanguageConfig {
71 name: "Rust".into(),
72 path_suffixes: vec!["rs".to_string()],
73 ..Default::default()
74 },
75 Some(tree_sitter_rust::language()),
76 )));
77 registry.add(Arc::new(Language::new(
78 LanguageConfig {
79 name: "Make".into(),
80 path_suffixes: vec!["Makefile".to_string(), "mk".to_string()],
81 ..Default::default()
82 },
83 Some(tree_sitter_rust::language()),
84 )));
85
86 // matching file extension
87 assert_eq!(
88 registry
89 .language_for_file("zed/lib.rs", None)
90 .now_or_never()
91 .and_then(|l| Some(l.ok()?.name())),
92 Some("Rust".into())
93 );
94 assert_eq!(
95 registry
96 .language_for_file("zed/lib.mk", None)
97 .now_or_never()
98 .and_then(|l| Some(l.ok()?.name())),
99 Some("Make".into())
100 );
101
102 // matching filename
103 assert_eq!(
104 registry
105 .language_for_file("zed/Makefile", None)
106 .now_or_never()
107 .and_then(|l| Some(l.ok()?.name())),
108 Some("Make".into())
109 );
110
111 // matching suffix that is not the full file extension or filename
112 assert_eq!(
113 registry
114 .language_for_file("zed/cars", None)
115 .now_or_never()
116 .and_then(|l| Some(l.ok()?.name())),
117 None
118 );
119 assert_eq!(
120 registry
121 .language_for_file("zed/a.cars", None)
122 .now_or_never()
123 .and_then(|l| Some(l.ok()?.name())),
124 None
125 );
126 assert_eq!(
127 registry
128 .language_for_file("zed/sumk", None)
129 .now_or_never()
130 .and_then(|l| Some(l.ok()?.name())),
131 None
132 );
133}
134
135#[gpui::test]
136fn test_edit_events(cx: &mut gpui::AppContext) {
137 let mut now = Instant::now();
138 let buffer_1_events = Rc::new(RefCell::new(Vec::new()));
139 let buffer_2_events = Rc::new(RefCell::new(Vec::new()));
140
141 let buffer1 = cx.add_model(|cx| Buffer::new(0, "abcdef", cx));
142 let buffer2 = cx.add_model(|cx| Buffer::new(1, "abcdef", cx));
143 let buffer1_ops = Rc::new(RefCell::new(Vec::new()));
144 buffer1.update(cx, {
145 let buffer1_ops = buffer1_ops.clone();
146 |buffer, cx| {
147 let buffer_1_events = buffer_1_events.clone();
148 cx.subscribe(&buffer1, move |_, _, event, _| match event.clone() {
149 Event::Operation(op) => buffer1_ops.borrow_mut().push(op),
150 event => buffer_1_events.borrow_mut().push(event),
151 })
152 .detach();
153 let buffer_2_events = buffer_2_events.clone();
154 cx.subscribe(&buffer2, move |_, _, event, _| {
155 buffer_2_events.borrow_mut().push(event.clone())
156 })
157 .detach();
158
159 // An edit emits an edited event, followed by a dirty changed event,
160 // since the buffer was previously in a clean state.
161 buffer.edit([(2..4, "XYZ")], None, cx);
162
163 // An empty transaction does not emit any events.
164 buffer.start_transaction();
165 buffer.end_transaction(cx);
166
167 // A transaction containing two edits emits one edited event.
168 now += Duration::from_secs(1);
169 buffer.start_transaction_at(now);
170 buffer.edit([(5..5, "u")], None, cx);
171 buffer.edit([(6..6, "w")], None, cx);
172 buffer.end_transaction_at(now, cx);
173
174 // Undoing a transaction emits one edited event.
175 buffer.undo(cx);
176 }
177 });
178
179 // Incorporating a set of remote ops emits a single edited event,
180 // followed by a dirty changed event.
181 buffer2.update(cx, |buffer, cx| {
182 buffer
183 .apply_ops(buffer1_ops.borrow_mut().drain(..), cx)
184 .unwrap();
185 });
186 assert_eq!(
187 mem::take(&mut *buffer_1_events.borrow_mut()),
188 vec![
189 Event::Edited,
190 Event::DirtyChanged,
191 Event::Edited,
192 Event::Edited,
193 ]
194 );
195 assert_eq!(
196 mem::take(&mut *buffer_2_events.borrow_mut()),
197 vec![Event::Edited, Event::DirtyChanged]
198 );
199
200 buffer1.update(cx, |buffer, cx| {
201 // Undoing the first transaction emits edited event, followed by a
202 // dirty changed event, since the buffer is again in a clean state.
203 buffer.undo(cx);
204 });
205 // Incorporating the remote ops again emits a single edited event,
206 // followed by a dirty changed event.
207 buffer2.update(cx, |buffer, cx| {
208 buffer
209 .apply_ops(buffer1_ops.borrow_mut().drain(..), cx)
210 .unwrap();
211 });
212 assert_eq!(
213 mem::take(&mut *buffer_1_events.borrow_mut()),
214 vec![Event::Edited, Event::DirtyChanged,]
215 );
216 assert_eq!(
217 mem::take(&mut *buffer_2_events.borrow_mut()),
218 vec![Event::Edited, Event::DirtyChanged]
219 );
220}
221
222#[gpui::test]
223async fn test_apply_diff(cx: &mut gpui::TestAppContext) {
224 let text = "a\nbb\nccc\ndddd\neeeee\nffffff\n";
225 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
226 let anchor = buffer.read_with(cx, |buffer, _| buffer.anchor_before(Point::new(3, 3)));
227
228 let text = "a\nccc\ndddd\nffffff\n";
229 let diff = buffer.read_with(cx, |b, cx| b.diff(text.into(), cx)).await;
230 buffer.update(cx, |buffer, cx| {
231 buffer.apply_diff(diff, cx).unwrap();
232 assert_eq!(buffer.text(), text);
233 assert_eq!(anchor.to_point(buffer), Point::new(2, 3));
234 });
235
236 let text = "a\n1\n\nccc\ndd2dd\nffffff\n";
237 let diff = buffer.read_with(cx, |b, cx| b.diff(text.into(), cx)).await;
238 buffer.update(cx, |buffer, cx| {
239 buffer.apply_diff(diff, cx).unwrap();
240 assert_eq!(buffer.text(), text);
241 assert_eq!(anchor.to_point(buffer), Point::new(4, 4));
242 });
243}
244
245#[gpui::test(iterations = 10)]
246async fn test_normalize_whitespace(cx: &mut gpui::TestAppContext) {
247 let text = [
248 "zero", //
249 "one ", // 2 trailing spaces
250 "two", //
251 "three ", // 3 trailing spaces
252 "four", //
253 "five ", // 4 trailing spaces
254 ]
255 .join("\n");
256
257 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
258
259 // Spawn a task to format the buffer's whitespace.
260 // Pause so that the foratting task starts running.
261 let format = buffer.read_with(cx, |buffer, cx| buffer.remove_trailing_whitespace(cx));
262 smol::future::yield_now().await;
263
264 // Edit the buffer while the normalization task is running.
265 let version_before_edit = buffer.read_with(cx, |buffer, _| buffer.version());
266 buffer.update(cx, |buffer, cx| {
267 buffer.edit(
268 [
269 (Point::new(0, 1)..Point::new(0, 1), "EE"),
270 (Point::new(3, 5)..Point::new(3, 5), "EEE"),
271 ],
272 None,
273 cx,
274 );
275 });
276
277 let format_diff = format.await;
278 buffer.update(cx, |buffer, cx| {
279 let version_before_format = format_diff.base_version.clone();
280 buffer.apply_diff(format_diff, cx);
281
282 // The outcome depends on the order of concurrent taks.
283 //
284 // If the edit occurred while searching for trailing whitespace ranges,
285 // then the trailing whitespace region touched by the edit is left intact.
286 if version_before_format == version_before_edit {
287 assert_eq!(
288 buffer.text(),
289 [
290 "zEEero", //
291 "one", //
292 "two", //
293 "threeEEE ", //
294 "four", //
295 "five", //
296 ]
297 .join("\n")
298 );
299 }
300 // Otherwise, all trailing whitespace is removed.
301 else {
302 assert_eq!(
303 buffer.text(),
304 [
305 "zEEero", //
306 "one", //
307 "two", //
308 "threeEEE", //
309 "four", //
310 "five", //
311 ]
312 .join("\n")
313 );
314 }
315 });
316}
317
318#[gpui::test]
319async fn test_reparse(cx: &mut gpui::TestAppContext) {
320 let text = "fn a() {}";
321 let buffer =
322 cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
323
324 // Wait for the initial text to parse
325 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
326 assert_eq!(
327 get_tree_sexp(&buffer, cx),
328 concat!(
329 "(source_file (function_item name: (identifier) ",
330 "parameters: (parameters) ",
331 "body: (block)))"
332 )
333 );
334
335 buffer.update(cx, |buffer, _| {
336 buffer.set_sync_parse_timeout(Duration::ZERO)
337 });
338
339 // Perform some edits (add parameter and variable reference)
340 // Parsing doesn't begin until the transaction is complete
341 buffer.update(cx, |buf, cx| {
342 buf.start_transaction();
343
344 let offset = buf.text().find(')').unwrap();
345 buf.edit([(offset..offset, "b: C")], None, cx);
346 assert!(!buf.is_parsing());
347
348 let offset = buf.text().find('}').unwrap();
349 buf.edit([(offset..offset, " d; ")], None, cx);
350 assert!(!buf.is_parsing());
351
352 buf.end_transaction(cx);
353 assert_eq!(buf.text(), "fn a(b: C) { d; }");
354 assert!(buf.is_parsing());
355 });
356 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
357 assert_eq!(
358 get_tree_sexp(&buffer, cx),
359 concat!(
360 "(source_file (function_item name: (identifier) ",
361 "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
362 "body: (block (expression_statement (identifier)))))"
363 )
364 );
365
366 // Perform a series of edits without waiting for the current parse to complete:
367 // * turn identifier into a field expression
368 // * turn field expression into a method call
369 // * add a turbofish to the method call
370 buffer.update(cx, |buf, cx| {
371 let offset = buf.text().find(';').unwrap();
372 buf.edit([(offset..offset, ".e")], None, cx);
373 assert_eq!(buf.text(), "fn a(b: C) { d.e; }");
374 assert!(buf.is_parsing());
375 });
376 buffer.update(cx, |buf, cx| {
377 let offset = buf.text().find(';').unwrap();
378 buf.edit([(offset..offset, "(f)")], None, cx);
379 assert_eq!(buf.text(), "fn a(b: C) { d.e(f); }");
380 assert!(buf.is_parsing());
381 });
382 buffer.update(cx, |buf, cx| {
383 let offset = buf.text().find("(f)").unwrap();
384 buf.edit([(offset..offset, "::<G>")], None, cx);
385 assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
386 assert!(buf.is_parsing());
387 });
388 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
389 assert_eq!(
390 get_tree_sexp(&buffer, cx),
391 concat!(
392 "(source_file (function_item name: (identifier) ",
393 "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
394 "body: (block (expression_statement (call_expression ",
395 "function: (generic_function ",
396 "function: (field_expression value: (identifier) field: (field_identifier)) ",
397 "type_arguments: (type_arguments (type_identifier))) ",
398 "arguments: (arguments (identifier)))))))",
399 )
400 );
401
402 buffer.update(cx, |buf, cx| {
403 buf.undo(cx);
404 buf.undo(cx);
405 buf.undo(cx);
406 buf.undo(cx);
407 assert_eq!(buf.text(), "fn a() {}");
408 assert!(buf.is_parsing());
409 });
410 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
411 assert_eq!(
412 get_tree_sexp(&buffer, cx),
413 concat!(
414 "(source_file (function_item name: (identifier) ",
415 "parameters: (parameters) ",
416 "body: (block)))"
417 )
418 );
419
420 buffer.update(cx, |buf, cx| {
421 buf.redo(cx);
422 buf.redo(cx);
423 buf.redo(cx);
424 buf.redo(cx);
425 assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
426 assert!(buf.is_parsing());
427 });
428 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
429 assert_eq!(
430 get_tree_sexp(&buffer, cx),
431 concat!(
432 "(source_file (function_item name: (identifier) ",
433 "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
434 "body: (block (expression_statement (call_expression ",
435 "function: (generic_function ",
436 "function: (field_expression value: (identifier) field: (field_identifier)) ",
437 "type_arguments: (type_arguments (type_identifier))) ",
438 "arguments: (arguments (identifier)))))))",
439 )
440 );
441}
442
443#[gpui::test]
444async fn test_resetting_language(cx: &mut gpui::TestAppContext) {
445 let buffer = cx.add_model(|cx| {
446 let mut buffer = Buffer::new(0, "{}", cx).with_language(Arc::new(rust_lang()), cx);
447 buffer.set_sync_parse_timeout(Duration::ZERO);
448 buffer
449 });
450
451 // Wait for the initial text to parse
452 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
453 assert_eq!(
454 get_tree_sexp(&buffer, cx),
455 "(source_file (expression_statement (block)))"
456 );
457
458 buffer.update(cx, |buffer, cx| {
459 buffer.set_language(Some(Arc::new(json_lang())), cx)
460 });
461 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
462 assert_eq!(get_tree_sexp(&buffer, cx), "(document (object))");
463}
464
465#[gpui::test]
466async fn test_outline(cx: &mut gpui::TestAppContext) {
467 let text = r#"
468 struct Person {
469 name: String,
470 age: usize,
471 }
472
473 mod module {
474 enum LoginState {
475 LoggedOut,
476 LoggingOn,
477 LoggedIn {
478 person: Person,
479 time: Instant,
480 }
481 }
482 }
483
484 impl Eq for Person {}
485
486 impl Drop for Person {
487 fn drop(&mut self) {
488 println!("bye");
489 }
490 }
491 "#
492 .unindent();
493
494 let buffer =
495 cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
496 let outline = buffer
497 .read_with(cx, |buffer, _| buffer.snapshot().outline(None))
498 .unwrap();
499
500 assert_eq!(
501 outline
502 .items
503 .iter()
504 .map(|item| (item.text.as_str(), item.depth))
505 .collect::<Vec<_>>(),
506 &[
507 ("struct Person", 0),
508 ("name", 1),
509 ("age", 1),
510 ("mod module", 0),
511 ("enum LoginState", 1),
512 ("LoggedOut", 2),
513 ("LoggingOn", 2),
514 ("LoggedIn", 2),
515 ("person", 3),
516 ("time", 3),
517 ("impl Eq for Person", 0),
518 ("impl Drop for Person", 0),
519 ("fn drop", 1),
520 ]
521 );
522
523 // Without space, we only match on names
524 assert_eq!(
525 search(&outline, "oon", cx).await,
526 &[
527 ("mod module", vec![]), // included as the parent of a match
528 ("enum LoginState", vec![]), // included as the parent of a match
529 ("LoggingOn", vec![1, 7, 8]), // matches
530 ("impl Drop for Person", vec![7, 18, 19]), // matches in two disjoint names
531 ]
532 );
533
534 assert_eq!(
535 search(&outline, "dp p", cx).await,
536 &[
537 ("impl Drop for Person", vec![5, 8, 9, 14]),
538 ("fn drop", vec![]),
539 ]
540 );
541 assert_eq!(
542 search(&outline, "dpn", cx).await,
543 &[("impl Drop for Person", vec![5, 14, 19])]
544 );
545 assert_eq!(
546 search(&outline, "impl ", cx).await,
547 &[
548 ("impl Eq for Person", vec![0, 1, 2, 3, 4]),
549 ("impl Drop for Person", vec![0, 1, 2, 3, 4]),
550 ("fn drop", vec![]),
551 ]
552 );
553
554 async fn search<'a>(
555 outline: &'a Outline<Anchor>,
556 query: &'a str,
557 cx: &'a gpui::TestAppContext,
558 ) -> Vec<(&'a str, Vec<usize>)> {
559 let matches = cx
560 .read(|cx| outline.search(query, cx.background().clone()))
561 .await;
562 matches
563 .into_iter()
564 .map(|mat| (outline.items[mat.candidate_id].text.as_str(), mat.positions))
565 .collect::<Vec<_>>()
566 }
567}
568
569#[gpui::test]
570async fn test_outline_nodes_with_newlines(cx: &mut gpui::TestAppContext) {
571 let text = r#"
572 impl A for B<
573 C
574 > {
575 };
576 "#
577 .unindent();
578
579 let buffer =
580 cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
581 let outline = buffer
582 .read_with(cx, |buffer, _| buffer.snapshot().outline(None))
583 .unwrap();
584
585 assert_eq!(
586 outline
587 .items
588 .iter()
589 .map(|item| (item.text.as_str(), item.depth))
590 .collect::<Vec<_>>(),
591 &[("impl A for B<", 0)]
592 );
593}
594
595#[gpui::test]
596async fn test_outline_with_extra_context(cx: &mut gpui::TestAppContext) {
597 let language = javascript_lang()
598 .with_outline_query(
599 r#"
600 (function_declaration
601 "function" @context
602 name: (_) @name
603 parameters: (formal_parameters
604 "(" @context.extra
605 ")" @context.extra)) @item
606 "#,
607 )
608 .unwrap();
609
610 let text = r#"
611 function a() {}
612 function b(c) {}
613 "#
614 .unindent();
615
616 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(language), cx));
617 let snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot());
618
619 // extra context nodes are included in the outline.
620 let outline = snapshot.outline(None).unwrap();
621 assert_eq!(
622 outline
623 .items
624 .iter()
625 .map(|item| (item.text.as_str(), item.depth))
626 .collect::<Vec<_>>(),
627 &[("function a()", 0), ("function b( )", 0),]
628 );
629
630 // extra context nodes do not appear in breadcrumbs.
631 let symbols = snapshot.symbols_containing(3, None).unwrap();
632 assert_eq!(
633 symbols
634 .iter()
635 .map(|item| (item.text.as_str(), item.depth))
636 .collect::<Vec<_>>(),
637 &[("function a", 0)]
638 );
639}
640
641#[gpui::test]
642async fn test_symbols_containing(cx: &mut gpui::TestAppContext) {
643 let text = r#"
644 impl Person {
645 fn one() {
646 1
647 }
648
649 fn two() {
650 2
651 }fn three() {
652 3
653 }
654 }
655 "#
656 .unindent();
657
658 let buffer =
659 cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
660 let snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot());
661
662 // point is at the start of an item
663 assert_eq!(
664 symbols_containing(Point::new(1, 4), &snapshot),
665 vec![
666 (
667 "impl Person".to_string(),
668 Point::new(0, 0)..Point::new(10, 1)
669 ),
670 ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
671 ]
672 );
673
674 // point is in the middle of an item
675 assert_eq!(
676 symbols_containing(Point::new(2, 8), &snapshot),
677 vec![
678 (
679 "impl Person".to_string(),
680 Point::new(0, 0)..Point::new(10, 1)
681 ),
682 ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
683 ]
684 );
685
686 // point is at the end of an item
687 assert_eq!(
688 symbols_containing(Point::new(3, 5), &snapshot),
689 vec![
690 (
691 "impl Person".to_string(),
692 Point::new(0, 0)..Point::new(10, 1)
693 ),
694 ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
695 ]
696 );
697
698 // point is in between two adjacent items
699 assert_eq!(
700 symbols_containing(Point::new(7, 5), &snapshot),
701 vec![
702 (
703 "impl Person".to_string(),
704 Point::new(0, 0)..Point::new(10, 1)
705 ),
706 ("fn two".to_string(), Point::new(5, 4)..Point::new(7, 5))
707 ]
708 );
709
710 fn symbols_containing(
711 position: Point,
712 snapshot: &BufferSnapshot,
713 ) -> Vec<(String, Range<Point>)> {
714 snapshot
715 .symbols_containing(position, None)
716 .unwrap()
717 .into_iter()
718 .map(|item| {
719 (
720 item.text,
721 item.range.start.to_point(snapshot)..item.range.end.to_point(snapshot),
722 )
723 })
724 .collect()
725 }
726}
727
728#[gpui::test]
729fn test_enclosing_bracket_ranges(cx: &mut AppContext) {
730 let mut assert = |selection_text, range_markers| {
731 assert_bracket_pairs(selection_text, range_markers, rust_lang(), cx)
732 };
733
734 assert(
735 indoc! {"
736 mod x {
737 moˇd y {
738
739 }
740 }
741 let foo = 1;"},
742 vec![indoc! {"
743 mod x «{»
744 mod y {
745
746 }
747 «}»
748 let foo = 1;"}],
749 );
750
751 assert(
752 indoc! {"
753 mod x {
754 mod y ˇ{
755
756 }
757 }
758 let foo = 1;"},
759 vec![
760 indoc! {"
761 mod x «{»
762 mod y {
763
764 }
765 «}»
766 let foo = 1;"},
767 indoc! {"
768 mod x {
769 mod y «{»
770
771 «}»
772 }
773 let foo = 1;"},
774 ],
775 );
776
777 assert(
778 indoc! {"
779 mod x {
780 mod y {
781
782 }ˇ
783 }
784 let foo = 1;"},
785 vec![
786 indoc! {"
787 mod x «{»
788 mod y {
789
790 }
791 «}»
792 let foo = 1;"},
793 indoc! {"
794 mod x {
795 mod y «{»
796
797 «}»
798 }
799 let foo = 1;"},
800 ],
801 );
802
803 assert(
804 indoc! {"
805 mod x {
806 mod y {
807
808 }
809 ˇ}
810 let foo = 1;"},
811 vec![indoc! {"
812 mod x «{»
813 mod y {
814
815 }
816 «}»
817 let foo = 1;"}],
818 );
819
820 assert(
821 indoc! {"
822 mod x {
823 mod y {
824
825 }
826 }
827 let fˇoo = 1;"},
828 vec![],
829 );
830
831 // Regression test: avoid crash when querying at the end of the buffer.
832 assert(
833 indoc! {"
834 mod x {
835 mod y {
836
837 }
838 }
839 let foo = 1;ˇ"},
840 vec![],
841 );
842}
843
844#[gpui::test]
845fn test_enclosing_bracket_ranges_where_brackets_are_not_outermost_children(cx: &mut AppContext) {
846 let mut assert = |selection_text, bracket_pair_texts| {
847 assert_bracket_pairs(selection_text, bracket_pair_texts, javascript_lang(), cx)
848 };
849
850 assert(
851 indoc! {"
852 for (const a in b)ˇ {
853 // a comment that's longer than the for-loop header
854 }"},
855 vec![indoc! {"
856 for «(»const a in b«)» {
857 // a comment that's longer than the for-loop header
858 }"}],
859 );
860
861 // Regression test: even though the parent node of the parentheses (the for loop) does
862 // intersect the given range, the parentheses themselves do not contain the range, so
863 // they should not be returned. Only the curly braces contain the range.
864 assert(
865 indoc! {"
866 for (const a in b) {ˇ
867 // a comment that's longer than the for-loop header
868 }"},
869 vec![indoc! {"
870 for (const a in b) «{»
871 // a comment that's longer than the for-loop header
872 «}»"}],
873 );
874}
875
876#[gpui::test]
877fn test_range_for_syntax_ancestor(cx: &mut AppContext) {
878 cx.add_model(|cx| {
879 let text = "fn a() { b(|c| {}) }";
880 let buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
881 let snapshot = buffer.snapshot();
882
883 assert_eq!(
884 snapshot.range_for_syntax_ancestor(empty_range_at(text, "|")),
885 Some(range_of(text, "|"))
886 );
887 assert_eq!(
888 snapshot.range_for_syntax_ancestor(range_of(text, "|")),
889 Some(range_of(text, "|c|"))
890 );
891 assert_eq!(
892 snapshot.range_for_syntax_ancestor(range_of(text, "|c|")),
893 Some(range_of(text, "|c| {}"))
894 );
895 assert_eq!(
896 snapshot.range_for_syntax_ancestor(range_of(text, "|c| {}")),
897 Some(range_of(text, "(|c| {})"))
898 );
899
900 buffer
901 });
902
903 fn empty_range_at(text: &str, part: &str) -> Range<usize> {
904 let start = text.find(part).unwrap();
905 start..start
906 }
907
908 fn range_of(text: &str, part: &str) -> Range<usize> {
909 let start = text.find(part).unwrap();
910 start..start + part.len()
911 }
912}
913
914#[gpui::test]
915fn test_autoindent_with_soft_tabs(cx: &mut AppContext) {
916 init_settings(cx, |_| {});
917
918 cx.add_model(|cx| {
919 let text = "fn a() {}";
920 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
921
922 buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
923 assert_eq!(buffer.text(), "fn a() {\n \n}");
924
925 buffer.edit(
926 [(Point::new(1, 4)..Point::new(1, 4), "b()\n")],
927 Some(AutoindentMode::EachLine),
928 cx,
929 );
930 assert_eq!(buffer.text(), "fn a() {\n b()\n \n}");
931
932 // Create a field expression on a new line, causing that line
933 // to be indented.
934 buffer.edit(
935 [(Point::new(2, 4)..Point::new(2, 4), ".c")],
936 Some(AutoindentMode::EachLine),
937 cx,
938 );
939 assert_eq!(buffer.text(), "fn a() {\n b()\n .c\n}");
940
941 // Remove the dot so that the line is no longer a field expression,
942 // causing the line to be outdented.
943 buffer.edit(
944 [(Point::new(2, 8)..Point::new(2, 9), "")],
945 Some(AutoindentMode::EachLine),
946 cx,
947 );
948 assert_eq!(buffer.text(), "fn a() {\n b()\n c\n}");
949
950 buffer
951 });
952}
953
954#[gpui::test]
955fn test_autoindent_with_hard_tabs(cx: &mut AppContext) {
956 init_settings(cx, |settings| {
957 settings.defaults.hard_tabs = Some(true);
958 });
959
960 cx.add_model(|cx| {
961 let text = "fn a() {}";
962 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
963
964 buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
965 assert_eq!(buffer.text(), "fn a() {\n\t\n}");
966
967 buffer.edit(
968 [(Point::new(1, 1)..Point::new(1, 1), "b()\n")],
969 Some(AutoindentMode::EachLine),
970 cx,
971 );
972 assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\n}");
973
974 // Create a field expression on a new line, causing that line
975 // to be indented.
976 buffer.edit(
977 [(Point::new(2, 1)..Point::new(2, 1), ".c")],
978 Some(AutoindentMode::EachLine),
979 cx,
980 );
981 assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\t.c\n}");
982
983 // Remove the dot so that the line is no longer a field expression,
984 // causing the line to be outdented.
985 buffer.edit(
986 [(Point::new(2, 2)..Point::new(2, 3), "")],
987 Some(AutoindentMode::EachLine),
988 cx,
989 );
990 assert_eq!(buffer.text(), "fn a() {\n\tb()\n\tc\n}");
991
992 buffer
993 });
994}
995
996#[gpui::test]
997fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut AppContext) {
998 init_settings(cx, |_| {});
999
1000 cx.add_model(|cx| {
1001 let mut buffer = Buffer::new(
1002 0,
1003 "
1004 fn a() {
1005 c;
1006 d;
1007 }
1008 "
1009 .unindent(),
1010 cx,
1011 )
1012 .with_language(Arc::new(rust_lang()), cx);
1013
1014 // Lines 2 and 3 don't match the indentation suggestion. When editing these lines,
1015 // their indentation is not adjusted.
1016 buffer.edit_via_marked_text(
1017 &"
1018 fn a() {
1019 c«()»;
1020 d«()»;
1021 }
1022 "
1023 .unindent(),
1024 Some(AutoindentMode::EachLine),
1025 cx,
1026 );
1027 assert_eq!(
1028 buffer.text(),
1029 "
1030 fn a() {
1031 c();
1032 d();
1033 }
1034 "
1035 .unindent()
1036 );
1037
1038 // When appending new content after these lines, the indentation is based on the
1039 // preceding lines' actual indentation.
1040 buffer.edit_via_marked_text(
1041 &"
1042 fn a() {
1043 c«
1044 .f
1045 .g()»;
1046 d«
1047 .f
1048 .g()»;
1049 }
1050 "
1051 .unindent(),
1052 Some(AutoindentMode::EachLine),
1053 cx,
1054 );
1055
1056 assert_eq!(
1057 buffer.text(),
1058 "
1059 fn a() {
1060 c
1061 .f
1062 .g();
1063 d
1064 .f
1065 .g();
1066 }
1067 "
1068 .unindent()
1069 );
1070 buffer
1071 });
1072
1073 cx.add_model(|cx| {
1074 let mut buffer = Buffer::new(
1075 0,
1076 "
1077 fn a() {
1078 b();
1079 |
1080 "
1081 .replace("|", "") // marker to preserve trailing whitespace
1082 .unindent(),
1083 cx,
1084 )
1085 .with_language(Arc::new(rust_lang()), cx);
1086
1087 // Insert a closing brace. It is outdented.
1088 buffer.edit_via_marked_text(
1089 &"
1090 fn a() {
1091 b();
1092 «}»
1093 "
1094 .unindent(),
1095 Some(AutoindentMode::EachLine),
1096 cx,
1097 );
1098 assert_eq!(
1099 buffer.text(),
1100 "
1101 fn a() {
1102 b();
1103 }
1104 "
1105 .unindent()
1106 );
1107
1108 // Manually edit the leading whitespace. The edit is preserved.
1109 buffer.edit_via_marked_text(
1110 &"
1111 fn a() {
1112 b();
1113 « »}
1114 "
1115 .unindent(),
1116 Some(AutoindentMode::EachLine),
1117 cx,
1118 );
1119 assert_eq!(
1120 buffer.text(),
1121 "
1122 fn a() {
1123 b();
1124 }
1125 "
1126 .unindent()
1127 );
1128 buffer
1129 });
1130}
1131
1132#[gpui::test]
1133fn test_autoindent_does_not_adjust_lines_within_newly_created_errors(cx: &mut AppContext) {
1134 init_settings(cx, |_| {});
1135
1136 cx.add_model(|cx| {
1137 let mut buffer = Buffer::new(
1138 0,
1139 "
1140 fn a() {
1141 i
1142 }
1143 "
1144 .unindent(),
1145 cx,
1146 )
1147 .with_language(Arc::new(rust_lang()), cx);
1148
1149 // Regression test: line does not get outdented due to syntax error
1150 buffer.edit_via_marked_text(
1151 &"
1152 fn a() {
1153 i«f let Some(x) = y»
1154 }
1155 "
1156 .unindent(),
1157 Some(AutoindentMode::EachLine),
1158 cx,
1159 );
1160 assert_eq!(
1161 buffer.text(),
1162 "
1163 fn a() {
1164 if let Some(x) = y
1165 }
1166 "
1167 .unindent()
1168 );
1169
1170 buffer.edit_via_marked_text(
1171 &"
1172 fn a() {
1173 if let Some(x) = y« {»
1174 }
1175 "
1176 .unindent(),
1177 Some(AutoindentMode::EachLine),
1178 cx,
1179 );
1180 assert_eq!(
1181 buffer.text(),
1182 "
1183 fn a() {
1184 if let Some(x) = y {
1185 }
1186 "
1187 .unindent()
1188 );
1189
1190 buffer
1191 });
1192}
1193
1194#[gpui::test]
1195fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut AppContext) {
1196 init_settings(cx, |_| {});
1197
1198 cx.add_model(|cx| {
1199 let mut buffer = Buffer::new(
1200 0,
1201 "
1202 fn a() {}
1203 "
1204 .unindent(),
1205 cx,
1206 )
1207 .with_language(Arc::new(rust_lang()), cx);
1208
1209 buffer.edit_via_marked_text(
1210 &"
1211 fn a(«
1212 b») {}
1213 "
1214 .unindent(),
1215 Some(AutoindentMode::EachLine),
1216 cx,
1217 );
1218 assert_eq!(
1219 buffer.text(),
1220 "
1221 fn a(
1222 b) {}
1223 "
1224 .unindent()
1225 );
1226
1227 // The indentation suggestion changed because `@end` node (a close paren)
1228 // is now at the beginning of the line.
1229 buffer.edit_via_marked_text(
1230 &"
1231 fn a(
1232 ˇ) {}
1233 "
1234 .unindent(),
1235 Some(AutoindentMode::EachLine),
1236 cx,
1237 );
1238 assert_eq!(
1239 buffer.text(),
1240 "
1241 fn a(
1242 ) {}
1243 "
1244 .unindent()
1245 );
1246
1247 buffer
1248 });
1249}
1250
1251#[gpui::test]
1252fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut AppContext) {
1253 init_settings(cx, |_| {});
1254
1255 cx.add_model(|cx| {
1256 let text = "a\nb";
1257 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
1258 buffer.edit(
1259 [(0..1, "\n"), (2..3, "\n")],
1260 Some(AutoindentMode::EachLine),
1261 cx,
1262 );
1263 assert_eq!(buffer.text(), "\n\n\n");
1264 buffer
1265 });
1266}
1267
1268#[gpui::test]
1269fn test_autoindent_multi_line_insertion(cx: &mut AppContext) {
1270 init_settings(cx, |_| {});
1271
1272 cx.add_model(|cx| {
1273 let text = "
1274 const a: usize = 1;
1275 fn b() {
1276 if c {
1277 let d = 2;
1278 }
1279 }
1280 "
1281 .unindent();
1282
1283 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
1284 buffer.edit(
1285 [(Point::new(3, 0)..Point::new(3, 0), "e(\n f()\n);\n")],
1286 Some(AutoindentMode::EachLine),
1287 cx,
1288 );
1289 assert_eq!(
1290 buffer.text(),
1291 "
1292 const a: usize = 1;
1293 fn b() {
1294 if c {
1295 e(
1296 f()
1297 );
1298 let d = 2;
1299 }
1300 }
1301 "
1302 .unindent()
1303 );
1304
1305 buffer
1306 });
1307}
1308
1309#[gpui::test]
1310fn test_autoindent_block_mode(cx: &mut AppContext) {
1311 init_settings(cx, |_| {});
1312
1313 cx.add_model(|cx| {
1314 let text = r#"
1315 fn a() {
1316 b();
1317 }
1318 "#
1319 .unindent();
1320 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
1321
1322 // When this text was copied, both of the quotation marks were at the same
1323 // indent level, but the indentation of the first line was not included in
1324 // the copied text. This information is retained in the
1325 // 'original_indent_columns' vector.
1326 let original_indent_columns = vec![4];
1327 let inserted_text = r#"
1328 "
1329 c
1330 d
1331 e
1332 "
1333 "#
1334 .unindent();
1335
1336 // Insert the block at column zero. The entire block is indented
1337 // so that the first line matches the previous line's indentation.
1338 buffer.edit(
1339 [(Point::new(2, 0)..Point::new(2, 0), inserted_text.clone())],
1340 Some(AutoindentMode::Block {
1341 original_indent_columns: original_indent_columns.clone(),
1342 }),
1343 cx,
1344 );
1345 assert_eq!(
1346 buffer.text(),
1347 r#"
1348 fn a() {
1349 b();
1350 "
1351 c
1352 d
1353 e
1354 "
1355 }
1356 "#
1357 .unindent()
1358 );
1359
1360 // Grouping is disabled in tests, so we need 2 undos
1361 buffer.undo(cx); // Undo the auto-indent
1362 buffer.undo(cx); // Undo the original edit
1363
1364 // Insert the block at a deeper indent level. The entire block is outdented.
1365 buffer.edit([(Point::new(2, 0)..Point::new(2, 0), " ")], None, cx);
1366 buffer.edit(
1367 [(Point::new(2, 8)..Point::new(2, 8), inserted_text)],
1368 Some(AutoindentMode::Block {
1369 original_indent_columns: original_indent_columns.clone(),
1370 }),
1371 cx,
1372 );
1373 assert_eq!(
1374 buffer.text(),
1375 r#"
1376 fn a() {
1377 b();
1378 "
1379 c
1380 d
1381 e
1382 "
1383 }
1384 "#
1385 .unindent()
1386 );
1387
1388 buffer
1389 });
1390}
1391
1392#[gpui::test]
1393fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut AppContext) {
1394 init_settings(cx, |_| {});
1395
1396 cx.add_model(|cx| {
1397 let text = r#"
1398 fn a() {
1399 if b() {
1400
1401 }
1402 }
1403 "#
1404 .unindent();
1405 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
1406
1407 // The original indent columns are not known, so this text is
1408 // auto-indented in a block as if the first line was copied in
1409 // its entirety.
1410 let original_indent_columns = Vec::new();
1411 let inserted_text = " c\n .d()\n .e();";
1412
1413 // Insert the block at column zero. The entire block is indented
1414 // so that the first line matches the previous line's indentation.
1415 buffer.edit(
1416 [(Point::new(2, 0)..Point::new(2, 0), inserted_text.clone())],
1417 Some(AutoindentMode::Block {
1418 original_indent_columns: original_indent_columns.clone(),
1419 }),
1420 cx,
1421 );
1422 assert_eq!(
1423 buffer.text(),
1424 r#"
1425 fn a() {
1426 if b() {
1427 c
1428 .d()
1429 .e();
1430 }
1431 }
1432 "#
1433 .unindent()
1434 );
1435
1436 // Grouping is disabled in tests, so we need 2 undos
1437 buffer.undo(cx); // Undo the auto-indent
1438 buffer.undo(cx); // Undo the original edit
1439
1440 // Insert the block at a deeper indent level. The entire block is outdented.
1441 buffer.edit(
1442 [(Point::new(2, 0)..Point::new(2, 0), " ".repeat(12))],
1443 None,
1444 cx,
1445 );
1446 buffer.edit(
1447 [(Point::new(2, 12)..Point::new(2, 12), inserted_text)],
1448 Some(AutoindentMode::Block {
1449 original_indent_columns: Vec::new(),
1450 }),
1451 cx,
1452 );
1453 assert_eq!(
1454 buffer.text(),
1455 r#"
1456 fn a() {
1457 if b() {
1458 c
1459 .d()
1460 .e();
1461 }
1462 }
1463 "#
1464 .unindent()
1465 );
1466
1467 buffer
1468 });
1469}
1470
1471#[gpui::test]
1472fn test_autoindent_language_without_indents_query(cx: &mut AppContext) {
1473 init_settings(cx, |_| {});
1474
1475 cx.add_model(|cx| {
1476 let text = "
1477 * one
1478 - a
1479 - b
1480 * two
1481 "
1482 .unindent();
1483
1484 let mut buffer = Buffer::new(0, text, cx).with_language(
1485 Arc::new(Language::new(
1486 LanguageConfig {
1487 name: "Markdown".into(),
1488 auto_indent_using_last_non_empty_line: false,
1489 ..Default::default()
1490 },
1491 Some(tree_sitter_json::language()),
1492 )),
1493 cx,
1494 );
1495 buffer.edit(
1496 [(Point::new(3, 0)..Point::new(3, 0), "\n")],
1497 Some(AutoindentMode::EachLine),
1498 cx,
1499 );
1500 assert_eq!(
1501 buffer.text(),
1502 "
1503 * one
1504 - a
1505 - b
1506
1507 * two
1508 "
1509 .unindent()
1510 );
1511 buffer
1512 });
1513}
1514
1515#[gpui::test]
1516fn test_autoindent_with_injected_languages(cx: &mut AppContext) {
1517 init_settings(cx, |settings| {
1518 settings.languages.extend([
1519 (
1520 "HTML".into(),
1521 LanguageSettingsContent {
1522 tab_size: Some(2.try_into().unwrap()),
1523 ..Default::default()
1524 },
1525 ),
1526 (
1527 "JavaScript".into(),
1528 LanguageSettingsContent {
1529 tab_size: Some(8.try_into().unwrap()),
1530 ..Default::default()
1531 },
1532 ),
1533 ])
1534 });
1535
1536 let html_language = Arc::new(html_lang());
1537
1538 let javascript_language = Arc::new(javascript_lang());
1539
1540 let language_registry = Arc::new(LanguageRegistry::test());
1541 language_registry.add(html_language.clone());
1542 language_registry.add(javascript_language.clone());
1543
1544 cx.add_model(|cx| {
1545 let (text, ranges) = marked_text_ranges(
1546 &"
1547 <div>ˇ
1548 </div>
1549 <script>
1550 init({ˇ
1551 })
1552 </script>
1553 <span>ˇ
1554 </span>
1555 "
1556 .unindent(),
1557 false,
1558 );
1559
1560 let mut buffer = Buffer::new(0, text, cx);
1561 buffer.set_language_registry(language_registry);
1562 buffer.set_language(Some(html_language), cx);
1563 buffer.edit(
1564 ranges.into_iter().map(|range| (range, "\na")),
1565 Some(AutoindentMode::EachLine),
1566 cx,
1567 );
1568 assert_eq!(
1569 buffer.text(),
1570 "
1571 <div>
1572 a
1573 </div>
1574 <script>
1575 init({
1576 a
1577 })
1578 </script>
1579 <span>
1580 a
1581 </span>
1582 "
1583 .unindent()
1584 );
1585 buffer
1586 });
1587}
1588
1589#[gpui::test]
1590fn test_autoindent_query_with_outdent_captures(cx: &mut AppContext) {
1591 init_settings(cx, |settings| {
1592 settings.defaults.tab_size = Some(2.try_into().unwrap());
1593 });
1594
1595 cx.add_model(|cx| {
1596 let mut buffer = Buffer::new(0, "", cx).with_language(Arc::new(ruby_lang()), cx);
1597
1598 let text = r#"
1599 class C
1600 def a(b, c)
1601 puts b
1602 puts c
1603 rescue
1604 puts "errored"
1605 exit 1
1606 end
1607 end
1608 "#
1609 .unindent();
1610
1611 buffer.edit([(0..0, text)], Some(AutoindentMode::EachLine), cx);
1612
1613 assert_eq!(
1614 buffer.text(),
1615 r#"
1616 class C
1617 def a(b, c)
1618 puts b
1619 puts c
1620 rescue
1621 puts "errored"
1622 exit 1
1623 end
1624 end
1625 "#
1626 .unindent()
1627 );
1628
1629 buffer
1630 });
1631}
1632
1633#[gpui::test]
1634fn test_language_scope_at(cx: &mut AppContext) {
1635 init_settings(cx, |_| {});
1636
1637 cx.add_model(|cx| {
1638 let language = Language::new(
1639 LanguageConfig {
1640 name: "JavaScript".into(),
1641 line_comment: Some("// ".into()),
1642 brackets: BracketPairConfig {
1643 pairs: vec![
1644 BracketPair {
1645 start: "{".into(),
1646 end: "}".into(),
1647 close: true,
1648 newline: false,
1649 },
1650 BracketPair {
1651 start: "'".into(),
1652 end: "'".into(),
1653 close: true,
1654 newline: false,
1655 },
1656 ],
1657 disabled_scopes_by_bracket_ix: vec![
1658 Vec::new(), //
1659 vec!["string".into()],
1660 ],
1661 },
1662 overrides: [(
1663 "element".into(),
1664 LanguageConfigOverride {
1665 line_comment: Override::Remove { remove: true },
1666 block_comment: Override::Set(("{/*".into(), "*/}".into())),
1667 ..Default::default()
1668 },
1669 )]
1670 .into_iter()
1671 .collect(),
1672 ..Default::default()
1673 },
1674 Some(tree_sitter_javascript::language()),
1675 )
1676 .with_override_query(
1677 r#"
1678 (jsx_element) @element
1679 (string) @string
1680 "#,
1681 )
1682 .unwrap();
1683
1684 let text = r#"a["b"] = <C d="e"></C>;"#;
1685
1686 let buffer = Buffer::new(0, text, cx).with_language(Arc::new(language), cx);
1687 let snapshot = buffer.snapshot();
1688
1689 let config = snapshot.language_scope_at(0).unwrap();
1690 assert_eq!(config.line_comment_prefix().unwrap().as_ref(), "// ");
1691 // Both bracket pairs are enabled
1692 assert_eq!(
1693 config.brackets().map(|e| e.1).collect::<Vec<_>>(),
1694 &[true, true]
1695 );
1696
1697 let string_config = snapshot.language_scope_at(3).unwrap();
1698 assert_eq!(string_config.line_comment_prefix().unwrap().as_ref(), "// ");
1699 // Second bracket pair is disabled
1700 assert_eq!(
1701 string_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
1702 &[true, false]
1703 );
1704
1705 let element_config = snapshot.language_scope_at(10).unwrap();
1706 assert_eq!(element_config.line_comment_prefix(), None);
1707 assert_eq!(
1708 element_config.block_comment_delimiters(),
1709 Some((&"{/*".into(), &"*/}".into()))
1710 );
1711 // Both bracket pairs are enabled
1712 assert_eq!(
1713 element_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
1714 &[true, true]
1715 );
1716
1717 buffer
1718 });
1719}
1720
1721#[gpui::test]
1722fn test_language_scope_at_with_combined_injections(cx: &mut AppContext) {
1723 init_settings(cx, |_| {});
1724
1725 cx.add_model(|cx| {
1726 let text = r#"
1727 <ol>
1728 <% people.each do |person| %>
1729 <li>
1730 <%= person.name %>
1731 </li>
1732 <% end %>
1733 </ol>
1734 "#
1735 .unindent();
1736
1737 let language_registry = Arc::new(LanguageRegistry::test());
1738 language_registry.add(Arc::new(ruby_lang()));
1739 language_registry.add(Arc::new(html_lang()));
1740 language_registry.add(Arc::new(erb_lang()));
1741
1742 let mut buffer = Buffer::new(0, text, cx);
1743 buffer.set_language_registry(language_registry.clone());
1744 buffer.set_language(
1745 language_registry
1746 .language_for_name("ERB")
1747 .now_or_never()
1748 .unwrap()
1749 .ok(),
1750 cx,
1751 );
1752
1753 let snapshot = buffer.snapshot();
1754 let html_config = snapshot.language_scope_at(Point::new(2, 4)).unwrap();
1755 assert_eq!(html_config.line_comment_prefix(), None);
1756 assert_eq!(
1757 html_config.block_comment_delimiters(),
1758 Some((&"<!--".into(), &"-->".into()))
1759 );
1760
1761 let ruby_config = snapshot.language_scope_at(Point::new(3, 12)).unwrap();
1762 assert_eq!(ruby_config.line_comment_prefix().unwrap().as_ref(), "# ");
1763 assert_eq!(ruby_config.block_comment_delimiters(), None);
1764
1765 buffer
1766 });
1767}
1768
1769#[gpui::test]
1770fn test_serialization(cx: &mut gpui::AppContext) {
1771 let mut now = Instant::now();
1772
1773 let buffer1 = cx.add_model(|cx| {
1774 let mut buffer = Buffer::new(0, "abc", cx);
1775 buffer.edit([(3..3, "D")], None, cx);
1776
1777 now += Duration::from_secs(1);
1778 buffer.start_transaction_at(now);
1779 buffer.edit([(4..4, "E")], None, cx);
1780 buffer.end_transaction_at(now, cx);
1781 assert_eq!(buffer.text(), "abcDE");
1782
1783 buffer.undo(cx);
1784 assert_eq!(buffer.text(), "abcD");
1785
1786 buffer.edit([(4..4, "F")], None, cx);
1787 assert_eq!(buffer.text(), "abcDF");
1788 buffer
1789 });
1790 assert_eq!(buffer1.read(cx).text(), "abcDF");
1791
1792 let state = buffer1.read(cx).to_proto();
1793 let ops = cx
1794 .background()
1795 .block(buffer1.read(cx).serialize_ops(None, cx));
1796 let buffer2 = cx.add_model(|cx| {
1797 let mut buffer = Buffer::from_proto(1, state, None).unwrap();
1798 buffer
1799 .apply_ops(
1800 ops.into_iter()
1801 .map(|op| proto::deserialize_operation(op).unwrap()),
1802 cx,
1803 )
1804 .unwrap();
1805 buffer
1806 });
1807 assert_eq!(buffer2.read(cx).text(), "abcDF");
1808}
1809
1810#[gpui::test(iterations = 100)]
1811fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
1812 let min_peers = env::var("MIN_PEERS")
1813 .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
1814 .unwrap_or(1);
1815 let max_peers = env::var("MAX_PEERS")
1816 .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
1817 .unwrap_or(5);
1818 let operations = env::var("OPERATIONS")
1819 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1820 .unwrap_or(10);
1821
1822 let base_text_len = rng.gen_range(0..10);
1823 let base_text = RandomCharIter::new(&mut rng)
1824 .take(base_text_len)
1825 .collect::<String>();
1826 let mut replica_ids = Vec::new();
1827 let mut buffers = Vec::new();
1828 let network = Rc::new(RefCell::new(Network::new(rng.clone())));
1829 let base_buffer = cx.add_model(|cx| Buffer::new(0, base_text.as_str(), cx));
1830
1831 for i in 0..rng.gen_range(min_peers..=max_peers) {
1832 let buffer = cx.add_model(|cx| {
1833 let state = base_buffer.read(cx).to_proto();
1834 let ops = cx
1835 .background()
1836 .block(base_buffer.read(cx).serialize_ops(None, cx));
1837 let mut buffer = Buffer::from_proto(i as ReplicaId, state, None).unwrap();
1838 buffer
1839 .apply_ops(
1840 ops.into_iter()
1841 .map(|op| proto::deserialize_operation(op).unwrap()),
1842 cx,
1843 )
1844 .unwrap();
1845 buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
1846 let network = network.clone();
1847 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1848 if let Event::Operation(op) = event {
1849 network
1850 .borrow_mut()
1851 .broadcast(buffer.replica_id(), vec![proto::serialize_operation(op)]);
1852 }
1853 })
1854 .detach();
1855 buffer
1856 });
1857 buffers.push(buffer);
1858 replica_ids.push(i as ReplicaId);
1859 network.borrow_mut().add_peer(i as ReplicaId);
1860 log::info!("Adding initial peer with replica id {}", i);
1861 }
1862
1863 log::info!("initial text: {:?}", base_text);
1864
1865 let mut now = Instant::now();
1866 let mut mutation_count = operations;
1867 let mut next_diagnostic_id = 0;
1868 let mut active_selections = BTreeMap::default();
1869 loop {
1870 let replica_index = rng.gen_range(0..replica_ids.len());
1871 let replica_id = replica_ids[replica_index];
1872 let buffer = &mut buffers[replica_index];
1873 let mut new_buffer = None;
1874 match rng.gen_range(0..100) {
1875 0..=29 if mutation_count != 0 => {
1876 buffer.update(cx, |buffer, cx| {
1877 buffer.start_transaction_at(now);
1878 buffer.randomly_edit(&mut rng, 5, cx);
1879 buffer.end_transaction_at(now, cx);
1880 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1881 });
1882 mutation_count -= 1;
1883 }
1884 30..=39 if mutation_count != 0 => {
1885 buffer.update(cx, |buffer, cx| {
1886 if rng.gen_bool(0.2) {
1887 log::info!("peer {} clearing active selections", replica_id);
1888 active_selections.remove(&replica_id);
1889 buffer.remove_active_selections(cx);
1890 } else {
1891 let mut selections = Vec::new();
1892 for id in 0..rng.gen_range(1..=5) {
1893 let range = buffer.random_byte_range(0, &mut rng);
1894 selections.push(Selection {
1895 id,
1896 start: buffer.anchor_before(range.start),
1897 end: buffer.anchor_before(range.end),
1898 reversed: false,
1899 goal: SelectionGoal::None,
1900 });
1901 }
1902 let selections: Arc<[Selection<Anchor>]> = selections.into();
1903 log::info!(
1904 "peer {} setting active selections: {:?}",
1905 replica_id,
1906 selections
1907 );
1908 active_selections.insert(replica_id, selections.clone());
1909 buffer.set_active_selections(selections, false, Default::default(), cx);
1910 }
1911 });
1912 mutation_count -= 1;
1913 }
1914 40..=49 if mutation_count != 0 && replica_id == 0 => {
1915 let entry_count = rng.gen_range(1..=5);
1916 buffer.update(cx, |buffer, cx| {
1917 let diagnostics = DiagnosticSet::new(
1918 (0..entry_count).map(|_| {
1919 let range = buffer.random_byte_range(0, &mut rng);
1920 let range = range.to_point_utf16(buffer);
1921 let range = range.start..range.end;
1922 DiagnosticEntry {
1923 range,
1924 diagnostic: Diagnostic {
1925 message: post_inc(&mut next_diagnostic_id).to_string(),
1926 ..Default::default()
1927 },
1928 }
1929 }),
1930 buffer,
1931 );
1932 log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
1933 buffer.update_diagnostics(LanguageServerId(0), diagnostics, cx);
1934 });
1935 mutation_count -= 1;
1936 }
1937 50..=59 if replica_ids.len() < max_peers => {
1938 let old_buffer_state = buffer.read(cx).to_proto();
1939 let old_buffer_ops = cx
1940 .background()
1941 .block(buffer.read(cx).serialize_ops(None, cx));
1942 let new_replica_id = (0..=replica_ids.len() as ReplicaId)
1943 .filter(|replica_id| *replica_id != buffer.read(cx).replica_id())
1944 .choose(&mut rng)
1945 .unwrap();
1946 log::info!(
1947 "Adding new replica {} (replicating from {})",
1948 new_replica_id,
1949 replica_id
1950 );
1951 new_buffer = Some(cx.add_model(|cx| {
1952 let mut new_buffer =
1953 Buffer::from_proto(new_replica_id, old_buffer_state, None).unwrap();
1954 new_buffer
1955 .apply_ops(
1956 old_buffer_ops
1957 .into_iter()
1958 .map(|op| deserialize_operation(op).unwrap()),
1959 cx,
1960 )
1961 .unwrap();
1962 log::info!(
1963 "New replica {} text: {:?}",
1964 new_buffer.replica_id(),
1965 new_buffer.text()
1966 );
1967 new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
1968 let network = network.clone();
1969 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1970 if let Event::Operation(op) = event {
1971 network.borrow_mut().broadcast(
1972 buffer.replica_id(),
1973 vec![proto::serialize_operation(op)],
1974 );
1975 }
1976 })
1977 .detach();
1978 new_buffer
1979 }));
1980 network.borrow_mut().replicate(replica_id, new_replica_id);
1981
1982 if new_replica_id as usize == replica_ids.len() {
1983 replica_ids.push(new_replica_id);
1984 } else {
1985 let new_buffer = new_buffer.take().unwrap();
1986 while network.borrow().has_unreceived(new_replica_id) {
1987 let ops = network
1988 .borrow_mut()
1989 .receive(new_replica_id)
1990 .into_iter()
1991 .map(|op| proto::deserialize_operation(op).unwrap());
1992 if ops.len() > 0 {
1993 log::info!(
1994 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1995 new_replica_id,
1996 buffer.read(cx).version(),
1997 ops.len(),
1998 ops
1999 );
2000 new_buffer.update(cx, |new_buffer, cx| {
2001 new_buffer.apply_ops(ops, cx).unwrap();
2002 });
2003 }
2004 }
2005 buffers[new_replica_id as usize] = new_buffer;
2006 }
2007 }
2008 60..=69 if mutation_count != 0 => {
2009 buffer.update(cx, |buffer, cx| {
2010 buffer.randomly_undo_redo(&mut rng, cx);
2011 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
2012 });
2013 mutation_count -= 1;
2014 }
2015 _ if network.borrow().has_unreceived(replica_id) => {
2016 let ops = network
2017 .borrow_mut()
2018 .receive(replica_id)
2019 .into_iter()
2020 .map(|op| proto::deserialize_operation(op).unwrap());
2021 if ops.len() > 0 {
2022 log::info!(
2023 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
2024 replica_id,
2025 buffer.read(cx).version(),
2026 ops.len(),
2027 ops
2028 );
2029 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
2030 }
2031 }
2032 _ => {}
2033 }
2034
2035 now += Duration::from_millis(rng.gen_range(0..=200));
2036 buffers.extend(new_buffer);
2037
2038 for buffer in &buffers {
2039 buffer.read(cx).check_invariants();
2040 }
2041
2042 if mutation_count == 0 && network.borrow().is_idle() {
2043 break;
2044 }
2045 }
2046
2047 let first_buffer = buffers[0].read(cx).snapshot();
2048 for buffer in &buffers[1..] {
2049 let buffer = buffer.read(cx).snapshot();
2050 assert_eq!(
2051 buffer.version(),
2052 first_buffer.version(),
2053 "Replica {} version != Replica 0 version",
2054 buffer.replica_id()
2055 );
2056 assert_eq!(
2057 buffer.text(),
2058 first_buffer.text(),
2059 "Replica {} text != Replica 0 text",
2060 buffer.replica_id()
2061 );
2062 assert_eq!(
2063 buffer
2064 .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
2065 .collect::<Vec<_>>(),
2066 first_buffer
2067 .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
2068 .collect::<Vec<_>>(),
2069 "Replica {} diagnostics != Replica 0 diagnostics",
2070 buffer.replica_id()
2071 );
2072 }
2073
2074 for buffer in &buffers {
2075 let buffer = buffer.read(cx).snapshot();
2076 let actual_remote_selections = buffer
2077 .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
2078 .map(|(replica_id, _, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
2079 .collect::<Vec<_>>();
2080 let expected_remote_selections = active_selections
2081 .iter()
2082 .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
2083 .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
2084 .collect::<Vec<_>>();
2085 assert_eq!(
2086 actual_remote_selections,
2087 expected_remote_selections,
2088 "Replica {} remote selections != expected selections",
2089 buffer.replica_id()
2090 );
2091 }
2092}
2093
2094#[test]
2095fn test_contiguous_ranges() {
2096 assert_eq!(
2097 contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
2098 &[1..4, 5..7, 9..13]
2099 );
2100
2101 // Respects the `max_len` parameter
2102 assert_eq!(
2103 contiguous_ranges(
2104 [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
2105 3
2106 )
2107 .collect::<Vec<_>>(),
2108 &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
2109 );
2110}
2111
2112#[gpui::test(iterations = 500)]
2113fn test_trailing_whitespace_ranges(mut rng: StdRng) {
2114 // Generate a random multi-line string containing
2115 // some lines with trailing whitespace.
2116 let mut text = String::new();
2117 for _ in 0..rng.gen_range(0..16) {
2118 for _ in 0..rng.gen_range(0..36) {
2119 text.push(match rng.gen_range(0..10) {
2120 0..=1 => ' ',
2121 3 => '\t',
2122 _ => rng.gen_range('a'..'z'),
2123 });
2124 }
2125 text.push('\n');
2126 }
2127
2128 match rng.gen_range(0..10) {
2129 // sometimes remove the last newline
2130 0..=1 => drop(text.pop()), //
2131
2132 // sometimes add extra newlines
2133 2..=3 => text.push_str(&"\n".repeat(rng.gen_range(1..5))),
2134 _ => {}
2135 }
2136
2137 let rope = Rope::from(text.as_str());
2138 let actual_ranges = trailing_whitespace_ranges(&rope);
2139 let expected_ranges = TRAILING_WHITESPACE_REGEX
2140 .find_iter(&text)
2141 .map(|m| m.range())
2142 .collect::<Vec<_>>();
2143 assert_eq!(
2144 actual_ranges,
2145 expected_ranges,
2146 "wrong ranges for text lines:\n{:?}",
2147 text.split("\n").collect::<Vec<_>>()
2148 );
2149}
2150
2151fn ruby_lang() -> Language {
2152 Language::new(
2153 LanguageConfig {
2154 name: "Ruby".into(),
2155 path_suffixes: vec!["rb".to_string()],
2156 line_comment: Some("# ".into()),
2157 ..Default::default()
2158 },
2159 Some(tree_sitter_ruby::language()),
2160 )
2161 .with_indents_query(
2162 r#"
2163 (class "end" @end) @indent
2164 (method "end" @end) @indent
2165 (rescue) @outdent
2166 (then) @indent
2167 "#,
2168 )
2169 .unwrap()
2170}
2171
2172fn html_lang() -> Language {
2173 Language::new(
2174 LanguageConfig {
2175 name: "HTML".into(),
2176 block_comment: Some(("<!--".into(), "-->".into())),
2177 ..Default::default()
2178 },
2179 Some(tree_sitter_html::language()),
2180 )
2181 .with_indents_query(
2182 "
2183 (element
2184 (start_tag) @start
2185 (end_tag)? @end) @indent
2186 ",
2187 )
2188 .unwrap()
2189 .with_injection_query(
2190 r#"
2191 (script_element
2192 (raw_text) @content
2193 (#set! "language" "javascript"))
2194 "#,
2195 )
2196 .unwrap()
2197}
2198
2199fn erb_lang() -> Language {
2200 Language::new(
2201 LanguageConfig {
2202 name: "ERB".into(),
2203 path_suffixes: vec!["erb".to_string()],
2204 block_comment: Some(("<%#".into(), "%>".into())),
2205 ..Default::default()
2206 },
2207 Some(tree_sitter_embedded_template::language()),
2208 )
2209 .with_injection_query(
2210 r#"
2211 (
2212 (code) @content
2213 (#set! "language" "ruby")
2214 (#set! "combined")
2215 )
2216
2217 (
2218 (content) @content
2219 (#set! "language" "html")
2220 (#set! "combined")
2221 )
2222 "#,
2223 )
2224 .unwrap()
2225}
2226
2227fn rust_lang() -> Language {
2228 Language::new(
2229 LanguageConfig {
2230 name: "Rust".into(),
2231 path_suffixes: vec!["rs".to_string()],
2232 ..Default::default()
2233 },
2234 Some(tree_sitter_rust::language()),
2235 )
2236 .with_indents_query(
2237 r#"
2238 (call_expression) @indent
2239 (field_expression) @indent
2240 (_ "(" ")" @end) @indent
2241 (_ "{" "}" @end) @indent
2242 "#,
2243 )
2244 .unwrap()
2245 .with_brackets_query(
2246 r#"
2247 ("{" @open "}" @close)
2248 "#,
2249 )
2250 .unwrap()
2251 .with_outline_query(
2252 r#"
2253 (struct_item
2254 "struct" @context
2255 name: (_) @name) @item
2256 (enum_item
2257 "enum" @context
2258 name: (_) @name) @item
2259 (enum_variant
2260 name: (_) @name) @item
2261 (field_declaration
2262 name: (_) @name) @item
2263 (impl_item
2264 "impl" @context
2265 trait: (_)? @name
2266 "for"? @context
2267 type: (_) @name) @item
2268 (function_item
2269 "fn" @context
2270 name: (_) @name) @item
2271 (mod_item
2272 "mod" @context
2273 name: (_) @name) @item
2274 "#,
2275 )
2276 .unwrap()
2277}
2278
2279fn json_lang() -> Language {
2280 Language::new(
2281 LanguageConfig {
2282 name: "Json".into(),
2283 path_suffixes: vec!["js".to_string()],
2284 ..Default::default()
2285 },
2286 Some(tree_sitter_json::language()),
2287 )
2288}
2289
2290fn javascript_lang() -> Language {
2291 Language::new(
2292 LanguageConfig {
2293 name: "JavaScript".into(),
2294 ..Default::default()
2295 },
2296 Some(tree_sitter_javascript::language()),
2297 )
2298 .with_brackets_query(
2299 r#"
2300 ("{" @open "}" @close)
2301 ("(" @open ")" @close)
2302 "#,
2303 )
2304 .unwrap()
2305 .with_indents_query(
2306 r#"
2307 (object "}" @end) @indent
2308 "#,
2309 )
2310 .unwrap()
2311}
2312
2313fn get_tree_sexp(buffer: &ModelHandle<Buffer>, cx: &gpui::TestAppContext) -> String {
2314 buffer.read_with(cx, |buffer, _| {
2315 let snapshot = buffer.snapshot();
2316 let layers = snapshot.syntax.layers(buffer.as_text_snapshot());
2317 layers[0].node().to_sexp()
2318 })
2319}
2320
2321// Assert that the enclosing bracket ranges around the selection match the pairs indicated by the marked text in `range_markers`
2322fn assert_bracket_pairs(
2323 selection_text: &'static str,
2324 bracket_pair_texts: Vec<&'static str>,
2325 language: Language,
2326 cx: &mut AppContext,
2327) {
2328 let (expected_text, selection_ranges) = marked_text_ranges(selection_text, false);
2329 let buffer = cx.add_model(|cx| {
2330 Buffer::new(0, expected_text.clone(), cx).with_language(Arc::new(language), cx)
2331 });
2332 let buffer = buffer.update(cx, |buffer, _cx| buffer.snapshot());
2333
2334 let selection_range = selection_ranges[0].clone();
2335
2336 let bracket_pairs = bracket_pair_texts
2337 .into_iter()
2338 .map(|pair_text| {
2339 let (bracket_text, ranges) = marked_text_ranges(pair_text, false);
2340 assert_eq!(bracket_text, expected_text);
2341 (ranges[0].clone(), ranges[1].clone())
2342 })
2343 .collect::<Vec<_>>();
2344
2345 assert_set_eq!(
2346 buffer.bracket_ranges(selection_range).collect::<Vec<_>>(),
2347 bracket_pairs
2348 );
2349}
2350
2351fn init_settings(cx: &mut AppContext, f: fn(&mut AllLanguageSettingsContent)) {
2352 cx.set_global(SettingsStore::test(cx));
2353 crate::init(cx);
2354 cx.update_global::<SettingsStore, _, _>(|settings, cx| {
2355 settings.update_user_settings::<AllLanguageSettings>(cx, f);
2356 });
2357}