4167 lines · 125.6 KB · Rust
RawDownload
use super::*;use crate::Buffer;use clock::ReplicaId;use collections::BTreeMap;use futures::FutureExt as _;use gpui::{App, AppContext as _, BorrowAppContext, Entity};use gpui::{HighlightStyle, TestAppContext};use indoc::indoc;use pretty_assertions::assert_eq;use proto::deserialize_operation;use rand::prelude::*;use regex::RegexBuilder;use settings::SettingsStore;use settings::{AllLanguageSettingsContent, LanguageSettingsContent};use std::collections::BTreeSet;use std::{    env,    ops::Range,    sync::LazyLock,    time::{Duration, Instant},};use syntax_map::TreeSitterOptions;use text::network::Network;use text::{BufferId, LineEnding};use text::{Point, ToPoint};use theme::ActiveTheme;use unindent::Unindent as _;use util::rel_path::rel_path;use util::test::marked_text_offsets;use util::{RandomCharIter, assert_set_eq, post_inc, test::marked_text_ranges}; pub static TRAILING_WHITESPACE_REGEX: LazyLock<regex::Regex> = LazyLock::new(|| {    RegexBuilder::new(r"[ \t]+$")        .multi_line(true)        .build()        .expect("Failed to create TRAILING_WHITESPACE_REGEX")}); #[cfg(test)]#[ctor::ctor]fn init_logger() {    zlog::init_test();} #[gpui::test]fn test_line_endings(cx: &mut gpui::App) {    init_settings(cx, |_| {});     cx.new(|cx| {        let mut buffer = Buffer::local("one\r\ntwo\rthree", cx).with_language(rust_lang(), cx);        assert_eq!(buffer.text(), "one\ntwo\nthree");        assert_eq!(buffer.line_ending(), LineEnding::Windows);         buffer.check_invariants();        buffer.edit(            [(buffer.len()..buffer.len(), "\r\nfour")],            Some(AutoindentMode::EachLine),            cx,        );        buffer.edit([(0..0, "zero\r\n")], None, cx);        assert_eq!(buffer.text(), "zero\none\ntwo\nthree\nfour");        assert_eq!(buffer.line_ending(), LineEnding::Windows);        buffer.check_invariants();         buffer    });} #[gpui::test]fn test_set_line_ending(cx: &mut TestAppContext) {    let base = cx.new(|cx| Buffer::local("one\ntwo\nthree\n", cx));    let base_replica = cx.new(|cx| {        Buffer::from_proto(            ReplicaId::new(1),            Capability::ReadWrite,            base.read(cx).to_proto(cx),            None,        )        .unwrap()    });    base.update(cx, |_buffer, cx| {        cx.subscribe(&base_replica, |this, _, event, cx| {            if let BufferEvent::Operation {                operation,                is_local: true,            } = event            {                this.apply_ops([operation.clone()], cx);            }        })        .detach();    });    base_replica.update(cx, |_buffer, cx| {        cx.subscribe(&base, |this, _, event, cx| {            if let BufferEvent::Operation {                operation,                is_local: true,            } = event            {                this.apply_ops([operation.clone()], cx);            }        })        .detach();    });     // Base    base_replica.read_with(cx, |buffer, _| {        assert_eq!(buffer.line_ending(), LineEnding::Unix);    });    base.update(cx, |buffer, cx| {        assert_eq!(buffer.line_ending(), LineEnding::Unix);        buffer.set_line_ending(LineEnding::Windows, cx);        assert_eq!(buffer.line_ending(), LineEnding::Windows);    });    base_replica.read_with(cx, |buffer, _| {        assert_eq!(buffer.line_ending(), LineEnding::Windows);    });    base.update(cx, |buffer, cx| {        buffer.set_line_ending(LineEnding::Unix, cx);        assert_eq!(buffer.line_ending(), LineEnding::Unix);    });    base_replica.read_with(cx, |buffer, _| {        assert_eq!(buffer.line_ending(), LineEnding::Unix);    });     // Replica    base.read_with(cx, |buffer, _| {        assert_eq!(buffer.line_ending(), LineEnding::Unix);    });    base_replica.update(cx, |buffer, cx| {        assert_eq!(buffer.line_ending(), LineEnding::Unix);        buffer.set_line_ending(LineEnding::Windows, cx);        assert_eq!(buffer.line_ending(), LineEnding::Windows);    });    base.read_with(cx, |buffer, _| {        assert_eq!(buffer.line_ending(), LineEnding::Windows);    });    base_replica.update(cx, |buffer, cx| {        buffer.set_line_ending(LineEnding::Unix, cx);        assert_eq!(buffer.line_ending(), LineEnding::Unix);    });    base.read_with(cx, |buffer, _| {        assert_eq!(buffer.line_ending(), LineEnding::Unix);    });} #[gpui::test]fn test_select_language(cx: &mut App) {    init_settings(cx, |_| {});     let registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));    registry.add(Arc::new(Language::new(        LanguageConfig {            name: LanguageName::new_static("Rust"),            matcher: LanguageMatcher {                path_suffixes: vec!["rs".to_string()],                ..Default::default()            },            ..Default::default()        },        Some(tree_sitter_rust::LANGUAGE.into()),    )));    registry.add(Arc::new(Language::new(        LanguageConfig {            name: "Rust with longer extension".into(),            matcher: LanguageMatcher {                path_suffixes: vec!["longer.rs".to_string()],                ..Default::default()            },            ..Default::default()        },        Some(tree_sitter_rust::LANGUAGE.into()),    )));    registry.add(Arc::new(Language::new(        LanguageConfig {            name: LanguageName::new_static("Make"),            matcher: LanguageMatcher {                path_suffixes: vec!["Makefile".to_string(), "mk".to_string()],                ..Default::default()            },            ..Default::default()        },        Some(tree_sitter_rust::LANGUAGE.into()),    )));     // matching file extension    assert_eq!(        registry            .language_for_file(&file("src/lib.rs"), None, cx)            .map(|l| l.name()),        Some("Rust".into())    );    assert_eq!(        registry            .language_for_file(&file("src/lib.mk"), None, cx)            .map(|l| l.name()),        Some("Make".into())    );     // matching longer, compound extension, part of which could also match another lang    assert_eq!(        registry            .language_for_file(&file("src/lib.longer.rs"), None, cx)            .map(|l| l.name()),        Some("Rust with longer extension".into())    );     // matching filename    assert_eq!(        registry            .language_for_file(&file("src/Makefile"), None, cx)            .map(|l| l.name()),        Some("Make".into())    );     // matching suffix that is not the full file extension or filename    assert_eq!(        registry            .language_for_file(&file("zed/cars"), None, cx)            .map(|l| l.name()),        None    );    assert_eq!(        registry            .language_for_file(&file("zed/a.cars"), None, cx)            .map(|l| l.name()),        None    );    assert_eq!(        registry            .language_for_file(&file("zed/sumk"), None, cx)            .map(|l| l.name()),        None    );} #[gpui::test(iterations = 10)]async fn test_first_line_pattern(cx: &mut TestAppContext) {    cx.update(|cx| init_settings(cx, |_| {}));     let languages = LanguageRegistry::test(cx.executor());    let languages = Arc::new(languages);     languages.register_test_language(LanguageConfig {        name: "JavaScript".into(),        matcher: LanguageMatcher {            path_suffixes: vec!["js".into()],            first_line_pattern: Some(Regex::new(r"\bnode\b").unwrap()),        },        ..Default::default()    });     assert!(        cx.read(|cx| languages.language_for_file(&file("the/script"), None, cx))            .is_none()    );    assert!(        cx.read(|cx| languages.language_for_file(&file("the/script"), Some(&"nothing".into()), cx))            .is_none()    );     assert_eq!(        cx.read(|cx| languages.language_for_file(            &file("the/script"),            Some(&"#!/bin/env node".into()),            cx        ))        .unwrap()        .name(),        "JavaScript"    );} #[gpui::test]async fn test_language_for_file_with_custom_file_types(cx: &mut TestAppContext) {    cx.update(|cx| {        init_settings(cx, |settings| {            settings.file_types.get_or_insert_default().extend([                ("TypeScript".into(), vec!["js".into()].into()),                (                    "JavaScript".into(),                    vec!["*longer.ts".into(), "ecmascript".into()].into(),                ),                ("C++".into(), vec!["c".into(), "*.dev".into()].into()),                (                    "Dockerfile".into(),                    vec!["Dockerfile".into(), "Dockerfile.*".into()].into(),                ),            ]);        })    });     let languages = Arc::new(LanguageRegistry::test(cx.executor()));     for config in [        LanguageConfig {            name: "JavaScript".into(),            matcher: LanguageMatcher {                path_suffixes: vec!["js".to_string()],                ..Default::default()            },            ..Default::default()        },        LanguageConfig {            name: "TypeScript".into(),            matcher: LanguageMatcher {                path_suffixes: vec!["ts".to_string(), "ts.ecmascript".to_string()],                ..Default::default()            },            ..Default::default()        },        LanguageConfig {            name: "C++".into(),            matcher: LanguageMatcher {                path_suffixes: vec!["cpp".to_string()],                ..Default::default()            },            ..Default::default()        },        LanguageConfig {            name: "C".into(),            matcher: LanguageMatcher {                path_suffixes: vec!["c".to_string()],                ..Default::default()            },            ..Default::default()        },        LanguageConfig {            name: "Dockerfile".into(),            matcher: LanguageMatcher {                path_suffixes: vec!["Dockerfile".to_string()],                ..Default::default()            },            ..Default::default()        },    ] {        languages.add(Arc::new(Language::new(config, None)));    }     // matches system-provided lang extension    let language = cx        .read(|cx| languages.language_for_file(&file("foo.ts"), None, cx))        .unwrap();    assert_eq!(language.name(), "TypeScript");    let language = cx        .read(|cx| languages.language_for_file(&file("foo.ts.ecmascript"), None, cx))        .unwrap();    assert_eq!(language.name(), "TypeScript");    let language = cx        .read(|cx| languages.language_for_file(&file("foo.cpp"), None, cx))        .unwrap();    assert_eq!(language.name(), "C++");     // user configured lang extension, same length as system-provided    let language = cx        .read(|cx| languages.language_for_file(&file("foo.js"), None, cx))        .unwrap();    assert_eq!(language.name(), "TypeScript");    let language = cx        .read(|cx| languages.language_for_file(&file("foo.c"), None, cx))        .unwrap();    assert_eq!(language.name(), "C++");     // user configured lang extension, longer than system-provided    let language = cx        .read(|cx| languages.language_for_file(&file("foo.longer.ts"), None, cx))        .unwrap();    assert_eq!(language.name(), "JavaScript");     // user configured lang extension, shorter than system-provided    let language = cx        .read(|cx| languages.language_for_file(&file("foo.ecmascript"), None, cx))        .unwrap();    assert_eq!(language.name(), "JavaScript");     // user configured glob matches    let language = cx        .read(|cx| languages.language_for_file(&file("c-plus-plus.dev"), None, cx))        .unwrap();    assert_eq!(language.name(), "C++");    // should match Dockerfile.* => Dockerfile, not *.dev => C++    let language = cx        .read(|cx| languages.language_for_file(&file("Dockerfile.dev"), None, cx))        .unwrap();    assert_eq!(language.name(), "Dockerfile");} fn file(path: &str) -> Arc<dyn File> {    Arc::new(TestFile {        path: Arc::from(rel_path(path)),        root_name: "zed".into(),        local_root: None,    })} #[gpui::test]fn test_edit_events(cx: &mut gpui::App) {    let mut now = Instant::now();    let buffer_1_events = Arc::new(Mutex::new(Vec::new()));    let buffer_2_events = Arc::new(Mutex::new(Vec::new()));     let buffer1 = cx.new(|cx| Buffer::local("abcdef", cx));    let buffer2 = cx.new(|cx| {        Buffer::remote(            BufferId::from(cx.entity_id().as_non_zero_u64()),            ReplicaId::new(1),            Capability::ReadWrite,            "abcdef",        )    });    let buffer1_ops = Arc::new(Mutex::new(Vec::new()));    buffer1.update(cx, {        let buffer1_ops = buffer1_ops.clone();        |buffer, cx| {            let buffer_1_events = buffer_1_events.clone();            cx.subscribe(&buffer1, move |_, _, event, _| match event.clone() {                BufferEvent::Operation {                    operation,                    is_local: true,                } => buffer1_ops.lock().push(operation),                event => buffer_1_events.lock().push(event),            })            .detach();            let buffer_2_events = buffer_2_events.clone();            cx.subscribe(&buffer2, move |_, _, event, _| match event.clone() {                BufferEvent::Operation {                    is_local: false, ..                } => {}                event => buffer_2_events.lock().push(event),            })            .detach();             // An edit emits an edited event, followed by a dirty changed event,            // since the buffer was previously in a clean state.            buffer.edit([(2..4, "XYZ")], None, cx);             // An empty transaction does not emit any events.            buffer.start_transaction();            buffer.end_transaction(cx);             // A transaction containing two edits emits one edited event.            now += Duration::from_secs(1);            buffer.start_transaction_at(now);            buffer.edit([(5..5, "u")], None, cx);            buffer.edit([(6..6, "w")], None, cx);            buffer.end_transaction_at(now, cx);             // Undoing a transaction emits one edited event.            buffer.undo(cx);        }    });     // Incorporating a set of remote ops emits a single edited event,    // followed by a dirty changed event.    buffer2.update(cx, |buffer, cx| {        buffer.apply_ops(buffer1_ops.lock().drain(..), cx);    });    assert_eq!(        mem::take(&mut *buffer_1_events.lock()),        vec![            BufferEvent::Edited { is_local: true },            BufferEvent::DirtyChanged,            BufferEvent::Edited { is_local: true },            BufferEvent::Edited { is_local: true },        ]    );    assert_eq!(        mem::take(&mut *buffer_2_events.lock()),        vec![            BufferEvent::Edited { is_local: false },            BufferEvent::DirtyChanged        ]    );     buffer1.update(cx, |buffer, cx| {        // Undoing the first transaction emits edited event, followed by a        // dirty changed event, since the buffer is again in a clean state.        buffer.undo(cx);    });    // Incorporating the remote ops again emits a single edited event,    // followed by a dirty changed event.    buffer2.update(cx, |buffer, cx| {        buffer.apply_ops(buffer1_ops.lock().drain(..), cx);    });    assert_eq!(        mem::take(&mut *buffer_1_events.lock()),        vec![            BufferEvent::Edited { is_local: true },            BufferEvent::DirtyChanged,        ]    );    assert_eq!(        mem::take(&mut *buffer_2_events.lock()),        vec![            BufferEvent::Edited { is_local: false },            BufferEvent::DirtyChanged        ]    );} 

Lines 1–500 of 4167.