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