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