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