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