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