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