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
1279 .background()
1280 .block(buffer1.read(cx).serialize_ops(None, cx));
1281 let buffer2 = cx.add_model(|cx| {
1282 let mut buffer = Buffer::from_proto(1, state, None).unwrap();
1283 buffer
1284 .apply_ops(
1285 ops.into_iter()
1286 .map(|op| proto::deserialize_operation(op).unwrap()),
1287 cx,
1288 )
1289 .unwrap();
1290 buffer
1291 });
1292 assert_eq!(buffer2.read(cx).text(), "abcDF");
1293}
1294
1295#[gpui::test(iterations = 100)]
1296fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) {
1297 let min_peers = env::var("MIN_PEERS")
1298 .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
1299 .unwrap_or(1);
1300 let max_peers = env::var("MAX_PEERS")
1301 .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
1302 .unwrap_or(5);
1303 let operations = env::var("OPERATIONS")
1304 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1305 .unwrap_or(10);
1306
1307 let base_text_len = rng.gen_range(0..10);
1308 let base_text = RandomCharIter::new(&mut rng)
1309 .take(base_text_len)
1310 .collect::<String>();
1311 let mut replica_ids = Vec::new();
1312 let mut buffers = Vec::new();
1313 let network = Rc::new(RefCell::new(Network::new(rng.clone())));
1314 let base_buffer = cx.add_model(|cx| Buffer::new(0, base_text.as_str(), cx));
1315
1316 for i in 0..rng.gen_range(min_peers..=max_peers) {
1317 let buffer = cx.add_model(|cx| {
1318 let state = base_buffer.read(cx).to_proto();
1319 let ops = cx
1320 .background()
1321 .block(base_buffer.read(cx).serialize_ops(None, cx));
1322 let mut buffer = Buffer::from_proto(i as ReplicaId, state, None).unwrap();
1323 buffer
1324 .apply_ops(
1325 ops.into_iter()
1326 .map(|op| proto::deserialize_operation(op).unwrap()),
1327 cx,
1328 )
1329 .unwrap();
1330 buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
1331 let network = network.clone();
1332 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1333 if let Event::Operation(op) = event {
1334 network
1335 .borrow_mut()
1336 .broadcast(buffer.replica_id(), vec![proto::serialize_operation(op)]);
1337 }
1338 })
1339 .detach();
1340 buffer
1341 });
1342 buffers.push(buffer);
1343 replica_ids.push(i as ReplicaId);
1344 network.borrow_mut().add_peer(i as ReplicaId);
1345 log::info!("Adding initial peer with replica id {}", i);
1346 }
1347
1348 log::info!("initial text: {:?}", base_text);
1349
1350 let mut now = Instant::now();
1351 let mut mutation_count = operations;
1352 let mut next_diagnostic_id = 0;
1353 let mut active_selections = BTreeMap::default();
1354 loop {
1355 let replica_index = rng.gen_range(0..replica_ids.len());
1356 let replica_id = replica_ids[replica_index];
1357 let buffer = &mut buffers[replica_index];
1358 let mut new_buffer = None;
1359 match rng.gen_range(0..100) {
1360 0..=29 if mutation_count != 0 => {
1361 buffer.update(cx, |buffer, cx| {
1362 buffer.start_transaction_at(now);
1363 buffer.randomly_edit(&mut rng, 5, cx);
1364 buffer.end_transaction_at(now, cx);
1365 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1366 });
1367 mutation_count -= 1;
1368 }
1369 30..=39 if mutation_count != 0 => {
1370 buffer.update(cx, |buffer, cx| {
1371 let mut selections = Vec::new();
1372 for id in 0..rng.gen_range(1..=5) {
1373 let range = buffer.random_byte_range(0, &mut rng);
1374 selections.push(Selection {
1375 id,
1376 start: buffer.anchor_before(range.start),
1377 end: buffer.anchor_before(range.end),
1378 reversed: false,
1379 goal: SelectionGoal::None,
1380 });
1381 }
1382 let selections: Arc<[Selection<Anchor>]> = selections.into();
1383 log::info!(
1384 "peer {} setting active selections: {:?}",
1385 replica_id,
1386 selections
1387 );
1388 active_selections.insert(replica_id, selections.clone());
1389 buffer.set_active_selections(selections, false, Default::default(), cx);
1390 });
1391 mutation_count -= 1;
1392 }
1393 40..=49 if mutation_count != 0 && replica_id == 0 => {
1394 let entry_count = rng.gen_range(1..=5);
1395 buffer.update(cx, |buffer, cx| {
1396 let diagnostics = DiagnosticSet::new(
1397 (0..entry_count).map(|_| {
1398 let range = buffer.random_byte_range(0, &mut rng);
1399 let range = range.to_point_utf16(buffer);
1400 let range = range.start..range.end;
1401 DiagnosticEntry {
1402 range,
1403 diagnostic: Diagnostic {
1404 message: post_inc(&mut next_diagnostic_id).to_string(),
1405 ..Default::default()
1406 },
1407 }
1408 }),
1409 buffer,
1410 );
1411 log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
1412 buffer.update_diagnostics(diagnostics, cx);
1413 });
1414 mutation_count -= 1;
1415 }
1416 50..=59 if replica_ids.len() < max_peers => {
1417 let old_buffer_state = buffer.read(cx).to_proto();
1418 let old_buffer_ops = cx
1419 .background()
1420 .block(buffer.read(cx).serialize_ops(None, cx));
1421 let new_replica_id = (0..=replica_ids.len() as ReplicaId)
1422 .filter(|replica_id| *replica_id != buffer.read(cx).replica_id())
1423 .choose(&mut rng)
1424 .unwrap();
1425 log::info!(
1426 "Adding new replica {} (replicating from {})",
1427 new_replica_id,
1428 replica_id
1429 );
1430 new_buffer = Some(cx.add_model(|cx| {
1431 let mut new_buffer =
1432 Buffer::from_proto(new_replica_id, old_buffer_state, None).unwrap();
1433 new_buffer
1434 .apply_ops(
1435 old_buffer_ops
1436 .into_iter()
1437 .map(|op| deserialize_operation(op).unwrap()),
1438 cx,
1439 )
1440 .unwrap();
1441 log::info!(
1442 "New replica {} text: {:?}",
1443 new_buffer.replica_id(),
1444 new_buffer.text()
1445 );
1446 new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
1447 let network = network.clone();
1448 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1449 if let Event::Operation(op) = event {
1450 network.borrow_mut().broadcast(
1451 buffer.replica_id(),
1452 vec![proto::serialize_operation(op)],
1453 );
1454 }
1455 })
1456 .detach();
1457 new_buffer
1458 }));
1459 network.borrow_mut().replicate(replica_id, new_replica_id);
1460
1461 if new_replica_id as usize == replica_ids.len() {
1462 replica_ids.push(new_replica_id);
1463 } else {
1464 let new_buffer = new_buffer.take().unwrap();
1465 while network.borrow().has_unreceived(new_replica_id) {
1466 let ops = network
1467 .borrow_mut()
1468 .receive(new_replica_id)
1469 .into_iter()
1470 .map(|op| proto::deserialize_operation(op).unwrap());
1471 if ops.len() > 0 {
1472 log::info!(
1473 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1474 new_replica_id,
1475 buffer.read(cx).version(),
1476 ops.len(),
1477 ops
1478 );
1479 new_buffer.update(cx, |new_buffer, cx| {
1480 new_buffer.apply_ops(ops, cx).unwrap();
1481 });
1482 }
1483 }
1484 buffers[new_replica_id as usize] = new_buffer;
1485 }
1486 }
1487 60..=69 if mutation_count != 0 => {
1488 buffer.update(cx, |buffer, cx| {
1489 buffer.randomly_undo_redo(&mut rng, cx);
1490 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1491 });
1492 mutation_count -= 1;
1493 }
1494 _ if network.borrow().has_unreceived(replica_id) => {
1495 let ops = network
1496 .borrow_mut()
1497 .receive(replica_id)
1498 .into_iter()
1499 .map(|op| proto::deserialize_operation(op).unwrap());
1500 if ops.len() > 0 {
1501 log::info!(
1502 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1503 replica_id,
1504 buffer.read(cx).version(),
1505 ops.len(),
1506 ops
1507 );
1508 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
1509 }
1510 }
1511 _ => {}
1512 }
1513
1514 now += Duration::from_millis(rng.gen_range(0..=200));
1515 buffers.extend(new_buffer);
1516
1517 for buffer in &buffers {
1518 buffer.read(cx).check_invariants();
1519 }
1520
1521 if mutation_count == 0 && network.borrow().is_idle() {
1522 break;
1523 }
1524 }
1525
1526 let first_buffer = buffers[0].read(cx).snapshot();
1527 for buffer in &buffers[1..] {
1528 let buffer = buffer.read(cx).snapshot();
1529 assert_eq!(
1530 buffer.version(),
1531 first_buffer.version(),
1532 "Replica {} version != Replica 0 version",
1533 buffer.replica_id()
1534 );
1535 assert_eq!(
1536 buffer.text(),
1537 first_buffer.text(),
1538 "Replica {} text != Replica 0 text",
1539 buffer.replica_id()
1540 );
1541 assert_eq!(
1542 buffer
1543 .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
1544 .collect::<Vec<_>>(),
1545 first_buffer
1546 .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
1547 .collect::<Vec<_>>(),
1548 "Replica {} diagnostics != Replica 0 diagnostics",
1549 buffer.replica_id()
1550 );
1551 }
1552
1553 for buffer in &buffers {
1554 let buffer = buffer.read(cx).snapshot();
1555 let actual_remote_selections = buffer
1556 .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
1557 .map(|(replica_id, _, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
1558 .collect::<Vec<_>>();
1559 let expected_remote_selections = active_selections
1560 .iter()
1561 .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
1562 .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
1563 .collect::<Vec<_>>();
1564 assert_eq!(
1565 actual_remote_selections,
1566 expected_remote_selections,
1567 "Replica {} remote selections != expected selections",
1568 buffer.replica_id()
1569 );
1570 }
1571}
1572
1573#[test]
1574fn test_contiguous_ranges() {
1575 assert_eq!(
1576 contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
1577 &[1..4, 5..7, 9..13]
1578 );
1579
1580 // Respects the `max_len` parameter
1581 assert_eq!(
1582 contiguous_ranges(
1583 [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
1584 3
1585 )
1586 .collect::<Vec<_>>(),
1587 &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
1588 );
1589}
1590
1591impl Buffer {
1592 pub fn enclosing_bracket_point_ranges<T: ToOffset>(
1593 &self,
1594 range: Range<T>,
1595 ) -> Option<(Range<Point>, Range<Point>)> {
1596 self.snapshot()
1597 .enclosing_bracket_ranges(range)
1598 .map(|(start, end)| {
1599 let point_start = start.start.to_point(self)..start.end.to_point(self);
1600 let point_end = end.start.to_point(self)..end.end.to_point(self);
1601 (point_start, point_end)
1602 })
1603 }
1604}
1605
1606fn ruby_lang() -> Language {
1607 Language::new(
1608 LanguageConfig {
1609 name: "Ruby".into(),
1610 path_suffixes: vec!["rb".to_string()],
1611 ..Default::default()
1612 },
1613 Some(tree_sitter_ruby::language()),
1614 )
1615 .with_indents_query(
1616 r#"
1617 (class "end" @end) @indent
1618 (method "end" @end) @indent
1619 (rescue) @outdent
1620 (then) @indent
1621 "#,
1622 )
1623 .unwrap()
1624}
1625
1626fn rust_lang() -> Language {
1627 Language::new(
1628 LanguageConfig {
1629 name: "Rust".into(),
1630 path_suffixes: vec!["rs".to_string()],
1631 ..Default::default()
1632 },
1633 Some(tree_sitter_rust::language()),
1634 )
1635 .with_indents_query(
1636 r#"
1637 (call_expression) @indent
1638 (field_expression) @indent
1639 (_ "(" ")" @end) @indent
1640 (_ "{" "}" @end) @indent
1641 "#,
1642 )
1643 .unwrap()
1644 .with_brackets_query(
1645 r#"
1646 ("{" @open "}" @close)
1647 "#,
1648 )
1649 .unwrap()
1650 .with_outline_query(
1651 r#"
1652 (struct_item
1653 "struct" @context
1654 name: (_) @name) @item
1655 (enum_item
1656 "enum" @context
1657 name: (_) @name) @item
1658 (enum_variant
1659 name: (_) @name) @item
1660 (field_declaration
1661 name: (_) @name) @item
1662 (impl_item
1663 "impl" @context
1664 trait: (_)? @name
1665 "for"? @context
1666 type: (_) @name) @item
1667 (function_item
1668 "fn" @context
1669 name: (_) @name) @item
1670 (mod_item
1671 "mod" @context
1672 name: (_) @name) @item
1673 "#,
1674 )
1675 .unwrap()
1676}
1677
1678fn json_lang() -> Language {
1679 Language::new(
1680 LanguageConfig {
1681 name: "Json".into(),
1682 path_suffixes: vec!["js".to_string()],
1683 ..Default::default()
1684 },
1685 Some(tree_sitter_json::language()),
1686 )
1687}
1688
1689fn get_tree_sexp(buffer: &ModelHandle<Buffer>, cx: &gpui::TestAppContext) -> String {
1690 buffer.read_with(cx, |buffer, _| {
1691 let snapshot = buffer.snapshot();
1692 let layers = snapshot.syntax.layers(buffer.as_text_snapshot());
1693 layers[0].node.to_sexp()
1694 })
1695}
1696
1697fn empty(point: Point) -> Range<Point> {
1698 point..point
1699}