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