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: vec![
1484 BracketPair {
1485 start: "{".into(),
1486 end: "}".into(),
1487 close: true,
1488 newline: false,
1489 },
1490 BracketPair {
1491 start: "'".into(),
1492 end: "'".into(),
1493 close: true,
1494 newline: false,
1495 },
1496 ],
1497 overrides: [
1498 (
1499 "element".into(),
1500 LanguageConfigOverride {
1501 line_comment: Override::Remove { remove: true },
1502 block_comment: Override::Set(("{/*".into(), "*/}".into())),
1503 ..Default::default()
1504 },
1505 ),
1506 (
1507 "string".into(),
1508 LanguageConfigOverride {
1509 brackets: Override::Set(vec![BracketPair {
1510 start: "{".into(),
1511 end: "}".into(),
1512 close: true,
1513 newline: false,
1514 }]),
1515 ..Default::default()
1516 },
1517 ),
1518 ]
1519 .into_iter()
1520 .collect(),
1521 ..Default::default()
1522 },
1523 Some(tree_sitter_javascript::language()),
1524 )
1525 .with_override_query(
1526 r#"
1527 (jsx_element) @element
1528 (string) @string
1529 "#,
1530 )
1531 .unwrap();
1532
1533 let text = r#"a["b"] = <C d="e"></C>;"#;
1534
1535 let buffer = Buffer::new(0, text, cx).with_language(Arc::new(language), cx);
1536 let snapshot = buffer.snapshot();
1537
1538 let config = snapshot.language_scope_at(0).unwrap();
1539 assert_eq!(config.line_comment_prefix().unwrap().as_ref(), "// ");
1540 assert_eq!(config.brackets().len(), 2);
1541
1542 let string_config = snapshot.language_scope_at(3).unwrap();
1543 assert_eq!(config.line_comment_prefix().unwrap().as_ref(), "// ");
1544 assert_eq!(string_config.brackets().len(), 1);
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 assert_eq!(element_config.brackets().len(), 2);
1553
1554 buffer
1555 });
1556}
1557
1558#[gpui::test]
1559fn test_serialization(cx: &mut gpui::MutableAppContext) {
1560 let mut now = Instant::now();
1561
1562 let buffer1 = cx.add_model(|cx| {
1563 let mut buffer = Buffer::new(0, "abc", cx);
1564 buffer.edit([(3..3, "D")], None, cx);
1565
1566 now += Duration::from_secs(1);
1567 buffer.start_transaction_at(now);
1568 buffer.edit([(4..4, "E")], None, cx);
1569 buffer.end_transaction_at(now, cx);
1570 assert_eq!(buffer.text(), "abcDE");
1571
1572 buffer.undo(cx);
1573 assert_eq!(buffer.text(), "abcD");
1574
1575 buffer.edit([(4..4, "F")], None, cx);
1576 assert_eq!(buffer.text(), "abcDF");
1577 buffer
1578 });
1579 assert_eq!(buffer1.read(cx).text(), "abcDF");
1580
1581 let state = buffer1.read(cx).to_proto();
1582 let ops = cx
1583 .background()
1584 .block(buffer1.read(cx).serialize_ops(None, cx));
1585 let buffer2 = cx.add_model(|cx| {
1586 let mut buffer = Buffer::from_proto(1, state, None).unwrap();
1587 buffer
1588 .apply_ops(
1589 ops.into_iter()
1590 .map(|op| proto::deserialize_operation(op).unwrap()),
1591 cx,
1592 )
1593 .unwrap();
1594 buffer
1595 });
1596 assert_eq!(buffer2.read(cx).text(), "abcDF");
1597}
1598
1599#[gpui::test(iterations = 100)]
1600fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) {
1601 let min_peers = env::var("MIN_PEERS")
1602 .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
1603 .unwrap_or(1);
1604 let max_peers = env::var("MAX_PEERS")
1605 .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
1606 .unwrap_or(5);
1607 let operations = env::var("OPERATIONS")
1608 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1609 .unwrap_or(10);
1610
1611 let base_text_len = rng.gen_range(0..10);
1612 let base_text = RandomCharIter::new(&mut rng)
1613 .take(base_text_len)
1614 .collect::<String>();
1615 let mut replica_ids = Vec::new();
1616 let mut buffers = Vec::new();
1617 let network = Rc::new(RefCell::new(Network::new(rng.clone())));
1618 let base_buffer = cx.add_model(|cx| Buffer::new(0, base_text.as_str(), cx));
1619
1620 for i in 0..rng.gen_range(min_peers..=max_peers) {
1621 let buffer = cx.add_model(|cx| {
1622 let state = base_buffer.read(cx).to_proto();
1623 let ops = cx
1624 .background()
1625 .block(base_buffer.read(cx).serialize_ops(None, cx));
1626 let mut buffer = Buffer::from_proto(i as ReplicaId, state, None).unwrap();
1627 buffer
1628 .apply_ops(
1629 ops.into_iter()
1630 .map(|op| proto::deserialize_operation(op).unwrap()),
1631 cx,
1632 )
1633 .unwrap();
1634 buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
1635 let network = network.clone();
1636 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1637 if let Event::Operation(op) = event {
1638 network
1639 .borrow_mut()
1640 .broadcast(buffer.replica_id(), vec![proto::serialize_operation(op)]);
1641 }
1642 })
1643 .detach();
1644 buffer
1645 });
1646 buffers.push(buffer);
1647 replica_ids.push(i as ReplicaId);
1648 network.borrow_mut().add_peer(i as ReplicaId);
1649 log::info!("Adding initial peer with replica id {}", i);
1650 }
1651
1652 log::info!("initial text: {:?}", base_text);
1653
1654 let mut now = Instant::now();
1655 let mut mutation_count = operations;
1656 let mut next_diagnostic_id = 0;
1657 let mut active_selections = BTreeMap::default();
1658 loop {
1659 let replica_index = rng.gen_range(0..replica_ids.len());
1660 let replica_id = replica_ids[replica_index];
1661 let buffer = &mut buffers[replica_index];
1662 let mut new_buffer = None;
1663 match rng.gen_range(0..100) {
1664 0..=29 if mutation_count != 0 => {
1665 buffer.update(cx, |buffer, cx| {
1666 buffer.start_transaction_at(now);
1667 buffer.randomly_edit(&mut rng, 5, cx);
1668 buffer.end_transaction_at(now, cx);
1669 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1670 });
1671 mutation_count -= 1;
1672 }
1673 30..=39 if mutation_count != 0 => {
1674 buffer.update(cx, |buffer, cx| {
1675 let mut selections = Vec::new();
1676 for id in 0..rng.gen_range(1..=5) {
1677 let range = buffer.random_byte_range(0, &mut rng);
1678 selections.push(Selection {
1679 id,
1680 start: buffer.anchor_before(range.start),
1681 end: buffer.anchor_before(range.end),
1682 reversed: false,
1683 goal: SelectionGoal::None,
1684 });
1685 }
1686 let selections: Arc<[Selection<Anchor>]> = selections.into();
1687 log::info!(
1688 "peer {} setting active selections: {:?}",
1689 replica_id,
1690 selections
1691 );
1692 active_selections.insert(replica_id, selections.clone());
1693 buffer.set_active_selections(selections, false, Default::default(), cx);
1694 });
1695 mutation_count -= 1;
1696 }
1697 40..=49 if mutation_count != 0 && replica_id == 0 => {
1698 let entry_count = rng.gen_range(1..=5);
1699 buffer.update(cx, |buffer, cx| {
1700 let diagnostics = DiagnosticSet::new(
1701 (0..entry_count).map(|_| {
1702 let range = buffer.random_byte_range(0, &mut rng);
1703 let range = range.to_point_utf16(buffer);
1704 let range = range.start..range.end;
1705 DiagnosticEntry {
1706 range,
1707 diagnostic: Diagnostic {
1708 message: post_inc(&mut next_diagnostic_id).to_string(),
1709 ..Default::default()
1710 },
1711 }
1712 }),
1713 buffer,
1714 );
1715 log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
1716 buffer.update_diagnostics(diagnostics, cx);
1717 });
1718 mutation_count -= 1;
1719 }
1720 50..=59 if replica_ids.len() < max_peers => {
1721 let old_buffer_state = buffer.read(cx).to_proto();
1722 let old_buffer_ops = cx
1723 .background()
1724 .block(buffer.read(cx).serialize_ops(None, cx));
1725 let new_replica_id = (0..=replica_ids.len() as ReplicaId)
1726 .filter(|replica_id| *replica_id != buffer.read(cx).replica_id())
1727 .choose(&mut rng)
1728 .unwrap();
1729 log::info!(
1730 "Adding new replica {} (replicating from {})",
1731 new_replica_id,
1732 replica_id
1733 );
1734 new_buffer = Some(cx.add_model(|cx| {
1735 let mut new_buffer =
1736 Buffer::from_proto(new_replica_id, old_buffer_state, None).unwrap();
1737 new_buffer
1738 .apply_ops(
1739 old_buffer_ops
1740 .into_iter()
1741 .map(|op| deserialize_operation(op).unwrap()),
1742 cx,
1743 )
1744 .unwrap();
1745 log::info!(
1746 "New replica {} text: {:?}",
1747 new_buffer.replica_id(),
1748 new_buffer.text()
1749 );
1750 new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
1751 let network = network.clone();
1752 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1753 if let Event::Operation(op) = event {
1754 network.borrow_mut().broadcast(
1755 buffer.replica_id(),
1756 vec![proto::serialize_operation(op)],
1757 );
1758 }
1759 })
1760 .detach();
1761 new_buffer
1762 }));
1763 network.borrow_mut().replicate(replica_id, new_replica_id);
1764
1765 if new_replica_id as usize == replica_ids.len() {
1766 replica_ids.push(new_replica_id);
1767 } else {
1768 let new_buffer = new_buffer.take().unwrap();
1769 while network.borrow().has_unreceived(new_replica_id) {
1770 let ops = network
1771 .borrow_mut()
1772 .receive(new_replica_id)
1773 .into_iter()
1774 .map(|op| proto::deserialize_operation(op).unwrap());
1775 if ops.len() > 0 {
1776 log::info!(
1777 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1778 new_replica_id,
1779 buffer.read(cx).version(),
1780 ops.len(),
1781 ops
1782 );
1783 new_buffer.update(cx, |new_buffer, cx| {
1784 new_buffer.apply_ops(ops, cx).unwrap();
1785 });
1786 }
1787 }
1788 buffers[new_replica_id as usize] = new_buffer;
1789 }
1790 }
1791 60..=69 if mutation_count != 0 => {
1792 buffer.update(cx, |buffer, cx| {
1793 buffer.randomly_undo_redo(&mut rng, cx);
1794 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1795 });
1796 mutation_count -= 1;
1797 }
1798 _ if network.borrow().has_unreceived(replica_id) => {
1799 let ops = network
1800 .borrow_mut()
1801 .receive(replica_id)
1802 .into_iter()
1803 .map(|op| proto::deserialize_operation(op).unwrap());
1804 if ops.len() > 0 {
1805 log::info!(
1806 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1807 replica_id,
1808 buffer.read(cx).version(),
1809 ops.len(),
1810 ops
1811 );
1812 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
1813 }
1814 }
1815 _ => {}
1816 }
1817
1818 now += Duration::from_millis(rng.gen_range(0..=200));
1819 buffers.extend(new_buffer);
1820
1821 for buffer in &buffers {
1822 buffer.read(cx).check_invariants();
1823 }
1824
1825 if mutation_count == 0 && network.borrow().is_idle() {
1826 break;
1827 }
1828 }
1829
1830 let first_buffer = buffers[0].read(cx).snapshot();
1831 for buffer in &buffers[1..] {
1832 let buffer = buffer.read(cx).snapshot();
1833 assert_eq!(
1834 buffer.version(),
1835 first_buffer.version(),
1836 "Replica {} version != Replica 0 version",
1837 buffer.replica_id()
1838 );
1839 assert_eq!(
1840 buffer.text(),
1841 first_buffer.text(),
1842 "Replica {} text != Replica 0 text",
1843 buffer.replica_id()
1844 );
1845 assert_eq!(
1846 buffer
1847 .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
1848 .collect::<Vec<_>>(),
1849 first_buffer
1850 .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
1851 .collect::<Vec<_>>(),
1852 "Replica {} diagnostics != Replica 0 diagnostics",
1853 buffer.replica_id()
1854 );
1855 }
1856
1857 for buffer in &buffers {
1858 let buffer = buffer.read(cx).snapshot();
1859 let actual_remote_selections = buffer
1860 .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
1861 .map(|(replica_id, _, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
1862 .collect::<Vec<_>>();
1863 let expected_remote_selections = active_selections
1864 .iter()
1865 .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
1866 .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
1867 .collect::<Vec<_>>();
1868 assert_eq!(
1869 actual_remote_selections,
1870 expected_remote_selections,
1871 "Replica {} remote selections != expected selections",
1872 buffer.replica_id()
1873 );
1874 }
1875}
1876
1877#[test]
1878fn test_contiguous_ranges() {
1879 assert_eq!(
1880 contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
1881 &[1..4, 5..7, 9..13]
1882 );
1883
1884 // Respects the `max_len` parameter
1885 assert_eq!(
1886 contiguous_ranges(
1887 [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
1888 3
1889 )
1890 .collect::<Vec<_>>(),
1891 &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
1892 );
1893}
1894
1895impl Buffer {
1896 pub fn enclosing_bracket_point_ranges<T: ToOffset>(
1897 &self,
1898 range: Range<T>,
1899 ) -> Option<(Range<Point>, Range<Point>)> {
1900 self.snapshot()
1901 .enclosing_bracket_ranges(range)
1902 .map(|(start, end)| {
1903 let point_start = start.start.to_point(self)..start.end.to_point(self);
1904 let point_end = end.start.to_point(self)..end.end.to_point(self);
1905 (point_start, point_end)
1906 })
1907 }
1908}
1909
1910fn ruby_lang() -> Language {
1911 Language::new(
1912 LanguageConfig {
1913 name: "Ruby".into(),
1914 path_suffixes: vec!["rb".to_string()],
1915 ..Default::default()
1916 },
1917 Some(tree_sitter_ruby::language()),
1918 )
1919 .with_indents_query(
1920 r#"
1921 (class "end" @end) @indent
1922 (method "end" @end) @indent
1923 (rescue) @outdent
1924 (then) @indent
1925 "#,
1926 )
1927 .unwrap()
1928}
1929
1930fn rust_lang() -> Language {
1931 Language::new(
1932 LanguageConfig {
1933 name: "Rust".into(),
1934 path_suffixes: vec!["rs".to_string()],
1935 ..Default::default()
1936 },
1937 Some(tree_sitter_rust::language()),
1938 )
1939 .with_indents_query(
1940 r#"
1941 (call_expression) @indent
1942 (field_expression) @indent
1943 (_ "(" ")" @end) @indent
1944 (_ "{" "}" @end) @indent
1945 "#,
1946 )
1947 .unwrap()
1948 .with_brackets_query(
1949 r#"
1950 ("{" @open "}" @close)
1951 "#,
1952 )
1953 .unwrap()
1954 .with_outline_query(
1955 r#"
1956 (struct_item
1957 "struct" @context
1958 name: (_) @name) @item
1959 (enum_item
1960 "enum" @context
1961 name: (_) @name) @item
1962 (enum_variant
1963 name: (_) @name) @item
1964 (field_declaration
1965 name: (_) @name) @item
1966 (impl_item
1967 "impl" @context
1968 trait: (_)? @name
1969 "for"? @context
1970 type: (_) @name) @item
1971 (function_item
1972 "fn" @context
1973 name: (_) @name) @item
1974 (mod_item
1975 "mod" @context
1976 name: (_) @name) @item
1977 "#,
1978 )
1979 .unwrap()
1980}
1981
1982fn json_lang() -> Language {
1983 Language::new(
1984 LanguageConfig {
1985 name: "Json".into(),
1986 path_suffixes: vec!["js".to_string()],
1987 ..Default::default()
1988 },
1989 Some(tree_sitter_json::language()),
1990 )
1991}
1992
1993fn get_tree_sexp(buffer: &ModelHandle<Buffer>, cx: &gpui::TestAppContext) -> String {
1994 buffer.read_with(cx, |buffer, _| {
1995 let snapshot = buffer.snapshot();
1996 let layers = snapshot.syntax.layers(buffer.as_text_snapshot());
1997 layers[0].node.to_sexp()
1998 })
1999}