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 // When this text was copied, both of the quotation marks were at the same
999 // indent level, but the indentation of the first line was not included in
1000 // the copied text. This information is retained in the
1001 // 'original_indent_columns' vector.
1002 let original_indent_columns = vec![4];
1003 let inserted_text = r#"
1004 "
1005 c
1006 d
1007 e
1008 "
1009 "#
1010 .unindent();
1011
1012 // Insert the block at column zero. The entire block is indented
1013 // so that the first line matches the previous line's indentation.
1014 buffer.edit(
1015 [(Point::new(2, 0)..Point::new(2, 0), inserted_text.clone())],
1016 Some(AutoindentMode::Block {
1017 original_indent_columns: original_indent_columns.clone(),
1018 }),
1019 cx,
1020 );
1021 assert_eq!(
1022 buffer.text(),
1023 r#"
1024 fn a() {
1025 b();
1026 "
1027 c
1028 d
1029 e
1030 "
1031 }
1032 "#
1033 .unindent()
1034 );
1035
1036 // Grouping is disabled in tests, so we need 2 undos
1037 buffer.undo(cx); // Undo the auto-indent
1038 buffer.undo(cx); // Undo the original edit
1039
1040 // Insert the block at a deeper indent level. The entire block is outdented.
1041 buffer.edit([(Point::new(2, 0)..Point::new(2, 0), " ")], None, cx);
1042 buffer.edit(
1043 [(Point::new(2, 8)..Point::new(2, 8), inserted_text)],
1044 Some(AutoindentMode::Block {
1045 original_indent_columns: original_indent_columns.clone(),
1046 }),
1047 cx,
1048 );
1049 assert_eq!(
1050 buffer.text(),
1051 r#"
1052 fn a() {
1053 b();
1054 "
1055 c
1056 d
1057 e
1058 "
1059 }
1060 "#
1061 .unindent()
1062 );
1063
1064 buffer
1065 });
1066}
1067
1068#[gpui::test]
1069fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut MutableAppContext) {
1070 cx.set_global(Settings::test(cx));
1071 cx.add_model(|cx| {
1072 let text = r#"
1073 fn a() {
1074 if b() {
1075
1076 }
1077 }
1078 "#
1079 .unindent();
1080 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
1081
1082 // The original indent columns are not known, so this text is
1083 // auto-indented in a block as if the first line was copied in
1084 // its entirety.
1085 let original_indent_columns = Vec::new();
1086 let inserted_text = " c\n .d()\n .e();";
1087
1088 // Insert the block at column zero. The entire block is indented
1089 // so that the first line matches the previous line's indentation.
1090 buffer.edit(
1091 [(Point::new(2, 0)..Point::new(2, 0), inserted_text.clone())],
1092 Some(AutoindentMode::Block {
1093 original_indent_columns: original_indent_columns.clone(),
1094 }),
1095 cx,
1096 );
1097 assert_eq!(
1098 buffer.text(),
1099 r#"
1100 fn a() {
1101 if b() {
1102 c
1103 .d()
1104 .e();
1105 }
1106 }
1107 "#
1108 .unindent()
1109 );
1110
1111 // Grouping is disabled in tests, so we need 2 undos
1112 buffer.undo(cx); // Undo the auto-indent
1113 buffer.undo(cx); // Undo the original edit
1114
1115 // Insert the block at a deeper indent level. The entire block is outdented.
1116 buffer.edit(
1117 [(Point::new(2, 0)..Point::new(2, 0), " ".repeat(12))],
1118 None,
1119 cx,
1120 );
1121 buffer.edit(
1122 [(Point::new(2, 12)..Point::new(2, 12), inserted_text)],
1123 Some(AutoindentMode::Block {
1124 original_indent_columns: Vec::new(),
1125 }),
1126 cx,
1127 );
1128 assert_eq!(
1129 buffer.text(),
1130 r#"
1131 fn a() {
1132 if b() {
1133 c
1134 .d()
1135 .e();
1136 }
1137 }
1138 "#
1139 .unindent()
1140 );
1141
1142 buffer
1143 });
1144}
1145
1146#[gpui::test]
1147fn test_autoindent_language_without_indents_query(cx: &mut MutableAppContext) {
1148 cx.set_global(Settings::test(cx));
1149 cx.add_model(|cx| {
1150 let text = "
1151 * one
1152 - a
1153 - b
1154 * two
1155 "
1156 .unindent();
1157
1158 let mut buffer = Buffer::new(0, text, cx).with_language(
1159 Arc::new(Language::new(
1160 LanguageConfig {
1161 name: "Markdown".into(),
1162 auto_indent_using_last_non_empty_line: false,
1163 ..Default::default()
1164 },
1165 Some(tree_sitter_json::language()),
1166 )),
1167 cx,
1168 );
1169 buffer.edit(
1170 [(Point::new(3, 0)..Point::new(3, 0), "\n")],
1171 Some(AutoindentMode::EachLine),
1172 cx,
1173 );
1174 assert_eq!(
1175 buffer.text(),
1176 "
1177 * one
1178 - a
1179 - b
1180
1181 * two
1182 "
1183 .unindent()
1184 );
1185 buffer
1186 });
1187}
1188
1189#[gpui::test]
1190fn test_autoindent_with_injected_languages(cx: &mut MutableAppContext) {
1191 cx.set_global({
1192 let mut settings = Settings::test(cx);
1193 settings.language_overrides.extend([
1194 (
1195 "HTML".into(),
1196 settings::EditorSettings {
1197 tab_size: Some(2.try_into().unwrap()),
1198 ..Default::default()
1199 },
1200 ),
1201 (
1202 "JavaScript".into(),
1203 settings::EditorSettings {
1204 tab_size: Some(8.try_into().unwrap()),
1205 ..Default::default()
1206 },
1207 ),
1208 ]);
1209 settings
1210 });
1211
1212 let html_language = Arc::new(
1213 Language::new(
1214 LanguageConfig {
1215 name: "HTML".into(),
1216 ..Default::default()
1217 },
1218 Some(tree_sitter_html::language()),
1219 )
1220 .with_indents_query(
1221 "
1222 (element
1223 (start_tag) @start
1224 (end_tag)? @end) @indent
1225 ",
1226 )
1227 .unwrap()
1228 .with_injection_query(
1229 r#"
1230 (script_element
1231 (raw_text) @content
1232 (#set! "language" "javascript"))
1233 "#,
1234 )
1235 .unwrap(),
1236 );
1237
1238 let javascript_language = Arc::new(
1239 Language::new(
1240 LanguageConfig {
1241 name: "JavaScript".into(),
1242 ..Default::default()
1243 },
1244 Some(tree_sitter_javascript::language()),
1245 )
1246 .with_indents_query(
1247 r#"
1248 (object "}" @end) @indent
1249 "#,
1250 )
1251 .unwrap(),
1252 );
1253
1254 let language_registry = Arc::new(LanguageRegistry::test());
1255 language_registry.add(html_language.clone());
1256 language_registry.add(javascript_language.clone());
1257
1258 cx.add_model(|cx| {
1259 let (text, ranges) = marked_text_ranges(
1260 &"
1261 <div>ˇ
1262 </div>
1263 <script>
1264 init({ˇ
1265 })
1266 </script>
1267 <span>ˇ
1268 </span>
1269 "
1270 .unindent(),
1271 false,
1272 );
1273
1274 let mut buffer = Buffer::new(0, text, cx);
1275 buffer.set_language_registry(language_registry);
1276 buffer.set_language(Some(html_language), cx);
1277 buffer.edit(
1278 ranges.into_iter().map(|range| (range, "\na")),
1279 Some(AutoindentMode::EachLine),
1280 cx,
1281 );
1282 assert_eq!(
1283 buffer.text(),
1284 "
1285 <div>
1286 a
1287 </div>
1288 <script>
1289 init({
1290 a
1291 })
1292 </script>
1293 <span>
1294 a
1295 </span>
1296 "
1297 .unindent()
1298 );
1299 buffer
1300 });
1301}
1302
1303#[gpui::test]
1304fn test_autoindent_query_with_outdent_captures(cx: &mut MutableAppContext) {
1305 let mut settings = Settings::test(cx);
1306 settings.editor_defaults.tab_size = Some(2.try_into().unwrap());
1307 cx.set_global(settings);
1308 cx.add_model(|cx| {
1309 let mut buffer = Buffer::new(0, "", cx).with_language(Arc::new(ruby_lang()), cx);
1310
1311 let text = r#"
1312 class C
1313 def a(b, c)
1314 puts b
1315 puts c
1316 rescue
1317 puts "errored"
1318 exit 1
1319 end
1320 end
1321 "#
1322 .unindent();
1323
1324 buffer.edit([(0..0, text)], Some(AutoindentMode::EachLine), cx);
1325
1326 assert_eq!(
1327 buffer.text(),
1328 r#"
1329 class C
1330 def a(b, c)
1331 puts b
1332 puts c
1333 rescue
1334 puts "errored"
1335 exit 1
1336 end
1337 end
1338 "#
1339 .unindent()
1340 );
1341
1342 buffer
1343 });
1344}
1345
1346#[gpui::test]
1347fn test_serialization(cx: &mut gpui::MutableAppContext) {
1348 let mut now = Instant::now();
1349
1350 let buffer1 = cx.add_model(|cx| {
1351 let mut buffer = Buffer::new(0, "abc", cx);
1352 buffer.edit([(3..3, "D")], None, cx);
1353
1354 now += Duration::from_secs(1);
1355 buffer.start_transaction_at(now);
1356 buffer.edit([(4..4, "E")], None, cx);
1357 buffer.end_transaction_at(now, cx);
1358 assert_eq!(buffer.text(), "abcDE");
1359
1360 buffer.undo(cx);
1361 assert_eq!(buffer.text(), "abcD");
1362
1363 buffer.edit([(4..4, "F")], None, cx);
1364 assert_eq!(buffer.text(), "abcDF");
1365 buffer
1366 });
1367 assert_eq!(buffer1.read(cx).text(), "abcDF");
1368
1369 let state = buffer1.read(cx).to_proto();
1370 let ops = cx
1371 .background()
1372 .block(buffer1.read(cx).serialize_ops(None, cx));
1373 let buffer2 = cx.add_model(|cx| {
1374 let mut buffer = Buffer::from_proto(1, state, None).unwrap();
1375 buffer
1376 .apply_ops(
1377 ops.into_iter()
1378 .map(|op| proto::deserialize_operation(op).unwrap()),
1379 cx,
1380 )
1381 .unwrap();
1382 buffer
1383 });
1384 assert_eq!(buffer2.read(cx).text(), "abcDF");
1385}
1386
1387#[gpui::test(iterations = 100)]
1388fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) {
1389 let min_peers = env::var("MIN_PEERS")
1390 .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
1391 .unwrap_or(1);
1392 let max_peers = env::var("MAX_PEERS")
1393 .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
1394 .unwrap_or(5);
1395 let operations = env::var("OPERATIONS")
1396 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1397 .unwrap_or(10);
1398
1399 let base_text_len = rng.gen_range(0..10);
1400 let base_text = RandomCharIter::new(&mut rng)
1401 .take(base_text_len)
1402 .collect::<String>();
1403 let mut replica_ids = Vec::new();
1404 let mut buffers = Vec::new();
1405 let network = Rc::new(RefCell::new(Network::new(rng.clone())));
1406 let base_buffer = cx.add_model(|cx| Buffer::new(0, base_text.as_str(), cx));
1407
1408 for i in 0..rng.gen_range(min_peers..=max_peers) {
1409 let buffer = cx.add_model(|cx| {
1410 let state = base_buffer.read(cx).to_proto();
1411 let ops = cx
1412 .background()
1413 .block(base_buffer.read(cx).serialize_ops(None, cx));
1414 let mut buffer = Buffer::from_proto(i as ReplicaId, state, None).unwrap();
1415 buffer
1416 .apply_ops(
1417 ops.into_iter()
1418 .map(|op| proto::deserialize_operation(op).unwrap()),
1419 cx,
1420 )
1421 .unwrap();
1422 buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
1423 let network = network.clone();
1424 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1425 if let Event::Operation(op) = event {
1426 network
1427 .borrow_mut()
1428 .broadcast(buffer.replica_id(), vec![proto::serialize_operation(op)]);
1429 }
1430 })
1431 .detach();
1432 buffer
1433 });
1434 buffers.push(buffer);
1435 replica_ids.push(i as ReplicaId);
1436 network.borrow_mut().add_peer(i as ReplicaId);
1437 log::info!("Adding initial peer with replica id {}", i);
1438 }
1439
1440 log::info!("initial text: {:?}", base_text);
1441
1442 let mut now = Instant::now();
1443 let mut mutation_count = operations;
1444 let mut next_diagnostic_id = 0;
1445 let mut active_selections = BTreeMap::default();
1446 loop {
1447 let replica_index = rng.gen_range(0..replica_ids.len());
1448 let replica_id = replica_ids[replica_index];
1449 let buffer = &mut buffers[replica_index];
1450 let mut new_buffer = None;
1451 match rng.gen_range(0..100) {
1452 0..=29 if mutation_count != 0 => {
1453 buffer.update(cx, |buffer, cx| {
1454 buffer.start_transaction_at(now);
1455 buffer.randomly_edit(&mut rng, 5, cx);
1456 buffer.end_transaction_at(now, cx);
1457 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1458 });
1459 mutation_count -= 1;
1460 }
1461 30..=39 if mutation_count != 0 => {
1462 buffer.update(cx, |buffer, cx| {
1463 let mut selections = Vec::new();
1464 for id in 0..rng.gen_range(1..=5) {
1465 let range = buffer.random_byte_range(0, &mut rng);
1466 selections.push(Selection {
1467 id,
1468 start: buffer.anchor_before(range.start),
1469 end: buffer.anchor_before(range.end),
1470 reversed: false,
1471 goal: SelectionGoal::None,
1472 });
1473 }
1474 let selections: Arc<[Selection<Anchor>]> = selections.into();
1475 log::info!(
1476 "peer {} setting active selections: {:?}",
1477 replica_id,
1478 selections
1479 );
1480 active_selections.insert(replica_id, selections.clone());
1481 buffer.set_active_selections(selections, false, Default::default(), cx);
1482 });
1483 mutation_count -= 1;
1484 }
1485 40..=49 if mutation_count != 0 && replica_id == 0 => {
1486 let entry_count = rng.gen_range(1..=5);
1487 buffer.update(cx, |buffer, cx| {
1488 let diagnostics = DiagnosticSet::new(
1489 (0..entry_count).map(|_| {
1490 let range = buffer.random_byte_range(0, &mut rng);
1491 let range = range.to_point_utf16(buffer);
1492 let range = range.start..range.end;
1493 DiagnosticEntry {
1494 range,
1495 diagnostic: Diagnostic {
1496 message: post_inc(&mut next_diagnostic_id).to_string(),
1497 ..Default::default()
1498 },
1499 }
1500 }),
1501 buffer,
1502 );
1503 log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
1504 buffer.update_diagnostics(diagnostics, cx);
1505 });
1506 mutation_count -= 1;
1507 }
1508 50..=59 if replica_ids.len() < max_peers => {
1509 let old_buffer_state = buffer.read(cx).to_proto();
1510 let old_buffer_ops = cx
1511 .background()
1512 .block(buffer.read(cx).serialize_ops(None, cx));
1513 let new_replica_id = (0..=replica_ids.len() as ReplicaId)
1514 .filter(|replica_id| *replica_id != buffer.read(cx).replica_id())
1515 .choose(&mut rng)
1516 .unwrap();
1517 log::info!(
1518 "Adding new replica {} (replicating from {})",
1519 new_replica_id,
1520 replica_id
1521 );
1522 new_buffer = Some(cx.add_model(|cx| {
1523 let mut new_buffer =
1524 Buffer::from_proto(new_replica_id, old_buffer_state, None).unwrap();
1525 new_buffer
1526 .apply_ops(
1527 old_buffer_ops
1528 .into_iter()
1529 .map(|op| deserialize_operation(op).unwrap()),
1530 cx,
1531 )
1532 .unwrap();
1533 log::info!(
1534 "New replica {} text: {:?}",
1535 new_buffer.replica_id(),
1536 new_buffer.text()
1537 );
1538 new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
1539 let network = network.clone();
1540 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1541 if let Event::Operation(op) = event {
1542 network.borrow_mut().broadcast(
1543 buffer.replica_id(),
1544 vec![proto::serialize_operation(op)],
1545 );
1546 }
1547 })
1548 .detach();
1549 new_buffer
1550 }));
1551 network.borrow_mut().replicate(replica_id, new_replica_id);
1552
1553 if new_replica_id as usize == replica_ids.len() {
1554 replica_ids.push(new_replica_id);
1555 } else {
1556 let new_buffer = new_buffer.take().unwrap();
1557 while network.borrow().has_unreceived(new_replica_id) {
1558 let ops = network
1559 .borrow_mut()
1560 .receive(new_replica_id)
1561 .into_iter()
1562 .map(|op| proto::deserialize_operation(op).unwrap());
1563 if ops.len() > 0 {
1564 log::info!(
1565 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1566 new_replica_id,
1567 buffer.read(cx).version(),
1568 ops.len(),
1569 ops
1570 );
1571 new_buffer.update(cx, |new_buffer, cx| {
1572 new_buffer.apply_ops(ops, cx).unwrap();
1573 });
1574 }
1575 }
1576 buffers[new_replica_id as usize] = new_buffer;
1577 }
1578 }
1579 60..=69 if mutation_count != 0 => {
1580 buffer.update(cx, |buffer, cx| {
1581 buffer.randomly_undo_redo(&mut rng, cx);
1582 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1583 });
1584 mutation_count -= 1;
1585 }
1586 _ if network.borrow().has_unreceived(replica_id) => {
1587 let ops = network
1588 .borrow_mut()
1589 .receive(replica_id)
1590 .into_iter()
1591 .map(|op| proto::deserialize_operation(op).unwrap());
1592 if ops.len() > 0 {
1593 log::info!(
1594 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1595 replica_id,
1596 buffer.read(cx).version(),
1597 ops.len(),
1598 ops
1599 );
1600 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
1601 }
1602 }
1603 _ => {}
1604 }
1605
1606 now += Duration::from_millis(rng.gen_range(0..=200));
1607 buffers.extend(new_buffer);
1608
1609 for buffer in &buffers {
1610 buffer.read(cx).check_invariants();
1611 }
1612
1613 if mutation_count == 0 && network.borrow().is_idle() {
1614 break;
1615 }
1616 }
1617
1618 let first_buffer = buffers[0].read(cx).snapshot();
1619 for buffer in &buffers[1..] {
1620 let buffer = buffer.read(cx).snapshot();
1621 assert_eq!(
1622 buffer.version(),
1623 first_buffer.version(),
1624 "Replica {} version != Replica 0 version",
1625 buffer.replica_id()
1626 );
1627 assert_eq!(
1628 buffer.text(),
1629 first_buffer.text(),
1630 "Replica {} text != Replica 0 text",
1631 buffer.replica_id()
1632 );
1633 assert_eq!(
1634 buffer
1635 .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
1636 .collect::<Vec<_>>(),
1637 first_buffer
1638 .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
1639 .collect::<Vec<_>>(),
1640 "Replica {} diagnostics != Replica 0 diagnostics",
1641 buffer.replica_id()
1642 );
1643 }
1644
1645 for buffer in &buffers {
1646 let buffer = buffer.read(cx).snapshot();
1647 let actual_remote_selections = buffer
1648 .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
1649 .map(|(replica_id, _, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
1650 .collect::<Vec<_>>();
1651 let expected_remote_selections = active_selections
1652 .iter()
1653 .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
1654 .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
1655 .collect::<Vec<_>>();
1656 assert_eq!(
1657 actual_remote_selections,
1658 expected_remote_selections,
1659 "Replica {} remote selections != expected selections",
1660 buffer.replica_id()
1661 );
1662 }
1663}
1664
1665#[test]
1666fn test_contiguous_ranges() {
1667 assert_eq!(
1668 contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
1669 &[1..4, 5..7, 9..13]
1670 );
1671
1672 // Respects the `max_len` parameter
1673 assert_eq!(
1674 contiguous_ranges(
1675 [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
1676 3
1677 )
1678 .collect::<Vec<_>>(),
1679 &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
1680 );
1681}
1682
1683impl Buffer {
1684 pub fn enclosing_bracket_point_ranges<T: ToOffset>(
1685 &self,
1686 range: Range<T>,
1687 ) -> Option<(Range<Point>, Range<Point>)> {
1688 self.snapshot()
1689 .enclosing_bracket_ranges(range)
1690 .map(|(start, end)| {
1691 let point_start = start.start.to_point(self)..start.end.to_point(self);
1692 let point_end = end.start.to_point(self)..end.end.to_point(self);
1693 (point_start, point_end)
1694 })
1695 }
1696}
1697
1698fn ruby_lang() -> Language {
1699 Language::new(
1700 LanguageConfig {
1701 name: "Ruby".into(),
1702 path_suffixes: vec!["rb".to_string()],
1703 ..Default::default()
1704 },
1705 Some(tree_sitter_ruby::language()),
1706 )
1707 .with_indents_query(
1708 r#"
1709 (class "end" @end) @indent
1710 (method "end" @end) @indent
1711 (rescue) @outdent
1712 (then) @indent
1713 "#,
1714 )
1715 .unwrap()
1716}
1717
1718fn rust_lang() -> Language {
1719 Language::new(
1720 LanguageConfig {
1721 name: "Rust".into(),
1722 path_suffixes: vec!["rs".to_string()],
1723 ..Default::default()
1724 },
1725 Some(tree_sitter_rust::language()),
1726 )
1727 .with_indents_query(
1728 r#"
1729 (call_expression) @indent
1730 (field_expression) @indent
1731 (_ "(" ")" @end) @indent
1732 (_ "{" "}" @end) @indent
1733 "#,
1734 )
1735 .unwrap()
1736 .with_brackets_query(
1737 r#"
1738 ("{" @open "}" @close)
1739 "#,
1740 )
1741 .unwrap()
1742 .with_outline_query(
1743 r#"
1744 (struct_item
1745 "struct" @context
1746 name: (_) @name) @item
1747 (enum_item
1748 "enum" @context
1749 name: (_) @name) @item
1750 (enum_variant
1751 name: (_) @name) @item
1752 (field_declaration
1753 name: (_) @name) @item
1754 (impl_item
1755 "impl" @context
1756 trait: (_)? @name
1757 "for"? @context
1758 type: (_) @name) @item
1759 (function_item
1760 "fn" @context
1761 name: (_) @name) @item
1762 (mod_item
1763 "mod" @context
1764 name: (_) @name) @item
1765 "#,
1766 )
1767 .unwrap()
1768}
1769
1770fn json_lang() -> Language {
1771 Language::new(
1772 LanguageConfig {
1773 name: "Json".into(),
1774 path_suffixes: vec!["js".to_string()],
1775 ..Default::default()
1776 },
1777 Some(tree_sitter_json::language()),
1778 )
1779}
1780
1781fn get_tree_sexp(buffer: &ModelHandle<Buffer>, cx: &gpui::TestAppContext) -> String {
1782 buffer.read_with(cx, |buffer, _| {
1783 let snapshot = buffer.snapshot();
1784 let layers = snapshot.syntax.layers(buffer.as_text_snapshot());
1785 layers[0].node.to_sexp()
1786 })
1787}
1788
1789fn empty(point: Point) -> Range<Point> {
1790 point..point
1791}