1use super::*;
2use crate::language_settings::{
3 AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent,
4};
5use crate::Buffer;
6use clock::ReplicaId;
7use collections::BTreeMap;
8use gpui::{AppContext, Model};
9use gpui::{Context, TestAppContext};
10use indoc::indoc;
11use proto::deserialize_operation;
12use rand::prelude::*;
13use regex::RegexBuilder;
14use settings::SettingsStore;
15use std::{
16 env,
17 ops::Range,
18 time::{Duration, Instant},
19};
20use text::network::Network;
21use text::{BufferId, LineEnding};
22use text::{Point, ToPoint};
23use unindent::Unindent as _;
24use util::{assert_set_eq, post_inc, test::marked_text_ranges, RandomCharIter};
25
26lazy_static! {
27 static ref TRAILING_WHITESPACE_REGEX: Regex = RegexBuilder::new("[ \t]+$")
28 .multi_line(true)
29 .build()
30 .unwrap();
31}
32
33#[cfg(test)]
34#[ctor::ctor]
35fn init_logger() {
36 if std::env::var("RUST_LOG").is_ok() {
37 env_logger::init();
38 }
39}
40
41#[gpui::test]
42fn test_line_endings(cx: &mut gpui::AppContext) {
43 init_settings(cx, |_| {});
44
45 cx.new_model(|cx| {
46 let mut buffer = Buffer::new(
47 0,
48 BufferId::new(cx.entity_id().as_u64()).unwrap(),
49 "one\r\ntwo\rthree",
50 )
51 .with_language(Arc::new(rust_lang()), cx);
52 assert_eq!(buffer.text(), "one\ntwo\nthree");
53 assert_eq!(buffer.line_ending(), LineEnding::Windows);
54
55 buffer.check_invariants();
56 buffer.edit(
57 [(buffer.len()..buffer.len(), "\r\nfour")],
58 Some(AutoindentMode::EachLine),
59 cx,
60 );
61 buffer.edit([(0..0, "zero\r\n")], None, cx);
62 assert_eq!(buffer.text(), "zero\none\ntwo\nthree\nfour");
63 assert_eq!(buffer.line_ending(), LineEnding::Windows);
64 buffer.check_invariants();
65
66 buffer
67 });
68}
69
70#[gpui::test]
71fn test_select_language() {
72 let registry = Arc::new(LanguageRegistry::test());
73 registry.add(Arc::new(Language::new(
74 LanguageConfig {
75 name: "Rust".into(),
76 path_suffixes: vec!["rs".to_string()],
77 ..Default::default()
78 },
79 Some(tree_sitter_rust::language()),
80 )));
81 registry.add(Arc::new(Language::new(
82 LanguageConfig {
83 name: "Make".into(),
84 path_suffixes: vec!["Makefile".to_string(), "mk".to_string()],
85 ..Default::default()
86 },
87 Some(tree_sitter_rust::language()),
88 )));
89
90 // matching file extension
91 assert_eq!(
92 registry
93 .language_for_file("zed/lib.rs", None)
94 .now_or_never()
95 .and_then(|l| Some(l.ok()?.name())),
96 Some("Rust".into())
97 );
98 assert_eq!(
99 registry
100 .language_for_file("zed/lib.mk", None)
101 .now_or_never()
102 .and_then(|l| Some(l.ok()?.name())),
103 Some("Make".into())
104 );
105
106 // matching filename
107 assert_eq!(
108 registry
109 .language_for_file("zed/Makefile", None)
110 .now_or_never()
111 .and_then(|l| Some(l.ok()?.name())),
112 Some("Make".into())
113 );
114
115 // matching suffix that is not the full file extension or filename
116 assert_eq!(
117 registry
118 .language_for_file("zed/cars", None)
119 .now_or_never()
120 .and_then(|l| Some(l.ok()?.name())),
121 None
122 );
123 assert_eq!(
124 registry
125 .language_for_file("zed/a.cars", None)
126 .now_or_never()
127 .and_then(|l| Some(l.ok()?.name())),
128 None
129 );
130 assert_eq!(
131 registry
132 .language_for_file("zed/sumk", None)
133 .now_or_never()
134 .and_then(|l| Some(l.ok()?.name())),
135 None
136 );
137}
138
139#[gpui::test]
140fn test_edit_events(cx: &mut gpui::AppContext) {
141 let mut now = Instant::now();
142 let buffer_1_events = Arc::new(Mutex::new(Vec::new()));
143 let buffer_2_events = Arc::new(Mutex::new(Vec::new()));
144
145 let buffer1 = cx
146 .new_model(|cx| Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "abcdef"));
147 let buffer2 = cx
148 .new_model(|cx| Buffer::new(1, BufferId::new(cx.entity_id().as_u64()).unwrap(), "abcdef"));
149 let buffer1_ops = Arc::new(Mutex::new(Vec::new()));
150 buffer1.update(cx, {
151 let buffer1_ops = buffer1_ops.clone();
152 |buffer, cx| {
153 let buffer_1_events = buffer_1_events.clone();
154 cx.subscribe(&buffer1, move |_, _, event, _| match event.clone() {
155 Event::Operation(op) => buffer1_ops.lock().push(op),
156 event => buffer_1_events.lock().push(event),
157 })
158 .detach();
159 let buffer_2_events = buffer_2_events.clone();
160 cx.subscribe(&buffer2, move |_, _, event, _| {
161 buffer_2_events.lock().push(event.clone())
162 })
163 .detach();
164
165 // An edit emits an edited event, followed by a dirty changed event,
166 // since the buffer was previously in a clean state.
167 buffer.edit([(2..4, "XYZ")], None, cx);
168
169 // An empty transaction does not emit any events.
170 buffer.start_transaction();
171 buffer.end_transaction(cx);
172
173 // A transaction containing two edits emits one edited event.
174 now += Duration::from_secs(1);
175 buffer.start_transaction_at(now);
176 buffer.edit([(5..5, "u")], None, cx);
177 buffer.edit([(6..6, "w")], None, cx);
178 buffer.end_transaction_at(now, cx);
179
180 // Undoing a transaction emits one edited event.
181 buffer.undo(cx);
182 }
183 });
184
185 // Incorporating a set of remote ops emits a single edited event,
186 // followed by a dirty changed event.
187 buffer2.update(cx, |buffer, cx| {
188 buffer.apply_ops(buffer1_ops.lock().drain(..), cx).unwrap();
189 });
190 assert_eq!(
191 mem::take(&mut *buffer_1_events.lock()),
192 vec![
193 Event::Edited,
194 Event::DirtyChanged,
195 Event::Edited,
196 Event::Edited,
197 ]
198 );
199 assert_eq!(
200 mem::take(&mut *buffer_2_events.lock()),
201 vec![Event::Edited, Event::DirtyChanged]
202 );
203
204 buffer1.update(cx, |buffer, cx| {
205 // Undoing the first transaction emits edited event, followed by a
206 // dirty changed event, since the buffer is again in a clean state.
207 buffer.undo(cx);
208 });
209 // Incorporating the remote ops again emits a single edited event,
210 // followed by a dirty changed event.
211 buffer2.update(cx, |buffer, cx| {
212 buffer.apply_ops(buffer1_ops.lock().drain(..), cx).unwrap();
213 });
214 assert_eq!(
215 mem::take(&mut *buffer_1_events.lock()),
216 vec![Event::Edited, Event::DirtyChanged,]
217 );
218 assert_eq!(
219 mem::take(&mut *buffer_2_events.lock()),
220 vec![Event::Edited, Event::DirtyChanged]
221 );
222}
223
224#[gpui::test]
225async fn test_apply_diff(cx: &mut TestAppContext) {
226 let text = "a\nbb\nccc\ndddd\neeeee\nffffff\n";
227 let buffer =
228 cx.new_model(|cx| Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text));
229 let anchor = buffer.update(cx, |buffer, _| buffer.anchor_before(Point::new(3, 3)));
230
231 let text = "a\nccc\ndddd\nffffff\n";
232 let diff = buffer.update(cx, |b, cx| b.diff(text.into(), cx)).await;
233 buffer.update(cx, |buffer, cx| {
234 buffer.apply_diff(diff, cx).unwrap();
235 assert_eq!(buffer.text(), text);
236 assert_eq!(anchor.to_point(buffer), Point::new(2, 3));
237 });
238
239 let text = "a\n1\n\nccc\ndd2dd\nffffff\n";
240 let diff = buffer.update(cx, |b, cx| b.diff(text.into(), cx)).await;
241 buffer.update(cx, |buffer, cx| {
242 buffer.apply_diff(diff, cx).unwrap();
243 assert_eq!(buffer.text(), text);
244 assert_eq!(anchor.to_point(buffer), Point::new(4, 4));
245 });
246}
247
248#[gpui::test(iterations = 10)]
249async fn test_normalize_whitespace(cx: &mut gpui::TestAppContext) {
250 let text = [
251 "zero", //
252 "one ", // 2 trailing spaces
253 "two", //
254 "three ", // 3 trailing spaces
255 "four", //
256 "five ", // 4 trailing spaces
257 ]
258 .join("\n");
259
260 let buffer =
261 cx.new_model(|cx| Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text));
262
263 // Spawn a task to format the buffer's whitespace.
264 // Pause so that the foratting task starts running.
265 let format = buffer.update(cx, |buffer, cx| buffer.remove_trailing_whitespace(cx));
266 smol::future::yield_now().await;
267
268 // Edit the buffer while the normalization task is running.
269 let version_before_edit = buffer.update(cx, |buffer, _| buffer.version());
270 buffer.update(cx, |buffer, cx| {
271 buffer.edit(
272 [
273 (Point::new(0, 1)..Point::new(0, 1), "EE"),
274 (Point::new(3, 5)..Point::new(3, 5), "EEE"),
275 ],
276 None,
277 cx,
278 );
279 });
280
281 let format_diff = format.await;
282 buffer.update(cx, |buffer, cx| {
283 let version_before_format = format_diff.base_version.clone();
284 buffer.apply_diff(format_diff, cx);
285
286 // The outcome depends on the order of concurrent tasks.
287 //
288 // If the edit occurred while searching for trailing whitespace ranges,
289 // then the trailing whitespace region touched by the edit is left intact.
290 if version_before_format == version_before_edit {
291 assert_eq!(
292 buffer.text(),
293 [
294 "zEEero", //
295 "one", //
296 "two", //
297 "threeEEE ", //
298 "four", //
299 "five", //
300 ]
301 .join("\n")
302 );
303 }
304 // Otherwise, all trailing whitespace is removed.
305 else {
306 assert_eq!(
307 buffer.text(),
308 [
309 "zEEero", //
310 "one", //
311 "two", //
312 "threeEEE", //
313 "four", //
314 "five", //
315 ]
316 .join("\n")
317 );
318 }
319 });
320}
321
322#[gpui::test]
323async fn test_reparse(cx: &mut gpui::TestAppContext) {
324 let text = "fn a() {}";
325 let buffer = cx.new_model(|cx| {
326 Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
327 .with_language(Arc::new(rust_lang()), cx)
328 });
329
330 // Wait for the initial text to parse
331 cx.executor().run_until_parked();
332 assert!(!buffer.update(cx, |buffer, _| buffer.is_parsing()));
333 assert_eq!(
334 get_tree_sexp(&buffer, cx),
335 concat!(
336 "(source_file (function_item name: (identifier) ",
337 "parameters: (parameters) ",
338 "body: (block)))"
339 )
340 );
341
342 buffer.update(cx, |buffer, _| {
343 buffer.set_sync_parse_timeout(Duration::ZERO)
344 });
345
346 // Perform some edits (add parameter and variable reference)
347 // Parsing doesn't begin until the transaction is complete
348 buffer.update(cx, |buf, cx| {
349 buf.start_transaction();
350
351 let offset = buf.text().find(')').unwrap();
352 buf.edit([(offset..offset, "b: C")], None, cx);
353 assert!(!buf.is_parsing());
354
355 let offset = buf.text().find('}').unwrap();
356 buf.edit([(offset..offset, " d; ")], None, cx);
357 assert!(!buf.is_parsing());
358
359 buf.end_transaction(cx);
360 assert_eq!(buf.text(), "fn a(b: C) { d; }");
361 assert!(buf.is_parsing());
362 });
363 cx.executor().run_until_parked();
364 assert!(!buffer.update(cx, |buffer, _| buffer.is_parsing()));
365 assert_eq!(
366 get_tree_sexp(&buffer, cx),
367 concat!(
368 "(source_file (function_item name: (identifier) ",
369 "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
370 "body: (block (expression_statement (identifier)))))"
371 )
372 );
373
374 // Perform a series of edits without waiting for the current parse to complete:
375 // * turn identifier into a field expression
376 // * turn field expression into a method call
377 // * add a turbofish to the method call
378 buffer.update(cx, |buf, cx| {
379 let offset = buf.text().find(';').unwrap();
380 buf.edit([(offset..offset, ".e")], None, cx);
381 assert_eq!(buf.text(), "fn a(b: C) { d.e; }");
382 assert!(buf.is_parsing());
383 });
384 buffer.update(cx, |buf, cx| {
385 let offset = buf.text().find(';').unwrap();
386 buf.edit([(offset..offset, "(f)")], None, cx);
387 assert_eq!(buf.text(), "fn a(b: C) { d.e(f); }");
388 assert!(buf.is_parsing());
389 });
390 buffer.update(cx, |buf, cx| {
391 let offset = buf.text().find("(f)").unwrap();
392 buf.edit([(offset..offset, "::<G>")], None, cx);
393 assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
394 assert!(buf.is_parsing());
395 });
396 cx.executor().run_until_parked();
397 assert_eq!(
398 get_tree_sexp(&buffer, cx),
399 concat!(
400 "(source_file (function_item name: (identifier) ",
401 "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
402 "body: (block (expression_statement (call_expression ",
403 "function: (generic_function ",
404 "function: (field_expression value: (identifier) field: (field_identifier)) ",
405 "type_arguments: (type_arguments (type_identifier))) ",
406 "arguments: (arguments (identifier)))))))",
407 )
408 );
409
410 buffer.update(cx, |buf, cx| {
411 buf.undo(cx);
412 buf.undo(cx);
413 buf.undo(cx);
414 buf.undo(cx);
415 assert_eq!(buf.text(), "fn a() {}");
416 assert!(buf.is_parsing());
417 });
418
419 cx.executor().run_until_parked();
420 assert_eq!(
421 get_tree_sexp(&buffer, cx),
422 concat!(
423 "(source_file (function_item name: (identifier) ",
424 "parameters: (parameters) ",
425 "body: (block)))"
426 )
427 );
428
429 buffer.update(cx, |buf, cx| {
430 buf.redo(cx);
431 buf.redo(cx);
432 buf.redo(cx);
433 buf.redo(cx);
434 assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
435 assert!(buf.is_parsing());
436 });
437 cx.executor().run_until_parked();
438 assert_eq!(
439 get_tree_sexp(&buffer, cx),
440 concat!(
441 "(source_file (function_item name: (identifier) ",
442 "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
443 "body: (block (expression_statement (call_expression ",
444 "function: (generic_function ",
445 "function: (field_expression value: (identifier) field: (field_identifier)) ",
446 "type_arguments: (type_arguments (type_identifier))) ",
447 "arguments: (arguments (identifier)))))))",
448 )
449 );
450}
451
452#[gpui::test]
453async fn test_resetting_language(cx: &mut gpui::TestAppContext) {
454 let buffer = cx.new_model(|cx| {
455 let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "{}")
456 .with_language(Arc::new(rust_lang()), cx);
457 buffer.set_sync_parse_timeout(Duration::ZERO);
458 buffer
459 });
460
461 // Wait for the initial text to parse
462 cx.executor().run_until_parked();
463 assert_eq!(
464 get_tree_sexp(&buffer, cx),
465 "(source_file (expression_statement (block)))"
466 );
467
468 buffer.update(cx, |buffer, cx| {
469 buffer.set_language(Some(Arc::new(json_lang())), cx)
470 });
471 cx.executor().run_until_parked();
472 assert_eq!(get_tree_sexp(&buffer, cx), "(document (object))");
473}
474
475#[gpui::test]
476async fn test_outline(cx: &mut gpui::TestAppContext) {
477 let text = r#"
478 struct Person {
479 name: String,
480 age: usize,
481 }
482
483 mod module {
484 enum LoginState {
485 LoggedOut,
486 LoggingOn,
487 LoggedIn {
488 person: Person,
489 time: Instant,
490 }
491 }
492 }
493
494 impl Eq for Person {}
495
496 impl Drop for Person {
497 fn drop(&mut self) {
498 println!("bye");
499 }
500 }
501 "#
502 .unindent();
503
504 let buffer = cx.new_model(|cx| {
505 Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
506 .with_language(Arc::new(rust_lang()), cx)
507 });
508 let outline = buffer
509 .update(cx, |buffer, _| buffer.snapshot().outline(None))
510 .unwrap();
511
512 assert_eq!(
513 outline
514 .items
515 .iter()
516 .map(|item| (item.text.as_str(), item.depth))
517 .collect::<Vec<_>>(),
518 &[
519 ("struct Person", 0),
520 ("name", 1),
521 ("age", 1),
522 ("mod module", 0),
523 ("enum LoginState", 1),
524 ("LoggedOut", 2),
525 ("LoggingOn", 2),
526 ("LoggedIn", 2),
527 ("person", 3),
528 ("time", 3),
529 ("impl Eq for Person", 0),
530 ("impl Drop for Person", 0),
531 ("fn drop", 1),
532 ]
533 );
534
535 // Without space, we only match on names
536 assert_eq!(
537 search(&outline, "oon", cx).await,
538 &[
539 ("mod module", vec![]), // included as the parent of a match
540 ("enum LoginState", vec![]), // included as the parent of a match
541 ("LoggingOn", vec![1, 7, 8]), // matches
542 ("impl Drop for Person", vec![7, 18, 19]), // matches in two disjoint names
543 ]
544 );
545
546 assert_eq!(
547 search(&outline, "dp p", cx).await,
548 &[
549 ("impl Drop for Person", vec![5, 8, 9, 14]),
550 ("fn drop", vec![]),
551 ]
552 );
553 assert_eq!(
554 search(&outline, "dpn", cx).await,
555 &[("impl Drop for Person", vec![5, 14, 19])]
556 );
557 assert_eq!(
558 search(&outline, "impl ", cx).await,
559 &[
560 ("impl Eq for Person", vec![0, 1, 2, 3, 4]),
561 ("impl Drop for Person", vec![0, 1, 2, 3, 4]),
562 ("fn drop", vec![]),
563 ]
564 );
565
566 async fn search<'a>(
567 outline: &'a Outline<Anchor>,
568 query: &'a str,
569 cx: &'a gpui::TestAppContext,
570 ) -> Vec<(&'a str, Vec<usize>)> {
571 let matches = cx
572 .update(|cx| outline.search(query, cx.background_executor().clone()))
573 .await;
574 matches
575 .into_iter()
576 .map(|mat| (outline.items[mat.candidate_id].text.as_str(), mat.positions))
577 .collect::<Vec<_>>()
578 }
579}
580
581#[gpui::test]
582async fn test_outline_nodes_with_newlines(cx: &mut gpui::TestAppContext) {
583 let text = r#"
584 impl A for B<
585 C
586 > {
587 };
588 "#
589 .unindent();
590
591 let buffer = cx.new_model(|cx| {
592 Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
593 .with_language(Arc::new(rust_lang()), cx)
594 });
595 let outline = buffer
596 .update(cx, |buffer, _| buffer.snapshot().outline(None))
597 .unwrap();
598
599 assert_eq!(
600 outline
601 .items
602 .iter()
603 .map(|item| (item.text.as_str(), item.depth))
604 .collect::<Vec<_>>(),
605 &[("impl A for B<", 0)]
606 );
607}
608
609#[gpui::test]
610async fn test_outline_with_extra_context(cx: &mut gpui::TestAppContext) {
611 let language = javascript_lang()
612 .with_outline_query(
613 r#"
614 (function_declaration
615 "function" @context
616 name: (_) @name
617 parameters: (formal_parameters
618 "(" @context.extra
619 ")" @context.extra)) @item
620 "#,
621 )
622 .unwrap();
623
624 let text = r#"
625 function a() {}
626 function b(c) {}
627 "#
628 .unindent();
629
630 let buffer = cx.new_model(|cx| {
631 Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
632 .with_language(Arc::new(language), cx)
633 });
634 let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
635
636 // extra context nodes are included in the outline.
637 let outline = snapshot.outline(None).unwrap();
638 assert_eq!(
639 outline
640 .items
641 .iter()
642 .map(|item| (item.text.as_str(), item.depth))
643 .collect::<Vec<_>>(),
644 &[("function a()", 0), ("function b( )", 0),]
645 );
646
647 // extra context nodes do not appear in breadcrumbs.
648 let symbols = snapshot.symbols_containing(3, None).unwrap();
649 assert_eq!(
650 symbols
651 .iter()
652 .map(|item| (item.text.as_str(), item.depth))
653 .collect::<Vec<_>>(),
654 &[("function a", 0)]
655 );
656}
657
658#[gpui::test]
659async fn test_symbols_containing(cx: &mut gpui::TestAppContext) {
660 let text = r#"
661 impl Person {
662 fn one() {
663 1
664 }
665
666 fn two() {
667 2
668 }fn three() {
669 3
670 }
671 }
672 "#
673 .unindent();
674
675 let buffer = cx.new_model(|cx| {
676 Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
677 .with_language(Arc::new(rust_lang()), cx)
678 });
679 let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
680
681 // point is at the start of an item
682 assert_eq!(
683 symbols_containing(Point::new(1, 4), &snapshot),
684 vec![
685 (
686 "impl Person".to_string(),
687 Point::new(0, 0)..Point::new(10, 1)
688 ),
689 ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
690 ]
691 );
692
693 // point is in the middle of an item
694 assert_eq!(
695 symbols_containing(Point::new(2, 8), &snapshot),
696 vec![
697 (
698 "impl Person".to_string(),
699 Point::new(0, 0)..Point::new(10, 1)
700 ),
701 ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
702 ]
703 );
704
705 // point is at the end of an item
706 assert_eq!(
707 symbols_containing(Point::new(3, 5), &snapshot),
708 vec![
709 (
710 "impl Person".to_string(),
711 Point::new(0, 0)..Point::new(10, 1)
712 ),
713 ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
714 ]
715 );
716
717 // point is in between two adjacent items
718 assert_eq!(
719 symbols_containing(Point::new(7, 5), &snapshot),
720 vec![
721 (
722 "impl Person".to_string(),
723 Point::new(0, 0)..Point::new(10, 1)
724 ),
725 ("fn two".to_string(), Point::new(5, 4)..Point::new(7, 5))
726 ]
727 );
728
729 fn symbols_containing(
730 position: Point,
731 snapshot: &BufferSnapshot,
732 ) -> Vec<(String, Range<Point>)> {
733 snapshot
734 .symbols_containing(position, None)
735 .unwrap()
736 .into_iter()
737 .map(|item| {
738 (
739 item.text,
740 item.range.start.to_point(snapshot)..item.range.end.to_point(snapshot),
741 )
742 })
743 .collect()
744 }
745}
746
747#[gpui::test]
748fn test_enclosing_bracket_ranges(cx: &mut AppContext) {
749 let mut assert = |selection_text, range_markers| {
750 assert_bracket_pairs(selection_text, range_markers, rust_lang(), cx)
751 };
752
753 assert(
754 indoc! {"
755 mod x {
756 moˇd y {
757
758 }
759 }
760 let foo = 1;"},
761 vec![indoc! {"
762 mod x «{»
763 mod y {
764
765 }
766 «}»
767 let foo = 1;"}],
768 );
769
770 assert(
771 indoc! {"
772 mod x {
773 mod y ˇ{
774
775 }
776 }
777 let foo = 1;"},
778 vec![
779 indoc! {"
780 mod x «{»
781 mod y {
782
783 }
784 «}»
785 let foo = 1;"},
786 indoc! {"
787 mod x {
788 mod y «{»
789
790 «}»
791 }
792 let foo = 1;"},
793 ],
794 );
795
796 assert(
797 indoc! {"
798 mod x {
799 mod y {
800
801 }ˇ
802 }
803 let foo = 1;"},
804 vec![
805 indoc! {"
806 mod x «{»
807 mod y {
808
809 }
810 «}»
811 let foo = 1;"},
812 indoc! {"
813 mod x {
814 mod y «{»
815
816 «}»
817 }
818 let foo = 1;"},
819 ],
820 );
821
822 assert(
823 indoc! {"
824 mod x {
825 mod y {
826
827 }
828 ˇ}
829 let foo = 1;"},
830 vec![indoc! {"
831 mod x «{»
832 mod y {
833
834 }
835 «}»
836 let foo = 1;"}],
837 );
838
839 assert(
840 indoc! {"
841 mod x {
842 mod y {
843
844 }
845 }
846 let fˇoo = 1;"},
847 vec![],
848 );
849
850 // Regression test: avoid crash when querying at the end of the buffer.
851 assert(
852 indoc! {"
853 mod x {
854 mod y {
855
856 }
857 }
858 let foo = 1;ˇ"},
859 vec![],
860 );
861}
862
863#[gpui::test]
864fn test_enclosing_bracket_ranges_where_brackets_are_not_outermost_children(cx: &mut AppContext) {
865 let mut assert = |selection_text, bracket_pair_texts| {
866 assert_bracket_pairs(selection_text, bracket_pair_texts, javascript_lang(), cx)
867 };
868
869 assert(
870 indoc! {"
871 for (const a in b)ˇ {
872 // a comment that's longer than the for-loop header
873 }"},
874 vec![indoc! {"
875 for «(»const a in b«)» {
876 // a comment that's longer than the for-loop header
877 }"}],
878 );
879
880 // Regression test: even though the parent node of the parentheses (the for loop) does
881 // intersect the given range, the parentheses themselves do not contain the range, so
882 // they should not be returned. Only the curly braces contain the range.
883 assert(
884 indoc! {"
885 for (const a in b) {ˇ
886 // a comment that's longer than the for-loop header
887 }"},
888 vec![indoc! {"
889 for (const a in b) «{»
890 // a comment that's longer than the for-loop header
891 «}»"}],
892 );
893}
894
895#[gpui::test]
896fn test_range_for_syntax_ancestor(cx: &mut AppContext) {
897 cx.new_model(|cx| {
898 let text = "fn a() { b(|c| {}) }";
899 let buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
900 .with_language(Arc::new(rust_lang()), cx);
901 let snapshot = buffer.snapshot();
902
903 assert_eq!(
904 snapshot.range_for_syntax_ancestor(empty_range_at(text, "|")),
905 Some(range_of(text, "|"))
906 );
907 assert_eq!(
908 snapshot.range_for_syntax_ancestor(range_of(text, "|")),
909 Some(range_of(text, "|c|"))
910 );
911 assert_eq!(
912 snapshot.range_for_syntax_ancestor(range_of(text, "|c|")),
913 Some(range_of(text, "|c| {}"))
914 );
915 assert_eq!(
916 snapshot.range_for_syntax_ancestor(range_of(text, "|c| {}")),
917 Some(range_of(text, "(|c| {})"))
918 );
919
920 buffer
921 });
922
923 fn empty_range_at(text: &str, part: &str) -> Range<usize> {
924 let start = text.find(part).unwrap();
925 start..start
926 }
927
928 fn range_of(text: &str, part: &str) -> Range<usize> {
929 let start = text.find(part).unwrap();
930 start..start + part.len()
931 }
932}
933
934#[gpui::test]
935fn test_autoindent_with_soft_tabs(cx: &mut AppContext) {
936 init_settings(cx, |_| {});
937
938 cx.new_model(|cx| {
939 let text = "fn a() {}";
940 let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
941 .with_language(Arc::new(rust_lang()), cx);
942
943 buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
944 assert_eq!(buffer.text(), "fn a() {\n \n}");
945
946 buffer.edit(
947 [(Point::new(1, 4)..Point::new(1, 4), "b()\n")],
948 Some(AutoindentMode::EachLine),
949 cx,
950 );
951 assert_eq!(buffer.text(), "fn a() {\n b()\n \n}");
952
953 // Create a field expression on a new line, causing that line
954 // to be indented.
955 buffer.edit(
956 [(Point::new(2, 4)..Point::new(2, 4), ".c")],
957 Some(AutoindentMode::EachLine),
958 cx,
959 );
960 assert_eq!(buffer.text(), "fn a() {\n b()\n .c\n}");
961
962 // Remove the dot so that the line is no longer a field expression,
963 // causing the line to be outdented.
964 buffer.edit(
965 [(Point::new(2, 8)..Point::new(2, 9), "")],
966 Some(AutoindentMode::EachLine),
967 cx,
968 );
969 assert_eq!(buffer.text(), "fn a() {\n b()\n c\n}");
970
971 buffer
972 });
973}
974
975#[gpui::test]
976fn test_autoindent_with_hard_tabs(cx: &mut AppContext) {
977 init_settings(cx, |settings| {
978 settings.defaults.hard_tabs = Some(true);
979 });
980
981 cx.new_model(|cx| {
982 let text = "fn a() {}";
983 let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
984 .with_language(Arc::new(rust_lang()), cx);
985
986 buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
987 assert_eq!(buffer.text(), "fn a() {\n\t\n}");
988
989 buffer.edit(
990 [(Point::new(1, 1)..Point::new(1, 1), "b()\n")],
991 Some(AutoindentMode::EachLine),
992 cx,
993 );
994 assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\n}");
995
996 // Create a field expression on a new line, causing that line
997 // to be indented.
998 buffer.edit(
999 [(Point::new(2, 1)..Point::new(2, 1), ".c")],
1000 Some(AutoindentMode::EachLine),
1001 cx,
1002 );
1003 assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\t.c\n}");
1004
1005 // Remove the dot so that the line is no longer a field expression,
1006 // causing the line to be outdented.
1007 buffer.edit(
1008 [(Point::new(2, 2)..Point::new(2, 3), "")],
1009 Some(AutoindentMode::EachLine),
1010 cx,
1011 );
1012 assert_eq!(buffer.text(), "fn a() {\n\tb()\n\tc\n}");
1013
1014 buffer
1015 });
1016}
1017
1018#[gpui::test]
1019fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut AppContext) {
1020 init_settings(cx, |_| {});
1021
1022 cx.new_model(|cx| {
1023 let mut buffer = Buffer::new(
1024 0,
1025 BufferId::new(cx.entity_id().as_u64()).unwrap(),
1026 "
1027 fn a() {
1028 c;
1029 d;
1030 }
1031 "
1032 .unindent(),
1033 )
1034 .with_language(Arc::new(rust_lang()), cx);
1035
1036 // Lines 2 and 3 don't match the indentation suggestion. When editing these lines,
1037 // their indentation is not adjusted.
1038 buffer.edit_via_marked_text(
1039 &"
1040 fn a() {
1041 c«()»;
1042 d«()»;
1043 }
1044 "
1045 .unindent(),
1046 Some(AutoindentMode::EachLine),
1047 cx,
1048 );
1049 assert_eq!(
1050 buffer.text(),
1051 "
1052 fn a() {
1053 c();
1054 d();
1055 }
1056 "
1057 .unindent()
1058 );
1059
1060 // When appending new content after these lines, the indentation is based on the
1061 // preceding lines' actual indentation.
1062 buffer.edit_via_marked_text(
1063 &"
1064 fn a() {
1065 c«
1066 .f
1067 .g()»;
1068 d«
1069 .f
1070 .g()»;
1071 }
1072 "
1073 .unindent(),
1074 Some(AutoindentMode::EachLine),
1075 cx,
1076 );
1077
1078 assert_eq!(
1079 buffer.text(),
1080 "
1081 fn a() {
1082 c
1083 .f
1084 .g();
1085 d
1086 .f
1087 .g();
1088 }
1089 "
1090 .unindent()
1091 );
1092 buffer
1093 });
1094
1095 cx.new_model(|cx| {
1096 eprintln!("second buffer: {:?}", cx.entity_id());
1097
1098 let mut buffer = Buffer::new(
1099 0,
1100 BufferId::new(cx.entity_id().as_u64()).unwrap(),
1101 "
1102 fn a() {
1103 b();
1104 |
1105 "
1106 .replace("|", "") // marker to preserve trailing whitespace
1107 .unindent(),
1108 )
1109 .with_language(Arc::new(rust_lang()), cx);
1110
1111 // Insert a closing brace. It is outdented.
1112 buffer.edit_via_marked_text(
1113 &"
1114 fn a() {
1115 b();
1116 «}»
1117 "
1118 .unindent(),
1119 Some(AutoindentMode::EachLine),
1120 cx,
1121 );
1122 assert_eq!(
1123 buffer.text(),
1124 "
1125 fn a() {
1126 b();
1127 }
1128 "
1129 .unindent()
1130 );
1131
1132 // Manually edit the leading whitespace. The edit is preserved.
1133 buffer.edit_via_marked_text(
1134 &"
1135 fn a() {
1136 b();
1137 « »}
1138 "
1139 .unindent(),
1140 Some(AutoindentMode::EachLine),
1141 cx,
1142 );
1143 assert_eq!(
1144 buffer.text(),
1145 "
1146 fn a() {
1147 b();
1148 }
1149 "
1150 .unindent()
1151 );
1152 buffer
1153 });
1154
1155 eprintln!("DONE");
1156}
1157
1158#[gpui::test]
1159fn test_autoindent_does_not_adjust_lines_within_newly_created_errors(cx: &mut AppContext) {
1160 init_settings(cx, |_| {});
1161
1162 cx.new_model(|cx| {
1163 let mut buffer = Buffer::new(
1164 0,
1165 BufferId::new(cx.entity_id().as_u64()).unwrap(),
1166 "
1167 fn a() {
1168 i
1169 }
1170 "
1171 .unindent(),
1172 )
1173 .with_language(Arc::new(rust_lang()), cx);
1174
1175 // Regression test: line does not get outdented due to syntax error
1176 buffer.edit_via_marked_text(
1177 &"
1178 fn a() {
1179 i«f let Some(x) = y»
1180 }
1181 "
1182 .unindent(),
1183 Some(AutoindentMode::EachLine),
1184 cx,
1185 );
1186 assert_eq!(
1187 buffer.text(),
1188 "
1189 fn a() {
1190 if let Some(x) = y
1191 }
1192 "
1193 .unindent()
1194 );
1195
1196 buffer.edit_via_marked_text(
1197 &"
1198 fn a() {
1199 if let Some(x) = y« {»
1200 }
1201 "
1202 .unindent(),
1203 Some(AutoindentMode::EachLine),
1204 cx,
1205 );
1206 assert_eq!(
1207 buffer.text(),
1208 "
1209 fn a() {
1210 if let Some(x) = y {
1211 }
1212 "
1213 .unindent()
1214 );
1215
1216 buffer
1217 });
1218}
1219
1220#[gpui::test]
1221fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut AppContext) {
1222 init_settings(cx, |_| {});
1223
1224 cx.new_model(|cx| {
1225 let mut buffer = Buffer::new(
1226 0,
1227 BufferId::new(cx.entity_id().as_u64()).unwrap(),
1228 "
1229 fn a() {}
1230 "
1231 .unindent(),
1232 )
1233 .with_language(Arc::new(rust_lang()), cx);
1234
1235 buffer.edit_via_marked_text(
1236 &"
1237 fn a(«
1238 b») {}
1239 "
1240 .unindent(),
1241 Some(AutoindentMode::EachLine),
1242 cx,
1243 );
1244 assert_eq!(
1245 buffer.text(),
1246 "
1247 fn a(
1248 b) {}
1249 "
1250 .unindent()
1251 );
1252
1253 // The indentation suggestion changed because `@end` node (a close paren)
1254 // is now at the beginning of the line.
1255 buffer.edit_via_marked_text(
1256 &"
1257 fn a(
1258 ˇ) {}
1259 "
1260 .unindent(),
1261 Some(AutoindentMode::EachLine),
1262 cx,
1263 );
1264 assert_eq!(
1265 buffer.text(),
1266 "
1267 fn a(
1268 ) {}
1269 "
1270 .unindent()
1271 );
1272
1273 buffer
1274 });
1275}
1276
1277#[gpui::test]
1278fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut AppContext) {
1279 init_settings(cx, |_| {});
1280
1281 cx.new_model(|cx| {
1282 let text = "a\nb";
1283 let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
1284 .with_language(Arc::new(rust_lang()), cx);
1285 buffer.edit(
1286 [(0..1, "\n"), (2..3, "\n")],
1287 Some(AutoindentMode::EachLine),
1288 cx,
1289 );
1290 assert_eq!(buffer.text(), "\n\n\n");
1291 buffer
1292 });
1293}
1294
1295#[gpui::test]
1296fn test_autoindent_multi_line_insertion(cx: &mut AppContext) {
1297 init_settings(cx, |_| {});
1298
1299 cx.new_model(|cx| {
1300 let text = "
1301 const a: usize = 1;
1302 fn b() {
1303 if c {
1304 let d = 2;
1305 }
1306 }
1307 "
1308 .unindent();
1309
1310 let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
1311 .with_language(Arc::new(rust_lang()), cx);
1312 buffer.edit(
1313 [(Point::new(3, 0)..Point::new(3, 0), "e(\n f()\n);\n")],
1314 Some(AutoindentMode::EachLine),
1315 cx,
1316 );
1317 assert_eq!(
1318 buffer.text(),
1319 "
1320 const a: usize = 1;
1321 fn b() {
1322 if c {
1323 e(
1324 f()
1325 );
1326 let d = 2;
1327 }
1328 }
1329 "
1330 .unindent()
1331 );
1332
1333 buffer
1334 });
1335}
1336
1337#[gpui::test]
1338fn test_autoindent_block_mode(cx: &mut AppContext) {
1339 init_settings(cx, |_| {});
1340
1341 cx.new_model(|cx| {
1342 let text = r#"
1343 fn a() {
1344 b();
1345 }
1346 "#
1347 .unindent();
1348 let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
1349 .with_language(Arc::new(rust_lang()), cx);
1350
1351 // When this text was copied, both of the quotation marks were at the same
1352 // indent level, but the indentation of the first line was not included in
1353 // the copied text. This information is retained in the
1354 // 'original_indent_columns' vector.
1355 let original_indent_columns = vec![4];
1356 let inserted_text = r#"
1357 "
1358 c
1359 d
1360 e
1361 "
1362 "#
1363 .unindent();
1364
1365 // Insert the block at column zero. The entire block is indented
1366 // so that the first line matches the previous line's indentation.
1367 buffer.edit(
1368 [(Point::new(2, 0)..Point::new(2, 0), inserted_text.clone())],
1369 Some(AutoindentMode::Block {
1370 original_indent_columns: original_indent_columns.clone(),
1371 }),
1372 cx,
1373 );
1374 assert_eq!(
1375 buffer.text(),
1376 r#"
1377 fn a() {
1378 b();
1379 "
1380 c
1381 d
1382 e
1383 "
1384 }
1385 "#
1386 .unindent()
1387 );
1388
1389 // Grouping is disabled in tests, so we need 2 undos
1390 buffer.undo(cx); // Undo the auto-indent
1391 buffer.undo(cx); // Undo the original edit
1392
1393 // Insert the block at a deeper indent level. The entire block is outdented.
1394 buffer.edit([(Point::new(2, 0)..Point::new(2, 0), " ")], None, cx);
1395 buffer.edit(
1396 [(Point::new(2, 8)..Point::new(2, 8), inserted_text)],
1397 Some(AutoindentMode::Block {
1398 original_indent_columns: original_indent_columns.clone(),
1399 }),
1400 cx,
1401 );
1402 assert_eq!(
1403 buffer.text(),
1404 r#"
1405 fn a() {
1406 b();
1407 "
1408 c
1409 d
1410 e
1411 "
1412 }
1413 "#
1414 .unindent()
1415 );
1416
1417 buffer
1418 });
1419}
1420
1421#[gpui::test]
1422fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut AppContext) {
1423 init_settings(cx, |_| {});
1424
1425 cx.new_model(|cx| {
1426 let text = r#"
1427 fn a() {
1428 if b() {
1429
1430 }
1431 }
1432 "#
1433 .unindent();
1434 let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
1435 .with_language(Arc::new(rust_lang()), cx);
1436
1437 // The original indent columns are not known, so this text is
1438 // auto-indented in a block as if the first line was copied in
1439 // its entirety.
1440 let original_indent_columns = Vec::new();
1441 let inserted_text = " c\n .d()\n .e();";
1442
1443 // Insert the block at column zero. The entire block is indented
1444 // so that the first line matches the previous line's indentation.
1445 buffer.edit(
1446 [(Point::new(2, 0)..Point::new(2, 0), inserted_text)],
1447 Some(AutoindentMode::Block {
1448 original_indent_columns: original_indent_columns.clone(),
1449 }),
1450 cx,
1451 );
1452 assert_eq!(
1453 buffer.text(),
1454 r#"
1455 fn a() {
1456 if b() {
1457 c
1458 .d()
1459 .e();
1460 }
1461 }
1462 "#
1463 .unindent()
1464 );
1465
1466 // Grouping is disabled in tests, so we need 2 undos
1467 buffer.undo(cx); // Undo the auto-indent
1468 buffer.undo(cx); // Undo the original edit
1469
1470 // Insert the block at a deeper indent level. The entire block is outdented.
1471 buffer.edit(
1472 [(Point::new(2, 0)..Point::new(2, 0), " ".repeat(12))],
1473 None,
1474 cx,
1475 );
1476 buffer.edit(
1477 [(Point::new(2, 12)..Point::new(2, 12), inserted_text)],
1478 Some(AutoindentMode::Block {
1479 original_indent_columns: Vec::new(),
1480 }),
1481 cx,
1482 );
1483 assert_eq!(
1484 buffer.text(),
1485 r#"
1486 fn a() {
1487 if b() {
1488 c
1489 .d()
1490 .e();
1491 }
1492 }
1493 "#
1494 .unindent()
1495 );
1496
1497 buffer
1498 });
1499}
1500
1501#[gpui::test]
1502fn test_autoindent_language_without_indents_query(cx: &mut AppContext) {
1503 init_settings(cx, |_| {});
1504
1505 cx.new_model(|cx| {
1506 let text = "
1507 * one
1508 - a
1509 - b
1510 * two
1511 "
1512 .unindent();
1513
1514 let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text)
1515 .with_language(
1516 Arc::new(Language::new(
1517 LanguageConfig {
1518 name: "Markdown".into(),
1519 auto_indent_using_last_non_empty_line: false,
1520 ..Default::default()
1521 },
1522 Some(tree_sitter_json::language()),
1523 )),
1524 cx,
1525 );
1526 buffer.edit(
1527 [(Point::new(3, 0)..Point::new(3, 0), "\n")],
1528 Some(AutoindentMode::EachLine),
1529 cx,
1530 );
1531 assert_eq!(
1532 buffer.text(),
1533 "
1534 * one
1535 - a
1536 - b
1537
1538 * two
1539 "
1540 .unindent()
1541 );
1542 buffer
1543 });
1544}
1545
1546#[gpui::test]
1547fn test_autoindent_with_injected_languages(cx: &mut AppContext) {
1548 init_settings(cx, |settings| {
1549 settings.languages.extend([
1550 (
1551 "HTML".into(),
1552 LanguageSettingsContent {
1553 tab_size: Some(2.try_into().unwrap()),
1554 ..Default::default()
1555 },
1556 ),
1557 (
1558 "JavaScript".into(),
1559 LanguageSettingsContent {
1560 tab_size: Some(8.try_into().unwrap()),
1561 ..Default::default()
1562 },
1563 ),
1564 ])
1565 });
1566
1567 let html_language = Arc::new(html_lang());
1568
1569 let javascript_language = Arc::new(javascript_lang());
1570
1571 let language_registry = Arc::new(LanguageRegistry::test());
1572 language_registry.add(html_language.clone());
1573 language_registry.add(javascript_language.clone());
1574
1575 cx.new_model(|cx| {
1576 let (text, ranges) = marked_text_ranges(
1577 &"
1578 <div>ˇ
1579 </div>
1580 <script>
1581 init({ˇ
1582 })
1583 </script>
1584 <span>ˇ
1585 </span>
1586 "
1587 .unindent(),
1588 false,
1589 );
1590
1591 let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text);
1592 buffer.set_language_registry(language_registry);
1593 buffer.set_language(Some(html_language), cx);
1594 buffer.edit(
1595 ranges.into_iter().map(|range| (range, "\na")),
1596 Some(AutoindentMode::EachLine),
1597 cx,
1598 );
1599 assert_eq!(
1600 buffer.text(),
1601 "
1602 <div>
1603 a
1604 </div>
1605 <script>
1606 init({
1607 a
1608 })
1609 </script>
1610 <span>
1611 a
1612 </span>
1613 "
1614 .unindent()
1615 );
1616 buffer
1617 });
1618}
1619
1620#[gpui::test]
1621fn test_autoindent_query_with_outdent_captures(cx: &mut AppContext) {
1622 init_settings(cx, |settings| {
1623 settings.defaults.tab_size = Some(2.try_into().unwrap());
1624 });
1625
1626 cx.new_model(|cx| {
1627 let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "")
1628 .with_language(Arc::new(ruby_lang()), cx);
1629
1630 let text = r#"
1631 class C
1632 def a(b, c)
1633 puts b
1634 puts c
1635 rescue
1636 puts "errored"
1637 exit 1
1638 end
1639 end
1640 "#
1641 .unindent();
1642
1643 buffer.edit([(0..0, text)], Some(AutoindentMode::EachLine), cx);
1644
1645 assert_eq!(
1646 buffer.text(),
1647 r#"
1648 class C
1649 def a(b, c)
1650 puts b
1651 puts c
1652 rescue
1653 puts "errored"
1654 exit 1
1655 end
1656 end
1657 "#
1658 .unindent()
1659 );
1660
1661 buffer
1662 });
1663}
1664
1665#[gpui::test]
1666fn test_language_scope_at_with_javascript(cx: &mut AppContext) {
1667 init_settings(cx, |_| {});
1668
1669 cx.new_model(|cx| {
1670 let language = Language::new(
1671 LanguageConfig {
1672 name: "JavaScript".into(),
1673 line_comments: vec!["// ".into()],
1674 brackets: BracketPairConfig {
1675 pairs: vec![
1676 BracketPair {
1677 start: "{".into(),
1678 end: "}".into(),
1679 close: true,
1680 newline: false,
1681 },
1682 BracketPair {
1683 start: "'".into(),
1684 end: "'".into(),
1685 close: true,
1686 newline: false,
1687 },
1688 ],
1689 disabled_scopes_by_bracket_ix: vec![
1690 Vec::new(), //
1691 vec!["string".into()],
1692 ],
1693 },
1694 overrides: [(
1695 "element".into(),
1696 LanguageConfigOverride {
1697 line_comments: Override::Remove { remove: true },
1698 block_comment: Override::Set(("{/*".into(), "*/}".into())),
1699 ..Default::default()
1700 },
1701 )]
1702 .into_iter()
1703 .collect(),
1704 ..Default::default()
1705 },
1706 Some(tree_sitter_typescript::language_tsx()),
1707 )
1708 .with_override_query(
1709 r#"
1710 (jsx_element) @element
1711 (string) @string
1712 [
1713 (jsx_opening_element)
1714 (jsx_closing_element)
1715 (jsx_expression)
1716 ] @default
1717 "#,
1718 )
1719 .unwrap();
1720
1721 let text = r#"
1722 a["b"] = <C d="e">
1723 <F></F>
1724 { g() }
1725 </C>;
1726 "#
1727 .unindent();
1728
1729 let buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), &text)
1730 .with_language(Arc::new(language), cx);
1731 let snapshot = buffer.snapshot();
1732
1733 let config = snapshot.language_scope_at(0).unwrap();
1734 assert_eq!(config.line_comment_prefixes().unwrap(), &[Arc::from("// ")]);
1735 // Both bracket pairs are enabled
1736 assert_eq!(
1737 config.brackets().map(|e| e.1).collect::<Vec<_>>(),
1738 &[true, true]
1739 );
1740
1741 let string_config = snapshot
1742 .language_scope_at(text.find("b\"").unwrap())
1743 .unwrap();
1744 assert_eq!(
1745 string_config.line_comment_prefixes().unwrap(),
1746 &[Arc::from("// ")]
1747 );
1748 // Second bracket pair is disabled
1749 assert_eq!(
1750 string_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
1751 &[true, false]
1752 );
1753
1754 // In between JSX tags: use the `element` override.
1755 let element_config = snapshot
1756 .language_scope_at(text.find("<F>").unwrap())
1757 .unwrap();
1758 assert_eq!(element_config.line_comment_prefixes(), None);
1759 assert_eq!(
1760 element_config.block_comment_delimiters(),
1761 Some((&"{/*".into(), &"*/}".into()))
1762 );
1763 assert_eq!(
1764 element_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
1765 &[true, true]
1766 );
1767
1768 // Within a JSX tag: use the default config.
1769 let tag_config = snapshot
1770 .language_scope_at(text.find(" d=").unwrap() + 1)
1771 .unwrap();
1772 assert_eq!(
1773 tag_config.line_comment_prefixes().unwrap(),
1774 &[Arc::from("// ")]
1775 );
1776 assert_eq!(
1777 tag_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
1778 &[true, true]
1779 );
1780
1781 // In a JSX expression: use the default config.
1782 let expression_in_element_config = snapshot
1783 .language_scope_at(text.find("{").unwrap() + 1)
1784 .unwrap();
1785 assert_eq!(
1786 expression_in_element_config
1787 .line_comment_prefixes()
1788 .unwrap(),
1789 &[Arc::from("// ")]
1790 );
1791 assert_eq!(
1792 expression_in_element_config
1793 .brackets()
1794 .map(|e| e.1)
1795 .collect::<Vec<_>>(),
1796 &[true, true]
1797 );
1798
1799 buffer
1800 });
1801}
1802
1803#[gpui::test]
1804fn test_language_scope_at_with_rust(cx: &mut AppContext) {
1805 init_settings(cx, |_| {});
1806
1807 cx.new_model(|cx| {
1808 let language = Language::new(
1809 LanguageConfig {
1810 name: "Rust".into(),
1811 brackets: BracketPairConfig {
1812 pairs: vec![
1813 BracketPair {
1814 start: "{".into(),
1815 end: "}".into(),
1816 close: true,
1817 newline: false,
1818 },
1819 BracketPair {
1820 start: "'".into(),
1821 end: "'".into(),
1822 close: true,
1823 newline: false,
1824 },
1825 ],
1826 disabled_scopes_by_bracket_ix: vec![
1827 Vec::new(), //
1828 vec!["string".into()],
1829 ],
1830 },
1831 ..Default::default()
1832 },
1833 Some(tree_sitter_rust::language()),
1834 )
1835 .with_override_query(
1836 r#"
1837 (string_literal) @string
1838 "#,
1839 )
1840 .unwrap();
1841
1842 let text = r#"
1843 const S: &'static str = "hello";
1844 "#
1845 .unindent();
1846
1847 let buffer = Buffer::new(
1848 0,
1849 BufferId::new(cx.entity_id().as_u64()).unwrap(),
1850 text.clone(),
1851 )
1852 .with_language(Arc::new(language), cx);
1853 let snapshot = buffer.snapshot();
1854
1855 // By default, all brackets are enabled
1856 let config = snapshot.language_scope_at(0).unwrap();
1857 assert_eq!(
1858 config.brackets().map(|e| e.1).collect::<Vec<_>>(),
1859 &[true, true]
1860 );
1861
1862 // Within a string, the quotation brackets are disabled.
1863 let string_config = snapshot
1864 .language_scope_at(text.find("ello").unwrap())
1865 .unwrap();
1866 assert_eq!(
1867 string_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
1868 &[true, false]
1869 );
1870
1871 buffer
1872 });
1873}
1874
1875#[gpui::test]
1876fn test_language_scope_at_with_combined_injections(cx: &mut AppContext) {
1877 init_settings(cx, |_| {});
1878
1879 cx.new_model(|cx| {
1880 let text = r#"
1881 <ol>
1882 <% people.each do |person| %>
1883 <li>
1884 <%= person.name %>
1885 </li>
1886 <% end %>
1887 </ol>
1888 "#
1889 .unindent();
1890
1891 let language_registry = Arc::new(LanguageRegistry::test());
1892 language_registry.add(Arc::new(ruby_lang()));
1893 language_registry.add(Arc::new(html_lang()));
1894 language_registry.add(Arc::new(erb_lang()));
1895
1896 let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), text);
1897 buffer.set_language_registry(language_registry.clone());
1898 buffer.set_language(
1899 language_registry
1900 .language_for_name("ERB")
1901 .now_or_never()
1902 .unwrap()
1903 .ok(),
1904 cx,
1905 );
1906
1907 let snapshot = buffer.snapshot();
1908 let html_config = snapshot.language_scope_at(Point::new(2, 4)).unwrap();
1909 assert_eq!(html_config.line_comment_prefixes(), Some(&vec![]));
1910 assert_eq!(
1911 html_config.block_comment_delimiters(),
1912 Some((&"<!--".into(), &"-->".into()))
1913 );
1914
1915 let ruby_config = snapshot.language_scope_at(Point::new(3, 12)).unwrap();
1916 assert_eq!(
1917 ruby_config.line_comment_prefixes().unwrap(),
1918 &[Arc::from("# ")]
1919 );
1920 assert_eq!(ruby_config.block_comment_delimiters(), None);
1921
1922 buffer
1923 });
1924}
1925
1926#[gpui::test]
1927fn test_serialization(cx: &mut gpui::AppContext) {
1928 let mut now = Instant::now();
1929
1930 let buffer1 = cx.new_model(|cx| {
1931 let mut buffer = Buffer::new(0, BufferId::new(cx.entity_id().as_u64()).unwrap(), "abc");
1932 buffer.edit([(3..3, "D")], None, cx);
1933
1934 now += Duration::from_secs(1);
1935 buffer.start_transaction_at(now);
1936 buffer.edit([(4..4, "E")], None, cx);
1937 buffer.end_transaction_at(now, cx);
1938 assert_eq!(buffer.text(), "abcDE");
1939
1940 buffer.undo(cx);
1941 assert_eq!(buffer.text(), "abcD");
1942
1943 buffer.edit([(4..4, "F")], None, cx);
1944 assert_eq!(buffer.text(), "abcDF");
1945 buffer
1946 });
1947 assert_eq!(buffer1.read(cx).text(), "abcDF");
1948
1949 let state = buffer1.read(cx).to_proto();
1950 let ops = cx
1951 .background_executor()
1952 .block(buffer1.read(cx).serialize_ops(None, cx));
1953 let buffer2 = cx.new_model(|cx| {
1954 let mut buffer = Buffer::from_proto(1, Capability::ReadWrite, state, None).unwrap();
1955 buffer
1956 .apply_ops(
1957 ops.into_iter()
1958 .map(|op| proto::deserialize_operation(op).unwrap()),
1959 cx,
1960 )
1961 .unwrap();
1962 buffer
1963 });
1964 assert_eq!(buffer2.read(cx).text(), "abcDF");
1965}
1966
1967#[gpui::test(iterations = 100)]
1968fn test_random_collaboration(cx: &mut AppContext, mut rng: StdRng) {
1969 let min_peers = env::var("MIN_PEERS")
1970 .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
1971 .unwrap_or(1);
1972 let max_peers = env::var("MAX_PEERS")
1973 .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
1974 .unwrap_or(5);
1975 let operations = env::var("OPERATIONS")
1976 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1977 .unwrap_or(10);
1978
1979 let base_text_len = rng.gen_range(0..10);
1980 let base_text = RandomCharIter::new(&mut rng)
1981 .take(base_text_len)
1982 .collect::<String>();
1983 let mut replica_ids = Vec::new();
1984 let mut buffers = Vec::new();
1985 let network = Arc::new(Mutex::new(Network::new(rng.clone())));
1986 let base_buffer = cx.new_model(|cx| {
1987 Buffer::new(
1988 0,
1989 BufferId::new(cx.entity_id().as_u64()).unwrap(),
1990 base_text.as_str(),
1991 )
1992 });
1993
1994 for i in 0..rng.gen_range(min_peers..=max_peers) {
1995 let buffer = cx.new_model(|cx| {
1996 let state = base_buffer.read(cx).to_proto();
1997 let ops = cx
1998 .background_executor()
1999 .block(base_buffer.read(cx).serialize_ops(None, cx));
2000 let mut buffer =
2001 Buffer::from_proto(i as ReplicaId, Capability::ReadWrite, state, None).unwrap();
2002 buffer
2003 .apply_ops(
2004 ops.into_iter()
2005 .map(|op| proto::deserialize_operation(op).unwrap()),
2006 cx,
2007 )
2008 .unwrap();
2009 buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
2010 let network = network.clone();
2011 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
2012 if let Event::Operation(op) = event {
2013 network
2014 .lock()
2015 .broadcast(buffer.replica_id(), vec![proto::serialize_operation(op)]);
2016 }
2017 })
2018 .detach();
2019 buffer
2020 });
2021
2022 buffers.push(buffer);
2023 replica_ids.push(i as ReplicaId);
2024 network.lock().add_peer(i as ReplicaId);
2025 log::info!("Adding initial peer with replica id {}", i);
2026 }
2027
2028 log::info!("initial text: {:?}", base_text);
2029
2030 let mut now = Instant::now();
2031 let mut mutation_count = operations;
2032 let mut next_diagnostic_id = 0;
2033 let mut active_selections = BTreeMap::default();
2034 loop {
2035 let replica_index = rng.gen_range(0..replica_ids.len());
2036 let replica_id = replica_ids[replica_index];
2037 let buffer = &mut buffers[replica_index];
2038 let mut new_buffer = None;
2039 match rng.gen_range(0..100) {
2040 0..=29 if mutation_count != 0 => {
2041 buffer.update(cx, |buffer, cx| {
2042 buffer.start_transaction_at(now);
2043 buffer.randomly_edit(&mut rng, 5, cx);
2044 buffer.end_transaction_at(now, cx);
2045 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
2046 });
2047 mutation_count -= 1;
2048 }
2049 30..=39 if mutation_count != 0 => {
2050 buffer.update(cx, |buffer, cx| {
2051 if rng.gen_bool(0.2) {
2052 log::info!("peer {} clearing active selections", replica_id);
2053 active_selections.remove(&replica_id);
2054 buffer.remove_active_selections(cx);
2055 } else {
2056 let mut selections = Vec::new();
2057 for id in 0..rng.gen_range(1..=5) {
2058 let range = buffer.random_byte_range(0, &mut rng);
2059 selections.push(Selection {
2060 id,
2061 start: buffer.anchor_before(range.start),
2062 end: buffer.anchor_before(range.end),
2063 reversed: false,
2064 goal: SelectionGoal::None,
2065 });
2066 }
2067 let selections: Arc<[Selection<Anchor>]> = selections.into();
2068 log::info!(
2069 "peer {} setting active selections: {:?}",
2070 replica_id,
2071 selections
2072 );
2073 active_selections.insert(replica_id, selections.clone());
2074 buffer.set_active_selections(selections, false, Default::default(), cx);
2075 }
2076 });
2077 mutation_count -= 1;
2078 }
2079 40..=49 if mutation_count != 0 && replica_id == 0 => {
2080 let entry_count = rng.gen_range(1..=5);
2081 buffer.update(cx, |buffer, cx| {
2082 let diagnostics = DiagnosticSet::new(
2083 (0..entry_count).map(|_| {
2084 let range = buffer.random_byte_range(0, &mut rng);
2085 let range = range.to_point_utf16(buffer);
2086 let range = range.start..range.end;
2087 DiagnosticEntry {
2088 range,
2089 diagnostic: Diagnostic {
2090 message: post_inc(&mut next_diagnostic_id).to_string(),
2091 ..Default::default()
2092 },
2093 }
2094 }),
2095 buffer,
2096 );
2097 log::info!("peer {} setting diagnostics: {:?}", replica_id, diagnostics);
2098 buffer.update_diagnostics(LanguageServerId(0), diagnostics, cx);
2099 });
2100 mutation_count -= 1;
2101 }
2102 50..=59 if replica_ids.len() < max_peers => {
2103 let old_buffer_state = buffer.read(cx).to_proto();
2104 let old_buffer_ops = cx
2105 .background_executor()
2106 .block(buffer.read(cx).serialize_ops(None, cx));
2107 let new_replica_id = (0..=replica_ids.len() as ReplicaId)
2108 .filter(|replica_id| *replica_id != buffer.read(cx).replica_id())
2109 .choose(&mut rng)
2110 .unwrap();
2111 log::info!(
2112 "Adding new replica {} (replicating from {})",
2113 new_replica_id,
2114 replica_id
2115 );
2116 new_buffer = Some(cx.new_model(|cx| {
2117 let mut new_buffer = Buffer::from_proto(
2118 new_replica_id,
2119 Capability::ReadWrite,
2120 old_buffer_state,
2121 None,
2122 )
2123 .unwrap();
2124 new_buffer
2125 .apply_ops(
2126 old_buffer_ops
2127 .into_iter()
2128 .map(|op| deserialize_operation(op).unwrap()),
2129 cx,
2130 )
2131 .unwrap();
2132 log::info!(
2133 "New replica {} text: {:?}",
2134 new_buffer.replica_id(),
2135 new_buffer.text()
2136 );
2137 new_buffer.set_group_interval(Duration::from_millis(rng.gen_range(0..=200)));
2138 let network = network.clone();
2139 cx.subscribe(&cx.handle(), move |buffer, _, event, _| {
2140 if let Event::Operation(op) = event {
2141 network.lock().broadcast(
2142 buffer.replica_id(),
2143 vec![proto::serialize_operation(op)],
2144 );
2145 }
2146 })
2147 .detach();
2148 new_buffer
2149 }));
2150 network.lock().replicate(replica_id, new_replica_id);
2151
2152 if new_replica_id as usize == replica_ids.len() {
2153 replica_ids.push(new_replica_id);
2154 } else {
2155 let new_buffer = new_buffer.take().unwrap();
2156 while network.lock().has_unreceived(new_replica_id) {
2157 let ops = network
2158 .lock()
2159 .receive(new_replica_id)
2160 .into_iter()
2161 .map(|op| proto::deserialize_operation(op).unwrap());
2162 if ops.len() > 0 {
2163 log::info!(
2164 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
2165 new_replica_id,
2166 buffer.read(cx).version(),
2167 ops.len(),
2168 ops
2169 );
2170 new_buffer.update(cx, |new_buffer, cx| {
2171 new_buffer.apply_ops(ops, cx).unwrap();
2172 });
2173 }
2174 }
2175 buffers[new_replica_id as usize] = new_buffer;
2176 }
2177 }
2178 60..=69 if mutation_count != 0 => {
2179 buffer.update(cx, |buffer, cx| {
2180 buffer.randomly_undo_redo(&mut rng, cx);
2181 log::info!("buffer {} text: {:?}", buffer.replica_id(), buffer.text());
2182 });
2183 mutation_count -= 1;
2184 }
2185 _ if network.lock().has_unreceived(replica_id) => {
2186 let ops = network
2187 .lock()
2188 .receive(replica_id)
2189 .into_iter()
2190 .map(|op| proto::deserialize_operation(op).unwrap());
2191 if ops.len() > 0 {
2192 log::info!(
2193 "peer {} (version: {:?}) applying {} ops from the network. {:?}",
2194 replica_id,
2195 buffer.read(cx).version(),
2196 ops.len(),
2197 ops
2198 );
2199 buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx).unwrap());
2200 }
2201 }
2202 _ => {}
2203 }
2204
2205 now += Duration::from_millis(rng.gen_range(0..=200));
2206 buffers.extend(new_buffer);
2207
2208 for buffer in &buffers {
2209 buffer.read(cx).check_invariants();
2210 }
2211
2212 if mutation_count == 0 && network.lock().is_idle() {
2213 break;
2214 }
2215 }
2216
2217 let first_buffer = buffers[0].read(cx).snapshot();
2218 for buffer in &buffers[1..] {
2219 let buffer = buffer.read(cx).snapshot();
2220 assert_eq!(
2221 buffer.version(),
2222 first_buffer.version(),
2223 "Replica {} version != Replica 0 version",
2224 buffer.replica_id()
2225 );
2226 assert_eq!(
2227 buffer.text(),
2228 first_buffer.text(),
2229 "Replica {} text != Replica 0 text",
2230 buffer.replica_id()
2231 );
2232 assert_eq!(
2233 buffer
2234 .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
2235 .collect::<Vec<_>>(),
2236 first_buffer
2237 .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
2238 .collect::<Vec<_>>(),
2239 "Replica {} diagnostics != Replica 0 diagnostics",
2240 buffer.replica_id()
2241 );
2242 }
2243
2244 for buffer in &buffers {
2245 let buffer = buffer.read(cx).snapshot();
2246 let actual_remote_selections = buffer
2247 .remote_selections_in_range(Anchor::MIN..Anchor::MAX)
2248 .map(|(replica_id, _, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
2249 .collect::<Vec<_>>();
2250 let expected_remote_selections = active_selections
2251 .iter()
2252 .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
2253 .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
2254 .collect::<Vec<_>>();
2255 assert_eq!(
2256 actual_remote_selections,
2257 expected_remote_selections,
2258 "Replica {} remote selections != expected selections",
2259 buffer.replica_id()
2260 );
2261 }
2262}
2263
2264#[test]
2265fn test_contiguous_ranges() {
2266 assert_eq!(
2267 contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
2268 &[1..4, 5..7, 9..13]
2269 );
2270
2271 // Respects the `max_len` parameter
2272 assert_eq!(
2273 contiguous_ranges(
2274 [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
2275 3
2276 )
2277 .collect::<Vec<_>>(),
2278 &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
2279 );
2280}
2281
2282#[gpui::test(iterations = 500)]
2283fn test_trailing_whitespace_ranges(mut rng: StdRng) {
2284 // Generate a random multi-line string containing
2285 // some lines with trailing whitespace.
2286 let mut text = String::new();
2287 for _ in 0..rng.gen_range(0..16) {
2288 for _ in 0..rng.gen_range(0..36) {
2289 text.push(match rng.gen_range(0..10) {
2290 0..=1 => ' ',
2291 3 => '\t',
2292 _ => rng.gen_range('a'..'z'),
2293 });
2294 }
2295 text.push('\n');
2296 }
2297
2298 match rng.gen_range(0..10) {
2299 // sometimes remove the last newline
2300 0..=1 => drop(text.pop()), //
2301
2302 // sometimes add extra newlines
2303 2..=3 => text.push_str(&"\n".repeat(rng.gen_range(1..5))),
2304 _ => {}
2305 }
2306
2307 let rope = Rope::from(text.as_str());
2308 let actual_ranges = trailing_whitespace_ranges(&rope);
2309 let expected_ranges = TRAILING_WHITESPACE_REGEX
2310 .find_iter(&text)
2311 .map(|m| m.range())
2312 .collect::<Vec<_>>();
2313 assert_eq!(
2314 actual_ranges,
2315 expected_ranges,
2316 "wrong ranges for text lines:\n{:?}",
2317 text.split("\n").collect::<Vec<_>>()
2318 );
2319}
2320
2321fn ruby_lang() -> Language {
2322 Language::new(
2323 LanguageConfig {
2324 name: "Ruby".into(),
2325 path_suffixes: vec!["rb".to_string()],
2326 line_comments: vec!["# ".into()],
2327 ..Default::default()
2328 },
2329 Some(tree_sitter_ruby::language()),
2330 )
2331 .with_indents_query(
2332 r#"
2333 (class "end" @end) @indent
2334 (method "end" @end) @indent
2335 (rescue) @outdent
2336 (then) @indent
2337 "#,
2338 )
2339 .unwrap()
2340}
2341
2342fn html_lang() -> Language {
2343 Language::new(
2344 LanguageConfig {
2345 name: "HTML".into(),
2346 block_comment: Some(("<!--".into(), "-->".into())),
2347 ..Default::default()
2348 },
2349 Some(tree_sitter_html::language()),
2350 )
2351 .with_indents_query(
2352 "
2353 (element
2354 (start_tag) @start
2355 (end_tag)? @end) @indent
2356 ",
2357 )
2358 .unwrap()
2359 .with_injection_query(
2360 r#"
2361 (script_element
2362 (raw_text) @content
2363 (#set! "language" "javascript"))
2364 "#,
2365 )
2366 .unwrap()
2367}
2368
2369fn erb_lang() -> Language {
2370 Language::new(
2371 LanguageConfig {
2372 name: "ERB".into(),
2373 path_suffixes: vec!["erb".to_string()],
2374 block_comment: Some(("<%#".into(), "%>".into())),
2375 ..Default::default()
2376 },
2377 Some(tree_sitter_embedded_template::language()),
2378 )
2379 .with_injection_query(
2380 r#"
2381 (
2382 (code) @content
2383 (#set! "language" "ruby")
2384 (#set! "combined")
2385 )
2386
2387 (
2388 (content) @content
2389 (#set! "language" "html")
2390 (#set! "combined")
2391 )
2392 "#,
2393 )
2394 .unwrap()
2395}
2396
2397fn rust_lang() -> Language {
2398 Language::new(
2399 LanguageConfig {
2400 name: "Rust".into(),
2401 path_suffixes: vec!["rs".to_string()],
2402 ..Default::default()
2403 },
2404 Some(tree_sitter_rust::language()),
2405 )
2406 .with_indents_query(
2407 r#"
2408 (call_expression) @indent
2409 (field_expression) @indent
2410 (_ "(" ")" @end) @indent
2411 (_ "{" "}" @end) @indent
2412 "#,
2413 )
2414 .unwrap()
2415 .with_brackets_query(
2416 r#"
2417 ("{" @open "}" @close)
2418 "#,
2419 )
2420 .unwrap()
2421 .with_outline_query(
2422 r#"
2423 (struct_item
2424 "struct" @context
2425 name: (_) @name) @item
2426 (enum_item
2427 "enum" @context
2428 name: (_) @name) @item
2429 (enum_variant
2430 name: (_) @name) @item
2431 (field_declaration
2432 name: (_) @name) @item
2433 (impl_item
2434 "impl" @context
2435 trait: (_)? @name
2436 "for"? @context
2437 type: (_) @name) @item
2438 (function_item
2439 "fn" @context
2440 name: (_) @name) @item
2441 (mod_item
2442 "mod" @context
2443 name: (_) @name) @item
2444 "#,
2445 )
2446 .unwrap()
2447}
2448
2449fn json_lang() -> Language {
2450 Language::new(
2451 LanguageConfig {
2452 name: "Json".into(),
2453 path_suffixes: vec!["js".to_string()],
2454 ..Default::default()
2455 },
2456 Some(tree_sitter_json::language()),
2457 )
2458}
2459
2460fn javascript_lang() -> Language {
2461 Language::new(
2462 LanguageConfig {
2463 name: "JavaScript".into(),
2464 ..Default::default()
2465 },
2466 Some(tree_sitter_typescript::language_tsx()),
2467 )
2468 .with_brackets_query(
2469 r#"
2470 ("{" @open "}" @close)
2471 ("(" @open ")" @close)
2472 "#,
2473 )
2474 .unwrap()
2475 .with_indents_query(
2476 r#"
2477 (object "}" @end) @indent
2478 "#,
2479 )
2480 .unwrap()
2481}
2482
2483fn get_tree_sexp(buffer: &Model<Buffer>, cx: &mut gpui::TestAppContext) -> String {
2484 buffer.update(cx, |buffer, _| {
2485 let snapshot = buffer.snapshot();
2486 let layers = snapshot.syntax.layers(buffer.as_text_snapshot());
2487 layers[0].node().to_sexp()
2488 })
2489}
2490
2491// Assert that the enclosing bracket ranges around the selection match the pairs indicated by the marked text in `range_markers`
2492fn assert_bracket_pairs(
2493 selection_text: &'static str,
2494 bracket_pair_texts: Vec<&'static str>,
2495 language: Language,
2496 cx: &mut AppContext,
2497) {
2498 let (expected_text, selection_ranges) = marked_text_ranges(selection_text, false);
2499 let buffer = cx.new_model(|cx| {
2500 Buffer::new(
2501 0,
2502 BufferId::new(cx.entity_id().as_u64()).unwrap(),
2503 expected_text.clone(),
2504 )
2505 .with_language(Arc::new(language), cx)
2506 });
2507 let buffer = buffer.update(cx, |buffer, _cx| buffer.snapshot());
2508
2509 let selection_range = selection_ranges[0].clone();
2510
2511 let bracket_pairs = bracket_pair_texts
2512 .into_iter()
2513 .map(|pair_text| {
2514 let (bracket_text, ranges) = marked_text_ranges(pair_text, false);
2515 assert_eq!(bracket_text, expected_text);
2516 (ranges[0].clone(), ranges[1].clone())
2517 })
2518 .collect::<Vec<_>>();
2519
2520 assert_set_eq!(
2521 buffer.bracket_ranges(selection_range).collect::<Vec<_>>(),
2522 bracket_pairs
2523 );
2524}
2525
2526fn init_settings(cx: &mut AppContext, f: fn(&mut AllLanguageSettingsContent)) {
2527 let settings_store = SettingsStore::test(cx);
2528 cx.set_global(settings_store);
2529 crate::init(cx);
2530 cx.update_global::<SettingsStore, _>(|settings, cx| {
2531 settings.update_user_settings::<AllLanguageSettings>(cx, f);
2532 });
2533}