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