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 = "
760 fn a() {
761 {
762 b()?
763 }
764 Ok(())
765 }
766 "
767 .unindent();
768 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
769
770 // Delete a closing curly brace changes the suggested indent for the line.
771 buffer.edit_with_autoindent(
772 [(Point::new(3, 4)..Point::new(3, 5), "")],
773 IndentSize::spaces(4),
774 cx,
775 );
776 assert_eq!(
777 buffer.text(),
778 "
779 fn a() {
780 {
781 b()?
782 |
783 Ok(())
784 }
785 "
786 .replace("|", "") // included in the string to preserve trailing whites
787 .unindent()
788 );
789
790 // Manually editing the leading whitespace
791 buffer.edit_with_autoindent(
792 [(Point::new(3, 0)..Point::new(3, 12), "")],
793 IndentSize::spaces(4),
794 cx,
795 );
796 assert_eq!(
797 buffer.text(),
798 "
799 fn a() {
800 {
801 b()?
802
803 Ok(())
804 }
805 "
806 .unindent()
807 );
808 buffer
809 });
810}
811
812#[gpui::test]
813fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut MutableAppContext) {
814 cx.add_model(|cx| {
815 let text = "
816 fn a() {}
817 "
818 .unindent();
819
820 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
821
822 buffer.edit_with_autoindent([(5..5, "\nb")], IndentSize::spaces(4), cx);
823 assert_eq!(
824 buffer.text(),
825 "
826 fn a(
827 b) {}
828 "
829 .unindent()
830 );
831
832 // The indentation suggestion changed because `@end` node (a close paren)
833 // is now at the beginning of the line.
834 buffer.edit_with_autoindent(
835 [(Point::new(1, 4)..Point::new(1, 5), "")],
836 IndentSize::spaces(4),
837 cx,
838 );
839 assert_eq!(
840 buffer.text(),
841 "
842 fn a(
843 ) {}
844 "
845 .unindent()
846 );
847
848 buffer
849 });
850}
851
852#[gpui::test]
853fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut MutableAppContext) {
854 cx.add_model(|cx| {
855 let text = "a\nb";
856 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
857 buffer.edit_with_autoindent([(0..1, "\n"), (2..3, "\n")], IndentSize::spaces(4), cx);
858 assert_eq!(buffer.text(), "\n\n\n");
859 buffer
860 });
861}
862
863#[gpui::test]
864fn test_autoindent_multi_line_insertion(cx: &mut MutableAppContext) {
865 cx.add_model(|cx| {
866 let text = "
867 const a: usize = 1;
868 fn b() {
869 if c {
870 let d = 2;
871 }
872 }
873 "
874 .unindent();
875
876 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
877 buffer.edit_with_autoindent(
878 [(Point::new(3, 0)..Point::new(3, 0), "e(\n f()\n);\n")],
879 IndentSize::spaces(4),
880 cx,
881 );
882 assert_eq!(
883 buffer.text(),
884 "
885 const a: usize = 1;
886 fn b() {
887 if c {
888 e(
889 f()
890 );
891 let d = 2;
892 }
893 }
894 "
895 .unindent()
896 );
897
898 buffer
899 });
900}
901
902#[gpui::test]
903fn test_autoindent_preserves_relative_indentation_in_multi_line_insertion(
904 cx: &mut MutableAppContext,
905) {
906 cx.add_model(|cx| {
907 let text = "
908 fn a() {
909 b();
910 }
911 "
912 .unindent();
913 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
914
915 let pasted_text = r#"
916 "
917 c
918 d
919 e
920 "
921 "#
922 .unindent();
923
924 // insert at the beginning of a line
925 buffer.edit_with_autoindent(
926 [(Point::new(2, 0)..Point::new(2, 0), pasted_text.clone())],
927 IndentSize::spaces(4),
928 cx,
929 );
930 assert_eq!(
931 buffer.text(),
932 r#"
933 fn a() {
934 b();
935 "
936 c
937 d
938 e
939 "
940 }
941 "#
942 .unindent()
943 );
944
945 buffer
946 });
947}
948
949#[gpui::test]
950fn test_autoindent_disabled(cx: &mut MutableAppContext) {
951 cx.add_model(|cx| {
952 let text = "
953 * one
954 - a
955 - b
956 * two
957 "
958 .unindent();
959
960 let mut buffer = Buffer::new(0, text, cx).with_language(
961 Arc::new(Language::new(
962 LanguageConfig {
963 name: "Markdown".into(),
964 ..Default::default()
965 },
966 Some(tree_sitter_json::language()),
967 )),
968 cx,
969 );
970 buffer.edit_with_autoindent(
971 [(Point::new(3, 0)..Point::new(3, 0), "\n")],
972 IndentSize::spaces(4),
973 cx,
974 );
975 assert_eq!(
976 buffer.text(),
977 "
978 * one
979 - a
980 - b
981
982 * two
983 "
984 .unindent()
985 );
986 buffer
987 });
988}
989
990#[gpui::test]
991fn test_serialization(cx: &mut gpui::MutableAppContext) {
992 let mut now = Instant::now();
993
994 let buffer1 = cx.add_model(|cx| {
995 let mut buffer = Buffer::new(0, "abc", cx);
996 buffer.edit([(3..3, "D")], cx);
997
998 now += Duration::from_secs(1);
999 buffer.start_transaction_at(now);
1000 buffer.edit([(4..4, "E")], cx);
1001 buffer.end_transaction_at(now, cx);
1002 assert_eq!(buffer.text(), "abcDE");
1003
1004 buffer.undo(cx);
1005 assert_eq!(buffer.text(), "abcD");
1006
1007 buffer.edit([(4..4, "F")], cx);
1008 assert_eq!(buffer.text(), "abcDF");
1009 buffer
1010 });
1011 assert_eq!(buffer1.read(cx).text(), "abcDF");
1012
1013 let message = buffer1.read(cx).to_proto();
1014 let buffer2 = cx.add_model(|cx| Buffer::from_proto(1, message, None, cx).unwrap());
1015 assert_eq!(buffer2.read(cx).text(), "abcDF");
1016}
1017
1018#[gpui::test(iterations = 100)]
1019fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) {
1020 let min_peers = env::var("MIN_PEERS")
1021 .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
1022 .unwrap_or(1);
1023 let max_peers = env::var("MAX_PEERS")
1024 .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
1025 .unwrap_or(5);
1026 let operations = env::var("OPERATIONS")
1027 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1028 .unwrap_or(10);
1029
1030 let base_text_len = rng.gen_range(0..10);
1031 let base_text = RandomCharIter::new(&mut rng)
1032 .take(base_text_len)
1033 .collect::<String>();
1034 let mut replica_ids = Vec::new();
1035 let mut buffers = Vec::new();
1036 let network = Rc::new(RefCell::new(Network::new(rng.clone())));
1037 let base_buffer = cx.add_model(|cx| Buffer::new(0, base_text.as_str(), cx));
1038
1039 for i in 0..rng.gen_range(min_peers..=max_peers) {
1040 let buffer = cx.add_model(|cx| {
1041 let mut buffer =
1042 Buffer::from_proto(i as ReplicaId, base_buffer.read(cx).to_proto(), None, cx)
1043 .unwrap();
1044 buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
1045 let network = network.clone();
1046 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1047 if let Event::Operation(op) = event {
1048 network
1049 .borrow_mut()
1050 .broadcast(buffer.replica_id(), vec![proto::serialize_operation(&op)]);
1051 }
1052 })
1053 .detach();
1054 buffer
1055 });
1056 buffers.push(buffer);
1057 replica_ids.push(i as ReplicaId);
1058 network.borrow_mut().add_peer(i as ReplicaId);
1059 log::info!("Adding initial peer with replica id {}", i);
1060 }
1061
1062 log::info!("initial text: {:?}", base_text);
1063
1064 let mut now = Instant::now();
1065 let mut mutation_count = operations;
1066 let mut next_diagnostic_id = 0;
1067 let mut active_selections = BTreeMap::default();
1068 loop {
1069 let replica_index = rng.gen_range(0..replica_ids.len());
1070 let replica_id = replica_ids[replica_index];
1071 let buffer = &mut buffers[replica_index];
1072 let mut new_buffer = None;
1073 match rng.gen_range(0..100) {
1074 0..=29 if mutation_count != 0 => {
1075 buffer.update(cx, |buffer, cx| {
1076 buffer.start_transaction_at(now);
1077 buffer.randomly_edit(&mut rng, 5, cx);
1078 buffer.end_transaction_at(now, cx);
1079 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1080 });
1081 mutation_count -= 1;
1082 }
1083 30..=39 if mutation_count != 0 => {
1084 buffer.update(cx, |buffer, cx| {
1085 let mut selections = Vec::new();
1086 for id in 0..rng.gen_range(1..=5) {
1087 let range = buffer.random_byte_range(0, &mut rng);
1088 selections.push(Selection {
1089 id,
1090 start: buffer.anchor_before(range.start),
1091 end: buffer.anchor_before(range.end),
1092 reversed: false,
1093 goal: SelectionGoal::None,
1094 });
1095 }
1096 let selections: Arc<[Selection<Anchor>]> = selections.into();
1097 log::info!(
1098 "peer {} setting active selections: {:?}",
1099 replica_id,
1100 selections
1101 );
1102 active_selections.insert(replica_id, selections.clone());
1103 buffer.set_active_selections(selections, false, cx);
1104 });
1105 mutation_count -= 1;
1106 }
1107 40..=49 if mutation_count != 0 && replica_id == 0 => {
1108 let entry_count = rng.gen_range(1..=5);
1109 buffer.update(cx, |buffer, cx| {
1110 let diagnostics = DiagnosticSet::new(
1111 (0..entry_count).map(|_| {
1112 let range = buffer.random_byte_range(0, &mut rng);
1113 let range = range.to_point_utf16(buffer);
1114 DiagnosticEntry {
1115 range,
1116 diagnostic: Diagnostic {
1117 message: post_inc(&mut next_diagnostic_id).to_string(),
1118 ..Default::default()
1119 },
1120 }
1121 }),
1122 buffer,
1123 );
1124 log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
1125 buffer.update_diagnostics(diagnostics, cx);
1126 });
1127 mutation_count -= 1;
1128 }
1129 50..=59 if replica_ids.len() < max_peers => {
1130 let old_buffer = buffer.read(cx).to_proto();
1131 let new_replica_id = (0..=replica_ids.len() as ReplicaId)
1132 .filter(|replica_id| *replica_id != buffer.read(cx).replica_id())
1133 .choose(&mut rng)
1134 .unwrap();
1135 log::info!(
1136 "Adding new replica {} (replicating from {})",
1137 new_replica_id,
1138 replica_id
1139 );
1140 new_buffer = Some(cx.add_model(|cx| {
1141 let mut new_buffer =
1142 Buffer::from_proto(new_replica_id, old_buffer, None, cx).unwrap();
1143 log::info!(
1144 "New replica {} text: {:?}",
1145 new_buffer.replica_id(),
1146 new_buffer.text()
1147 );
1148 new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
1149 let network = network.clone();
1150 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1151 if let Event::Operation(op) = event {
1152 network.borrow_mut().broadcast(
1153 buffer.replica_id(),
1154 vec![proto::serialize_operation(&op)],
1155 );
1156 }
1157 })
1158 .detach();
1159 new_buffer
1160 }));
1161 network.borrow_mut().replicate(replica_id, new_replica_id);
1162
1163 if new_replica_id as usize == replica_ids.len() {
1164 replica_ids.push(new_replica_id);
1165 } else {
1166 let new_buffer = new_buffer.take().unwrap();
1167 while network.borrow().has_unreceived(new_replica_id) {
1168 let ops = network
1169 .borrow_mut()
1170 .receive(new_replica_id)
1171 .into_iter()
1172 .map(|op| proto::deserialize_operation(op).unwrap());
1173 if ops.len() > 0 {
1174 log::info!(
1175 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1176 new_replica_id,
1177 buffer.read(cx).version(),
1178 ops.len(),
1179 ops
1180 );
1181 new_buffer.update(cx, |new_buffer, cx| {
1182 new_buffer.apply_ops(ops, cx).unwrap();
1183 });
1184 }
1185 }
1186 buffers[new_replica_id as usize] = new_buffer;
1187 }
1188 }
1189 60..=69 if mutation_count != 0 => {
1190 buffer.update(cx, |buffer, cx| {
1191 buffer.randomly_undo_redo(&mut rng, cx);
1192 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1193 });
1194 mutation_count -= 1;
1195 }
1196 _ if network.borrow().has_unreceived(replica_id) => {
1197 let ops = network
1198 .borrow_mut()
1199 .receive(replica_id)
1200 .into_iter()
1201 .map(|op| proto::deserialize_operation(op).unwrap());
1202 if ops.len() > 0 {
1203 log::info!(
1204 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1205 replica_id,
1206 buffer.read(cx).version(),
1207 ops.len(),
1208 ops
1209 );
1210 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
1211 }
1212 }
1213 _ => {}
1214 }
1215
1216 now += Duration::from_millis(rng.gen_range(0..=200));
1217 buffers.extend(new_buffer);
1218
1219 for buffer in &buffers {
1220 buffer.read(cx).check_invariants();
1221 }
1222
1223 if mutation_count == 0 && network.borrow().is_idle() {
1224 break;
1225 }
1226 }
1227
1228 let first_buffer = buffers[0].read(cx).snapshot();
1229 for buffer in &buffers[1..] {
1230 let buffer = buffer.read(cx).snapshot();
1231 assert_eq!(
1232 buffer.version(),
1233 first_buffer.version(),
1234 "Replica {} version != Replica 0 version",
1235 buffer.replica_id()
1236 );
1237 assert_eq!(
1238 buffer.text(),
1239 first_buffer.text(),
1240 "Replica {} text != Replica 0 text",
1241 buffer.replica_id()
1242 );
1243 assert_eq!(
1244 buffer
1245 .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
1246 .collect::<Vec<_>>(),
1247 first_buffer
1248 .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
1249 .collect::<Vec<_>>(),
1250 "Replica {} diagnostics != Replica 0 diagnostics",
1251 buffer.replica_id()
1252 );
1253 }
1254
1255 for buffer in &buffers {
1256 let buffer = buffer.read(cx).snapshot();
1257 let actual_remote_selections = buffer
1258 .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
1259 .map(|(replica_id, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
1260 .collect::<Vec<_>>();
1261 let expected_remote_selections = active_selections
1262 .iter()
1263 .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
1264 .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
1265 .collect::<Vec<_>>();
1266 assert_eq!(
1267 actual_remote_selections,
1268 expected_remote_selections,
1269 "Replica {} remote selections != expected selections",
1270 buffer.replica_id()
1271 );
1272 }
1273}
1274
1275#[test]
1276fn test_contiguous_ranges() {
1277 assert_eq!(
1278 contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
1279 &[1..4, 5..7, 9..13]
1280 );
1281
1282 // Respects the `max_len` parameter
1283 assert_eq!(
1284 contiguous_ranges(
1285 [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
1286 3
1287 )
1288 .collect::<Vec<_>>(),
1289 &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
1290 );
1291}
1292
1293impl Buffer {
1294 pub fn enclosing_bracket_point_ranges<T: ToOffset>(
1295 &self,
1296 range: Range<T>,
1297 ) -> Option<(Range<Point>, Range<Point>)> {
1298 self.snapshot()
1299 .enclosing_bracket_ranges(range)
1300 .map(|(start, end)| {
1301 let point_start = start.start.to_point(self)..start.end.to_point(self);
1302 let point_end = end.start.to_point(self)..end.end.to_point(self);
1303 (point_start, point_end)
1304 })
1305 }
1306}
1307
1308fn rust_lang() -> Language {
1309 Language::new(
1310 LanguageConfig {
1311 name: "Rust".into(),
1312 path_suffixes: vec!["rs".to_string()],
1313 ..Default::default()
1314 },
1315 Some(tree_sitter_rust::language()),
1316 )
1317 .with_indents_query(
1318 r#"
1319 (call_expression) @indent
1320 (field_expression) @indent
1321 (_ "(" ")" @end) @indent
1322 (_ "{" "}" @end) @indent
1323 "#,
1324 )
1325 .unwrap()
1326 .with_brackets_query(
1327 r#"
1328 ("{" @open "}" @close)
1329 "#,
1330 )
1331 .unwrap()
1332 .with_outline_query(
1333 r#"
1334 (struct_item
1335 "struct" @context
1336 name: (_) @name) @item
1337 (enum_item
1338 "enum" @context
1339 name: (_) @name) @item
1340 (enum_variant
1341 name: (_) @name) @item
1342 (field_declaration
1343 name: (_) @name) @item
1344 (impl_item
1345 "impl" @context
1346 trait: (_)? @name
1347 "for"? @context
1348 type: (_) @name) @item
1349 (function_item
1350 "fn" @context
1351 name: (_) @name) @item
1352 (mod_item
1353 "mod" @context
1354 name: (_) @name) @item
1355 "#,
1356 )
1357 .unwrap()
1358}
1359
1360fn json_lang() -> Language {
1361 Language::new(
1362 LanguageConfig {
1363 name: "Json".into(),
1364 path_suffixes: vec!["js".to_string()],
1365 ..Default::default()
1366 },
1367 Some(tree_sitter_json::language()),
1368 )
1369}
1370
1371fn get_tree_sexp(buffer: &ModelHandle<Buffer>, cx: &gpui::TestAppContext) -> String {
1372 buffer.read_with(cx, |buffer, _| {
1373 buffer.syntax_tree().unwrap().root_node().to_sexp()
1374 })
1375}
1376
1377fn empty(point: Point) -> Range<Point> {
1378 point..point
1379}