1use super::*;
2use clock::ReplicaId;
3use collections::BTreeMap;
4use gpui::{ModelHandle, MutableAppContext};
5use rand::prelude::*;
6use std::{
7 cell::RefCell,
8 env,
9 ops::Range,
10 rc::Rc,
11 time::{Duration, Instant},
12};
13use text::network::Network;
14use unindent::Unindent as _;
15use util::post_inc;
16
17#[cfg(test)]
18#[ctor::ctor]
19fn init_logger() {
20 if std::env::var("RUST_LOG").is_ok() {
21 env_logger::init();
22 }
23}
24
25#[gpui::test]
26fn test_select_language() {
27 let registry = LanguageRegistry::test();
28 registry.add(Arc::new(Language::new(
29 LanguageConfig {
30 name: "Rust".into(),
31 path_suffixes: vec!["rs".to_string()],
32 ..Default::default()
33 },
34 Some(tree_sitter_rust::language()),
35 )));
36 registry.add(Arc::new(Language::new(
37 LanguageConfig {
38 name: "Make".into(),
39 path_suffixes: vec!["Makefile".to_string(), "mk".to_string()],
40 ..Default::default()
41 },
42 Some(tree_sitter_rust::language()),
43 )));
44
45 // matching file extension
46 assert_eq!(
47 registry.select_language("zed/lib.rs").map(|l| l.name()),
48 Some("Rust".into())
49 );
50 assert_eq!(
51 registry.select_language("zed/lib.mk").map(|l| l.name()),
52 Some("Make".into())
53 );
54
55 // matching filename
56 assert_eq!(
57 registry.select_language("zed/Makefile").map(|l| l.name()),
58 Some("Make".into())
59 );
60
61 // matching suffix that is not the full file extension or filename
62 assert_eq!(registry.select_language("zed/cars").map(|l| l.name()), None);
63 assert_eq!(
64 registry.select_language("zed/a.cars").map(|l| l.name()),
65 None
66 );
67 assert_eq!(registry.select_language("zed/sumk").map(|l| l.name()), None);
68}
69
70#[gpui::test]
71fn test_edit_events(cx: &mut gpui::MutableAppContext) {
72 let mut now = Instant::now();
73 let buffer_1_events = Rc::new(RefCell::new(Vec::new()));
74 let buffer_2_events = Rc::new(RefCell::new(Vec::new()));
75
76 let buffer1 = cx.add_model(|cx| Buffer::new(0, "abcdef", cx));
77 let buffer2 = cx.add_model(|cx| Buffer::new(1, "abcdef", cx));
78 let buffer1_ops = Rc::new(RefCell::new(Vec::new()));
79 buffer1.update(cx, {
80 let buffer1_ops = buffer1_ops.clone();
81 |buffer, cx| {
82 let buffer_1_events = buffer_1_events.clone();
83 cx.subscribe(&buffer1, move |_, _, event, _| match event.clone() {
84 Event::Operation(op) => buffer1_ops.borrow_mut().push(op),
85 event @ _ => buffer_1_events.borrow_mut().push(event),
86 })
87 .detach();
88 let buffer_2_events = buffer_2_events.clone();
89 cx.subscribe(&buffer2, move |_, _, event, _| {
90 buffer_2_events.borrow_mut().push(event.clone())
91 })
92 .detach();
93
94 // An edit emits an edited event, followed by a dirty changed event,
95 // since the buffer was previously in a clean state.
96 buffer.edit([(2..4, "XYZ")], cx);
97
98 // An empty transaction does not emit any events.
99 buffer.start_transaction();
100 buffer.end_transaction(cx);
101
102 // A transaction containing two edits emits one edited event.
103 now += Duration::from_secs(1);
104 buffer.start_transaction_at(now);
105 buffer.edit([(5..5, "u")], cx);
106 buffer.edit([(6..6, "w")], cx);
107 buffer.end_transaction_at(now, cx);
108
109 // Undoing a transaction emits one edited event.
110 buffer.undo(cx);
111 }
112 });
113
114 // Incorporating a set of remote ops emits a single edited event,
115 // followed by a dirty changed event.
116 buffer2.update(cx, |buffer, cx| {
117 buffer
118 .apply_ops(buffer1_ops.borrow_mut().drain(..), cx)
119 .unwrap();
120 });
121 assert_eq!(
122 mem::take(&mut *buffer_1_events.borrow_mut()),
123 vec![
124 Event::Edited,
125 Event::DirtyChanged,
126 Event::Edited,
127 Event::Edited,
128 ]
129 );
130 assert_eq!(
131 mem::take(&mut *buffer_2_events.borrow_mut()),
132 vec![Event::Edited, Event::DirtyChanged]
133 );
134
135 buffer1.update(cx, |buffer, cx| {
136 // Undoing the first transaction emits edited event, followed by a
137 // dirty changed event, since the buffer is again in a clean state.
138 buffer.undo(cx);
139 });
140 // Incorporating the remote ops again emits a single edited event,
141 // followed by a dirty changed event.
142 buffer2.update(cx, |buffer, cx| {
143 buffer
144 .apply_ops(buffer1_ops.borrow_mut().drain(..), cx)
145 .unwrap();
146 });
147 assert_eq!(
148 mem::take(&mut *buffer_1_events.borrow_mut()),
149 vec![Event::Edited, Event::DirtyChanged,]
150 );
151 assert_eq!(
152 mem::take(&mut *buffer_2_events.borrow_mut()),
153 vec![Event::Edited, Event::DirtyChanged]
154 );
155}
156
157#[gpui::test]
158async fn test_apply_diff(cx: &mut gpui::TestAppContext) {
159 let text = "a\nbb\nccc\ndddd\neeeee\nffffff\n";
160 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
161
162 let text = "a\nccc\ndddd\nffffff\n";
163 let diff = buffer.read_with(cx, |b, cx| b.diff(text.into(), cx)).await;
164 buffer.update(cx, |buffer, cx| {
165 buffer.apply_diff(diff, cx).unwrap();
166 });
167 cx.read(|cx| assert_eq!(buffer.read(cx).text(), text));
168
169 let text = "a\n1\n\nccc\ndd2dd\nffffff\n";
170 let diff = buffer.read_with(cx, |b, cx| b.diff(text.into(), cx)).await;
171 buffer.update(cx, |buffer, cx| {
172 buffer.apply_diff(diff, cx).unwrap();
173 });
174 cx.read(|cx| assert_eq!(buffer.read(cx).text(), text));
175}
176
177#[gpui::test]
178async fn test_reparse(cx: &mut gpui::TestAppContext) {
179 let text = "fn a() {}";
180 let buffer =
181 cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
182
183 // Wait for the initial text to parse
184 buffer
185 .condition(&cx, |buffer, _| !buffer.is_parsing())
186 .await;
187 assert_eq!(
188 get_tree_sexp(&buffer, &cx),
189 concat!(
190 "(source_file (function_item name: (identifier) ",
191 "parameters: (parameters) ",
192 "body: (block)))"
193 )
194 );
195
196 buffer.update(cx, |buffer, _| {
197 buffer.set_sync_parse_timeout(Duration::ZERO)
198 });
199
200 // Perform some edits (add parameter and variable reference)
201 // Parsing doesn't begin until the transaction is complete
202 buffer.update(cx, |buf, cx| {
203 buf.start_transaction();
204
205 let offset = buf.text().find(")").unwrap();
206 buf.edit([(offset..offset, "b: C")], cx);
207 assert!(!buf.is_parsing());
208
209 let offset = buf.text().find("}").unwrap();
210 buf.edit([(offset..offset, " d; ")], cx);
211 assert!(!buf.is_parsing());
212
213 buf.end_transaction(cx);
214 assert_eq!(buf.text(), "fn a(b: C) { d; }");
215 assert!(buf.is_parsing());
216 });
217 buffer
218 .condition(&cx, |buffer, _| !buffer.is_parsing())
219 .await;
220 assert_eq!(
221 get_tree_sexp(&buffer, &cx),
222 concat!(
223 "(source_file (function_item name: (identifier) ",
224 "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
225 "body: (block (expression_statement (identifier)))))"
226 )
227 );
228
229 // Perform a series of edits without waiting for the current parse to complete:
230 // * turn identifier into a field expression
231 // * turn field expression into a method call
232 // * add a turbofish to the method call
233 buffer.update(cx, |buf, cx| {
234 let offset = buf.text().find(";").unwrap();
235 buf.edit([(offset..offset, ".e")], cx);
236 assert_eq!(buf.text(), "fn a(b: C) { d.e; }");
237 assert!(buf.is_parsing());
238 });
239 buffer.update(cx, |buf, cx| {
240 let offset = buf.text().find(";").unwrap();
241 buf.edit([(offset..offset, "(f)")], cx);
242 assert_eq!(buf.text(), "fn a(b: C) { d.e(f); }");
243 assert!(buf.is_parsing());
244 });
245 buffer.update(cx, |buf, cx| {
246 let offset = buf.text().find("(f)").unwrap();
247 buf.edit([(offset..offset, "::<G>")], cx);
248 assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
249 assert!(buf.is_parsing());
250 });
251 buffer
252 .condition(&cx, |buffer, _| !buffer.is_parsing())
253 .await;
254 assert_eq!(
255 get_tree_sexp(&buffer, &cx),
256 concat!(
257 "(source_file (function_item name: (identifier) ",
258 "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
259 "body: (block (expression_statement (call_expression ",
260 "function: (generic_function ",
261 "function: (field_expression value: (identifier) field: (field_identifier)) ",
262 "type_arguments: (type_arguments (type_identifier))) ",
263 "arguments: (arguments (identifier)))))))",
264 )
265 );
266
267 buffer.update(cx, |buf, cx| {
268 buf.undo(cx);
269 assert_eq!(buf.text(), "fn a() {}");
270 assert!(buf.is_parsing());
271 });
272 buffer
273 .condition(&cx, |buffer, _| !buffer.is_parsing())
274 .await;
275 assert_eq!(
276 get_tree_sexp(&buffer, &cx),
277 concat!(
278 "(source_file (function_item name: (identifier) ",
279 "parameters: (parameters) ",
280 "body: (block)))"
281 )
282 );
283
284 buffer.update(cx, |buf, cx| {
285 buf.redo(cx);
286 assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
287 assert!(buf.is_parsing());
288 });
289 buffer
290 .condition(&cx, |buffer, _| !buffer.is_parsing())
291 .await;
292 assert_eq!(
293 get_tree_sexp(&buffer, &cx),
294 concat!(
295 "(source_file (function_item name: (identifier) ",
296 "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
297 "body: (block (expression_statement (call_expression ",
298 "function: (generic_function ",
299 "function: (field_expression value: (identifier) field: (field_identifier)) ",
300 "type_arguments: (type_arguments (type_identifier))) ",
301 "arguments: (arguments (identifier)))))))",
302 )
303 );
304}
305
306#[gpui::test]
307async fn test_resetting_language(cx: &mut gpui::TestAppContext) {
308 let buffer = cx.add_model(|cx| {
309 let mut buffer = Buffer::new(0, "{}", cx).with_language(Arc::new(rust_lang()), cx);
310 buffer.set_sync_parse_timeout(Duration::ZERO);
311 buffer
312 });
313
314 // Wait for the initial text to parse
315 buffer
316 .condition(&cx, |buffer, _| !buffer.is_parsing())
317 .await;
318 assert_eq!(
319 get_tree_sexp(&buffer, &cx),
320 "(source_file (expression_statement (block)))"
321 );
322
323 buffer.update(cx, |buffer, cx| {
324 buffer.set_language(Some(Arc::new(json_lang())), cx)
325 });
326 buffer
327 .condition(&cx, |buffer, _| !buffer.is_parsing())
328 .await;
329 assert_eq!(get_tree_sexp(&buffer, &cx), "(document (object))");
330}
331
332#[gpui::test]
333async fn test_outline(cx: &mut gpui::TestAppContext) {
334 let text = r#"
335 struct Person {
336 name: String,
337 age: usize,
338 }
339
340 mod module {
341 enum LoginState {
342 LoggedOut,
343 LoggingOn,
344 LoggedIn {
345 person: Person,
346 time: Instant,
347 }
348 }
349 }
350
351 impl Eq for Person {}
352
353 impl Drop for Person {
354 fn drop(&mut self) {
355 println!("bye");
356 }
357 }
358 "#
359 .unindent();
360
361 let buffer =
362 cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
363 let outline = buffer
364 .read_with(cx, |buffer, _| buffer.snapshot().outline(None))
365 .unwrap();
366
367 assert_eq!(
368 outline
369 .items
370 .iter()
371 .map(|item| (item.text.as_str(), item.depth))
372 .collect::<Vec<_>>(),
373 &[
374 ("struct Person", 0),
375 ("name", 1),
376 ("age", 1),
377 ("mod module", 0),
378 ("enum LoginState", 1),
379 ("LoggedOut", 2),
380 ("LoggingOn", 2),
381 ("LoggedIn", 2),
382 ("person", 3),
383 ("time", 3),
384 ("impl Eq for Person", 0),
385 ("impl Drop for Person", 0),
386 ("fn drop", 1),
387 ]
388 );
389
390 // Without space, we only match on names
391 assert_eq!(
392 search(&outline, "oon", &cx).await,
393 &[
394 ("mod module", vec![]), // included as the parent of a match
395 ("enum LoginState", vec![]), // included as the parent of a match
396 ("LoggingOn", vec![1, 7, 8]), // matches
397 ("impl Drop for Person", vec![7, 18, 19]), // matches in two disjoint names
398 ]
399 );
400
401 assert_eq!(
402 search(&outline, "dp p", &cx).await,
403 &[
404 ("impl Drop for Person", vec![5, 8, 9, 14]),
405 ("fn drop", vec![]),
406 ]
407 );
408 assert_eq!(
409 search(&outline, "dpn", &cx).await,
410 &[("impl Drop for Person", vec![5, 14, 19])]
411 );
412 assert_eq!(
413 search(&outline, "impl ", &cx).await,
414 &[
415 ("impl Eq for Person", vec![0, 1, 2, 3, 4]),
416 ("impl Drop for Person", vec![0, 1, 2, 3, 4]),
417 ("fn drop", vec![]),
418 ]
419 );
420
421 async fn search<'a>(
422 outline: &'a Outline<Anchor>,
423 query: &str,
424 cx: &gpui::TestAppContext,
425 ) -> Vec<(&'a str, Vec<usize>)> {
426 let matches = cx
427 .read(|cx| outline.search(query, cx.background().clone()))
428 .await;
429 matches
430 .into_iter()
431 .map(|mat| (outline.items[mat.candidate_id].text.as_str(), mat.positions))
432 .collect::<Vec<_>>()
433 }
434}
435
436#[gpui::test]
437async fn test_symbols_containing(cx: &mut gpui::TestAppContext) {
438 let text = r#"
439 impl Person {
440 fn one() {
441 1
442 }
443
444 fn two() {
445 2
446 }fn three() {
447 3
448 }
449 }
450 "#
451 .unindent();
452
453 let buffer =
454 cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
455 let snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot());
456
457 // point is at the start of an item
458 assert_eq!(
459 symbols_containing(Point::new(1, 4), &snapshot),
460 vec![
461 (
462 "impl Person".to_string(),
463 Point::new(0, 0)..Point::new(10, 1)
464 ),
465 ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
466 ]
467 );
468
469 // point is in the middle of an item
470 assert_eq!(
471 symbols_containing(Point::new(2, 8), &snapshot),
472 vec![
473 (
474 "impl Person".to_string(),
475 Point::new(0, 0)..Point::new(10, 1)
476 ),
477 ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
478 ]
479 );
480
481 // point is at the end of an item
482 assert_eq!(
483 symbols_containing(Point::new(3, 5), &snapshot),
484 vec![
485 (
486 "impl Person".to_string(),
487 Point::new(0, 0)..Point::new(10, 1)
488 ),
489 ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
490 ]
491 );
492
493 // point is in between two adjacent items
494 assert_eq!(
495 symbols_containing(Point::new(7, 5), &snapshot),
496 vec![
497 (
498 "impl Person".to_string(),
499 Point::new(0, 0)..Point::new(10, 1)
500 ),
501 ("fn two".to_string(), Point::new(5, 4)..Point::new(7, 5))
502 ]
503 );
504
505 fn symbols_containing<'a>(
506 position: Point,
507 snapshot: &'a BufferSnapshot,
508 ) -> Vec<(String, Range<Point>)> {
509 snapshot
510 .symbols_containing(position, None)
511 .unwrap()
512 .into_iter()
513 .map(|item| {
514 (
515 item.text,
516 item.range.start.to_point(snapshot)..item.range.end.to_point(snapshot),
517 )
518 })
519 .collect()
520 }
521}
522
523#[gpui::test]
524fn test_enclosing_bracket_ranges(cx: &mut MutableAppContext) {
525 let buffer = cx.add_model(|cx| {
526 let text = "
527 mod x {
528 mod y {
529
530 }
531 }
532 "
533 .unindent();
534 Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx)
535 });
536 let buffer = buffer.read(cx);
537 assert_eq!(
538 buffer.enclosing_bracket_point_ranges(Point::new(1, 6)..Point::new(1, 6)),
539 Some((
540 Point::new(0, 6)..Point::new(0, 7),
541 Point::new(4, 0)..Point::new(4, 1)
542 ))
543 );
544 assert_eq!(
545 buffer.enclosing_bracket_point_ranges(Point::new(1, 10)..Point::new(1, 10)),
546 Some((
547 Point::new(1, 10)..Point::new(1, 11),
548 Point::new(3, 4)..Point::new(3, 5)
549 ))
550 );
551 assert_eq!(
552 buffer.enclosing_bracket_point_ranges(Point::new(3, 5)..Point::new(3, 5)),
553 Some((
554 Point::new(1, 10)..Point::new(1, 11),
555 Point::new(3, 4)..Point::new(3, 5)
556 ))
557 );
558}
559
560#[gpui::test]
561fn test_range_for_syntax_ancestor(cx: &mut MutableAppContext) {
562 cx.add_model(|cx| {
563 let text = "fn a() { b(|c| {}) }";
564 let buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
565 let snapshot = buffer.snapshot();
566
567 assert_eq!(
568 snapshot.range_for_syntax_ancestor(empty_range_at(text, "|")),
569 Some(range_of(text, "|"))
570 );
571 assert_eq!(
572 snapshot.range_for_syntax_ancestor(range_of(text, "|")),
573 Some(range_of(text, "|c|"))
574 );
575 assert_eq!(
576 snapshot.range_for_syntax_ancestor(range_of(text, "|c|")),
577 Some(range_of(text, "|c| {}"))
578 );
579 assert_eq!(
580 snapshot.range_for_syntax_ancestor(range_of(text, "|c| {}")),
581 Some(range_of(text, "(|c| {})"))
582 );
583
584 buffer
585 });
586
587 fn empty_range_at(text: &str, part: &str) -> Range<usize> {
588 let start = text.find(part).unwrap();
589 start..start
590 }
591
592 fn range_of(text: &str, part: &str) -> Range<usize> {
593 let start = text.find(part).unwrap();
594 start..start + part.len()
595 }
596}
597
598#[gpui::test]
599fn test_autoindent_with_soft_tabs(cx: &mut MutableAppContext) {
600 cx.add_model(|cx| {
601 let text = "fn a() {}";
602 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
603
604 buffer.edit_with_autoindent([(8..8, "\n\n")], IndentSize::spaces(4), cx);
605 assert_eq!(buffer.text(), "fn a() {\n \n}");
606
607 buffer.edit_with_autoindent(
608 [(Point::new(1, 4)..Point::new(1, 4), "b()\n")],
609 IndentSize::spaces(4),
610 cx,
611 );
612 assert_eq!(buffer.text(), "fn a() {\n b()\n \n}");
613
614 // Create a field expression on a new line, causing that line
615 // to be indented.
616 buffer.edit_with_autoindent(
617 [(Point::new(2, 4)..Point::new(2, 4), ".c")],
618 IndentSize::spaces(4),
619 cx,
620 );
621 assert_eq!(buffer.text(), "fn a() {\n b()\n .c\n}");
622
623 // Remove the dot so that the line is no longer a field expression,
624 // causing the line to be outdented.
625 buffer.edit_with_autoindent(
626 [(Point::new(2, 8)..Point::new(2, 9), "")],
627 IndentSize::spaces(4),
628 cx,
629 );
630 assert_eq!(buffer.text(), "fn a() {\n b()\n c\n}");
631
632 buffer
633 });
634}
635
636#[gpui::test]
637fn test_autoindent_with_hard_tabs(cx: &mut MutableAppContext) {
638 cx.add_model(|cx| {
639 let text = "fn a() {}";
640 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
641
642 buffer.edit_with_autoindent([(8..8, "\n\n")], IndentSize::tab(), cx);
643 assert_eq!(buffer.text(), "fn a() {\n\t\n}");
644
645 buffer.edit_with_autoindent(
646 [(Point::new(1, 1)..Point::new(1, 1), "b()\n")],
647 IndentSize::tab(),
648 cx,
649 );
650 assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\n}");
651
652 // Create a field expression on a new line, causing that line
653 // to be indented.
654 buffer.edit_with_autoindent(
655 [(Point::new(2, 1)..Point::new(2, 1), ".c")],
656 IndentSize::tab(),
657 cx,
658 );
659 assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\t.c\n}");
660
661 // Remove the dot so that the line is no longer a field expression,
662 // causing the line to be outdented.
663 buffer.edit_with_autoindent(
664 [(Point::new(2, 2)..Point::new(2, 3), "")],
665 IndentSize::tab(),
666 cx,
667 );
668 assert_eq!(buffer.text(), "fn a() {\n\tb()\n\tc\n}");
669
670 buffer
671 });
672}
673
674#[gpui::test]
675fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut MutableAppContext) {
676 cx.add_model(|cx| {
677 let text = "
678 fn a() {
679 c;
680 d;
681 }
682 "
683 .unindent();
684
685 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
686
687 // Lines 2 and 3 don't match the indentation suggestion. When editing these lines,
688 // their indentation is not adjusted.
689 buffer.edit_with_autoindent(
690 [
691 (empty(Point::new(1, 1)), "()"),
692 (empty(Point::new(2, 1)), "()"),
693 ],
694 IndentSize::spaces(4),
695 cx,
696 );
697 assert_eq!(
698 buffer.text(),
699 "
700 fn a() {
701 c();
702 d();
703 }
704 "
705 .unindent()
706 );
707
708 // When appending new content after these lines, the indentation is based on the
709 // preceding lines' actual indentation.
710 buffer.edit_with_autoindent(
711 [
712 (empty(Point::new(1, 1)), "\n.f\n.g"),
713 (empty(Point::new(2, 1)), "\n.f\n.g"),
714 ],
715 IndentSize::spaces(4),
716 cx,
717 );
718 assert_eq!(
719 buffer.text(),
720 "
721 fn a() {
722 c
723 .f
724 .g();
725 d
726 .f
727 .g();
728 }
729 "
730 .unindent()
731 );
732 buffer
733 });
734
735 cx.add_model(|cx| {
736 let text = "fn a() {\n {\n b()?\n }\n\n Ok(())\n}";
737 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
738 buffer.edit_with_autoindent(
739 [(Point::new(3, 4)..Point::new(3, 5), "")],
740 IndentSize::spaces(4),
741 cx,
742 );
743 assert_eq!(
744 buffer.text(),
745 "fn a() {\n {\n b()?\n \n\n Ok(())\n}"
746 );
747
748 buffer.edit_with_autoindent(
749 [(Point::new(3, 0)..Point::new(3, 12), "")],
750 IndentSize::spaces(4),
751 cx,
752 );
753 assert_eq!(
754 buffer.text(),
755 "fn a() {\n {\n b()?\n\n\n Ok(())\n}"
756 );
757 buffer
758 });
759}
760
761#[gpui::test]
762fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut MutableAppContext) {
763 cx.add_model(|cx| {
764 let text = "
765 fn a() {}
766 "
767 .unindent();
768
769 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
770
771 buffer.edit_with_autoindent([(5..5, "\nb")], IndentSize::spaces(4), cx);
772 assert_eq!(
773 buffer.text(),
774 "
775 fn a(
776 b) {}
777 "
778 .unindent()
779 );
780
781 // The indentation suggestion changed because `@end` node (a close paren)
782 // is now at the beginning of the line.
783 buffer.edit_with_autoindent(
784 [(Point::new(1, 4)..Point::new(1, 5), "")],
785 IndentSize::spaces(4),
786 cx,
787 );
788 assert_eq!(
789 buffer.text(),
790 "
791 fn a(
792 ) {}
793 "
794 .unindent()
795 );
796
797 buffer
798 });
799}
800
801#[gpui::test]
802fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut MutableAppContext) {
803 cx.add_model(|cx| {
804 let text = "a\nb";
805 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
806 buffer.edit_with_autoindent([(0..1, "\n"), (2..3, "\n")], IndentSize::spaces(4), cx);
807 assert_eq!(buffer.text(), "\n\n\n");
808 buffer
809 });
810}
811
812#[gpui::test]
813fn test_autoindent_disabled(cx: &mut MutableAppContext) {
814 cx.add_model(|cx| {
815 let text = "
816 * one
817 - a
818 - b
819 * two
820 "
821 .unindent();
822
823 let mut buffer = Buffer::new(0, text, cx).with_language(
824 Arc::new(Language::new(
825 LanguageConfig {
826 name: "Markdown".into(),
827 ..Default::default()
828 },
829 Some(tree_sitter_json::language()),
830 )),
831 cx,
832 );
833 buffer.edit_with_autoindent(
834 [(Point::new(3, 0)..Point::new(3, 0), "\n")],
835 IndentSize::spaces(4),
836 cx,
837 );
838 assert_eq!(
839 buffer.text(),
840 "
841 * one
842 - a
843 - b
844
845 * two
846 "
847 .unindent()
848 );
849 buffer
850 });
851}
852
853#[gpui::test]
854fn test_serialization(cx: &mut gpui::MutableAppContext) {
855 let mut now = Instant::now();
856
857 let buffer1 = cx.add_model(|cx| {
858 let mut buffer = Buffer::new(0, "abc", cx);
859 buffer.edit([(3..3, "D")], cx);
860
861 now += Duration::from_secs(1);
862 buffer.start_transaction_at(now);
863 buffer.edit([(4..4, "E")], cx);
864 buffer.end_transaction_at(now, cx);
865 assert_eq!(buffer.text(), "abcDE");
866
867 buffer.undo(cx);
868 assert_eq!(buffer.text(), "abcD");
869
870 buffer.edit([(4..4, "F")], cx);
871 assert_eq!(buffer.text(), "abcDF");
872 buffer
873 });
874 assert_eq!(buffer1.read(cx).text(), "abcDF");
875
876 let message = buffer1.read(cx).to_proto();
877 let buffer2 = cx.add_model(|cx| Buffer::from_proto(1, message, None, cx).unwrap());
878 assert_eq!(buffer2.read(cx).text(), "abcDF");
879}
880
881#[gpui::test(iterations = 100)]
882fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) {
883 let min_peers = env::var("MIN_PEERS")
884 .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
885 .unwrap_or(1);
886 let max_peers = env::var("MAX_PEERS")
887 .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
888 .unwrap_or(5);
889 let operations = env::var("OPERATIONS")
890 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
891 .unwrap_or(10);
892
893 let base_text_len = rng.gen_range(0..10);
894 let base_text = RandomCharIter::new(&mut rng)
895 .take(base_text_len)
896 .collect::<String>();
897 let mut replica_ids = Vec::new();
898 let mut buffers = Vec::new();
899 let network = Rc::new(RefCell::new(Network::new(rng.clone())));
900 let base_buffer = cx.add_model(|cx| Buffer::new(0, base_text.as_str(), cx));
901
902 for i in 0..rng.gen_range(min_peers..=max_peers) {
903 let buffer = cx.add_model(|cx| {
904 let mut buffer =
905 Buffer::from_proto(i as ReplicaId, base_buffer.read(cx).to_proto(), None, cx)
906 .unwrap();
907 buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
908 let network = network.clone();
909 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
910 if let Event::Operation(op) = event {
911 network
912 .borrow_mut()
913 .broadcast(buffer.replica_id(), vec![proto::serialize_operation(&op)]);
914 }
915 })
916 .detach();
917 buffer
918 });
919 buffers.push(buffer);
920 replica_ids.push(i as ReplicaId);
921 network.borrow_mut().add_peer(i as ReplicaId);
922 log::info!("Adding initial peer with replica id {}", i);
923 }
924
925 log::info!("initial text: {:?}", base_text);
926
927 let mut now = Instant::now();
928 let mut mutation_count = operations;
929 let mut next_diagnostic_id = 0;
930 let mut active_selections = BTreeMap::default();
931 loop {
932 let replica_index = rng.gen_range(0..replica_ids.len());
933 let replica_id = replica_ids[replica_index];
934 let buffer = &mut buffers[replica_index];
935 let mut new_buffer = None;
936 match rng.gen_range(0..100) {
937 0..=29 if mutation_count != 0 => {
938 buffer.update(cx, |buffer, cx| {
939 buffer.start_transaction_at(now);
940 buffer.randomly_edit(&mut rng, 5, cx);
941 buffer.end_transaction_at(now, cx);
942 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
943 });
944 mutation_count -= 1;
945 }
946 30..=39 if mutation_count != 0 => {
947 buffer.update(cx, |buffer, cx| {
948 let mut selections = Vec::new();
949 for id in 0..rng.gen_range(1..=5) {
950 let range = buffer.random_byte_range(0, &mut rng);
951 selections.push(Selection {
952 id,
953 start: buffer.anchor_before(range.start),
954 end: buffer.anchor_before(range.end),
955 reversed: false,
956 goal: SelectionGoal::None,
957 });
958 }
959 let selections: Arc<[Selection<Anchor>]> = selections.into();
960 log::info!(
961 "peer {} setting active selections: {:?}",
962 replica_id,
963 selections
964 );
965 active_selections.insert(replica_id, selections.clone());
966 buffer.set_active_selections(selections, false, cx);
967 });
968 mutation_count -= 1;
969 }
970 40..=49 if mutation_count != 0 && replica_id == 0 => {
971 let entry_count = rng.gen_range(1..=5);
972 buffer.update(cx, |buffer, cx| {
973 let diagnostics = DiagnosticSet::new(
974 (0..entry_count).map(|_| {
975 let range = buffer.random_byte_range(0, &mut rng);
976 let range = range.to_point_utf16(buffer);
977 DiagnosticEntry {
978 range,
979 diagnostic: Diagnostic {
980 message: post_inc(&mut next_diagnostic_id).to_string(),
981 ..Default::default()
982 },
983 }
984 }),
985 buffer,
986 );
987 log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
988 buffer.update_diagnostics(diagnostics, cx);
989 });
990 mutation_count -= 1;
991 }
992 50..=59 if replica_ids.len() < max_peers => {
993 let old_buffer = buffer.read(cx).to_proto();
994 let new_replica_id = (0..=replica_ids.len() as ReplicaId)
995 .filter(|replica_id| *replica_id != buffer.read(cx).replica_id())
996 .choose(&mut rng)
997 .unwrap();
998 log::info!(
999 "Adding new replica {} (replicating from {})",
1000 new_replica_id,
1001 replica_id
1002 );
1003 new_buffer = Some(cx.add_model(|cx| {
1004 let mut new_buffer =
1005 Buffer::from_proto(new_replica_id, old_buffer, None, cx).unwrap();
1006 log::info!(
1007 "New replica {} text: {:?}",
1008 new_buffer.replica_id(),
1009 new_buffer.text()
1010 );
1011 new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
1012 let network = network.clone();
1013 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1014 if let Event::Operation(op) = event {
1015 network.borrow_mut().broadcast(
1016 buffer.replica_id(),
1017 vec![proto::serialize_operation(&op)],
1018 );
1019 }
1020 })
1021 .detach();
1022 new_buffer
1023 }));
1024 network.borrow_mut().replicate(replica_id, new_replica_id);
1025
1026 if new_replica_id as usize == replica_ids.len() {
1027 replica_ids.push(new_replica_id);
1028 } else {
1029 let new_buffer = new_buffer.take().unwrap();
1030 while network.borrow().has_unreceived(new_replica_id) {
1031 let ops = network
1032 .borrow_mut()
1033 .receive(new_replica_id)
1034 .into_iter()
1035 .map(|op| proto::deserialize_operation(op).unwrap());
1036 if ops.len() > 0 {
1037 log::info!(
1038 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1039 new_replica_id,
1040 buffer.read(cx).version(),
1041 ops.len(),
1042 ops
1043 );
1044 new_buffer.update(cx, |new_buffer, cx| {
1045 new_buffer.apply_ops(ops, cx).unwrap();
1046 });
1047 }
1048 }
1049 buffers[new_replica_id as usize] = new_buffer;
1050 }
1051 }
1052 60..=69 if mutation_count != 0 => {
1053 buffer.update(cx, |buffer, cx| {
1054 buffer.randomly_undo_redo(&mut rng, cx);
1055 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1056 });
1057 mutation_count -= 1;
1058 }
1059 _ if network.borrow().has_unreceived(replica_id) => {
1060 let ops = network
1061 .borrow_mut()
1062 .receive(replica_id)
1063 .into_iter()
1064 .map(|op| proto::deserialize_operation(op).unwrap());
1065 if ops.len() > 0 {
1066 log::info!(
1067 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1068 replica_id,
1069 buffer.read(cx).version(),
1070 ops.len(),
1071 ops
1072 );
1073 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
1074 }
1075 }
1076 _ => {}
1077 }
1078
1079 now += Duration::from_millis(rng.gen_range(0..=200));
1080 buffers.extend(new_buffer);
1081
1082 for buffer in &buffers {
1083 buffer.read(cx).check_invariants();
1084 }
1085
1086 if mutation_count == 0 && network.borrow().is_idle() {
1087 break;
1088 }
1089 }
1090
1091 let first_buffer = buffers[0].read(cx).snapshot();
1092 for buffer in &buffers[1..] {
1093 let buffer = buffer.read(cx).snapshot();
1094 assert_eq!(
1095 buffer.version(),
1096 first_buffer.version(),
1097 "Replica {} version != Replica 0 version",
1098 buffer.replica_id()
1099 );
1100 assert_eq!(
1101 buffer.text(),
1102 first_buffer.text(),
1103 "Replica {} text != Replica 0 text",
1104 buffer.replica_id()
1105 );
1106 assert_eq!(
1107 buffer
1108 .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
1109 .collect::<Vec<_>>(),
1110 first_buffer
1111 .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
1112 .collect::<Vec<_>>(),
1113 "Replica {} diagnostics != Replica 0 diagnostics",
1114 buffer.replica_id()
1115 );
1116 }
1117
1118 for buffer in &buffers {
1119 let buffer = buffer.read(cx).snapshot();
1120 let actual_remote_selections = buffer
1121 .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
1122 .map(|(replica_id, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
1123 .collect::<Vec<_>>();
1124 let expected_remote_selections = active_selections
1125 .iter()
1126 .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
1127 .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
1128 .collect::<Vec<_>>();
1129 assert_eq!(
1130 actual_remote_selections,
1131 expected_remote_selections,
1132 "Replica {} remote selections != expected selections",
1133 buffer.replica_id()
1134 );
1135 }
1136}
1137
1138#[test]
1139fn test_contiguous_ranges() {
1140 assert_eq!(
1141 contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
1142 &[1..4, 5..7, 9..13]
1143 );
1144
1145 // Respects the `max_len` parameter
1146 assert_eq!(
1147 contiguous_ranges(
1148 [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
1149 3
1150 )
1151 .collect::<Vec<_>>(),
1152 &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
1153 );
1154}
1155
1156impl Buffer {
1157 pub fn enclosing_bracket_point_ranges<T: ToOffset>(
1158 &self,
1159 range: Range<T>,
1160 ) -> Option<(Range<Point>, Range<Point>)> {
1161 self.snapshot()
1162 .enclosing_bracket_ranges(range)
1163 .map(|(start, end)| {
1164 let point_start = start.start.to_point(self)..start.end.to_point(self);
1165 let point_end = end.start.to_point(self)..end.end.to_point(self);
1166 (point_start, point_end)
1167 })
1168 }
1169}
1170
1171fn rust_lang() -> Language {
1172 Language::new(
1173 LanguageConfig {
1174 name: "Rust".into(),
1175 path_suffixes: vec!["rs".to_string()],
1176 ..Default::default()
1177 },
1178 Some(tree_sitter_rust::language()),
1179 )
1180 .with_indents_query(
1181 r#"
1182 (call_expression) @indent
1183 (field_expression) @indent
1184 (_ "(" ")" @end) @indent
1185 (_ "{" "}" @end) @indent
1186 "#,
1187 )
1188 .unwrap()
1189 .with_brackets_query(
1190 r#"
1191 ("{" @open "}" @close)
1192 "#,
1193 )
1194 .unwrap()
1195 .with_outline_query(
1196 r#"
1197 (struct_item
1198 "struct" @context
1199 name: (_) @name) @item
1200 (enum_item
1201 "enum" @context
1202 name: (_) @name) @item
1203 (enum_variant
1204 name: (_) @name) @item
1205 (field_declaration
1206 name: (_) @name) @item
1207 (impl_item
1208 "impl" @context
1209 trait: (_)? @name
1210 "for"? @context
1211 type: (_) @name) @item
1212 (function_item
1213 "fn" @context
1214 name: (_) @name) @item
1215 (mod_item
1216 "mod" @context
1217 name: (_) @name) @item
1218 "#,
1219 )
1220 .unwrap()
1221}
1222
1223fn json_lang() -> Language {
1224 Language::new(
1225 LanguageConfig {
1226 name: "Json".into(),
1227 path_suffixes: vec!["js".to_string()],
1228 ..Default::default()
1229 },
1230 Some(tree_sitter_json::language()),
1231 )
1232}
1233
1234fn get_tree_sexp(buffer: &ModelHandle<Buffer>, cx: &gpui::TestAppContext) -> String {
1235 buffer.read_with(cx, |buffer, _| {
1236 buffer.syntax_tree().unwrap().root_node().to_sexp()
1237 })
1238}
1239
1240fn empty(point: Point) -> Range<Point> {
1241 point..point
1242}