1087 lines · 38.6 KB · Rust
RawDownload
use super::*;use collections::HashMap;use editor::{    DisplayPoint, GutterDimensions,    display_map::{Block, BlockContext, DisplayRow},};use gpui::{AvailableSpace, Stateful, TestAppContext, VisualTestContext, px};use language::{    Diagnostic, DiagnosticEntry, DiagnosticSeverity, OffsetRangeExt, PointUtf16, Rope, Unclipped,};use pretty_assertions::assert_eq;use project::FakeFs;use rand::{Rng, rngs::StdRng, seq::IteratorRandom as _};use serde_json::json;use settings::SettingsStore;use std::{    env,    path::{Path, PathBuf},};use unindent::Unindent as _;use util::{RandomCharIter, path, post_inc}; #[ctor::ctor]fn init_logger() {    if env::var("RUST_LOG").is_ok() {        env_logger::init();    }} #[gpui::test]async fn test_diagnostics(cx: &mut TestAppContext) {    init_test(cx);     let fs = FakeFs::new(cx.executor());    fs.insert_tree(        path!("/test"),        json!({            "consts.rs": "                const a: i32 = 'a';                const b: i32 = c;            "            .unindent(),             "main.rs": "                fn main() {                    let x = vec![];                    let y = vec![];                    a(x);                    b(y);                    // comment 1                    // comment 2                    c(y);                    d(x);                }            "            .unindent(),        }),    )    .await;     let language_server_id = LanguageServerId(0);    let project = Project::test(fs.clone(), [path!("/test").as_ref()], cx).await;    let lsp_store = project.read_with(cx, |project, _| project.lsp_store());    let window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));    let cx = &mut VisualTestContext::from_window(*window, cx);    let workspace = window.root(cx).unwrap();     // Create some diagnostics    lsp_store.update(cx, |lsp_store, cx| {        lsp_store            .update_diagnostic_entries(                language_server_id,                PathBuf::from(path!("/test/main.rs")),                None,                vec![                    DiagnosticEntry {                        range: Unclipped(PointUtf16::new(1, 8))..Unclipped(PointUtf16::new(1, 9)),                        diagnostic: Diagnostic {                            message:                                "move occurs because `x` has type `Vec<char>`, which does not implement the `Copy` trait"                                    .to_string(),                            severity: DiagnosticSeverity::INFORMATION,                            is_primary: false,                            is_disk_based: true,                            group_id: 1,                            ..Default::default()                        },                    },                    DiagnosticEntry {                        range: Unclipped(PointUtf16::new(2, 8))..Unclipped(PointUtf16::new(2, 9)),                        diagnostic: Diagnostic {                            message:                                "move occurs because `y` has type `Vec<char>`, which does not implement the `Copy` trait"                                    .to_string(),                            severity: DiagnosticSeverity::INFORMATION,                            is_primary: false,                            is_disk_based: true,                            group_id: 0,                            ..Default::default()                        },                    },                    DiagnosticEntry {                        range: Unclipped(PointUtf16::new(3, 6))..Unclipped(PointUtf16::new(3, 7)),                        diagnostic: Diagnostic {                            message: "value moved here".to_string(),                            severity: DiagnosticSeverity::INFORMATION,                            is_primary: false,                            is_disk_based: true,                            group_id: 1,                            ..Default::default()                        },                    },                    DiagnosticEntry {                        range: Unclipped(PointUtf16::new(4, 6))..Unclipped(PointUtf16::new(4, 7)),                        diagnostic: Diagnostic {                            message: "value moved here".to_string(),                            severity: DiagnosticSeverity::INFORMATION,                            is_primary: false,                            is_disk_based: true,                            group_id: 0,                            ..Default::default()                        },                    },                    DiagnosticEntry {                        range: Unclipped(PointUtf16::new(7, 6))..Unclipped(PointUtf16::new(7, 7)),                        diagnostic: Diagnostic {                            message: "use of moved value\nvalue used here after move".to_string(),                            severity: DiagnosticSeverity::ERROR,                            is_primary: true,                            is_disk_based: true,                            group_id: 0,                            ..Default::default()                        },                    },                    DiagnosticEntry {                        range: Unclipped(PointUtf16::new(8, 6))..Unclipped(PointUtf16::new(8, 7)),                        diagnostic: Diagnostic {                            message: "use of moved value\nvalue used here after move".to_string(),                            severity: DiagnosticSeverity::ERROR,                            is_primary: true,                            is_disk_based: true,                            group_id: 1,                            ..Default::default()                        },                    },                ],                cx,            )            .unwrap();    });     // Open the project diagnostics view while there are already diagnostics.    let diagnostics = window.build_entity(cx, |window, cx| {        ProjectDiagnosticsEditor::new_with_context(            1,            true,            project.clone(),            workspace.downgrade(),            window,            cx,        )    });    let editor = diagnostics.update(cx, |diagnostics, _| diagnostics.editor.clone());     diagnostics        .next_notification(DIAGNOSTICS_UPDATE_DEBOUNCE + Duration::from_millis(10), cx)        .await;    assert_eq!(        editor_blocks(&editor, cx),        [            (DisplayRow(0), FILE_HEADER.into()),            (DisplayRow(2), DIAGNOSTIC_HEADER.into()),            (DisplayRow(15), EXCERPT_HEADER.into()),            (DisplayRow(16), DIAGNOSTIC_HEADER.into()),            (DisplayRow(25), EXCERPT_HEADER.into()),        ]    );    assert_eq!(        editor.update(cx, |editor, cx| editor.display_text(cx)),        concat!(            //            // main.rs            //            "\n", // filename            "\n", // padding            // diagnostic group 1            "\n", // primary message            "\n", // padding            "    let x = vec![];\n",            "    let y = vec![];\n",            "\n", // supporting diagnostic            "    a(x);\n",            "    b(y);\n",            "\n", // supporting diagnostic            "    // comment 1\n",            "    // comment 2\n",            "    c(y);\n",            "\n", // supporting diagnostic            "    d(x);\n",            "\n", // context ellipsis            // diagnostic group 2            "\n", // primary message            "\n", // padding            "fn main() {\n",            "    let x = vec![];\n",            "\n", // supporting diagnostic            "    let y = vec![];\n",            "    a(x);\n",            "\n", // supporting diagnostic            "    b(y);\n",            "\n", // context ellipsis            "    c(y);\n",            "    d(x);\n",            "\n", // supporting diagnostic            "}",        )    );     // Cursor is at the first diagnostic    editor.update(cx, |editor, cx| {        assert_eq!(            editor.selections.display_ranges(cx),            [DisplayPoint::new(DisplayRow(12), 6)..DisplayPoint::new(DisplayRow(12), 6)]        );    });     // Diagnostics are added for another earlier path.    lsp_store.update(cx, |lsp_store, cx| {        lsp_store.disk_based_diagnostics_started(language_server_id, cx);        lsp_store            .update_diagnostic_entries(                language_server_id,                PathBuf::from(path!("/test/consts.rs")),                None,                vec![DiagnosticEntry {                    range: Unclipped(PointUtf16::new(0, 15))..Unclipped(PointUtf16::new(0, 15)),                    diagnostic: Diagnostic {                        message: "mismatched types\nexpected `usize`, found `char`".to_string(),                        severity: DiagnosticSeverity::ERROR,                        is_primary: true,                        is_disk_based: true,                        group_id: 0,                        ..Default::default()                    },                }],                cx,            )            .unwrap();        lsp_store.disk_based_diagnostics_finished(language_server_id, cx);    });     diagnostics        .next_notification(DIAGNOSTICS_UPDATE_DEBOUNCE + Duration::from_millis(10), cx)        .await;    assert_eq!(        editor_blocks(&editor, cx),        [            (DisplayRow(0), FILE_HEADER.into()),            (DisplayRow(2), DIAGNOSTIC_HEADER.into()),            (DisplayRow(7), FILE_HEADER.into()),            (DisplayRow(9), DIAGNOSTIC_HEADER.into()),            (DisplayRow(22), EXCERPT_HEADER.into()),            (DisplayRow(23), DIAGNOSTIC_HEADER.into()),            (DisplayRow(32), EXCERPT_HEADER.into()),        ]    );     assert_eq!(        editor.update(cx, |editor, cx| editor.display_text(cx)),        concat!(            //            // consts.rs            //            "\n", // filename            "\n", // padding            // diagnostic group 1            "\n", // primary message            "\n", // padding            "const a: i32 = 'a';\n",            "\n", // supporting diagnostic            "const b: i32 = c;\n",            //            // main.rs            //            "\n", // filename            "\n", // padding            // diagnostic group 1            "\n", // primary message            "\n", // padding            "    let x = vec![];\n",            "    let y = vec![];\n",            "\n", // supporting diagnostic            "    a(x);\n",            "    b(y);\n",            "\n", // supporting diagnostic            "    // comment 1\n",            "    // comment 2\n",            "    c(y);\n",            "\n", // supporting diagnostic            "    d(x);\n",            "\n", // collapsed context            // diagnostic group 2            "\n", // primary message            "\n", // filename            "fn main() {\n",            "    let x = vec![];\n",            "\n", // supporting diagnostic            "    let y = vec![];\n",            "    a(x);\n",            "\n", // supporting diagnostic            "    b(y);\n",            "\n", // context ellipsis            "    c(y);\n",            "    d(x);\n",            "\n", // supporting diagnostic            "}",        )    );     // Cursor keeps its position.    editor.update(cx, |editor, cx| {        assert_eq!(            editor.selections.display_ranges(cx),            [DisplayPoint::new(DisplayRow(19), 6)..DisplayPoint::new(DisplayRow(19), 6)]        );    });     // Diagnostics are added to the first path    lsp_store.update(cx, |lsp_store, cx| {        lsp_store.disk_based_diagnostics_started(language_server_id, cx);        lsp_store            .update_diagnostic_entries(                language_server_id,                PathBuf::from(path!("/test/consts.rs")),                None,                vec![                    DiagnosticEntry {                        range: Unclipped(PointUtf16::new(0, 15))..Unclipped(PointUtf16::new(0, 15)),                        diagnostic: Diagnostic {                            message: "mismatched types\nexpected `usize`, found `char`".to_string(),                            severity: DiagnosticSeverity::ERROR,                            is_primary: true,                            is_disk_based: true,                            group_id: 0,                            ..Default::default()                        },                    },                    DiagnosticEntry {                        range: Unclipped(PointUtf16::new(1, 15))..Unclipped(PointUtf16::new(1, 15)),                        diagnostic: Diagnostic {                            message: "unresolved name `c`".to_string(),                            severity: DiagnosticSeverity::ERROR,                            is_primary: true,                            is_disk_based: true,                            group_id: 1,                            ..Default::default()                        },                    },                ],                cx,            )            .unwrap();        lsp_store.disk_based_diagnostics_finished(language_server_id, cx);    });     diagnostics        .next_notification(DIAGNOSTICS_UPDATE_DEBOUNCE + Duration::from_millis(10), cx)        .await;    assert_eq!(        editor_blocks(&editor, cx),        [            (DisplayRow(0), FILE_HEADER.into()),            (DisplayRow(2), DIAGNOSTIC_HEADER.into()),            (DisplayRow(7), EXCERPT_HEADER.into()),            (DisplayRow(8), DIAGNOSTIC_HEADER.into()),            (DisplayRow(13), FILE_HEADER.into()),            (DisplayRow(15), DIAGNOSTIC_HEADER.into()),            (DisplayRow(28), EXCERPT_HEADER.into()),            (DisplayRow(29), DIAGNOSTIC_HEADER.into()),            (DisplayRow(38), EXCERPT_HEADER.into()),        ]    );     assert_eq!(        editor.update(cx, |editor, cx| editor.display_text(cx)),        concat!(            //            // consts.rs            //            "\n", // filename            "\n", // padding            // diagnostic group 1            "\n", // primary message            "\n", // padding            "const a: i32 = 'a';\n",            "\n", // supporting diagnostic            "const b: i32 = c;\n",            "\n", // context ellipsis            // diagnostic group 2            "\n", // primary message            "\n", // padding            "const a: i32 = 'a';\n",            "const b: i32 = c;\n",            "\n", // supporting diagnostic            //            // main.rs            //            "\n", // filename            "\n", // padding            // diagnostic group 1            "\n", // primary message            "\n", // padding            "    let x = vec![];\n",            "    let y = vec![];\n",            "\n", // supporting diagnostic            "    a(x);\n",            "    b(y);\n",            "\n", // supporting diagnostic            "    // comment 1\n",            "    // comment 2\n",            "    c(y);\n",            "\n", // supporting diagnostic            "    d(x);\n",            "\n", // context ellipsis            // diagnostic group 2            "\n", // primary message            "\n", // filename            "fn main() {\n",            "    let x = vec![];\n",            "\n", // supporting diagnostic            "    let y = vec![];\n",            "    a(x);\n",            "\n", // supporting diagnostic            "    b(y);\n",            "\n", // context ellipsis            "    c(y);\n",            "    d(x);\n",            "\n", // supporting diagnostic            "}",        )    );} #[gpui::test]async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {    init_test(cx);     let fs = FakeFs::new(cx.executor());    fs.insert_tree(        path!("/test"),        json!({            "main.js": "                a();                b();                c();                d();                e();            ".unindent()        }),    )    .await;     let server_id_1 = LanguageServerId(100);    let server_id_2 = LanguageServerId(101);    let project = Project::test(fs.clone(), [path!("/test").as_ref()], cx).await;    let lsp_store = project.read_with(cx, |project, _| project.lsp_store());    let window = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));    let cx = &mut VisualTestContext::from_window(*window, cx);    let workspace = window.root(cx).unwrap();     let diagnostics = window.build_entity(cx, |window, cx| {        ProjectDiagnosticsEditor::new_with_context(            1,            true,            project.clone(),            workspace.downgrade(),            window,            cx,        )    });    let editor = diagnostics.update(cx, |diagnostics, _| diagnostics.editor.clone());     // Two language servers start updating diagnostics    lsp_store.update(cx, |lsp_store, cx| {        lsp_store.disk_based_diagnostics_started(server_id_1, cx);        lsp_store.disk_based_diagnostics_started(server_id_2, cx);        lsp_store            .update_diagnostic_entries(                server_id_1,                PathBuf::from(path!("/test/main.js")),                None,                vec![DiagnosticEntry {                    range: Unclipped(PointUtf16::new(0, 0))..Unclipped(PointUtf16::new(0, 1)),                    diagnostic: Diagnostic {                        message: "error 1".to_string(),                        severity: DiagnosticSeverity::WARNING,                        is_primary: true,                        is_disk_based: true,                        group_id: 1,                        ..Default::default()

Lines 1–500 of 1087.