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_serialization(cx: &mut gpui::MutableAppContext) {
814 let mut now = Instant::now();
815
816 let buffer1 = cx.add_model(|cx| {
817 let mut buffer = Buffer::new(0, "abc", cx);
818 buffer.edit([(3..3, "D")], cx);
819
820 now += Duration::from_secs(1);
821 buffer.start_transaction_at(now);
822 buffer.edit([(4..4, "E")], cx);
823 buffer.end_transaction_at(now, cx);
824 assert_eq!(buffer.text(), "abcDE");
825
826 buffer.undo(cx);
827 assert_eq!(buffer.text(), "abcD");
828
829 buffer.edit([(4..4, "F")], cx);
830 assert_eq!(buffer.text(), "abcDF");
831 buffer
832 });
833 assert_eq!(buffer1.read(cx).text(), "abcDF");
834
835 let message = buffer1.read(cx).to_proto();
836 let buffer2 = cx.add_model(|cx| Buffer::from_proto(1, message, None, cx).unwrap());
837 assert_eq!(buffer2.read(cx).text(), "abcDF");
838}
839
840#[gpui::test(iterations = 100)]
841fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) {
842 let min_peers = env::var("MIN_PEERS")
843 .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
844 .unwrap_or(1);
845 let max_peers = env::var("MAX_PEERS")
846 .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
847 .unwrap_or(5);
848 let operations = env::var("OPERATIONS")
849 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
850 .unwrap_or(10);
851
852 let base_text_len = rng.gen_range(0..10);
853 let base_text = RandomCharIter::new(&mut rng)
854 .take(base_text_len)
855 .collect::<String>();
856 let mut replica_ids = Vec::new();
857 let mut buffers = Vec::new();
858 let network = Rc::new(RefCell::new(Network::new(rng.clone())));
859 let base_buffer = cx.add_model(|cx| Buffer::new(0, base_text.as_str(), cx));
860
861 for i in 0..rng.gen_range(min_peers..=max_peers) {
862 let buffer = cx.add_model(|cx| {
863 let mut buffer =
864 Buffer::from_proto(i as ReplicaId, base_buffer.read(cx).to_proto(), None, cx)
865 .unwrap();
866 buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
867 let network = network.clone();
868 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
869 if let Event::Operation(op) = event {
870 network
871 .borrow_mut()
872 .broadcast(buffer.replica_id(), vec![proto::serialize_operation(&op)]);
873 }
874 })
875 .detach();
876 buffer
877 });
878 buffers.push(buffer);
879 replica_ids.push(i as ReplicaId);
880 network.borrow_mut().add_peer(i as ReplicaId);
881 log::info!("Adding initial peer with replica id {}", i);
882 }
883
884 log::info!("initial text: {:?}", base_text);
885
886 let mut now = Instant::now();
887 let mut mutation_count = operations;
888 let mut next_diagnostic_id = 0;
889 let mut active_selections = BTreeMap::default();
890 loop {
891 let replica_index = rng.gen_range(0..replica_ids.len());
892 let replica_id = replica_ids[replica_index];
893 let buffer = &mut buffers[replica_index];
894 let mut new_buffer = None;
895 match rng.gen_range(0..100) {
896 0..=29 if mutation_count != 0 => {
897 buffer.update(cx, |buffer, cx| {
898 buffer.start_transaction_at(now);
899 buffer.randomly_edit(&mut rng, 5, cx);
900 buffer.end_transaction_at(now, cx);
901 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
902 });
903 mutation_count -= 1;
904 }
905 30..=39 if mutation_count != 0 => {
906 buffer.update(cx, |buffer, cx| {
907 let mut selections = Vec::new();
908 for id in 0..rng.gen_range(1..=5) {
909 let range = buffer.random_byte_range(0, &mut rng);
910 selections.push(Selection {
911 id,
912 start: buffer.anchor_before(range.start),
913 end: buffer.anchor_before(range.end),
914 reversed: false,
915 goal: SelectionGoal::None,
916 });
917 }
918 let selections: Arc<[Selection<Anchor>]> = selections.into();
919 log::info!(
920 "peer {} setting active selections: {:?}",
921 replica_id,
922 selections
923 );
924 active_selections.insert(replica_id, selections.clone());
925 buffer.set_active_selections(selections, false, cx);
926 });
927 mutation_count -= 1;
928 }
929 40..=49 if mutation_count != 0 && replica_id == 0 => {
930 let entry_count = rng.gen_range(1..=5);
931 buffer.update(cx, |buffer, cx| {
932 let diagnostics = DiagnosticSet::new(
933 (0..entry_count).map(|_| {
934 let range = buffer.random_byte_range(0, &mut rng);
935 let range = range.to_point_utf16(buffer);
936 DiagnosticEntry {
937 range,
938 diagnostic: Diagnostic {
939 message: post_inc(&mut next_diagnostic_id).to_string(),
940 ..Default::default()
941 },
942 }
943 }),
944 buffer,
945 );
946 log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
947 buffer.update_diagnostics(diagnostics, cx);
948 });
949 mutation_count -= 1;
950 }
951 50..=59 if replica_ids.len() < max_peers => {
952 let old_buffer = buffer.read(cx).to_proto();
953 let new_replica_id = (0..=replica_ids.len() as ReplicaId)
954 .filter(|replica_id| *replica_id != buffer.read(cx).replica_id())
955 .choose(&mut rng)
956 .unwrap();
957 log::info!(
958 "Adding new replica {} (replicating from {})",
959 new_replica_id,
960 replica_id
961 );
962 new_buffer = Some(cx.add_model(|cx| {
963 let mut new_buffer =
964 Buffer::from_proto(new_replica_id, old_buffer, None, cx).unwrap();
965 log::info!(
966 "New replica {} text: {:?}",
967 new_buffer.replica_id(),
968 new_buffer.text()
969 );
970 new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
971 let network = network.clone();
972 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
973 if let Event::Operation(op) = event {
974 network.borrow_mut().broadcast(
975 buffer.replica_id(),
976 vec![proto::serialize_operation(&op)],
977 );
978 }
979 })
980 .detach();
981 new_buffer
982 }));
983 network.borrow_mut().replicate(replica_id, new_replica_id);
984
985 if new_replica_id as usize == replica_ids.len() {
986 replica_ids.push(new_replica_id);
987 } else {
988 let new_buffer = new_buffer.take().unwrap();
989 while network.borrow().has_unreceived(new_replica_id) {
990 let ops = network
991 .borrow_mut()
992 .receive(new_replica_id)
993 .into_iter()
994 .map(|op| proto::deserialize_operation(op).unwrap());
995 if ops.len() > 0 {
996 log::info!(
997 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
998 new_replica_id,
999 buffer.read(cx).version(),
1000 ops.len(),
1001 ops
1002 );
1003 new_buffer.update(cx, |new_buffer, cx| {
1004 new_buffer.apply_ops(ops, cx).unwrap();
1005 });
1006 }
1007 }
1008 buffers[new_replica_id as usize] = new_buffer;
1009 }
1010 }
1011 60..=69 if mutation_count != 0 => {
1012 buffer.update(cx, |buffer, cx| {
1013 buffer.randomly_undo_redo(&mut rng, cx);
1014 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1015 });
1016 mutation_count -= 1;
1017 }
1018 _ if network.borrow().has_unreceived(replica_id) => {
1019 let ops = network
1020 .borrow_mut()
1021 .receive(replica_id)
1022 .into_iter()
1023 .map(|op| proto::deserialize_operation(op).unwrap());
1024 if ops.len() > 0 {
1025 log::info!(
1026 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1027 replica_id,
1028 buffer.read(cx).version(),
1029 ops.len(),
1030 ops
1031 );
1032 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
1033 }
1034 }
1035 _ => {}
1036 }
1037
1038 now += Duration::from_millis(rng.gen_range(0..=200));
1039 buffers.extend(new_buffer);
1040
1041 for buffer in &buffers {
1042 buffer.read(cx).check_invariants();
1043 }
1044
1045 if mutation_count == 0 && network.borrow().is_idle() {
1046 break;
1047 }
1048 }
1049
1050 let first_buffer = buffers[0].read(cx).snapshot();
1051 for buffer in &buffers[1..] {
1052 let buffer = buffer.read(cx).snapshot();
1053 assert_eq!(
1054 buffer.version(),
1055 first_buffer.version(),
1056 "Replica {} version != Replica 0 version",
1057 buffer.replica_id()
1058 );
1059 assert_eq!(
1060 buffer.text(),
1061 first_buffer.text(),
1062 "Replica {} text != Replica 0 text",
1063 buffer.replica_id()
1064 );
1065 assert_eq!(
1066 buffer
1067 .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
1068 .collect::<Vec<_>>(),
1069 first_buffer
1070 .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
1071 .collect::<Vec<_>>(),
1072 "Replica {} diagnostics != Replica 0 diagnostics",
1073 buffer.replica_id()
1074 );
1075 }
1076
1077 for buffer in &buffers {
1078 let buffer = buffer.read(cx).snapshot();
1079 let actual_remote_selections = buffer
1080 .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
1081 .map(|(replica_id, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
1082 .collect::<Vec<_>>();
1083 let expected_remote_selections = active_selections
1084 .iter()
1085 .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
1086 .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
1087 .collect::<Vec<_>>();
1088 assert_eq!(
1089 actual_remote_selections,
1090 expected_remote_selections,
1091 "Replica {} remote selections != expected selections",
1092 buffer.replica_id()
1093 );
1094 }
1095}
1096
1097#[test]
1098fn test_contiguous_ranges() {
1099 assert_eq!(
1100 contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
1101 &[1..4, 5..7, 9..13]
1102 );
1103
1104 // Respects the `max_len` parameter
1105 assert_eq!(
1106 contiguous_ranges(
1107 [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
1108 3
1109 )
1110 .collect::<Vec<_>>(),
1111 &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
1112 );
1113}
1114
1115impl Buffer {
1116 pub fn enclosing_bracket_point_ranges<T: ToOffset>(
1117 &self,
1118 range: Range<T>,
1119 ) -> Option<(Range<Point>, Range<Point>)> {
1120 self.snapshot()
1121 .enclosing_bracket_ranges(range)
1122 .map(|(start, end)| {
1123 let point_start = start.start.to_point(self)..start.end.to_point(self);
1124 let point_end = end.start.to_point(self)..end.end.to_point(self);
1125 (point_start, point_end)
1126 })
1127 }
1128}
1129
1130fn rust_lang() -> Language {
1131 Language::new(
1132 LanguageConfig {
1133 name: "Rust".into(),
1134 path_suffixes: vec!["rs".to_string()],
1135 ..Default::default()
1136 },
1137 Some(tree_sitter_rust::language()),
1138 )
1139 .with_indents_query(
1140 r#"
1141 (call_expression) @indent
1142 (field_expression) @indent
1143 (_ "(" ")" @end) @indent
1144 (_ "{" "}" @end) @indent
1145 "#,
1146 )
1147 .unwrap()
1148 .with_brackets_query(
1149 r#"
1150 ("{" @open "}" @close)
1151 "#,
1152 )
1153 .unwrap()
1154 .with_outline_query(
1155 r#"
1156 (struct_item
1157 "struct" @context
1158 name: (_) @name) @item
1159 (enum_item
1160 "enum" @context
1161 name: (_) @name) @item
1162 (enum_variant
1163 name: (_) @name) @item
1164 (field_declaration
1165 name: (_) @name) @item
1166 (impl_item
1167 "impl" @context
1168 trait: (_)? @name
1169 "for"? @context
1170 type: (_) @name) @item
1171 (function_item
1172 "fn" @context
1173 name: (_) @name) @item
1174 (mod_item
1175 "mod" @context
1176 name: (_) @name) @item
1177 "#,
1178 )
1179 .unwrap()
1180}
1181
1182fn json_lang() -> Language {
1183 Language::new(
1184 LanguageConfig {
1185 name: "Json".into(),
1186 path_suffixes: vec!["js".to_string()],
1187 ..Default::default()
1188 },
1189 Some(tree_sitter_json::language()),
1190 )
1191}
1192
1193fn get_tree_sexp(buffer: &ModelHandle<Buffer>, cx: &gpui::TestAppContext) -> String {
1194 buffer.read_with(cx, |buffer, _| {
1195 buffer.syntax_tree().unwrap().root_node().to_sexp()
1196 })
1197}
1198
1199fn empty(point: Point) -> Range<Point> {
1200 point..point
1201}