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