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