1use super::*;
2use clock::ReplicaId;
3use collections::BTreeMap;
4use fs::LineEnding;
5use gpui::{ModelHandle, MutableAppContext};
6use proto::deserialize_operation;
7use rand::prelude::*;
8use settings::Settings;
9use std::{
10 cell::RefCell,
11 env,
12 ops::Range,
13 rc::Rc,
14 time::{Duration, Instant},
15};
16use text::network::Network;
17use unindent::Unindent as _;
18use util::{post_inc, test::marked_text_ranges, RandomCharIter};
19
20#[cfg(test)]
21#[ctor::ctor]
22fn init_logger() {
23 if std::env::var("RUST_LOG").is_ok() {
24 env_logger::init();
25 }
26}
27
28#[gpui::test]
29fn test_line_endings(cx: &mut gpui::MutableAppContext) {
30 cx.set_global(Settings::test(cx));
31 cx.add_model(|cx| {
32 let mut buffer =
33 Buffer::new(0, "one\r\ntwo\rthree", cx).with_language(Arc::new(rust_lang()), cx);
34 assert_eq!(buffer.text(), "one\ntwo\nthree");
35 assert_eq!(buffer.line_ending(), LineEnding::Windows);
36
37 buffer.check_invariants();
38 buffer.edit(
39 [(buffer.len()..buffer.len(), "\r\nfour")],
40 Some(AutoindentMode::EachLine),
41 cx,
42 );
43 buffer.edit([(0..0, "zero\r\n")], None, cx);
44 assert_eq!(buffer.text(), "zero\none\ntwo\nthree\nfour");
45 assert_eq!(buffer.line_ending(), LineEnding::Windows);
46 buffer.check_invariants();
47
48 buffer
49 });
50}
51
52#[gpui::test]
53fn test_select_language() {
54 let registry = LanguageRegistry::test();
55 registry.add(Arc::new(Language::new(
56 LanguageConfig {
57 name: "Rust".into(),
58 path_suffixes: vec!["rs".to_string()],
59 ..Default::default()
60 },
61 Some(tree_sitter_rust::language()),
62 )));
63 registry.add(Arc::new(Language::new(
64 LanguageConfig {
65 name: "Make".into(),
66 path_suffixes: vec!["Makefile".to_string(), "mk".to_string()],
67 ..Default::default()
68 },
69 Some(tree_sitter_rust::language()),
70 )));
71
72 // matching file extension
73 assert_eq!(
74 registry.select_language("zed/lib.rs").map(|l| l.name()),
75 Some("Rust".into())
76 );
77 assert_eq!(
78 registry.select_language("zed/lib.mk").map(|l| l.name()),
79 Some("Make".into())
80 );
81
82 // matching filename
83 assert_eq!(
84 registry.select_language("zed/Makefile").map(|l| l.name()),
85 Some("Make".into())
86 );
87
88 // matching suffix that is not the full file extension or filename
89 assert_eq!(registry.select_language("zed/cars").map(|l| l.name()), None);
90 assert_eq!(
91 registry.select_language("zed/a.cars").map(|l| l.name()),
92 None
93 );
94 assert_eq!(registry.select_language("zed/sumk").map(|l| l.name()), None);
95}
96
97#[gpui::test]
98fn test_edit_events(cx: &mut gpui::MutableAppContext) {
99 let mut now = Instant::now();
100 let buffer_1_events = Rc::new(RefCell::new(Vec::new()));
101 let buffer_2_events = Rc::new(RefCell::new(Vec::new()));
102
103 let buffer1 = cx.add_model(|cx| Buffer::new(0, "abcdef", cx));
104 let buffer2 = cx.add_model(|cx| Buffer::new(1, "abcdef", cx));
105 let buffer1_ops = Rc::new(RefCell::new(Vec::new()));
106 buffer1.update(cx, {
107 let buffer1_ops = buffer1_ops.clone();
108 |buffer, cx| {
109 let buffer_1_events = buffer_1_events.clone();
110 cx.subscribe(&buffer1, move |_, _, event, _| match event.clone() {
111 Event::Operation(op) => buffer1_ops.borrow_mut().push(op),
112 event => buffer_1_events.borrow_mut().push(event),
113 })
114 .detach();
115 let buffer_2_events = buffer_2_events.clone();
116 cx.subscribe(&buffer2, move |_, _, event, _| {
117 buffer_2_events.borrow_mut().push(event.clone())
118 })
119 .detach();
120
121 // An edit emits an edited event, followed by a dirty changed event,
122 // since the buffer was previously in a clean state.
123 buffer.edit([(2..4, "XYZ")], None, cx);
124
125 // An empty transaction does not emit any events.
126 buffer.start_transaction();
127 buffer.end_transaction(cx);
128
129 // A transaction containing two edits emits one edited event.
130 now += Duration::from_secs(1);
131 buffer.start_transaction_at(now);
132 buffer.edit([(5..5, "u")], None, cx);
133 buffer.edit([(6..6, "w")], None, cx);
134 buffer.end_transaction_at(now, cx);
135
136 // Undoing a transaction emits one edited event.
137 buffer.undo(cx);
138 }
139 });
140
141 // Incorporating a set of remote ops emits a single edited event,
142 // followed by a dirty changed event.
143 buffer2.update(cx, |buffer, cx| {
144 buffer
145 .apply_ops(buffer1_ops.borrow_mut().drain(..), cx)
146 .unwrap();
147 });
148 assert_eq!(
149 mem::take(&mut *buffer_1_events.borrow_mut()),
150 vec![
151 Event::Edited,
152 Event::DirtyChanged,
153 Event::Edited,
154 Event::Edited,
155 ]
156 );
157 assert_eq!(
158 mem::take(&mut *buffer_2_events.borrow_mut()),
159 vec![Event::Edited, Event::DirtyChanged]
160 );
161
162 buffer1.update(cx, |buffer, cx| {
163 // Undoing the first transaction emits edited event, followed by a
164 // dirty changed event, since the buffer is again in a clean state.
165 buffer.undo(cx);
166 });
167 // Incorporating the remote ops again emits a single edited event,
168 // followed by a dirty changed event.
169 buffer2.update(cx, |buffer, cx| {
170 buffer
171 .apply_ops(buffer1_ops.borrow_mut().drain(..), cx)
172 .unwrap();
173 });
174 assert_eq!(
175 mem::take(&mut *buffer_1_events.borrow_mut()),
176 vec![Event::Edited, Event::DirtyChanged,]
177 );
178 assert_eq!(
179 mem::take(&mut *buffer_2_events.borrow_mut()),
180 vec![Event::Edited, Event::DirtyChanged]
181 );
182}
183
184#[gpui::test]
185async fn test_apply_diff(cx: &mut gpui::TestAppContext) {
186 let text = "a\nbb\nccc\ndddd\neeeee\nffffff\n";
187 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
188 let anchor = buffer.read_with(cx, |buffer, _| buffer.anchor_before(Point::new(3, 3)));
189
190 let text = "a\nccc\ndddd\nffffff\n";
191 let diff = buffer.read_with(cx, |b, cx| b.diff(text.into(), cx)).await;
192 buffer.update(cx, |buffer, cx| {
193 buffer.apply_diff(diff, cx).unwrap();
194 assert_eq!(buffer.text(), text);
195 assert_eq!(anchor.to_point(buffer), Point::new(2, 3));
196 });
197
198 let text = "a\n1\n\nccc\ndd2dd\nffffff\n";
199 let diff = buffer.read_with(cx, |b, cx| b.diff(text.into(), cx)).await;
200 buffer.update(cx, |buffer, cx| {
201 buffer.apply_diff(diff, cx).unwrap();
202 assert_eq!(buffer.text(), text);
203 assert_eq!(anchor.to_point(buffer), Point::new(4, 4));
204 });
205}
206
207#[gpui::test]
208async fn test_reparse(cx: &mut gpui::TestAppContext) {
209 let text = "fn a() {}";
210 let buffer =
211 cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
212
213 // Wait for the initial text to parse
214 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
215 assert_eq!(
216 get_tree_sexp(&buffer, cx),
217 concat!(
218 "(source_file (function_item name: (identifier) ",
219 "parameters: (parameters) ",
220 "body: (block)))"
221 )
222 );
223
224 buffer.update(cx, |buffer, _| {
225 buffer.set_sync_parse_timeout(Duration::ZERO)
226 });
227
228 // Perform some edits (add parameter and variable reference)
229 // Parsing doesn't begin until the transaction is complete
230 buffer.update(cx, |buf, cx| {
231 buf.start_transaction();
232
233 let offset = buf.text().find(')').unwrap();
234 buf.edit([(offset..offset, "b: C")], None, cx);
235 assert!(!buf.is_parsing());
236
237 let offset = buf.text().find('}').unwrap();
238 buf.edit([(offset..offset, " d; ")], None, cx);
239 assert!(!buf.is_parsing());
240
241 buf.end_transaction(cx);
242 assert_eq!(buf.text(), "fn a(b: C) { d; }");
243 assert!(buf.is_parsing());
244 });
245 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
246 assert_eq!(
247 get_tree_sexp(&buffer, cx),
248 concat!(
249 "(source_file (function_item name: (identifier) ",
250 "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
251 "body: (block (expression_statement (identifier)))))"
252 )
253 );
254
255 // Perform a series of edits without waiting for the current parse to complete:
256 // * turn identifier into a field expression
257 // * turn field expression into a method call
258 // * add a turbofish to the method call
259 buffer.update(cx, |buf, cx| {
260 let offset = buf.text().find(';').unwrap();
261 buf.edit([(offset..offset, ".e")], None, cx);
262 assert_eq!(buf.text(), "fn a(b: C) { d.e; }");
263 assert!(buf.is_parsing());
264 });
265 buffer.update(cx, |buf, cx| {
266 let offset = buf.text().find(';').unwrap();
267 buf.edit([(offset..offset, "(f)")], None, cx);
268 assert_eq!(buf.text(), "fn a(b: C) { d.e(f); }");
269 assert!(buf.is_parsing());
270 });
271 buffer.update(cx, |buf, cx| {
272 let offset = buf.text().find("(f)").unwrap();
273 buf.edit([(offset..offset, "::<G>")], None, cx);
274 assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
275 assert!(buf.is_parsing());
276 });
277 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
278 assert_eq!(
279 get_tree_sexp(&buffer, cx),
280 concat!(
281 "(source_file (function_item name: (identifier) ",
282 "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
283 "body: (block (expression_statement (call_expression ",
284 "function: (generic_function ",
285 "function: (field_expression value: (identifier) field: (field_identifier)) ",
286 "type_arguments: (type_arguments (type_identifier))) ",
287 "arguments: (arguments (identifier)))))))",
288 )
289 );
290
291 buffer.update(cx, |buf, cx| {
292 buf.undo(cx);
293 assert_eq!(buf.text(), "fn a() {}");
294 assert!(buf.is_parsing());
295 });
296 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
297 assert_eq!(
298 get_tree_sexp(&buffer, cx),
299 concat!(
300 "(source_file (function_item name: (identifier) ",
301 "parameters: (parameters) ",
302 "body: (block)))"
303 )
304 );
305
306 buffer.update(cx, |buf, cx| {
307 buf.redo(cx);
308 assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
309 assert!(buf.is_parsing());
310 });
311 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
312 assert_eq!(
313 get_tree_sexp(&buffer, cx),
314 concat!(
315 "(source_file (function_item name: (identifier) ",
316 "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
317 "body: (block (expression_statement (call_expression ",
318 "function: (generic_function ",
319 "function: (field_expression value: (identifier) field: (field_identifier)) ",
320 "type_arguments: (type_arguments (type_identifier))) ",
321 "arguments: (arguments (identifier)))))))",
322 )
323 );
324}
325
326#[gpui::test]
327async fn test_resetting_language(cx: &mut gpui::TestAppContext) {
328 let buffer = cx.add_model(|cx| {
329 let mut buffer = Buffer::new(0, "{}", cx).with_language(Arc::new(rust_lang()), cx);
330 buffer.set_sync_parse_timeout(Duration::ZERO);
331 buffer
332 });
333
334 // Wait for the initial text to parse
335 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
336 assert_eq!(
337 get_tree_sexp(&buffer, cx),
338 "(source_file (expression_statement (block)))"
339 );
340
341 buffer.update(cx, |buffer, cx| {
342 buffer.set_language(Some(Arc::new(json_lang())), cx)
343 });
344 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
345 assert_eq!(get_tree_sexp(&buffer, cx), "(document (object))");
346}
347
348#[gpui::test]
349async fn test_outline(cx: &mut gpui::TestAppContext) {
350 let text = r#"
351 struct Person {
352 name: String,
353 age: usize,
354 }
355
356 mod module {
357 enum LoginState {
358 LoggedOut,
359 LoggingOn,
360 LoggedIn {
361 person: Person,
362 time: Instant,
363 }
364 }
365 }
366
367 impl Eq for Person {}
368
369 impl Drop for Person {
370 fn drop(&mut self) {
371 println!("bye");
372 }
373 }
374 "#
375 .unindent();
376
377 let buffer =
378 cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
379 let outline = buffer
380 .read_with(cx, |buffer, _| buffer.snapshot().outline(None))
381 .unwrap();
382
383 assert_eq!(
384 outline
385 .items
386 .iter()
387 .map(|item| (item.text.as_str(), item.depth))
388 .collect::<Vec<_>>(),
389 &[
390 ("struct Person", 0),
391 ("name", 1),
392 ("age", 1),
393 ("mod module", 0),
394 ("enum LoginState", 1),
395 ("LoggedOut", 2),
396 ("LoggingOn", 2),
397 ("LoggedIn", 2),
398 ("person", 3),
399 ("time", 3),
400 ("impl Eq for Person", 0),
401 ("impl Drop for Person", 0),
402 ("fn drop", 1),
403 ]
404 );
405
406 // Without space, we only match on names
407 assert_eq!(
408 search(&outline, "oon", cx).await,
409 &[
410 ("mod module", vec![]), // included as the parent of a match
411 ("enum LoginState", vec![]), // included as the parent of a match
412 ("LoggingOn", vec![1, 7, 8]), // matches
413 ("impl Drop for Person", vec![7, 18, 19]), // matches in two disjoint names
414 ]
415 );
416
417 assert_eq!(
418 search(&outline, "dp p", cx).await,
419 &[
420 ("impl Drop for Person", vec![5, 8, 9, 14]),
421 ("fn drop", vec![]),
422 ]
423 );
424 assert_eq!(
425 search(&outline, "dpn", cx).await,
426 &[("impl Drop for Person", vec![5, 14, 19])]
427 );
428 assert_eq!(
429 search(&outline, "impl ", cx).await,
430 &[
431 ("impl Eq for Person", vec![0, 1, 2, 3, 4]),
432 ("impl Drop for Person", vec![0, 1, 2, 3, 4]),
433 ("fn drop", vec![]),
434 ]
435 );
436
437 async fn search<'a>(
438 outline: &'a Outline<Anchor>,
439 query: &'a str,
440 cx: &'a gpui::TestAppContext,
441 ) -> Vec<(&'a str, Vec<usize>)> {
442 let matches = cx
443 .read(|cx| outline.search(query, cx.background().clone()))
444 .await;
445 matches
446 .into_iter()
447 .map(|mat| (outline.items[mat.candidate_id].text.as_str(), mat.positions))
448 .collect::<Vec<_>>()
449 }
450}
451
452#[gpui::test]
453async fn test_symbols_containing(cx: &mut gpui::TestAppContext) {
454 let text = r#"
455 impl Person {
456 fn one() {
457 1
458 }
459
460 fn two() {
461 2
462 }fn three() {
463 3
464 }
465 }
466 "#
467 .unindent();
468
469 let buffer =
470 cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
471 let snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot());
472
473 // point is at the start of an item
474 assert_eq!(
475 symbols_containing(Point::new(1, 4), &snapshot),
476 vec![
477 (
478 "impl Person".to_string(),
479 Point::new(0, 0)..Point::new(10, 1)
480 ),
481 ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
482 ]
483 );
484
485 // point is in the middle of an item
486 assert_eq!(
487 symbols_containing(Point::new(2, 8), &snapshot),
488 vec![
489 (
490 "impl Person".to_string(),
491 Point::new(0, 0)..Point::new(10, 1)
492 ),
493 ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
494 ]
495 );
496
497 // point is at the end of an item
498 assert_eq!(
499 symbols_containing(Point::new(3, 5), &snapshot),
500 vec![
501 (
502 "impl Person".to_string(),
503 Point::new(0, 0)..Point::new(10, 1)
504 ),
505 ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
506 ]
507 );
508
509 // point is in between two adjacent items
510 assert_eq!(
511 symbols_containing(Point::new(7, 5), &snapshot),
512 vec![
513 (
514 "impl Person".to_string(),
515 Point::new(0, 0)..Point::new(10, 1)
516 ),
517 ("fn two".to_string(), Point::new(5, 4)..Point::new(7, 5))
518 ]
519 );
520
521 fn symbols_containing(
522 position: Point,
523 snapshot: &BufferSnapshot,
524 ) -> Vec<(String, Range<Point>)> {
525 snapshot
526 .symbols_containing(position, None)
527 .unwrap()
528 .into_iter()
529 .map(|item| {
530 (
531 item.text,
532 item.range.start.to_point(snapshot)..item.range.end.to_point(snapshot),
533 )
534 })
535 .collect()
536 }
537}
538
539#[gpui::test]
540fn test_enclosing_bracket_ranges(cx: &mut MutableAppContext) {
541 cx.set_global(Settings::test(cx));
542 let buffer = cx.add_model(|cx| {
543 let text = "
544 mod x {
545 mod y {
546
547 }
548 }
549 "
550 .unindent();
551 Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx)
552 });
553 let buffer = buffer.read(cx);
554 assert_eq!(
555 buffer.enclosing_bracket_point_ranges(Point::new(1, 6)..Point::new(1, 6)),
556 Some((
557 Point::new(0, 6)..Point::new(0, 7),
558 Point::new(4, 0)..Point::new(4, 1)
559 ))
560 );
561 assert_eq!(
562 buffer.enclosing_bracket_point_ranges(Point::new(1, 10)..Point::new(1, 10)),
563 Some((
564 Point::new(1, 10)..Point::new(1, 11),
565 Point::new(3, 4)..Point::new(3, 5)
566 ))
567 );
568 assert_eq!(
569 buffer.enclosing_bracket_point_ranges(Point::new(3, 5)..Point::new(3, 5)),
570 Some((
571 Point::new(1, 10)..Point::new(1, 11),
572 Point::new(3, 4)..Point::new(3, 5)
573 ))
574 );
575
576 assert_eq!(
577 buffer.enclosing_bracket_point_ranges(Point::new(4, 1)..Point::new(4, 1)),
578 Some((
579 Point::new(0, 6)..Point::new(0, 7),
580 Point::new(4, 0)..Point::new(4, 1)
581 ))
582 );
583
584 // Regression test: avoid crash when querying at the end of the buffer.
585 assert_eq!(
586 buffer.enclosing_bracket_point_ranges(Point::new(4, 1)..Point::new(5, 0)),
587 None
588 );
589}
590
591#[gpui::test]
592fn test_enclosing_bracket_ranges_where_brackets_are_not_outermost_children(
593 cx: &mut MutableAppContext,
594) {
595 let javascript_language = Arc::new(
596 Language::new(
597 LanguageConfig {
598 name: "JavaScript".into(),
599 ..Default::default()
600 },
601 Some(tree_sitter_javascript::language()),
602 )
603 .with_brackets_query(
604 r#"
605 ("{" @open "}" @close)
606 ("(" @open ")" @close)
607 "#,
608 )
609 .unwrap(),
610 );
611
612 cx.set_global(Settings::test(cx));
613 let buffer = cx.add_model(|cx| {
614 let text = "
615 for (const a in b) {
616 // a comment that's longer than the for-loop header
617 }
618 "
619 .unindent();
620 Buffer::new(0, text, cx).with_language(javascript_language, cx)
621 });
622
623 let buffer = buffer.read(cx);
624 assert_eq!(
625 buffer.enclosing_bracket_point_ranges(Point::new(0, 18)..Point::new(0, 18)),
626 Some((
627 Point::new(0, 4)..Point::new(0, 5),
628 Point::new(0, 17)..Point::new(0, 18)
629 ))
630 );
631
632 // Regression test: even though the parent node of the parentheses (the for loop) does
633 // intersect the given range, the parentheses themselves do not contain the range, so
634 // they should not be returned. Only the curly braces contain the range.
635 assert_eq!(
636 buffer.enclosing_bracket_point_ranges(Point::new(0, 20)..Point::new(0, 20)),
637 Some((
638 Point::new(0, 19)..Point::new(0, 20),
639 Point::new(2, 0)..Point::new(2, 1)
640 ))
641 );
642}
643
644#[gpui::test]
645fn test_range_for_syntax_ancestor(cx: &mut MutableAppContext) {
646 cx.add_model(|cx| {
647 let text = "fn a() { b(|c| {}) }";
648 let buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
649 let snapshot = buffer.snapshot();
650
651 assert_eq!(
652 snapshot.range_for_syntax_ancestor(empty_range_at(text, "|")),
653 Some(range_of(text, "|"))
654 );
655 assert_eq!(
656 snapshot.range_for_syntax_ancestor(range_of(text, "|")),
657 Some(range_of(text, "|c|"))
658 );
659 assert_eq!(
660 snapshot.range_for_syntax_ancestor(range_of(text, "|c|")),
661 Some(range_of(text, "|c| {}"))
662 );
663 assert_eq!(
664 snapshot.range_for_syntax_ancestor(range_of(text, "|c| {}")),
665 Some(range_of(text, "(|c| {})"))
666 );
667
668 buffer
669 });
670
671 fn empty_range_at(text: &str, part: &str) -> Range<usize> {
672 let start = text.find(part).unwrap();
673 start..start
674 }
675
676 fn range_of(text: &str, part: &str) -> Range<usize> {
677 let start = text.find(part).unwrap();
678 start..start + part.len()
679 }
680}
681
682#[gpui::test]
683fn test_autoindent_with_soft_tabs(cx: &mut MutableAppContext) {
684 let settings = Settings::test(cx);
685 cx.set_global(settings);
686
687 cx.add_model(|cx| {
688 let text = "fn a() {}";
689 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
690
691 buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
692 assert_eq!(buffer.text(), "fn a() {\n \n}");
693
694 buffer.edit(
695 [(Point::new(1, 4)..Point::new(1, 4), "b()\n")],
696 Some(AutoindentMode::EachLine),
697 cx,
698 );
699 assert_eq!(buffer.text(), "fn a() {\n b()\n \n}");
700
701 // Create a field expression on a new line, causing that line
702 // to be indented.
703 buffer.edit(
704 [(Point::new(2, 4)..Point::new(2, 4), ".c")],
705 Some(AutoindentMode::EachLine),
706 cx,
707 );
708 assert_eq!(buffer.text(), "fn a() {\n b()\n .c\n}");
709
710 // Remove the dot so that the line is no longer a field expression,
711 // causing the line to be outdented.
712 buffer.edit(
713 [(Point::new(2, 8)..Point::new(2, 9), "")],
714 Some(AutoindentMode::EachLine),
715 cx,
716 );
717 assert_eq!(buffer.text(), "fn a() {\n b()\n c\n}");
718
719 buffer
720 });
721}
722
723#[gpui::test]
724fn test_autoindent_with_hard_tabs(cx: &mut MutableAppContext) {
725 let mut settings = Settings::test(cx);
726 settings.editor_overrides.hard_tabs = Some(true);
727 cx.set_global(settings);
728
729 cx.add_model(|cx| {
730 let text = "fn a() {}";
731 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
732
733 buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
734 assert_eq!(buffer.text(), "fn a() {\n\t\n}");
735
736 buffer.edit(
737 [(Point::new(1, 1)..Point::new(1, 1), "b()\n")],
738 Some(AutoindentMode::EachLine),
739 cx,
740 );
741 assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\n}");
742
743 // Create a field expression on a new line, causing that line
744 // to be indented.
745 buffer.edit(
746 [(Point::new(2, 1)..Point::new(2, 1), ".c")],
747 Some(AutoindentMode::EachLine),
748 cx,
749 );
750 assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\t.c\n}");
751
752 // Remove the dot so that the line is no longer a field expression,
753 // causing the line to be outdented.
754 buffer.edit(
755 [(Point::new(2, 2)..Point::new(2, 3), "")],
756 Some(AutoindentMode::EachLine),
757 cx,
758 );
759 assert_eq!(buffer.text(), "fn a() {\n\tb()\n\tc\n}");
760
761 buffer
762 });
763}
764
765#[gpui::test]
766fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut MutableAppContext) {
767 let settings = Settings::test(cx);
768 cx.set_global(settings);
769
770 cx.add_model(|cx| {
771 let text = "
772 fn a() {
773 c;
774 d;
775 }
776 "
777 .unindent();
778
779 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
780
781 // Lines 2 and 3 don't match the indentation suggestion. When editing these lines,
782 // their indentation is not adjusted.
783 buffer.edit(
784 [
785 (empty(Point::new(1, 1)), "()"),
786 (empty(Point::new(2, 1)), "()"),
787 ],
788 Some(AutoindentMode::EachLine),
789 cx,
790 );
791 assert_eq!(
792 buffer.text(),
793 "
794 fn a() {
795 c();
796 d();
797 }
798 "
799 .unindent()
800 );
801
802 // When appending new content after these lines, the indentation is based on the
803 // preceding lines' actual indentation.
804 buffer.edit(
805 [
806 (empty(Point::new(1, 1)), "\n.f\n.g"),
807 (empty(Point::new(2, 1)), "\n.f\n.g"),
808 ],
809 Some(AutoindentMode::EachLine),
810 cx,
811 );
812 assert_eq!(
813 buffer.text(),
814 "
815 fn a() {
816 c
817 .f
818 .g();
819 d
820 .f
821 .g();
822 }
823 "
824 .unindent()
825 );
826 buffer
827 });
828
829 cx.add_model(|cx| {
830 let text = "
831 fn a() {
832 {
833 b()?
834 }
835 Ok(())
836 }
837 "
838 .unindent();
839 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
840
841 // Delete a closing curly brace changes the suggested indent for the line.
842 buffer.edit(
843 [(Point::new(3, 4)..Point::new(3, 5), "")],
844 Some(AutoindentMode::EachLine),
845 cx,
846 );
847 assert_eq!(
848 buffer.text(),
849 "
850 fn a() {
851 {
852 b()?
853 |
854 Ok(())
855 }
856 "
857 .replace('|', "") // included in the string to preserve trailing whites
858 .unindent()
859 );
860
861 // Manually editing the leading whitespace
862 buffer.edit(
863 [(Point::new(3, 0)..Point::new(3, 12), "")],
864 Some(AutoindentMode::EachLine),
865 cx,
866 );
867 assert_eq!(
868 buffer.text(),
869 "
870 fn a() {
871 {
872 b()?
873
874 Ok(())
875 }
876 "
877 .unindent()
878 );
879 buffer
880 });
881}
882
883#[gpui::test]
884fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut MutableAppContext) {
885 cx.set_global(Settings::test(cx));
886 cx.add_model(|cx| {
887 let text = "
888 fn a() {}
889 "
890 .unindent();
891
892 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
893
894 buffer.edit([(5..5, "\nb")], Some(AutoindentMode::EachLine), cx);
895 assert_eq!(
896 buffer.text(),
897 "
898 fn a(
899 b) {}
900 "
901 .unindent()
902 );
903
904 // The indentation suggestion changed because `@end` node (a close paren)
905 // is now at the beginning of the line.
906 buffer.edit(
907 [(Point::new(1, 4)..Point::new(1, 5), "")],
908 Some(AutoindentMode::EachLine),
909 cx,
910 );
911 assert_eq!(
912 buffer.text(),
913 "
914 fn a(
915 ) {}
916 "
917 .unindent()
918 );
919
920 buffer
921 });
922}
923
924#[gpui::test]
925fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut MutableAppContext) {
926 cx.set_global(Settings::test(cx));
927 cx.add_model(|cx| {
928 let text = "a\nb";
929 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
930 buffer.edit(
931 [(0..1, "\n"), (2..3, "\n")],
932 Some(AutoindentMode::EachLine),
933 cx,
934 );
935 assert_eq!(buffer.text(), "\n\n\n");
936 buffer
937 });
938}
939
940#[gpui::test]
941fn test_autoindent_multi_line_insertion(cx: &mut MutableAppContext) {
942 cx.set_global(Settings::test(cx));
943 cx.add_model(|cx| {
944 let text = "
945 const a: usize = 1;
946 fn b() {
947 if c {
948 let d = 2;
949 }
950 }
951 "
952 .unindent();
953
954 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
955 buffer.edit(
956 [(Point::new(3, 0)..Point::new(3, 0), "e(\n f()\n);\n")],
957 Some(AutoindentMode::EachLine),
958 cx,
959 );
960 assert_eq!(
961 buffer.text(),
962 "
963 const a: usize = 1;
964 fn b() {
965 if c {
966 e(
967 f()
968 );
969 let d = 2;
970 }
971 }
972 "
973 .unindent()
974 );
975
976 buffer
977 });
978}
979
980#[gpui::test]
981fn test_autoindent_block_mode(cx: &mut MutableAppContext) {
982 cx.set_global(Settings::test(cx));
983 cx.add_model(|cx| {
984 let text = r#"
985 fn a() {
986 b();
987 }
988 "#
989 .unindent();
990 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
991
992 let inserted_text = r#"
993 "
994 c
995 d
996 e
997 "
998 "#
999 .unindent();
1000
1001 // Insert the block at column zero. The entire block is indented
1002 // so that the first line matches the previous line's indentation.
1003 buffer.edit(
1004 [(Point::new(2, 0)..Point::new(2, 0), inserted_text.clone())],
1005 Some(AutoindentMode::Block {
1006 original_indent_columns: vec![0],
1007 }),
1008 cx,
1009 );
1010 assert_eq!(
1011 buffer.text(),
1012 r#"
1013 fn a() {
1014 b();
1015 "
1016 c
1017 d
1018 e
1019 "
1020 }
1021 "#
1022 .unindent()
1023 );
1024
1025 // Insert the block at a deeper indent level. The entire block is outdented.
1026 buffer.undo(cx);
1027 buffer.edit([(Point::new(2, 0)..Point::new(2, 0), " ")], None, cx);
1028 buffer.edit(
1029 [(Point::new(2, 8)..Point::new(2, 8), inserted_text)],
1030 Some(AutoindentMode::Block {
1031 original_indent_columns: vec![0],
1032 }),
1033 cx,
1034 );
1035 assert_eq!(
1036 buffer.text(),
1037 r#"
1038 fn a() {
1039 b();
1040 "
1041 c
1042 d
1043 e
1044 "
1045 }
1046 "#
1047 .unindent()
1048 );
1049
1050 buffer
1051 });
1052}
1053
1054#[gpui::test]
1055fn test_autoindent_language_without_indents_query(cx: &mut MutableAppContext) {
1056 cx.set_global(Settings::test(cx));
1057 cx.add_model(|cx| {
1058 let text = "
1059 * one
1060 - a
1061 - b
1062 * two
1063 "
1064 .unindent();
1065
1066 let mut buffer = Buffer::new(0, text, cx).with_language(
1067 Arc::new(Language::new(
1068 LanguageConfig {
1069 name: "Markdown".into(),
1070 auto_indent_using_last_non_empty_line: false,
1071 ..Default::default()
1072 },
1073 Some(tree_sitter_json::language()),
1074 )),
1075 cx,
1076 );
1077 buffer.edit(
1078 [(Point::new(3, 0)..Point::new(3, 0), "\n")],
1079 Some(AutoindentMode::EachLine),
1080 cx,
1081 );
1082 assert_eq!(
1083 buffer.text(),
1084 "
1085 * one
1086 - a
1087 - b
1088
1089 * two
1090 "
1091 .unindent()
1092 );
1093 buffer
1094 });
1095}
1096
1097#[gpui::test]
1098fn test_autoindent_with_injected_languages(cx: &mut MutableAppContext) {
1099 cx.set_global({
1100 let mut settings = Settings::test(cx);
1101 settings.language_overrides.extend([
1102 (
1103 "HTML".into(),
1104 settings::EditorSettings {
1105 tab_size: Some(2.try_into().unwrap()),
1106 ..Default::default()
1107 },
1108 ),
1109 (
1110 "JavaScript".into(),
1111 settings::EditorSettings {
1112 tab_size: Some(8.try_into().unwrap()),
1113 ..Default::default()
1114 },
1115 ),
1116 ]);
1117 settings
1118 });
1119
1120 let html_language = Arc::new(
1121 Language::new(
1122 LanguageConfig {
1123 name: "HTML".into(),
1124 ..Default::default()
1125 },
1126 Some(tree_sitter_html::language()),
1127 )
1128 .with_indents_query(
1129 "
1130 (element
1131 (start_tag) @start
1132 (end_tag)? @end) @indent
1133 ",
1134 )
1135 .unwrap()
1136 .with_injection_query(
1137 r#"
1138 (script_element
1139 (raw_text) @content
1140 (#set! "language" "javascript"))
1141 "#,
1142 )
1143 .unwrap(),
1144 );
1145
1146 let javascript_language = Arc::new(
1147 Language::new(
1148 LanguageConfig {
1149 name: "JavaScript".into(),
1150 ..Default::default()
1151 },
1152 Some(tree_sitter_javascript::language()),
1153 )
1154 .with_indents_query(
1155 r#"
1156 (object "}" @end) @indent
1157 "#,
1158 )
1159 .unwrap(),
1160 );
1161
1162 let language_registry = Arc::new(LanguageRegistry::test());
1163 language_registry.add(html_language.clone());
1164 language_registry.add(javascript_language.clone());
1165
1166 cx.add_model(|cx| {
1167 let (text, ranges) = marked_text_ranges(
1168 &"
1169 <div>ˇ
1170 </div>
1171 <script>
1172 init({ˇ
1173 })
1174 </script>
1175 <span>ˇ
1176 </span>
1177 "
1178 .unindent(),
1179 false,
1180 );
1181
1182 let mut buffer = Buffer::new(0, text, cx);
1183 buffer.set_language_registry(language_registry);
1184 buffer.set_language(Some(html_language), cx);
1185 buffer.edit(
1186 ranges.into_iter().map(|range| (range, "\na")),
1187 Some(AutoindentMode::EachLine),
1188 cx,
1189 );
1190 assert_eq!(
1191 buffer.text(),
1192 "
1193 <div>
1194 a
1195 </div>
1196 <script>
1197 init({
1198 a
1199 })
1200 </script>
1201 <span>
1202 a
1203 </span>
1204 "
1205 .unindent()
1206 );
1207 buffer
1208 });
1209}
1210
1211#[gpui::test]
1212fn test_autoindent_query_with_outdent_captures(cx: &mut MutableAppContext) {
1213 let mut settings = Settings::test(cx);
1214 settings.editor_defaults.tab_size = Some(2.try_into().unwrap());
1215 cx.set_global(settings);
1216 cx.add_model(|cx| {
1217 let mut buffer = Buffer::new(0, "", cx).with_language(Arc::new(ruby_lang()), cx);
1218
1219 let text = r#"
1220 class C
1221 def a(b, c)
1222 puts b
1223 puts c
1224 rescue
1225 puts "errored"
1226 exit 1
1227 end
1228 end
1229 "#
1230 .unindent();
1231
1232 buffer.edit([(0..0, text)], Some(AutoindentMode::EachLine), cx);
1233
1234 assert_eq!(
1235 buffer.text(),
1236 r#"
1237 class C
1238 def a(b, c)
1239 puts b
1240 puts c
1241 rescue
1242 puts "errored"
1243 exit 1
1244 end
1245 end
1246 "#
1247 .unindent()
1248 );
1249
1250 buffer
1251 });
1252}
1253
1254#[gpui::test]
1255fn test_serialization(cx: &mut gpui::MutableAppContext) {
1256 let mut now = Instant::now();
1257
1258 let buffer1 = cx.add_model(|cx| {
1259 let mut buffer = Buffer::new(0, "abc", cx);
1260 buffer.edit([(3..3, "D")], None, cx);
1261
1262 now += Duration::from_secs(1);
1263 buffer.start_transaction_at(now);
1264 buffer.edit([(4..4, "E")], None, cx);
1265 buffer.end_transaction_at(now, cx);
1266 assert_eq!(buffer.text(), "abcDE");
1267
1268 buffer.undo(cx);
1269 assert_eq!(buffer.text(), "abcD");
1270
1271 buffer.edit([(4..4, "F")], None, cx);
1272 assert_eq!(buffer.text(), "abcDF");
1273 buffer
1274 });
1275 assert_eq!(buffer1.read(cx).text(), "abcDF");
1276
1277 let state = buffer1.read(cx).to_proto();
1278 let ops = cx.background().block(buffer1.read(cx).serialize_ops(cx));
1279 let buffer2 = cx.add_model(|cx| {
1280 let mut buffer = Buffer::from_proto(1, state, None).unwrap();
1281 buffer
1282 .apply_ops(
1283 ops.into_iter()
1284 .map(|op| proto::deserialize_operation(op).unwrap()),
1285 cx,
1286 )
1287 .unwrap();
1288 buffer
1289 });
1290 assert_eq!(buffer2.read(cx).text(), "abcDF");
1291}
1292
1293#[gpui::test(iterations = 100)]
1294fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) {
1295 let min_peers = env::var("MIN_PEERS")
1296 .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
1297 .unwrap_or(1);
1298 let max_peers = env::var("MAX_PEERS")
1299 .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
1300 .unwrap_or(5);
1301 let operations = env::var("OPERATIONS")
1302 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1303 .unwrap_or(10);
1304
1305 let base_text_len = rng.gen_range(0..10);
1306 let base_text = RandomCharIter::new(&mut rng)
1307 .take(base_text_len)
1308 .collect::<String>();
1309 let mut replica_ids = Vec::new();
1310 let mut buffers = Vec::new();
1311 let network = Rc::new(RefCell::new(Network::new(rng.clone())));
1312 let base_buffer = cx.add_model(|cx| Buffer::new(0, base_text.as_str(), cx));
1313
1314 for i in 0..rng.gen_range(min_peers..=max_peers) {
1315 let buffer = cx.add_model(|cx| {
1316 let state = base_buffer.read(cx).to_proto();
1317 let ops = cx
1318 .background()
1319 .block(base_buffer.read(cx).serialize_ops(cx));
1320 let mut buffer = Buffer::from_proto(i as ReplicaId, state, None).unwrap();
1321 buffer
1322 .apply_ops(
1323 ops.into_iter()
1324 .map(|op| proto::deserialize_operation(op).unwrap()),
1325 cx,
1326 )
1327 .unwrap();
1328 buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
1329 let network = network.clone();
1330 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1331 if let Event::Operation(op) = event {
1332 network
1333 .borrow_mut()
1334 .broadcast(buffer.replica_id(), vec![proto::serialize_operation(op)]);
1335 }
1336 })
1337 .detach();
1338 buffer
1339 });
1340 buffers.push(buffer);
1341 replica_ids.push(i as ReplicaId);
1342 network.borrow_mut().add_peer(i as ReplicaId);
1343 log::info!("Adding initial peer with replica id {}", i);
1344 }
1345
1346 log::info!("initial text: {:?}", base_text);
1347
1348 let mut now = Instant::now();
1349 let mut mutation_count = operations;
1350 let mut next_diagnostic_id = 0;
1351 let mut active_selections = BTreeMap::default();
1352 loop {
1353 let replica_index = rng.gen_range(0..replica_ids.len());
1354 let replica_id = replica_ids[replica_index];
1355 let buffer = &mut buffers[replica_index];
1356 let mut new_buffer = None;
1357 match rng.gen_range(0..100) {
1358 0..=29 if mutation_count != 0 => {
1359 buffer.update(cx, |buffer, cx| {
1360 buffer.start_transaction_at(now);
1361 buffer.randomly_edit(&mut rng, 5, cx);
1362 buffer.end_transaction_at(now, cx);
1363 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1364 });
1365 mutation_count -= 1;
1366 }
1367 30..=39 if mutation_count != 0 => {
1368 buffer.update(cx, |buffer, cx| {
1369 let mut selections = Vec::new();
1370 for id in 0..rng.gen_range(1..=5) {
1371 let range = buffer.random_byte_range(0, &mut rng);
1372 selections.push(Selection {
1373 id,
1374 start: buffer.anchor_before(range.start),
1375 end: buffer.anchor_before(range.end),
1376 reversed: false,
1377 goal: SelectionGoal::None,
1378 });
1379 }
1380 let selections: Arc<[Selection<Anchor>]> = selections.into();
1381 log::info!(
1382 "peer {} setting active selections: {:?}",
1383 replica_id,
1384 selections
1385 );
1386 active_selections.insert(replica_id, selections.clone());
1387 buffer.set_active_selections(selections, false, Default::default(), cx);
1388 });
1389 mutation_count -= 1;
1390 }
1391 40..=49 if mutation_count != 0 && replica_id == 0 => {
1392 let entry_count = rng.gen_range(1..=5);
1393 buffer.update(cx, |buffer, cx| {
1394 let diagnostics = DiagnosticSet::new(
1395 (0..entry_count).map(|_| {
1396 let range = buffer.random_byte_range(0, &mut rng);
1397 let range = range.to_point_utf16(buffer);
1398 DiagnosticEntry {
1399 range,
1400 diagnostic: Diagnostic {
1401 message: post_inc(&mut next_diagnostic_id).to_string(),
1402 ..Default::default()
1403 },
1404 }
1405 }),
1406 buffer,
1407 );
1408 log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
1409 buffer.update_diagnostics(diagnostics, cx);
1410 });
1411 mutation_count -= 1;
1412 }
1413 50..=59 if replica_ids.len() < max_peers => {
1414 let old_buffer_state = buffer.read(cx).to_proto();
1415 let old_buffer_ops = cx.background().block(buffer.read(cx).serialize_ops(cx));
1416 let new_replica_id = (0..=replica_ids.len() as ReplicaId)
1417 .filter(|replica_id| *replica_id != buffer.read(cx).replica_id())
1418 .choose(&mut rng)
1419 .unwrap();
1420 log::info!(
1421 "Adding new replica {} (replicating from {})",
1422 new_replica_id,
1423 replica_id
1424 );
1425 new_buffer = Some(cx.add_model(|cx| {
1426 let mut new_buffer =
1427 Buffer::from_proto(new_replica_id, old_buffer_state, None).unwrap();
1428 new_buffer
1429 .apply_ops(
1430 old_buffer_ops
1431 .into_iter()
1432 .map(|op| deserialize_operation(op).unwrap()),
1433 cx,
1434 )
1435 .unwrap();
1436 log::info!(
1437 "New replica {} text: {:?}",
1438 new_buffer.replica_id(),
1439 new_buffer.text()
1440 );
1441 new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
1442 let network = network.clone();
1443 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1444 if let Event::Operation(op) = event {
1445 network.borrow_mut().broadcast(
1446 buffer.replica_id(),
1447 vec![proto::serialize_operation(op)],
1448 );
1449 }
1450 })
1451 .detach();
1452 new_buffer
1453 }));
1454 network.borrow_mut().replicate(replica_id, new_replica_id);
1455
1456 if new_replica_id as usize == replica_ids.len() {
1457 replica_ids.push(new_replica_id);
1458 } else {
1459 let new_buffer = new_buffer.take().unwrap();
1460 while network.borrow().has_unreceived(new_replica_id) {
1461 let ops = network
1462 .borrow_mut()
1463 .receive(new_replica_id)
1464 .into_iter()
1465 .map(|op| proto::deserialize_operation(op).unwrap());
1466 if ops.len() > 0 {
1467 log::info!(
1468 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1469 new_replica_id,
1470 buffer.read(cx).version(),
1471 ops.len(),
1472 ops
1473 );
1474 new_buffer.update(cx, |new_buffer, cx| {
1475 new_buffer.apply_ops(ops, cx).unwrap();
1476 });
1477 }
1478 }
1479 buffers[new_replica_id as usize] = new_buffer;
1480 }
1481 }
1482 60..=69 if mutation_count != 0 => {
1483 buffer.update(cx, |buffer, cx| {
1484 buffer.randomly_undo_redo(&mut rng, cx);
1485 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1486 });
1487 mutation_count -= 1;
1488 }
1489 _ if network.borrow().has_unreceived(replica_id) => {
1490 let ops = network
1491 .borrow_mut()
1492 .receive(replica_id)
1493 .into_iter()
1494 .map(|op| proto::deserialize_operation(op).unwrap());
1495 if ops.len() > 0 {
1496 log::info!(
1497 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1498 replica_id,
1499 buffer.read(cx).version(),
1500 ops.len(),
1501 ops
1502 );
1503 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
1504 }
1505 }
1506 _ => {}
1507 }
1508
1509 now += Duration::from_millis(rng.gen_range(0..=200));
1510 buffers.extend(new_buffer);
1511
1512 for buffer in &buffers {
1513 buffer.read(cx).check_invariants();
1514 }
1515
1516 if mutation_count == 0 && network.borrow().is_idle() {
1517 break;
1518 }
1519 }
1520
1521 let first_buffer = buffers[0].read(cx).snapshot();
1522 for buffer in &buffers[1..] {
1523 let buffer = buffer.read(cx).snapshot();
1524 assert_eq!(
1525 buffer.version(),
1526 first_buffer.version(),
1527 "Replica {} version != Replica 0 version",
1528 buffer.replica_id()
1529 );
1530 assert_eq!(
1531 buffer.text(),
1532 first_buffer.text(),
1533 "Replica {} text != Replica 0 text",
1534 buffer.replica_id()
1535 );
1536 assert_eq!(
1537 buffer
1538 .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
1539 .collect::<Vec<_>>(),
1540 first_buffer
1541 .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
1542 .collect::<Vec<_>>(),
1543 "Replica {} diagnostics != Replica 0 diagnostics",
1544 buffer.replica_id()
1545 );
1546 }
1547
1548 for buffer in &buffers {
1549 let buffer = buffer.read(cx).snapshot();
1550 let actual_remote_selections = buffer
1551 .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
1552 .map(|(replica_id, _, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
1553 .collect::<Vec<_>>();
1554 let expected_remote_selections = active_selections
1555 .iter()
1556 .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
1557 .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
1558 .collect::<Vec<_>>();
1559 assert_eq!(
1560 actual_remote_selections,
1561 expected_remote_selections,
1562 "Replica {} remote selections != expected selections",
1563 buffer.replica_id()
1564 );
1565 }
1566}
1567
1568#[test]
1569fn test_contiguous_ranges() {
1570 assert_eq!(
1571 contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
1572 &[1..4, 5..7, 9..13]
1573 );
1574
1575 // Respects the `max_len` parameter
1576 assert_eq!(
1577 contiguous_ranges(
1578 [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
1579 3
1580 )
1581 .collect::<Vec<_>>(),
1582 &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
1583 );
1584}
1585
1586impl Buffer {
1587 pub fn enclosing_bracket_point_ranges<T: ToOffset>(
1588 &self,
1589 range: Range<T>,
1590 ) -> Option<(Range<Point>, Range<Point>)> {
1591 self.snapshot()
1592 .enclosing_bracket_ranges(range)
1593 .map(|(start, end)| {
1594 let point_start = start.start.to_point(self)..start.end.to_point(self);
1595 let point_end = end.start.to_point(self)..end.end.to_point(self);
1596 (point_start, point_end)
1597 })
1598 }
1599}
1600
1601fn ruby_lang() -> Language {
1602 Language::new(
1603 LanguageConfig {
1604 name: "Ruby".into(),
1605 path_suffixes: vec!["rb".to_string()],
1606 ..Default::default()
1607 },
1608 Some(tree_sitter_ruby::language()),
1609 )
1610 .with_indents_query(
1611 r#"
1612 (class "end" @end) @indent
1613 (method "end" @end) @indent
1614 (rescue) @outdent
1615 (then) @indent
1616 "#,
1617 )
1618 .unwrap()
1619}
1620
1621fn rust_lang() -> Language {
1622 Language::new(
1623 LanguageConfig {
1624 name: "Rust".into(),
1625 path_suffixes: vec!["rs".to_string()],
1626 ..Default::default()
1627 },
1628 Some(tree_sitter_rust::language()),
1629 )
1630 .with_indents_query(
1631 r#"
1632 (call_expression) @indent
1633 (field_expression) @indent
1634 (_ "(" ")" @end) @indent
1635 (_ "{" "}" @end) @indent
1636 "#,
1637 )
1638 .unwrap()
1639 .with_brackets_query(
1640 r#"
1641 ("{" @open "}" @close)
1642 "#,
1643 )
1644 .unwrap()
1645 .with_outline_query(
1646 r#"
1647 (struct_item
1648 "struct" @context
1649 name: (_) @name) @item
1650 (enum_item
1651 "enum" @context
1652 name: (_) @name) @item
1653 (enum_variant
1654 name: (_) @name) @item
1655 (field_declaration
1656 name: (_) @name) @item
1657 (impl_item
1658 "impl" @context
1659 trait: (_)? @name
1660 "for"? @context
1661 type: (_) @name) @item
1662 (function_item
1663 "fn" @context
1664 name: (_) @name) @item
1665 (mod_item
1666 "mod" @context
1667 name: (_) @name) @item
1668 "#,
1669 )
1670 .unwrap()
1671}
1672
1673fn json_lang() -> Language {
1674 Language::new(
1675 LanguageConfig {
1676 name: "Json".into(),
1677 path_suffixes: vec!["js".to_string()],
1678 ..Default::default()
1679 },
1680 Some(tree_sitter_json::language()),
1681 )
1682}
1683
1684fn get_tree_sexp(buffer: &ModelHandle<Buffer>, cx: &gpui::TestAppContext) -> String {
1685 buffer.read_with(cx, |buffer, _| {
1686 let snapshot = buffer.snapshot();
1687 let layers = snapshot.syntax.layers(buffer.as_text_snapshot());
1688 layers[0].node.to_sexp()
1689 })
1690}
1691
1692fn empty(point: Point) -> Range<Point> {
1693 point..point
1694}