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