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