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