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