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