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