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