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 assert_eq!(buf.text(), "fn a() {}");
294 assert!(buf.is_parsing());
295 });
296 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
297 assert_eq!(
298 get_tree_sexp(&buffer, cx),
299 concat!(
300 "(source_file (function_item name: (identifier) ",
301 "parameters: (parameters) ",
302 "body: (block)))"
303 )
304 );
305
306 buffer.update(cx, |buf, cx| {
307 buf.redo(cx);
308 assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
309 assert!(buf.is_parsing());
310 });
311 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
312 assert_eq!(
313 get_tree_sexp(&buffer, cx),
314 concat!(
315 "(source_file (function_item name: (identifier) ",
316 "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
317 "body: (block (expression_statement (call_expression ",
318 "function: (generic_function ",
319 "function: (field_expression value: (identifier) field: (field_identifier)) ",
320 "type_arguments: (type_arguments (type_identifier))) ",
321 "arguments: (arguments (identifier)))))))",
322 )
323 );
324}
325
326#[gpui::test]
327async fn test_resetting_language(cx: &mut gpui::TestAppContext) {
328 let buffer = cx.add_model(|cx| {
329 let mut buffer = Buffer::new(0, "{}", cx).with_language(Arc::new(rust_lang()), cx);
330 buffer.set_sync_parse_timeout(Duration::ZERO);
331 buffer
332 });
333
334 // Wait for the initial text to parse
335 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
336 assert_eq!(
337 get_tree_sexp(&buffer, cx),
338 "(source_file (expression_statement (block)))"
339 );
340
341 buffer.update(cx, |buffer, cx| {
342 buffer.set_language(Some(Arc::new(json_lang())), cx)
343 });
344 buffer.condition(cx, |buffer, _| !buffer.is_parsing()).await;
345 assert_eq!(get_tree_sexp(&buffer, cx), "(document (object))");
346}
347
348#[gpui::test]
349async fn test_outline(cx: &mut gpui::TestAppContext) {
350 let text = r#"
351 struct Person {
352 name: String,
353 age: usize,
354 }
355
356 mod module {
357 enum LoginState {
358 LoggedOut,
359 LoggingOn,
360 LoggedIn {
361 person: Person,
362 time: Instant,
363 }
364 }
365 }
366
367 impl Eq for Person {}
368
369 impl Drop for Person {
370 fn drop(&mut self) {
371 println!("bye");
372 }
373 }
374 "#
375 .unindent();
376
377 let buffer =
378 cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
379 let outline = buffer
380 .read_with(cx, |buffer, _| buffer.snapshot().outline(None))
381 .unwrap();
382
383 assert_eq!(
384 outline
385 .items
386 .iter()
387 .map(|item| (item.text.as_str(), item.depth))
388 .collect::<Vec<_>>(),
389 &[
390 ("struct Person", 0),
391 ("name", 1),
392 ("age", 1),
393 ("mod module", 0),
394 ("enum LoginState", 1),
395 ("LoggedOut", 2),
396 ("LoggingOn", 2),
397 ("LoggedIn", 2),
398 ("person", 3),
399 ("time", 3),
400 ("impl Eq for Person", 0),
401 ("impl Drop for Person", 0),
402 ("fn drop", 1),
403 ]
404 );
405
406 // Without space, we only match on names
407 assert_eq!(
408 search(&outline, "oon", cx).await,
409 &[
410 ("mod module", vec![]), // included as the parent of a match
411 ("enum LoginState", vec![]), // included as the parent of a match
412 ("LoggingOn", vec![1, 7, 8]), // matches
413 ("impl Drop for Person", vec![7, 18, 19]), // matches in two disjoint names
414 ]
415 );
416
417 assert_eq!(
418 search(&outline, "dp p", cx).await,
419 &[
420 ("impl Drop for Person", vec![5, 8, 9, 14]),
421 ("fn drop", vec![]),
422 ]
423 );
424 assert_eq!(
425 search(&outline, "dpn", cx).await,
426 &[("impl Drop for Person", vec![5, 14, 19])]
427 );
428 assert_eq!(
429 search(&outline, "impl ", cx).await,
430 &[
431 ("impl Eq for Person", vec![0, 1, 2, 3, 4]),
432 ("impl Drop for Person", vec![0, 1, 2, 3, 4]),
433 ("fn drop", vec![]),
434 ]
435 );
436
437 async fn search<'a>(
438 outline: &'a Outline<Anchor>,
439 query: &'a str,
440 cx: &'a gpui::TestAppContext,
441 ) -> Vec<(&'a str, Vec<usize>)> {
442 let matches = cx
443 .read(|cx| outline.search(query, cx.background().clone()))
444 .await;
445 matches
446 .into_iter()
447 .map(|mat| (outline.items[mat.candidate_id].text.as_str(), mat.positions))
448 .collect::<Vec<_>>()
449 }
450}
451
452#[gpui::test]
453async fn test_symbols_containing(cx: &mut gpui::TestAppContext) {
454 let text = r#"
455 impl Person {
456 fn one() {
457 1
458 }
459
460 fn two() {
461 2
462 }fn three() {
463 3
464 }
465 }
466 "#
467 .unindent();
468
469 let buffer =
470 cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx));
471 let snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot());
472
473 // point is at the start of an item
474 assert_eq!(
475 symbols_containing(Point::new(1, 4), &snapshot),
476 vec![
477 (
478 "impl Person".to_string(),
479 Point::new(0, 0)..Point::new(10, 1)
480 ),
481 ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
482 ]
483 );
484
485 // point is in the middle of an item
486 assert_eq!(
487 symbols_containing(Point::new(2, 8), &snapshot),
488 vec![
489 (
490 "impl Person".to_string(),
491 Point::new(0, 0)..Point::new(10, 1)
492 ),
493 ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
494 ]
495 );
496
497 // point is at the end of an item
498 assert_eq!(
499 symbols_containing(Point::new(3, 5), &snapshot),
500 vec![
501 (
502 "impl Person".to_string(),
503 Point::new(0, 0)..Point::new(10, 1)
504 ),
505 ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
506 ]
507 );
508
509 // point is in between two adjacent items
510 assert_eq!(
511 symbols_containing(Point::new(7, 5), &snapshot),
512 vec![
513 (
514 "impl Person".to_string(),
515 Point::new(0, 0)..Point::new(10, 1)
516 ),
517 ("fn two".to_string(), Point::new(5, 4)..Point::new(7, 5))
518 ]
519 );
520
521 fn symbols_containing(
522 position: Point,
523 snapshot: &BufferSnapshot,
524 ) -> Vec<(String, Range<Point>)> {
525 snapshot
526 .symbols_containing(position, None)
527 .unwrap()
528 .into_iter()
529 .map(|item| {
530 (
531 item.text,
532 item.range.start.to_point(snapshot)..item.range.end.to_point(snapshot),
533 )
534 })
535 .collect()
536 }
537}
538
539#[gpui::test]
540fn test_enclosing_bracket_ranges(cx: &mut MutableAppContext) {
541 cx.set_global(Settings::test(cx));
542 let buffer = cx.add_model(|cx| {
543 let text = "
544 mod x {
545 mod y {
546
547 }
548 }
549 "
550 .unindent();
551 Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx)
552 });
553 let buffer = buffer.read(cx);
554 assert_eq!(
555 buffer.enclosing_bracket_point_ranges(Point::new(1, 6)..Point::new(1, 6)),
556 Some((
557 Point::new(0, 6)..Point::new(0, 7),
558 Point::new(4, 0)..Point::new(4, 1)
559 ))
560 );
561 assert_eq!(
562 buffer.enclosing_bracket_point_ranges(Point::new(1, 10)..Point::new(1, 10)),
563 Some((
564 Point::new(1, 10)..Point::new(1, 11),
565 Point::new(3, 4)..Point::new(3, 5)
566 ))
567 );
568 assert_eq!(
569 buffer.enclosing_bracket_point_ranges(Point::new(3, 5)..Point::new(3, 5)),
570 Some((
571 Point::new(1, 10)..Point::new(1, 11),
572 Point::new(3, 4)..Point::new(3, 5)
573 ))
574 );
575
576 // Regression test: avoid crash when querying at the end of the buffer.
577 assert_eq!(
578 buffer.enclosing_bracket_point_ranges(buffer.len() - 1..buffer.len()),
579 Some((
580 Point::new(0, 6)..Point::new(0, 7),
581 Point::new(4, 0)..Point::new(4, 1)
582 ))
583 );
584}
585
586#[gpui::test]
587fn test_range_for_syntax_ancestor(cx: &mut MutableAppContext) {
588 cx.add_model(|cx| {
589 let text = "fn a() { b(|c| {}) }";
590 let buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
591 let snapshot = buffer.snapshot();
592
593 assert_eq!(
594 snapshot.range_for_syntax_ancestor(empty_range_at(text, "|")),
595 Some(range_of(text, "|"))
596 );
597 assert_eq!(
598 snapshot.range_for_syntax_ancestor(range_of(text, "|")),
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 assert_eq!(
606 snapshot.range_for_syntax_ancestor(range_of(text, "|c| {}")),
607 Some(range_of(text, "(|c| {})"))
608 );
609
610 buffer
611 });
612
613 fn empty_range_at(text: &str, part: &str) -> Range<usize> {
614 let start = text.find(part).unwrap();
615 start..start
616 }
617
618 fn range_of(text: &str, part: &str) -> Range<usize> {
619 let start = text.find(part).unwrap();
620 start..start + part.len()
621 }
622}
623
624#[gpui::test]
625fn test_autoindent_with_soft_tabs(cx: &mut MutableAppContext) {
626 let settings = Settings::test(cx);
627 cx.set_global(settings);
628
629 cx.add_model(|cx| {
630 let text = "fn a() {}";
631 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
632
633 buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
634 assert_eq!(buffer.text(), "fn a() {\n \n}");
635
636 buffer.edit(
637 [(Point::new(1, 4)..Point::new(1, 4), "b()\n")],
638 Some(AutoindentMode::EachLine),
639 cx,
640 );
641 assert_eq!(buffer.text(), "fn a() {\n b()\n \n}");
642
643 // Create a field expression on a new line, causing that line
644 // to be indented.
645 buffer.edit(
646 [(Point::new(2, 4)..Point::new(2, 4), ".c")],
647 Some(AutoindentMode::EachLine),
648 cx,
649 );
650 assert_eq!(buffer.text(), "fn a() {\n b()\n .c\n}");
651
652 // Remove the dot so that the line is no longer a field expression,
653 // causing the line to be outdented.
654 buffer.edit(
655 [(Point::new(2, 8)..Point::new(2, 9), "")],
656 Some(AutoindentMode::EachLine),
657 cx,
658 );
659 assert_eq!(buffer.text(), "fn a() {\n b()\n c\n}");
660
661 buffer
662 });
663}
664
665#[gpui::test]
666fn test_autoindent_with_hard_tabs(cx: &mut MutableAppContext) {
667 let mut settings = Settings::test(cx);
668 settings.editor_overrides.hard_tabs = Some(true);
669 cx.set_global(settings);
670
671 cx.add_model(|cx| {
672 let text = "fn a() {}";
673 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
674
675 buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
676 assert_eq!(buffer.text(), "fn a() {\n\t\n}");
677
678 buffer.edit(
679 [(Point::new(1, 1)..Point::new(1, 1), "b()\n")],
680 Some(AutoindentMode::EachLine),
681 cx,
682 );
683 assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\n}");
684
685 // Create a field expression on a new line, causing that line
686 // to be indented.
687 buffer.edit(
688 [(Point::new(2, 1)..Point::new(2, 1), ".c")],
689 Some(AutoindentMode::EachLine),
690 cx,
691 );
692 assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\t.c\n}");
693
694 // Remove the dot so that the line is no longer a field expression,
695 // causing the line to be outdented.
696 buffer.edit(
697 [(Point::new(2, 2)..Point::new(2, 3), "")],
698 Some(AutoindentMode::EachLine),
699 cx,
700 );
701 assert_eq!(buffer.text(), "fn a() {\n\tb()\n\tc\n}");
702
703 buffer
704 });
705}
706
707#[gpui::test]
708fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut MutableAppContext) {
709 let settings = Settings::test(cx);
710 cx.set_global(settings);
711
712 cx.add_model(|cx| {
713 let text = "
714 fn a() {
715 c;
716 d;
717 }
718 "
719 .unindent();
720
721 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
722
723 // Lines 2 and 3 don't match the indentation suggestion. When editing these lines,
724 // their indentation is not adjusted.
725 buffer.edit(
726 [
727 (empty(Point::new(1, 1)), "()"),
728 (empty(Point::new(2, 1)), "()"),
729 ],
730 Some(AutoindentMode::EachLine),
731 cx,
732 );
733 assert_eq!(
734 buffer.text(),
735 "
736 fn a() {
737 c();
738 d();
739 }
740 "
741 .unindent()
742 );
743
744 // When appending new content after these lines, the indentation is based on the
745 // preceding lines' actual indentation.
746 buffer.edit(
747 [
748 (empty(Point::new(1, 1)), "\n.f\n.g"),
749 (empty(Point::new(2, 1)), "\n.f\n.g"),
750 ],
751 Some(AutoindentMode::EachLine),
752 cx,
753 );
754 assert_eq!(
755 buffer.text(),
756 "
757 fn a() {
758 c
759 .f
760 .g();
761 d
762 .f
763 .g();
764 }
765 "
766 .unindent()
767 );
768 buffer
769 });
770
771 cx.add_model(|cx| {
772 let text = "
773 fn a() {
774 {
775 b()?
776 }
777 Ok(())
778 }
779 "
780 .unindent();
781 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
782
783 // Delete a closing curly brace changes the suggested indent for the line.
784 buffer.edit(
785 [(Point::new(3, 4)..Point::new(3, 5), "")],
786 Some(AutoindentMode::EachLine),
787 cx,
788 );
789 assert_eq!(
790 buffer.text(),
791 "
792 fn a() {
793 {
794 b()?
795 |
796 Ok(())
797 }
798 "
799 .replace('|', "") // included in the string to preserve trailing whites
800 .unindent()
801 );
802
803 // Manually editing the leading whitespace
804 buffer.edit(
805 [(Point::new(3, 0)..Point::new(3, 12), "")],
806 Some(AutoindentMode::EachLine),
807 cx,
808 );
809 assert_eq!(
810 buffer.text(),
811 "
812 fn a() {
813 {
814 b()?
815
816 Ok(())
817 }
818 "
819 .unindent()
820 );
821 buffer
822 });
823}
824
825#[gpui::test]
826fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut MutableAppContext) {
827 cx.set_global(Settings::test(cx));
828 cx.add_model(|cx| {
829 let text = "
830 fn a() {}
831 "
832 .unindent();
833
834 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
835
836 buffer.edit([(5..5, "\nb")], Some(AutoindentMode::EachLine), cx);
837 assert_eq!(
838 buffer.text(),
839 "
840 fn a(
841 b) {}
842 "
843 .unindent()
844 );
845
846 // The indentation suggestion changed because `@end` node (a close paren)
847 // is now at the beginning of the line.
848 buffer.edit(
849 [(Point::new(1, 4)..Point::new(1, 5), "")],
850 Some(AutoindentMode::EachLine),
851 cx,
852 );
853 assert_eq!(
854 buffer.text(),
855 "
856 fn a(
857 ) {}
858 "
859 .unindent()
860 );
861
862 buffer
863 });
864}
865
866#[gpui::test]
867fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut MutableAppContext) {
868 cx.set_global(Settings::test(cx));
869 cx.add_model(|cx| {
870 let text = "a\nb";
871 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
872 buffer.edit(
873 [(0..1, "\n"), (2..3, "\n")],
874 Some(AutoindentMode::EachLine),
875 cx,
876 );
877 assert_eq!(buffer.text(), "\n\n\n");
878 buffer
879 });
880}
881
882#[gpui::test]
883fn test_autoindent_multi_line_insertion(cx: &mut MutableAppContext) {
884 cx.set_global(Settings::test(cx));
885 cx.add_model(|cx| {
886 let text = "
887 const a: usize = 1;
888 fn b() {
889 if c {
890 let d = 2;
891 }
892 }
893 "
894 .unindent();
895
896 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
897 buffer.edit(
898 [(Point::new(3, 0)..Point::new(3, 0), "e(\n f()\n);\n")],
899 Some(AutoindentMode::EachLine),
900 cx,
901 );
902 assert_eq!(
903 buffer.text(),
904 "
905 const a: usize = 1;
906 fn b() {
907 if c {
908 e(
909 f()
910 );
911 let d = 2;
912 }
913 }
914 "
915 .unindent()
916 );
917
918 buffer
919 });
920}
921
922#[gpui::test]
923fn test_autoindent_block_mode(cx: &mut MutableAppContext) {
924 cx.set_global(Settings::test(cx));
925 cx.add_model(|cx| {
926 let text = r#"
927 fn a() {
928 b();
929 }
930 "#
931 .unindent();
932 let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
933
934 let inserted_text = r#"
935 "
936 c
937 d
938 e
939 "
940 "#
941 .unindent();
942
943 // Insert the block at column zero. The entire block is indented
944 // so that the first line matches the previous line's indentation.
945 buffer.edit(
946 [(Point::new(2, 0)..Point::new(2, 0), inserted_text.clone())],
947 Some(AutoindentMode::Block {
948 original_indent_columns: vec![0],
949 }),
950 cx,
951 );
952 assert_eq!(
953 buffer.text(),
954 r#"
955 fn a() {
956 b();
957 "
958 c
959 d
960 e
961 "
962 }
963 "#
964 .unindent()
965 );
966
967 // Insert the block at a deeper indent level. The entire block is outdented.
968 buffer.undo(cx);
969 buffer.edit([(Point::new(2, 0)..Point::new(2, 0), " ")], None, cx);
970 buffer.edit(
971 [(Point::new(2, 8)..Point::new(2, 8), inserted_text)],
972 Some(AutoindentMode::Block {
973 original_indent_columns: vec![0],
974 }),
975 cx,
976 );
977 assert_eq!(
978 buffer.text(),
979 r#"
980 fn a() {
981 b();
982 "
983 c
984 d
985 e
986 "
987 }
988 "#
989 .unindent()
990 );
991
992 buffer
993 });
994}
995
996#[gpui::test]
997fn test_autoindent_language_without_indents_query(cx: &mut MutableAppContext) {
998 cx.set_global(Settings::test(cx));
999 cx.add_model(|cx| {
1000 let text = "
1001 * one
1002 - a
1003 - b
1004 * two
1005 "
1006 .unindent();
1007
1008 let mut buffer = Buffer::new(0, text, cx).with_language(
1009 Arc::new(Language::new(
1010 LanguageConfig {
1011 name: "Markdown".into(),
1012 auto_indent_using_last_non_empty_line: false,
1013 ..Default::default()
1014 },
1015 Some(tree_sitter_json::language()),
1016 )),
1017 cx,
1018 );
1019 buffer.edit(
1020 [(Point::new(3, 0)..Point::new(3, 0), "\n")],
1021 Some(AutoindentMode::EachLine),
1022 cx,
1023 );
1024 assert_eq!(
1025 buffer.text(),
1026 "
1027 * one
1028 - a
1029 - b
1030
1031 * two
1032 "
1033 .unindent()
1034 );
1035 buffer
1036 });
1037}
1038
1039#[gpui::test]
1040fn test_autoindent_with_injected_languages(cx: &mut MutableAppContext) {
1041 cx.set_global({
1042 let mut settings = Settings::test(cx);
1043 settings.language_overrides.extend([
1044 (
1045 "HTML".into(),
1046 settings::EditorSettings {
1047 tab_size: Some(2.try_into().unwrap()),
1048 ..Default::default()
1049 },
1050 ),
1051 (
1052 "JavaScript".into(),
1053 settings::EditorSettings {
1054 tab_size: Some(8.try_into().unwrap()),
1055 ..Default::default()
1056 },
1057 ),
1058 ]);
1059 settings
1060 });
1061
1062 let html_language = Arc::new(
1063 Language::new(
1064 LanguageConfig {
1065 name: "HTML".into(),
1066 ..Default::default()
1067 },
1068 Some(tree_sitter_html::language()),
1069 )
1070 .with_indents_query(
1071 "
1072 (element
1073 (start_tag) @start
1074 (end_tag)? @end) @indent
1075 ",
1076 )
1077 .unwrap()
1078 .with_injection_query(
1079 r#"
1080 (script_element
1081 (raw_text) @content
1082 (#set! "language" "javascript"))
1083 "#,
1084 )
1085 .unwrap(),
1086 );
1087
1088 let javascript_language = Arc::new(
1089 Language::new(
1090 LanguageConfig {
1091 name: "JavaScript".into(),
1092 ..Default::default()
1093 },
1094 Some(tree_sitter_javascript::language()),
1095 )
1096 .with_indents_query(
1097 r#"
1098 (object "}" @end) @indent
1099 "#,
1100 )
1101 .unwrap(),
1102 );
1103
1104 let language_registry = Arc::new(LanguageRegistry::test());
1105 language_registry.add(html_language.clone());
1106 language_registry.add(javascript_language.clone());
1107
1108 cx.add_model(|cx| {
1109 let (text, ranges) = marked_text_ranges(
1110 &"
1111 <div>ˇ
1112 </div>
1113 <script>
1114 init({ˇ
1115 })
1116 </script>
1117 <span>ˇ
1118 </span>
1119 "
1120 .unindent(),
1121 false,
1122 );
1123
1124 let mut buffer = Buffer::new(0, text, cx);
1125 buffer.set_language_registry(language_registry);
1126 buffer.set_language(Some(html_language), cx);
1127 buffer.edit(
1128 ranges.into_iter().map(|range| (range, "\na")),
1129 Some(AutoindentMode::EachLine),
1130 cx,
1131 );
1132 assert_eq!(
1133 buffer.text(),
1134 "
1135 <div>
1136 a
1137 </div>
1138 <script>
1139 init({
1140 a
1141 })
1142 </script>
1143 <span>
1144 a
1145 </span>
1146 "
1147 .unindent()
1148 );
1149 buffer
1150 });
1151}
1152
1153#[gpui::test]
1154fn test_autoindent_query_with_outdent_captures(cx: &mut MutableAppContext) {
1155 let mut settings = Settings::test(cx);
1156 settings.editor_defaults.tab_size = Some(2.try_into().unwrap());
1157 cx.set_global(settings);
1158 cx.add_model(|cx| {
1159 let mut buffer = Buffer::new(0, "", cx).with_language(Arc::new(ruby_lang()), cx);
1160
1161 let text = r#"
1162 class C
1163 def a(b, c)
1164 puts b
1165 puts c
1166 rescue
1167 puts "errored"
1168 exit 1
1169 end
1170 end
1171 "#
1172 .unindent();
1173
1174 buffer.edit([(0..0, text)], Some(AutoindentMode::EachLine), cx);
1175
1176 assert_eq!(
1177 buffer.text(),
1178 r#"
1179 class C
1180 def a(b, c)
1181 puts b
1182 puts c
1183 rescue
1184 puts "errored"
1185 exit 1
1186 end
1187 end
1188 "#
1189 .unindent()
1190 );
1191
1192 buffer
1193 });
1194}
1195
1196#[gpui::test]
1197fn test_serialization(cx: &mut gpui::MutableAppContext) {
1198 let mut now = Instant::now();
1199
1200 let buffer1 = cx.add_model(|cx| {
1201 let mut buffer = Buffer::new(0, "abc", cx);
1202 buffer.edit([(3..3, "D")], None, cx);
1203
1204 now += Duration::from_secs(1);
1205 buffer.start_transaction_at(now);
1206 buffer.edit([(4..4, "E")], None, cx);
1207 buffer.end_transaction_at(now, cx);
1208 assert_eq!(buffer.text(), "abcDE");
1209
1210 buffer.undo(cx);
1211 assert_eq!(buffer.text(), "abcD");
1212
1213 buffer.edit([(4..4, "F")], None, cx);
1214 assert_eq!(buffer.text(), "abcDF");
1215 buffer
1216 });
1217 assert_eq!(buffer1.read(cx).text(), "abcDF");
1218
1219 let state = buffer1.read(cx).to_proto();
1220 let ops = cx.background().block(buffer1.read(cx).serialize_ops(cx));
1221 let buffer2 = cx.add_model(|cx| {
1222 let mut buffer = Buffer::from_proto(1, state, None).unwrap();
1223 buffer
1224 .apply_ops(
1225 ops.into_iter()
1226 .map(|op| proto::deserialize_operation(op).unwrap()),
1227 cx,
1228 )
1229 .unwrap();
1230 buffer
1231 });
1232 assert_eq!(buffer2.read(cx).text(), "abcDF");
1233}
1234
1235#[gpui::test(iterations = 100)]
1236fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) {
1237 let min_peers = env::var("MIN_PEERS")
1238 .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
1239 .unwrap_or(1);
1240 let max_peers = env::var("MAX_PEERS")
1241 .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
1242 .unwrap_or(5);
1243 let operations = env::var("OPERATIONS")
1244 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1245 .unwrap_or(10);
1246
1247 let base_text_len = rng.gen_range(0..10);
1248 let base_text = RandomCharIter::new(&mut rng)
1249 .take(base_text_len)
1250 .collect::<String>();
1251 let mut replica_ids = Vec::new();
1252 let mut buffers = Vec::new();
1253 let network = Rc::new(RefCell::new(Network::new(rng.clone())));
1254 let base_buffer = cx.add_model(|cx| Buffer::new(0, base_text.as_str(), cx));
1255
1256 for i in 0..rng.gen_range(min_peers..=max_peers) {
1257 let buffer = cx.add_model(|cx| {
1258 let state = base_buffer.read(cx).to_proto();
1259 let ops = cx
1260 .background()
1261 .block(base_buffer.read(cx).serialize_ops(cx));
1262 let mut buffer = Buffer::from_proto(i as ReplicaId, state, None).unwrap();
1263 buffer
1264 .apply_ops(
1265 ops.into_iter()
1266 .map(|op| proto::deserialize_operation(op).unwrap()),
1267 cx,
1268 )
1269 .unwrap();
1270 buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
1271 let network = network.clone();
1272 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1273 if let Event::Operation(op) = event {
1274 network
1275 .borrow_mut()
1276 .broadcast(buffer.replica_id(), vec![proto::serialize_operation(op)]);
1277 }
1278 })
1279 .detach();
1280 buffer
1281 });
1282 buffers.push(buffer);
1283 replica_ids.push(i as ReplicaId);
1284 network.borrow_mut().add_peer(i as ReplicaId);
1285 log::info!("Adding initial peer with replica id {}", i);
1286 }
1287
1288 log::info!("initial text: {:?}", base_text);
1289
1290 let mut now = Instant::now();
1291 let mut mutation_count = operations;
1292 let mut next_diagnostic_id = 0;
1293 let mut active_selections = BTreeMap::default();
1294 loop {
1295 let replica_index = rng.gen_range(0..replica_ids.len());
1296 let replica_id = replica_ids[replica_index];
1297 let buffer = &mut buffers[replica_index];
1298 let mut new_buffer = None;
1299 match rng.gen_range(0..100) {
1300 0..=29 if mutation_count != 0 => {
1301 buffer.update(cx, |buffer, cx| {
1302 buffer.start_transaction_at(now);
1303 buffer.randomly_edit(&mut rng, 5, cx);
1304 buffer.end_transaction_at(now, cx);
1305 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1306 });
1307 mutation_count -= 1;
1308 }
1309 30..=39 if mutation_count != 0 => {
1310 buffer.update(cx, |buffer, cx| {
1311 let mut selections = Vec::new();
1312 for id in 0..rng.gen_range(1..=5) {
1313 let range = buffer.random_byte_range(0, &mut rng);
1314 selections.push(Selection {
1315 id,
1316 start: buffer.anchor_before(range.start),
1317 end: buffer.anchor_before(range.end),
1318 reversed: false,
1319 goal: SelectionGoal::None,
1320 });
1321 }
1322 let selections: Arc<[Selection<Anchor>]> = selections.into();
1323 log::info!(
1324 "peer {} setting active selections: {:?}",
1325 replica_id,
1326 selections
1327 );
1328 active_selections.insert(replica_id, selections.clone());
1329 buffer.set_active_selections(selections, false, Default::default(), cx);
1330 });
1331 mutation_count -= 1;
1332 }
1333 40..=49 if mutation_count != 0 && replica_id == 0 => {
1334 let entry_count = rng.gen_range(1..=5);
1335 buffer.update(cx, |buffer, cx| {
1336 let diagnostics = DiagnosticSet::new(
1337 (0..entry_count).map(|_| {
1338 let range = buffer.random_byte_range(0, &mut rng);
1339 let range = range.to_point_utf16(buffer);
1340 DiagnosticEntry {
1341 range,
1342 diagnostic: Diagnostic {
1343 message: post_inc(&mut next_diagnostic_id).to_string(),
1344 ..Default::default()
1345 },
1346 }
1347 }),
1348 buffer,
1349 );
1350 log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
1351 buffer.update_diagnostics(diagnostics, cx);
1352 });
1353 mutation_count -= 1;
1354 }
1355 50..=59 if replica_ids.len() < max_peers => {
1356 let old_buffer_state = buffer.read(cx).to_proto();
1357 let old_buffer_ops = cx.background().block(buffer.read(cx).serialize_ops(cx));
1358 let new_replica_id = (0..=replica_ids.len() as ReplicaId)
1359 .filter(|replica_id| *replica_id != buffer.read(cx).replica_id())
1360 .choose(&mut rng)
1361 .unwrap();
1362 log::info!(
1363 "Adding new replica {} (replicating from {})",
1364 new_replica_id,
1365 replica_id
1366 );
1367 new_buffer = Some(cx.add_model(|cx| {
1368 let mut new_buffer =
1369 Buffer::from_proto(new_replica_id, old_buffer_state, None).unwrap();
1370 new_buffer
1371 .apply_ops(
1372 old_buffer_ops
1373 .into_iter()
1374 .map(|op| deserialize_operation(op).unwrap()),
1375 cx,
1376 )
1377 .unwrap();
1378 log::info!(
1379 "New replica {} text: {:?}",
1380 new_buffer.replica_id(),
1381 new_buffer.text()
1382 );
1383 new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
1384 let network = network.clone();
1385 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1386 if let Event::Operation(op) = event {
1387 network.borrow_mut().broadcast(
1388 buffer.replica_id(),
1389 vec![proto::serialize_operation(op)],
1390 );
1391 }
1392 })
1393 .detach();
1394 new_buffer
1395 }));
1396 network.borrow_mut().replicate(replica_id, new_replica_id);
1397
1398 if new_replica_id as usize == replica_ids.len() {
1399 replica_ids.push(new_replica_id);
1400 } else {
1401 let new_buffer = new_buffer.take().unwrap();
1402 while network.borrow().has_unreceived(new_replica_id) {
1403 let ops = network
1404 .borrow_mut()
1405 .receive(new_replica_id)
1406 .into_iter()
1407 .map(|op| proto::deserialize_operation(op).unwrap());
1408 if ops.len() > 0 {
1409 log::info!(
1410 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1411 new_replica_id,
1412 buffer.read(cx).version(),
1413 ops.len(),
1414 ops
1415 );
1416 new_buffer.update(cx, |new_buffer, cx| {
1417 new_buffer.apply_ops(ops, cx).unwrap();
1418 });
1419 }
1420 }
1421 buffers[new_replica_id as usize] = new_buffer;
1422 }
1423 }
1424 60..=69 if mutation_count != 0 => {
1425 buffer.update(cx, |buffer, cx| {
1426 buffer.randomly_undo_redo(&mut rng, cx);
1427 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1428 });
1429 mutation_count -= 1;
1430 }
1431 _ if network.borrow().has_unreceived(replica_id) => {
1432 let ops = network
1433 .borrow_mut()
1434 .receive(replica_id)
1435 .into_iter()
1436 .map(|op| proto::deserialize_operation(op).unwrap());
1437 if ops.len() > 0 {
1438 log::info!(
1439 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1440 replica_id,
1441 buffer.read(cx).version(),
1442 ops.len(),
1443 ops
1444 );
1445 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
1446 }
1447 }
1448 _ => {}
1449 }
1450
1451 now += Duration::from_millis(rng.gen_range(0..=200));
1452 buffers.extend(new_buffer);
1453
1454 for buffer in &buffers {
1455 buffer.read(cx).check_invariants();
1456 }
1457
1458 if mutation_count == 0 && network.borrow().is_idle() {
1459 break;
1460 }
1461 }
1462
1463 let first_buffer = buffers[0].read(cx).snapshot();
1464 for buffer in &buffers[1..] {
1465 let buffer = buffer.read(cx).snapshot();
1466 assert_eq!(
1467 buffer.version(),
1468 first_buffer.version(),
1469 "Replica {} version != Replica 0 version",
1470 buffer.replica_id()
1471 );
1472 assert_eq!(
1473 buffer.text(),
1474 first_buffer.text(),
1475 "Replica {} text != Replica 0 text",
1476 buffer.replica_id()
1477 );
1478 assert_eq!(
1479 buffer
1480 .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
1481 .collect::<Vec<_>>(),
1482 first_buffer
1483 .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
1484 .collect::<Vec<_>>(),
1485 "Replica {} diagnostics != Replica 0 diagnostics",
1486 buffer.replica_id()
1487 );
1488 }
1489
1490 for buffer in &buffers {
1491 let buffer = buffer.read(cx).snapshot();
1492 let actual_remote_selections = buffer
1493 .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
1494 .map(|(replica_id, _, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
1495 .collect::<Vec<_>>();
1496 let expected_remote_selections = active_selections
1497 .iter()
1498 .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
1499 .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
1500 .collect::<Vec<_>>();
1501 assert_eq!(
1502 actual_remote_selections,
1503 expected_remote_selections,
1504 "Replica {} remote selections != expected selections",
1505 buffer.replica_id()
1506 );
1507 }
1508}
1509
1510#[test]
1511fn test_contiguous_ranges() {
1512 assert_eq!(
1513 contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
1514 &[1..4, 5..7, 9..13]
1515 );
1516
1517 // Respects the `max_len` parameter
1518 assert_eq!(
1519 contiguous_ranges(
1520 [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
1521 3
1522 )
1523 .collect::<Vec<_>>(),
1524 &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
1525 );
1526}
1527
1528impl Buffer {
1529 pub fn enclosing_bracket_point_ranges<T: ToOffset>(
1530 &self,
1531 range: Range<T>,
1532 ) -> Option<(Range<Point>, Range<Point>)> {
1533 self.snapshot()
1534 .enclosing_bracket_ranges(range)
1535 .map(|(start, end)| {
1536 let point_start = start.start.to_point(self)..start.end.to_point(self);
1537 let point_end = end.start.to_point(self)..end.end.to_point(self);
1538 (point_start, point_end)
1539 })
1540 }
1541}
1542
1543fn ruby_lang() -> Language {
1544 Language::new(
1545 LanguageConfig {
1546 name: "Ruby".into(),
1547 path_suffixes: vec!["rb".to_string()],
1548 ..Default::default()
1549 },
1550 Some(tree_sitter_ruby::language()),
1551 )
1552 .with_indents_query(
1553 r#"
1554 (class "end" @end) @indent
1555 (method "end" @end) @indent
1556 (rescue) @outdent
1557 (then) @indent
1558 "#,
1559 )
1560 .unwrap()
1561}
1562
1563fn rust_lang() -> Language {
1564 Language::new(
1565 LanguageConfig {
1566 name: "Rust".into(),
1567 path_suffixes: vec!["rs".to_string()],
1568 ..Default::default()
1569 },
1570 Some(tree_sitter_rust::language()),
1571 )
1572 .with_indents_query(
1573 r#"
1574 (call_expression) @indent
1575 (field_expression) @indent
1576 (_ "(" ")" @end) @indent
1577 (_ "{" "}" @end) @indent
1578 "#,
1579 )
1580 .unwrap()
1581 .with_brackets_query(
1582 r#"
1583 ("{" @open "}" @close)
1584 "#,
1585 )
1586 .unwrap()
1587 .with_outline_query(
1588 r#"
1589 (struct_item
1590 "struct" @context
1591 name: (_) @name) @item
1592 (enum_item
1593 "enum" @context
1594 name: (_) @name) @item
1595 (enum_variant
1596 name: (_) @name) @item
1597 (field_declaration
1598 name: (_) @name) @item
1599 (impl_item
1600 "impl" @context
1601 trait: (_)? @name
1602 "for"? @context
1603 type: (_) @name) @item
1604 (function_item
1605 "fn" @context
1606 name: (_) @name) @item
1607 (mod_item
1608 "mod" @context
1609 name: (_) @name) @item
1610 "#,
1611 )
1612 .unwrap()
1613}
1614
1615fn json_lang() -> Language {
1616 Language::new(
1617 LanguageConfig {
1618 name: "Json".into(),
1619 path_suffixes: vec!["js".to_string()],
1620 ..Default::default()
1621 },
1622 Some(tree_sitter_json::language()),
1623 )
1624}
1625
1626fn get_tree_sexp(buffer: &ModelHandle<Buffer>, cx: &gpui::TestAppContext) -> String {
1627 buffer.read_with(cx, |buffer, _| {
1628 let snapshot = buffer.snapshot();
1629 let layers = snapshot.syntax.layers(buffer.as_text_snapshot());
1630 layers[0].node.to_sexp()
1631 })
1632}
1633
1634fn empty(point: Point) -> Range<Point> {
1635 point..point
1636}