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_disabled(cx: &mut MutableAppContext) {
904 cx.add_model(|cx| {
905 let text = "
906 * one
907 - a
908 - b
909 * two
910 "
911 .unindent();
912
913 let mut buffer = Buffer::new(0, text, cx).with_language(
914 Arc::new(Language::new(
915 LanguageConfig {
916 name: "Markdown".into(),
917 ..Default::default()
918 },
919 Some(tree_sitter_json::language()),
920 )),
921 cx,
922 );
923 buffer.edit_with_autoindent(
924 [(Point::new(3, 0)..Point::new(3, 0), "\n")],
925 IndentSize::spaces(4),
926 cx,
927 );
928 assert_eq!(
929 buffer.text(),
930 "
931 * one
932 - a
933 - b
934
935 * two
936 "
937 .unindent()
938 );
939 buffer
940 });
941}
942
943#[gpui::test]
944fn test_serialization(cx: &mut gpui::MutableAppContext) {
945 let mut now = Instant::now();
946
947 let buffer1 = cx.add_model(|cx| {
948 let mut buffer = Buffer::new(0, "abc", cx);
949 buffer.edit([(3..3, "D")], cx);
950
951 now += Duration::from_secs(1);
952 buffer.start_transaction_at(now);
953 buffer.edit([(4..4, "E")], cx);
954 buffer.end_transaction_at(now, cx);
955 assert_eq!(buffer.text(), "abcDE");
956
957 buffer.undo(cx);
958 assert_eq!(buffer.text(), "abcD");
959
960 buffer.edit([(4..4, "F")], cx);
961 assert_eq!(buffer.text(), "abcDF");
962 buffer
963 });
964 assert_eq!(buffer1.read(cx).text(), "abcDF");
965
966 let message = buffer1.read(cx).to_proto();
967 let buffer2 = cx.add_model(|cx| Buffer::from_proto(1, message, None, cx).unwrap());
968 assert_eq!(buffer2.read(cx).text(), "abcDF");
969}
970
971#[gpui::test(iterations = 100)]
972fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) {
973 let min_peers = env::var("MIN_PEERS")
974 .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
975 .unwrap_or(1);
976 let max_peers = env::var("MAX_PEERS")
977 .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
978 .unwrap_or(5);
979 let operations = env::var("OPERATIONS")
980 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
981 .unwrap_or(10);
982
983 let base_text_len = rng.gen_range(0..10);
984 let base_text = RandomCharIter::new(&mut rng)
985 .take(base_text_len)
986 .collect::<String>();
987 let mut replica_ids = Vec::new();
988 let mut buffers = Vec::new();
989 let network = Rc::new(RefCell::new(Network::new(rng.clone())));
990 let base_buffer = cx.add_model(|cx| Buffer::new(0, base_text.as_str(), cx));
991
992 for i in 0..rng.gen_range(min_peers..=max_peers) {
993 let buffer = cx.add_model(|cx| {
994 let mut buffer =
995 Buffer::from_proto(i as ReplicaId, base_buffer.read(cx).to_proto(), None, cx)
996 .unwrap();
997 buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
998 let network = network.clone();
999 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1000 if let Event::Operation(op) = event {
1001 network
1002 .borrow_mut()
1003 .broadcast(buffer.replica_id(), vec![proto::serialize_operation(&op)]);
1004 }
1005 })
1006 .detach();
1007 buffer
1008 });
1009 buffers.push(buffer);
1010 replica_ids.push(i as ReplicaId);
1011 network.borrow_mut().add_peer(i as ReplicaId);
1012 log::info!("Adding initial peer with replica id {}", i);
1013 }
1014
1015 log::info!("initial text: {:?}", base_text);
1016
1017 let mut now = Instant::now();
1018 let mut mutation_count = operations;
1019 let mut next_diagnostic_id = 0;
1020 let mut active_selections = BTreeMap::default();
1021 loop {
1022 let replica_index = rng.gen_range(0..replica_ids.len());
1023 let replica_id = replica_ids[replica_index];
1024 let buffer = &mut buffers[replica_index];
1025 let mut new_buffer = None;
1026 match rng.gen_range(0..100) {
1027 0..=29 if mutation_count != 0 => {
1028 buffer.update(cx, |buffer, cx| {
1029 buffer.start_transaction_at(now);
1030 buffer.randomly_edit(&mut rng, 5, cx);
1031 buffer.end_transaction_at(now, cx);
1032 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1033 });
1034 mutation_count -= 1;
1035 }
1036 30..=39 if mutation_count != 0 => {
1037 buffer.update(cx, |buffer, cx| {
1038 let mut selections = Vec::new();
1039 for id in 0..rng.gen_range(1..=5) {
1040 let range = buffer.random_byte_range(0, &mut rng);
1041 selections.push(Selection {
1042 id,
1043 start: buffer.anchor_before(range.start),
1044 end: buffer.anchor_before(range.end),
1045 reversed: false,
1046 goal: SelectionGoal::None,
1047 });
1048 }
1049 let selections: Arc<[Selection<Anchor>]> = selections.into();
1050 log::info!(
1051 "peer {} setting active selections: {:?}",
1052 replica_id,
1053 selections
1054 );
1055 active_selections.insert(replica_id, selections.clone());
1056 buffer.set_active_selections(selections, false, cx);
1057 });
1058 mutation_count -= 1;
1059 }
1060 40..=49 if mutation_count != 0 && replica_id == 0 => {
1061 let entry_count = rng.gen_range(1..=5);
1062 buffer.update(cx, |buffer, cx| {
1063 let diagnostics = DiagnosticSet::new(
1064 (0..entry_count).map(|_| {
1065 let range = buffer.random_byte_range(0, &mut rng);
1066 let range = range.to_point_utf16(buffer);
1067 DiagnosticEntry {
1068 range,
1069 diagnostic: Diagnostic {
1070 message: post_inc(&mut next_diagnostic_id).to_string(),
1071 ..Default::default()
1072 },
1073 }
1074 }),
1075 buffer,
1076 );
1077 log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
1078 buffer.update_diagnostics(diagnostics, cx);
1079 });
1080 mutation_count -= 1;
1081 }
1082 50..=59 if replica_ids.len() < max_peers => {
1083 let old_buffer = buffer.read(cx).to_proto();
1084 let new_replica_id = (0..=replica_ids.len() as ReplicaId)
1085 .filter(|replica_id| *replica_id != buffer.read(cx).replica_id())
1086 .choose(&mut rng)
1087 .unwrap();
1088 log::info!(
1089 "Adding new replica {} (replicating from {})",
1090 new_replica_id,
1091 replica_id
1092 );
1093 new_buffer = Some(cx.add_model(|cx| {
1094 let mut new_buffer =
1095 Buffer::from_proto(new_replica_id, old_buffer, None, cx).unwrap();
1096 log::info!(
1097 "New replica {} text: {:?}",
1098 new_buffer.replica_id(),
1099 new_buffer.text()
1100 );
1101 new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
1102 let network = network.clone();
1103 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1104 if let Event::Operation(op) = event {
1105 network.borrow_mut().broadcast(
1106 buffer.replica_id(),
1107 vec![proto::serialize_operation(&op)],
1108 );
1109 }
1110 })
1111 .detach();
1112 new_buffer
1113 }));
1114 network.borrow_mut().replicate(replica_id, new_replica_id);
1115
1116 if new_replica_id as usize == replica_ids.len() {
1117 replica_ids.push(new_replica_id);
1118 } else {
1119 let new_buffer = new_buffer.take().unwrap();
1120 while network.borrow().has_unreceived(new_replica_id) {
1121 let ops = network
1122 .borrow_mut()
1123 .receive(new_replica_id)
1124 .into_iter()
1125 .map(|op| proto::deserialize_operation(op).unwrap());
1126 if ops.len() > 0 {
1127 log::info!(
1128 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1129 new_replica_id,
1130 buffer.read(cx).version(),
1131 ops.len(),
1132 ops
1133 );
1134 new_buffer.update(cx, |new_buffer, cx| {
1135 new_buffer.apply_ops(ops, cx).unwrap();
1136 });
1137 }
1138 }
1139 buffers[new_replica_id as usize] = new_buffer;
1140 }
1141 }
1142 60..=69 if mutation_count != 0 => {
1143 buffer.update(cx, |buffer, cx| {
1144 buffer.randomly_undo_redo(&mut rng, cx);
1145 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1146 });
1147 mutation_count -= 1;
1148 }
1149 _ if network.borrow().has_unreceived(replica_id) => {
1150 let ops = network
1151 .borrow_mut()
1152 .receive(replica_id)
1153 .into_iter()
1154 .map(|op| proto::deserialize_operation(op).unwrap());
1155 if ops.len() > 0 {
1156 log::info!(
1157 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1158 replica_id,
1159 buffer.read(cx).version(),
1160 ops.len(),
1161 ops
1162 );
1163 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
1164 }
1165 }
1166 _ => {}
1167 }
1168
1169 now += Duration::from_millis(rng.gen_range(0..=200));
1170 buffers.extend(new_buffer);
1171
1172 for buffer in &buffers {
1173 buffer.read(cx).check_invariants();
1174 }
1175
1176 if mutation_count == 0 && network.borrow().is_idle() {
1177 break;
1178 }
1179 }
1180
1181 let first_buffer = buffers[0].read(cx).snapshot();
1182 for buffer in &buffers[1..] {
1183 let buffer = buffer.read(cx).snapshot();
1184 assert_eq!(
1185 buffer.version(),
1186 first_buffer.version(),
1187 "Replica {} version != Replica 0 version",
1188 buffer.replica_id()
1189 );
1190 assert_eq!(
1191 buffer.text(),
1192 first_buffer.text(),
1193 "Replica {} text != Replica 0 text",
1194 buffer.replica_id()
1195 );
1196 assert_eq!(
1197 buffer
1198 .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
1199 .collect::<Vec<_>>(),
1200 first_buffer
1201 .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
1202 .collect::<Vec<_>>(),
1203 "Replica {} diagnostics != Replica 0 diagnostics",
1204 buffer.replica_id()
1205 );
1206 }
1207
1208 for buffer in &buffers {
1209 let buffer = buffer.read(cx).snapshot();
1210 let actual_remote_selections = buffer
1211 .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
1212 .map(|(replica_id, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
1213 .collect::<Vec<_>>();
1214 let expected_remote_selections = active_selections
1215 .iter()
1216 .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
1217 .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
1218 .collect::<Vec<_>>();
1219 assert_eq!(
1220 actual_remote_selections,
1221 expected_remote_selections,
1222 "Replica {} remote selections != expected selections",
1223 buffer.replica_id()
1224 );
1225 }
1226}
1227
1228#[test]
1229fn test_contiguous_ranges() {
1230 assert_eq!(
1231 contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
1232 &[1..4, 5..7, 9..13]
1233 );
1234
1235 // Respects the `max_len` parameter
1236 assert_eq!(
1237 contiguous_ranges(
1238 [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
1239 3
1240 )
1241 .collect::<Vec<_>>(),
1242 &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
1243 );
1244}
1245
1246impl Buffer {
1247 pub fn enclosing_bracket_point_ranges<T: ToOffset>(
1248 &self,
1249 range: Range<T>,
1250 ) -> Option<(Range<Point>, Range<Point>)> {
1251 self.snapshot()
1252 .enclosing_bracket_ranges(range)
1253 .map(|(start, end)| {
1254 let point_start = start.start.to_point(self)..start.end.to_point(self);
1255 let point_end = end.start.to_point(self)..end.end.to_point(self);
1256 (point_start, point_end)
1257 })
1258 }
1259}
1260
1261fn rust_lang() -> Language {
1262 Language::new(
1263 LanguageConfig {
1264 name: "Rust".into(),
1265 path_suffixes: vec!["rs".to_string()],
1266 ..Default::default()
1267 },
1268 Some(tree_sitter_rust::language()),
1269 )
1270 .with_indents_query(
1271 r#"
1272 (call_expression) @indent
1273 (field_expression) @indent
1274 (_ "(" ")" @end) @indent
1275 (_ "{" "}" @end) @indent
1276 "#,
1277 )
1278 .unwrap()
1279 .with_brackets_query(
1280 r#"
1281 ("{" @open "}" @close)
1282 "#,
1283 )
1284 .unwrap()
1285 .with_outline_query(
1286 r#"
1287 (struct_item
1288 "struct" @context
1289 name: (_) @name) @item
1290 (enum_item
1291 "enum" @context
1292 name: (_) @name) @item
1293 (enum_variant
1294 name: (_) @name) @item
1295 (field_declaration
1296 name: (_) @name) @item
1297 (impl_item
1298 "impl" @context
1299 trait: (_)? @name
1300 "for"? @context
1301 type: (_) @name) @item
1302 (function_item
1303 "fn" @context
1304 name: (_) @name) @item
1305 (mod_item
1306 "mod" @context
1307 name: (_) @name) @item
1308 "#,
1309 )
1310 .unwrap()
1311}
1312
1313fn json_lang() -> Language {
1314 Language::new(
1315 LanguageConfig {
1316 name: "Json".into(),
1317 path_suffixes: vec!["js".to_string()],
1318 ..Default::default()
1319 },
1320 Some(tree_sitter_json::language()),
1321 )
1322}
1323
1324fn get_tree_sexp(buffer: &ModelHandle<Buffer>, cx: &gpui::TestAppContext) -> String {
1325 buffer.read_with(cx, |buffer, _| {
1326 buffer.syntax_tree().unwrap().root_node().to_sexp()
1327 })
1328}
1329
1330fn empty(point: Point) -> Range<Point> {
1331 point..point
1332}