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_serialization(cx: &mut gpui::MutableAppContext) {
1155 let mut now = Instant::now();
1156
1157 let buffer1 = cx.add_model(|cx| {
1158 let mut buffer = Buffer::new(0, "abc", cx);
1159 buffer.edit([(3..3, "D")], None, cx);
1160
1161 now += Duration::from_secs(1);
1162 buffer.start_transaction_at(now);
1163 buffer.edit([(4..4, "E")], None, cx);
1164 buffer.end_transaction_at(now, cx);
1165 assert_eq!(buffer.text(), "abcDE");
1166
1167 buffer.undo(cx);
1168 assert_eq!(buffer.text(), "abcD");
1169
1170 buffer.edit([(4..4, "F")], None, cx);
1171 assert_eq!(buffer.text(), "abcDF");
1172 buffer
1173 });
1174 assert_eq!(buffer1.read(cx).text(), "abcDF");
1175
1176 let state = buffer1.read(cx).to_proto();
1177 let ops = cx.background().block(buffer1.read(cx).serialize_ops(cx));
1178 let buffer2 = cx.add_model(|cx| {
1179 let mut buffer = Buffer::from_proto(1, state, None).unwrap();
1180 buffer
1181 .apply_ops(
1182 ops.into_iter()
1183 .map(|op| proto::deserialize_operation(op).unwrap()),
1184 cx,
1185 )
1186 .unwrap();
1187 buffer
1188 });
1189 assert_eq!(buffer2.read(cx).text(), "abcDF");
1190}
1191
1192#[gpui::test(iterations = 100)]
1193fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) {
1194 let min_peers = env::var("MIN_PEERS")
1195 .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
1196 .unwrap_or(1);
1197 let max_peers = env::var("MAX_PEERS")
1198 .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
1199 .unwrap_or(5);
1200 let operations = env::var("OPERATIONS")
1201 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1202 .unwrap_or(10);
1203
1204 let base_text_len = rng.gen_range(0..10);
1205 let base_text = RandomCharIter::new(&mut rng)
1206 .take(base_text_len)
1207 .collect::<String>();
1208 let mut replica_ids = Vec::new();
1209 let mut buffers = Vec::new();
1210 let network = Rc::new(RefCell::new(Network::new(rng.clone())));
1211 let base_buffer = cx.add_model(|cx| Buffer::new(0, base_text.as_str(), cx));
1212
1213 for i in 0..rng.gen_range(min_peers..=max_peers) {
1214 let buffer = cx.add_model(|cx| {
1215 let state = base_buffer.read(cx).to_proto();
1216 let ops = cx
1217 .background()
1218 .block(base_buffer.read(cx).serialize_ops(cx));
1219 let mut buffer = Buffer::from_proto(i as ReplicaId, state, None).unwrap();
1220 buffer
1221 .apply_ops(
1222 ops.into_iter()
1223 .map(|op| proto::deserialize_operation(op).unwrap()),
1224 cx,
1225 )
1226 .unwrap();
1227 buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
1228 let network = network.clone();
1229 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1230 if let Event::Operation(op) = event {
1231 network
1232 .borrow_mut()
1233 .broadcast(buffer.replica_id(), vec![proto::serialize_operation(op)]);
1234 }
1235 })
1236 .detach();
1237 buffer
1238 });
1239 buffers.push(buffer);
1240 replica_ids.push(i as ReplicaId);
1241 network.borrow_mut().add_peer(i as ReplicaId);
1242 log::info!("Adding initial peer with replica id {}", i);
1243 }
1244
1245 log::info!("initial text: {:?}", base_text);
1246
1247 let mut now = Instant::now();
1248 let mut mutation_count = operations;
1249 let mut next_diagnostic_id = 0;
1250 let mut active_selections = BTreeMap::default();
1251 loop {
1252 let replica_index = rng.gen_range(0..replica_ids.len());
1253 let replica_id = replica_ids[replica_index];
1254 let buffer = &mut buffers[replica_index];
1255 let mut new_buffer = None;
1256 match rng.gen_range(0..100) {
1257 0..=29 if mutation_count != 0 => {
1258 buffer.update(cx, |buffer, cx| {
1259 buffer.start_transaction_at(now);
1260 buffer.randomly_edit(&mut rng, 5, cx);
1261 buffer.end_transaction_at(now, cx);
1262 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1263 });
1264 mutation_count -= 1;
1265 }
1266 30..=39 if mutation_count != 0 => {
1267 buffer.update(cx, |buffer, cx| {
1268 let mut selections = Vec::new();
1269 for id in 0..rng.gen_range(1..=5) {
1270 let range = buffer.random_byte_range(0, &mut rng);
1271 selections.push(Selection {
1272 id,
1273 start: buffer.anchor_before(range.start),
1274 end: buffer.anchor_before(range.end),
1275 reversed: false,
1276 goal: SelectionGoal::None,
1277 });
1278 }
1279 let selections: Arc<[Selection<Anchor>]> = selections.into();
1280 log::info!(
1281 "peer {} setting active selections: {:?}",
1282 replica_id,
1283 selections
1284 );
1285 active_selections.insert(replica_id, selections.clone());
1286 buffer.set_active_selections(selections, false, Default::default(), cx);
1287 });
1288 mutation_count -= 1;
1289 }
1290 40..=49 if mutation_count != 0 && replica_id == 0 => {
1291 let entry_count = rng.gen_range(1..=5);
1292 buffer.update(cx, |buffer, cx| {
1293 let diagnostics = DiagnosticSet::new(
1294 (0..entry_count).map(|_| {
1295 let range = buffer.random_byte_range(0, &mut rng);
1296 let range = range.to_point_utf16(buffer);
1297 DiagnosticEntry {
1298 range,
1299 diagnostic: Diagnostic {
1300 message: post_inc(&mut next_diagnostic_id).to_string(),
1301 ..Default::default()
1302 },
1303 }
1304 }),
1305 buffer,
1306 );
1307 log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
1308 buffer.update_diagnostics(diagnostics, cx);
1309 });
1310 mutation_count -= 1;
1311 }
1312 50..=59 if replica_ids.len() < max_peers => {
1313 let old_buffer_state = buffer.read(cx).to_proto();
1314 let old_buffer_ops = cx.background().block(buffer.read(cx).serialize_ops(cx));
1315 let new_replica_id = (0..=replica_ids.len() as ReplicaId)
1316 .filter(|replica_id| *replica_id != buffer.read(cx).replica_id())
1317 .choose(&mut rng)
1318 .unwrap();
1319 log::info!(
1320 "Adding new replica {} (replicating from {})",
1321 new_replica_id,
1322 replica_id
1323 );
1324 new_buffer = Some(cx.add_model(|cx| {
1325 let mut new_buffer =
1326 Buffer::from_proto(new_replica_id, old_buffer_state, None).unwrap();
1327 new_buffer
1328 .apply_ops(
1329 old_buffer_ops
1330 .into_iter()
1331 .map(|op| deserialize_operation(op).unwrap()),
1332 cx,
1333 )
1334 .unwrap();
1335 log::info!(
1336 "New replica {} text: {:?}",
1337 new_buffer.replica_id(),
1338 new_buffer.text()
1339 );
1340 new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
1341 let network = network.clone();
1342 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
1343 if let Event::Operation(op) = event {
1344 network.borrow_mut().broadcast(
1345 buffer.replica_id(),
1346 vec![proto::serialize_operation(op)],
1347 );
1348 }
1349 })
1350 .detach();
1351 new_buffer
1352 }));
1353 network.borrow_mut().replicate(replica_id, new_replica_id);
1354
1355 if new_replica_id as usize == replica_ids.len() {
1356 replica_ids.push(new_replica_id);
1357 } else {
1358 let new_buffer = new_buffer.take().unwrap();
1359 while network.borrow().has_unreceived(new_replica_id) {
1360 let ops = network
1361 .borrow_mut()
1362 .receive(new_replica_id)
1363 .into_iter()
1364 .map(|op| proto::deserialize_operation(op).unwrap());
1365 if ops.len() > 0 {
1366 log::info!(
1367 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1368 new_replica_id,
1369 buffer.read(cx).version(),
1370 ops.len(),
1371 ops
1372 );
1373 new_buffer.update(cx, |new_buffer, cx| {
1374 new_buffer.apply_ops(ops, cx).unwrap();
1375 });
1376 }
1377 }
1378 buffers[new_replica_id as usize] = new_buffer;
1379 }
1380 }
1381 60..=69 if mutation_count != 0 => {
1382 buffer.update(cx, |buffer, cx| {
1383 buffer.randomly_undo_redo(&mut rng, cx);
1384 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
1385 });
1386 mutation_count -= 1;
1387 }
1388 _ if network.borrow().has_unreceived(replica_id) => {
1389 let ops = network
1390 .borrow_mut()
1391 .receive(replica_id)
1392 .into_iter()
1393 .map(|op| proto::deserialize_operation(op).unwrap());
1394 if ops.len() > 0 {
1395 log::info!(
1396 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
1397 replica_id,
1398 buffer.read(cx).version(),
1399 ops.len(),
1400 ops
1401 );
1402 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
1403 }
1404 }
1405 _ => {}
1406 }
1407
1408 now += Duration::from_millis(rng.gen_range(0..=200));
1409 buffers.extend(new_buffer);
1410
1411 for buffer in &buffers {
1412 buffer.read(cx).check_invariants();
1413 }
1414
1415 if mutation_count == 0 && network.borrow().is_idle() {
1416 break;
1417 }
1418 }
1419
1420 let first_buffer = buffers[0].read(cx).snapshot();
1421 for buffer in &buffers[1..] {
1422 let buffer = buffer.read(cx).snapshot();
1423 assert_eq!(
1424 buffer.version(),
1425 first_buffer.version(),
1426 "Replica {} version != Replica 0 version",
1427 buffer.replica_id()
1428 );
1429 assert_eq!(
1430 buffer.text(),
1431 first_buffer.text(),
1432 "Replica {} text != Replica 0 text",
1433 buffer.replica_id()
1434 );
1435 assert_eq!(
1436 buffer
1437 .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
1438 .collect::<Vec<_>>(),
1439 first_buffer
1440 .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
1441 .collect::<Vec<_>>(),
1442 "Replica {} diagnostics != Replica 0 diagnostics",
1443 buffer.replica_id()
1444 );
1445 }
1446
1447 for buffer in &buffers {
1448 let buffer = buffer.read(cx).snapshot();
1449 let actual_remote_selections = buffer
1450 .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
1451 .map(|(replica_id, _, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
1452 .collect::<Vec<_>>();
1453 let expected_remote_selections = active_selections
1454 .iter()
1455 .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
1456 .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
1457 .collect::<Vec<_>>();
1458 assert_eq!(
1459 actual_remote_selections,
1460 expected_remote_selections,
1461 "Replica {} remote selections != expected selections",
1462 buffer.replica_id()
1463 );
1464 }
1465}
1466
1467#[test]
1468fn test_contiguous_ranges() {
1469 assert_eq!(
1470 contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
1471 &[1..4, 5..7, 9..13]
1472 );
1473
1474 // Respects the `max_len` parameter
1475 assert_eq!(
1476 contiguous_ranges(
1477 [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
1478 3
1479 )
1480 .collect::<Vec<_>>(),
1481 &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
1482 );
1483}
1484
1485impl Buffer {
1486 pub fn enclosing_bracket_point_ranges<T: ToOffset>(
1487 &self,
1488 range: Range<T>,
1489 ) -> Option<(Range<Point>, Range<Point>)> {
1490 self.snapshot()
1491 .enclosing_bracket_ranges(range)
1492 .map(|(start, end)| {
1493 let point_start = start.start.to_point(self)..start.end.to_point(self);
1494 let point_end = end.start.to_point(self)..end.end.to_point(self);
1495 (point_start, point_end)
1496 })
1497 }
1498}
1499
1500fn rust_lang() -> Language {
1501 Language::new(
1502 LanguageConfig {
1503 name: "Rust".into(),
1504 path_suffixes: vec!["rs".to_string()],
1505 ..Default::default()
1506 },
1507 Some(tree_sitter_rust::language()),
1508 )
1509 .with_indents_query(
1510 r#"
1511 (call_expression) @indent
1512 (field_expression) @indent
1513 (_ "(" ")" @end) @indent
1514 (_ "{" "}" @end) @indent
1515 "#,
1516 )
1517 .unwrap()
1518 .with_brackets_query(
1519 r#"
1520 ("{" @open "}" @close)
1521 "#,
1522 )
1523 .unwrap()
1524 .with_outline_query(
1525 r#"
1526 (struct_item
1527 "struct" @context
1528 name: (_) @name) @item
1529 (enum_item
1530 "enum" @context
1531 name: (_) @name) @item
1532 (enum_variant
1533 name: (_) @name) @item
1534 (field_declaration
1535 name: (_) @name) @item
1536 (impl_item
1537 "impl" @context
1538 trait: (_)? @name
1539 "for"? @context
1540 type: (_) @name) @item
1541 (function_item
1542 "fn" @context
1543 name: (_) @name) @item
1544 (mod_item
1545 "mod" @context
1546 name: (_) @name) @item
1547 "#,
1548 )
1549 .unwrap()
1550}
1551
1552fn json_lang() -> Language {
1553 Language::new(
1554 LanguageConfig {
1555 name: "Json".into(),
1556 path_suffixes: vec!["js".to_string()],
1557 ..Default::default()
1558 },
1559 Some(tree_sitter_json::language()),
1560 )
1561}
1562
1563fn get_tree_sexp(buffer: &ModelHandle<Buffer>, cx: &gpui::TestAppContext) -> String {
1564 buffer.read_with(cx, |buffer, _| {
1565 let snapshot = buffer.snapshot();
1566 let layers = snapshot.syntax.layers(buffer.as_text_snapshot());
1567 layers[0].node.to_sexp()
1568 })
1569}
1570
1571fn empty(point: Point) -> Range<Point> {
1572 point..point
1573}