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_outline_nodes_with_newlines(cx: &mut gpui::TestAppContext) {
460 let text = r#"
461 impl A for B<
462 C
463 > {
464 };
465 "#
466 .unindent();
467
468 let buffer =
469 cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
470 let outline = buffer
471 .read_with(cx, |buffer, _| buffer.snapshot().outline(None))
472 .unwrap();
473
474 assert_eq!(
475 outline
476 .items
477 .iter()
478 .map(|item| (item.text.as_str(), item.depth))
479 .collect::<Vec<_>>(),
480 &[("impl A for B<", 0)]
481 );
482}
483
484#[gpui::test]
485async fn test_symbols_containing(cx: &mut gpui::TestAppContext) {
486 let text = r#"
487 impl Person {
488 fn one() {
489 1
490 }
491
492 fn two() {
493 2
494 }fn three() {
495 3
496 }
497 }
498 "#
499 .unindent();
500
501 let buffer =
502 cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
503 let snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot());
504
505 // point is at the start of an item
506 assert_eq!(
507 symbols_containing(Point::new(1, 4), &snapshot),
508 vec![
509 (
510 "impl Person".to_string(),
511 Point::new(0, 0)..Point::new(10, 1)
512 ),
513 ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
514 ]
515 );
516
517 // point is in the middle of an item
518 assert_eq!(
519 symbols_containing(Point::new(2, 8), &snapshot),
520 vec![
521 (
522 "impl Person".to_string(),
523 Point::new(0, 0)..Point::new(10, 1)
524 ),
525 ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
526 ]
527 );
528
529 // point is at the end of an item
530 assert_eq!(
531 symbols_containing(Point::new(3, 5), &snapshot),
532 vec![
533 (
534 "impl Person".to_string(),
535 Point::new(0, 0)..Point::new(10, 1)
536 ),
537 ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
538 ]
539 );
540
541 // point is in between two adjacent items
542 assert_eq!(
543 symbols_containing(Point::new(7, 5), &snapshot),
544 vec![
545 (
546 "impl Person".to_string(),
547 Point::new(0, 0)..Point::new(10, 1)
548 ),
549 ("fn two".to_string(), Point::new(5, 4)..Point::new(7, 5))
550 ]
551 );
552
553 fn symbols_containing(
554 position: Point,
555 snapshot: &BufferSnapshot,
556 ) -> Vec<(String, Range<Point>)> {
557 snapshot
558 .symbols_containing(position, None)
559 .unwrap()
560 .into_iter()
561 .map(|item| {
562 (
563 item.text,
564 item.range.start.to_point(snapshot)..item.range.end.to_point(snapshot),
565 )
566 })
567 .collect()
568 }
569}
570
571#[gpui::test]
572fn test_enclosing_bracket_ranges(cx: &mut MutableAppContext) {
573 cx.set_global(Settings::test(cx));
574 let buffer = cx.add_model(|cx| {
575 let text = "
576 mod x {
577 mod y {
578
579 }
580 }
581 "
582 .unindent();
583 Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx)
584 });
585 let buffer = buffer.read(cx);
586 assert_eq!(
587 buffer.enclosing_bracket_point_ranges(Point::new(1, 6)..Point::new(1, 6)),
588 Some((
589 Point::new(0, 6)..Point::new(0, 7),
590 Point::new(4, 0)..Point::new(4, 1)
591 ))
592 );
593 assert_eq!(
594 buffer.enclosing_bracket_point_ranges(Point::new(1, 10)..Point::new(1, 10)),
595 Some((
596 Point::new(1, 10)..Point::new(1, 11),
597 Point::new(3, 4)..Point::new(3, 5)
598 ))
599 );
600 assert_eq!(
601 buffer.enclosing_bracket_point_ranges(Point::new(3, 5)..Point::new(3, 5)),
602 Some((
603 Point::new(1, 10)..Point::new(1, 11),
604 Point::new(3, 4)..Point::new(3, 5)
605 ))
606 );
607
608 assert_eq!(
609 buffer.enclosing_bracket_point_ranges(Point::new(4, 1)..Point::new(4, 1)),
610 Some((
611 Point::new(0, 6)..Point::new(0, 7),
612 Point::new(4, 0)..Point::new(4, 1)
613 ))
614 );
615
616 // Regression test: avoid crash when querying at the end of the buffer.
617 assert_eq!(
618 buffer.enclosing_bracket_point_ranges(Point::new(4, 1)..Point::new(5, 0)),
619 None
620 );
621}
622
623#[gpui::test]
624fn test_enclosing_bracket_ranges_where_brackets_are_not_outermost_children(
625 cx: &mut MutableAppContext,
626) {
627 let javascript_language = Arc::new(
628 Language::new(
629 LanguageConfig {
630 name: "JavaScript".into(),
631 ..Default::default()
632 },
633 Some(tree_sitter_javascript::language()),
634 )
635 .with_brackets_query(
636 r#"
637 ("{" @open "}" @close)
638 ("(" @open ")" @close)
639 "#,
640 )
641 .unwrap(),
642 );
643
644 cx.set_global(Settings::test(cx));
645 let buffer = cx.add_model(|cx| {
646 let text = "
647 for (const a in b) {
648 // a comment that's longer than the for-loop header
649 }
650 "
651 .unindent();
652 Buffer::new(0, text, cx).with_language(javascript_language, cx)
653 });
654
655 let buffer = buffer.read(cx);
656 assert_eq!(
657 buffer.enclosing_bracket_point_ranges(Point::new(0, 18)..Point::new(0, 18)),
658 Some((
659 Point::new(0, 4)..Point::new(0, 5),
660 Point::new(0, 17)..Point::new(0, 18)
661 ))
662 );
663
664 // Regression test: even though the parent node of the parentheses (the for loop) does
665 // intersect the given range, the parentheses themselves do not contain the range, so
666 // they should not be returned. Only the curly braces contain the range.
667 assert_eq!(
668 buffer.enclosing_bracket_point_ranges(Point::new(0, 20)..Point::new(0, 20)),
669 Some((
670 Point::new(0, 19)..Point::new(0, 20),
671 Point::new(2, 0)..Point::new(2, 1)
672 ))
673 );
674}
675
676#[gpui::test]
677fn test_range_for_syntax_ancestor(cx: &mut MutableAppContext) {
678 cx.add_model(|cx| {
679 let text = "fn a() { b(|c| {}) }";
680 let buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
681 let snapshot = buffer.snapshot();
682
683 assert_eq!(
684 snapshot.range_for_syntax_ancestor(empty_range_at(text, "|")),
685 Some(range_of(text, "|"))
686 );
687 assert_eq!(
688 snapshot.range_for_syntax_ancestor(range_of(text, "|")),
689 Some(range_of(text, "|c|"))
690 );
691 assert_eq!(
692 snapshot.range_for_syntax_ancestor(range_of(text, "|c|")),
693 Some(range_of(text, "|c| {}"))
694 );
695 assert_eq!(
696 snapshot.range_for_syntax_ancestor(range_of(text, "|c| {}")),
697 Some(range_of(text, "(|c| {})"))
698 );
699
700 buffer
701 });
702
703 fn empty_range_at(text: &str, part: &str) -> Range<usize> {
704 let start = text.find(part).unwrap();
705 start..start
706 }
707
708 fn range_of(text: &str, part: &str) -> Range<usize> {
709 let start = text.find(part).unwrap();
710 start..start + part.len()
711 }
712}
713
714#[gpui::test]
715fn test_autoindent_with_soft_tabs(cx: &mut MutableAppContext) {
716 let settings = Settings::test(cx);
717 cx.set_global(settings);
718
719 cx.add_model(|cx| {
720 let text = "fn a() {}";
721 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
722
723 buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
724 assert_eq!(buffer.text(), "fn a() {\n \n}");
725
726 buffer.edit(
727 [(Point::new(1, 4)..Point::new(1, 4), "b()\n")],
728 Some(AutoindentMode::EachLine),
729 cx,
730 );
731 assert_eq!(buffer.text(), "fn a() {\n b()\n \n}");
732
733 // Create a field expression on a new line, causing that line
734 // to be indented.
735 buffer.edit(
736 [(Point::new(2, 4)..Point::new(2, 4), ".c")],
737 Some(AutoindentMode::EachLine),
738 cx,
739 );
740 assert_eq!(buffer.text(), "fn a() {\n b()\n .c\n}");
741
742 // Remove the dot so that the line is no longer a field expression,
743 // causing the line to be outdented.
744 buffer.edit(
745 [(Point::new(2, 8)..Point::new(2, 9), "")],
746 Some(AutoindentMode::EachLine),
747 cx,
748 );
749 assert_eq!(buffer.text(), "fn a() {\n b()\n c\n}");
750
751 buffer
752 });
753}
754
755#[gpui::test]
756fn test_autoindent_with_hard_tabs(cx: &mut MutableAppContext) {
757 let mut settings = Settings::test(cx);
758 settings.editor_overrides.hard_tabs = Some(true);
759 cx.set_global(settings);
760
761 cx.add_model(|cx| {
762 let text = "fn a() {}";
763 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
764
765 buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
766 assert_eq!(buffer.text(), "fn a() {\n\t\n}");
767
768 buffer.edit(
769 [(Point::new(1, 1)..Point::new(1, 1), "b()\n")],
770 Some(AutoindentMode::EachLine),
771 cx,
772 );
773 assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\n}");
774
775 // Create a field expression on a new line, causing that line
776 // to be indented.
777 buffer.edit(
778 [(Point::new(2, 1)..Point::new(2, 1), ".c")],
779 Some(AutoindentMode::EachLine),
780 cx,
781 );
782 assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\t.c\n}");
783
784 // Remove the dot so that the line is no longer a field expression,
785 // causing the line to be outdented.
786 buffer.edit(
787 [(Point::new(2, 2)..Point::new(2, 3), "")],
788 Some(AutoindentMode::EachLine),
789 cx,
790 );
791 assert_eq!(buffer.text(), "fn a() {\n\tb()\n\tc\n}");
792
793 buffer
794 });
795}
796
797#[gpui::test]
798fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut MutableAppContext) {
799 let settings = Settings::test(cx);
800 cx.set_global(settings);
801
802 cx.add_model(|cx| {
803 let text = "
804 fn a() {
805 c;
806 d;
807 }
808 "
809 .unindent();
810
811 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
812
813 // Lines 2 and 3 don't match the indentation suggestion. When editing these lines,
814 // their indentation is not adjusted.
815 buffer.edit(
816 [
817 (empty(Point::new(1, 1)), "()"),
818 (empty(Point::new(2, 1)), "()"),
819 ],
820 Some(AutoindentMode::EachLine),
821 cx,
822 );
823 assert_eq!(
824 buffer.text(),
825 "
826 fn a() {
827 c();
828 d();
829 }
830 "
831 .unindent()
832 );
833
834 // When appending new content after these lines, the indentation is based on the
835 // preceding lines' actual indentation.
836 buffer.edit(
837 [
838 (empty(Point::new(1, 1)), "\n.f\n.g"),
839 (empty(Point::new(2, 1)), "\n.f\n.g"),
840 ],
841 Some(AutoindentMode::EachLine),
842 cx,
843 );
844 assert_eq!(
845 buffer.text(),
846 "
847 fn a() {
848 c
849 .f
850 .g();
851 d
852 .f
853 .g();
854 }
855 "
856 .unindent()
857 );
858 buffer
859 });
860
861 cx.add_model(|cx| {
862 let text = "
863 fn a() {
864 {
865 b()?
866 }
867 Ok(())
868 }
869 "
870 .unindent();
871 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
872
873 // Delete a closing curly brace changes the suggested indent for the line.
874 buffer.edit(
875 [(Point::new(3, 4)..Point::new(3, 5), "")],
876 Some(AutoindentMode::EachLine),
877 cx,
878 );
879 assert_eq!(
880 buffer.text(),
881 "
882 fn a() {
883 {
884 b()?
885 |
886 Ok(())
887 }
888 "
889 .replace('|', "") // included in the string to preserve trailing whites
890 .unindent()
891 );
892
893 // Manually editing the leading whitespace
894 buffer.edit(
895 [(Point::new(3, 0)..Point::new(3, 12), "")],
896 Some(AutoindentMode::EachLine),
897 cx,
898 );
899 assert_eq!(
900 buffer.text(),
901 "
902 fn a() {
903 {
904 b()?
905
906 Ok(())
907 }
908 "
909 .unindent()
910 );
911 buffer
912 });
913}
914
915#[gpui::test]
916fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut MutableAppContext) {
917 cx.set_global(Settings::test(cx));
918 cx.add_model(|cx| {
919 let text = "
920 fn a() {}
921 "
922 .unindent();
923
924 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
925
926 buffer.edit([(5..5, "\nb")], Some(AutoindentMode::EachLine), cx);
927 assert_eq!(
928 buffer.text(),
929 "
930 fn a(
931 b) {}
932 "
933 .unindent()
934 );
935
936 // The indentation suggestion changed because `@end` node (a close paren)
937 // is now at the beginning of the line.
938 buffer.edit(
939 [(Point::new(1, 4)..Point::new(1, 5), "")],
940 Some(AutoindentMode::EachLine),
941 cx,
942 );
943 assert_eq!(
944 buffer.text(),
945 "
946 fn a(
947 ) {}
948 "
949 .unindent()
950 );
951
952 buffer
953 });
954}
955
956#[gpui::test]
957fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut MutableAppContext) {
958 cx.set_global(Settings::test(cx));
959 cx.add_model(|cx| {
960 let text = "a\nb";
961 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
962 buffer.edit(
963 [(0..1, "\n"), (2..3, "\n")],
964 Some(AutoindentMode::EachLine),
965 cx,
966 );
967 assert_eq!(buffer.text(), "\n\n\n");
968 buffer
969 });
970}
971
972#[gpui::test]
973fn test_autoindent_multi_line_insertion(cx: &mut MutableAppContext) {
974 cx.set_global(Settings::test(cx));
975 cx.add_model(|cx| {
976 let text = "
977 const a: usize = 1;
978 fn b() {
979 if c {
980 let d = 2;
981 }
982 }
983 "
984 .unindent();
985
986 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
987 buffer.edit(
988 [(Point::new(3, 0)..Point::new(3, 0), "e(\n f()\n);\n")],
989 Some(AutoindentMode::EachLine),
990 cx,
991 );
992 assert_eq!(
993 buffer.text(),
994 "
995 const a: usize = 1;
996 fn b() {
997 if c {
998 e(
999 f()
1000 );
1001 let d = 2;
1002 }
1003 }
1004 "
1005 .unindent()
1006 );
1007
1008 buffer
1009 });
1010}
1011
1012#[gpui::test]
1013fn test_autoindent_block_mode(cx: &mut MutableAppContext) {
1014 cx.set_global(Settings::test(cx));
1015 cx.add_model(|cx| {
1016 let text = r#"
1017 fn a() {
1018 b();
1019 }
1020 "#
1021 .unindent();
1022 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
1023
1024 // When this text was copied, both of the quotation marks were at the same
1025 // indent level, but the indentation of the first line was not included in
1026 // the copied text. This information is retained in the
1027 // 'original_indent_columns' vector.
1028 let original_indent_columns = vec![4];
1029 let inserted_text = r#"
1030 "
1031 c
1032 d
1033 e
1034 "
1035 "#
1036 .unindent();
1037
1038 // Insert the block at column zero. The entire block is indented
1039 // so that the first line matches the previous line's indentation.
1040 buffer.edit(
1041 [(Point::new(2, 0)..Point::new(2, 0), inserted_text.clone())],
1042 Some(AutoindentMode::Block {
1043 original_indent_columns: original_indent_columns.clone(),
1044 }),
1045 cx,
1046 );
1047 assert_eq!(
1048 buffer.text(),
1049 r#"
1050 fn a() {
1051 b();
1052 "
1053 c
1054 d
1055 e
1056 "
1057 }
1058 "#
1059 .unindent()
1060 );
1061
1062 // Grouping is disabled in tests, so we need 2 undos
1063 buffer.undo(cx); // Undo the auto-indent
1064 buffer.undo(cx); // Undo the original edit
1065
1066 // Insert the block at a deeper indent level. The entire block is outdented.
1067 buffer.edit([(Point::new(2, 0)..Point::new(2, 0), " ")], None, cx);
1068 buffer.edit(
1069 [(Point::new(2, 8)..Point::new(2, 8), inserted_text)],
1070 Some(AutoindentMode::Block {
1071 original_indent_columns: original_indent_columns.clone(),
1072 }),
1073 cx,
1074 );
1075 assert_eq!(
1076 buffer.text(),
1077 r#"
1078 fn a() {
1079 b();
1080 "
1081 c
1082 d
1083 e
1084 "
1085 }
1086 "#
1087 .unindent()
1088 );
1089
1090 buffer
1091 });
1092}
1093
1094#[gpui::test]
1095fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut MutableAppContext) {
1096 cx.set_global(Settings::test(cx));
1097 cx.add_model(|cx| {
1098 let text = r#"
1099 fn a() {
1100 if b() {
1101
1102 }
1103 }
1104 "#
1105 .unindent();
1106 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
1107
1108 // The original indent columns are not known, so this text is
1109 // auto-indented in a block as if the first line was copied in
1110 // its entirety.
1111 let original_indent_columns = Vec::new();
1112 let inserted_text = " c\n .d()\n .e();";
1113
1114 // Insert the block at column zero. The entire block is indented
1115 // so that the first line matches the previous line's indentation.
1116 buffer.edit(
1117 [(Point::new(2, 0)..Point::new(2, 0), inserted_text.clone())],
1118 Some(AutoindentMode::Block {
1119 original_indent_columns: original_indent_columns.clone(),
1120 }),
1121 cx,
1122 );
1123 assert_eq!(
1124 buffer.text(),
1125 r#"
1126 fn a() {
1127 if b() {
1128 c
1129 .d()
1130 .e();
1131 }
1132 }
1133 "#
1134 .unindent()
1135 );
1136
1137 // Grouping is disabled in tests, so we need 2 undos
1138 buffer.undo(cx); // Undo the auto-indent
1139 buffer.undo(cx); // Undo the original edit
1140
1141 // Insert the block at a deeper indent level. The entire block is outdented.
1142 buffer.edit(
1143 [(Point::new(2, 0)..Point::new(2, 0), " ".repeat(12))],
1144 None,
1145 cx,
1146 );
1147 buffer.edit(
1148 [(Point::new(2, 12)..Point::new(2, 12), inserted_text)],
1149 Some(AutoindentMode::Block {
1150 original_indent_columns: Vec::new(),
1151 }),
1152 cx,
1153 );
1154 assert_eq!(
1155 buffer.text(),
1156 r#"
1157 fn a() {
1158 if b() {
1159 c
1160 .d()
1161 .e();
1162 }
1163 }
1164 "#
1165 .unindent()
1166 );
1167
1168 buffer
1169 });
1170}
1171
1172#[gpui::test]
1173fn test_autoindent_language_without_indents_query(cx: &mut MutableAppContext) {
1174 cx.set_global(Settings::test(cx));
1175 cx.add_model(|cx| {
1176 let text = "
1177 * one
1178 - a
1179 - b
1180 * two
1181 "
1182 .unindent();
1183
1184 let mut buffer = Buffer::new(0, text, cx).with_language(
1185 Arc::new(Language::new(
1186 LanguageConfig {
1187 name: "Markdown".into(),
1188 auto_indent_using_last_non_empty_line: false,
1189 ..Default::default()
1190 },
1191 Some(tree_sitter_json::language()),
1192 )),
1193 cx,
1194 );
1195 buffer.edit(
1196 [(Point::new(3, 0)..Point::new(3, 0), "\n")],
1197 Some(AutoindentMode::EachLine),
1198 cx,
1199 );
1200 assert_eq!(
1201 buffer.text(),
1202 "
1203 * one
1204 - a
1205 - b
1206
1207 * two
1208 "
1209 .unindent()
1210 );
1211 buffer
1212 });
1213}
1214
1215#[gpui::test]
1216fn test_autoindent_with_injected_languages(cx: &mut MutableAppContext) {
1217 cx.set_global({
1218 let mut settings = Settings::test(cx);
1219 settings.language_overrides.extend([
1220 (
1221 "HTML".into(),
1222 settings::EditorSettings {
1223 tab_size: Some(2.try_into().unwrap()),
1224 ..Default::default()
1225 },
1226 ),
1227 (
1228 "JavaScript".into(),
1229 settings::EditorSettings {
1230 tab_size: Some(8.try_into().unwrap()),
1231 ..Default::default()
1232 },
1233 ),
1234 ]);
1235 settings
1236 });
1237
1238 let html_language = Arc::new(
1239 Language::new(
1240 LanguageConfig {
1241 name: "HTML".into(),
1242 ..Default::default()
1243 },
1244 Some(tree_sitter_html::language()),
1245 )
1246 .with_indents_query(
1247 "
1248 (element
1249 (start_tag) @start
1250 (end_tag)? @end) @indent
1251 ",
1252 )
1253 .unwrap()
1254 .with_injection_query(
1255 r#"
1256 (script_element
1257 (raw_text) @content
1258 (#set! "language" "javascript"))
1259 "#,
1260 )
1261 .unwrap(),
1262 );
1263
1264 let javascript_language = Arc::new(
1265 Language::new(
1266 LanguageConfig {
1267 name: "JavaScript".into(),
1268 ..Default::default()
1269 },
1270 Some(tree_sitter_javascript::language()),
1271 )
1272 .with_indents_query(
1273 r#"
1274 (object "}" @end) @indent
1275 "#,
1276 )
1277 .unwrap(),
1278 );
1279
1280 let language_registry = Arc::new(LanguageRegistry::test());
1281 language_registry.add(html_language.clone());
1282 language_registry.add(javascript_language.clone());
1283
1284 cx.add_model(|cx| {
1285 let (text, ranges) = marked_text_ranges(
1286 &"
1287 <div>ˇ
1288 </div>
1289 <script>
1290 init({ˇ
1291 })
1292 </script>
1293 <span>ˇ
1294 </span>
1295 "
1296 .unindent(),
1297 false,
1298 );
1299
1300 let mut buffer = Buffer::new(0, text, cx);
1301 buffer.set_language_registry(language_registry);
1302 buffer.set_language(Some(html_language), cx);
1303 buffer.edit(
1304 ranges.into_iter().map(|range| (range, "\na")),
1305 Some(AutoindentMode::EachLine),
1306 cx,
1307 );
1308 assert_eq!(
1309 buffer.text(),
1310 "
1311 <div>
1312 a
1313 </div>
1314 <script>
1315 init({
1316 a
1317 })
1318 </script>
1319 <span>
1320 a
1321 </span>
1322 "
1323 .unindent()
1324 );
1325 buffer
1326 });
1327}
1328
1329#[gpui::test]
1330fn test_autoindent_query_with_outdent_captures(cx: &mut MutableAppContext) {
1331 let mut settings = Settings::test(cx);
1332 settings.editor_defaults.tab_size = Some(2.try_into().unwrap());
1333 cx.set_global(settings);
1334 cx.add_model(|cx| {
1335 let mut buffer = Buffer::new(0, "", cx).with_language(Arc::new(ruby_lang()), cx);
1336
1337 let text = r#"
1338 class C
1339 def a(b, c)
1340 puts b
1341 puts c
1342 rescue
1343 puts "errored"
1344 exit 1
1345 end
1346 end
1347 "#
1348 .unindent();
1349
1350 buffer.edit([(0..0, text)], Some(AutoindentMode::EachLine), cx);
1351
1352 assert_eq!(
1353 buffer.text(),
1354 r#"
1355 class C
1356 def a(b, c)
1357 puts b
1358 puts c
1359 rescue
1360 puts "errored"
1361 exit 1
1362 end
1363 end
1364 "#
1365 .unindent()
1366 );
1367
1368 buffer
1369 });
1370}
1371
1372#[gpui::test]
1373fn test_language_config_at(cx: &mut MutableAppContext) {
1374 cx.set_global(Settings::test(cx));
1375 cx.add_model(|cx| {
1376 let language = Language::new(
1377 LanguageConfig {
1378 name: "JavaScript".into(),
1379 line_comment: Some("// ".into()),
1380 brackets: vec![
1381 BracketPair {
1382 start: "{".into(),
1383 end: "}".into(),
1384 close: true,
1385 newline: false,
1386 },
1387 BracketPair {
1388 start: "'".into(),
1389 end: "'".into(),
1390 close: true,
1391 newline: false,
1392 },
1393 ],
1394 overrides: [
1395 (
1396 "element".into(),
1397 LanguageConfigOverride {
1398 line_comment: Override::Remove { remove: true },
1399 block_comment: Override::Set(("{/*".into(), "*/}".into())),
1400 ..Default::default()
1401 },
1402 ),
1403 (
1404 "string".into(),
1405 LanguageConfigOverride {
1406 brackets: Override::Set(vec![BracketPair {
1407 start: "{".into(),
1408 end: "}".into(),
1409 close: true,
1410 newline: false,
1411 }]),
1412 ..Default::default()
1413 },
1414 ),
1415 ]
1416 .into_iter()
1417 .collect(),
1418 ..Default::default()
1419 },
1420 Some(tree_sitter_javascript::language()),
1421 )
1422 .with_override_query(
1423 r#"
1424 (jsx_element) @override.element
1425 (string) @override.string
1426 "#,
1427 )
1428 .unwrap();
1429
1430 let text = r#"a["b"] = <C d="e"></C>;"#;
1431
1432 let buffer = Buffer::new(0, text, cx).with_language(Arc::new(language), cx);
1433 let snapshot = buffer.snapshot();
1434
1435 let config = snapshot.language_scope_at(0).unwrap();
1436 assert_eq!(config.line_comment_prefix().unwrap().as_ref(), "// ");
1437 assert_eq!(config.brackets().len(), 2);
1438
1439 let string_config = snapshot.language_scope_at(3).unwrap();
1440 assert_eq!(config.line_comment_prefix().unwrap().as_ref(), "// ");
1441 assert_eq!(string_config.brackets().len(), 1);
1442
1443 let element_config = snapshot.language_scope_at(10).unwrap();
1444 assert_eq!(element_config.line_comment_prefix(), None);
1445 assert_eq!(
1446 element_config.block_comment_delimiters(),
1447 Some((&"{/*".into(), &"*/}".into()))
1448 );
1449 assert_eq!(element_config.brackets().len(), 2);
1450
1451 buffer
1452 });
1453}
1454
1455#[gpui::test]
1456fn test_serialization(cx: &mut gpui::MutableAppContext) {
1457 let mut now = Instant::now();
1458
1459 let buffer1 = cx.add_model(|cx| {
1460 let mut buffer = Buffer::new(0, "abc", cx);
1461 buffer.edit([(3..3, "D")], None, cx);
1462
1463 now += Duration::from_secs(1);
1464 buffer.start_transaction_at(now);
1465 buffer.edit([(4..4, "E")], None, cx);
1466 buffer.end_transaction_at(now, cx);
1467 assert_eq!(buffer.text(), "abcDE");
1468
1469 buffer.undo(cx);
1470 assert_eq!(buffer.text(), "abcD");
1471
1472 buffer.edit([(4..4, "F")], None, cx);
1473 assert_eq!(buffer.text(), "abcDF");
1474 buffer
1475 });
1476 assert_eq!(buffer1.read(cx).text(), "abcDF");
1477
1478 let state = buffer1.read(cx).to_proto();
1479 let ops = cx
1480 .background()
1481 .block(buffer1.read(cx).serialize_ops(None, cx));
1482 let buffer2 = cx.add_model(|cx| {
1483 let mut buffer = Buffer::from_proto(1, state, None).unwrap();
1484 buffer
1485 .apply_ops(
1486 ops.into_iter()
1487 .map(|op| proto::deserialize_operation(op).unwrap()),
1488 cx,
1489 )
1490 .unwrap();
1491 buffer
1492 });
1493 assert_eq!(buffer2.read(cx).text(), "abcDF");
1494}
1495
1496#[gpui::test(iterations = 100)]
1497fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) {
1498 let min_peers = env::var("MIN_PEERS")
1499 .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
1500 .unwrap_or(1);
1501 let max_peers = env::var("MAX_PEERS")
1502 .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
1503 .unwrap_or(5);
1504 let operations = env::var("OPERATIONS")
1505 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1506 .unwrap_or(10);
1507
1508 let base_text_len = rng.gen_range(0..10);
1509 let base_text = RandomCharIter::new(&mut rng)
1510 .take(base_text_len)
1511 .collect::<String>();
1512 let mut replica_ids = Vec::new();
1513 let mut buffers = Vec::new();
1514 let network = Rc::new(RefCell::new(Network::new(rng.clone())));
1515 let base_buffer = cx.add_model(|cx| Buffer::new(0, base_text.as_str(), cx));
1516
1517 for i in 0..rng.gen_range(min_peers..=max_peers) {
1518 let buffer = cx.add_model(|cx| {
1519 let state = base_buffer.read(cx).to_proto();
1520 let ops = cx
1521 .background()
1522 .block(base_buffer.read(cx).serialize_ops(None, cx));
1523 let mut buffer = Buffer::from_proto(i as ReplicaId, state, None).unwrap();
1524 buffer
1525 .apply_ops(
1526 ops.into_iter()
1527 .map(|op| proto::deserialize_operation(op).unwrap()),
1528 cx,
1529 )
1530 .unwrap();
1531 buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
1532 let network = network.clone();
1533 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1534 if let Event::Operation(op) = event {
1535 network
1536 .borrow_mut()
1537 .broadcast(buffer.replica_id(), vec![proto::serialize_operation(op)]);
1538 }
1539 })
1540 .detach();
1541 buffer
1542 });
1543 buffers.push(buffer);
1544 replica_ids.push(i as ReplicaId);
1545 network.borrow_mut().add_peer(i as ReplicaId);
1546 log::info!("Adding initial peer with replica id {}", i);
1547 }
1548
1549 log::info!("initial text: {:?}", base_text);
1550
1551 let mut now = Instant::now();
1552 let mut mutation_count = operations;
1553 let mut next_diagnostic_id = 0;
1554 let mut active_selections = BTreeMap::default();
1555 loop {
1556 let replica_index = rng.gen_range(0..replica_ids.len());
1557 let replica_id = replica_ids[replica_index];
1558 let buffer = &mut buffers[replica_index];
1559 let mut new_buffer = None;
1560 match rng.gen_range(0..100) {
1561 0..=29 if mutation_count != 0 => {
1562 buffer.update(cx, |buffer, cx| {
1563 buffer.start_transaction_at(now);
1564 buffer.randomly_edit(&mut rng, 5, cx);
1565 buffer.end_transaction_at(now, cx);
1566 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1567 });
1568 mutation_count -= 1;
1569 }
1570 30..=39 if mutation_count != 0 => {
1571 buffer.update(cx, |buffer, cx| {
1572 let mut selections = Vec::new();
1573 for id in 0..rng.gen_range(1..=5) {
1574 let range = buffer.random_byte_range(0, &mut rng);
1575 selections.push(Selection {
1576 id,
1577 start: buffer.anchor_before(range.start),
1578 end: buffer.anchor_before(range.end),
1579 reversed: false,
1580 goal: SelectionGoal::None,
1581 });
1582 }
1583 let selections: Arc<[Selection<Anchor>]> = selections.into();
1584 log::info!(
1585 "peer {} setting active selections: {:?}",
1586 replica_id,
1587 selections
1588 );
1589 active_selections.insert(replica_id, selections.clone());
1590 buffer.set_active_selections(selections, false, Default::default(), cx);
1591 });
1592 mutation_count -= 1;
1593 }
1594 40..=49 if mutation_count != 0 && replica_id == 0 => {
1595 let entry_count = rng.gen_range(1..=5);
1596 buffer.update(cx, |buffer, cx| {
1597 let diagnostics = DiagnosticSet::new(
1598 (0..entry_count).map(|_| {
1599 let range = buffer.random_byte_range(0, &mut rng);
1600 let range = range.to_point_utf16(buffer);
1601 let range = range.start..range.end;
1602 DiagnosticEntry {
1603 range,
1604 diagnostic: Diagnostic {
1605 message: post_inc(&mut next_diagnostic_id).to_string(),
1606 ..Default::default()
1607 },
1608 }
1609 }),
1610 buffer,
1611 );
1612 log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
1613 buffer.update_diagnostics(diagnostics, cx);
1614 });
1615 mutation_count -= 1;
1616 }
1617 50..=59 if replica_ids.len() < max_peers => {
1618 let old_buffer_state = buffer.read(cx).to_proto();
1619 let old_buffer_ops = cx
1620 .background()
1621 .block(buffer.read(cx).serialize_ops(None, cx));
1622 let new_replica_id = (0..=replica_ids.len() as ReplicaId)
1623 .filter(|replica_id| *replica_id != buffer.read(cx).replica_id())
1624 .choose(&mut rng)
1625 .unwrap();
1626 log::info!(
1627 "Adding new replica {} (replicating from {})",
1628 new_replica_id,
1629 replica_id
1630 );
1631 new_buffer = Some(cx.add_model(|cx| {
1632 let mut new_buffer =
1633 Buffer::from_proto(new_replica_id, old_buffer_state, None).unwrap();
1634 new_buffer
1635 .apply_ops(
1636 old_buffer_ops
1637 .into_iter()
1638 .map(|op| deserialize_operation(op).unwrap()),
1639 cx,
1640 )
1641 .unwrap();
1642 log::info!(
1643 "New replica {} text: {:?}",
1644 new_buffer.replica_id(),
1645 new_buffer.text()
1646 );
1647 new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
1648 let network = network.clone();
1649 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1650 if let Event::Operation(op) = event {
1651 network.borrow_mut().broadcast(
1652 buffer.replica_id(),
1653 vec![proto::serialize_operation(op)],
1654 );
1655 }
1656 })
1657 .detach();
1658 new_buffer
1659 }));
1660 network.borrow_mut().replicate(replica_id, new_replica_id);
1661
1662 if new_replica_id as usize == replica_ids.len() {
1663 replica_ids.push(new_replica_id);
1664 } else {
1665 let new_buffer = new_buffer.take().unwrap();
1666 while network.borrow().has_unreceived(new_replica_id) {
1667 let ops = network
1668 .borrow_mut()
1669 .receive(new_replica_id)
1670 .into_iter()
1671 .map(|op| proto::deserialize_operation(op).unwrap());
1672 if ops.len() > 0 {
1673 log::info!(
1674 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1675 new_replica_id,
1676 buffer.read(cx).version(),
1677 ops.len(),
1678 ops
1679 );
1680 new_buffer.update(cx, |new_buffer, cx| {
1681 new_buffer.apply_ops(ops, cx).unwrap();
1682 });
1683 }
1684 }
1685 buffers[new_replica_id as usize] = new_buffer;
1686 }
1687 }
1688 60..=69 if mutation_count != 0 => {
1689 buffer.update(cx, |buffer, cx| {
1690 buffer.randomly_undo_redo(&mut rng, cx);
1691 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1692 });
1693 mutation_count -= 1;
1694 }
1695 _ if network.borrow().has_unreceived(replica_id) => {
1696 let ops = network
1697 .borrow_mut()
1698 .receive(replica_id)
1699 .into_iter()
1700 .map(|op| proto::deserialize_operation(op).unwrap());
1701 if ops.len() > 0 {
1702 log::info!(
1703 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1704 replica_id,
1705 buffer.read(cx).version(),
1706 ops.len(),
1707 ops
1708 );
1709 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
1710 }
1711 }
1712 _ => {}
1713 }
1714
1715 now += Duration::from_millis(rng.gen_range(0..=200));
1716 buffers.extend(new_buffer);
1717
1718 for buffer in &buffers {
1719 buffer.read(cx).check_invariants();
1720 }
1721
1722 if mutation_count == 0 && network.borrow().is_idle() {
1723 break;
1724 }
1725 }
1726
1727 let first_buffer = buffers[0].read(cx).snapshot();
1728 for buffer in &buffers[1..] {
1729 let buffer = buffer.read(cx).snapshot();
1730 assert_eq!(
1731 buffer.version(),
1732 first_buffer.version(),
1733 "Replica {} version != Replica 0 version",
1734 buffer.replica_id()
1735 );
1736 assert_eq!(
1737 buffer.text(),
1738 first_buffer.text(),
1739 "Replica {} text != Replica 0 text",
1740 buffer.replica_id()
1741 );
1742 assert_eq!(
1743 buffer
1744 .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
1745 .collect::<Vec<_>>(),
1746 first_buffer
1747 .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
1748 .collect::<Vec<_>>(),
1749 "Replica {} diagnostics != Replica 0 diagnostics",
1750 buffer.replica_id()
1751 );
1752 }
1753
1754 for buffer in &buffers {
1755 let buffer = buffer.read(cx).snapshot();
1756 let actual_remote_selections = buffer
1757 .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
1758 .map(|(replica_id, _, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
1759 .collect::<Vec<_>>();
1760 let expected_remote_selections = active_selections
1761 .iter()
1762 .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
1763 .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
1764 .collect::<Vec<_>>();
1765 assert_eq!(
1766 actual_remote_selections,
1767 expected_remote_selections,
1768 "Replica {} remote selections != expected selections",
1769 buffer.replica_id()
1770 );
1771 }
1772}
1773
1774#[test]
1775fn test_contiguous_ranges() {
1776 assert_eq!(
1777 contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
1778 &[1..4, 5..7, 9..13]
1779 );
1780
1781 // Respects the `max_len` parameter
1782 assert_eq!(
1783 contiguous_ranges(
1784 [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
1785 3
1786 )
1787 .collect::<Vec<_>>(),
1788 &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
1789 );
1790}
1791
1792impl Buffer {
1793 pub fn enclosing_bracket_point_ranges<T: ToOffset>(
1794 &self,
1795 range: Range<T>,
1796 ) -> Option<(Range<Point>, Range<Point>)> {
1797 self.snapshot()
1798 .enclosing_bracket_ranges(range)
1799 .map(|(start, end)| {
1800 let point_start = start.start.to_point(self)..start.end.to_point(self);
1801 let point_end = end.start.to_point(self)..end.end.to_point(self);
1802 (point_start, point_end)
1803 })
1804 }
1805}
1806
1807fn ruby_lang() -> Language {
1808 Language::new(
1809 LanguageConfig {
1810 name: "Ruby".into(),
1811 path_suffixes: vec!["rb".to_string()],
1812 ..Default::default()
1813 },
1814 Some(tree_sitter_ruby::language()),
1815 )
1816 .with_indents_query(
1817 r#"
1818 (class "end" @end) @indent
1819 (method "end" @end) @indent
1820 (rescue) @outdent
1821 (then) @indent
1822 "#,
1823 )
1824 .unwrap()
1825}
1826
1827fn rust_lang() -> Language {
1828 Language::new(
1829 LanguageConfig {
1830 name: "Rust".into(),
1831 path_suffixes: vec!["rs".to_string()],
1832 ..Default::default()
1833 },
1834 Some(tree_sitter_rust::language()),
1835 )
1836 .with_indents_query(
1837 r#"
1838 (call_expression) @indent
1839 (field_expression) @indent
1840 (_ "(" ")" @end) @indent
1841 (_ "{" "}" @end) @indent
1842 "#,
1843 )
1844 .unwrap()
1845 .with_brackets_query(
1846 r#"
1847 ("{" @open "}" @close)
1848 "#,
1849 )
1850 .unwrap()
1851 .with_outline_query(
1852 r#"
1853 (struct_item
1854 "struct" @context
1855 name: (_) @name) @item
1856 (enum_item
1857 "enum" @context
1858 name: (_) @name) @item
1859 (enum_variant
1860 name: (_) @name) @item
1861 (field_declaration
1862 name: (_) @name) @item
1863 (impl_item
1864 "impl" @context
1865 trait: (_)? @name
1866 "for"? @context
1867 type: (_) @name) @item
1868 (function_item
1869 "fn" @context
1870 name: (_) @name) @item
1871 (mod_item
1872 "mod" @context
1873 name: (_) @name) @item
1874 "#,
1875 )
1876 .unwrap()
1877}
1878
1879fn json_lang() -> Language {
1880 Language::new(
1881 LanguageConfig {
1882 name: "Json".into(),
1883 path_suffixes: vec!["js".to_string()],
1884 ..Default::default()
1885 },
1886 Some(tree_sitter_json::language()),
1887 )
1888}
1889
1890fn get_tree_sexp(buffer: &ModelHandle<Buffer>, cx: &gpui::TestAppContext) -> String {
1891 buffer.read_with(cx, |buffer, _| {
1892 let snapshot = buffer.snapshot();
1893 let layers = snapshot.syntax.layers(buffer.as_text_snapshot());
1894 layers[0].node.to_sexp()
1895 })
1896}
1897
1898fn empty(point: Point) -> Range<Point> {
1899 point..point
1900}