16449 lines · 520.5 KB · Rust
RawDownload
#![allow(clippy::format_collect)] mod agent_registry_store;mod bookmark_store;mod color_extractor;mod context_server_store;mod debugger;mod dynamic_registration;mod git_store;mod image_store;mod lsp_command;mod lsp_store;mod manifest_tree;mod project_search;mod search;mod search_history;mod signature_help;mod task_inventory;mod trusted_worktrees;mod yarn; use anyhow::Result;use async_trait::async_trait;use buffer_diff::{    BufferDiff, DiffHunkSecondaryStatus, DiffHunkStatus, DiffHunkStatusKind, assert_hunks,};use collections::{BTreeSet, HashMap, HashSet};use encoding_rs;use fs::{FakeFs, PathEventKind, RealFs};use futures::{FutureExt as _, StreamExt, future};use git::{    GitHostingProviderRegistry,    repository::{RepoPath, repo_path},    status::{DiffStat, FileStatus, StatusCode, TrackedStatus},};use gpui::{    App, AppContext, BackgroundExecutor, BorrowAppContext, Entity, FutureExt, SharedString, Task,    TestAppContext, UpdateGlobal,};use itertools::Itertools;use language::{    Buffer, BufferEvent, Diagnostic, DiagnosticEntry, DiagnosticEntryRef, DiagnosticSet,    DiagnosticSourceKind, DiskState, FakeLspAdapter, Language, LanguageAwareStyling,    LanguageConfig, LanguageMatcher, LanguageName, LineEnding, ManifestName, ManifestProvider,    ManifestQuery, OffsetRangeExt, Point, ToPoint, Toolchain, ToolchainList, ToolchainLister,    ToolchainMetadata,    language_settings::{        Formatter, FormatterList, LanguageSettings, LanguageSettingsContent, LineEndingSetting,    },    markdown_lang, rust_lang, tree_sitter_typescript,};use lsp::{    CodeActionKind, DEFAULT_LSP_REQUEST_TIMEOUT, DiagnosticSeverity, DocumentChanges,    FileOperationFilter, LanguageServerId, LanguageServerName, NumberOrString, TextDocumentEdit,    Uri, WillRenameFiles, notification::DidRenameFiles,};use parking_lot::Mutex;use paths::{config_dir, global_gitignore_path, tasks_file};use postage::stream::Stream as _;use pretty_assertions::{assert_eq, assert_matches};use project::{    Event, TaskContexts,    git_store::{GitStoreEvent, Repository, RepositoryEvent, StatusEntry, pending_op},    search::{SearchQuery, SearchResult},    task_store::{TaskSettingsLocation, TaskStore},    *,};use rand::{Rng as _, rngs::StdRng};use serde_json::json;use settings::{GlobalLspSettingsContent, SettingsStore};#[cfg(target_os = "linux")]use settings::{LocalSettingsKind, LocalSettingsPath};#[cfg(not(windows))]use std::os;use std::{    cell::RefCell,    env, mem,    num::NonZeroU32,    ops::Range,    path::{Path, PathBuf},    process::Command,    rc::Rc,    str::FromStr,    sync::{Arc, OnceLock, atomic},    task::Poll,    time::Duration,};use sum_tree::SumTree;use task::{ResolvedTask, ShellKind, TaskContext};use text::{Anchor, PointUtf16, ReplicaId, ToOffset, Unclipped};use unindent::Unindent as _;use util::{    RandomCharIter, TryFutureExt as _, assert_set_eq, maybe, path,    paths::{PathMatcher, PathStyle},    rel_path::{RelPath, rel_path},    test::{TempTree, marked_text_offsets},    uri,};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 || {        #[cfg(not(target_os = "windows"))]        std::fs::metadata("/tmp").unwrap();        #[cfg(target_os = "windows")]        std::fs::metadata("C:/Windows").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;} #[gpui::test]async fn test_default_session_work_dirs_prefers_directory_worktrees_over_single_file_parents(    cx: &mut gpui::TestAppContext,) {    init_test(cx);     let fs = FakeFs::new(cx.executor());    fs.insert_tree(        path!("/root"),        json!({            "dir-project": {                "src": {                    "main.rs": "fn main() {}"                }            },            "single-file.rs": "fn helper() {}"        }),    )    .await;     let project = Project::test(        fs,        [            Path::new(path!("/root/single-file.rs")),            Path::new(path!("/root/dir-project")),        ],        cx,    )    .await;     let work_dirs = project.read_with(cx, |project, cx| project.default_path_list(cx));    let ordered_paths = work_dirs.ordered_paths().cloned().collect::<Vec<_>>();     assert_eq!(        ordered_paths,        vec![            PathBuf::from(path!("/root/dir-project")),            PathBuf::from(path!("/root")),        ]    );} #[gpui::test]async fn test_default_session_work_dirs_falls_back_to_home_for_empty_project(    cx: &mut gpui::TestAppContext,) {    init_test(cx);     let fs = FakeFs::new(cx.executor());    let project = Project::test(fs, [], cx).await;     let work_dirs = project.read_with(cx, |project, cx| project.default_path_list(cx));    let ordered_paths = work_dirs.ordered_paths().cloned().collect::<Vec<_>>();     assert_eq!(ordered_paths, vec![paths::home_dir().to_path_buf()]);} // NOTE:// While POSIX symbolic links are somewhat supported on Windows, they are an opt in by the user, and thus// we assume that they are not supported out of the box.#[cfg(not(windows))]#[gpui::test]async fn test_symlinks(cx: &mut gpui::TestAppContext) {    init_test(cx);    cx.executor().allow_parking();     let dir = TempTree::new(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::new(None, cx.executor())),        [root_link_path.as_ref()],        cx,    )    .await;     project.update(cx, |project, cx| {        let tree = project.worktrees(cx).next().unwrap().read(cx);        assert_eq!(tree.file_count(), 5);        assert_eq!(            tree.entry_for_path(rel_path("fennel/grape")).unwrap().inode,            tree.entry_for_path(rel_path("finnochio/grape"))                .unwrap()                .inode        );    });} #[gpui::test]async fn test_editorconfig_support(cx: &mut gpui::TestAppContext) {    init_test(cx);     let dir = TempTree::new(json!({        ".editorconfig": r#"        root = true        [*.rs]            indent_style = tab            indent_size = 3            end_of_line = lf            insert_final_newline = true            trim_trailing_whitespace = true            max_line_length = 120        [*.js]            tab_width = 10            trim_trailing_whitespace = false            max_line_length = off        [*.json]            trim_trailing_whitespace = true        "#,        ".zed": {            "settings.json": r#"{                "tab_size": 8,                "hard_tabs": false,                "ensure_final_newline_on_save": false,                "remove_trailing_whitespace_on_save": false,                "preferred_line_length": 64,                "soft_wrap": "editor_width",                "languages": {                    "JavaScript": {                        "remove_trailing_whitespace_on_save": true,                    },                    "JSON": {                        "remove_trailing_whitespace_on_save": true,                    },                    "Markdown": {                        "remove_trailing_whitespace_on_save": true,                    },                },            }"#,        },        "a.rs": "fn a() {\n    A\n}",        "b": {            ".editorconfig": r#"            [*.rs]                indent_size = 2                max_line_length = off,            "#,            "b.rs": "fn b() {\n    B\n}",        },        "c.js": "def c\n  C\nend",        "d": {            ".editorconfig": r#"            [*.rs]                indent_size = 1            "#,            "d.rs": "fn d() {\n    D\n}",        },        "e": {            ".editorconfig": r#"            [*.rs]                indent_size = 5                indent_style = space                max_line_length =            "#,            "e.rs": "fn e() {\n    E\n}",        },        "README.json": "tabs are better\n",        "README.md": "spaces are meaningful  \n",    }));     let path = dir.path();    let fs = FakeFs::new(cx.executor());    fs.insert_tree_from_real_fs(path, path).await;    let project = Project::test(fs, [path], cx).await;     let language_registry = project.read_with(cx, |project, _| project.languages().clone());    language_registry.add(js_lang());    language_registry.add(json_lang());    language_registry.add(markdown_lang());    language_registry.add(rust_lang());     let worktree = project.update(cx, |project, cx| project.worktrees(cx).next().unwrap());     cx.executor().run_until_parked();     let settings_for = async |path: &str, cx: &mut TestAppContext| -> LanguageSettings {        let buffer = project            .update(cx, |project, cx| {                project.open_buffer((worktree.read(cx).id(), rel_path(path)), cx)            })            .await            .unwrap();        cx.update(|cx| LanguageSettings::for_buffer(&buffer.read(cx), cx).into_owned())    };     let settings_a = settings_for("a.rs", cx).await;    let settings_b = settings_for("b/b.rs", cx).await;    let settings_c = settings_for("c.js", cx).await;    let settings_d = settings_for("d/d.rs", cx).await;    let settings_e = settings_for("e/e.rs", cx).await;    let settings_readme = settings_for("README.json", cx).await;    let settings_markdown = settings_for("README.md", cx).await;    // .editorconfig overrides .zed/settings    assert_eq!(Some(settings_a.tab_size), NonZeroU32::new(3));    assert_eq!(settings_a.hard_tabs, true);    assert_eq!(settings_a.ensure_final_newline_on_save, true);    // .editorconfig can disable trailing whitespace removal, but should not    // re-enable it when Zed settings have disabled it.    assert_eq!(settings_a.remove_trailing_whitespace_on_save, false);    assert_eq!(settings_a.line_ending, LineEndingSetting::EnforceLf);    assert_eq!(settings_a.preferred_line_length, 120);     // .editorconfig in b/ overrides .editorconfig in root    assert_eq!(Some(settings_b.tab_size), NonZeroU32::new(2));     // .editorconfig in subdirectory overrides .editorconfig in root    assert_eq!(Some(settings_d.tab_size), NonZeroU32::new(1));     // Non-empty values in e/ are parsed and applied as usual.    assert_eq!(Some(settings_e.tab_size), NonZeroU32::new(5));    assert_eq!(settings_e.hard_tabs, false);    // An empty value opts out of the inherited `max_line_length = 120`,    // falling back to .zed/settings.json instead of rejecting the whole file.    assert_eq!(settings_e.preferred_line_length, 64);     // "indent_size" is not set, so "tab_width" is used    assert_eq!(Some(settings_c.tab_size), NonZeroU32::new(10));    assert_eq!(settings_c.remove_trailing_whitespace_on_save, false);    assert_eq!(settings_readme.remove_trailing_whitespace_on_save, true);    assert_eq!(settings_markdown.remove_trailing_whitespace_on_save, true);     // When max_line_length is "off", default to .zed/settings.json    assert_eq!(settings_b.preferred_line_length, 64);    assert_eq!(settings_c.preferred_line_length, 64);     // README.md should not be affected by .editorconfig's globe "*.rs"    assert_eq!(Some(settings_readme.tab_size), NonZeroU32::new(8));} #[gpui::test]async fn test_external_editorconfig_support(cx: &mut gpui::TestAppContext) {    init_test(cx);     let fs = FakeFs::new(cx.executor());    fs.insert_tree(        path!("/grandparent"),        json!({            ".editorconfig": "[*]\nindent_size = 4\n",            "parent": {                ".editorconfig": "[*.rs]\nindent_size = 2\n",                "worktree": {                    ".editorconfig": "[*.md]\nindent_size = 3\n",                    "main.rs": "fn main() {}",                    "README.md": "# README",                    "other.txt": "other content",                }            }        }),    )    .await;     let project = Project::test(fs, [path!("/grandparent/parent/worktree").as_ref()], cx).await;     let language_registry = project.read_with(cx, |project, _| project.languages().clone());    language_registry.add(rust_lang());    language_registry.add(markdown_lang());     let worktree = project.update(cx, |project, cx| project.worktrees(cx).next().unwrap());     cx.executor().run_until_parked();    let settings_for = async |path: &str, cx: &mut TestAppContext| -> LanguageSettings {        let buffer = project            .update(cx, |project, cx| {                project.open_buffer((worktree.read(cx).id(), rel_path(path)), cx)            })            .await            .unwrap();        cx.update(|cx| LanguageSettings::for_buffer(&buffer.read(cx), cx).into_owned())    };     let settings_rs = settings_for("main.rs", cx).await;    let settings_md = settings_for("README.md", cx).await;    let settings_txt = settings_for("other.txt", cx).await;     // main.rs gets indent_size = 2 from parent's external .editorconfig    assert_eq!(Some(settings_rs.tab_size), NonZeroU32::new(2));     // README.md gets indent_size = 3 from internal worktree .editorconfig    assert_eq!(Some(settings_md.tab_size), NonZeroU32::new(3));     // other.txt gets indent_size = 4 from grandparent's external .editorconfig    assert_eq!(Some(settings_txt.tab_size), NonZeroU32::new(4));} #[gpui::test]async fn test_internal_editorconfig_root_stops_traversal(cx: &mut gpui::TestAppContext) {    init_test(cx);     let fs = FakeFs::new(cx.executor());    fs.insert_tree(        path!("/worktree"),        json!({            ".editorconfig": "[*]\nindent_size = 99\n",            "src": {                ".editorconfig": "root = true\n[*]\nindent_size = 2\n",                "file.rs": "fn main() {}",            }        }),    )    .await;     let project = Project::test(fs, [path!("/worktree").as_ref()], cx).await;     let language_registry = project.read_with(cx, |project, _| project.languages().clone());    language_registry.add(rust_lang());     let worktree = project.update(cx, |project, cx| project.worktrees(cx).next().unwrap());     cx.executor().run_until_parked();     let buffer = project        .update(cx, |project, cx| {            project.open_buffer((worktree.read(cx).id(), rel_path("src/file.rs")), cx)        })        .await        .unwrap();    cx.update(|cx| {        let settings = LanguageSettings::for_buffer(buffer.read(cx), cx).into_owned();        assert_eq!(Some(settings.tab_size), NonZeroU32::new(2));    });} #[gpui::test]async fn test_external_editorconfig_root_stops_traversal(cx: &mut gpui::TestAppContext) {    init_test(cx);     let fs = FakeFs::new(cx.executor());    fs.insert_tree(        path!("/parent"),        json!({            ".editorconfig": "[*]\nindent_size = 99\n",            "worktree": {                ".editorconfig": "root = true\n[*]\nindent_size = 2\n",                "file.rs": "fn main() {}",            }        }),    )    .await;     let project = Project::test(fs, [path!("/parent/worktree").as_ref()], cx).await;     let language_registry = project.read_with(cx, |project, _| project.languages().clone());    language_registry.add(rust_lang());     let worktree = project.update(cx, |project, cx| project.worktrees(cx).next().unwrap());     cx.executor().run_until_parked();

Lines 1–500 of 16449.