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