5029 lines · 161.7 KB · Rust
RawDownload
use crate::{Event, *};use fs::FakeFs;use futures::{future, StreamExt};use gpui::{AppContext, UpdateGlobal};use language::{    language_settings::{AllLanguageSettings, LanguageSettingsContent},    tree_sitter_rust, tree_sitter_typescript, Diagnostic, FakeLspAdapter, LanguageConfig,    LanguageMatcher, LineEnding, OffsetRangeExt, Point, ToPoint,};use lsp::Url;use parking_lot::Mutex;use pretty_assertions::assert_eq;use serde_json::json;#[cfg(not(windows))]use std::os;use std::task::Poll;use task::{TaskContext, TaskTemplate, TaskTemplates};use unindent::Unindent as _;use util::{assert_set_eq, paths::PathMatcher, test::temp_tree};use worktree::WorktreeModelHandle as _; #[gpui::test]async fn test_block_via_channel(cx: &mut gpui::TestAppContext) {    cx.executor().allow_parking();     let (tx, mut rx) = futures::channel::mpsc::unbounded();    let _thread = std::thread::spawn(move || {        std::fs::metadata("/Users").unwrap();        std::thread::sleep(Duration::from_millis(1000));        tx.unbounded_send(1).unwrap();    });    rx.next().await.unwrap();} #[gpui::test]async fn test_block_via_smol(cx: &mut gpui::TestAppContext) {    cx.executor().allow_parking();     let io_task = smol::unblock(move || {        println!("sleeping on thread {:?}", std::thread::current().id());        std::thread::sleep(Duration::from_millis(10));        1    });     let task = cx.foreground_executor().spawn(async move {        io_task.await;    });     task.await;} #[cfg(not(windows))]#[gpui::test]async fn test_symlinks(cx: &mut gpui::TestAppContext) {    init_test(cx);    cx.executor().allow_parking();     let dir = temp_tree(json!({        "root": {            "apple": "",            "banana": {                "carrot": {                    "date": "",                    "endive": "",                }            },            "fennel": {                "grape": "",            }        }    }));     let root_link_path = dir.path().join("root_link");    os::unix::fs::symlink(&dir.path().join("root"), &root_link_path).unwrap();    os::unix::fs::symlink(        &dir.path().join("root/fennel"),        &dir.path().join("root/finnochio"),    )    .unwrap();     let project = Project::test(Arc::new(RealFs::default()), [root_link_path.as_ref()], cx).await;     project.update(cx, |project, cx| {        let tree = project.worktrees().next().unwrap().read(cx);        assert_eq!(tree.file_count(), 5);        assert_eq!(            tree.inode_for_path("fennel/grape"),            tree.inode_for_path("finnochio/grape")        );    });} #[gpui::test]async fn test_managing_project_specific_settings(cx: &mut gpui::TestAppContext) {    init_test(cx);     let fs = FakeFs::new(cx.executor());    fs.insert_tree(        "/the-root",        json!({            ".zed": {                "settings.json": r#"{ "tab_size": 8 }"#,                "tasks.json": r#"[{                    "label": "cargo check",                    "command": "cargo",                    "args": ["check", "--all"]                },]"#,            },            "a": {                "a.rs": "fn a() {\n    A\n}"            },            "b": {                ".zed": {                    "settings.json": r#"{ "tab_size": 2 }"#,                    "tasks.json": r#"[{                        "label": "cargo check",                        "command": "cargo",                        "args": ["check"]                    },]"#,                },                "b.rs": "fn b() {\n  B\n}"            }        }),    )    .await;     let project = Project::test(fs.clone(), ["/the-root".as_ref()], cx).await;    let worktree = project.update(cx, |project, _| project.worktrees().next().unwrap());    let task_context = TaskContext::default();     cx.executor().run_until_parked();    let workree_id = cx.update(|cx| {        project.update(cx, |project, cx| {            project.worktrees().next().unwrap().read(cx).id()        })    });    let global_task_source_kind = TaskSourceKind::Worktree {        id: workree_id,        abs_path: PathBuf::from("/the-root/.zed/tasks.json"),        id_base: "local_tasks_for_worktree",    };    cx.update(|cx| {        let tree = worktree.read(cx);         let settings_a = language_settings(            None,            Some(                &(File::for_entry(                    tree.entry_for_path("a/a.rs").unwrap().clone(),                    worktree.clone(),                ) as _),            ),            cx,        );        let settings_b = language_settings(            None,            Some(                &(File::for_entry(                    tree.entry_for_path("b/b.rs").unwrap().clone(),                    worktree.clone(),                ) as _),            ),            cx,        );         assert_eq!(settings_a.tab_size.get(), 8);        assert_eq!(settings_b.tab_size.get(), 2);         let all_tasks = project            .update(cx, |project, cx| {                project.task_inventory().update(cx, |inventory, _| {                    let (mut old, new) = inventory.used_and_current_resolved_tasks(                        None,                        Some(workree_id),                        &task_context,                    );                    old.extend(new);                    old                })            })            .into_iter()            .map(|(source_kind, task)| {                let resolved = task.resolved.unwrap();                (                    source_kind,                    task.resolved_label,                    resolved.args,                    resolved.env,                )            })            .collect::<Vec<_>>();        assert_eq!(            all_tasks,            vec![                (                    global_task_source_kind.clone(),                    "cargo check".to_string(),                    vec!["check".to_string(), "--all".to_string()],                    HashMap::default(),                ),                (                    TaskSourceKind::Worktree {                        id: workree_id,                        abs_path: PathBuf::from("/the-root/b/.zed/tasks.json"),                        id_base: "local_tasks_for_worktree",                    },                    "cargo check".to_string(),                    vec!["check".to_string()],                    HashMap::default(),                ),            ]        );    });     project.update(cx, |project, cx| {        let inventory = project.task_inventory();        inventory.update(cx, |inventory, _| {            let (mut old, new) =                inventory.used_and_current_resolved_tasks(None, Some(workree_id), &task_context);            old.extend(new);            let (_, resolved_task) = old                .into_iter()                .find(|(source_kind, _)| source_kind == &global_task_source_kind)                .expect("should have one global task");            inventory.task_scheduled(global_task_source_kind.clone(), resolved_task);        })    });     let tasks = serde_json::to_string(&TaskTemplates(vec![TaskTemplate {        label: "cargo check".to_string(),        command: "cargo".to_string(),        args: vec![            "check".to_string(),            "--all".to_string(),            "--all-targets".to_string(),        ],        env: HashMap::from_iter(Some((            "RUSTFLAGS".to_string(),            "-Zunstable-options".to_string(),        ))),        ..TaskTemplate::default()    }]))    .unwrap();    let (tx, rx) = futures::channel::mpsc::unbounded();    cx.update(|cx| {        project.update(cx, |project, cx| {            project.task_inventory().update(cx, |inventory, cx| {                inventory.remove_local_static_source(Path::new("/the-root/.zed/tasks.json"));                inventory.add_source(                    global_task_source_kind.clone(),                    |tx, cx| StaticSource::new(TrackedFile::new(rx, tx, cx)),                    cx,                );            });        })    });    tx.unbounded_send(tasks).unwrap();     cx.run_until_parked();    cx.update(|cx| {        let all_tasks = project            .update(cx, |project, cx| {                project.task_inventory().update(cx, |inventory, _| {                    let (mut old, new) = inventory.used_and_current_resolved_tasks(                        None,                        Some(workree_id),                        &task_context,                    );                    old.extend(new);                    old                })            })            .into_iter()            .map(|(source_kind, task)| {                let resolved = task.resolved.unwrap();                (                    source_kind,                    task.resolved_label,                    resolved.args,                    resolved.env,                )            })            .collect::<Vec<_>>();        assert_eq!(            all_tasks,            vec![                (                    TaskSourceKind::Worktree {                        id: workree_id,                        abs_path: PathBuf::from("/the-root/.zed/tasks.json"),                        id_base: "local_tasks_for_worktree",                    },                    "cargo check".to_string(),                    vec![                        "check".to_string(),                        "--all".to_string(),                        "--all-targets".to_string()                    ],                    HashMap::from_iter(Some((                        "RUSTFLAGS".to_string(),                        "-Zunstable-options".to_string()                    ))),                ),                (                    TaskSourceKind::Worktree {                        id: workree_id,                        abs_path: PathBuf::from("/the-root/b/.zed/tasks.json"),                        id_base: "local_tasks_for_worktree",                    },                    "cargo check".to_string(),                    vec!["check".to_string()],                    HashMap::default(),                ),            ]        );    });} #[gpui::test]async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) {    init_test(cx);     let fs = FakeFs::new(cx.executor());    fs.insert_tree(        "/the-root",        json!({            "test.rs": "const A: i32 = 1;",            "test2.rs": "",            "Cargo.toml": "a = 1",            "package.json": "{\"a\": 1}",        }),    )    .await;     let project = Project::test(fs.clone(), ["/the-root".as_ref()], cx).await;    let language_registry = project.read_with(cx, |project, _| project.languages().clone());     let mut fake_rust_servers = language_registry.register_fake_lsp_adapter(        "Rust",        FakeLspAdapter {            name: "the-rust-language-server",            capabilities: lsp::ServerCapabilities {                completion_provider: Some(lsp::CompletionOptions {                    trigger_characters: Some(vec![".".to_string(), "::".to_string()]),                    ..Default::default()                }),                ..Default::default()            },            ..Default::default()        },    );    let mut fake_json_servers = language_registry.register_fake_lsp_adapter(        "JSON",        FakeLspAdapter {            name: "the-json-language-server",            capabilities: lsp::ServerCapabilities {                completion_provider: Some(lsp::CompletionOptions {                    trigger_characters: Some(vec![":".to_string()]),                    ..Default::default()                }),                ..Default::default()            },            ..Default::default()        },    );     // Open a buffer without an associated language server.    let toml_buffer = project        .update(cx, |project, cx| {            project.open_local_buffer("/the-root/Cargo.toml", cx)        })        .await        .unwrap();     // Open a buffer with an associated language server before the language for it has been loaded.    let rust_buffer = project        .update(cx, |project, cx| {            project.open_local_buffer("/the-root/test.rs", cx)        })        .await        .unwrap();    rust_buffer.update(cx, |buffer, _| {        assert_eq!(buffer.language().map(|l| l.name()), None);    });     // Now we add the languages to the project, and ensure they get assigned to all    // the relevant open buffers.    language_registry.add(json_lang());    language_registry.add(rust_lang());    cx.executor().run_until_parked();    rust_buffer.update(cx, |buffer, _| {        assert_eq!(buffer.language().map(|l| l.name()), Some("Rust".into()));    });     // A server is started up, and it is notified about Rust files.    let mut fake_rust_server = fake_rust_servers.next().await.unwrap();    assert_eq!(        fake_rust_server            .receive_notification::<lsp::notification::DidOpenTextDocument>()            .await            .text_document,        lsp::TextDocumentItem {            uri: lsp::Url::from_file_path("/the-root/test.rs").unwrap(),            version: 0,            text: "const A: i32 = 1;".to_string(),            language_id: "rust".to_string(),        }    );     // The buffer is configured based on the language server's capabilities.    rust_buffer.update(cx, |buffer, _| {        assert_eq!(            buffer.completion_triggers(),            &[".".to_string(), "::".to_string()]        );    });    toml_buffer.update(cx, |buffer, _| {        assert!(buffer.completion_triggers().is_empty());    });     // Edit a buffer. The changes are reported to the language server.    rust_buffer.update(cx, |buffer, cx| buffer.edit([(16..16, "2")], None, cx));    assert_eq!(        fake_rust_server            .receive_notification::<lsp::notification::DidChangeTextDocument>()            .await            .text_document,        lsp::VersionedTextDocumentIdentifier::new(            lsp::Url::from_file_path("/the-root/test.rs").unwrap(),            1        )    );     // Open a third buffer with a different associated language server.    let json_buffer = project        .update(cx, |project, cx| {            project.open_local_buffer("/the-root/package.json", cx)        })        .await        .unwrap();     // A json language server is started up and is only notified about the json buffer.    let mut fake_json_server = fake_json_servers.next().await.unwrap();    assert_eq!(        fake_json_server            .receive_notification::<lsp::notification::DidOpenTextDocument>()            .await            .text_document,        lsp::TextDocumentItem {            uri: lsp::Url::from_file_path("/the-root/package.json").unwrap(),            version: 0,            text: "{\"a\": 1}".to_string(),            language_id: "json".to_string(),        }    );     // This buffer is configured based on the second language server's    // capabilities.    json_buffer.update(cx, |buffer, _| {        assert_eq!(buffer.completion_triggers(), &[":".to_string()]);    });     // When opening another buffer whose language server is already running,    // it is also configured based on the existing language server's capabilities.    let rust_buffer2 = project        .update(cx, |project, cx| {            project.open_local_buffer("/the-root/test2.rs", cx)        })        .await        .unwrap();    rust_buffer2.update(cx, |buffer, _| {        assert_eq!(            buffer.completion_triggers(),            &[".".to_string(), "::".to_string()]        );    });     // Changes are reported only to servers matching the buffer's language.    toml_buffer.update(cx, |buffer, cx| buffer.edit([(5..5, "23")], None, cx));    rust_buffer2.update(cx, |buffer, cx| {        buffer.edit([(0..0, "let x = 1;")], None, cx)    });    assert_eq!(        fake_rust_server            .receive_notification::<lsp::notification::DidChangeTextDocument>()            .await            .text_document,        lsp::VersionedTextDocumentIdentifier::new(            lsp::Url::from_file_path("/the-root/test2.rs").unwrap(),            1        )    );     // Save notifications are reported to all servers.    project        .update(cx, |project, cx| project.save_buffer(toml_buffer, cx))        .await        .unwrap();    assert_eq!(        fake_rust_server

Lines 1–500 of 5029.