1use super::*;
2use clock::ReplicaId;
3use collections::BTreeMap;
4use fs::LineEnding;
5use gpui::{ModelHandle, MutableAppContext};
6use proto::deserialize_operation;
7use rand::prelude::*;
8use settings::Settings;
9use std::{
10 cell::RefCell,
11 env,
12 ops::Range,
13 rc::Rc,
14 time::{Duration, Instant},
15};
16use text::network::Network;
17use unindent::Unindent as _;
18use util::{post_inc, test::marked_text_ranges, RandomCharIter};
19
20#[cfg(test)]
21#[ctor::ctor]
22fn init_logger() {
23 if std::env::var("RUST_LOG").is_ok() {
24 env_logger::init();
25 }
26}
27
28#[gpui::test]
29fn test_line_endings(cx: &mut gpui::MutableAppContext) {
30 cx.set_global(Settings::test(cx));
31 cx.add_model(|cx| {
32 let mut buffer =
33 Buffer::new(0, "one\r\ntwo\rthree", cx).with_language(Arc::new(rust_lang()), cx);
34 assert_eq!(buffer.text(), "one\ntwo\nthree");
35 assert_eq!(buffer.line_ending(), LineEnding::Windows);
36
37 buffer.check_invariants();
38 buffer.edit(
39 [(buffer.len()..buffer.len(), "\r\nfour")],
40 Some(AutoindentMode::EachLine),
41 cx,
42 );
43 buffer.edit([(0..0, "zero\r\n")], None, cx);
44 assert_eq!(buffer.text(), "zero\none\ntwo\nthree\nfour");
45 assert_eq!(buffer.line_ending(), LineEnding::Windows);
46 buffer.check_invariants();
47
48 buffer
49 });
50}
51
52#[gpui::test]
53fn test_select_language() {
54 let registry = Arc::new(LanguageRegistry::test());
55 registry.add(Arc::new(Language::new(
56 LanguageConfig {
57 name: "Rust".into(),
58 path_suffixes: vec!["rs".to_string()],
59 ..Default::default()
60 },
61 Some(tree_sitter_rust::language()),
62 )));
63 registry.add(Arc::new(Language::new(
64 LanguageConfig {
65 name: "Make".into(),
66 path_suffixes: vec!["Makefile".to_string(), "mk".to_string()],
67 ..Default::default()
68 },
69 Some(tree_sitter_rust::language()),
70 )));
71
72 // matching file extension
73 assert_eq!(
74 registry.language_for_path("zed/lib.rs").map(|l| l.name()),
75 Some("Rust".into())
76 );
77 assert_eq!(
78 registry.language_for_path("zed/lib.mk").map(|l| l.name()),
79 Some("Make".into())
80 );
81
82 // matching filename
83 assert_eq!(
84 registry.language_for_path("zed/Makefile").map(|l| l.name()),
85 Some("Make".into())
86 );
87
88 // matching suffix that is not the full file extension or filename
89 assert_eq!(
90 registry.language_for_path("zed/cars").map(|l| l.name()),
91 None
92 );
93 assert_eq!(
94 registry.language_for_path("zed/a.cars").map(|l| l.name()),
95 None
96 );
97 assert_eq!(
98 registry.language_for_path("zed/sumk").map(|l| l.name()),
99 None
100 );
101}
102
103#[gpui::test]
104fn test_edit_events(cx: &mut gpui::MutableAppContext) {
105 let mut now = Instant::now();
106 let buffer_1_events = Rc::new(RefCell::new(Vec::new()));
107 let buffer_2_events = Rc::new(RefCell::new(Vec::new()));
108
109 let buffer1 = cx.add_model(|cx| Buffer::new(0, "abcdef", cx));
110 let buffer2 = cx.add_model(|cx| Buffer::new(1, "abcdef", cx));
111 let buffer1_ops = Rc::new(RefCell::new(Vec::new()));
112 buffer1.update(cx, {
113 let buffer1_ops = buffer1_ops.clone();
114 |buffer, cx| {
115 let buffer_1_events = buffer_1_events.clone();
116 cx.subscribe(&buffer1, move |_, _, event, _| match event.clone() {
117 Event::Operation(op) => buffer1_ops.borrow_mut().push(op),
118 event => buffer_1_events.borrow_mut().push(event),
119 })
120 .detach();
121 let buffer_2_events = buffer_2_events.clone();
122 cx.subscribe(&buffer2, move |_, _, event, _| {
123 buffer_2_events.borrow_mut().push(event.clone())
124 })
125 .detach();
126
127 // An edit emits an edited event, followed by a dirty changed event,
128 // since the buffer was previously in a clean state.
129 buffer.edit([(2..4, "XYZ")], None, cx);
130
131 // An empty transaction does not emit any events.
132 buffer.start_transaction();
133 buffer.end_transaction(cx);
134
135 // A transaction containing two edits emits one edited event.
136 now += Duration::from_secs(1);
137 buffer.start_transaction_at(now);
138 buffer.edit([(5..5, "u")], None, cx);
139 buffer.edit([(6..6, "w")], None, cx);
140 buffer.end_transaction_at(now, cx);
141
142 // Undoing a transaction emits one edited event.
143 buffer.undo(cx);
144 }
145 });
146
147 // Incorporating a set of remote ops emits a single edited event,
148 // followed by a dirty changed event.
149 buffer2.update(cx, |buffer, cx| {
150 buffer
151 .apply_ops(buffer1_ops.borrow_mut().drain(..), cx)
152 .unwrap();
153 });
154 assert_eq!(
155 mem::take(&mut *buffer_1_events.borrow_mut()),
156 vec![
157 Event::Edited,
158 Event::DirtyChanged,
159 Event::Edited,
160 Event::Edited,
161 ]
162 );
163 assert_eq!(
164 mem::take(&mut *buffer_2_events.borrow_mut()),
165 vec![Event::Edited, Event::DirtyChanged]
166 );
167
168 buffer1.update(cx, |buffer, cx| {
169 // Undoing the first transaction emits edited event, followed by a
170 // dirty changed event, since the buffer is again in a clean state.
171 buffer.undo(cx);
172 });
173 // Incorporating the remote ops again emits a single edited event,
174 // followed by a dirty changed event.
175 buffer2.update(cx, |buffer, cx| {
176 buffer
177 .apply_ops(buffer1_ops.borrow_mut().drain(..), cx)
178 .unwrap();
179 });
180 assert_eq!(
181 mem::take(&mut *buffer_1_events.borrow_mut()),
182 vec![Event::Edited, Event::DirtyChanged,]
183 );
184 assert_eq!(
185 mem::take(&mut *buffer_2_events.borrow_mut()),
186 vec![Event::Edited, Event::DirtyChanged]
187 );
188}
189
190#[gpui::test]
191async fn test_apply_diff(cx: &mut gpui::TestAppContext) {
192 let text = "a\nbb\nccc\ndddd\neeeee\nffffff\n";
193 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
194 let anchor = buffer.read_with(cx, |buffer, _| buffer.anchor_before(Point::new(3, 3)));
195
196 let text = "a\nccc\ndddd\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(2, 3));
202 });
203
204 let text = "a\n1\n\nccc\ndd2dd\nffffff\n";
205 let diff = buffer.read_with(cx, |b, cx| b.diff(text.into(), cx)).await;
206 buffer.update(cx, |buffer, cx| {
207 buffer.apply_diff(diff, cx).unwrap();
208 assert_eq!(buffer.text(), text);
209 assert_eq!(anchor.to_point(buffer), Point::new(4, 4));
210 });
211}
212
213#[gpui::test]
214async fn test_reparse(cx: &mut gpui::TestAppContext) {
215 let text = "fn a() {}";
216 let buffer =
217 cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
218
219 // Wait for the initial text to parse
220 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
221 assert_eq!(
222 get_tree_sexp(&buffer, cx),
223 concat!(
224 "(source_file (function_item name: (identifier) ",
225 "parameters: (parameters) ",
226 "body: (block)))"
227 )
228 );
229
230 buffer.update(cx, |buffer, _| {
231 buffer.set_sync_parse_timeout(Duration::ZERO)
232 });
233
234 // Perform some edits (add parameter and variable reference)
235 // Parsing doesn't begin until the transaction is complete
236 buffer.update(cx, |buf, cx| {
237 buf.start_transaction();
238
239 let offset = buf.text().find(')').unwrap();
240 buf.edit([(offset..offset, "b: C")], None, cx);
241 assert!(!buf.is_parsing());
242
243 let offset = buf.text().find('}').unwrap();
244 buf.edit([(offset..offset, " d; ")], None, cx);
245 assert!(!buf.is_parsing());
246
247 buf.end_transaction(cx);
248 assert_eq!(buf.text(), "fn a(b: C) { d; }");
249 assert!(buf.is_parsing());
250 });
251 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
252 assert_eq!(
253 get_tree_sexp(&buffer, cx),
254 concat!(
255 "(source_file (function_item name: (identifier) ",
256 "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
257 "body: (block (expression_statement (identifier)))))"
258 )
259 );
260
261 // Perform a series of edits without waiting for the current parse to complete:
262 // * turn identifier into a field expression
263 // * turn field expression into a method call
264 // * add a turbofish to the method call
265 buffer.update(cx, |buf, cx| {
266 let offset = buf.text().find(';').unwrap();
267 buf.edit([(offset..offset, ".e")], None, cx);
268 assert_eq!(buf.text(), "fn a(b: C) { d.e; }");
269 assert!(buf.is_parsing());
270 });
271 buffer.update(cx, |buf, cx| {
272 let offset = buf.text().find(';').unwrap();
273 buf.edit([(offset..offset, "(f)")], None, cx);
274 assert_eq!(buf.text(), "fn a(b: C) { d.e(f); }");
275 assert!(buf.is_parsing());
276 });
277 buffer.update(cx, |buf, cx| {
278 let offset = buf.text().find("(f)").unwrap();
279 buf.edit([(offset..offset, "::<G>")], None, cx);
280 assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
281 assert!(buf.is_parsing());
282 });
283 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
284 assert_eq!(
285 get_tree_sexp(&buffer, cx),
286 concat!(
287 "(source_file (function_item name: (identifier) ",
288 "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
289 "body: (block (expression_statement (call_expression ",
290 "function: (generic_function ",
291 "function: (field_expression value: (identifier) field: (field_identifier)) ",
292 "type_arguments: (type_arguments (type_identifier))) ",
293 "arguments: (arguments (identifier)))))))",
294 )
295 );
296
297 buffer.update(cx, |buf, cx| {
298 buf.undo(cx);
299 buf.undo(cx);
300 buf.undo(cx);
301 buf.undo(cx);
302 assert_eq!(buf.text(), "fn a() {}");
303 assert!(buf.is_parsing());
304 });
305 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
306 assert_eq!(
307 get_tree_sexp(&buffer, cx),
308 concat!(
309 "(source_file (function_item name: (identifier) ",
310 "parameters: (parameters) ",
311 "body: (block)))"
312 )
313 );
314
315 buffer.update(cx, |buf, cx| {
316 buf.redo(cx);
317 buf.redo(cx);
318 buf.redo(cx);
319 buf.redo(cx);
320 assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
321 assert!(buf.is_parsing());
322 });
323 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
324 assert_eq!(
325 get_tree_sexp(&buffer, cx),
326 concat!(
327 "(source_file (function_item name: (identifier) ",
328 "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
329 "body: (block (expression_statement (call_expression ",
330 "function: (generic_function ",
331 "function: (field_expression value: (identifier) field: (field_identifier)) ",
332 "type_arguments: (type_arguments (type_identifier))) ",
333 "arguments: (arguments (identifier)))))))",
334 )
335 );
336}
337
338#[gpui::test]
339async fn test_resetting_language(cx: &mut gpui::TestAppContext) {
340 let buffer = cx.add_model(|cx| {
341 let mut buffer = Buffer::new(0, "{}", cx).with_language(Arc::new(rust_lang()), cx);
342 buffer.set_sync_parse_timeout(Duration::ZERO);
343 buffer
344 });
345
346 // Wait for the initial text to parse
347 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
348 assert_eq!(
349 get_tree_sexp(&buffer, cx),
350 "(source_file (expression_statement (block)))"
351 );
352
353 buffer.update(cx, |buffer, cx| {
354 buffer.set_language(Some(Arc::new(json_lang())), cx)
355 });
356 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
357 assert_eq!(get_tree_sexp(&buffer, cx), "(document (object))");
358}
359
360#[gpui::test]
361async fn test_outline(cx: &mut gpui::TestAppContext) {
362 let text = r#"
363 struct Person {
364 name: String,
365 age: usize,
366 }
367
368 mod module {
369 enum LoginState {
370 LoggedOut,
371 LoggingOn,
372 LoggedIn {
373 person: Person,
374 time: Instant,
375 }
376 }
377 }
378
379 impl Eq for Person {}
380
381 impl Drop for Person {
382 fn drop(&mut self) {
383 println!("bye");
384 }
385 }
386 "#
387 .unindent();
388
389 let buffer =
390 cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
391 let outline = buffer
392 .read_with(cx, |buffer, _| buffer.snapshot().outline(None))
393 .unwrap();
394
395 assert_eq!(
396 outline
397 .items
398 .iter()
399 .map(|item| (item.text.as_str(), item.depth))
400 .collect::<Vec<_>>(),
401 &[
402 ("struct Person", 0),
403 ("name", 1),
404 ("age", 1),
405 ("mod module", 0),
406 ("enum LoginState", 1),
407 ("LoggedOut", 2),
408 ("LoggingOn", 2),
409 ("LoggedIn", 2),
410 ("person", 3),
411 ("time", 3),
412 ("impl Eq for Person", 0),
413 ("impl Drop for Person", 0),
414 ("fn drop", 1),
415 ]
416 );
417
418 // Without space, we only match on names
419 assert_eq!(
420 search(&outline, "oon", cx).await,
421 &[
422 ("mod module", vec![]), // included as the parent of a match
423 ("enum LoginState", vec![]), // included as the parent of a match
424 ("LoggingOn", vec![1, 7, 8]), // matches
425 ("impl Drop for Person", vec![7, 18, 19]), // matches in two disjoint names
426 ]
427 );
428
429 assert_eq!(
430 search(&outline, "dp p", cx).await,
431 &[
432 ("impl Drop for Person", vec![5, 8, 9, 14]),
433 ("fn drop", vec![]),
434 ]
435 );
436 assert_eq!(
437 search(&outline, "dpn", cx).await,
438 &[("impl Drop for Person", vec![5, 14, 19])]
439 );
440 assert_eq!(
441 search(&outline, "impl ", cx).await,
442 &[
443 ("impl Eq for Person", vec![0, 1, 2, 3, 4]),
444 ("impl Drop for Person", vec![0, 1, 2, 3, 4]),
445 ("fn drop", vec![]),
446 ]
447 );
448
449 async fn search<'a>(
450 outline: &'a Outline<Anchor>,
451 query: &'a str,
452 cx: &'a gpui::TestAppContext,
453 ) -> Vec<(&'a str, Vec<usize>)> {
454 let matches = cx
455 .read(|cx| outline.search(query, cx.background().clone()))
456 .await;
457 matches
458 .into_iter()
459 .map(|mat| (outline.items[mat.candidate_id].text.as_str(), mat.positions))
460 .collect::<Vec<_>>()
461 }
462}
463
464#[gpui::test]
465async fn test_outline_nodes_with_newlines(cx: &mut gpui::TestAppContext) {
466 let text = r#"
467 impl A for B<
468 C
469 > {
470 };
471 "#
472 .unindent();
473
474 let buffer =
475 cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
476 let outline = buffer
477 .read_with(cx, |buffer, _| buffer.snapshot().outline(None))
478 .unwrap();
479
480 assert_eq!(
481 outline
482 .items
483 .iter()
484 .map(|item| (item.text.as_str(), item.depth))
485 .collect::<Vec<_>>(),
486 &[("impl A for B<", 0)]
487 );
488}
489
490#[gpui::test]
491async fn test_symbols_containing(cx: &mut gpui::TestAppContext) {
492 let text = r#"
493 impl Person {
494 fn one() {
495 1
496 }
497
498 fn two() {
499 2
500 }fn three() {
501 3
502 }
503 }
504 "#
505 .unindent();
506
507 let buffer =
508 cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
509 let snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot());
510
511 // point is at the start of an item
512 assert_eq!(
513 symbols_containing(Point::new(1, 4), &snapshot),
514 vec![
515 (
516 "impl Person".to_string(),
517 Point::new(0, 0)..Point::new(10, 1)
518 ),
519 ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
520 ]
521 );
522
523 // point is in the middle of an item
524 assert_eq!(
525 symbols_containing(Point::new(2, 8), &snapshot),
526 vec![
527 (
528 "impl Person".to_string(),
529 Point::new(0, 0)..Point::new(10, 1)
530 ),
531 ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
532 ]
533 );
534
535 // point is at the end of an item
536 assert_eq!(
537 symbols_containing(Point::new(3, 5), &snapshot),
538 vec![
539 (
540 "impl Person".to_string(),
541 Point::new(0, 0)..Point::new(10, 1)
542 ),
543 ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
544 ]
545 );
546
547 // point is in between two adjacent items
548 assert_eq!(
549 symbols_containing(Point::new(7, 5), &snapshot),
550 vec![
551 (
552 "impl Person".to_string(),
553 Point::new(0, 0)..Point::new(10, 1)
554 ),
555 ("fn two".to_string(), Point::new(5, 4)..Point::new(7, 5))
556 ]
557 );
558
559 fn symbols_containing(
560 position: Point,
561 snapshot: &BufferSnapshot,
562 ) -> Vec<(String, Range<Point>)> {
563 snapshot
564 .symbols_containing(position, None)
565 .unwrap()
566 .into_iter()
567 .map(|item| {
568 (
569 item.text,
570 item.range.start.to_point(snapshot)..item.range.end.to_point(snapshot),
571 )
572 })
573 .collect()
574 }
575}
576
577#[gpui::test]
578fn test_enclosing_bracket_ranges(cx: &mut MutableAppContext) {
579 cx.set_global(Settings::test(cx));
580 let buffer = cx.add_model(|cx| {
581 let text = "
582 mod x {
583 mod y {
584
585 }
586 }
587 "
588 .unindent();
589 Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx)
590 });
591 let buffer = buffer.read(cx);
592 assert_eq!(
593 buffer.enclosing_bracket_point_ranges(Point::new(1, 6)..Point::new(1, 6)),
594 Some((
595 Point::new(0, 6)..Point::new(0, 7),
596 Point::new(4, 0)..Point::new(4, 1)
597 ))
598 );
599 assert_eq!(
600 buffer.enclosing_bracket_point_ranges(Point::new(1, 10)..Point::new(1, 10)),
601 Some((
602 Point::new(1, 10)..Point::new(1, 11),
603 Point::new(3, 4)..Point::new(3, 5)
604 ))
605 );
606 assert_eq!(
607 buffer.enclosing_bracket_point_ranges(Point::new(3, 5)..Point::new(3, 5)),
608 Some((
609 Point::new(1, 10)..Point::new(1, 11),
610 Point::new(3, 4)..Point::new(3, 5)
611 ))
612 );
613
614 assert_eq!(
615 buffer.enclosing_bracket_point_ranges(Point::new(4, 1)..Point::new(4, 1)),
616 Some((
617 Point::new(0, 6)..Point::new(0, 7),
618 Point::new(4, 0)..Point::new(4, 1)
619 ))
620 );
621
622 // Regression test: avoid crash when querying at the end of the buffer.
623 assert_eq!(
624 buffer.enclosing_bracket_point_ranges(Point::new(4, 1)..Point::new(5, 0)),
625 None
626 );
627}
628
629#[gpui::test]
630fn test_enclosing_bracket_ranges_where_brackets_are_not_outermost_children(
631 cx: &mut MutableAppContext,
632) {
633 let javascript_language = Arc::new(
634 Language::new(
635 LanguageConfig {
636 name: "JavaScript".into(),
637 ..Default::default()
638 },
639 Some(tree_sitter_javascript::language()),
640 )
641 .with_brackets_query(
642 r#"
643 ("{" @open "}" @close)
644 ("(" @open ")" @close)
645 "#,
646 )
647 .unwrap(),
648 );
649
650 cx.set_global(Settings::test(cx));
651 let buffer = cx.add_model(|cx| {
652 let text = "
653 for (const a in b) {
654 // a comment that's longer than the for-loop header
655 }
656 "
657 .unindent();
658 Buffer::new(0, text, cx).with_language(javascript_language, cx)
659 });
660
661 let buffer = buffer.read(cx);
662 assert_eq!(
663 buffer.enclosing_bracket_point_ranges(Point::new(0, 18)..Point::new(0, 18)),
664 Some((
665 Point::new(0, 4)..Point::new(0, 5),
666 Point::new(0, 17)..Point::new(0, 18)
667 ))
668 );
669
670 // Regression test: even though the parent node of the parentheses (the for loop) does
671 // intersect the given range, the parentheses themselves do not contain the range, so
672 // they should not be returned. Only the curly braces contain the range.
673 assert_eq!(
674 buffer.enclosing_bracket_point_ranges(Point::new(0, 20)..Point::new(0, 20)),
675 Some((
676 Point::new(0, 19)..Point::new(0, 20),
677 Point::new(2, 0)..Point::new(2, 1)
678 ))
679 );
680}
681
682#[gpui::test]
683fn test_range_for_syntax_ancestor(cx: &mut MutableAppContext) {
684 cx.add_model(|cx| {
685 let text = "fn a() { b(|c| {}) }";
686 let buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
687 let snapshot = buffer.snapshot();
688
689 assert_eq!(
690 snapshot.range_for_syntax_ancestor(empty_range_at(text, "|")),
691 Some(range_of(text, "|"))
692 );
693 assert_eq!(
694 snapshot.range_for_syntax_ancestor(range_of(text, "|")),
695 Some(range_of(text, "|c|"))
696 );
697 assert_eq!(
698 snapshot.range_for_syntax_ancestor(range_of(text, "|c|")),
699 Some(range_of(text, "|c| {}"))
700 );
701 assert_eq!(
702 snapshot.range_for_syntax_ancestor(range_of(text, "|c| {}")),
703 Some(range_of(text, "(|c| {})"))
704 );
705
706 buffer
707 });
708
709 fn empty_range_at(text: &str, part: &str) -> Range<usize> {
710 let start = text.find(part).unwrap();
711 start..start
712 }
713
714 fn range_of(text: &str, part: &str) -> Range<usize> {
715 let start = text.find(part).unwrap();
716 start..start + part.len()
717 }
718}
719
720#[gpui::test]
721fn test_autoindent_with_soft_tabs(cx: &mut MutableAppContext) {
722 let settings = Settings::test(cx);
723 cx.set_global(settings);
724
725 cx.add_model(|cx| {
726 let text = "fn a() {}";
727 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
728
729 buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
730 assert_eq!(buffer.text(), "fn a() {\n \n}");
731
732 buffer.edit(
733 [(Point::new(1, 4)..Point::new(1, 4), "b()\n")],
734 Some(AutoindentMode::EachLine),
735 cx,
736 );
737 assert_eq!(buffer.text(), "fn a() {\n b()\n \n}");
738
739 // Create a field expression on a new line, causing that line
740 // to be indented.
741 buffer.edit(
742 [(Point::new(2, 4)..Point::new(2, 4), ".c")],
743 Some(AutoindentMode::EachLine),
744 cx,
745 );
746 assert_eq!(buffer.text(), "fn a() {\n b()\n .c\n}");
747
748 // Remove the dot so that the line is no longer a field expression,
749 // causing the line to be outdented.
750 buffer.edit(
751 [(Point::new(2, 8)..Point::new(2, 9), "")],
752 Some(AutoindentMode::EachLine),
753 cx,
754 );
755 assert_eq!(buffer.text(), "fn a() {\n b()\n c\n}");
756
757 buffer
758 });
759}
760
761#[gpui::test]
762fn test_autoindent_with_hard_tabs(cx: &mut MutableAppContext) {
763 let mut settings = Settings::test(cx);
764 settings.editor_overrides.hard_tabs = Some(true);
765 cx.set_global(settings);
766
767 cx.add_model(|cx| {
768 let text = "fn a() {}";
769 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
770
771 buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
772 assert_eq!(buffer.text(), "fn a() {\n\t\n}");
773
774 buffer.edit(
775 [(Point::new(1, 1)..Point::new(1, 1), "b()\n")],
776 Some(AutoindentMode::EachLine),
777 cx,
778 );
779 assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\n}");
780
781 // Create a field expression on a new line, causing that line
782 // to be indented.
783 buffer.edit(
784 [(Point::new(2, 1)..Point::new(2, 1), ".c")],
785 Some(AutoindentMode::EachLine),
786 cx,
787 );
788 assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\t.c\n}");
789
790 // Remove the dot so that the line is no longer a field expression,
791 // causing the line to be outdented.
792 buffer.edit(
793 [(Point::new(2, 2)..Point::new(2, 3), "")],
794 Some(AutoindentMode::EachLine),
795 cx,
796 );
797 assert_eq!(buffer.text(), "fn a() {\n\tb()\n\tc\n}");
798
799 buffer
800 });
801}
802
803#[gpui::test]
804fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut MutableAppContext) {
805 let settings = Settings::test(cx);
806 cx.set_global(settings);
807
808 cx.add_model(|cx| {
809 let mut buffer = Buffer::new(
810 0,
811 "
812 fn a() {
813 c;
814 d;
815 }
816 "
817 .unindent(),
818 cx,
819 )
820 .with_language(Arc::new(rust_lang()), cx);
821
822 // Lines 2 and 3 don't match the indentation suggestion. When editing these lines,
823 // their indentation is not adjusted.
824 buffer.edit_via_marked_text(
825 &"
826 fn a() {
827 c«()»;
828 d«()»;
829 }
830 "
831 .unindent(),
832 Some(AutoindentMode::EachLine),
833 cx,
834 );
835 assert_eq!(
836 buffer.text(),
837 "
838 fn a() {
839 c();
840 d();
841 }
842 "
843 .unindent()
844 );
845
846 // When appending new content after these lines, the indentation is based on the
847 // preceding lines' actual indentation.
848 buffer.edit_via_marked_text(
849 &"
850 fn a() {
851 c«
852 .f
853 .g()»;
854 d«
855 .f
856 .g()»;
857 }
858 "
859 .unindent(),
860 Some(AutoindentMode::EachLine),
861 cx,
862 );
863
864 assert_eq!(
865 buffer.text(),
866 "
867 fn a() {
868 c
869 .f
870 .g();
871 d
872 .f
873 .g();
874 }
875 "
876 .unindent()
877 );
878 buffer
879 });
880
881 cx.add_model(|cx| {
882 let mut buffer = Buffer::new(
883 0,
884 "
885 fn a() {
886 b();
887 |
888 "
889 .replace("|", "") // marker to preserve trailing whitespace
890 .unindent(),
891 cx,
892 )
893 .with_language(Arc::new(rust_lang()), cx);
894
895 // Insert a closing brace. It is outdented.
896 buffer.edit_via_marked_text(
897 &"
898 fn a() {
899 b();
900 «}»
901 "
902 .unindent(),
903 Some(AutoindentMode::EachLine),
904 cx,
905 );
906 assert_eq!(
907 buffer.text(),
908 "
909 fn a() {
910 b();
911 }
912 "
913 .unindent()
914 );
915
916 // Manually edit the leading whitespace. The edit is preserved.
917 buffer.edit_via_marked_text(
918 &"
919 fn a() {
920 b();
921 « »}
922 "
923 .unindent(),
924 Some(AutoindentMode::EachLine),
925 cx,
926 );
927 assert_eq!(
928 buffer.text(),
929 "
930 fn a() {
931 b();
932 }
933 "
934 .unindent()
935 );
936 buffer
937 });
938}
939
940#[gpui::test]
941fn test_autoindent_does_not_adjust_lines_within_newly_created_errors(cx: &mut MutableAppContext) {
942 let settings = Settings::test(cx);
943 cx.set_global(settings);
944
945 cx.add_model(|cx| {
946 let mut buffer = Buffer::new(
947 0,
948 "
949 fn a() {
950 i
951 }
952 "
953 .unindent(),
954 cx,
955 )
956 .with_language(Arc::new(rust_lang()), cx);
957
958 // Regression test: line does not get outdented due to syntax error
959 buffer.edit_via_marked_text(
960 &"
961 fn a() {
962 i«f let Some(x) = y»
963 }
964 "
965 .unindent(),
966 Some(AutoindentMode::EachLine),
967 cx,
968 );
969 assert_eq!(
970 buffer.text(),
971 "
972 fn a() {
973 if let Some(x) = y
974 }
975 "
976 .unindent()
977 );
978
979 buffer.edit_via_marked_text(
980 &"
981 fn a() {
982 if let Some(x) = y« {»
983 }
984 "
985 .unindent(),
986 Some(AutoindentMode::EachLine),
987 cx,
988 );
989 assert_eq!(
990 buffer.text(),
991 "
992 fn a() {
993 if let Some(x) = y {
994 }
995 "
996 .unindent()
997 );
998
999 buffer
1000 });
1001}
1002
1003#[gpui::test]
1004fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut MutableAppContext) {
1005 cx.set_global(Settings::test(cx));
1006 cx.add_model(|cx| {
1007 let mut buffer = Buffer::new(
1008 0,
1009 "
1010 fn a() {}
1011 "
1012 .unindent(),
1013 cx,
1014 )
1015 .with_language(Arc::new(rust_lang()), cx);
1016
1017 buffer.edit_via_marked_text(
1018 &"
1019 fn a(«
1020 b») {}
1021 "
1022 .unindent(),
1023 Some(AutoindentMode::EachLine),
1024 cx,
1025 );
1026 assert_eq!(
1027 buffer.text(),
1028 "
1029 fn a(
1030 b) {}
1031 "
1032 .unindent()
1033 );
1034
1035 // The indentation suggestion changed because `@end` node (a close paren)
1036 // is now at the beginning of the line.
1037 buffer.edit_via_marked_text(
1038 &"
1039 fn a(
1040 ˇ) {}
1041 "
1042 .unindent(),
1043 Some(AutoindentMode::EachLine),
1044 cx,
1045 );
1046 assert_eq!(
1047 buffer.text(),
1048 "
1049 fn a(
1050 ) {}
1051 "
1052 .unindent()
1053 );
1054
1055 buffer
1056 });
1057}
1058
1059#[gpui::test]
1060fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut MutableAppContext) {
1061 cx.set_global(Settings::test(cx));
1062 cx.add_model(|cx| {
1063 let text = "a\nb";
1064 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
1065 buffer.edit(
1066 [(0..1, "\n"), (2..3, "\n")],
1067 Some(AutoindentMode::EachLine),
1068 cx,
1069 );
1070 assert_eq!(buffer.text(), "\n\n\n");
1071 buffer
1072 });
1073}
1074
1075#[gpui::test]
1076fn test_autoindent_multi_line_insertion(cx: &mut MutableAppContext) {
1077 cx.set_global(Settings::test(cx));
1078 cx.add_model(|cx| {
1079 let text = "
1080 const a: usize = 1;
1081 fn b() {
1082 if c {
1083 let d = 2;
1084 }
1085 }
1086 "
1087 .unindent();
1088
1089 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
1090 buffer.edit(
1091 [(Point::new(3, 0)..Point::new(3, 0), "e(\n f()\n);\n")],
1092 Some(AutoindentMode::EachLine),
1093 cx,
1094 );
1095 assert_eq!(
1096 buffer.text(),
1097 "
1098 const a: usize = 1;
1099 fn b() {
1100 if c {
1101 e(
1102 f()
1103 );
1104 let d = 2;
1105 }
1106 }
1107 "
1108 .unindent()
1109 );
1110
1111 buffer
1112 });
1113}
1114
1115#[gpui::test]
1116fn test_autoindent_block_mode(cx: &mut MutableAppContext) {
1117 cx.set_global(Settings::test(cx));
1118 cx.add_model(|cx| {
1119 let text = r#"
1120 fn a() {
1121 b();
1122 }
1123 "#
1124 .unindent();
1125 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
1126
1127 // When this text was copied, both of the quotation marks were at the same
1128 // indent level, but the indentation of the first line was not included in
1129 // the copied text. This information is retained in the
1130 // 'original_indent_columns' vector.
1131 let original_indent_columns = vec![4];
1132 let inserted_text = r#"
1133 "
1134 c
1135 d
1136 e
1137 "
1138 "#
1139 .unindent();
1140
1141 // Insert the block at column zero. The entire block is indented
1142 // so that the first line matches the previous line's indentation.
1143 buffer.edit(
1144 [(Point::new(2, 0)..Point::new(2, 0), inserted_text.clone())],
1145 Some(AutoindentMode::Block {
1146 original_indent_columns: original_indent_columns.clone(),
1147 }),
1148 cx,
1149 );
1150 assert_eq!(
1151 buffer.text(),
1152 r#"
1153 fn a() {
1154 b();
1155 "
1156 c
1157 d
1158 e
1159 "
1160 }
1161 "#
1162 .unindent()
1163 );
1164
1165 // Grouping is disabled in tests, so we need 2 undos
1166 buffer.undo(cx); // Undo the auto-indent
1167 buffer.undo(cx); // Undo the original edit
1168
1169 // Insert the block at a deeper indent level. The entire block is outdented.
1170 buffer.edit([(Point::new(2, 0)..Point::new(2, 0), " ")], None, cx);
1171 buffer.edit(
1172 [(Point::new(2, 8)..Point::new(2, 8), inserted_text)],
1173 Some(AutoindentMode::Block {
1174 original_indent_columns: original_indent_columns.clone(),
1175 }),
1176 cx,
1177 );
1178 assert_eq!(
1179 buffer.text(),
1180 r#"
1181 fn a() {
1182 b();
1183 "
1184 c
1185 d
1186 e
1187 "
1188 }
1189 "#
1190 .unindent()
1191 );
1192
1193 buffer
1194 });
1195}
1196
1197#[gpui::test]
1198fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut MutableAppContext) {
1199 cx.set_global(Settings::test(cx));
1200 cx.add_model(|cx| {
1201 let text = r#"
1202 fn a() {
1203 if b() {
1204
1205 }
1206 }
1207 "#
1208 .unindent();
1209 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
1210
1211 // The original indent columns are not known, so this text is
1212 // auto-indented in a block as if the first line was copied in
1213 // its entirety.
1214 let original_indent_columns = Vec::new();
1215 let inserted_text = " c\n .d()\n .e();";
1216
1217 // Insert the block at column zero. The entire block is indented
1218 // so that the first line matches the previous line's indentation.
1219 buffer.edit(
1220 [(Point::new(2, 0)..Point::new(2, 0), inserted_text.clone())],
1221 Some(AutoindentMode::Block {
1222 original_indent_columns: original_indent_columns.clone(),
1223 }),
1224 cx,
1225 );
1226 assert_eq!(
1227 buffer.text(),
1228 r#"
1229 fn a() {
1230 if b() {
1231 c
1232 .d()
1233 .e();
1234 }
1235 }
1236 "#
1237 .unindent()
1238 );
1239
1240 // Grouping is disabled in tests, so we need 2 undos
1241 buffer.undo(cx); // Undo the auto-indent
1242 buffer.undo(cx); // Undo the original edit
1243
1244 // Insert the block at a deeper indent level. The entire block is outdented.
1245 buffer.edit(
1246 [(Point::new(2, 0)..Point::new(2, 0), " ".repeat(12))],
1247 None,
1248 cx,
1249 );
1250 buffer.edit(
1251 [(Point::new(2, 12)..Point::new(2, 12), inserted_text)],
1252 Some(AutoindentMode::Block {
1253 original_indent_columns: Vec::new(),
1254 }),
1255 cx,
1256 );
1257 assert_eq!(
1258 buffer.text(),
1259 r#"
1260 fn a() {
1261 if b() {
1262 c
1263 .d()
1264 .e();
1265 }
1266 }
1267 "#
1268 .unindent()
1269 );
1270
1271 buffer
1272 });
1273}
1274
1275#[gpui::test]
1276fn test_autoindent_language_without_indents_query(cx: &mut MutableAppContext) {
1277 cx.set_global(Settings::test(cx));
1278 cx.add_model(|cx| {
1279 let text = "
1280 * one
1281 - a
1282 - b
1283 * two
1284 "
1285 .unindent();
1286
1287 let mut buffer = Buffer::new(0, text, cx).with_language(
1288 Arc::new(Language::new(
1289 LanguageConfig {
1290 name: "Markdown".into(),
1291 auto_indent_using_last_non_empty_line: false,
1292 ..Default::default()
1293 },
1294 Some(tree_sitter_json::language()),
1295 )),
1296 cx,
1297 );
1298 buffer.edit(
1299 [(Point::new(3, 0)..Point::new(3, 0), "\n")],
1300 Some(AutoindentMode::EachLine),
1301 cx,
1302 );
1303 assert_eq!(
1304 buffer.text(),
1305 "
1306 * one
1307 - a
1308 - b
1309
1310 * two
1311 "
1312 .unindent()
1313 );
1314 buffer
1315 });
1316}
1317
1318#[gpui::test]
1319fn test_autoindent_with_injected_languages(cx: &mut MutableAppContext) {
1320 cx.set_global({
1321 let mut settings = Settings::test(cx);
1322 settings.language_overrides.extend([
1323 (
1324 "HTML".into(),
1325 settings::EditorSettings {
1326 tab_size: Some(2.try_into().unwrap()),
1327 ..Default::default()
1328 },
1329 ),
1330 (
1331 "JavaScript".into(),
1332 settings::EditorSettings {
1333 tab_size: Some(8.try_into().unwrap()),
1334 ..Default::default()
1335 },
1336 ),
1337 ]);
1338 settings
1339 });
1340
1341 let html_language = Arc::new(
1342 Language::new(
1343 LanguageConfig {
1344 name: "HTML".into(),
1345 ..Default::default()
1346 },
1347 Some(tree_sitter_html::language()),
1348 )
1349 .with_indents_query(
1350 "
1351 (element
1352 (start_tag) @start
1353 (end_tag)? @end) @indent
1354 ",
1355 )
1356 .unwrap()
1357 .with_injection_query(
1358 r#"
1359 (script_element
1360 (raw_text) @content
1361 (#set! "language" "javascript"))
1362 "#,
1363 )
1364 .unwrap(),
1365 );
1366
1367 let javascript_language = Arc::new(
1368 Language::new(
1369 LanguageConfig {
1370 name: "JavaScript".into(),
1371 ..Default::default()
1372 },
1373 Some(tree_sitter_javascript::language()),
1374 )
1375 .with_indents_query(
1376 r#"
1377 (object "}" @end) @indent
1378 "#,
1379 )
1380 .unwrap(),
1381 );
1382
1383 let language_registry = Arc::new(LanguageRegistry::test());
1384 language_registry.add(html_language.clone());
1385 language_registry.add(javascript_language.clone());
1386
1387 cx.add_model(|cx| {
1388 let (text, ranges) = marked_text_ranges(
1389 &"
1390 <div>ˇ
1391 </div>
1392 <script>
1393 init({ˇ
1394 })
1395 </script>
1396 <span>ˇ
1397 </span>
1398 "
1399 .unindent(),
1400 false,
1401 );
1402
1403 let mut buffer = Buffer::new(0, text, cx);
1404 buffer.set_language_registry(language_registry);
1405 buffer.set_language(Some(html_language), cx);
1406 buffer.edit(
1407 ranges.into_iter().map(|range| (range, "\na")),
1408 Some(AutoindentMode::EachLine),
1409 cx,
1410 );
1411 assert_eq!(
1412 buffer.text(),
1413 "
1414 <div>
1415 a
1416 </div>
1417 <script>
1418 init({
1419 a
1420 })
1421 </script>
1422 <span>
1423 a
1424 </span>
1425 "
1426 .unindent()
1427 );
1428 buffer
1429 });
1430}
1431
1432#[gpui::test]
1433fn test_autoindent_query_with_outdent_captures(cx: &mut MutableAppContext) {
1434 let mut settings = Settings::test(cx);
1435 settings.editor_defaults.tab_size = Some(2.try_into().unwrap());
1436 cx.set_global(settings);
1437 cx.add_model(|cx| {
1438 let mut buffer = Buffer::new(0, "", cx).with_language(Arc::new(ruby_lang()), cx);
1439
1440 let text = r#"
1441 class C
1442 def a(b, c)
1443 puts b
1444 puts c
1445 rescue
1446 puts "errored"
1447 exit 1
1448 end
1449 end
1450 "#
1451 .unindent();
1452
1453 buffer.edit([(0..0, text)], Some(AutoindentMode::EachLine), cx);
1454
1455 assert_eq!(
1456 buffer.text(),
1457 r#"
1458 class C
1459 def a(b, c)
1460 puts b
1461 puts c
1462 rescue
1463 puts "errored"
1464 exit 1
1465 end
1466 end
1467 "#
1468 .unindent()
1469 );
1470
1471 buffer
1472 });
1473}
1474
1475#[gpui::test]
1476fn test_language_config_at(cx: &mut MutableAppContext) {
1477 cx.set_global(Settings::test(cx));
1478 cx.add_model(|cx| {
1479 let language = Language::new(
1480 LanguageConfig {
1481 name: "JavaScript".into(),
1482 line_comment: Some("// ".into()),
1483 brackets: BracketPairConfig {
1484 pairs: vec![
1485 BracketPair {
1486 start: "{".into(),
1487 end: "}".into(),
1488 close: true,
1489 newline: false,
1490 },
1491 BracketPair {
1492 start: "'".into(),
1493 end: "'".into(),
1494 close: true,
1495 newline: false,
1496 },
1497 ],
1498 disabled_scopes_by_bracket_ix: vec![
1499 Vec::new(), //
1500 vec!["string".into()],
1501 ],
1502 },
1503 overrides: [(
1504 "element".into(),
1505 LanguageConfigOverride {
1506 line_comment: Override::Remove { remove: true },
1507 block_comment: Override::Set(("{/*".into(), "*/}".into())),
1508 ..Default::default()
1509 },
1510 )]
1511 .into_iter()
1512 .collect(),
1513 ..Default::default()
1514 },
1515 Some(tree_sitter_javascript::language()),
1516 )
1517 .with_override_query(
1518 r#"
1519 (jsx_element) @element
1520 (string) @string
1521 "#,
1522 )
1523 .unwrap();
1524
1525 let text = r#"a["b"] = <C d="e"></C>;"#;
1526
1527 let buffer = Buffer::new(0, text, cx).with_language(Arc::new(language), cx);
1528 let snapshot = buffer.snapshot();
1529
1530 let config = snapshot.language_scope_at(0).unwrap();
1531 assert_eq!(config.line_comment_prefix().unwrap().as_ref(), "// ");
1532 // Both bracket pairs are enabled
1533 assert_eq!(
1534 config.brackets().map(|e| e.1).collect::<Vec<_>>(),
1535 &[true, true]
1536 );
1537
1538 let string_config = snapshot.language_scope_at(3).unwrap();
1539 assert_eq!(string_config.line_comment_prefix().unwrap().as_ref(), "// ");
1540 // Second bracket pair is disabled
1541 assert_eq!(
1542 string_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
1543 &[true, false]
1544 );
1545
1546 let element_config = snapshot.language_scope_at(10).unwrap();
1547 assert_eq!(element_config.line_comment_prefix(), None);
1548 assert_eq!(
1549 element_config.block_comment_delimiters(),
1550 Some((&"{/*".into(), &"*/}".into()))
1551 );
1552 // Both bracket pairs are enabled
1553 assert_eq!(
1554 element_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
1555 &[true, true]
1556 );
1557
1558 buffer
1559 });
1560}
1561
1562#[gpui::test]
1563fn test_serialization(cx: &mut gpui::MutableAppContext) {
1564 let mut now = Instant::now();
1565
1566 let buffer1 = cx.add_model(|cx| {
1567 let mut buffer = Buffer::new(0, "abc", cx);
1568 buffer.edit([(3..3, "D")], None, cx);
1569
1570 now += Duration::from_secs(1);
1571 buffer.start_transaction_at(now);
1572 buffer.edit([(4..4, "E")], None, cx);
1573 buffer.end_transaction_at(now, cx);
1574 assert_eq!(buffer.text(), "abcDE");
1575
1576 buffer.undo(cx);
1577 assert_eq!(buffer.text(), "abcD");
1578
1579 buffer.edit([(4..4, "F")], None, cx);
1580 assert_eq!(buffer.text(), "abcDF");
1581 buffer
1582 });
1583 assert_eq!(buffer1.read(cx).text(), "abcDF");
1584
1585 let state = buffer1.read(cx).to_proto();
1586 let ops = cx
1587 .background()
1588 .block(buffer1.read(cx).serialize_ops(None, cx));
1589 let buffer2 = cx.add_model(|cx| {
1590 let mut buffer = Buffer::from_proto(1, state, None).unwrap();
1591 buffer
1592 .apply_ops(
1593 ops.into_iter()
1594 .map(|op| proto::deserialize_operation(op).unwrap()),
1595 cx,
1596 )
1597 .unwrap();
1598 buffer
1599 });
1600 assert_eq!(buffer2.read(cx).text(), "abcDF");
1601}
1602
1603#[gpui::test(iterations = 100)]
1604fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) {
1605 let min_peers = env::var("MIN_PEERS")
1606 .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
1607 .unwrap_or(1);
1608 let max_peers = env::var("MAX_PEERS")
1609 .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
1610 .unwrap_or(5);
1611 let operations = env::var("OPERATIONS")
1612 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1613 .unwrap_or(10);
1614
1615 let base_text_len = rng.gen_range(0..10);
1616 let base_text = RandomCharIter::new(&mut rng)
1617 .take(base_text_len)
1618 .collect::<String>();
1619 let mut replica_ids = Vec::new();
1620 let mut buffers = Vec::new();
1621 let network = Rc::new(RefCell::new(Network::new(rng.clone())));
1622 let base_buffer = cx.add_model(|cx| Buffer::new(0, base_text.as_str(), cx));
1623
1624 for i in 0..rng.gen_range(min_peers..=max_peers) {
1625 let buffer = cx.add_model(|cx| {
1626 let state = base_buffer.read(cx).to_proto();
1627 let ops = cx
1628 .background()
1629 .block(base_buffer.read(cx).serialize_ops(None, cx));
1630 let mut buffer = Buffer::from_proto(i as ReplicaId, state, None).unwrap();
1631 buffer
1632 .apply_ops(
1633 ops.into_iter()
1634 .map(|op| proto::deserialize_operation(op).unwrap()),
1635 cx,
1636 )
1637 .unwrap();
1638 buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
1639 let network = network.clone();
1640 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1641 if let Event::Operation(op) = event {
1642 network
1643 .borrow_mut()
1644 .broadcast(buffer.replica_id(), vec![proto::serialize_operation(op)]);
1645 }
1646 })
1647 .detach();
1648 buffer
1649 });
1650 buffers.push(buffer);
1651 replica_ids.push(i as ReplicaId);
1652 network.borrow_mut().add_peer(i as ReplicaId);
1653 log::info!("Adding initial peer with replica id {}", i);
1654 }
1655
1656 log::info!("initial text: {:?}", base_text);
1657
1658 let mut now = Instant::now();
1659 let mut mutation_count = operations;
1660 let mut next_diagnostic_id = 0;
1661 let mut active_selections = BTreeMap::default();
1662 loop {
1663 let replica_index = rng.gen_range(0..replica_ids.len());
1664 let replica_id = replica_ids[replica_index];
1665 let buffer = &mut buffers[replica_index];
1666 let mut new_buffer = None;
1667 match rng.gen_range(0..100) {
1668 0..=29 if mutation_count != 0 => {
1669 buffer.update(cx, |buffer, cx| {
1670 buffer.start_transaction_at(now);
1671 buffer.randomly_edit(&mut rng, 5, cx);
1672 buffer.end_transaction_at(now, cx);
1673 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1674 });
1675 mutation_count -= 1;
1676 }
1677 30..=39 if mutation_count != 0 => {
1678 buffer.update(cx, |buffer, cx| {
1679 let mut selections = Vec::new();
1680 for id in 0..rng.gen_range(1..=5) {
1681 let range = buffer.random_byte_range(0, &mut rng);
1682 selections.push(Selection {
1683 id,
1684 start: buffer.anchor_before(range.start),
1685 end: buffer.anchor_before(range.end),
1686 reversed: false,
1687 goal: SelectionGoal::None,
1688 });
1689 }
1690 let selections: Arc<[Selection<Anchor>]> = selections.into();
1691 log::info!(
1692 "peer {} setting active selections: {:?}",
1693 replica_id,
1694 selections
1695 );
1696 active_selections.insert(replica_id, selections.clone());
1697 buffer.set_active_selections(selections, false, Default::default(), cx);
1698 });
1699 mutation_count -= 1;
1700 }
1701 40..=49 if mutation_count != 0 && replica_id == 0 => {
1702 let entry_count = rng.gen_range(1..=5);
1703 buffer.update(cx, |buffer, cx| {
1704 let diagnostics = DiagnosticSet::new(
1705 (0..entry_count).map(|_| {
1706 let range = buffer.random_byte_range(0, &mut rng);
1707 let range = range.to_point_utf16(buffer);
1708 let range = range.start..range.end;
1709 DiagnosticEntry {
1710 range,
1711 diagnostic: Diagnostic {
1712 message: post_inc(&mut next_diagnostic_id).to_string(),
1713 ..Default::default()
1714 },
1715 }
1716 }),
1717 buffer,
1718 );
1719 log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
1720 buffer.update_diagnostics(diagnostics, cx);
1721 });
1722 mutation_count -= 1;
1723 }
1724 50..=59 if replica_ids.len() < max_peers => {
1725 let old_buffer_state = buffer.read(cx).to_proto();
1726 let old_buffer_ops = cx
1727 .background()
1728 .block(buffer.read(cx).serialize_ops(None, cx));
1729 let new_replica_id = (0..=replica_ids.len() as ReplicaId)
1730 .filter(|replica_id| *replica_id != buffer.read(cx).replica_id())
1731 .choose(&mut rng)
1732 .unwrap();
1733 log::info!(
1734 "Adding new replica {} (replicating from {})",
1735 new_replica_id,
1736 replica_id
1737 );
1738 new_buffer = Some(cx.add_model(|cx| {
1739 let mut new_buffer =
1740 Buffer::from_proto(new_replica_id, old_buffer_state, None).unwrap();
1741 new_buffer
1742 .apply_ops(
1743 old_buffer_ops
1744 .into_iter()
1745 .map(|op| deserialize_operation(op).unwrap()),
1746 cx,
1747 )
1748 .unwrap();
1749 log::info!(
1750 "New replica {} text: {:?}",
1751 new_buffer.replica_id(),
1752 new_buffer.text()
1753 );
1754 new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
1755 let network = network.clone();
1756 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1757 if let Event::Operation(op) = event {
1758 network.borrow_mut().broadcast(
1759 buffer.replica_id(),
1760 vec![proto::serialize_operation(op)],
1761 );
1762 }
1763 })
1764 .detach();
1765 new_buffer
1766 }));
1767 network.borrow_mut().replicate(replica_id, new_replica_id);
1768
1769 if new_replica_id as usize == replica_ids.len() {
1770 replica_ids.push(new_replica_id);
1771 } else {
1772 let new_buffer = new_buffer.take().unwrap();
1773 while network.borrow().has_unreceived(new_replica_id) {
1774 let ops = network
1775 .borrow_mut()
1776 .receive(new_replica_id)
1777 .into_iter()
1778 .map(|op| proto::deserialize_operation(op).unwrap());
1779 if ops.len() > 0 {
1780 log::info!(
1781 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1782 new_replica_id,
1783 buffer.read(cx).version(),
1784 ops.len(),
1785 ops
1786 );
1787 new_buffer.update(cx, |new_buffer, cx| {
1788 new_buffer.apply_ops(ops, cx).unwrap();
1789 });
1790 }
1791 }
1792 buffers[new_replica_id as usize] = new_buffer;
1793 }
1794 }
1795 60..=69 if mutation_count != 0 => {
1796 buffer.update(cx, |buffer, cx| {
1797 buffer.randomly_undo_redo(&mut rng, cx);
1798 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1799 });
1800 mutation_count -= 1;
1801 }
1802 _ if network.borrow().has_unreceived(replica_id) => {
1803 let ops = network
1804 .borrow_mut()
1805 .receive(replica_id)
1806 .into_iter()
1807 .map(|op| proto::deserialize_operation(op).unwrap());
1808 if ops.len() > 0 {
1809 log::info!(
1810 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1811 replica_id,
1812 buffer.read(cx).version(),
1813 ops.len(),
1814 ops
1815 );
1816 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
1817 }
1818 }
1819 _ => {}
1820 }
1821
1822 now += Duration::from_millis(rng.gen_range(0..=200));
1823 buffers.extend(new_buffer);
1824
1825 for buffer in &buffers {
1826 buffer.read(cx).check_invariants();
1827 }
1828
1829 if mutation_count == 0 && network.borrow().is_idle() {
1830 break;
1831 }
1832 }
1833
1834 let first_buffer = buffers[0].read(cx).snapshot();
1835 for buffer in &buffers[1..] {
1836 let buffer = buffer.read(cx).snapshot();
1837 assert_eq!(
1838 buffer.version(),
1839 first_buffer.version(),
1840 "Replica {} version != Replica 0 version",
1841 buffer.replica_id()
1842 );
1843 assert_eq!(
1844 buffer.text(),
1845 first_buffer.text(),
1846 "Replica {} text != Replica 0 text",
1847 buffer.replica_id()
1848 );
1849 assert_eq!(
1850 buffer
1851 .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
1852 .collect::<Vec<_>>(),
1853 first_buffer
1854 .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
1855 .collect::<Vec<_>>(),
1856 "Replica {} diagnostics != Replica 0 diagnostics",
1857 buffer.replica_id()
1858 );
1859 }
1860
1861 for buffer in &buffers {
1862 let buffer = buffer.read(cx).snapshot();
1863 let actual_remote_selections = buffer
1864 .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
1865 .map(|(replica_id, _, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
1866 .collect::<Vec<_>>();
1867 let expected_remote_selections = active_selections
1868 .iter()
1869 .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
1870 .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
1871 .collect::<Vec<_>>();
1872 assert_eq!(
1873 actual_remote_selections,
1874 expected_remote_selections,
1875 "Replica {} remote selections != expected selections",
1876 buffer.replica_id()
1877 );
1878 }
1879}
1880
1881#[test]
1882fn test_contiguous_ranges() {
1883 assert_eq!(
1884 contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
1885 &[1..4, 5..7, 9..13]
1886 );
1887
1888 // Respects the `max_len` parameter
1889 assert_eq!(
1890 contiguous_ranges(
1891 [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
1892 3
1893 )
1894 .collect::<Vec<_>>(),
1895 &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
1896 );
1897}
1898
1899impl Buffer {
1900 pub fn enclosing_bracket_point_ranges<T: ToOffset>(
1901 &self,
1902 range: Range<T>,
1903 ) -> Option<(Range<Point>, Range<Point>)> {
1904 self.snapshot()
1905 .enclosing_bracket_ranges(range)
1906 .map(|(start, end)| {
1907 let point_start = start.start.to_point(self)..start.end.to_point(self);
1908 let point_end = end.start.to_point(self)..end.end.to_point(self);
1909 (point_start, point_end)
1910 })
1911 }
1912}
1913
1914fn ruby_lang() -> Language {
1915 Language::new(
1916 LanguageConfig {
1917 name: "Ruby".into(),
1918 path_suffixes: vec!["rb".to_string()],
1919 ..Default::default()
1920 },
1921 Some(tree_sitter_ruby::language()),
1922 )
1923 .with_indents_query(
1924 r#"
1925 (class "end" @end) @indent
1926 (method "end" @end) @indent
1927 (rescue) @outdent
1928 (then) @indent
1929 "#,
1930 )
1931 .unwrap()
1932}
1933
1934fn rust_lang() -> Language {
1935 Language::new(
1936 LanguageConfig {
1937 name: "Rust".into(),
1938 path_suffixes: vec!["rs".to_string()],
1939 ..Default::default()
1940 },
1941 Some(tree_sitter_rust::language()),
1942 )
1943 .with_indents_query(
1944 r#"
1945 (call_expression) @indent
1946 (field_expression) @indent
1947 (_ "(" ")" @end) @indent
1948 (_ "{" "}" @end) @indent
1949 "#,
1950 )
1951 .unwrap()
1952 .with_brackets_query(
1953 r#"
1954 ("{" @open "}" @close)
1955 "#,
1956 )
1957 .unwrap()
1958 .with_outline_query(
1959 r#"
1960 (struct_item
1961 "struct" @context
1962 name: (_) @name) @item
1963 (enum_item
1964 "enum" @context
1965 name: (_) @name) @item
1966 (enum_variant
1967 name: (_) @name) @item
1968 (field_declaration
1969 name: (_) @name) @item
1970 (impl_item
1971 "impl" @context
1972 trait: (_)? @name
1973 "for"? @context
1974 type: (_) @name) @item
1975 (function_item
1976 "fn" @context
1977 name: (_) @name) @item
1978 (mod_item
1979 "mod" @context
1980 name: (_) @name) @item
1981 "#,
1982 )
1983 .unwrap()
1984}
1985
1986fn json_lang() -> Language {
1987 Language::new(
1988 LanguageConfig {
1989 name: "Json".into(),
1990 path_suffixes: vec!["js".to_string()],
1991 ..Default::default()
1992 },
1993 Some(tree_sitter_json::language()),
1994 )
1995}
1996
1997fn get_tree_sexp(buffer: &ModelHandle<Buffer>, cx: &gpui::TestAppContext) -> String {
1998 buffer.read_with(cx, |buffer, _| {
1999 let snapshot = buffer.snapshot();
2000 let layers = snapshot.syntax.layers(buffer.as_text_snapshot());
2001 layers[0].node.to_sexp()
2002 })
2003}