Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T02:52:53.816Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

buffer_tests.rs

4941 lines · 156.0 KB · rust
1use super::*;
2use crate::Buffer;
3use clock::ReplicaId;
4use collections::BTreeMap;
5use futures::FutureExt as _;
6use futures_lite::future::yield_now;
7use gpui::{App, AppContext as _, BorrowAppContext, Entity};
8use gpui::{HighlightStyle, TestAppContext};
9use indoc::indoc;
10use pretty_assertions::assert_eq;
11use proto::deserialize_operation;
12use rand::prelude::*;
13use regex::RegexBuilder;
14use settings::SettingsStore;
15use settings::{AllLanguageSettingsContent, LanguageSettingsContent};
16use std::collections::BTreeSet;
17use std::{
18    env,
19    ops::Range,
20    sync::LazyLock,
21    time::{Duration, Instant},
22};
23use syntax_map::{MAX_BYTES_TO_QUERY, TreeSitterOptions};
24use text::network::Network;
25use text::{BufferId, LineEnding};
26use text::{Point, ToPoint};
27use theme::ActiveTheme;
28use unindent::Unindent as _;
29use util::rel_path::rel_path;
30use util::test::marked_text_offsets;
31use util::{RandomCharIter, assert_set_eq, post_inc, test::marked_text_ranges};
32
33pub static TRAILING_WHITESPACE_REGEX: LazyLock<regex::Regex> = LazyLock::new(|| {
34    RegexBuilder::new(r"[ \t]+$")
35        .multi_line(true)
36        .build()
37        .expect("Failed to create TRAILING_WHITESPACE_REGEX")
38});
39
40#[cfg(test)]
41#[ctor::ctor(unsafe)]
42fn init_logger() {
43    zlog::init_test();
44}
45
46#[gpui::test]
47fn test_line_endings(cx: &mut gpui::App) {
48    init_settings(cx, |_| {});
49
50    cx.new(|cx| {
51        let mut buffer = Buffer::local("one\r\ntwo\rthree", cx).with_language(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_set_line_ending(cx: &mut TestAppContext) {
72    let base = cx.new(|cx| Buffer::local("one\ntwo\nthree\n", cx));
73    let base_replica = cx.new(|cx| {
74        Buffer::from_proto(
75            ReplicaId::new(1),
76            Capability::ReadWrite,
77            base.read(cx).to_proto(cx),
78            None,
79        )
80        .unwrap()
81    });
82    base.update(cx, |_buffer, cx| {
83        cx.subscribe(&base_replica, |this, _, event, cx| {
84            if let BufferEvent::Operation {
85                operation,
86                is_local: true,
87            } = event
88            {
89                this.apply_ops([operation.clone()], cx);
90            }
91        })
92        .detach();
93    });
94    base_replica.update(cx, |_buffer, cx| {
95        cx.subscribe(&base, |this, _, event, cx| {
96            if let BufferEvent::Operation {
97                operation,
98                is_local: true,
99            } = event
100            {
101                this.apply_ops([operation.clone()], cx);
102            }
103        })
104        .detach();
105    });
106
107    // Base
108    base_replica.read_with(cx, |buffer, _| {
109        assert_eq!(buffer.line_ending(), LineEnding::Unix);
110    });
111    base.update(cx, |buffer, cx| {
112        assert_eq!(buffer.line_ending(), LineEnding::Unix);
113        buffer.set_line_ending(LineEnding::Windows, cx);
114        assert_eq!(buffer.line_ending(), LineEnding::Windows);
115    });
116    base_replica.read_with(cx, |buffer, _| {
117        assert_eq!(buffer.line_ending(), LineEnding::Windows);
118    });
119    base.update(cx, |buffer, cx| {
120        buffer.set_line_ending(LineEnding::Unix, cx);
121        assert_eq!(buffer.line_ending(), LineEnding::Unix);
122    });
123    base_replica.read_with(cx, |buffer, _| {
124        assert_eq!(buffer.line_ending(), LineEnding::Unix);
125    });
126
127    // Replica
128    base.read_with(cx, |buffer, _| {
129        assert_eq!(buffer.line_ending(), LineEnding::Unix);
130    });
131    base_replica.update(cx, |buffer, cx| {
132        assert_eq!(buffer.line_ending(), LineEnding::Unix);
133        buffer.set_line_ending(LineEnding::Windows, cx);
134        assert_eq!(buffer.line_ending(), LineEnding::Windows);
135    });
136    base.read_with(cx, |buffer, _| {
137        assert_eq!(buffer.line_ending(), LineEnding::Windows);
138    });
139    base_replica.update(cx, |buffer, cx| {
140        buffer.set_line_ending(LineEnding::Unix, cx);
141        assert_eq!(buffer.line_ending(), LineEnding::Unix);
142    });
143    base.read_with(cx, |buffer, _| {
144        assert_eq!(buffer.line_ending(), LineEnding::Unix);
145    });
146}
147
148#[gpui::test]
149fn test_select_language(cx: &mut App) {
150    init_settings(cx, |_| {});
151
152    let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
153    registry.add(Arc::new(Language::new(
154        LanguageConfig {
155            name: LanguageName::new_static("Rust"),
156            matcher: (LanguageMatcher {
157                path_suffixes: vec!["rs".to_string()],
158                ..Default::default()
159            })
160            .into(),
161            ..Default::default()
162        },
163        Some(tree_sitter_rust::LANGUAGE.into()),
164    )));
165    registry.add(Arc::new(Language::new(
166        LanguageConfig {
167            name: "Rust with longer extension".into(),
168            matcher: (LanguageMatcher {
169                path_suffixes: vec!["longer.rs".to_string()],
170                ..Default::default()
171            })
172            .into(),
173            ..Default::default()
174        },
175        Some(tree_sitter_rust::LANGUAGE.into()),
176    )));
177    registry.add(Arc::new(Language::new(
178        LanguageConfig {
179            name: LanguageName::new_static("Make"),
180            matcher: (LanguageMatcher {
181                path_suffixes: vec!["Makefile".to_string(), "mk".to_string()],
182                ..Default::default()
183            })
184            .into(),
185            ..Default::default()
186        },
187        Some(tree_sitter_rust::LANGUAGE.into()),
188    )));
189
190    // matching file extension
191    assert_eq!(
192        registry
193            .language_for_file(&file("src/lib.rs"), None, cx)
194            .and_then(|id| registry.language_name_for_id(id)),
195        Some("Rust".into())
196    );
197    assert_eq!(
198        registry
199            .language_for_file(&file("src/lib.mk"), None, cx)
200            .and_then(|id| registry.language_name_for_id(id)),
201        Some("Make".into())
202    );
203
204    // matching longer, compound extension, part of which could also match another lang
205    assert_eq!(
206        registry
207            .language_for_file(&file("src/lib.longer.rs"), None, cx)
208            .and_then(|id| registry.language_name_for_id(id)),
209        Some("Rust with longer extension".into())
210    );
211
212    // matching filename
213    assert_eq!(
214        registry
215            .language_for_file(&file("src/Makefile"), None, cx)
216            .and_then(|id| registry.language_name_for_id(id)),
217        Some("Make".into())
218    );
219
220    // matching suffix that is not the full file extension or filename
221    assert_eq!(
222        registry
223            .language_for_file(&file("zed/cars"), None, cx)
224            .and_then(|id| registry.language_name_for_id(id)),
225        None
226    );
227    assert_eq!(
228        registry
229            .language_for_file(&file("zed/a.cars"), None, cx)
230            .and_then(|id| registry.language_name_for_id(id)),
231        None
232    );
233    assert_eq!(
234        registry
235            .language_for_file(&file("zed/sumk"), None, cx)
236            .and_then(|id| registry.language_name_for_id(id)),
237        None
238    );
239}
240
241#[gpui::test(iterations = 10)]
242async fn test_first_line_pattern(cx: &mut TestAppContext) {
243    cx.update(|cx| init_settings(cx, |_| {}));
244
245    let languages = LanguageRegistry::test(cx.executor());
246    let languages = Arc::new(languages);
247
248    languages.register_test_language(LanguageConfig {
249        name: "JavaScript".into(),
250        matcher: (LanguageMatcher {
251            path_suffixes: vec!["js".into()],
252            first_line_pattern: Some(Regex::new(r"\bnode\b").unwrap()),
253            ..LanguageMatcher::default()
254        })
255        .into(),
256        ..Default::default()
257    });
258
259    assert!(
260        cx.read(|cx| languages.language_for_file(&file("the/script"), None, cx))
261            .is_none()
262    );
263    assert!(
264        cx.read(|cx| languages.language_for_file(&file("the/script"), Some(&"nothing".into()), cx))
265            .is_none()
266    );
267
268    assert_eq!(
269        cx.read(|cx| languages.language_for_file(
270            &file("the/script"),
271            Some(&"#!/bin/env node".into()),
272            cx
273        ))
274        .and_then(|id| languages.language_name_for_id(id))
275        .unwrap(),
276        "JavaScript"
277    );
278}
279
280#[gpui::test]
281async fn test_language_for_file_with_custom_file_types(cx: &mut TestAppContext) {
282    cx.update(|cx| {
283        init_settings(cx, |settings| {
284            settings.file_types.get_or_insert_default().0.extend([
285                ("TypeScript".into(), vec!["js".into()].into()),
286                (
287                    "JavaScript".into(),
288                    vec!["*longer.ts".into(), "ecmascript".into()].into(),
289                ),
290                ("C++".into(), vec!["c".into(), "*.dev".into()].into()),
291                (
292                    "Dockerfile".into(),
293                    vec!["Dockerfile".into(), "Dockerfile.*".into()].into(),
294                ),
295            ]);
296        })
297    });
298
299    let languages = Arc::new(LanguageRegistry::test(cx.executor()));
300    let language_name = |id| languages.language_name_for_id(id).unwrap();
301
302    for config in [
303        LanguageConfig {
304            name: "JavaScript".into(),
305            matcher: (LanguageMatcher {
306                path_suffixes: vec!["js".to_string()],
307                ..Default::default()
308            })
309            .into(),
310            ..Default::default()
311        },
312        LanguageConfig {
313            name: "TypeScript".into(),
314            matcher: (LanguageMatcher {
315                path_suffixes: vec!["ts".to_string(), "ts.ecmascript".to_string()],
316                ..Default::default()
317            })
318            .into(),
319            ..Default::default()
320        },
321        LanguageConfig {
322            name: "C++".into(),
323            matcher: (LanguageMatcher {
324                path_suffixes: vec!["cpp".to_string()],
325                ..Default::default()
326            })
327            .into(),
328            ..Default::default()
329        },
330        LanguageConfig {
331            name: "C".into(),
332            matcher: (LanguageMatcher {
333                path_suffixes: vec!["c".to_string()],
334                ..Default::default()
335            })
336            .into(),
337            ..Default::default()
338        },
339        LanguageConfig {
340            name: "Dockerfile".into(),
341            matcher: (LanguageMatcher {
342                path_suffixes: vec!["Dockerfile".to_string()],
343                ..Default::default()
344            })
345            .into(),
346            ..Default::default()
347        },
348    ] {
349        languages.add(Arc::new(Language::new(config, None)));
350    }
351
352    // matches system-provided lang extension
353    let language = cx
354        .read(|cx| languages.language_for_file(&file("foo.ts"), None, cx))
355        .unwrap();
356    assert_eq!(language_name(language), "TypeScript");
357    let language = cx
358        .read(|cx| languages.language_for_file(&file("foo.ts.ecmascript"), None, cx))
359        .unwrap();
360    assert_eq!(language_name(language), "TypeScript");
361    let language = cx
362        .read(|cx| languages.language_for_file(&file("foo.cpp"), None, cx))
363        .unwrap();
364    assert_eq!(language_name(language), "C++");
365
366    // user configured lang extension, same length as system-provided
367    let language = cx
368        .read(|cx| languages.language_for_file(&file("foo.js"), None, cx))
369        .unwrap();
370    assert_eq!(language_name(language), "TypeScript");
371    let language = cx
372        .read(|cx| languages.language_for_file(&file("foo.c"), None, cx))
373        .unwrap();
374    assert_eq!(language_name(language), "C++");
375
376    // user configured lang extension, longer than system-provided
377    let language = cx
378        .read(|cx| languages.language_for_file(&file("foo.longer.ts"), None, cx))
379        .unwrap();
380    assert_eq!(language_name(language), "JavaScript");
381
382    // user configured lang extension, shorter than system-provided
383    let language = cx
384        .read(|cx| languages.language_for_file(&file("foo.ecmascript"), None, cx))
385        .unwrap();
386    assert_eq!(language_name(language), "JavaScript");
387
388    // user configured glob matches
389    let language = cx
390        .read(|cx| languages.language_for_file(&file("c-plus-plus.dev"), None, cx))
391        .unwrap();
392    assert_eq!(language_name(language), "C++");
393    // should match Dockerfile.* => Dockerfile, not *.dev => C++
394    let language = cx
395        .read(|cx| languages.language_for_file(&file("Dockerfile.dev"), None, cx))
396        .unwrap();
397    assert_eq!(language_name(language), "Dockerfile");
398}
399
400fn file(path: &str) -> Arc<dyn File> {
401    Arc::new(TestFile {
402        path: Arc::from(rel_path(path)),
403        root_name: "zed".into(),
404        local_root: None,
405    })
406}
407
408#[gpui::test]
409fn test_edit_events(cx: &mut gpui::App) {
410    let mut now = Instant::now();
411    let buffer_1_events = Arc::new(Mutex::new(Vec::new()));
412    let buffer_2_events = Arc::new(Mutex::new(Vec::new()));
413
414    let buffer1 = cx.new(|cx| Buffer::local("abcdef", cx));
415    let buffer2 = cx.new(|cx| {
416        Buffer::remote(
417            BufferId::from(cx.entity_id().as_non_zero_u64()),
418            ReplicaId::new(1),
419            Capability::ReadWrite,
420            "abcdef",
421        )
422    });
423    let buffer1_ops = Arc::new(Mutex::new(Vec::new()));
424    buffer1.update(cx, {
425        let buffer1_ops = buffer1_ops.clone();
426        |buffer, cx| {
427            let buffer_1_events = buffer_1_events.clone();
428            cx.subscribe(&buffer1, move |_, _, event, _| match event.clone() {
429                BufferEvent::Operation {
430                    operation,
431                    is_local: true,
432                } => buffer1_ops.lock().push(operation),
433                event => buffer_1_events.lock().push(event),
434            })
435            .detach();
436            let buffer_2_events = buffer_2_events.clone();
437            cx.subscribe(&buffer2, move |_, _, event, _| match event.clone() {
438                BufferEvent::Operation {
439                    is_local: false, ..
440                } => {}
441                event => buffer_2_events.lock().push(event),
442            })
443            .detach();
444
445            // An edit emits an edited event, followed by a dirty changed event,
446            // since the buffer was previously in a clean state.
447            buffer.edit([(2..4, "XYZ")], None, cx);
448
449            // An empty transaction does not emit any events.
450            buffer.start_transaction();
451            buffer.end_transaction(cx);
452
453            // A transaction containing two edits emits one edited event.
454            now += Duration::from_secs(1);
455            buffer.start_transaction_at(now);
456            buffer.edit([(5..5, "u")], None, cx);
457            buffer.edit([(6..6, "w")], None, cx);
458            buffer.end_transaction_at(now, cx);
459
460            // Undoing a transaction emits one edited event.
461            buffer.undo(cx);
462        }
463    });
464
465    // Incorporating a set of remote ops emits a single edited event,
466    // followed by a dirty changed event.
467    buffer2.update(cx, |buffer, cx| {
468        buffer.apply_ops(buffer1_ops.lock().drain(..), cx);
469    });
470    assert_eq!(
471        mem::take(&mut *buffer_1_events.lock()),
472        vec![
473            BufferEvent::Edited {
474                source: BufferEditSource::User
475            },
476            BufferEvent::DirtyChanged,
477            BufferEvent::Edited {
478                source: BufferEditSource::User
479            },
480            BufferEvent::Edited {
481                source: BufferEditSource::User
482            },
483        ]
484    );
485    assert_eq!(
486        mem::take(&mut *buffer_2_events.lock()),
487        vec![
488            BufferEvent::Edited {
489                source: BufferEditSource::Remote
490            },
491            BufferEvent::DirtyChanged
492        ]
493    );
494
495    buffer1.update(cx, |buffer, cx| {
496        // Undoing the first transaction emits edited event, followed by a
497        // dirty changed event, since the buffer is again in a clean state.
498        buffer.undo(cx);
499    });
500    // Incorporating the remote ops again emits a single edited event,
501    // followed by a dirty changed event.
502    buffer2.update(cx, |buffer, cx| {
503        buffer.apply_ops(buffer1_ops.lock().drain(..), cx);
504    });
505    assert_eq!(
506        mem::take(&mut *buffer_1_events.lock()),
507        vec![
508            BufferEvent::Edited {
509                source: BufferEditSource::User
510            },
511            BufferEvent::DirtyChanged,
512        ]
513    );
514    assert_eq!(
515        mem::take(&mut *buffer_2_events.lock()),
516        vec![
517            BufferEvent::Edited {
518                source: BufferEditSource::Remote
519            },
520            BufferEvent::DirtyChanged
521        ]
522    );
523}
524
525#[gpui::test]
526async fn test_apply_diff(cx: &mut TestAppContext) {
527    let (text, offsets) = marked_text_offsets(
528        "one two three\nfour fiˇve six\nseven eightˇ nine\nten eleven twelve\n",
529    );
530    let buffer = cx.new(|cx| Buffer::local(text, cx));
531    let anchors = buffer.update(cx, |buffer, _| {
532        offsets
533            .iter()
534            .map(|offset| buffer.anchor_before(offset))
535            .collect::<Vec<_>>()
536    });
537
538    let (text, offsets) = marked_text_offsets(
539        "one two three\n{\nfour FIVEˇ six\n}\nseven AND EIGHTˇ nine\nten eleven twelve\n",
540    );
541
542    let diff = buffer.update(cx, |b, cx| b.diff(text.clone(), cx)).await;
543    buffer.update(cx, |buffer, cx| {
544        buffer.apply_diff(diff, cx).unwrap();
545        assert_eq!(buffer.text(), text);
546        let actual_offsets = anchors
547            .iter()
548            .map(|anchor| anchor.to_offset(buffer))
549            .collect::<Vec<_>>();
550        assert_eq!(actual_offsets, offsets);
551    });
552
553    let (text, offsets) =
554        marked_text_offsets("one two three\n{\nˇ}\nseven AND EIGHTEENˇ nine\nten eleven twelve\n");
555
556    let diff = buffer.update(cx, |b, cx| b.diff(text.clone(), cx)).await;
557    buffer.update(cx, |buffer, cx| {
558        buffer.apply_diff(diff, cx).unwrap();
559        assert_eq!(buffer.text(), text);
560        let actual_offsets = anchors
561            .iter()
562            .map(|anchor| anchor.to_offset(buffer))
563            .collect::<Vec<_>>();
564        assert_eq!(actual_offsets, offsets);
565    });
566}
567
568#[gpui::test(iterations = 10)]
569async fn test_normalize_whitespace(cx: &mut gpui::TestAppContext) {
570    let text = [
571        "zero",     //
572        "one  ",    // 2 trailing spaces
573        "two",      //
574        "three   ", // 3 trailing spaces
575        "four",     //
576        "five    ", // 4 trailing spaces
577    ]
578    .join("\n");
579
580    let buffer = cx.new(|cx| Buffer::local(text, cx));
581
582    // Spawn a task to format the buffer's whitespace.
583    // Pause so that the formatting task starts running.
584    let format = buffer.update(cx, |buffer, cx| buffer.remove_trailing_whitespace(None, cx));
585    yield_now().await;
586
587    // Edit the buffer while the normalization task is running.
588    let version_before_edit = buffer.update(cx, |buffer, _| buffer.version());
589    buffer.update(cx, |buffer, cx| {
590        buffer.edit(
591            [
592                (Point::new(0, 1)..Point::new(0, 1), "EE"),
593                (Point::new(3, 5)..Point::new(3, 5), "EEE"),
594            ],
595            None,
596            cx,
597        );
598    });
599
600    let format_diff = format.await;
601    buffer.update(cx, |buffer, cx| {
602        let version_before_format = format_diff.base_version.clone();
603        buffer.apply_diff(format_diff, cx);
604
605        // The outcome depends on the order of concurrent tasks.
606        //
607        // If the edit occurred while searching for trailing whitespace ranges,
608        // then the trailing whitespace region touched by the edit is left intact.
609        if version_before_format == version_before_edit {
610            assert_eq!(
611                buffer.text(),
612                [
613                    "zEEero",      //
614                    "one",         //
615                    "two",         //
616                    "threeEEE   ", //
617                    "four",        //
618                    "five",        //
619                ]
620                .join("\n")
621            );
622        }
623        // Otherwise, all trailing whitespace is removed.
624        else {
625            assert_eq!(
626                buffer.text(),
627                [
628                    "zEEero",   //
629                    "one",      //
630                    "two",      //
631                    "threeEEE", //
632                    "four",     //
633                    "five",     //
634                ]
635                .join("\n")
636            );
637        }
638    });
639}
640
641#[gpui::test]
642async fn test_reparse(cx: &mut gpui::TestAppContext) {
643    let text = "fn a() {}";
644    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(rust_lang(), cx));
645
646    // Wait for the initial text to parse
647    cx.executor().run_until_parked();
648    assert!(!buffer.update(cx, |buffer, _| buffer.is_parsing()));
649    assert_eq!(
650        get_tree_sexp(&buffer, cx),
651        concat!(
652            "(source_file (function_item name: (identifier) ",
653            "parameters: (parameters) ",
654            "body: (block)))"
655        )
656    );
657
658    buffer.update(cx, |buffer, _| buffer.set_sync_parse_timeout(None));
659
660    // Perform some edits (add parameter and variable reference)
661    // Parsing doesn't begin until the transaction is complete
662    buffer.update(cx, |buf, cx| {
663        buf.start_transaction();
664
665        let offset = buf.text().find(')').unwrap();
666        buf.edit([(offset..offset, "b: C")], None, cx);
667        assert!(!buf.is_parsing());
668
669        let offset = buf.text().find('}').unwrap();
670        buf.edit([(offset..offset, " d; ")], None, cx);
671        assert!(!buf.is_parsing());
672
673        buf.end_transaction(cx);
674        assert_eq!(buf.text(), "fn a(b: C) { d; }");
675        assert!(buf.is_parsing());
676    });
677    cx.executor().run_until_parked();
678    assert!(!buffer.update(cx, |buffer, _| buffer.is_parsing()));
679    assert_eq!(
680        get_tree_sexp(&buffer, cx),
681        concat!(
682            "(source_file (function_item name: (identifier) ",
683            "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
684            "body: (block (expression_statement (identifier)))))"
685        )
686    );
687
688    // Perform a series of edits without waiting for the current parse to complete:
689    // * turn identifier into a field expression
690    // * turn field expression into a method call
691    // * add a turbofish to the method call
692    buffer.update(cx, |buf, cx| {
693        let offset = buf.text().find(';').unwrap();
694        buf.edit([(offset..offset, ".e")], None, cx);
695        assert_eq!(buf.text(), "fn a(b: C) { d.e; }");
696        assert!(buf.is_parsing());
697    });
698    buffer.update(cx, |buf, cx| {
699        let offset = buf.text().find(';').unwrap();
700        buf.edit([(offset..offset, "(f)")], None, cx);
701        assert_eq!(buf.text(), "fn a(b: C) { d.e(f); }");
702        assert!(buf.is_parsing());
703    });
704    buffer.update(cx, |buf, cx| {
705        let offset = buf.text().find("(f)").unwrap();
706        buf.edit([(offset..offset, "::<G>")], None, cx);
707        assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
708        assert!(buf.is_parsing());
709    });
710    cx.executor().run_until_parked();
711    assert_eq!(
712        get_tree_sexp(&buffer, cx),
713        concat!(
714            "(source_file (function_item name: (identifier) ",
715            "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
716            "body: (block (expression_statement (call_expression ",
717            "function: (generic_function ",
718            "function: (field_expression value: (identifier) field: (field_identifier)) ",
719            "type_arguments: (type_arguments (type_identifier))) ",
720            "arguments: (arguments (identifier)))))))",
721        )
722    );
723
724    buffer.update(cx, |buf, cx| {
725        buf.undo(cx);
726        buf.undo(cx);
727        buf.undo(cx);
728        buf.undo(cx);
729        assert_eq!(buf.text(), "fn a() {}");
730        assert!(buf.is_parsing());
731    });
732
733    cx.executor().run_until_parked();
734    assert_eq!(
735        get_tree_sexp(&buffer, cx),
736        concat!(
737            "(source_file (function_item name: (identifier) ",
738            "parameters: (parameters) ",
739            "body: (block)))"
740        )
741    );
742
743    buffer.update(cx, |buf, cx| {
744        buf.redo(cx);
745        buf.redo(cx);
746        buf.redo(cx);
747        buf.redo(cx);
748        assert_eq!(buf.text(), "fn a(b: C) { d.e::<G>(f); }");
749        assert!(buf.is_parsing());
750    });
751    cx.executor().run_until_parked();
752    assert_eq!(
753        get_tree_sexp(&buffer, cx),
754        concat!(
755            "(source_file (function_item name: (identifier) ",
756            "parameters: (parameters (parameter pattern: (identifier) type: (type_identifier))) ",
757            "body: (block (expression_statement (call_expression ",
758            "function: (generic_function ",
759            "function: (field_expression value: (identifier) field: (field_identifier)) ",
760            "type_arguments: (type_arguments (type_identifier))) ",
761            "arguments: (arguments (identifier)))))))",
762        )
763    );
764}
765
766#[gpui::test]
767async fn test_resetting_language(cx: &mut gpui::TestAppContext) {
768    let buffer = cx.new(|cx| {
769        let mut buffer = Buffer::local("{}", cx).with_language(rust_lang(), cx);
770        buffer.set_sync_parse_timeout(None);
771        buffer
772    });
773
774    // Wait for the initial text to parse
775    cx.executor().run_until_parked();
776    assert_eq!(
777        get_tree_sexp(&buffer, cx),
778        "(source_file (expression_statement (block)))"
779    );
780
781    buffer.update(cx, |buffer, cx| buffer.set_language(Some(json_lang()), cx));
782    cx.executor().run_until_parked();
783    assert_eq!(get_tree_sexp(&buffer, cx), "(document (object))");
784}
785
786#[gpui::test]
787async fn test_outline(cx: &mut gpui::TestAppContext) {
788    let text = r#"
789        struct Person {
790            name: String,
791            age: usize,
792        }
793
794        mod module {
795            enum LoginState {
796                LoggedOut,
797                LoggingOn,
798                LoggedIn {
799                    person: Person,
800                    time: Instant,
801                }
802            }
803        }
804
805        impl Eq for Person {}
806
807        impl Drop for Person {
808            fn drop(&mut self) {
809                println!("bye");
810            }
811        }
812    "#
813    .unindent();
814
815    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(rust_lang(), cx));
816    let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
817    let outline = snapshot.outline(None);
818
819    assert_eq!(
820        outline
821            .items
822            .iter()
823            .map(|item| (
824                item.text.as_str(),
825                item.depth,
826                item.to_point(&snapshot).body_range(&snapshot)
827                    .map(|range| minimize_space(&snapshot.text_for_range(range).collect::<String>()))
828            ))
829            .collect::<Vec<_>>(),
830        &[
831            ("struct Person", 0, Some("name: String, age: usize,".to_string())),
832            ("name", 1, None),
833            ("age", 1, None),
834            (
835                "mod module",
836                0,
837                Some(
838                    "enum LoginState { LoggedOut, LoggingOn, LoggedIn { person: Person, time: Instant, } }".to_string()
839                )
840            ),
841            (
842                "enum LoginState",
843                1,
844                Some("LoggedOut, LoggingOn, LoggedIn { person: Person, time: Instant, }".to_string())
845            ),
846            ("LoggedOut", 2, None),
847            ("LoggingOn", 2, None),
848            ("LoggedIn", 2, Some("person: Person, time: Instant,".to_string())),
849            ("person", 3, None),
850            ("time", 3, None),
851            ("impl Eq for Person", 0, Some("".to_string())),
852            (
853                "impl Drop for Person",
854                0,
855                Some("fn drop(&mut self) { println!(\"bye\"); }".to_string())
856            ),
857            ("fn drop", 1, Some("println!(\"bye\");".to_string())),
858        ]
859    );
860
861    // Single-atom queries (no whitespace): all matched chars must land in the leaf,
862    // so items whose ancestor path coincidentally contains the query chars don't
863    // show up unless the leaf itself matches.
864    assert_eq!(
865        search(&outline, "oon", cx).await,
866        &[
867            ("mod module", vec![]),                     // parent context for LoggingOn
868            ("enum LoginState", vec![]),                // parent context for LoggingOn
869            ("LoggingOn", vec![1, 7, 8]),               // all three chars in leaf
870            ("impl Eq for Person", vec![9, 16, 17]),    // o-o-n in "for Person"
871            ("impl Drop for Person", vec![11, 18, 19]), // o-o-n in "for Person"
872        ]
873    );
874
875    // Multi-atom queries: rows whose match lives entirely in an ancestor
876    // are kept as context (empty positions, score zeroed) so descendants
877    // of a matched container surface alongside it.
878    assert_eq!(
879        search(&outline, "dp p", cx).await,
880        &[("impl Drop for Person", vec![5, 14]), ("fn drop", vec![]),]
881    );
882    assert_eq!(
883        search(&outline, "dpn", cx).await,
884        &[("impl Drop for Person", vec![5, 14, 19])]
885    );
886    assert_eq!(
887        search(&outline, "impl ", cx).await,
888        &[
889            ("impl Eq for Person", vec![0, 1, 2, 3]),
890            ("impl Drop for Person", vec![0, 1, 2, 3]),
891            ("fn drop", vec![]),
892        ]
893    );
894
895    fn minimize_space(text: &str) -> String {
896        static WHITESPACE: LazyLock<Regex> = LazyLock::new(|| Regex::new("[\\n\\s]+").unwrap());
897        WHITESPACE.replace_all(text, " ").trim().to_string()
898    }
899
900    async fn search<'a>(
901        outline: &'a Outline<Anchor>,
902        query: &'a str,
903        cx: &'a gpui::TestAppContext,
904    ) -> Vec<(&'a str, Vec<usize>)> {
905        let entries = cx
906            .update(|cx| outline.search(query, cx.background_executor().clone()))
907            .await;
908        entries
909            .into_iter()
910            .map(|entry| {
911                let candidate_id = entry.candidate_id();
912                let positions = entry.into_match().map(|m| m.positions).unwrap_or_default();
913                (outline.items[candidate_id].text.as_str(), positions)
914            })
915            .collect::<Vec<_>>()
916    }
917}
918
919#[gpui::test]
920async fn test_outline_nodes_with_newlines(cx: &mut gpui::TestAppContext) {
921    let text = r#"
922        impl A for B<
923            C
924        > {
925        };
926    "#
927    .unindent();
928
929    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(rust_lang(), cx));
930    let outline = buffer.update(cx, |buffer, _| buffer.snapshot().outline(None));
931
932    assert_eq!(
933        outline
934            .items
935            .iter()
936            .map(|item| (item.text.as_str(), item.depth))
937            .collect::<Vec<_>>(),
938        &[("impl A for B<", 0)]
939    );
940}
941
942#[gpui::test]
943async fn test_outline_with_extra_context(cx: &mut gpui::TestAppContext) {
944    let language = javascript_lang()
945        .with_outline_query(
946            r#"
947            (function_declaration
948                "function" @context
949                name: (_) @name
950                parameters: (formal_parameters
951                    "(" @context.extra
952                    ")" @context.extra)) @item
953            "#,
954        )
955        .unwrap();
956
957    let text = r#"
958        function a() {}
959        function b(c) {}
960    "#
961    .unindent();
962
963    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(Arc::new(language), cx));
964    let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
965
966    // extra context nodes are included in the outline.
967    let outline = snapshot.outline(None);
968    assert_eq!(
969        outline
970            .items
971            .iter()
972            .map(|item| (item.text.as_str(), item.depth))
973            .collect::<Vec<_>>(),
974        &[("function a()", 0), ("function b( )", 0),]
975    );
976
977    // extra context nodes do not appear in breadcrumbs.
978    let symbols = snapshot.symbols_containing(3, None);
979    assert_eq!(
980        symbols
981            .iter()
982            .map(|item| (item.text.as_str(), item.depth))
983            .collect::<Vec<_>>(),
984        &[("function a", 0)]
985    );
986}
987
988#[gpui::test]
989async fn test_outline_selection_range_for_multiline_c_signature(cx: &mut gpui::TestAppContext) {
990    let text = indoc! {"
991        void
992        evdev_post_scroll(struct evdev_device *device,
993                  usec_t time,
994                  enum libinput_pointer_axis_source source,
995                  const struct normalized_coords *delta)
996        {
997            return;
998        }
999    "};
1000
1001    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(c_lang(), cx));
1002    let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
1003    let outline = snapshot.outline(None);
1004
1005    let item = outline
1006        .items
1007        .iter()
1008        .find(|item| item.text.contains("evdev_post_scroll"))
1009        .unwrap()
1010        .to_point(&snapshot);
1011
1012    assert_eq!(item.source_range_for_text.start, Point::new(0, 0));
1013    assert_eq!(item.selection_range.start, Point::new(1, 0));
1014    assert_eq!(item.text, "void evdev_post_scroll( )");
1015}
1016
1017#[gpui::test]
1018fn test_outline_annotations(cx: &mut App) {
1019    // Add this new test case
1020    let text = r#"
1021        /// This is a doc comment
1022        /// that spans multiple lines
1023        fn annotated_function() {
1024            // This is not an annotation
1025        }
1026
1027        // This is a single-line annotation
1028        fn another_function() {}
1029
1030        fn unannotated_function() {}
1031
1032        // This comment is not an annotation
1033
1034        fn function_after_blank_line() {}
1035    "#
1036    .unindent();
1037
1038    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(rust_lang(), cx));
1039    let outline = buffer.update(cx, |buffer, _| buffer.snapshot().outline(None));
1040
1041    assert_eq!(
1042        outline
1043            .items
1044            .into_iter()
1045            .map(|item| (
1046                item.text.to_string(),
1047                item.depth,
1048                item.annotation_range
1049                    .map(|range| { buffer.read(cx).text_for_range(range).collect::<String>() })
1050            ))
1051            .collect::<Vec<_>>(),
1052        &[
1053            (
1054                "fn annotated_function".to_string(),
1055                0,
1056                Some("/// This is a doc comment\n/// that spans multiple lines".to_string())
1057            ),
1058            (
1059                "fn another_function".to_string(),
1060                0,
1061                Some("// This is a single-line annotation".to_string())
1062            ),
1063            ("fn unannotated_function".to_string(), 0, None),
1064            ("fn function_after_blank_line".to_string(), 0, None),
1065        ]
1066    );
1067}
1068
1069#[gpui::test]
1070async fn test_symbols_containing(cx: &mut gpui::TestAppContext) {
1071    let text = r#"
1072        impl Person {
1073            fn one() {
1074                1
1075            }
1076
1077            fn two() {
1078                2
1079            }fn three() {
1080                3
1081            }
1082        }
1083    "#
1084    .unindent();
1085
1086    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(rust_lang(), cx));
1087    let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
1088
1089    // point is at the start of an item
1090    assert_eq!(
1091        symbols_containing(Point::new(1, 4), &snapshot),
1092        vec![
1093            (
1094                "impl Person".to_string(),
1095                Point::new(0, 0)..Point::new(10, 1)
1096            ),
1097            ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
1098        ]
1099    );
1100
1101    // point is in the middle of an item
1102    assert_eq!(
1103        symbols_containing(Point::new(2, 8), &snapshot),
1104        vec![
1105            (
1106                "impl Person".to_string(),
1107                Point::new(0, 0)..Point::new(10, 1)
1108            ),
1109            ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
1110        ]
1111    );
1112
1113    // point is at the end of an item
1114    assert_eq!(
1115        symbols_containing(Point::new(3, 5), &snapshot),
1116        vec![
1117            (
1118                "impl Person".to_string(),
1119                Point::new(0, 0)..Point::new(10, 1)
1120            ),
1121            ("fn one".to_string(), Point::new(1, 4)..Point::new(3, 5))
1122        ]
1123    );
1124
1125    // point is in between two adjacent items
1126    assert_eq!(
1127        symbols_containing(Point::new(7, 5), &snapshot),
1128        vec![
1129            (
1130                "impl Person".to_string(),
1131                Point::new(0, 0)..Point::new(10, 1)
1132            ),
1133            ("fn two".to_string(), Point::new(5, 4)..Point::new(7, 5))
1134        ]
1135    );
1136
1137    fn symbols_containing(
1138        position: Point,
1139        snapshot: &BufferSnapshot,
1140    ) -> Vec<(String, Range<Point>)> {
1141        snapshot
1142            .symbols_containing(position, None)
1143            .into_iter()
1144            .map(|item| {
1145                (
1146                    item.text.to_string(),
1147                    item.range.start.to_point(snapshot)..item.range.end.to_point(snapshot),
1148                )
1149            })
1150            .collect()
1151    }
1152
1153    let (text, offsets) = marked_text_offsets(
1154        &"
1155        // ˇ😅 //
1156        fn test() {
1157        }
1158    "
1159        .unindent(),
1160    );
1161    let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(rust_lang(), cx));
1162    let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
1163
1164    // note, it would be nice to actually return the method test in this
1165    // case, but primarily asserting we don't crash because of the multibyte character.
1166    assert_eq!(snapshot.symbols_containing(offsets[0], None), vec![]);
1167}
1168
1169#[gpui::test]
1170fn test_text_objects(cx: &mut App) {
1171    let (text, ranges) = marked_text_ranges(
1172        indoc! {r#"
1173            impl Hello {
1174                fn say() -> u8 { return /* ˇhi */ 1 }
1175            }"#
1176        },
1177        false,
1178    );
1179
1180    let buffer = cx.new(|cx| Buffer::local(text.clone(), cx).with_language(rust_lang(), cx));
1181    let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
1182
1183    let matches = snapshot
1184        .text_object_ranges(ranges[0].clone(), TreeSitterOptions::default())
1185        .map(|(range, text_object)| (&text[range], text_object))
1186        .collect::<Vec<_>>();
1187
1188    assert_eq!(
1189        matches,
1190        &[
1191            ("/* hi */", TextObject::AroundComment),
1192            ("return /* hi */ 1", TextObject::InsideFunction),
1193            (
1194                "fn say() -> u8 { return /* hi */ 1 }",
1195                TextObject::AroundFunction
1196            ),
1197            (
1198                "fn say() -> u8 { return /* hi */ 1 }",
1199                TextObject::InsideClass
1200            ),
1201            (
1202                "impl Hello {\n    fn say() -> u8 { return /* hi */ 1 }\n}",
1203                TextObject::AroundClass
1204            ),
1205        ],
1206    )
1207}
1208
1209#[gpui::test]
1210fn test_text_objects_with_has_parent_predicate(cx: &mut App) {
1211    use std::borrow::Cow;
1212
1213    // Create a language with a custom text_objects query that uses #has-parent?
1214    // This query only matches closure_expression when it's inside a call_expression
1215    let language = Language::new(
1216        LanguageConfig {
1217            name: "Rust".into(),
1218            matcher: (LanguageMatcher {
1219                path_suffixes: vec!["rs".to_string()],
1220                ..Default::default()
1221            })
1222            .into(),
1223            ..Default::default()
1224        },
1225        Some(tree_sitter_rust::LANGUAGE.into()),
1226    )
1227    .with_queries(LanguageQueries {
1228        text_objects: Some(Cow::from(indoc! {r#"
1229            ; Only match closures that are arguments to function calls
1230            (closure_expression) @function.around
1231              (#has-parent? @function.around arguments)
1232        "#})),
1233        ..Default::default()
1234    })
1235    .expect("Could not parse queries");
1236
1237    let (text, ranges) = marked_text_ranges(
1238        indoc! {r#"
1239            fn main() {
1240                let standalone = |x| x + 1;
1241                let result = foo(|y| y * ˇ2);
1242            }"#
1243        },
1244        false,
1245    );
1246
1247    let buffer = cx.new(|cx| Buffer::local(text.clone(), cx).with_language(Arc::new(language), cx));
1248    let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
1249
1250    let matches = snapshot
1251        .text_object_ranges(ranges[0].clone(), TreeSitterOptions::default())
1252        .map(|(range, text_object)| (&text[range], text_object))
1253        .collect::<Vec<_>>();
1254
1255    // Should only match the closure inside foo(), not the standalone closure
1256    assert_eq!(matches, &[("|y| y * 2", TextObject::AroundFunction),]);
1257}
1258
1259#[gpui::test]
1260fn test_text_objects_with_not_has_parent_predicate(cx: &mut App) {
1261    use std::borrow::Cow;
1262
1263    // Create a language with a custom text_objects query that uses #not-has-parent?
1264    // This query only matches closure_expression when it's NOT inside a call_expression
1265    let language = Language::new(
1266        LanguageConfig {
1267            name: "Rust".into(),
1268            matcher: (LanguageMatcher {
1269                path_suffixes: vec!["rs".to_string()],
1270                ..Default::default()
1271            })
1272            .into(),
1273            ..Default::default()
1274        },
1275        Some(tree_sitter_rust::LANGUAGE.into()),
1276    )
1277    .with_queries(LanguageQueries {
1278        text_objects: Some(Cow::from(indoc! {r#"
1279            ; Only match closures that are NOT arguments to function calls
1280            (closure_expression) @function.around
1281              (#not-has-parent? @function.around arguments)
1282        "#})),
1283        ..Default::default()
1284    })
1285    .expect("Could not parse queries");
1286
1287    let (text, ranges) = marked_text_ranges(
1288        indoc! {r#"
1289            fn main() {
1290                let standalone = |x| x +ˇ 1;
1291                let result = foo(|y| y * 2);
1292            }"#
1293        },
1294        false,
1295    );
1296
1297    let buffer = cx.new(|cx| Buffer::local(text.clone(), cx).with_language(Arc::new(language), cx));
1298    let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
1299
1300    let matches = snapshot
1301        .text_object_ranges(ranges[0].clone(), TreeSitterOptions::default())
1302        .map(|(range, text_object)| (&text[range], text_object))
1303        .collect::<Vec<_>>();
1304
1305    // Should only match the standalone closure, not the one inside foo()
1306    assert_eq!(matches, &[("|x| x + 1", TextObject::AroundFunction),]);
1307}
1308
1309#[gpui::test]
1310fn test_enclosing_bracket_ranges(cx: &mut App) {
1311    #[track_caller]
1312    fn assert(selection_text: &'static str, range_markers: Vec<&'static str>, cx: &mut App) {
1313        assert_bracket_pairs(selection_text, range_markers, rust_lang(), cx)
1314    }
1315
1316    assert(
1317        indoc! {"
1318            mod x {
1319                moˇd y {
1320
1321                }
1322            }
1323            let foo = 1;"},
1324        vec![indoc! {"
1325            mod x «{»
1326                mod y {
1327
1328                }
1329            «}»
1330            let foo = 1;"}],
1331        cx,
1332    );
1333
1334    assert(
1335        indoc! {"
1336            mod x {
1337                mod y ˇ{
1338
1339                }
1340            }
1341            let foo = 1;"},
1342        vec![
1343            indoc! {"
1344                mod x «{»
1345                    mod y {
1346
1347                    }
1348                «}»
1349                let foo = 1;"},
1350            indoc! {"
1351                mod x {
1352                    mod y «{»
1353
1354                    «}»
1355                }
1356                let foo = 1;"},
1357        ],
1358        cx,
1359    );
1360
1361    assert(
1362        indoc! {"
1363            mod x {
1364                mod y {
1365
1366
1367            }
1368            let foo = 1;"},
1369        vec![
1370            indoc! {"
1371                mod x «{»
1372                    mod y {
1373
1374                    }
1375                «}»
1376                let foo = 1;"},
1377            indoc! {"
1378                mod x {
1379                    mod y «{»
1380
1381                    «}»
1382                }
1383                let foo = 1;"},
1384        ],
1385        cx,
1386    );
1387
1388    assert(
1389        indoc! {"
1390            mod x {
1391                mod y {
1392
1393                }
1394            ˇ}
1395            let foo = 1;"},
1396        vec![indoc! {"
1397            mod x «{»
1398                mod y {
1399
1400                }
1401            «}»
1402            let foo = 1;"}],
1403        cx,
1404    );
1405
1406    assert(
1407        indoc! {"
1408            mod x {
1409                mod y {
1410
1411                }
1412            }
1413            let fˇoo = 1;"},
1414        Vec::new(),
1415        cx,
1416    );
1417
1418    // Regression test: avoid crash when querying at the end of the buffer.
1419    assert(
1420        indoc! {"
1421            mod x {
1422                mod y {
1423
1424                }
1425            }
1426            let foo = 1;ˇ"},
1427        Vec::new(),
1428        cx,
1429    );
1430}
1431
1432#[gpui::test]
1433fn test_bracket_colorization_indices_remain_stable_across_row_chunks(cx: &mut App) {
1434    let mut text = String::from("{\n  \"theme\": {\n");
1435    let mut property_object_open_offsets = Vec::new();
1436    for index in 0..500 {
1437        text.push_str(&format!("    \"scope_{index:03}\": "));
1438        property_object_open_offsets.push(text.len());
1439        text.push_str("{\n      \"color\": \"#ffffff\"\n    },\n");
1440    }
1441    text.push_str("    \"last\": {}\n  }\n}\n");
1442    assert!(
1443        text.len() > MAX_BYTES_TO_QUERY,
1444        "fixture should exceed the bounded tree-sitter query window"
1445    );
1446
1447    let buffer = cx.new(|cx| Buffer::local(text.clone(), cx).with_language(json_lang(), cx));
1448    let snapshot = buffer.update(cx, |buffer, _| buffer.snapshot());
1449
1450    let late_open_offset = property_object_open_offsets[400];
1451    let late_matches = snapshot.fetch_bracket_ranges(late_open_offset..late_open_offset + 1, None);
1452    let late_color_index = color_index_for_open(&late_matches, late_open_offset);
1453
1454    assert_eq!(
1455        late_color_index,
1456        Some(2),
1457        "Jumping directly into a later row chunk should preserve enclosing JSON object depth"
1458    );
1459
1460    let first_open_offset = property_object_open_offsets[0];
1461    let all_matches = snapshot.fetch_bracket_ranges(0..snapshot.len(), None);
1462    assert_eq!(
1463        color_index_for_open(&all_matches, first_open_offset),
1464        late_color_index,
1465        "Sibling object braces should keep the same color across row chunks"
1466    );
1467
1468    for open_offset in property_object_open_offsets {
1469        assert_eq!(
1470            color_index_for_open(&all_matches, open_offset),
1471            late_color_index,
1472            "All generated sibling object braces should share the same depth color"
1473        );
1474    }
1475}
1476
1477#[test]
1478fn test_applicable_row_chunks() {
1479    let text = (0..125)
1480        .map(|row| format!("line {row}\n"))
1481        .collect::<String>();
1482    let buffer = TextBuffer::new(ReplicaId::LOCAL, BufferId::new(1).unwrap(), text);
1483    let snapshot = buffer.snapshot();
1484    let chunks = row_chunk::RowChunks::new(snapshot, 10);
1485    assert_eq!(chunks.len(), 13);
1486
1487    let row_ranges = |ranges: &[Range<Point>]| {
1488        chunks
1489            .applicable_chunks(ranges)
1490            .map(|chunk| chunk.row_range())
1491            .collect::<Vec<_>>()
1492    };
1493
1494    assert_eq!(
1495        row_ranges(&[Point::new(15, 0)..Point::new(17, 3)]),
1496        vec![10..20],
1497        "Range in the middle of a chunk should yield that chunk only"
1498    );
1499    assert_eq!(
1500        row_ranges(&[Point::new(10, 0)..Point::new(12, 0)]),
1501        vec![0..10, 10..20],
1502        "Range starting exactly at a chunk boundary should also touch the previous chunk"
1503    );
1504    assert_eq!(
1505        row_ranges(&[Point::new(5, 0)..Point::new(9, 0)]),
1506        vec![0..10],
1507        "Range ending before a chunk boundary should not touch the next chunk"
1508    );
1509    assert_eq!(
1510        row_ranges(&[Point::new(5, 0)..Point::new(10, 0)]),
1511        vec![0..10, 10..20],
1512        "Range ending exactly at a chunk boundary should touch the next chunk"
1513    );
1514    assert_eq!(
1515        row_ranges(&[
1516            Point::new(112, 0)..Point::new(112, 0),
1517            Point::new(15, 0)..Point::new(16, 0),
1518            Point::new(11, 0)..Point::new(18, 0),
1519        ]),
1520        vec![10..20, 110..120],
1521        "Chunks for multiple ranges should be deduplicated and sorted"
1522    );
1523
1524    let all_chunks = chunks
1525        .applicable_chunks(&[Point::zero()..Point::new(125, 0)])
1526        .collect::<Vec<_>>();
1527    assert_eq!(
1528        all_chunks.iter().map(|chunk| chunk.id).collect::<Vec<_>>(),
1529        (0..13).collect::<Vec<_>>()
1530    );
1531    assert_eq!(all_chunks[12].row_range(), 120..125);
1532    for chunk in &all_chunks {
1533        assert_eq!(
1534            chunk.start_anchor.to_point(snapshot),
1535            Point::new(chunk.start, 0)
1536        );
1537        assert_eq!(
1538            chunk.end_anchor.to_point(snapshot),
1539            Point::new(chunk.end_exclusive, 0)
1540        );
1541    }
1542    assert_eq!(
1543        chunks
1544            .applicable_chunks(&[Point::zero()..Point::new(125, 0)])
1545            .collect::<Vec<_>>(),
1546        all_chunks,
1547        "Memoized chunks should be identical to the initially computed ones"
1548    );
1549}
1550
1551#[gpui::test]
1552fn test_enclosing_bracket_ranges_where_brackets_are_not_outermost_children(cx: &mut App) {
1553    let mut assert = |selection_text, bracket_pair_texts| {
1554        assert_bracket_pairs(
1555            selection_text,
1556            bracket_pair_texts,
1557            Arc::new(javascript_lang()),
1558            cx,
1559        )
1560    };
1561
1562    assert(
1563        indoc! {"
1564        for (const a in b)ˇ {
1565            // a comment that's longer than the for-loop header
1566        }"},
1567        vec![indoc! {"
1568        for «(»const a in b«)» {
1569            // a comment that's longer than the for-loop header
1570        }"}],
1571    );
1572
1573    // Regression test: even though the parent node of the parentheses (the for loop) does
1574    // intersect the given range, the parentheses themselves do not contain the range, so
1575    // they should not be returned. Only the curly braces contain the range.
1576    assert(
1577        indoc! {"
1578        for (const a in b) {ˇ
1579            // a comment that's longer than the for-loop header
1580        }"},
1581        vec![indoc! {"
1582        for (const a in b) «{»
1583            // a comment that's longer than the for-loop header
1584        «}»"}],
1585    );
1586}
1587
1588#[gpui::test]
1589fn test_range_for_syntax_ancestor(cx: &mut App) {
1590    cx.new(|cx| {
1591        let text = "fn a() { b(|c| {}) }";
1592        let buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
1593        let snapshot = buffer.snapshot();
1594
1595        assert_eq!(
1596            snapshot
1597                .syntax_ancestor(empty_range_at(text, "|"))
1598                .unwrap()
1599                .byte_range(),
1600            range_of(text, "|")
1601        );
1602        assert_eq!(
1603            snapshot
1604                .syntax_ancestor(range_of(text, "|"))
1605                .unwrap()
1606                .byte_range(),
1607            range_of(text, "|c|")
1608        );
1609        assert_eq!(
1610            snapshot
1611                .syntax_ancestor(range_of(text, "|c|"))
1612                .unwrap()
1613                .byte_range(),
1614            range_of(text, "|c| {}")
1615        );
1616        assert_eq!(
1617            snapshot
1618                .syntax_ancestor(range_of(text, "|c| {}"))
1619                .unwrap()
1620                .byte_range(),
1621            range_of(text, "(|c| {})")
1622        );
1623
1624        buffer
1625    });
1626
1627    fn empty_range_at(text: &str, part: &str) -> Range<usize> {
1628        let start = text.find(part).unwrap();
1629        start..start
1630    }
1631
1632    fn range_of(text: &str, part: &str) -> Range<usize> {
1633        let start = text.find(part).unwrap();
1634        start..start + part.len()
1635    }
1636}
1637
1638#[gpui::test]
1639fn test_autoindent_with_soft_tabs(cx: &mut App) {
1640    init_settings(cx, |_| {});
1641
1642    cx.new(|cx| {
1643        let text = "fn a() {}";
1644        let mut buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
1645
1646        buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
1647        assert_eq!(buffer.text(), "fn a() {\n    \n}");
1648
1649        buffer.edit(
1650            [(Point::new(1, 4)..Point::new(1, 4), "b()\n")],
1651            Some(AutoindentMode::EachLine),
1652            cx,
1653        );
1654        assert_eq!(buffer.text(), "fn a() {\n    b()\n    \n}");
1655
1656        // Create a field expression on a new line, causing that line
1657        // to be indented.
1658        buffer.edit(
1659            [(Point::new(2, 4)..Point::new(2, 4), ".c")],
1660            Some(AutoindentMode::EachLine),
1661            cx,
1662        );
1663        assert_eq!(buffer.text(), "fn a() {\n    b()\n        .c\n}");
1664
1665        // Remove the dot so that the line is no longer a field expression,
1666        // causing the line to be outdented.
1667        buffer.edit(
1668            [(Point::new(2, 8)..Point::new(2, 9), "")],
1669            Some(AutoindentMode::EachLine),
1670            cx,
1671        );
1672        assert_eq!(buffer.text(), "fn a() {\n    b()\n    c\n}");
1673
1674        buffer
1675    });
1676}
1677
1678#[gpui::test]
1679fn test_autoindent_with_hard_tabs(cx: &mut App) {
1680    init_settings(cx, |settings| {
1681        settings.defaults.hard_tabs = Some(true);
1682    });
1683
1684    cx.new(|cx| {
1685        let text = "fn a() {}";
1686        let mut buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
1687
1688        buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
1689        assert_eq!(buffer.text(), "fn a() {\n\t\n}");
1690
1691        buffer.edit(
1692            [(Point::new(1, 1)..Point::new(1, 1), "b()\n")],
1693            Some(AutoindentMode::EachLine),
1694            cx,
1695        );
1696        assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\n}");
1697
1698        // Create a field expression on a new line, causing that line
1699        // to be indented.
1700        buffer.edit(
1701            [(Point::new(2, 1)..Point::new(2, 1), ".c")],
1702            Some(AutoindentMode::EachLine),
1703            cx,
1704        );
1705        assert_eq!(buffer.text(), "fn a() {\n\tb()\n\t\t.c\n}");
1706
1707        // Remove the dot so that the line is no longer a field expression,
1708        // causing the line to be outdented.
1709        buffer.edit(
1710            [(Point::new(2, 2)..Point::new(2, 3), "")],
1711            Some(AutoindentMode::EachLine),
1712            cx,
1713        );
1714        assert_eq!(buffer.text(), "fn a() {\n\tb()\n\tc\n}");
1715
1716        buffer
1717    });
1718}
1719
1720#[gpui::test]
1721fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut App) {
1722    init_settings(cx, |_| {});
1723
1724    cx.new(|cx| {
1725        let mut buffer = Buffer::local(
1726            "
1727            fn a() {
1728            c;
1729            d;
1730            }
1731            "
1732            .unindent(),
1733            cx,
1734        )
1735        .with_language(rust_lang(), cx);
1736
1737        // Lines 2 and 3 don't match the indentation suggestion. When editing these lines,
1738        // their indentation is not adjusted.
1739        buffer.edit_via_marked_text(
1740            &"
1741            fn a() {
1742            c«()»;
1743            d«()»;
1744            }
1745            "
1746            .unindent(),
1747            Some(AutoindentMode::EachLine),
1748            cx,
1749        );
1750        assert_eq!(
1751            buffer.text(),
1752            "
1753            fn a() {
1754            c();
1755            d();
1756            }
1757            "
1758            .unindent()
1759        );
1760
1761        // When appending new content after these lines, the indentation is based on the
1762        // preceding lines' actual indentation.
1763        buffer.edit_via_marked_text(
1764            &"
1765            fn a() {
1766
1767            .f
1768            .g()»;
1769
1770            .f
1771            .g()»;
1772            }
1773            "
1774            .unindent(),
1775            Some(AutoindentMode::EachLine),
1776            cx,
1777        );
1778        assert_eq!(
1779            buffer.text(),
1780            "
1781            fn a() {
1782            c
1783                .f
1784                .g();
1785            d
1786                .f
1787                .g();
1788            }
1789            "
1790            .unindent()
1791        );
1792
1793        // Insert a newline after the open brace. It is auto-indented
1794        buffer.edit_via_marked_text(
1795            &"
1796            fn a() {«
1797            »
1798            c
1799                .f
1800                .g();
1801            d
1802                .f
1803                .g();
1804            }
1805            "
1806            .unindent(),
1807            Some(AutoindentMode::EachLine),
1808            cx,
1809        );
1810        assert_eq!(
1811            buffer.text(),
1812            "
1813            fn a() {
1814                ˇ
1815            c
1816                .f
1817                .g();
1818            d
1819                .f
1820                .g();
1821            }
1822            "
1823            .unindent()
1824            .replace("ˇ", "")
1825        );
1826
1827        // Manually outdent the line. It stays outdented.
1828        buffer.edit_via_marked_text(
1829            &"
1830            fn a() {
1831            «»
1832            c
1833                .f
1834                .g();
1835            d
1836                .f
1837                .g();
1838            }
1839            "
1840            .unindent(),
1841            Some(AutoindentMode::EachLine),
1842            cx,
1843        );
1844        assert_eq!(
1845            buffer.text(),
1846            "
1847            fn a() {
1848
1849            c
1850                .f
1851                .g();
1852            d
1853                .f
1854                .g();
1855            }
1856            "
1857            .unindent()
1858        );
1859
1860        buffer
1861    });
1862
1863    cx.new(|cx| {
1864        eprintln!("second buffer: {:?}", cx.entity_id());
1865
1866        let mut buffer = Buffer::local(
1867            "
1868            fn a() {
1869                b();
1870                |
1871            "
1872            .replace('|', "") // marker to preserve trailing whitespace
1873            .unindent(),
1874            cx,
1875        )
1876        .with_language(rust_lang(), cx);
1877
1878        // Insert a closing brace. It is outdented.
1879        buffer.edit_via_marked_text(
1880            &"
1881            fn a() {
1882                b();
1883                «}»
1884            "
1885            .unindent(),
1886            Some(AutoindentMode::EachLine),
1887            cx,
1888        );
1889        assert_eq!(
1890            buffer.text(),
1891            "
1892            fn a() {
1893                b();
1894            }
1895            "
1896            .unindent()
1897        );
1898
1899        // Manually edit the leading whitespace. The edit is preserved.
1900        buffer.edit_via_marked_text(
1901            &"
1902            fn a() {
1903                b();
1904            «    »}
1905            "
1906            .unindent(),
1907            Some(AutoindentMode::EachLine),
1908            cx,
1909        );
1910        assert_eq!(
1911            buffer.text(),
1912            "
1913            fn a() {
1914                b();
1915                }
1916            "
1917            .unindent()
1918        );
1919        buffer
1920    });
1921
1922    eprintln!("DONE");
1923}
1924
1925#[gpui::test]
1926fn test_autoindent_does_not_adjust_lines_within_newly_created_errors(cx: &mut App) {
1927    init_settings(cx, |_| {});
1928
1929    cx.new(|cx| {
1930        let mut buffer = Buffer::local(
1931            "
1932            fn a() {
1933                i
1934            }
1935            "
1936            .unindent(),
1937            cx,
1938        )
1939        .with_language(rust_lang(), cx);
1940
1941        // Regression test: line does not get outdented due to syntax error
1942        buffer.edit_via_marked_text(
1943            &"
1944            fn a() {
1945                i«f let Some(x) = y»
1946            }
1947            "
1948            .unindent(),
1949            Some(AutoindentMode::EachLine),
1950            cx,
1951        );
1952        assert_eq!(
1953            buffer.text(),
1954            "
1955            fn a() {
1956                if let Some(x) = y
1957            }
1958            "
1959            .unindent()
1960        );
1961
1962        buffer.edit_via_marked_text(
1963            &"
1964            fn a() {
1965                if let Some(x) = y« {»
1966            }
1967            "
1968            .unindent(),
1969            Some(AutoindentMode::EachLine),
1970            cx,
1971        );
1972        assert_eq!(
1973            buffer.text(),
1974            "
1975            fn a() {
1976                if let Some(x) = y {
1977            }
1978            "
1979            .unindent()
1980        );
1981
1982        buffer
1983    });
1984}
1985
1986#[gpui::test]
1987fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut App) {
1988    init_settings(cx, |_| {});
1989
1990    cx.new(|cx| {
1991        let mut buffer = Buffer::local(
1992            "
1993            fn a() {}
1994            "
1995            .unindent(),
1996            cx,
1997        )
1998        .with_language(rust_lang(), cx);
1999
2000        buffer.edit_via_marked_text(
2001            &"
2002            fn a(«
2003            b») {}
2004            "
2005            .unindent(),
2006            Some(AutoindentMode::EachLine),
2007            cx,
2008        );
2009        assert_eq!(
2010            buffer.text(),
2011            "
2012            fn a(
2013                b) {}
2014            "
2015            .unindent()
2016        );
2017
2018        // The indentation suggestion changed because `@end` node (a close paren)
2019        // is now at the beginning of the line.
2020        buffer.edit_via_marked_text(
2021            &"
2022            fn a(
2023                ˇ) {}
2024            "
2025            .unindent(),
2026            Some(AutoindentMode::EachLine),
2027            cx,
2028        );
2029        assert_eq!(
2030            buffer.text(),
2031            "
2032                fn a(
2033                ) {}
2034            "
2035            .unindent()
2036        );
2037
2038        buffer
2039    });
2040}
2041
2042#[gpui::test]
2043fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut App) {
2044    init_settings(cx, |_| {});
2045
2046    cx.new(|cx| {
2047        let text = "a\nb";
2048        let mut buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
2049        buffer.edit(
2050            [(0..1, "\n"), (2..3, "\n")],
2051            Some(AutoindentMode::EachLine),
2052            cx,
2053        );
2054        assert_eq!(buffer.text(), "\n\n\n");
2055        buffer
2056    });
2057}
2058
2059#[gpui::test]
2060fn test_autoindent_multi_line_insertion(cx: &mut App) {
2061    init_settings(cx, |_| {});
2062
2063    cx.new(|cx| {
2064        let text = "
2065            const a: usize = 1;
2066            fn b() {
2067                if c {
2068                    let d = 2;
2069                }
2070            }
2071        "
2072        .unindent();
2073
2074        let mut buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
2075        buffer.edit(
2076            [(Point::new(3, 0)..Point::new(3, 0), "e(\n    f()\n);\n")],
2077            Some(AutoindentMode::EachLine),
2078            cx,
2079        );
2080        assert_eq!(
2081            buffer.text(),
2082            "
2083                const a: usize = 1;
2084                fn b() {
2085                    if c {
2086                        e(
2087                            f()
2088                        );
2089                        let d = 2;
2090                    }
2091                }
2092            "
2093            .unindent()
2094        );
2095
2096        buffer
2097    });
2098}
2099
2100#[gpui::test]
2101fn test_autoindent_block_mode(cx: &mut App) {
2102    init_settings(cx, |_| {});
2103
2104    cx.new(|cx| {
2105        let text = r#"
2106            fn a() {
2107                b();
2108            }
2109        "#
2110        .unindent();
2111        let mut buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
2112
2113        // When this text was copied, both of the quotation marks were at the same
2114        // indent level, but the indentation of the first line was not included in
2115        // the copied text. This information is retained in the
2116        // 'original_indent_columns' vector.
2117        let original_indent_columns = vec![Some(4)];
2118        let inserted_text = r#"
2119            "
2120                  c
2121                    d
2122                      e
2123                "
2124        "#
2125        .unindent();
2126
2127        // Insert the block at column zero. The entire block is indented
2128        // so that the first line matches the previous line's indentation.
2129        buffer.edit(
2130            [(Point::new(2, 0)..Point::new(2, 0), inserted_text.clone())],
2131            Some(AutoindentMode::Block {
2132                original_indent_columns: original_indent_columns.clone(),
2133            }),
2134            cx,
2135        );
2136        assert_eq!(
2137            buffer.text(),
2138            r#"
2139            fn a() {
2140                b();
2141                "
2142                  c
2143                    d
2144                      e
2145                "
2146            }
2147            "#
2148            .unindent()
2149        );
2150
2151        // Grouping is disabled in tests, so we need 2 undos
2152        buffer.undo(cx); // Undo the auto-indent
2153        buffer.undo(cx); // Undo the original edit
2154
2155        // Insert the block at a deeper indent level. The entire block is outdented.
2156        buffer.edit([(Point::new(2, 0)..Point::new(2, 0), "        ")], None, cx);
2157        buffer.edit(
2158            [(Point::new(2, 8)..Point::new(2, 8), inserted_text)],
2159            Some(AutoindentMode::Block {
2160                original_indent_columns,
2161            }),
2162            cx,
2163        );
2164        assert_eq!(
2165            buffer.text(),
2166            r#"
2167            fn a() {
2168                b();
2169                "
2170                  c
2171                    d
2172                      e
2173                "
2174            }
2175            "#
2176            .unindent()
2177        );
2178
2179        buffer
2180    });
2181}
2182
2183#[gpui::test]
2184fn test_autoindent_block_mode_with_newline(cx: &mut App) {
2185    init_settings(cx, |_| {});
2186
2187    cx.new(|cx| {
2188        let text = r#"
2189            fn a() {
2190                b();
2191            }
2192        "#
2193        .unindent();
2194        let mut buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
2195
2196        // First line contains just '\n', it's indentation is stored in "original_indent_columns"
2197        let original_indent_columns = vec![Some(4)];
2198        let inserted_text = r#"
2199
2200                c();
2201                    d();
2202                        e();
2203        "#
2204        .unindent();
2205        buffer.edit(
2206            [(Point::new(2, 0)..Point::new(2, 0), inserted_text)],
2207            Some(AutoindentMode::Block {
2208                original_indent_columns,
2209            }),
2210            cx,
2211        );
2212
2213        // While making edit, we ignore first line as it only contains '\n'
2214        // hence second line indent is used to calculate delta
2215        assert_eq!(
2216            buffer.text(),
2217            r#"
2218            fn a() {
2219                b();
2220
2221                c();
2222                    d();
2223                        e();
2224            }
2225            "#
2226            .unindent()
2227        );
2228
2229        buffer
2230    });
2231}
2232
2233#[gpui::test]
2234fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut App) {
2235    init_settings(cx, |_| {});
2236
2237    cx.new(|cx| {
2238        let text = r#"
2239            fn a() {
2240                if b() {
2241
2242                }
2243            }
2244        "#
2245        .unindent();
2246        let mut buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
2247
2248        // The original indent columns are not known, so this text is
2249        // auto-indented in a block as if the first line was copied in
2250        // its entirety.
2251        let original_indent_columns = Vec::new();
2252        let inserted_text = "    c\n        .d()\n        .e();";
2253
2254        // Insert the block at column zero. The entire block is indented
2255        // so that the first line matches the previous line's indentation.
2256        buffer.edit(
2257            [(Point::new(2, 0)..Point::new(2, 0), inserted_text)],
2258            Some(AutoindentMode::Block {
2259                original_indent_columns,
2260            }),
2261            cx,
2262        );
2263        assert_eq!(
2264            buffer.text(),
2265            r#"
2266            fn a() {
2267                if b() {
2268                    c
2269                        .d()
2270                        .e();
2271                }
2272            }
2273            "#
2274            .unindent()
2275        );
2276
2277        // Grouping is disabled in tests, so we need 2 undos
2278        buffer.undo(cx); // Undo the auto-indent
2279        buffer.undo(cx); // Undo the original edit
2280
2281        // Insert the block at a deeper indent level. The entire block is outdented.
2282        buffer.edit(
2283            [(Point::new(2, 0)..Point::new(2, 0), " ".repeat(12))],
2284            None,
2285            cx,
2286        );
2287        buffer.edit(
2288            [(Point::new(2, 12)..Point::new(2, 12), inserted_text)],
2289            Some(AutoindentMode::Block {
2290                original_indent_columns: Vec::new(),
2291            }),
2292            cx,
2293        );
2294        assert_eq!(
2295            buffer.text(),
2296            r#"
2297            fn a() {
2298                if b() {
2299                    c
2300                        .d()
2301                        .e();
2302                }
2303            }
2304            "#
2305            .unindent()
2306        );
2307
2308        buffer
2309    });
2310}
2311
2312#[gpui::test]
2313fn test_autoindent_block_mode_with_hard_tabs(cx: &mut App) {
2314    init_settings(cx, |settings| {
2315        settings.defaults.hard_tabs = Some(true);
2316    });
2317
2318    cx.new(|cx| {
2319        let text = "fn a() {\n\tb();\n}";
2320        let mut buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
2321
2322        // Insert a block whose indentation mixes tab-indented lines with
2323        // lines that have no leading whitespace, like a snippet body.
2324        let inserted_text = "if c {\n\td();\n}\n";
2325        buffer.edit(
2326            [(Point::new(2, 0)..Point::new(2, 0), inserted_text)],
2327            Some(AutoindentMode::Block {
2328                original_indent_columns: Vec::new(),
2329            }),
2330            cx,
2331        );
2332
2333        // All of the block's lines are indented, including the ones that
2334        // originally had no indentation.
2335        assert_eq!(
2336            buffer.text(),
2337            "fn a() {\n\tb();\n\tif c {\n\t\td();\n\t}\n}"
2338        );
2339
2340        buffer
2341    });
2342}
2343
2344#[gpui::test]
2345fn test_autoindent_block_mode_multiple_adjacent_ranges(cx: &mut App) {
2346    init_settings(cx, |_| {});
2347
2348    cx.new(|cx| {
2349        let (text, ranges_to_replace) = marked_text_ranges(
2350            &"
2351            mod numbers {
2352                «fn one() {
2353                    1
2354                }
2355            »
2356                «fn two() {
2357                    2
2358                }
2359            »
2360                «fn three() {
2361                    3
2362                }
2363            »}
2364            "
2365            .unindent(),
2366            false,
2367        );
2368
2369        let mut buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
2370
2371        buffer.edit(
2372            [
2373                (ranges_to_replace[0].clone(), "fn one() {\n    101\n}\n"),
2374                (ranges_to_replace[1].clone(), "fn two() {\n    102\n}\n"),
2375                (ranges_to_replace[2].clone(), "fn three() {\n    103\n}\n"),
2376            ],
2377            Some(AutoindentMode::Block {
2378                original_indent_columns: vec![Some(0), Some(0), Some(0)],
2379            }),
2380            cx,
2381        );
2382
2383        assert_eq!(
2384            buffer.text(),
2385            "
2386            mod numbers {
2387                fn one() {
2388                    101
2389                }
2390
2391                fn two() {
2392                    102
2393                }
2394
2395                fn three() {
2396                    103
2397                }
2398            }
2399            "
2400            .unindent()
2401        );
2402
2403        buffer
2404    });
2405}
2406
2407#[gpui::test]
2408fn test_autoindent_language_without_indents_query(cx: &mut App) {
2409    init_settings(cx, |_| {});
2410
2411    cx.new(|cx| {
2412        let text = "
2413            * one
2414                - a
2415                - b
2416            * two
2417        "
2418        .unindent();
2419
2420        let mut buffer = Buffer::local(text, cx).with_language(
2421            Arc::new(Language::new(
2422                LanguageConfig {
2423                    name: "Markdown".into(),
2424                    auto_indent_using_last_non_empty_line: false,
2425                    ..Default::default()
2426                },
2427                Some(tree_sitter_json::LANGUAGE.into()),
2428            )),
2429            cx,
2430        );
2431        buffer.edit(
2432            [(Point::new(3, 0)..Point::new(3, 0), "\n")],
2433            Some(AutoindentMode::EachLine),
2434            cx,
2435        );
2436        assert_eq!(
2437            buffer.text(),
2438            "
2439            * one
2440                - a
2441                - b
2442
2443            * two
2444            "
2445            .unindent()
2446        );
2447        buffer
2448    });
2449}
2450
2451#[gpui::test]
2452fn test_autoindent_with_injected_languages(cx: &mut App) {
2453    init_settings(cx, |settings| {
2454        settings.languages.0.extend([
2455            (
2456                "HTML".into(),
2457                LanguageSettingsContent {
2458                    tab_size: Some(2.try_into().unwrap()),
2459                    ..Default::default()
2460                },
2461            ),
2462            (
2463                "JavaScript".into(),
2464                LanguageSettingsContent {
2465                    tab_size: Some(8.try_into().unwrap()),
2466                    ..Default::default()
2467                },
2468            ),
2469        ])
2470    });
2471
2472    let html_language = Arc::new(html_lang());
2473
2474    let javascript_language = Arc::new(javascript_lang());
2475
2476    let language_registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
2477    language_registry.add(html_language.clone());
2478    language_registry.add(javascript_language);
2479
2480    cx.new(|cx| {
2481        let (text, ranges) = marked_text_ranges(
2482            &"
2483                <div>ˇ
2484                </div>
2485                <script>
2486                    init({ˇ
2487                    })
2488                </script>
2489                <span>ˇ
2490                </span>
2491            "
2492            .unindent(),
2493            false,
2494        );
2495
2496        let mut buffer = Buffer::local(text, cx);
2497        buffer.set_language_registry(language_registry);
2498        buffer.set_language(Some(html_language), cx);
2499        buffer.edit(
2500            ranges.into_iter().map(|range| (range, "\na")),
2501            Some(AutoindentMode::EachLine),
2502            cx,
2503        );
2504        assert_eq!(
2505            buffer.text(),
2506            "
2507                <div>
2508                  a
2509                </div>
2510                <script>
2511                    init({
2512                            a
2513                    })
2514                </script>
2515                <span>
2516                  a
2517                </span>
2518            "
2519            .unindent()
2520        );
2521        buffer
2522    });
2523}
2524
2525#[gpui::test]
2526fn test_autoindent_query_with_outdent_captures(cx: &mut App) {
2527    init_settings(cx, |settings| {
2528        settings.defaults.tab_size = Some(2.try_into().unwrap());
2529    });
2530
2531    cx.new(|cx| {
2532        let mut buffer = Buffer::local("", cx).with_language(Arc::new(ruby_lang()), cx);
2533
2534        let text = r#"
2535            class C
2536            def a(b, c)
2537            puts b
2538            puts c
2539            rescue
2540            puts "errored"
2541            exit 1
2542            end
2543            end
2544        "#
2545        .unindent();
2546
2547        buffer.edit([(0..0, text)], Some(AutoindentMode::EachLine), cx);
2548
2549        assert_eq!(
2550            buffer.text(),
2551            r#"
2552                class C
2553                  def a(b, c)
2554                    puts b
2555                    puts c
2556                  rescue
2557                    puts "errored"
2558                    exit 1
2559                  end
2560                end
2561            "#
2562            .unindent()
2563        );
2564
2565        buffer
2566    });
2567}
2568
2569#[gpui::test]
2570async fn test_async_autoindents_preserve_preview(cx: &mut TestAppContext) {
2571    cx.update(|cx| init_settings(cx, |_| {}));
2572
2573    // First we insert some newlines to request an auto-indent (asynchronously).
2574    // Then we request that a preview tab be preserved for the new version, even though it's edited.
2575    let buffer = cx.new(|cx| {
2576        let text = "fn a() {}";
2577        let mut buffer = Buffer::local(text, cx).with_language(rust_lang(), cx);
2578
2579        // This causes autoindent to be async.
2580        buffer.set_sync_parse_timeout(None);
2581
2582        buffer.edit([(8..8, "\n\n")], Some(AutoindentMode::EachLine), cx);
2583        buffer.refresh_preview();
2584
2585        // Synchronously, we haven't auto-indented and we're still preserving the preview.
2586        assert_eq!(buffer.text(), "fn a() {\n\n}");
2587        assert!(buffer.preserve_preview());
2588        buffer
2589    });
2590
2591    // Now let the autoindent finish
2592    cx.executor().run_until_parked();
2593
2594    // The auto-indent applied, but didn't dismiss our preview
2595    buffer.update(cx, |buffer, cx| {
2596        assert_eq!(buffer.text(), "fn a() {\n    \n}");
2597        assert!(buffer.preserve_preview());
2598
2599        // Edit inserting another line. It will autoindent async.
2600        // Then refresh the preview version.
2601        buffer.edit(
2602            [(Point::new(1, 4)..Point::new(1, 4), "\n")],
2603            Some(AutoindentMode::EachLine),
2604            cx,
2605        );
2606        buffer.refresh_preview();
2607        assert_eq!(buffer.text(), "fn a() {\n    \n\n}");
2608        assert!(buffer.preserve_preview());
2609
2610        // Then perform another edit, this time without refreshing the preview version.
2611        buffer.edit([(Point::new(1, 4)..Point::new(1, 4), "x")], None, cx);
2612        // This causes the preview to not be preserved.
2613        assert!(!buffer.preserve_preview());
2614    });
2615
2616    // Let the async autoindent from the first edit finish.
2617    cx.executor().run_until_parked();
2618
2619    // The autoindent applies, but it shouldn't restore the preview status because we had an edit in the meantime.
2620    buffer.update(cx, |buffer, _| {
2621        assert_eq!(buffer.text(), "fn a() {\n    x\n    \n}");
2622        assert!(!buffer.preserve_preview());
2623    });
2624}
2625
2626#[gpui::test]
2627fn test_insert_empty_line(cx: &mut App) {
2628    init_settings(cx, |_| {});
2629
2630    // Insert empty line at the beginning, requesting an empty line above
2631    cx.new(|cx| {
2632        let mut buffer = Buffer::local("abc\ndef\nghi", cx);
2633        let point = buffer.insert_empty_line(Point::new(0, 0), true, false, cx);
2634        assert_eq!(buffer.text(), "\nabc\ndef\nghi");
2635        assert_eq!(point, Point::new(0, 0));
2636        buffer
2637    });
2638
2639    // Insert empty line at the beginning, requesting an empty line above and below
2640    cx.new(|cx| {
2641        let mut buffer = Buffer::local("abc\ndef\nghi", cx);
2642        let point = buffer.insert_empty_line(Point::new(0, 0), true, true, cx);
2643        assert_eq!(buffer.text(), "\n\nabc\ndef\nghi");
2644        assert_eq!(point, Point::new(0, 0));
2645        buffer
2646    });
2647
2648    // Insert empty line at the start of a line, requesting empty lines above and below
2649    cx.new(|cx| {
2650        let mut buffer = Buffer::local("abc\ndef\nghi", cx);
2651        let point = buffer.insert_empty_line(Point::new(2, 0), true, true, cx);
2652        assert_eq!(buffer.text(), "abc\ndef\n\n\n\nghi");
2653        assert_eq!(point, Point::new(3, 0));
2654        buffer
2655    });
2656
2657    // Insert empty line in the middle of a line, requesting empty lines above and below
2658    cx.new(|cx| {
2659        let mut buffer = Buffer::local("abc\ndefghi\njkl", cx);
2660        let point = buffer.insert_empty_line(Point::new(1, 3), true, true, cx);
2661        assert_eq!(buffer.text(), "abc\ndef\n\n\n\nghi\njkl");
2662        assert_eq!(point, Point::new(3, 0));
2663        buffer
2664    });
2665
2666    // Insert empty line in the middle of a line, requesting empty line above only
2667    cx.new(|cx| {
2668        let mut buffer = Buffer::local("abc\ndefghi\njkl", cx);
2669        let point = buffer.insert_empty_line(Point::new(1, 3), true, false, cx);
2670        assert_eq!(buffer.text(), "abc\ndef\n\n\nghi\njkl");
2671        assert_eq!(point, Point::new(3, 0));
2672        buffer
2673    });
2674
2675    // Insert empty line in the middle of a line, requesting empty line below only
2676    cx.new(|cx| {
2677        let mut buffer = Buffer::local("abc\ndefghi\njkl", cx);
2678        let point = buffer.insert_empty_line(Point::new(1, 3), false, true, cx);
2679        assert_eq!(buffer.text(), "abc\ndef\n\n\nghi\njkl");
2680        assert_eq!(point, Point::new(2, 0));
2681        buffer
2682    });
2683
2684    // Insert empty line at the end, requesting empty lines above and below
2685    cx.new(|cx| {
2686        let mut buffer = Buffer::local("abc\ndef\nghi", cx);
2687        let point = buffer.insert_empty_line(Point::new(2, 3), true, true, cx);
2688        assert_eq!(buffer.text(), "abc\ndef\nghi\n\n\n");
2689        assert_eq!(point, Point::new(4, 0));
2690        buffer
2691    });
2692
2693    // Insert empty line at the end, requesting empty line above only
2694    cx.new(|cx| {
2695        let mut buffer = Buffer::local("abc\ndef\nghi", cx);
2696        let point = buffer.insert_empty_line(Point::new(2, 3), true, false, cx);
2697        assert_eq!(buffer.text(), "abc\ndef\nghi\n\n");
2698        assert_eq!(point, Point::new(4, 0));
2699        buffer
2700    });
2701
2702    // Insert empty line at the end, requesting empty line below only
2703    cx.new(|cx| {
2704        let mut buffer = Buffer::local("abc\ndef\nghi", cx);
2705        let point = buffer.insert_empty_line(Point::new(2, 3), false, true, cx);
2706        assert_eq!(buffer.text(), "abc\ndef\nghi\n\n");
2707        assert_eq!(point, Point::new(3, 0));
2708        buffer
2709    });
2710}
2711
2712#[gpui::test]
2713fn test_language_scope_at_with_javascript(cx: &mut App) {
2714    init_settings(cx, |_| {});
2715
2716    cx.new(|cx| {
2717        let language = Language::new(
2718            LanguageConfig {
2719                name: "JavaScript".into(),
2720                line_comments: vec!["// ".into()],
2721                block_comment: Some(BlockCommentConfig {
2722                    start: "/*".into(),
2723                    end: "*/".into(),
2724                    prefix: "* ".into(),
2725                    tab_size: 1,
2726                }),
2727                brackets: BracketPairConfig {
2728                    pairs: vec![
2729                        BracketPair {
2730                            start: "{".into(),
2731                            end: "}".into(),
2732                            close: true,
2733                            surround: true,
2734                            newline: false,
2735                        },
2736                        BracketPair {
2737                            start: "'".into(),
2738                            end: "'".into(),
2739                            close: true,
2740                            surround: true,
2741                            newline: false,
2742                        },
2743                    ],
2744                    disabled_scopes_by_bracket_ix: vec![
2745                        Vec::new(),                              //
2746                        vec!["string".into(), "comment".into()], // single quotes disabled
2747                    ],
2748                },
2749                overrides: [(
2750                    "element".into(),
2751                    LanguageConfigOverride {
2752                        line_comments: Override::Remove { remove: true },
2753                        block_comment: Override::Set(BlockCommentConfig {
2754                            start: "{/*".into(),
2755                            prefix: "".into(),
2756                            end: "*/}".into(),
2757                            tab_size: 0,
2758                        }),
2759                        ..Default::default()
2760                    },
2761                )]
2762                .into_iter()
2763                .collect(),
2764                ..Default::default()
2765            },
2766            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
2767        )
2768        .with_override_query(
2769            r#"
2770                (jsx_element) @element
2771                (string) @string
2772                (comment) @comment.inclusive
2773                [
2774                    (jsx_opening_element)
2775                    (jsx_closing_element)
2776                    (jsx_expression)
2777                ] @default
2778            "#,
2779        )
2780        .unwrap();
2781
2782        let text = r#"
2783            a["b"] = <C d="e">
2784                <F></F>
2785                { g() }
2786            </C>; // a comment
2787        "#
2788        .unindent();
2789
2790        let buffer = Buffer::local(&text, cx).with_language(Arc::new(language), cx);
2791        let snapshot = buffer.snapshot();
2792
2793        let config = snapshot.language_scope_at(0).unwrap();
2794        assert_eq!(config.line_comment_prefixes(), &[Arc::from("// ")]);
2795        assert_eq!(
2796            config.block_comment(),
2797            Some(&BlockCommentConfig {
2798                start: "/*".into(),
2799                prefix: "* ".into(),
2800                end: "*/".into(),
2801                tab_size: 1,
2802            })
2803        );
2804
2805        // Both bracket pairs are enabled
2806        assert_eq!(
2807            config.brackets().map(|e| e.1).collect::<Vec<_>>(),
2808            &[true, true]
2809        );
2810
2811        let comment_config = snapshot
2812            .language_scope_at(text.find("comment").unwrap() + "comment".len())
2813            .unwrap();
2814        assert_eq!(
2815            comment_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
2816            &[true, false]
2817        );
2818
2819        let string_config = snapshot
2820            .language_scope_at(text.find("b\"").unwrap())
2821            .unwrap();
2822        assert_eq!(string_config.line_comment_prefixes(), &[Arc::from("// ")]);
2823        assert_eq!(
2824            string_config.block_comment(),
2825            Some(&BlockCommentConfig {
2826                start: "/*".into(),
2827                prefix: "* ".into(),
2828                end: "*/".into(),
2829                tab_size: 1,
2830            })
2831        );
2832        // Second bracket pair is disabled
2833        assert_eq!(
2834            string_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
2835            &[true, false]
2836        );
2837
2838        // In between JSX tags: use the `element` override.
2839        let element_config = snapshot
2840            .language_scope_at(text.find("<F>").unwrap())
2841            .unwrap();
2842        // TODO nested blocks after newlines are captured with all whitespaces
2843        // https://github.com/tree-sitter/tree-sitter-typescript/issues/306
2844        // assert_eq!(element_config.line_comment_prefixes(), &[]);
2845        // assert_eq!(
2846        //     element_config.block_comment_delimiters(),
2847        //     Some((&"{/*".into(), &"*/}".into()))
2848        // );
2849        assert_eq!(
2850            element_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
2851            &[true, true]
2852        );
2853
2854        // Within a JSX tag: use the default config.
2855        let tag_config = snapshot
2856            .language_scope_at(text.find(" d=").unwrap() + 1)
2857            .unwrap();
2858        assert_eq!(tag_config.line_comment_prefixes(), &[Arc::from("// ")]);
2859        assert_eq!(
2860            tag_config.block_comment(),
2861            Some(&BlockCommentConfig {
2862                start: "/*".into(),
2863                prefix: "* ".into(),
2864                end: "*/".into(),
2865                tab_size: 1,
2866            })
2867        );
2868        assert_eq!(
2869            tag_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
2870            &[true, true]
2871        );
2872
2873        // In a JSX expression: use the default config.
2874        let expression_in_element_config = snapshot
2875            .language_scope_at(text.find('{').unwrap() + 1)
2876            .unwrap();
2877        assert_eq!(
2878            expression_in_element_config.line_comment_prefixes(),
2879            &[Arc::from("// ")]
2880        );
2881        assert_eq!(
2882            expression_in_element_config.block_comment(),
2883            Some(&BlockCommentConfig {
2884                start: "/*".into(),
2885                prefix: "* ".into(),
2886                end: "*/".into(),
2887                tab_size: 1,
2888            })
2889        );
2890        assert_eq!(
2891            expression_in_element_config
2892                .brackets()
2893                .map(|e| e.1)
2894                .collect::<Vec<_>>(),
2895            &[true, true]
2896        );
2897
2898        buffer
2899    });
2900}
2901
2902#[gpui::test]
2903fn test_language_scope_at_with_rust(cx: &mut App) {
2904    init_settings(cx, |_| {});
2905
2906    cx.new(|cx| {
2907        let language = Language::new(
2908            LanguageConfig {
2909                name: "Rust".into(),
2910                brackets: BracketPairConfig {
2911                    pairs: vec![
2912                        BracketPair {
2913                            start: "{".into(),
2914                            end: "}".into(),
2915                            close: true,
2916                            surround: true,
2917                            newline: false,
2918                        },
2919                        BracketPair {
2920                            start: "'".into(),
2921                            end: "'".into(),
2922                            close: true,
2923                            surround: true,
2924                            newline: false,
2925                        },
2926                    ],
2927                    disabled_scopes_by_bracket_ix: vec![
2928                        Vec::new(), //
2929                        vec!["string".into()],
2930                    ],
2931                },
2932                ..Default::default()
2933            },
2934            Some(tree_sitter_rust::LANGUAGE.into()),
2935        )
2936        .with_override_query(
2937            r#"
2938                (string_literal) @string
2939            "#,
2940        )
2941        .unwrap();
2942
2943        let text = r#"
2944            const S: &'static str = "hello";
2945        "#
2946        .unindent();
2947
2948        let buffer = Buffer::local(text.clone(), cx).with_language(Arc::new(language), cx);
2949        let snapshot = buffer.snapshot();
2950
2951        // By default, all brackets are enabled
2952        let config = snapshot.language_scope_at(0).unwrap();
2953        assert_eq!(
2954            config.brackets().map(|e| e.1).collect::<Vec<_>>(),
2955            &[true, true]
2956        );
2957
2958        // Within a string, the quotation brackets are disabled.
2959        let string_config = snapshot
2960            .language_scope_at(text.find("ello").unwrap())
2961            .unwrap();
2962        assert_eq!(
2963            string_config.brackets().map(|e| e.1).collect::<Vec<_>>(),
2964            &[true, false]
2965        );
2966
2967        buffer
2968    });
2969}
2970
2971#[gpui::test]
2972fn test_language_scope_at_with_combined_injections(cx: &mut App) {
2973    init_settings(cx, |_| {});
2974
2975    cx.new(|cx| {
2976        let text = r#"
2977            <ol>
2978            <% people.each do |person| %>
2979                <li>
2980                    <%= person.name %>
2981                </li>
2982            <% end %>
2983            </ol>
2984        "#
2985        .unindent();
2986
2987        let language_registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
2988        language_registry.add(Arc::new(ruby_lang()));
2989        language_registry.add(Arc::new(html_lang()));
2990        language_registry.add(Arc::new(erb_lang()));
2991
2992        let mut buffer = Buffer::local(text, cx);
2993        buffer.set_language_registry(language_registry.clone());
2994        let language = language_registry
2995            .language_for_name("HTML+ERB")
2996            .now_or_never()
2997            .and_then(Result::ok);
2998        buffer.set_language(language, cx);
2999
3000        let snapshot = buffer.snapshot();
3001        let html_config = snapshot.language_scope_at(Point::new(2, 4)).unwrap();
3002        assert_eq!(html_config.line_comment_prefixes(), &[]);
3003        assert_eq!(
3004            html_config.block_comment(),
3005            Some(&BlockCommentConfig {
3006                start: "<!--".into(),
3007                end: "-->".into(),
3008                prefix: "".into(),
3009                tab_size: 0,
3010            })
3011        );
3012
3013        let ruby_config = snapshot.language_scope_at(Point::new(3, 12)).unwrap();
3014        assert_eq!(ruby_config.line_comment_prefixes(), &[Arc::from("# ")]);
3015        assert_eq!(ruby_config.block_comment(), None);
3016
3017        buffer
3018    });
3019}
3020
3021#[gpui::test]
3022fn test_language_at_with_hidden_languages(cx: &mut App) {
3023    init_settings(cx, |_| {});
3024
3025    cx.new(|cx| {
3026        let text = r#"
3027            this is an *emphasized* word.
3028        "#
3029        .unindent();
3030
3031        let language_registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
3032        language_registry.add(markdown_lang());
3033        language_registry.add(Arc::new(markdown_inline_lang()));
3034
3035        let mut buffer = Buffer::local(text, cx);
3036        buffer.set_language_registry(language_registry.clone());
3037        buffer.set_language(
3038            language_registry
3039                .language_for_name("Markdown")
3040                .now_or_never()
3041                .unwrap()
3042                .ok(),
3043            cx,
3044        );
3045
3046        let snapshot = buffer.snapshot();
3047
3048        for point in [Point::new(0, 4), Point::new(0, 16)] {
3049            let config = snapshot.language_scope_at(point).unwrap();
3050            assert_eq!(config.language_name(), "Markdown");
3051
3052            let language = snapshot.language_at(point).unwrap();
3053            assert_eq!(language.name().as_ref(), "Markdown");
3054        }
3055
3056        buffer
3057    });
3058}
3059
3060#[gpui::test]
3061fn test_language_at_for_markdown_code_block(cx: &mut App) {
3062    init_settings(cx, |_| {});
3063
3064    cx.new(|cx| {
3065        let text = r#"
3066            ```rs
3067            let a = 2;
3068            // let b = 3;
3069            ```
3070        "#
3071        .unindent();
3072
3073        let language_registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
3074        language_registry.add(markdown_lang());
3075        language_registry.add(Arc::new(markdown_inline_lang()));
3076        language_registry.add(rust_lang());
3077
3078        let mut buffer = Buffer::local(text, cx);
3079        buffer.set_language_registry(language_registry.clone());
3080        buffer.set_language(
3081            language_registry
3082                .language_for_name("Markdown")
3083                .now_or_never()
3084                .unwrap()
3085                .ok(),
3086            cx,
3087        );
3088
3089        let snapshot = buffer.snapshot();
3090
3091        // Test points in the code line
3092        for point in [Point::new(1, 4), Point::new(1, 6)] {
3093            let config = snapshot.language_scope_at(point).unwrap();
3094            assert_eq!(config.language_name(), "Rust");
3095
3096            let language = snapshot.language_at(point).unwrap();
3097            assert_eq!(language.name().as_ref(), "Rust");
3098        }
3099
3100        // Test points in the comment line to verify it's still detected as Rust
3101        for point in [Point::new(2, 4), Point::new(2, 6)] {
3102            let config = snapshot.language_scope_at(point).unwrap();
3103            assert_eq!(config.language_name(), "Rust");
3104
3105            let language = snapshot.language_at(point).unwrap();
3106            assert_eq!(language.name().as_ref(), "Rust");
3107        }
3108
3109        buffer
3110    });
3111}
3112
3113#[gpui::test]
3114async fn test_markdown_inline_html_highlighting(cx: &mut TestAppContext) {
3115    let markdown_language = markdown_lang();
3116    let markdown_inline_language = Arc::new(
3117        Language::new(
3118            LanguageConfig {
3119                name: "markdown-inline".into(),
3120                grammar: Some("markdown-inline".into()),
3121                ..Default::default()
3122            },
3123            Some(tree_sitter_md::INLINE_LANGUAGE.into()),
3124        )
3125        .with_highlights_query(include_str!(
3126            "../../grammars/src/markdown-inline/highlights.scm"
3127        ))
3128        .unwrap()
3129        .with_injection_query(include_str!(
3130            "../../grammars/src/markdown-inline/injections.scm"
3131        ))
3132        .unwrap(),
3133    );
3134    let html_language = Arc::new(
3135        Language::new(
3136            LanguageConfig {
3137                name: "HTML".into(),
3138                ..Default::default()
3139            },
3140            Some(tree_sitter_html::LANGUAGE.into()),
3141        )
3142        .with_highlights_query("(comment) @comment (tag_name) @tag")
3143        .unwrap(),
3144    );
3145    let syntax_theme = SyntaxTheme::new([
3146        ("comment".to_string(), gpui::rgba(0xffffffff).into()),
3147        ("tag".to_string(), gpui::rgba(0xff0000ff).into()),
3148    ]);
3149    markdown_language.set_theme(&syntax_theme);
3150    markdown_inline_language.set_theme(&syntax_theme);
3151    html_language.set_theme(&syntax_theme);
3152    let language_registry = Arc::new(LanguageRegistry::test(cx.background_executor.clone()));
3153    language_registry.add(markdown_language.clone());
3154    language_registry.add(markdown_inline_language);
3155    language_registry.add(html_language);
3156
3157    let text = "<!--Annotation from the start is OK-->\n\n\
3158        Annotation in the middle <!--is rendered badly.-->\n\n\
3159        An inline comment can span <!--multiple\nlines--> within a paragraph.\n\n\
3160        Ordinary inline HTML: <em>emphasized</em>.";
3161    let buffer = cx.new(|cx| {
3162        let mut buffer = Buffer::local(text, cx);
3163        buffer.set_language_registry(language_registry);
3164        buffer.set_language(Some(markdown_language), cx);
3165        buffer
3166    });
3167
3168    cx.run_until_parked();
3169
3170    buffer.read_with(cx, |buffer, _cx| {
3171        let snapshot = buffer.snapshot();
3172        let highlighted_text = |capture_name: &str| {
3173            let highlight_id = syntax_theme
3174                .highlight_id(capture_name)
3175                .map(HighlightId::new);
3176            assert!(highlight_id.is_some(), "{capture_name} not in test theme");
3177            let mut runs: Vec<String> = Vec::new();
3178            let mut previous_chunk_matched = false;
3179            let chunks = snapshot.chunks(
3180                0..snapshot.len(),
3181                LanguageAwareStyling {
3182                    tree_sitter: true,
3183                    diagnostics: false,
3184                },
3185            );
3186            for chunk in chunks {
3187                let chunk_matches = chunk.syntax_highlight_id == highlight_id;
3188                if chunk_matches {
3189                    match runs.last_mut() {
3190                        Some(last_run) if previous_chunk_matched => last_run.push_str(chunk.text),
3191                        _ => runs.push(chunk.text.to_string()),
3192                    }
3193                }
3194                previous_chunk_matched = chunk_matches;
3195            }
3196            runs
3197        };
3198
3199        assert_eq!(
3200            highlighted_text("comment"),
3201            vec![
3202                "<!--Annotation from the start is OK-->",
3203                "<!--is rendered badly.-->",
3204                "<!--multiple\nlines-->",
3205            ]
3206        );
3207        assert_eq!(highlighted_text("tag"), vec!["em", "em"]);
3208    });
3209}
3210
3211#[gpui::test]
3212fn test_syntax_layer_at_for_combined_injections(cx: &mut App) {
3213    init_settings(cx, |_| {});
3214
3215    cx.new(|cx| {
3216        // ERB template with HTML and Ruby content
3217        let text = r#"
3218<div>Hello</div>
3219<%= link_to "Click", url %>
3220<p>World</p>
3221        "#
3222        .unindent();
3223
3224        let language_registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
3225        language_registry.add(Arc::new(erb_lang()));
3226        language_registry.add(Arc::new(html_lang()));
3227        language_registry.add(Arc::new(ruby_lang()));
3228
3229        let mut buffer = Buffer::local(text, cx);
3230        buffer.set_language_registry(language_registry.clone());
3231        let language = language_registry
3232            .language_for_name("HTML+ERB")
3233            .now_or_never()
3234            .and_then(Result::ok);
3235        buffer.set_language(language, cx);
3236
3237        let snapshot = buffer.snapshot();
3238
3239        // Test language_at for HTML content (line 0: "<div>Hello</div>")
3240        let html_point = Point::new(0, 4);
3241        let language = snapshot.language_at(html_point).unwrap();
3242        assert_eq!(
3243            language.name().as_ref(),
3244            "HTML",
3245            "Expected HTML at {:?}, got {}",
3246            html_point,
3247            language.name()
3248        );
3249
3250        // Test language_at for Ruby code (line 1: "<%= link_to ... %>")
3251        let ruby_point = Point::new(1, 6);
3252        let language = snapshot.language_at(ruby_point).unwrap();
3253        assert_eq!(
3254            language.name().as_ref(),
3255            "Ruby",
3256            "Expected Ruby at {:?}, got {}",
3257            ruby_point,
3258            language.name()
3259        );
3260
3261        // Test language_at for HTML after Ruby (line 2: "<p>World</p>")
3262        let html_after_ruby = Point::new(2, 2);
3263        let language = snapshot.language_at(html_after_ruby).unwrap();
3264        assert_eq!(
3265            language.name().as_ref(),
3266            "HTML",
3267            "Expected HTML at {:?}, got {}",
3268            html_after_ruby,
3269            language.name()
3270        );
3271
3272        buffer
3273    });
3274}
3275
3276#[gpui::test]
3277fn test_languages_at_for_combined_injections(cx: &mut App) {
3278    init_settings(cx, |_| {});
3279
3280    cx.new(|cx| {
3281        // ERB template with HTML and Ruby content
3282        let text = r#"
3283<div>Hello</div>
3284<%= yield %>
3285<p>World</p>
3286        "#
3287        .unindent();
3288
3289        let language_registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
3290        language_registry.add(Arc::new(erb_lang()));
3291        language_registry.add(Arc::new(html_lang()));
3292        language_registry.add(Arc::new(ruby_lang()));
3293
3294        let mut buffer = Buffer::local(text, cx);
3295        buffer.set_language_registry(language_registry.clone());
3296        buffer.set_language(
3297            language_registry
3298                .language_for_name("HTML+ERB")
3299                .now_or_never()
3300                .unwrap()
3301                .ok(),
3302            cx,
3303        );
3304
3305        // Test languages_at for HTML content - should NOT include Ruby
3306        let html_point = Point::new(0, 4);
3307        let languages = buffer.languages_at(html_point);
3308        let language_names: Vec<_> = languages.iter().map(|language| language.name()).collect();
3309        assert!(
3310            language_names
3311                .iter()
3312                .any(|language_name| language_name.as_ref() == "HTML"),
3313            "Expected HTML in languages at {:?}, got {:?}",
3314            html_point,
3315            language_names
3316        );
3317        assert!(
3318            !language_names
3319                .iter()
3320                .any(|language_name| language_name.as_ref() == "Ruby"),
3321            "Did not expect Ruby in languages at {:?}, got {:?}",
3322            html_point,
3323            language_names
3324        );
3325
3326        // Test languages_at for Ruby code - should NOT include HTML
3327        let ruby_point = Point::new(1, 6);
3328        let languages = buffer.languages_at(ruby_point);
3329        let language_names: Vec<_> = languages.iter().map(|language| language.name()).collect();
3330        assert!(
3331            language_names
3332                .iter()
3333                .any(|language_name| language_name.as_ref() == "Ruby"),
3334            "Expected Ruby in languages at {:?}, got {:?}",
3335            ruby_point,
3336            language_names
3337        );
3338        assert!(
3339            !language_names
3340                .iter()
3341                .any(|language_name| language_name.as_ref() == "HTML"),
3342            "Did not expect HTML in languages at {:?}, got {:?}",
3343            ruby_point,
3344            language_names
3345        );
3346
3347        buffer
3348    });
3349}
3350
3351#[gpui::test]
3352fn test_serialization(cx: &mut gpui::App) {
3353    let mut now = Instant::now();
3354
3355    let buffer1 = cx.new(|cx| {
3356        let mut buffer = Buffer::local("abc", cx);
3357        buffer.edit([(3..3, "D")], None, cx);
3358
3359        now += Duration::from_secs(1);
3360        buffer.start_transaction_at(now);
3361        buffer.edit([(4..4, "E")], None, cx);
3362        buffer.end_transaction_at(now, cx);
3363        assert_eq!(buffer.text(), "abcDE");
3364
3365        buffer.undo(cx);
3366        assert_eq!(buffer.text(), "abcD");
3367
3368        buffer.edit([(4..4, "F")], None, cx);
3369        assert_eq!(buffer.text(), "abcDF");
3370        buffer
3371    });
3372    assert_eq!(buffer1.read(cx).text(), "abcDF");
3373
3374    let state = buffer1.read(cx).to_proto(cx);
3375    let ops = cx
3376        .foreground_executor()
3377        .block_on(buffer1.read(cx).serialize_ops(None, cx));
3378    let buffer2 = cx.new(|cx| {
3379        let mut buffer =
3380            Buffer::from_proto(ReplicaId::new(1), Capability::ReadWrite, state, None).unwrap();
3381        buffer.apply_ops(
3382            ops.into_iter()
3383                .map(|op| proto::deserialize_operation(op).unwrap()),
3384            cx,
3385        );
3386        buffer
3387    });
3388    assert_eq!(buffer2.read(cx).text(), "abcDF");
3389}
3390
3391#[gpui::test]
3392fn test_branch_and_merge(cx: &mut TestAppContext) {
3393    cx.update(|cx| init_settings(cx, |_| {}));
3394
3395    let base = cx.new(|cx| Buffer::local("one\ntwo\nthree\n", cx));
3396
3397    // Create a remote replica of the base buffer.
3398    let base_replica = cx.new(|cx| {
3399        Buffer::from_proto(
3400            ReplicaId::new(1),
3401            Capability::ReadWrite,
3402            base.read(cx).to_proto(cx),
3403            None,
3404        )
3405        .unwrap()
3406    });
3407    base.update(cx, |_buffer, cx| {
3408        cx.subscribe(&base_replica, |this, _, event, cx| {
3409            if let BufferEvent::Operation {
3410                operation,
3411                is_local: true,
3412            } = event
3413            {
3414                this.apply_ops([operation.clone()], cx);
3415            }
3416        })
3417        .detach();
3418    });
3419
3420    // Create a branch, which initially has the same state as the base buffer.
3421    let branch = base.update(cx, |buffer, cx| buffer.branch(cx));
3422    branch.read_with(cx, |buffer, _| {
3423        assert_eq!(buffer.text(), "one\ntwo\nthree\n");
3424    });
3425
3426    // Edits to the branch are not applied to the base.
3427    branch.update(cx, |buffer, cx| {
3428        buffer.edit(
3429            [
3430                (Point::new(1, 0)..Point::new(1, 0), "1.5\n"),
3431                (Point::new(2, 0)..Point::new(2, 5), "THREE"),
3432            ],
3433            None,
3434            cx,
3435        )
3436    });
3437    branch.read_with(cx, |buffer, cx| {
3438        assert_eq!(base.read(cx).text(), "one\ntwo\nthree\n");
3439        assert_eq!(buffer.text(), "one\n1.5\ntwo\nTHREE\n");
3440    });
3441
3442    // Convert from branch buffer ranges to the corresponding ranges in the
3443    // base buffer.
3444    branch.read_with(cx, |buffer, cx| {
3445        assert_eq!(
3446            buffer.range_to_version(4..7, &base.read(cx).version()),
3447            4..4
3448        );
3449        assert_eq!(
3450            buffer.range_to_version(2..9, &base.read(cx).version()),
3451            2..5
3452        );
3453    });
3454
3455    // Edits to the base are applied to the branch.
3456    base.update(cx, |buffer, cx| {
3457        buffer.edit([(Point::new(0, 0)..Point::new(0, 0), "ZERO\n")], None, cx)
3458    });
3459    branch.read_with(cx, |buffer, cx| {
3460        assert_eq!(base.read(cx).text(), "ZERO\none\ntwo\nthree\n");
3461        assert_eq!(buffer.text(), "ZERO\none\n1.5\ntwo\nTHREE\n");
3462    });
3463
3464    // Edits to any replica of the base are applied to the branch.
3465    base_replica.update(cx, |buffer, cx| {
3466        buffer.edit([(Point::new(2, 0)..Point::new(2, 0), "2.5\n")], None, cx)
3467    });
3468    branch.read_with(cx, |buffer, cx| {
3469        assert_eq!(base.read(cx).text(), "ZERO\none\ntwo\n2.5\nthree\n");
3470        assert_eq!(buffer.text(), "ZERO\none\n1.5\ntwo\n2.5\nTHREE\n");
3471    });
3472
3473    // Merging the branch applies all of its changes to the base.
3474    branch.update(cx, |buffer, cx| {
3475        buffer.merge_into_base(Vec::new(), cx);
3476    });
3477
3478    branch.update(cx, |buffer, cx| {
3479        assert_eq!(base.read(cx).text(), "ZERO\none\n1.5\ntwo\n2.5\nTHREE\n");
3480        assert_eq!(buffer.text(), "ZERO\none\n1.5\ntwo\n2.5\nTHREE\n");
3481    });
3482}
3483
3484#[gpui::test]
3485fn test_merge_into_base(cx: &mut TestAppContext) {
3486    cx.update(|cx| init_settings(cx, |_| {}));
3487
3488    let base = cx.new(|cx| Buffer::local("abcdefghijk", cx));
3489    let branch = base.update(cx, |buffer, cx| buffer.branch(cx));
3490
3491    // Make 3 edits, merge one into the base.
3492    branch.update(cx, |branch, cx| {
3493        branch.edit([(0..3, "ABC"), (7..9, "HI"), (11..11, "LMN")], None, cx);
3494        branch.merge_into_base(vec![5..8], cx);
3495    });
3496
3497    branch.read_with(cx, |branch, _| assert_eq!(branch.text(), "ABCdefgHIjkLMN"));
3498    base.read_with(cx, |base, _| assert_eq!(base.text(), "abcdefgHIjk"));
3499
3500    // Undo the one already-merged edit. Merge that into the base.
3501    branch.update(cx, |branch, cx| {
3502        branch.edit([(7..9, "hi")], None, cx);
3503        branch.merge_into_base(vec![5..8], cx);
3504    });
3505    base.read_with(cx, |base, _| assert_eq!(base.text(), "abcdefghijk"));
3506
3507    // Merge an insertion into the base.
3508    branch.update(cx, |branch, cx| {
3509        branch.merge_into_base(vec![11..11], cx);
3510    });
3511
3512    branch.read_with(cx, |branch, _| assert_eq!(branch.text(), "ABCdefghijkLMN"));
3513    base.read_with(cx, |base, _| assert_eq!(base.text(), "abcdefghijkLMN"));
3514
3515    // Deleted the inserted text and merge that into the base.
3516    branch.update(cx, |branch, cx| {
3517        branch.edit([(11..14, "")], None, cx);
3518        branch.merge_into_base(vec![10..11], cx);
3519    });
3520
3521    base.read_with(cx, |base, _| assert_eq!(base.text(), "abcdefghijk"));
3522}
3523
3524#[gpui::test]
3525fn test_undo_after_merge_into_base(cx: &mut TestAppContext) {
3526    cx.update(|cx| init_settings(cx, |_| {}));
3527
3528    let base = cx.new(|cx| Buffer::local("abcdefghijk", cx));
3529    let branch = base.update(cx, |buffer, cx| buffer.branch(cx));
3530
3531    // Make 2 edits, merge one into the base.
3532    branch.update(cx, |branch, cx| {
3533        branch.edit([(0..3, "ABC"), (7..9, "HI")], None, cx);
3534        branch.merge_into_base(vec![7..7], cx);
3535    });
3536    base.read_with(cx, |base, _| assert_eq!(base.text(), "abcdefgHIjk"));
3537    branch.read_with(cx, |branch, _| assert_eq!(branch.text(), "ABCdefgHIjk"));
3538
3539    // Undo the merge in the base buffer.
3540    base.update(cx, |base, cx| {
3541        base.undo(cx);
3542    });
3543    base.read_with(cx, |base, _| assert_eq!(base.text(), "abcdefghijk"));
3544    branch.read_with(cx, |branch, _| assert_eq!(branch.text(), "ABCdefgHIjk"));
3545
3546    // Merge that operation into the base again.
3547    branch.update(cx, |branch, cx| {
3548        branch.merge_into_base(vec![7..7], cx);
3549    });
3550    base.read_with(cx, |base, _| assert_eq!(base.text(), "abcdefgHIjk"));
3551    branch.read_with(cx, |branch, _| assert_eq!(branch.text(), "ABCdefgHIjk"));
3552}
3553
3554#[gpui::test]
3555async fn test_preview_edits(cx: &mut TestAppContext) {
3556    cx.update(|cx| {
3557        init_settings(cx, |_| {});
3558        theme_settings::init(theme::LoadThemes::JustBase, cx);
3559    });
3560
3561    let insertion_style = HighlightStyle {
3562        background_color: Some(cx.read(|cx| cx.theme().status().created_background)),
3563        ..Default::default()
3564    };
3565    let deletion_style = HighlightStyle {
3566        background_color: Some(cx.read(|cx| cx.theme().status().deleted_background)),
3567        ..Default::default()
3568    };
3569
3570    // no edits
3571    assert_preview_edits(
3572        indoc! {"
3573        fn test_empty() -> bool {
3574            false
3575        }"
3576        },
3577        vec![],
3578        true,
3579        cx,
3580        |hl| {
3581            assert!(hl.text.is_empty());
3582            assert!(hl.highlights.is_empty());
3583        },
3584    )
3585    .await;
3586
3587    // only insertions
3588    assert_preview_edits(
3589        indoc! {"
3590        fn calculate_area(: f64) -> f64 {
3591            std::f64::consts::PI * .powi(2)
3592        }"
3593        },
3594        vec![
3595            (Point::new(0, 18)..Point::new(0, 18), "radius"),
3596            (Point::new(1, 27)..Point::new(1, 27), "radius"),
3597        ],
3598        true,
3599        cx,
3600        |hl| {
3601            assert_eq!(
3602                hl.text,
3603                indoc! {"
3604                fn calculate_area(radius: f64) -> f64 {
3605                    std::f64::consts::PI * radius.powi(2)"
3606                }
3607            );
3608
3609            assert_eq!(hl.highlights.len(), 2);
3610            assert_eq!(hl.highlights[0], ((18..24), insertion_style));
3611            assert_eq!(hl.highlights[1], ((67..73), insertion_style));
3612        },
3613    )
3614    .await;
3615
3616    // insertions & deletions
3617    assert_preview_edits(
3618        indoc! {"
3619        struct Person {
3620            first_name: String,
3621        }
3622
3623        impl Person {
3624            fn first_name(&self) -> &String {
3625                &self.first_name
3626            }
3627        }"
3628        },
3629        vec![
3630            (Point::new(1, 4)..Point::new(1, 9), "last"),
3631            (Point::new(5, 7)..Point::new(5, 12), "last"),
3632            (Point::new(6, 14)..Point::new(6, 19), "last"),
3633        ],
3634        true,
3635        cx,
3636        |hl| {
3637            assert_eq!(
3638                hl.text,
3639                indoc! {"
3640                        firstlast_name: String,
3641                    }
3642
3643                    impl Person {
3644                        fn firstlast_name(&self) -> &String {
3645                            &self.firstlast_name"
3646                }
3647            );
3648
3649            assert_eq!(hl.highlights.len(), 6);
3650            assert_eq!(hl.highlights[0], ((4..9), deletion_style));
3651            assert_eq!(hl.highlights[1], ((9..13), insertion_style));
3652            assert_eq!(hl.highlights[2], ((52..57), deletion_style));
3653            assert_eq!(hl.highlights[3], ((57..61), insertion_style));
3654            assert_eq!(hl.highlights[4], ((101..106), deletion_style));
3655            assert_eq!(hl.highlights[5], ((106..110), insertion_style));
3656        },
3657    )
3658    .await;
3659
3660    async fn assert_preview_edits(
3661        text: &str,
3662        edits: Vec<(Range<Point>, &str)>,
3663        include_deletions: bool,
3664        cx: &mut TestAppContext,
3665        assert_fn: impl Fn(HighlightedText),
3666    ) {
3667        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(rust_lang(), cx));
3668        let edits = buffer.read_with(cx, |buffer, _| {
3669            edits
3670                .into_iter()
3671                .map(|(range, text)| {
3672                    (
3673                        buffer.anchor_before(range.start)..buffer.anchor_after(range.end),
3674                        text.into(),
3675                    )
3676                })
3677                .collect::<Arc<[_]>>()
3678        });
3679        let edit_preview = buffer
3680            .read_with(cx, |buffer, cx| buffer.preview_edits(edits.clone(), cx))
3681            .await;
3682        let highlighted_edits = cx.read(|cx| {
3683            edit_preview.highlight_edits(&buffer.read(cx).snapshot(), &edits, include_deletions, cx)
3684        });
3685        assert_fn(highlighted_edits);
3686    }
3687}
3688
3689#[gpui::test(iterations = 100)]
3690fn test_random_collaboration(cx: &mut App, mut rng: StdRng) {
3691    let min_peers = env::var("MIN_PEERS")
3692        .map(|i| i.parse().expect("invalid `MIN_PEERS` variable"))
3693        .unwrap_or(1);
3694    let max_peers = env::var("MAX_PEERS")
3695        .map(|i| i.parse().expect("invalid `MAX_PEERS` variable"))
3696        .unwrap_or(5);
3697    let operations = env::var("OPERATIONS")
3698        .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
3699        .unwrap_or(10);
3700
3701    let base_text_len = rng.random_range(0..10);
3702    let base_text = RandomCharIter::new(&mut rng)
3703        .take(base_text_len)
3704        .collect::<String>();
3705    let mut replica_ids = Vec::new();
3706    let mut buffers = Vec::new();
3707    let network = Arc::new(Mutex::new(Network::new(rng.clone())));
3708    let base_buffer = cx.new(|cx| Buffer::local(base_text.as_str(), cx));
3709
3710    for i in 0..rng.random_range(min_peers..=max_peers) {
3711        let buffer = cx.new(|cx| {
3712            let state = base_buffer.read(cx).to_proto(cx);
3713            let ops = cx
3714                .foreground_executor()
3715                .block_on(base_buffer.read(cx).serialize_ops(None, cx));
3716            let mut buffer =
3717                Buffer::from_proto(ReplicaId::new(i as u16), Capability::ReadWrite, state, None)
3718                    .unwrap();
3719            buffer.apply_ops(
3720                ops.into_iter()
3721                    .map(|op| proto::deserialize_operation(op).unwrap()),
3722                cx,
3723            );
3724            buffer.set_group_interval(Duration::from_millis(rng.random_range(0..=200)));
3725            let network = network.clone();
3726            cx.subscribe(&cx.entity(), move |buffer, _, event, _| {
3727                if let BufferEvent::Operation {
3728                    operation,
3729                    is_local: true,
3730                } = event
3731                {
3732                    network.lock().broadcast(
3733                        buffer.replica_id(),
3734                        vec![proto::serialize_operation(operation)],
3735                    );
3736                }
3737            })
3738            .detach();
3739            buffer
3740        });
3741
3742        buffers.push(buffer);
3743        replica_ids.push(ReplicaId::new(i as u16));
3744        network.lock().add_peer(ReplicaId::new(i as u16));
3745        log::info!("Adding initial peer with replica id {:?}", replica_ids[i]);
3746    }
3747
3748    log::info!("initial text: {:?}", base_text);
3749
3750    let mut now = Instant::now();
3751    let mut mutation_count = operations;
3752    let mut next_diagnostic_id = 0;
3753    let mut active_selections = BTreeMap::default();
3754    loop {
3755        let replica_index = rng.random_range(0..replica_ids.len());
3756        let replica_id = replica_ids[replica_index];
3757        let buffer = &mut buffers[replica_index];
3758        let mut new_buffer = None;
3759        match rng.random_range(0..100) {
3760            0..=29 if mutation_count != 0 => {
3761                buffer.update(cx, |buffer, cx| {
3762                    buffer.start_transaction_at(now);
3763                    buffer.randomly_edit(&mut rng, 5, cx);
3764                    buffer.end_transaction_at(now, cx);
3765                    log::info!("buffer {:?} text: {:?}", buffer.replica_id(), buffer.text());
3766                });
3767                mutation_count -= 1;
3768            }
3769            30..=39 if mutation_count != 0 => {
3770                buffer.update(cx, |buffer, cx| {
3771                    if rng.random_bool(0.2) {
3772                        log::info!("peer {:?} clearing active selections", replica_id);
3773                        active_selections.remove(&replica_id);
3774                        buffer.remove_active_selections(cx);
3775                    } else {
3776                        let mut selections = Vec::new();
3777                        for id in 0..rng.random_range(1..=5) {
3778                            let range = buffer.random_byte_range(0, &mut rng);
3779                            selections.push(Selection {
3780                                id,
3781                                start: buffer.anchor_before(range.start),
3782                                end: buffer.anchor_before(range.end),
3783                                reversed: false,
3784                                goal: SelectionGoal::None,
3785                            });
3786                        }
3787                        let selections: Arc<[Selection<Anchor>]> = selections.into();
3788                        log::info!(
3789                            "peer {:?} setting active selections: {:?}",
3790                            replica_id,
3791                            selections
3792                        );
3793                        active_selections.insert(replica_id, selections.clone());
3794                        buffer.set_active_selections(selections, false, Default::default(), cx);
3795                    }
3796                });
3797                mutation_count -= 1;
3798            }
3799            40..=49 if mutation_count != 0 && replica_id == ReplicaId::REMOTE_SERVER => {
3800                let entry_count = rng.random_range(1..=5);
3801                buffer.update(cx, |buffer, cx| {
3802                    let diagnostics = DiagnosticSet::new(
3803                        (0..entry_count).map(|_| {
3804                            let range = buffer.random_byte_range(0, &mut rng);
3805                            let range = range.to_point_utf16(buffer);
3806                            let range = range.start..range.end;
3807                            DiagnosticEntry {
3808                                range,
3809                                diagnostic: Diagnostic {
3810                                    message: post_inc(&mut next_diagnostic_id).to_string(),
3811                                    ..Default::default()
3812                                },
3813                            }
3814                        }),
3815                        buffer,
3816                    );
3817                    log::info!(
3818                        "peer {:?} setting diagnostics: {:?}",
3819                        replica_id,
3820                        diagnostics
3821                    );
3822                    buffer.update_diagnostics(LanguageServerId(0), diagnostics, cx);
3823                });
3824                mutation_count -= 1;
3825            }
3826            50..=59 if replica_ids.len() < max_peers => {
3827                let old_buffer_state = buffer.read(cx).to_proto(cx);
3828                let old_buffer_ops = cx
3829                    .foreground_executor()
3830                    .block_on(buffer.read(cx).serialize_ops(None, cx));
3831                let new_replica_id = (0..=replica_ids.len() as u16)
3832                    .map(ReplicaId::new)
3833                    .filter(|replica_id| *replica_id != buffer.read(cx).replica_id())
3834                    .choose(&mut rng)
3835                    .unwrap();
3836                log::info!(
3837                    "Adding new replica {:?} (replicating from {:?})",
3838                    new_replica_id,
3839                    replica_id
3840                );
3841                new_buffer = Some(cx.new(|cx| {
3842                    let mut new_buffer = Buffer::from_proto(
3843                        new_replica_id,
3844                        Capability::ReadWrite,
3845                        old_buffer_state,
3846                        None,
3847                    )
3848                    .unwrap();
3849                    new_buffer.apply_ops(
3850                        old_buffer_ops
3851                            .into_iter()
3852                            .map(|op| deserialize_operation(op).unwrap()),
3853                        cx,
3854                    );
3855                    log::info!(
3856                        "New replica {:?} text: {:?}",
3857                        new_buffer.replica_id(),
3858                        new_buffer.text()
3859                    );
3860                    new_buffer.set_group_interval(Duration::from_millis(rng.random_range(0..=200)));
3861                    let network = network.clone();
3862                    cx.subscribe(&cx.entity(), move |buffer, _, event, _| {
3863                        if let BufferEvent::Operation {
3864                            operation,
3865                            is_local: true,
3866                        } = event
3867                        {
3868                            network.lock().broadcast(
3869                                buffer.replica_id(),
3870                                vec![proto::serialize_operation(operation)],
3871                            );
3872                        }
3873                    })
3874                    .detach();
3875                    new_buffer
3876                }));
3877                network.lock().replicate(replica_id, new_replica_id);
3878
3879                if new_replica_id.as_u16() as usize == replica_ids.len() {
3880                    replica_ids.push(new_replica_id);
3881                } else {
3882                    let new_buffer = new_buffer.take().unwrap();
3883                    while network.lock().has_unreceived(new_replica_id) {
3884                        let ops = network
3885                            .lock()
3886                            .receive(new_replica_id)
3887                            .into_iter()
3888                            .map(|op| proto::deserialize_operation(op).unwrap());
3889                        if ops.len() > 0 {
3890                            log::info!(
3891                                "peer {:?} (version: {:?}) applying {} ops from the network. {:?}",
3892                                new_replica_id,
3893                                buffer.read(cx).version(),
3894                                ops.len(),
3895                                ops
3896                            );
3897                            new_buffer.update(cx, |new_buffer, cx| {
3898                                new_buffer.apply_ops(ops, cx);
3899                            });
3900                        }
3901                    }
3902                    buffers[new_replica_id.as_u16() as usize] = new_buffer;
3903                }
3904            }
3905            60..=69 if mutation_count != 0 => {
3906                buffer.update(cx, |buffer, cx| {
3907                    buffer.randomly_undo_redo(&mut rng, cx);
3908                    log::info!("buffer {:?} text: {:?}", buffer.replica_id(), buffer.text());
3909                });
3910                mutation_count -= 1;
3911            }
3912            _ if network.lock().has_unreceived(replica_id) => {
3913                let ops = network
3914                    .lock()
3915                    .receive(replica_id)
3916                    .into_iter()
3917                    .map(|op| proto::deserialize_operation(op).unwrap());
3918                if ops.len() > 0 {
3919                    log::info!(
3920                        "peer {:?} (version: {:?}) applying {} ops from the network. {:?}",
3921                        replica_id,
3922                        buffer.read(cx).version(),
3923                        ops.len(),
3924                        ops
3925                    );
3926                    buffer.update(cx, |buffer, cx| buffer.apply_ops(ops, cx));
3927                }
3928            }
3929            _ => {}
3930        }
3931
3932        now += Duration::from_millis(rng.random_range(0..=200));
3933        buffers.extend(new_buffer);
3934
3935        for buffer in &buffers {
3936            buffer.read(cx).check_invariants();
3937        }
3938
3939        if mutation_count == 0 && network.lock().is_idle() {
3940            break;
3941        }
3942    }
3943
3944    let first_buffer = buffers[0].read(cx).snapshot();
3945    for buffer in &buffers[1..] {
3946        let buffer = buffer.read(cx).snapshot();
3947        assert_eq!(
3948            buffer.version(),
3949            first_buffer.version(),
3950            "Replica {:?} version != Replica 0 version",
3951            buffer.replica_id()
3952        );
3953        assert_eq!(
3954            buffer.text(),
3955            first_buffer.text(),
3956            "Replica {:?} text != Replica 0 text",
3957            buffer.replica_id()
3958        );
3959        assert_eq!(
3960            buffer
3961                .diagnostics_in_range::<_, usize>(0..buffer.len(), false)
3962                .collect::<Vec<_>>(),
3963            first_buffer
3964                .diagnostics_in_range::<_, usize>(0..first_buffer.len(), false)
3965                .collect::<Vec<_>>(),
3966            "Replica {:?} diagnostics != Replica 0 diagnostics",
3967            buffer.replica_id()
3968        );
3969    }
3970
3971    for buffer in &buffers {
3972        let buffer = buffer.read(cx).snapshot();
3973        let actual_remote_selections = buffer
3974            .selections_in_range(Anchor::min_max_range_for_buffer(buffer.remote_id()), false)
3975            .map(|(replica_id, _, _, selections)| (replica_id, selections.collect::<Vec<_>>()))
3976            .collect::<Vec<_>>();
3977        let expected_remote_selections = active_selections
3978            .iter()
3979            .filter(|(replica_id, _)| **replica_id != buffer.replica_id())
3980            .map(|(replica_id, selections)| (*replica_id, selections.iter().collect::<Vec<_>>()))
3981            .collect::<Vec<_>>();
3982        assert_eq!(
3983            actual_remote_selections,
3984            expected_remote_selections,
3985            "Replica {:?} remote selections != expected selections",
3986            buffer.replica_id()
3987        );
3988    }
3989}
3990
3991#[test]
3992fn test_contiguous_ranges() {
3993    assert_eq!(
3994        contiguous_ranges([1, 2, 3, 5, 6, 9, 10, 11, 12].into_iter(), 100).collect::<Vec<_>>(),
3995        &[1..4, 5..7, 9..13]
3996    );
3997
3998    // Respects the `max_len` parameter
3999    assert_eq!(
4000        contiguous_ranges(
4001            [2, 3, 4, 5, 6, 7, 8, 9, 23, 24, 25, 26, 30, 31].into_iter(),
4002            3
4003        )
4004        .collect::<Vec<_>>(),
4005        &[2..5, 5..8, 8..10, 23..26, 26..27, 30..32],
4006    );
4007}
4008
4009#[gpui::test]
4010fn test_insertion_after_deletion(cx: &mut gpui::App) {
4011    let buffer = cx.new(|cx| Buffer::local("struct Foo {\n    \n}", cx));
4012    buffer.update(cx, |buffer, cx| {
4013        let mut anchor = buffer.anchor_after(17);
4014        buffer.edit([(12..18, "")], None, cx);
4015        let snapshot = buffer.snapshot();
4016        assert_eq!(snapshot.text(), "struct Foo {}");
4017        if !anchor.is_valid(&snapshot) {
4018            anchor = snapshot.anchor_after(snapshot.offset_for_anchor(&anchor));
4019        }
4020        buffer.edit([(anchor..anchor, "\n")], None, cx);
4021        buffer.edit([(anchor..anchor, "field1:")], None, cx);
4022        buffer.edit([(anchor..anchor, " i32,")], None, cx);
4023        let snapshot = buffer.snapshot();
4024        assert_eq!(snapshot.text(), "struct Foo {\nfield1: i32,}");
4025    })
4026}
4027
4028#[gpui::test(iterations = 500)]
4029fn test_trailing_whitespace_ranges(mut rng: StdRng) {
4030    // Generate a random multi-line string containing
4031    // some lines with trailing whitespace.
4032    let mut text = String::new();
4033    for _ in 0..rng.random_range(0..16) {
4034        for _ in 0..rng.random_range(0..36) {
4035            text.push(match rng.random_range(0..10) {
4036                0..=1 => ' ',
4037                3 => '\t',
4038                _ => rng.random_range('a'..='z'),
4039            });
4040        }
4041        text.push('\n');
4042    }
4043
4044    match rng.random_range(0..10) {
4045        // sometimes remove the last newline
4046        0..=1 => drop(text.pop()), //
4047
4048        // sometimes add extra newlines
4049        2..=3 => text.push_str(&"\n".repeat(rng.random_range(1..5))),
4050        _ => {}
4051    }
4052
4053    let rope = Rope::from(text.as_str());
4054    let actual_ranges = trailing_whitespace_ranges(&rope, None);
4055    let expected_ranges = TRAILING_WHITESPACE_REGEX
4056        .find_iter(&text)
4057        .map(|m| m.range())
4058        .collect::<Vec<_>>();
4059    assert_eq!(
4060        actual_ranges,
4061        expected_ranges,
4062        "wrong ranges for text lines:\n{:?}",
4063        text.split('\n').collect::<Vec<_>>()
4064    );
4065}
4066
4067#[gpui::test(iterations = 500)]
4068fn test_trailing_whitespace_ranges_in_rows(mut rng: StdRng) {
4069    let mut text = String::new();
4070    for _ in 0..rng.random_range(0..16) {
4071        for _ in 0..rng.random_range(0..36) {
4072            text.push(match rng.random_range(0..10) {
4073                0..=1 => ' ',
4074                3 => '\t',
4075                _ => rng.random_range('a'..='z'),
4076            });
4077        }
4078        text.push('\n');
4079    }
4080    match rng.random_range(0..10) {
4081        0..=1 => drop(text.pop()),
4082        2..=3 => text.push_str(&"\n".repeat(rng.random_range(1..5))),
4083        _ => {}
4084    }
4085
4086    let rope = Rope::from(text.as_str());
4087    let all_ranges = trailing_whitespace_ranges(&rope, None);
4088    let lines = text.split('\n').collect::<Vec<_>>();
4089
4090    // A range covering every row must reproduce the unfiltered full scan exactly.
4091    assert_eq!(
4092        trailing_whitespace_ranges(&rope, Some(&[0..u32::MAX])),
4093        all_ranges,
4094        "full-coverage mismatch for lines:\n{lines:?}",
4095    );
4096
4097    // For a random (possibly gappy) subset of rows, the filtered variant must equal
4098    // the full scan restricted to ranges whose line is in the subset.
4099    let max_row = rope.max_point().row;
4100    let mut row_ranges = Vec::new();
4101    let mut row = 0;
4102    while row <= max_row {
4103        let span = rng.random_range(0..=3);
4104        if span > 0 {
4105            let end = (row + span).min(max_row + 1);
4106            row_ranges.push(row..end);
4107            row = end;
4108        }
4109        row += 1;
4110    }
4111
4112    let expected = all_ranges
4113        .iter()
4114        .filter(|range| {
4115            let row = rope.offset_to_point(range.start).row;
4116            row_ranges.iter().any(|r| r.contains(&row))
4117        })
4118        .cloned()
4119        .collect::<Vec<_>>();
4120    assert_eq!(
4121        trailing_whitespace_ranges(&rope, Some(&row_ranges)),
4122        expected,
4123        "subset mismatch for ranges {row_ranges:?} and lines:\n{lines:?}",
4124    );
4125}
4126
4127#[gpui::test]
4128async fn test_trailing_whitespace_in_ranges(cx: &mut gpui::TestAppContext) {
4129    // line 0: "zero"      (no trailing whitespace)
4130    // line 1: "one  "     (2 trailing spaces)
4131    // line 2: "two"       (no trailing whitespace)
4132    // line 3: "three   "  (3 trailing spaces)
4133    // line 4: "four"      (no trailing whitespace)
4134    // line 5: "five    "  (4 trailing spaces)
4135    let text = ["zero", "one  ", "two", "three   ", "four", "five    "].join("\n");
4136    let buffer = cx.new(|cx| Buffer::local(text, cx));
4137
4138    // Only rows 1 and 5 are modified, so only those lines get cleaned; line 3 stays untouched.
4139    let modified_rows = [1u32..2, 5..6];
4140    let diff = buffer
4141        .update(cx, |buffer, cx| {
4142            buffer.remove_trailing_whitespace(Some(&modified_rows), cx)
4143        })
4144        .await;
4145    buffer.update(cx, |buffer, cx| {
4146        buffer.apply_diff(diff, cx);
4147        assert_eq!(
4148            buffer.text(),
4149            ["zero", "one", "two", "three   ", "four", "five"].join("\n")
4150        );
4151    });
4152}
4153
4154#[gpui::test]
4155async fn test_trailing_whitespace_empty_ranges(cx: &mut gpui::TestAppContext) {
4156    let text = ["zero", "one  ", "two  "].join("\n");
4157    let buffer = cx.new(|cx| Buffer::local(text.clone(), cx));
4158
4159    let diff = buffer
4160        .update(cx, |buffer, cx| {
4161            buffer.remove_trailing_whitespace(Some(&[]), cx)
4162        })
4163        .await;
4164    buffer.update(cx, |buffer, cx| {
4165        buffer.apply_diff(diff, cx);
4166        assert_eq!(buffer.text(), text);
4167    });
4168}
4169
4170#[gpui::test]
4171async fn test_final_newline_modified_last_line(cx: &mut gpui::TestAppContext) {
4172    // No final newline; the modified range (rows 0..3) includes the last line (row 2).
4173    let text = "line0\nline1\nline2";
4174    let buffer = cx.new(|cx| Buffer::local(text, cx));
4175
4176    buffer.update(cx, |buffer, cx| {
4177        let diff = buffer.ensure_final_newline(Some(&[0u32..3]));
4178        buffer.apply_diff(diff, cx);
4179        assert_eq!(buffer.text(), "line0\nline1\nline2\n");
4180    });
4181}
4182
4183#[gpui::test]
4184async fn test_final_newline_unmodified_last_line(cx: &mut gpui::TestAppContext) {
4185    // No final newline; the modified range (rows 0..2) excludes the last line (row 2), so nothing changes.
4186    let text = "line0\nline1\nline2";
4187    let buffer = cx.new(|cx| Buffer::local(text, cx));
4188
4189    buffer.update(cx, |buffer, cx| {
4190        let diff = buffer.ensure_final_newline(Some(&[0u32..2]));
4191        buffer.apply_diff(diff, cx);
4192        assert_eq!(buffer.text(), "line0\nline1\nline2");
4193    });
4194}
4195
4196// An empty last line (file already ends with a newline) is left untouched, even with extra
4197// trailing blank lines. With `None` these would collapse; scoped to rows they must not, to
4198// avoid deleting unselected rows.
4199#[gpui::test]
4200async fn test_final_newline_does_not_collapse_trailing_blank_lines(cx: &mut gpui::TestAppContext) {
4201    let text = "line0\nline1\n\n";
4202    let buffer = cx.new(|cx| Buffer::local(text, cx));
4203
4204    buffer.update(cx, |buffer, cx| {
4205        let diff = buffer.ensure_final_newline(Some(&[0u32..4]));
4206        buffer.apply_diff(diff, cx);
4207        assert_eq!(buffer.text(), "line0\nline1\n\n");
4208    });
4209}
4210
4211// When scoped to rows, only a newline is inserted; unlike the `None` (whole-buffer) case, it
4212// does not trim trailing whitespace on the last line.
4213#[gpui::test]
4214async fn test_final_newline_in_range_only_inserts(cx: &mut gpui::TestAppContext) {
4215    let text = "line0\nline1  ";
4216    let buffer = cx.new(|cx| Buffer::local(text, cx));
4217
4218    buffer.update(cx, |buffer, cx| {
4219        let diff = buffer.ensure_final_newline(Some(&[0u32..2]));
4220        buffer.apply_diff(diff, cx);
4221        assert_eq!(buffer.text(), "line0\nline1  \n");
4222    });
4223}
4224
4225#[gpui::test]
4226async fn test_final_newline_whole_buffer(cx: &mut gpui::TestAppContext) {
4227    // (input, expected) pairs for the whole-buffer (`None`) case.
4228    let cases = [
4229        // Content without a trailing newline gets exactly one appended.
4230        ("line0\nline1", "line0\nline1\n"),
4231        // A buffer already ending in a single newline is left untouched.
4232        ("line0\nline1\n", "line0\nline1\n"),
4233        // Trailing blank lines and whitespace at the end of the file collapse to one newline.
4234        ("line0\nline1\n\n\n", "line0\nline1\n"),
4235        ("line0\nline1  \n  ", "line0\nline1\n"),
4236        // An empty buffer stays empty.
4237        ("", ""),
4238    ];
4239
4240    for (input, expected) in cases {
4241        let buffer = cx.new(|cx| Buffer::local(input, cx));
4242        buffer.update(cx, |buffer, cx| {
4243            let diff = buffer.ensure_final_newline(None);
4244            buffer.apply_diff(diff, cx);
4245            assert_eq!(buffer.text(), expected, "wrong result for input {input:?}");
4246        });
4247    }
4248}
4249
4250#[gpui::test]
4251async fn test_trailing_whitespace_in_ranges_crlf(cx: &mut gpui::TestAppContext) {
4252    let text = "zero\r\none  \r\ntwo\r\nthree   \r\nfour\r\nfive    ";
4253    let buffer = cx.new(|cx| {
4254        let buffer = Buffer::local(text, cx);
4255        assert_eq!(buffer.line_ending(), LineEnding::Windows);
4256        buffer
4257    });
4258
4259    let modified_rows = [1u32..2, 5..6];
4260    let diff = buffer
4261        .update(cx, |buffer, cx| {
4262            buffer.remove_trailing_whitespace(Some(&modified_rows), cx)
4263        })
4264        .await;
4265    buffer.update(cx, |buffer, cx| {
4266        buffer.apply_diff(diff, cx);
4267        assert_eq!(buffer.text(), "zero\none\ntwo\nthree   \nfour\nfive");
4268        assert_eq!(buffer.line_ending(), LineEnding::Windows);
4269    });
4270}
4271
4272#[gpui::test]
4273async fn test_final_newline_in_range_crlf(cx: &mut gpui::TestAppContext) {
4274    let text = "line0\r\nline1\r\nline2";
4275    let buffer = cx.new(|cx| {
4276        let buffer = Buffer::local(text, cx);
4277        assert_eq!(buffer.line_ending(), LineEnding::Windows);
4278        buffer
4279    });
4280
4281    buffer.update(cx, |buffer, cx| {
4282        let diff = buffer.ensure_final_newline(Some(&[0u32..3]));
4283        buffer.apply_diff(diff, cx);
4284        assert_eq!(buffer.text(), "line0\nline1\nline2\n");
4285        assert_eq!(buffer.line_ending(), LineEnding::Windows);
4286    });
4287}
4288
4289#[gpui::test]
4290fn test_words_in_range(cx: &mut gpui::App) {
4291    init_settings(cx, |_| {});
4292
4293    // The first line are words excluded from the results with heuristics, we do not expect them in the test assertions.
4294    let contents = r#"
42950_isize 123 3.4 4  
4296let word=öäpple.bar你 Öäpple word2-öÄpPlE-Pizza-word ÖÄPPLE word
4297    "#;
4298
4299    let buffer = cx.new(|cx| {
4300        let buffer = Buffer::local(contents, cx).with_language(rust_lang(), cx);
4301        assert_eq!(buffer.text(), contents);
4302        buffer.check_invariants();
4303        buffer
4304    });
4305
4306    buffer.update(cx, |buffer, _| {
4307        let snapshot = buffer.snapshot();
4308        assert_eq!(
4309            BTreeSet::from_iter(["Pizza".to_string()]),
4310            snapshot
4311                .words_in_range(WordsQuery {
4312                    fuzzy_contents: Some("piz"),
4313                    skip_digits: true,
4314                    range: 0..snapshot.len(),
4315                })
4316                .into_keys()
4317                .collect::<BTreeSet<_>>()
4318        );
4319        assert_eq!(
4320            BTreeSet::from_iter([
4321                "öäpple".to_string(),
4322                "Öäpple".to_string(),
4323                "öÄpPlE".to_string(),
4324                "ÖÄPPLE".to_string(),
4325            ]),
4326            snapshot
4327                .words_in_range(WordsQuery {
4328                    fuzzy_contents: Some("öp"),
4329                    skip_digits: true,
4330                    range: 0..snapshot.len(),
4331                })
4332                .into_keys()
4333                .collect::<BTreeSet<_>>()
4334        );
4335        assert_eq!(
4336            BTreeSet::from_iter([
4337                "öÄpPlE".to_string(),
4338                "Öäpple".to_string(),
4339                "ÖÄPPLE".to_string(),
4340                "öäpple".to_string(),
4341            ]),
4342            snapshot
4343                .words_in_range(WordsQuery {
4344                    fuzzy_contents: Some("öÄ"),
4345                    skip_digits: true,
4346                    range: 0..snapshot.len(),
4347                })
4348                .into_keys()
4349                .collect::<BTreeSet<_>>()
4350        );
4351        assert_eq!(
4352            BTreeSet::default(),
4353            snapshot
4354                .words_in_range(WordsQuery {
4355                    fuzzy_contents: Some("öÄ好"),
4356                    skip_digits: true,
4357                    range: 0..snapshot.len(),
4358                })
4359                .into_keys()
4360                .collect::<BTreeSet<_>>()
4361        );
4362        assert_eq!(
4363            BTreeSet::from_iter(["bar你".to_string(),]),
4364            snapshot
4365                .words_in_range(WordsQuery {
4366                    fuzzy_contents: Some("你"),
4367                    skip_digits: true,
4368                    range: 0..snapshot.len(),
4369                })
4370                .into_keys()
4371                .collect::<BTreeSet<_>>()
4372        );
4373        assert_eq!(
4374            BTreeSet::default(),
4375            snapshot
4376                .words_in_range(WordsQuery {
4377                    fuzzy_contents: Some(""),
4378                    skip_digits: true,
4379                    range: 0..snapshot.len(),
4380                },)
4381                .into_keys()
4382                .collect::<BTreeSet<_>>()
4383        );
4384        assert_eq!(
4385            BTreeSet::from_iter([
4386                "bar你".to_string(),
4387                "öÄpPlE".to_string(),
4388                "Öäpple".to_string(),
4389                "ÖÄPPLE".to_string(),
4390                "öäpple".to_string(),
4391                "let".to_string(),
4392                "Pizza".to_string(),
4393                "word".to_string(),
4394                "word2".to_string(),
4395            ]),
4396            snapshot
4397                .words_in_range(WordsQuery {
4398                    fuzzy_contents: None,
4399                    skip_digits: true,
4400                    range: 0..snapshot.len(),
4401                })
4402                .into_keys()
4403                .collect::<BTreeSet<_>>()
4404        );
4405        assert_eq!(
4406            BTreeSet::from_iter([
4407                "0_isize".to_string(),
4408                "123".to_string(),
4409                "3".to_string(),
4410                "4".to_string(),
4411                "bar你".to_string(),
4412                "öÄpPlE".to_string(),
4413                "Öäpple".to_string(),
4414                "ÖÄPPLE".to_string(),
4415                "öäpple".to_string(),
4416                "let".to_string(),
4417                "Pizza".to_string(),
4418                "word".to_string(),
4419                "word2".to_string(),
4420            ]),
4421            snapshot
4422                .words_in_range(WordsQuery {
4423                    fuzzy_contents: None,
4424                    skip_digits: false,
4425                    range: 0..snapshot.len(),
4426                })
4427                .into_keys()
4428                .collect::<BTreeSet<_>>()
4429        );
4430    });
4431}
4432
4433fn ruby_lang() -> Language {
4434    Language::new(
4435        LanguageConfig {
4436            name: "Ruby".into(),
4437            matcher: (LanguageMatcher {
4438                path_suffixes: vec!["rb".to_string()],
4439                ..Default::default()
4440            })
4441            .into(),
4442            line_comments: vec!["# ".into()],
4443            ..Default::default()
4444        },
4445        Some(tree_sitter_ruby::LANGUAGE.into()),
4446    )
4447    .with_indents_query(
4448        r#"
4449            (class "end" @end) @indent
4450            (method "end" @end) @indent
4451            (rescue) @outdent
4452            (then) @indent
4453        "#,
4454    )
4455    .unwrap()
4456}
4457
4458fn html_lang() -> Language {
4459    Language::new(
4460        LanguageConfig {
4461            name: LanguageName::new_static("HTML"),
4462            block_comment: Some(BlockCommentConfig {
4463                start: "<!--".into(),
4464                prefix: "".into(),
4465                end: "-->".into(),
4466                tab_size: 0,
4467            }),
4468            ..Default::default()
4469        },
4470        Some(tree_sitter_html::LANGUAGE.into()),
4471    )
4472    .with_indents_query(
4473        "
4474        (element
4475          (start_tag) @start
4476          (end_tag)? @end) @indent
4477        ",
4478    )
4479    .unwrap()
4480    .with_injection_query(
4481        r#"
4482        (script_element
4483            (raw_text) @injection.content
4484            (#set! injection.language "javascript"))
4485        "#,
4486    )
4487    .unwrap()
4488}
4489
4490fn erb_lang() -> Language {
4491    Language::new(
4492        LanguageConfig {
4493            name: "HTML+ERB".into(),
4494            matcher: (LanguageMatcher {
4495                path_suffixes: vec!["erb".to_string()],
4496                ..Default::default()
4497            })
4498            .into(),
4499            block_comment: Some(BlockCommentConfig {
4500                start: "<%#".into(),
4501                prefix: "".into(),
4502                end: "%>".into(),
4503                tab_size: 0,
4504            }),
4505            ..Default::default()
4506        },
4507        Some(tree_sitter_embedded_template::LANGUAGE.into()),
4508    )
4509    .with_injection_query(
4510        r#"
4511            (
4512                (code) @content
4513                (#set! "language" "ruby")
4514                (#set! "combined")
4515            )
4516
4517            (
4518                (content) @content
4519                (#set! "language" "html")
4520                (#set! "combined")
4521            )
4522        "#,
4523    )
4524    .unwrap()
4525}
4526
4527fn color_index_for_open(
4528    matches: &HashMap<Range<BufferRow>, Vec<BracketMatch<usize>>>,
4529    open_offset: usize,
4530) -> Option<usize> {
4531    matches
4532        .values()
4533        .flatten()
4534        .find(|bracket_match| bracket_match.open_range.start == open_offset)
4535        .and_then(|bracket_match| bracket_match.color_index)
4536}
4537
4538fn javascript_lang() -> Language {
4539    Language::new(
4540        LanguageConfig {
4541            name: "JavaScript".into(),
4542            ..Default::default()
4543        },
4544        Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
4545    )
4546    .with_brackets_query(
4547        r#"
4548        ("{" @open "}" @close)
4549        ("(" @open ")" @close)
4550        "#,
4551    )
4552    .unwrap()
4553    .with_indents_query(
4554        r#"
4555        (object "}" @end) @indent
4556        "#,
4557    )
4558    .unwrap()
4559}
4560
4561fn c_lang() -> Arc<Language> {
4562    Arc::new(
4563        Language::new(
4564            LanguageConfig {
4565                name: "C".into(),
4566                ..Default::default()
4567            },
4568            Some(tree_sitter_c::LANGUAGE.into()),
4569        )
4570        .with_outline_query(include_str!("../../grammars/src/c/outline.scm"))
4571        .unwrap(),
4572    )
4573}
4574
4575pub fn markdown_inline_lang() -> Language {
4576    Language::new(
4577        LanguageConfig {
4578            name: "Markdown-Inline".into(),
4579            hidden: true,
4580            ..LanguageConfig::default()
4581        },
4582        Some(tree_sitter_md::INLINE_LANGUAGE.into()),
4583    )
4584    .with_highlights_query("(emphasis) @emphasis")
4585    .unwrap()
4586}
4587
4588fn get_tree_sexp(buffer: &Entity<Buffer>, cx: &mut gpui::TestAppContext) -> String {
4589    buffer.update(cx, |buffer, _| {
4590        let snapshot = buffer.snapshot();
4591        let layers = snapshot.syntax.layers(buffer.as_text_snapshot());
4592        layers[0].node().to_sexp()
4593    })
4594}
4595
4596fn typescript_lang_with_indents() -> Arc<Language> {
4597    Arc::new(
4598        Language::new(
4599            LanguageConfig {
4600                name: "TypeScript".into(),
4601                ..Default::default()
4602            },
4603            Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
4604        )
4605        .with_brackets_query(r#"("{" @open "}" @close) ("(" @open ")" @close)"#)
4606        .unwrap()
4607        .with_indents_query(include_str!("../../grammars/src/typescript/indents.scm"))
4608        .unwrap(),
4609    )
4610}
4611
4612fn tsx_lang_with_indents() -> Arc<Language> {
4613    Arc::new(
4614        Language::new(
4615            LanguageConfig {
4616                name: "TSX".into(),
4617                ..Default::default()
4618            },
4619            Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
4620        )
4621        .with_brackets_query(r#"("{" @open "}" @close) ("(" @open ")" @close)"#)
4622        .unwrap()
4623        .with_indents_query(include_str!("../../grammars/src/tsx/indents.scm"))
4624        .unwrap(),
4625    )
4626}
4627
4628#[gpui::test]
4629fn test_autoindent_typescript_braceless_control_flow(cx: &mut App) {
4630    init_settings(cx, |_| {});
4631    cx.new(|cx| {
4632        for lang in [typescript_lang_with_indents(), tsx_lang_with_indents()] {
4633            let mut indent = |header: &str, header_len: usize, body: &str| {
4634                let mut buffer = Buffer::local(header, cx).with_language(lang.clone(), cx);
4635                buffer.edit(
4636                    [(header_len..header_len, body)],
4637                    Some(AutoindentMode::EachLine),
4638                    cx,
4639                );
4640                buffer.text()
4641            };
4642
4643            // A braceless body is indented under its `if`/`for`/`while`.
4644            assert_eq!(indent("if (true)", 9, "\nx()"), "if (true)\n    x()");
4645            assert_eq!(indent("for (;;)", 8, "\nx()"), "for (;;)\n    x()");
4646            assert_eq!(indent("while (true)", 12, "\nx()"), "while (true)\n    x()");
4647            assert_eq!(
4648                indent("for (const a of b)", 18, "\nx()"),
4649                "for (const a of b)\n    x()"
4650            );
4651
4652            // The statement after a braceless body returns to the outer indent.
4653            assert_eq!(
4654                indent("if (true)\n    x()", 17, "\ny()"),
4655                "if (true)\n    x()\ny()"
4656            );
4657
4658            // A `{}` block keeps its brace unindented (Allman style), leaving the
4659            // block rule to indent the contents. Regression guard for #24976.
4660            assert_eq!(indent("if (true)", 9, "\n{}"), "if (true)\n{}");
4661            assert_eq!(indent("for (;;)", 8, "\n{}"), "for (;;)\n{}");
4662            assert_eq!(indent("while (true)", 12, "\n{}"), "while (true)\n{}");
4663
4664            // K&R braced bodies indent their contents once.
4665            assert_eq!(
4666                indent("if (true) {\n}", 11, "\nx()"),
4667                "if (true) {\n    x()\n}"
4668            );
4669
4670            // A braceless `else` body is indented under the `else`.
4671            assert_eq!(
4672                indent("if (true)\n    x()\nelse", 22, "\ny()"),
4673                "if (true)\n    x()\nelse\n    y()"
4674            );
4675        }
4676        Buffer::local("", cx)
4677    });
4678}
4679
4680#[gpui::test]
4681fn test_completion_triggers_across_language_servers(cx: &mut TestAppContext) {
4682    cx.update(|cx| init_settings(cx, |_| {}));
4683
4684    let buffer = cx.new(|cx| Buffer::local("", cx));
4685    let replica = cx.new(|cx| {
4686        Buffer::from_proto(
4687            ReplicaId::new(1),
4688            Capability::ReadWrite,
4689            buffer.read(cx).to_proto(cx),
4690            None,
4691        )
4692        .unwrap()
4693    });
4694    replica.update(cx, |_, cx| {
4695        cx.subscribe(&buffer, |this, _, event, cx| {
4696            if let BufferEvent::Operation {
4697                operation,
4698                is_local: true,
4699            } = event
4700            {
4701                this.apply_ops([operation.clone()], cx);
4702            }
4703        })
4704        .detach();
4705    });
4706
4707    let server_a = LanguageServerId(1);
4708    let server_b = LanguageServerId(2);
4709    let triggers = |buffer: &Entity<Buffer>, cx: &mut TestAppContext| {
4710        buffer.read_with(cx, |buffer, _| buffer.completion_triggers().clone())
4711    };
4712
4713    buffer.update(cx, |buffer, cx| {
4714        buffer.set_completion_triggers(server_a, BTreeSet::from_iter([".".to_string()]), cx);
4715        buffer.set_completion_triggers(server_b, BTreeSet::from_iter([":".to_string()]), cx);
4716    });
4717    let expected = BTreeSet::from_iter([".".to_string(), ":".to_string()]);
4718    assert_eq!(
4719        triggers(&buffer, cx),
4720        expected,
4721        "expected triggers from both servers to be combined",
4722    );
4723    assert_eq!(
4724        triggers(&replica, cx),
4725        expected,
4726        "expected the replica to combine triggers from both servers",
4727    );
4728
4729    buffer.update(cx, |buffer, cx| {
4730        buffer.set_completion_triggers(server_a, BTreeSet::from_iter([",".to_string()]), cx);
4731    });
4732    let expected = BTreeSet::from_iter([",".to_string(), ":".to_string()]);
4733    assert_eq!(
4734        triggers(&buffer, cx),
4735        expected,
4736        "expected replaced triggers to not linger in the combined set",
4737    );
4738    assert_eq!(
4739        triggers(&replica, cx),
4740        expected,
4741        "expected the replica to not keep replaced triggers in the combined set",
4742    );
4743
4744    buffer.update(cx, |buffer, cx| {
4745        buffer.set_completion_triggers(server_a, BTreeSet::new(), cx);
4746    });
4747    let expected = BTreeSet::from_iter([":".to_string()]);
4748    assert_eq!(
4749        triggers(&buffer, cx),
4750        expected,
4751        "expected the other server's triggers to survive clearing one server's triggers",
4752    );
4753    assert_eq!(
4754        triggers(&replica, cx),
4755        expected,
4756        "expected the replica to keep the other server's triggers",
4757    );
4758
4759    buffer.update(cx, |buffer, cx| {
4760        buffer.set_completion_triggers(server_b, BTreeSet::new(), cx);
4761    });
4762    assert_eq!(
4763        triggers(&buffer, cx),
4764        BTreeSet::new(),
4765        "expected no triggers after clearing all servers",
4766    );
4767    assert_eq!(
4768        triggers(&replica, cx),
4769        BTreeSet::new(),
4770        "expected no triggers on the replica after clearing all servers",
4771    );
4772}
4773
4774// Assert that the enclosing bracket ranges around the selection match the pairs indicated by the marked text in `range_markers`
4775#[track_caller]
4776fn assert_bracket_pairs(
4777    selection_text: &'static str,
4778    bracket_pair_texts: Vec<&'static str>,
4779    language: Arc<Language>,
4780    cx: &mut App,
4781) {
4782    let (expected_text, selection_ranges) = marked_text_ranges(selection_text, false);
4783    let buffer = cx.new(|cx| Buffer::local(expected_text.clone(), cx).with_language(language, cx));
4784    let buffer = buffer.update(cx, |buffer, _cx| buffer.snapshot());
4785
4786    let selection_range = selection_ranges[0].clone();
4787
4788    let bracket_pairs = bracket_pair_texts
4789        .into_iter()
4790        .map(|pair_text| {
4791            let (bracket_text, ranges) = marked_text_ranges(pair_text, false);
4792            assert_eq!(bracket_text, expected_text);
4793            (ranges[0].clone(), ranges[1].clone())
4794        })
4795        .collect::<Vec<_>>();
4796
4797    assert_set_eq!(
4798        buffer
4799            .bracket_ranges(selection_range)
4800            .map(|pair| (pair.open_range, pair.close_range))
4801            .collect::<Vec<_>>(),
4802        bracket_pairs
4803    );
4804}
4805
4806fn init_settings(cx: &mut App, f: fn(&mut AllLanguageSettingsContent)) {
4807    let settings_store = SettingsStore::test(cx);
4808    cx.set_global(settings_store);
4809    cx.update_global::<SettingsStore, _>(|settings, cx| {
4810        settings.update_user_settings(cx, |content| f(&mut content.project.all_languages));
4811    });
4812}
4813
4814#[gpui::test(iterations = 100)]
4815fn test_random_chunk_bitmaps(cx: &mut App, mut rng: StdRng) {
4816    use util::RandomCharIter;
4817
4818    // Generate random text
4819    let len = rng.random_range(0..10000);
4820    let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
4821
4822    let buffer = cx.new(|cx| Buffer::local(text, cx));
4823    let snapshot = buffer.read(cx).snapshot();
4824
4825    // Get all chunks and verify their bitmaps
4826    let chunks = snapshot.chunks(
4827        0..snapshot.len(),
4828        LanguageAwareStyling {
4829            tree_sitter: false,
4830            diagnostics: false,
4831        },
4832    );
4833
4834    for chunk in chunks {
4835        let chunk_text = chunk.text;
4836        let chars_bitmap = chunk.chars;
4837        let tabs_bitmap = chunk.tabs;
4838
4839        // Check empty chunks have empty bitmaps
4840        if chunk_text.is_empty() {
4841            assert_eq!(
4842                chars_bitmap, 0,
4843                "Empty chunk should have empty chars bitmap"
4844            );
4845            assert_eq!(tabs_bitmap, 0, "Empty chunk should have empty tabs bitmap");
4846            continue;
4847        }
4848
4849        // Verify that chunk text doesn't exceed 128 bytes
4850        assert!(
4851            chunk_text.len() <= 128,
4852            "Chunk text length {} exceeds 128 bytes",
4853            chunk_text.len()
4854        );
4855
4856        // Verify chars bitmap
4857        let char_indices = chunk_text
4858            .char_indices()
4859            .map(|(i, _)| i)
4860            .collect::<Vec<_>>();
4861
4862        for byte_idx in 0..chunk_text.len() {
4863            let should_have_bit = char_indices.contains(&byte_idx);
4864            let has_bit = chars_bitmap & (1 << byte_idx) != 0;
4865
4866            if has_bit != should_have_bit {
4867                eprintln!("Chunk text bytes: {:?}", chunk_text.as_bytes());
4868                eprintln!("Char indices: {:?}", char_indices);
4869                eprintln!("Chars bitmap: {:#b}", chars_bitmap);
4870            }
4871
4872            assert_eq!(
4873                has_bit, should_have_bit,
4874                "Chars bitmap mismatch at byte index {} in chunk {:?}. Expected bit: {}, Got bit: {}",
4875                byte_idx, chunk_text, should_have_bit, has_bit
4876            );
4877        }
4878
4879        // Verify tabs bitmap
4880        for (byte_idx, byte) in chunk_text.bytes().enumerate() {
4881            let is_tab = byte == b'\t';
4882            let has_bit = tabs_bitmap & (1 << byte_idx) != 0;
4883
4884            if has_bit != is_tab {
4885                eprintln!("Chunk text bytes: {:?}", chunk_text.as_bytes());
4886                eprintln!("Tabs bitmap: {:#b}", tabs_bitmap);
4887                assert_eq!(
4888                    has_bit, is_tab,
4889                    "Tabs bitmap mismatch at byte index {} in chunk {:?}. Byte: {:?}, Expected bit: {}, Got bit: {}",
4890                    byte_idx, chunk_text, byte as char, is_tab, has_bit
4891                );
4892            }
4893        }
4894    }
4895}
4896
4897#[gpui::test]
4898fn test_formatted_chunks(cx: &mut gpui::App) {
4899    init_settings(cx, |_| {});
4900    let buffer = cx.new(|cx| Buffer::local("use std::cmp::Eq;", cx).with_language(rust_lang(), cx));
4901    let snapshot = buffer.read(cx).snapshot();
4902
4903    let chunks = snapshot.chunks(
4904        0..snapshot.len(),
4905        LanguageAwareStyling {
4906            tree_sitter: true,
4907            diagnostics: false,
4908        },
4909    );
4910
4911    for chunk in chunks {
4912        let chunk_text = chunk.text;
4913        let chars_bitmap = chunk.chars;
4914
4915        // Verify chars bitmap
4916        let char_indices = chunk_text
4917            .char_indices()
4918            .map(|(i, _)| i)
4919            .collect::<Vec<_>>();
4920
4921        assert_eq!(char_indices.len() as u32, chars_bitmap.count_ones());
4922
4923        for byte_idx in 0..chunk_text.len() {
4924            let should_have_bit = char_indices.contains(&byte_idx);
4925            let has_bit = chars_bitmap & (1 << byte_idx) != 0;
4926
4927            if has_bit != should_have_bit {
4928                eprintln!("Chunk text bytes: {:?}", chunk_text.as_bytes());
4929                eprintln!("Char indices: {:?}", char_indices);
4930                eprintln!("Chars bitmap: {:#b}", chars_bitmap);
4931            }
4932
4933            assert_eq!(
4934                has_bit, should_have_bit,
4935                "Chars bitmap mismatch at byte index {} in chunk {:?}. Expected bit: {}, Got bit: {}",
4936                byte_idx, chunk_text, should_have_bit, has_bit
4937            );
4938        }
4939    }
4940}
4941
Served at tenant.openagents/omega Member data and write actions are omitted.