|  | //! The most high-level integrated tests for rust-analyzer. | 
|  | //! | 
|  | //! This tests run a full LSP event loop, spawn cargo and process stdlib from | 
|  | //! sysroot. For this reason, the tests here are very slow, and should be | 
|  | //! avoided unless absolutely necessary. | 
|  | //! | 
|  | //! In particular, it's fine *not* to test that client & server agree on | 
|  | //! specific JSON shapes here -- there's little value in such tests, as we can't | 
|  | //! be sure without a real client anyway. | 
|  |  | 
|  | #![allow(clippy::disallowed_types)] | 
|  |  | 
|  | mod cli; | 
|  | mod ratoml; | 
|  | mod support; | 
|  | mod testdir; | 
|  |  | 
|  | use std::{collections::HashMap, path::PathBuf, time::Instant}; | 
|  |  | 
|  | use lsp_types::{ | 
|  | CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams, | 
|  | DocumentFormattingParams, DocumentRangeFormattingParams, FileRename, FormattingOptions, | 
|  | GotoDefinitionParams, HoverParams, InlayHint, InlayHintLabel, InlayHintParams, | 
|  | PartialResultParams, Position, Range, RenameFilesParams, TextDocumentItem, | 
|  | TextDocumentPositionParams, WorkDoneProgressParams, | 
|  | notification::DidOpenTextDocument, | 
|  | request::{ | 
|  | CodeActionRequest, Completion, Formatting, GotoTypeDefinition, HoverRequest, | 
|  | InlayHintRequest, InlayHintResolveRequest, RangeFormatting, WillRenameFiles, | 
|  | WorkspaceSymbolRequest, | 
|  | }, | 
|  | }; | 
|  | use rust_analyzer::lsp::ext::{OnEnter, Runnables, RunnablesParams}; | 
|  | use serde_json::json; | 
|  | use stdx::format_to_acc; | 
|  |  | 
|  | use test_utils::skip_slow_tests; | 
|  | use testdir::TestDir; | 
|  |  | 
|  | use crate::support::{Project, project}; | 
|  |  | 
|  | #[test] | 
|  | fn completes_items_from_standard_library() { | 
|  | if skip_slow_tests() { | 
|  | return; | 
|  | } | 
|  |  | 
|  | let server = Project::with_fixture( | 
|  | r#" | 
|  | //- /Cargo.toml | 
|  | [package] | 
|  | name = "foo" | 
|  | version = "0.0.0" | 
|  |  | 
|  | //- /src/lib.rs | 
|  | use std::collections::Spam; | 
|  | "#, | 
|  | ) | 
|  | .with_config(serde_json::json!({ | 
|  | "cargo": { "sysroot": "discover" }, | 
|  | })) | 
|  | .server() | 
|  | .wait_until_workspace_is_loaded(); | 
|  |  | 
|  | let res = server.send_request::<Completion>(CompletionParams { | 
|  | text_document_position: TextDocumentPositionParams::new( | 
|  | server.doc_id("src/lib.rs"), | 
|  | Position::new(0, 23), | 
|  | ), | 
|  | context: None, | 
|  | partial_result_params: PartialResultParams::default(), | 
|  | work_done_progress_params: WorkDoneProgressParams::default(), | 
|  | }); | 
|  | assert!(res.to_string().contains("HashMap")); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn resolves_inlay_hints() { | 
|  | if skip_slow_tests() { | 
|  | return; | 
|  | } | 
|  |  | 
|  | let server = Project::with_fixture( | 
|  | r#" | 
|  | //- /Cargo.toml | 
|  | [package] | 
|  | name = "foo" | 
|  | version = "0.0.0" | 
|  |  | 
|  | //- /src/lib.rs | 
|  | struct Foo; | 
|  | fn f() { | 
|  | let x = Foo; | 
|  | } | 
|  | "#, | 
|  | ) | 
|  | .server() | 
|  | .wait_until_workspace_is_loaded(); | 
|  |  | 
|  | let res = server.send_request::<InlayHintRequest>(InlayHintParams { | 
|  | range: Range::new(Position::new(0, 0), Position::new(3, 1)), | 
|  | text_document: server.doc_id("src/lib.rs"), | 
|  | work_done_progress_params: WorkDoneProgressParams::default(), | 
|  | }); | 
|  | let mut hints = serde_json::from_value::<Option<Vec<InlayHint>>>(res).unwrap().unwrap(); | 
|  | let hint = hints.pop().unwrap(); | 
|  | assert!(hint.data.is_some()); | 
|  | assert!( | 
|  | matches!(&hint.label, InlayHintLabel::LabelParts(parts) if parts[1].location.is_none()) | 
|  | ); | 
|  | let res = server.send_request::<InlayHintResolveRequest>(hint); | 
|  | let hint = serde_json::from_value::<InlayHint>(res).unwrap(); | 
|  | assert!(hint.data.is_none()); | 
|  | assert!( | 
|  | matches!(&hint.label, InlayHintLabel::LabelParts(parts) if parts[1].location.is_some()) | 
|  | ); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn completes_items_from_standard_library_in_cargo_script() { | 
|  | // this test requires nightly so CI can't run it | 
|  | if skip_slow_tests() || std::env::var("CI").is_ok() { | 
|  | return; | 
|  | } | 
|  |  | 
|  | let server = Project::with_fixture( | 
|  | r#" | 
|  | //- /dependency/Cargo.toml | 
|  | [package] | 
|  | name = "dependency" | 
|  | version = "0.1.0" | 
|  | //- /dependency/src/lib.rs | 
|  | pub struct SpecialHashMap; | 
|  | //- /dependency2/Cargo.toml | 
|  | [package] | 
|  | name = "dependency2" | 
|  | version = "0.1.0" | 
|  | //- /dependency2/src/lib.rs | 
|  | pub struct SpecialHashMap2; | 
|  | //- /src/lib.rs | 
|  | #!/usr/bin/env -S cargo +nightly -Zscript | 
|  | --- | 
|  | [dependencies] | 
|  | dependency = { path = "../dependency" } | 
|  | --- | 
|  | use dependency::Spam; | 
|  | use dependency2::Spam; | 
|  | "#, | 
|  | ) | 
|  | .with_config(serde_json::json!({ | 
|  | "cargo": { "sysroot": null }, | 
|  | "linkedProjects": ["src/lib.rs"], | 
|  | })) | 
|  | .server() | 
|  | .wait_until_workspace_is_loaded(); | 
|  |  | 
|  | let res = server.send_request::<Completion>(CompletionParams { | 
|  | text_document_position: TextDocumentPositionParams::new( | 
|  | server.doc_id("src/lib.rs"), | 
|  | Position::new(5, 18), | 
|  | ), | 
|  | context: None, | 
|  | partial_result_params: PartialResultParams::default(), | 
|  | work_done_progress_params: WorkDoneProgressParams::default(), | 
|  | }); | 
|  | assert!(res.to_string().contains("SpecialHashMap"), "{}", res.to_string()); | 
|  |  | 
|  | let res = server.send_request::<Completion>(CompletionParams { | 
|  | text_document_position: TextDocumentPositionParams::new( | 
|  | server.doc_id("src/lib.rs"), | 
|  | Position::new(6, 18), | 
|  | ), | 
|  | context: None, | 
|  | partial_result_params: PartialResultParams::default(), | 
|  | work_done_progress_params: WorkDoneProgressParams::default(), | 
|  | }); | 
|  | assert!(!res.to_string().contains("SpecialHashMap")); | 
|  |  | 
|  | server.write_file_and_save( | 
|  | "src/lib.rs", | 
|  | r#"#!/usr/bin/env -S cargo +nightly -Zscript | 
|  | --- | 
|  | [dependencies] | 
|  | dependency2 = { path = "../dependency2" } | 
|  | --- | 
|  | use dependency::Spam; | 
|  | use dependency2::Spam; | 
|  | "# | 
|  | .to_owned(), | 
|  | ); | 
|  |  | 
|  | let server = server.wait_until_workspace_is_loaded(); | 
|  |  | 
|  | std::thread::sleep(std::time::Duration::from_secs(3)); | 
|  |  | 
|  | let res = server.send_request::<Completion>(CompletionParams { | 
|  | text_document_position: TextDocumentPositionParams::new( | 
|  | server.doc_id("src/lib.rs"), | 
|  | Position::new(5, 18), | 
|  | ), | 
|  | context: None, | 
|  | partial_result_params: PartialResultParams::default(), | 
|  | work_done_progress_params: WorkDoneProgressParams::default(), | 
|  | }); | 
|  | assert!(!res.to_string().contains("SpecialHashMap")); | 
|  |  | 
|  | let res = server.send_request::<Completion>(CompletionParams { | 
|  | text_document_position: TextDocumentPositionParams::new( | 
|  | server.doc_id("src/lib.rs"), | 
|  | Position::new(6, 18), | 
|  | ), | 
|  | context: None, | 
|  | partial_result_params: PartialResultParams::default(), | 
|  | work_done_progress_params: WorkDoneProgressParams::default(), | 
|  | }); | 
|  | assert!(res.to_string().contains("SpecialHashMap")); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn test_runnables_project() { | 
|  | if skip_slow_tests() { | 
|  | return; | 
|  | } | 
|  |  | 
|  | let server = Project::with_fixture( | 
|  | r#" | 
|  | //- /foo/Cargo.toml | 
|  | [package] | 
|  | name = "foo" | 
|  | version = "0.0.0" | 
|  |  | 
|  | //- /foo/src/lib.rs | 
|  | pub fn foo() {} | 
|  |  | 
|  | //- /foo/tests/spam.rs | 
|  | #[test] | 
|  | fn test_eggs() {} | 
|  |  | 
|  | //- /bar/Cargo.toml | 
|  | [package] | 
|  | name = "bar" | 
|  | version = "0.0.0" | 
|  |  | 
|  | //- /bar/src/main.rs | 
|  | fn main() {} | 
|  | "#, | 
|  | ) | 
|  | .root("foo") | 
|  | .root("bar") | 
|  | .server() | 
|  | .wait_until_workspace_is_loaded(); | 
|  |  | 
|  | server.request::<Runnables>( | 
|  | RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None }, | 
|  | json!([ | 
|  | { | 
|  | "args": { | 
|  | "cargoArgs": ["test", "--package", "foo", "--test", "spam"], | 
|  | "executableArgs": ["test_eggs", "--exact", "--show-output"], | 
|  | "overrideCargo": null, | 
|  | "cwd": server.path().join("foo"), | 
|  | "workspaceRoot": server.path().join("foo") | 
|  | }, | 
|  | "kind": "cargo", | 
|  | "label": "test test_eggs", | 
|  | "location": { | 
|  | "targetRange": { | 
|  | "end": { "character": 17, "line": 1 }, | 
|  | "start": { "character": 0, "line": 0 } | 
|  | }, | 
|  | "targetSelectionRange": { | 
|  | "end": { "character": 12, "line": 1 }, | 
|  | "start": { "character": 3, "line": 1 } | 
|  | }, | 
|  | "targetUri": "file:///[..]/tests/spam.rs" | 
|  | } | 
|  | }, | 
|  | { | 
|  | "args": { | 
|  | "overrideCargo": null, | 
|  | "cwd": server.path().join("foo"), | 
|  | "workspaceRoot": server.path().join("foo"), | 
|  | "cargoArgs": [ | 
|  | "test", | 
|  | "--package", | 
|  | "foo", | 
|  | "--test", | 
|  | "spam" | 
|  | ], | 
|  | "executableArgs": [ | 
|  | "", | 
|  | "--show-output" | 
|  | ] | 
|  | }, | 
|  | "kind": "cargo", | 
|  | "label": "test-mod ", | 
|  | "location": { | 
|  | "targetUri": "file:///[..]/tests/spam.rs", | 
|  | "targetRange": { | 
|  | "start": { | 
|  | "line": 0, | 
|  | "character": 0 | 
|  | }, | 
|  | "end": { | 
|  | "line": 3, | 
|  | "character": 0 | 
|  | } | 
|  | }, | 
|  | "targetSelectionRange": { | 
|  | "start": { | 
|  | "line": 0, | 
|  | "character": 0 | 
|  | }, | 
|  | "end": { | 
|  | "line": 3, | 
|  | "character": 0 | 
|  | } | 
|  | } | 
|  | }, | 
|  | }, | 
|  | { | 
|  | "args": { | 
|  | "cargoArgs": ["check", "--package", "foo", "--all-targets"], | 
|  | "executableArgs": [], | 
|  | "overrideCargo": null, | 
|  | "cwd": server.path().join("foo"), | 
|  | "workspaceRoot": server.path().join("foo") | 
|  | }, | 
|  | "kind": "cargo", | 
|  | "label": "cargo check -p foo --all-targets" | 
|  | }, | 
|  | { | 
|  | "args": { | 
|  | "cargoArgs": ["test", "--package", "foo", "--all-targets"], | 
|  | "executableArgs": [], | 
|  | "overrideCargo": null, | 
|  | "cwd": server.path().join("foo"), | 
|  | "workspaceRoot": server.path().join("foo") | 
|  | }, | 
|  | "kind": "cargo", | 
|  | "label": "cargo test -p foo --all-targets" | 
|  | } | 
|  | ]), | 
|  | ); | 
|  | } | 
|  |  | 
|  | // Each package in these workspaces should be run from its own root | 
|  | #[test] | 
|  | fn test_path_dependency_runnables() { | 
|  | if skip_slow_tests() { | 
|  | return; | 
|  | } | 
|  |  | 
|  | let server = Project::with_fixture( | 
|  | r#" | 
|  | //- /consumer/Cargo.toml | 
|  | [package] | 
|  | name = "consumer" | 
|  | version = "0.1.0" | 
|  | [dependencies] | 
|  | dependency = { path = "../dependency" } | 
|  |  | 
|  | //- /consumer/src/lib.rs | 
|  | #[cfg(test)] | 
|  | mod tests { | 
|  | #[test] | 
|  | fn consumer() {} | 
|  | } | 
|  |  | 
|  | //- /dependency/Cargo.toml | 
|  | [package] | 
|  | name = "dependency" | 
|  | version = "0.1.0" | 
|  | [dev-dependencies] | 
|  | devdependency = { path = "../devdependency" } | 
|  |  | 
|  | //- /dependency/src/lib.rs | 
|  | #[cfg(test)] | 
|  | mod tests { | 
|  | #[test] | 
|  | fn dependency() {} | 
|  | } | 
|  |  | 
|  | //- /devdependency/Cargo.toml | 
|  | [package] | 
|  | name = "devdependency" | 
|  | version = "0.1.0" | 
|  |  | 
|  | //- /devdependency/src/lib.rs | 
|  | #[cfg(test)] | 
|  | mod tests { | 
|  | #[test] | 
|  | fn devdependency() {} | 
|  | } | 
|  | "#, | 
|  | ) | 
|  | .root("consumer") | 
|  | .root("dependency") | 
|  | .root("devdependency") | 
|  | .server() | 
|  | .wait_until_workspace_is_loaded(); | 
|  |  | 
|  | for runnable in ["consumer", "dependency", "devdependency"] { | 
|  | server.request::<Runnables>( | 
|  | RunnablesParams { | 
|  | text_document: server.doc_id(&format!("{runnable}/src/lib.rs")), | 
|  | position: None, | 
|  | }, | 
|  | json!([ | 
|  | "{...}", | 
|  | { | 
|  | "label": "cargo test -p [..] --all-targets", | 
|  | "kind": "cargo", | 
|  | "args": { | 
|  | "overrideCargo": null, | 
|  | "workspaceRoot": server.path().join(runnable), | 
|  | "cwd": server.path().join(runnable), | 
|  | "cargoArgs": [ | 
|  | "test", | 
|  | "--package", | 
|  | runnable, | 
|  | "--all-targets" | 
|  | ], | 
|  | "executableArgs": [] | 
|  | }, | 
|  | }, | 
|  | "{...}", | 
|  | "{...}" | 
|  | ]), | 
|  | ); | 
|  | } | 
|  | } | 
|  |  | 
|  | // The main fn in packages should be run from the workspace root | 
|  | #[test] | 
|  | fn test_runnables_cwd() { | 
|  | if skip_slow_tests() { | 
|  | return; | 
|  | } | 
|  |  | 
|  | let server = Project::with_fixture( | 
|  | r#" | 
|  | //- /foo/Cargo.toml | 
|  | [workspace] | 
|  | members = ["mainpkg", "otherpkg"] | 
|  |  | 
|  | //- /foo/mainpkg/Cargo.toml | 
|  | [package] | 
|  | name = "mainpkg" | 
|  | version = "0.1.0" | 
|  |  | 
|  | //- /foo/mainpkg/src/main.rs | 
|  | fn main() {} | 
|  |  | 
|  | //- /foo/otherpkg/Cargo.toml | 
|  | [package] | 
|  | name = "otherpkg" | 
|  | version = "0.1.0" | 
|  |  | 
|  | //- /foo/otherpkg/src/lib.rs | 
|  | #[test] | 
|  | fn otherpkg() {} | 
|  | "#, | 
|  | ) | 
|  | .root("foo") | 
|  | .server() | 
|  | .wait_until_workspace_is_loaded(); | 
|  |  | 
|  | server.request::<Runnables>( | 
|  | RunnablesParams { text_document: server.doc_id("foo/mainpkg/src/main.rs"), position: None }, | 
|  | json!([ | 
|  | "{...}", | 
|  | { | 
|  | "label": "cargo test -p mainpkg --all-targets", | 
|  | "kind": "cargo", | 
|  | "args": { | 
|  | "overrideCargo": null, | 
|  | "workspaceRoot": server.path().join("foo"), | 
|  | "cwd": server.path().join("foo"), | 
|  | "cargoArgs": [ | 
|  | "test", | 
|  | "--package", | 
|  | "mainpkg", | 
|  | "--all-targets" | 
|  | ], | 
|  | "executableArgs": [] | 
|  | }, | 
|  | }, | 
|  | "{...}", | 
|  | "{...}" | 
|  | ]), | 
|  | ); | 
|  |  | 
|  | server.request::<Runnables>( | 
|  | RunnablesParams { text_document: server.doc_id("foo/otherpkg/src/lib.rs"), position: None }, | 
|  | json!([ | 
|  | "{...}", | 
|  | { | 
|  | "label": "cargo test -p otherpkg --all-targets", | 
|  | "kind": "cargo", | 
|  | "args": { | 
|  | "overrideCargo": null, | 
|  | "workspaceRoot": server.path().join("foo"), | 
|  | "cwd": server.path().join("foo").join("otherpkg"), | 
|  | "cargoArgs": [ | 
|  | "test", | 
|  | "--package", | 
|  | "otherpkg", | 
|  | "--all-targets" | 
|  | ], | 
|  | "executableArgs": [] | 
|  | }, | 
|  | }, | 
|  | "{...}", | 
|  | "{...}" | 
|  | ]), | 
|  | ); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn test_format_document() { | 
|  | if skip_slow_tests() { | 
|  | return; | 
|  | } | 
|  |  | 
|  | let server = project( | 
|  | r#" | 
|  | //- /Cargo.toml | 
|  | [package] | 
|  | name = "foo" | 
|  | version = "0.0.0" | 
|  |  | 
|  | //- /src/lib.rs | 
|  | mod bar; | 
|  |  | 
|  | fn main() { | 
|  | } | 
|  |  | 
|  | pub use std::collections::HashMap; | 
|  | "#, | 
|  | ) | 
|  | .wait_until_workspace_is_loaded(); | 
|  |  | 
|  | server.request::<Formatting>( | 
|  | DocumentFormattingParams { | 
|  | text_document: server.doc_id("src/lib.rs"), | 
|  | options: FormattingOptions { | 
|  | tab_size: 4, | 
|  | insert_spaces: false, | 
|  | insert_final_newline: None, | 
|  | trim_final_newlines: None, | 
|  | trim_trailing_whitespace: None, | 
|  | properties: HashMap::new(), | 
|  | }, | 
|  | work_done_progress_params: WorkDoneProgressParams::default(), | 
|  | }, | 
|  | json!([ | 
|  | { | 
|  | "newText": "", | 
|  | "range": { | 
|  | "end": { "character": 0, "line": 3 }, | 
|  | "start": { "character": 11, "line": 2 } | 
|  | } | 
|  | } | 
|  | ]), | 
|  | ); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn test_format_document_2018() { | 
|  | if skip_slow_tests() { | 
|  | return; | 
|  | } | 
|  |  | 
|  | let server = project( | 
|  | r#" | 
|  | //- /Cargo.toml | 
|  | [package] | 
|  | name = "foo" | 
|  | version = "0.0.0" | 
|  | edition = "2018" | 
|  |  | 
|  | //- /src/lib.rs | 
|  | mod bar; | 
|  |  | 
|  | async fn test() { | 
|  | } | 
|  |  | 
|  | fn main() { | 
|  | } | 
|  |  | 
|  | pub use std::collections::HashMap; | 
|  | "#, | 
|  | ) | 
|  | .wait_until_workspace_is_loaded(); | 
|  |  | 
|  | server.request::<Formatting>( | 
|  | DocumentFormattingParams { | 
|  | text_document: server.doc_id("src/lib.rs"), | 
|  | options: FormattingOptions { | 
|  | tab_size: 4, | 
|  | insert_spaces: false, | 
|  | properties: HashMap::new(), | 
|  | insert_final_newline: None, | 
|  | trim_final_newlines: None, | 
|  | trim_trailing_whitespace: None, | 
|  | }, | 
|  | work_done_progress_params: WorkDoneProgressParams::default(), | 
|  | }, | 
|  | json!([ | 
|  | { | 
|  | "newText": "", | 
|  | "range": { | 
|  | "end": { "character": 0, "line": 3 }, | 
|  | "start": { "character": 17, "line": 2 } | 
|  | } | 
|  | }, | 
|  | { | 
|  | "newText": "", | 
|  | "range": { | 
|  | "end": { "character": 0, "line": 6 }, | 
|  | "start": { "character": 11, "line": 5 } | 
|  | } | 
|  | } | 
|  | ]), | 
|  | ); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn test_format_document_unchanged() { | 
|  | if skip_slow_tests() { | 
|  | return; | 
|  | } | 
|  |  | 
|  | let server = project( | 
|  | r#" | 
|  | //- /Cargo.toml | 
|  | [package] | 
|  | name = "foo" | 
|  | version = "0.0.0" | 
|  |  | 
|  | //- /src/lib.rs | 
|  | fn main() {} | 
|  | "#, | 
|  | ) | 
|  | .wait_until_workspace_is_loaded(); | 
|  |  | 
|  | server.request::<Formatting>( | 
|  | DocumentFormattingParams { | 
|  | text_document: server.doc_id("src/lib.rs"), | 
|  | options: FormattingOptions { | 
|  | tab_size: 4, | 
|  | insert_spaces: false, | 
|  | insert_final_newline: None, | 
|  | trim_final_newlines: None, | 
|  | trim_trailing_whitespace: None, | 
|  | properties: HashMap::new(), | 
|  | }, | 
|  | work_done_progress_params: WorkDoneProgressParams::default(), | 
|  | }, | 
|  | json!(null), | 
|  | ); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn test_format_document_range() { | 
|  | if skip_slow_tests() { | 
|  | return; | 
|  | } | 
|  |  | 
|  | let server = Project::with_fixture( | 
|  | r#" | 
|  | //- /Cargo.toml | 
|  | [package] | 
|  | name = "foo" | 
|  | version = "0.0.0" | 
|  |  | 
|  | //- /src/lib.rs | 
|  | fn main() { | 
|  | let unit_offsets_cache = collect(dwarf.units  ())  ?; | 
|  | } | 
|  | "#, | 
|  | ) | 
|  | .with_config(serde_json::json!({ | 
|  | "rustfmt": { | 
|  | "overrideCommand": [ "rustfmt", "+nightly", ], | 
|  | "rangeFormatting": { "enable": true } | 
|  | }, | 
|  | })) | 
|  | .server() | 
|  | .wait_until_workspace_is_loaded(); | 
|  |  | 
|  | server.request::<RangeFormatting>( | 
|  | DocumentRangeFormattingParams { | 
|  | range: Range { | 
|  | end: Position { line: 1, character: 0 }, | 
|  | start: Position { line: 1, character: 0 }, | 
|  | }, | 
|  | text_document: server.doc_id("src/lib.rs"), | 
|  | options: FormattingOptions { | 
|  | tab_size: 4, | 
|  | insert_spaces: false, | 
|  | insert_final_newline: None, | 
|  | trim_final_newlines: None, | 
|  | trim_trailing_whitespace: None, | 
|  | properties: HashMap::new(), | 
|  | }, | 
|  | work_done_progress_params: WorkDoneProgressParams::default(), | 
|  | }, | 
|  | json!([ | 
|  | { | 
|  | "newText": "", | 
|  | "range": { | 
|  | "start": { "character": 48, "line": 1 }, | 
|  | "end": { "character": 50, "line": 1 }, | 
|  | }, | 
|  | }, | 
|  | { | 
|  | "newText": "", | 
|  | "range": { | 
|  | "start": { "character": 53, "line": 1 }, | 
|  | "end": { "character": 55, "line": 1 }, | 
|  | }, | 
|  | } | 
|  | ]), | 
|  | ); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn test_missing_module_code_action() { | 
|  | if skip_slow_tests() { | 
|  | return; | 
|  | } | 
|  |  | 
|  | let server = project( | 
|  | r#" | 
|  | //- /Cargo.toml | 
|  | [package] | 
|  | name = "foo" | 
|  | version = "0.0.0" | 
|  |  | 
|  | //- /src/lib.rs | 
|  | mod bar; | 
|  |  | 
|  | fn main() {} | 
|  | "#, | 
|  | ) | 
|  | .wait_until_workspace_is_loaded(); | 
|  |  | 
|  | server.request::<CodeActionRequest>( | 
|  | CodeActionParams { | 
|  | text_document: server.doc_id("src/lib.rs"), | 
|  | range: Range::new(Position::new(0, 4), Position::new(0, 7)), | 
|  | context: CodeActionContext::default(), | 
|  | partial_result_params: PartialResultParams::default(), | 
|  | work_done_progress_params: WorkDoneProgressParams::default(), | 
|  | }, | 
|  | json!([ | 
|  | { | 
|  | "title": "Create module at `bar.rs`", | 
|  | "kind": "quickfix", | 
|  | "edit": { | 
|  | "documentChanges": [ | 
|  | { | 
|  | "kind": "create", | 
|  | "uri": "file://[..]/src/bar.rs" | 
|  | } | 
|  | ] | 
|  | } | 
|  | }, | 
|  | { | 
|  | "title": "Create module at `bar/mod.rs`", | 
|  | "kind": "quickfix", | 
|  | "edit": { | 
|  | "documentChanges": [ | 
|  | { | 
|  | "kind": "create", | 
|  | "uri": "file://[..]src/bar/mod.rs" | 
|  | } | 
|  | ] | 
|  | } | 
|  | } | 
|  | ]), | 
|  | ); | 
|  |  | 
|  | server.request::<CodeActionRequest>( | 
|  | CodeActionParams { | 
|  | text_document: server.doc_id("src/lib.rs"), | 
|  | range: Range::new(Position::new(2, 8), Position::new(2, 8)), | 
|  | context: CodeActionContext::default(), | 
|  | partial_result_params: PartialResultParams::default(), | 
|  | work_done_progress_params: WorkDoneProgressParams::default(), | 
|  | }, | 
|  | json!([]), | 
|  | ); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn test_missing_module_code_action_in_json_project() { | 
|  | if skip_slow_tests() { | 
|  | return; | 
|  | } | 
|  |  | 
|  | let tmp_dir = TestDir::new(); | 
|  |  | 
|  | let path = tmp_dir.path(); | 
|  |  | 
|  | let project = json!({ | 
|  | "roots": [path], | 
|  | "crates": [ { | 
|  | "root_module": path.join("src/lib.rs"), | 
|  | "deps": [], | 
|  | "edition": "2015", | 
|  | "cfg": [ "cfg_atom_1", "feature=\"cfg_1\""], | 
|  | } ] | 
|  | }); | 
|  |  | 
|  | let code = format!( | 
|  | r#" | 
|  | //- /.rust-project.json | 
|  | {project} | 
|  |  | 
|  | //- /src/lib.rs | 
|  | mod bar; | 
|  |  | 
|  | fn main() {{}} | 
|  | "#, | 
|  | ); | 
|  |  | 
|  | let server = | 
|  | Project::with_fixture(&code).tmp_dir(tmp_dir).server().wait_until_workspace_is_loaded(); | 
|  |  | 
|  | server.request::<CodeActionRequest>( | 
|  | CodeActionParams { | 
|  | text_document: server.doc_id("src/lib.rs"), | 
|  | range: Range::new(Position::new(0, 4), Position::new(0, 7)), | 
|  | context: CodeActionContext::default(), | 
|  | partial_result_params: PartialResultParams::default(), | 
|  | work_done_progress_params: WorkDoneProgressParams::default(), | 
|  | }, | 
|  | json!([ | 
|  | { | 
|  | "title": "Create module at `bar.rs`", | 
|  | "kind": "quickfix", | 
|  | "edit": { | 
|  | "documentChanges": [ | 
|  | { | 
|  | "kind": "create", | 
|  | "uri": "file://[..]/src/bar.rs" | 
|  | } | 
|  | ] | 
|  | } | 
|  | }, | 
|  | { | 
|  | "title": "Create module at `bar/mod.rs`", | 
|  | "kind": "quickfix", | 
|  | "edit": { | 
|  | "documentChanges": [ | 
|  | { | 
|  | "kind": "create", | 
|  | "uri": "file://[..]src/bar/mod.rs" | 
|  | } | 
|  | ] | 
|  | } | 
|  | } | 
|  | ]), | 
|  | ); | 
|  |  | 
|  | server.request::<CodeActionRequest>( | 
|  | CodeActionParams { | 
|  | text_document: server.doc_id("src/lib.rs"), | 
|  | range: Range::new(Position::new(2, 8), Position::new(2, 8)), | 
|  | context: CodeActionContext::default(), | 
|  | partial_result_params: PartialResultParams::default(), | 
|  | work_done_progress_params: WorkDoneProgressParams::default(), | 
|  | }, | 
|  | json!([]), | 
|  | ); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn diagnostics_dont_block_typing() { | 
|  | if skip_slow_tests() || std::env::var("CI").is_ok() { | 
|  | // FIXME: This test is failing too frequently (therefore we disable it on CI). | 
|  | return; | 
|  | } | 
|  |  | 
|  | let librs: String = (0..10).fold(String::new(), |mut acc, i| format_to_acc!(acc, "mod m{i};")); | 
|  | let libs: String = (0..10).fold(String::new(), |mut acc, i| { | 
|  | format_to_acc!(acc, "//- /src/m{i}.rs\nfn foo() {{}}\n\n") | 
|  | }); | 
|  | let server = Project::with_fixture(&format!( | 
|  | r#" | 
|  | //- /Cargo.toml | 
|  | [package] | 
|  | name = "foo" | 
|  | version = "0.0.0" | 
|  |  | 
|  | //- /src/lib.rs | 
|  | {librs} | 
|  |  | 
|  | {libs} | 
|  |  | 
|  | fn main() {{}} | 
|  | "# | 
|  | )) | 
|  | .with_config(serde_json::json!({ | 
|  | "cargo": { "sysroot": "discover" }, | 
|  | })) | 
|  | .server() | 
|  | .wait_until_workspace_is_loaded(); | 
|  |  | 
|  | for i in 0..10 { | 
|  | server.notification::<DidOpenTextDocument>(DidOpenTextDocumentParams { | 
|  | text_document: TextDocumentItem { | 
|  | uri: server.doc_id(&format!("src/m{i}.rs")).uri, | 
|  | language_id: "rust".to_owned(), | 
|  | version: 0, | 
|  | text: "/// Docs\nfn foo() {}".to_owned(), | 
|  | }, | 
|  | }); | 
|  | } | 
|  | let start = Instant::now(); | 
|  | server.request::<OnEnter>( | 
|  | TextDocumentPositionParams { | 
|  | text_document: server.doc_id("src/m0.rs"), | 
|  | position: Position { line: 0, character: 5 }, | 
|  | }, | 
|  | json!([{ | 
|  | "insertTextFormat": 2, | 
|  | "newText": "\n/// $0", | 
|  | "range": { | 
|  | "end": { "character": 5, "line": 0 }, | 
|  | "start": { "character": 5, "line": 0 } | 
|  | } | 
|  | }]), | 
|  | ); | 
|  | let elapsed = start.elapsed(); | 
|  | assert!(elapsed.as_millis() < 2000, "typing enter took {elapsed:?}"); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn preserves_dos_line_endings() { | 
|  | if skip_slow_tests() { | 
|  | return; | 
|  | } | 
|  |  | 
|  | let server = Project::with_fixture( | 
|  | " | 
|  | //- /Cargo.toml | 
|  | [package] | 
|  | name = \"foo\" | 
|  | version = \"0.0.0\" | 
|  |  | 
|  | //- /src/main.rs | 
|  | /// Some Docs\r\nfn main() {} | 
|  | ", | 
|  | ) | 
|  | .server() | 
|  | .wait_until_workspace_is_loaded(); | 
|  |  | 
|  | server.request::<OnEnter>( | 
|  | TextDocumentPositionParams { | 
|  | text_document: server.doc_id("src/main.rs"), | 
|  | position: Position { line: 0, character: 8 }, | 
|  | }, | 
|  | json!([{ | 
|  | "insertTextFormat": 2, | 
|  | "newText": "\r\n/// $0", | 
|  | "range": { | 
|  | "end": { "line": 0, "character": 8 }, | 
|  | "start": { "line": 0, "character": 8 } | 
|  | } | 
|  | }]), | 
|  | ); | 
|  | } | 
|  |  | 
|  | fn out_dirs_check_impl(root_contains_symlink: bool) { | 
|  | let mut server = Project::with_fixture( | 
|  | r###" | 
|  | //- /Cargo.toml | 
|  | [package] | 
|  | name = "foo" | 
|  | version = "0.0.0" | 
|  |  | 
|  | //- /build.rs | 
|  | use std::{env, fs, path::Path}; | 
|  |  | 
|  | fn main() { | 
|  | let out_dir = env::var_os("OUT_DIR").unwrap(); | 
|  | let dest_path = Path::new(&out_dir).join("hello.rs"); | 
|  | fs::write( | 
|  | &dest_path, | 
|  | r#"pub fn message() -> &'static str { "Hello, World!" }"#, | 
|  | ) | 
|  | .unwrap(); | 
|  | println!("cargo:rustc-cfg=atom_cfg"); | 
|  | println!("cargo:rustc-cfg=featlike=\"set\""); | 
|  | println!("cargo:rerun-if-changed=build.rs"); | 
|  | } | 
|  | //- /src/main.rs | 
|  | #![allow(warnings)] | 
|  | #![feature(rustc_attrs)] | 
|  | #[rustc_builtin_macro] macro_rules! include { | 
|  | ($file:expr $(,)?) => {{ /* compiler built-in */ }}; | 
|  | } | 
|  | #[rustc_builtin_macro] macro_rules! include_str { | 
|  | ($file:expr $(,)?) => {{ /* compiler built-in */ }}; | 
|  | } | 
|  | #[rustc_builtin_macro] macro_rules! concat { | 
|  | ($($e:ident),+ $(,)?) => {{ /* compiler built-in */ }}; | 
|  | } | 
|  | #[rustc_builtin_macro] macro_rules! env { | 
|  | ($name:expr $(,)?) => {{ /* compiler built-in */ }}; | 
|  | ($name:expr, $error_msg:expr $(,)?) => {{ /* compiler built-in */ }}; | 
|  | } | 
|  |  | 
|  | include!(concat!(env!("OUT_DIR"), "/hello.rs")); | 
|  |  | 
|  | #[cfg(atom_cfg)] | 
|  | struct A; | 
|  | #[cfg(bad_atom_cfg)] | 
|  | struct A; | 
|  | #[cfg(featlike = "set")] | 
|  | struct B; | 
|  | #[cfg(featlike = "not_set")] | 
|  | struct B; | 
|  |  | 
|  | fn main() { | 
|  | let va = A; | 
|  | let vb = B; | 
|  | let should_be_str = message(); | 
|  | let another_str = include_str!("main.rs"); | 
|  | } | 
|  | "###, | 
|  | ); | 
|  |  | 
|  | if root_contains_symlink { | 
|  | server = server.with_root_dir_contains_symlink(); | 
|  | } | 
|  |  | 
|  | let server = server | 
|  | .with_config(serde_json::json!({ | 
|  | "cargo": { | 
|  | "buildScripts": { | 
|  | "enable": true | 
|  | }, | 
|  | "sysroot": null, | 
|  | "extraEnv": { | 
|  | "RUSTC_BOOTSTRAP": "1" | 
|  | } | 
|  | } | 
|  | })) | 
|  | .server() | 
|  | .wait_until_workspace_is_loaded(); | 
|  |  | 
|  | let res = server.send_request::<HoverRequest>(HoverParams { | 
|  | text_document_position_params: TextDocumentPositionParams::new( | 
|  | server.doc_id("src/main.rs"), | 
|  | Position::new(30, 10), | 
|  | ), | 
|  | work_done_progress_params: Default::default(), | 
|  | }); | 
|  | assert!(res.to_string().contains("&'static str")); | 
|  |  | 
|  | let res = server.send_request::<HoverRequest>(HoverParams { | 
|  | text_document_position_params: TextDocumentPositionParams::new( | 
|  | server.doc_id("src/main.rs"), | 
|  | Position::new(31, 10), | 
|  | ), | 
|  | work_done_progress_params: Default::default(), | 
|  | }); | 
|  | assert!(res.to_string().contains("&'static str")); | 
|  |  | 
|  | server.request::<GotoTypeDefinition>( | 
|  | GotoDefinitionParams { | 
|  | text_document_position_params: TextDocumentPositionParams::new( | 
|  | server.doc_id("src/main.rs"), | 
|  | Position::new(28, 9), | 
|  | ), | 
|  | work_done_progress_params: Default::default(), | 
|  | partial_result_params: Default::default(), | 
|  | }, | 
|  | json!([{ | 
|  | "originSelectionRange": { | 
|  | "end": { "character": 10, "line": 28 }, | 
|  | "start": { "character": 8, "line": 28 } | 
|  | }, | 
|  | "targetRange": { | 
|  | "end": { "character": 9, "line": 19 }, | 
|  | "start": { "character": 0, "line": 18 } | 
|  | }, | 
|  | "targetSelectionRange": { | 
|  | "end": { "character": 8, "line": 19 }, | 
|  | "start": { "character": 7, "line": 19 } | 
|  | }, | 
|  | "targetUri": "file:///[..]src/main.rs" | 
|  | }]), | 
|  | ); | 
|  |  | 
|  | server.request::<GotoTypeDefinition>( | 
|  | GotoDefinitionParams { | 
|  | text_document_position_params: TextDocumentPositionParams::new( | 
|  | server.doc_id("src/main.rs"), | 
|  | Position::new(29, 9), | 
|  | ), | 
|  | work_done_progress_params: Default::default(), | 
|  | partial_result_params: Default::default(), | 
|  | }, | 
|  | json!([{ | 
|  | "originSelectionRange": { | 
|  | "end": { "character": 10, "line": 29 }, | 
|  | "start": { "character": 8, "line": 29 } | 
|  | }, | 
|  | "targetRange": { | 
|  | "end": { "character": 9, "line": 23 }, | 
|  | "start": { "character": 0, "line": 22 } | 
|  | }, | 
|  | "targetSelectionRange": { | 
|  | "end": { "character": 8, "line": 23 }, | 
|  | "start": { "character": 7, "line": 23 } | 
|  | }, | 
|  | "targetUri": "file:///[..]src/main.rs" | 
|  | }]), | 
|  | ); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn out_dirs_check() { | 
|  | if skip_slow_tests() { | 
|  | return; | 
|  | } | 
|  | out_dirs_check_impl(false); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | #[cfg(not(windows))] // windows requires elevated permissions to create symlinks | 
|  | fn root_contains_symlink_out_dirs_check() { | 
|  | if skip_slow_tests() { | 
|  | return; | 
|  | } | 
|  | out_dirs_check_impl(true); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn resolve_proc_macro() { | 
|  | use expect_test::expect; | 
|  | if skip_slow_tests() { | 
|  | return; | 
|  | } | 
|  |  | 
|  | let server = Project::with_fixture( | 
|  | r###" | 
|  | //- /foo/Cargo.toml | 
|  | [package] | 
|  | name = "foo" | 
|  | version = "0.0.0" | 
|  | edition = "2021" | 
|  | [dependencies] | 
|  | bar = {path = "../bar"} | 
|  |  | 
|  | //- /foo/src/main.rs | 
|  | use bar::Bar; | 
|  |  | 
|  | trait Bar { | 
|  | fn bar(); | 
|  | } | 
|  | #[derive(Bar)] | 
|  | struct Foo {} | 
|  | fn main() { | 
|  | Foo::bar(); | 
|  | } | 
|  |  | 
|  | //- /bar/Cargo.toml | 
|  | [package] | 
|  | name = "bar" | 
|  | version = "0.0.0" | 
|  | edition = "2021" | 
|  |  | 
|  | [lib] | 
|  | proc-macro = true | 
|  |  | 
|  | //- /bar/src/lib.rs | 
|  | use proc_macro::{Delimiter, Group, Ident, Span, TokenStream, TokenTree}; | 
|  | macro_rules! t { | 
|  | ($n:literal) => { | 
|  | TokenTree::from(Ident::new($n, Span::call_site())) | 
|  | }; | 
|  | ({}) => { | 
|  | TokenTree::from(Group::new(Delimiter::Brace, TokenStream::new())) | 
|  | }; | 
|  | (()) => { | 
|  | TokenTree::from(Group::new(Delimiter::Parenthesis, TokenStream::new())) | 
|  | }; | 
|  | } | 
|  | #[proc_macro_derive(Bar)] | 
|  | pub fn foo(_input: TokenStream) -> TokenStream { | 
|  | // We hard code the output here for preventing to use any deps | 
|  | let mut res = TokenStream::new(); | 
|  |  | 
|  | // ill behaved proc-macro will use the stdout | 
|  | // we should ignore it | 
|  | println!("I am bad guy"); | 
|  |  | 
|  | // impl Bar for Foo { fn bar() {} } | 
|  | let mut tokens = vec![t!("impl"), t!("Bar"), t!("for"), t!("Foo")]; | 
|  | let mut fn_stream = TokenStream::new(); | 
|  | fn_stream.extend(vec![t!("fn"), t!("bar"), t!(()), t!({})]); | 
|  | tokens.push(Group::new(Delimiter::Brace, fn_stream).into()); | 
|  | res.extend(tokens); | 
|  | res | 
|  | } | 
|  |  | 
|  | "###, | 
|  | ) | 
|  | .with_config(serde_json::json!({ | 
|  | "cargo": { | 
|  | "buildScripts": { | 
|  | "enable": true | 
|  | }, | 
|  | "sysroot": "discover", | 
|  | }, | 
|  | "procMacro": { | 
|  | "enable": true, | 
|  | } | 
|  | })) | 
|  | .root("foo") | 
|  | .root("bar") | 
|  | .server() | 
|  | .wait_until_workspace_is_loaded(); | 
|  |  | 
|  | let res = server.send_request::<HoverRequest>(HoverParams { | 
|  | text_document_position_params: TextDocumentPositionParams::new( | 
|  | server.doc_id("foo/src/main.rs"), | 
|  | Position::new(8, 9), | 
|  | ), | 
|  | work_done_progress_params: Default::default(), | 
|  | }); | 
|  | let value = res.get("contents").unwrap().get("value").unwrap().as_str().unwrap(); | 
|  |  | 
|  | expect![[r#" | 
|  |  | 
|  | ```rust | 
|  | foo::Foo | 
|  | ``` | 
|  |  | 
|  | ```rust | 
|  | fn bar() | 
|  | ```"#]] | 
|  | .assert_eq(value); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn test_will_rename_files_same_level() { | 
|  | if skip_slow_tests() { | 
|  | return; | 
|  | } | 
|  |  | 
|  | let tmp_dir = TestDir::new(); | 
|  | let tmp_dir_path = tmp_dir.path().to_owned(); | 
|  | let tmp_dir_str = tmp_dir_path.as_str(); | 
|  | let base_path = PathBuf::from(format!("file://{tmp_dir_str}")); | 
|  |  | 
|  | let code = r#" | 
|  | //- /Cargo.toml | 
|  | [package] | 
|  | name = "foo" | 
|  | version = "0.0.0" | 
|  |  | 
|  | //- /src/lib.rs | 
|  | mod old_file; | 
|  | mod from_mod; | 
|  | mod to_mod; | 
|  | mod old_folder; | 
|  | fn main() {} | 
|  |  | 
|  | //- /src/old_file.rs | 
|  |  | 
|  | //- /src/old_folder/mod.rs | 
|  | mod nested; | 
|  |  | 
|  | //- /src/old_folder/nested.rs | 
|  | struct foo; | 
|  | use crate::old_folder::nested::foo as bar; | 
|  |  | 
|  | //- /src/from_mod/mod.rs | 
|  |  | 
|  | //- /src/to_mod/foo.rs | 
|  |  | 
|  | "#; | 
|  | let server = | 
|  | Project::with_fixture(code).tmp_dir(tmp_dir).server().wait_until_workspace_is_loaded(); | 
|  |  | 
|  | //rename same level file | 
|  | server.request::<WillRenameFiles>( | 
|  | RenameFilesParams { | 
|  | files: vec![FileRename { | 
|  | old_uri: base_path.join("src/old_file.rs").to_str().unwrap().to_owned(), | 
|  | new_uri: base_path.join("src/new_file.rs").to_str().unwrap().to_owned(), | 
|  | }], | 
|  | }, | 
|  | json!({ | 
|  | "documentChanges": [ | 
|  | { | 
|  | "textDocument": { | 
|  | "uri": format!("file://{}", tmp_dir_path.join("src").join("lib.rs").as_str().to_owned().replace("C:\\", "/c:/").replace('\\', "/")), | 
|  | "version": null | 
|  | }, | 
|  | "edits": [ | 
|  | { | 
|  | "range": { | 
|  | "start": { | 
|  | "line": 0, | 
|  | "character": 4 | 
|  | }, | 
|  | "end": { | 
|  | "line": 0, | 
|  | "character": 12 | 
|  | } | 
|  | }, | 
|  | "newText": "new_file" | 
|  | } | 
|  | ] | 
|  | } | 
|  | ] | 
|  | }), | 
|  | ); | 
|  |  | 
|  | //rename file from mod.rs to foo.rs | 
|  | server.request::<WillRenameFiles>( | 
|  | RenameFilesParams { | 
|  | files: vec![FileRename { | 
|  | old_uri: base_path.join("src/from_mod/mod.rs").to_str().unwrap().to_owned(), | 
|  | new_uri: base_path.join("src/from_mod/foo.rs").to_str().unwrap().to_owned(), | 
|  | }], | 
|  | }, | 
|  | json!(null), | 
|  | ); | 
|  |  | 
|  | //rename file from foo.rs to mod.rs | 
|  | server.request::<WillRenameFiles>( | 
|  | RenameFilesParams { | 
|  | files: vec![FileRename { | 
|  | old_uri: base_path.join("src/to_mod/foo.rs").to_str().unwrap().to_owned(), | 
|  | new_uri: base_path.join("src/to_mod/mod.rs").to_str().unwrap().to_owned(), | 
|  | }], | 
|  | }, | 
|  | json!(null), | 
|  | ); | 
|  |  | 
|  | //rename same level file | 
|  | server.request::<WillRenameFiles>( | 
|  | RenameFilesParams { | 
|  | files: vec![FileRename { | 
|  | old_uri: base_path.join("src/old_folder").to_str().unwrap().to_owned(), | 
|  | new_uri: base_path.join("src/new_folder").to_str().unwrap().to_owned(), | 
|  | }], | 
|  | }, | 
|  | json!({ | 
|  | "documentChanges": [ | 
|  | { | 
|  | "textDocument": { | 
|  | "uri": format!("file://{}", tmp_dir_path.join("src").join("lib.rs").as_str().to_owned().replace("C:\\", "/c:/").replace('\\', "/")), | 
|  | "version": null | 
|  | }, | 
|  | "edits": [ | 
|  | { | 
|  | "range": { | 
|  | "start": { | 
|  | "line": 3, | 
|  | "character": 4 | 
|  | }, | 
|  | "end": { | 
|  | "line": 3, | 
|  | "character": 14 | 
|  | } | 
|  | }, | 
|  | "newText": "new_folder" | 
|  | } | 
|  | ] | 
|  | }, | 
|  | { | 
|  | "textDocument": { | 
|  | "uri": format!("file://{}", tmp_dir_path.join("src").join("old_folder").join("nested.rs").as_str().to_owned().replace("C:\\", "/c:/").replace('\\', "/")), | 
|  | "version": null | 
|  | }, | 
|  | "edits": [ | 
|  | { | 
|  | "range": { | 
|  | "start": { | 
|  | "line": 1, | 
|  | "character": 11 | 
|  | }, | 
|  | "end": { | 
|  | "line": 1, | 
|  | "character": 21 | 
|  | } | 
|  | }, | 
|  | "newText": "new_folder" | 
|  | } | 
|  | ] | 
|  | } | 
|  | ] | 
|  | }), | 
|  | ); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn test_exclude_config_works() { | 
|  | if skip_slow_tests() { | 
|  | return; | 
|  | } | 
|  |  | 
|  | let server = Project::with_fixture( | 
|  | r#" | 
|  | //- /foo/Cargo.toml | 
|  | [package] | 
|  | name = "foo" | 
|  | version = "0.0.0" | 
|  |  | 
|  | //- /foo/src/lib.rs | 
|  | pub fn foo() {} | 
|  |  | 
|  | //- /bar/Cargo.toml | 
|  | [package] | 
|  | name = "bar" | 
|  | version = "0.0.0" | 
|  |  | 
|  | [dependencies] | 
|  | foo = { path = "../foo" } | 
|  |  | 
|  | //- /bar/src/lib.rs | 
|  | "#, | 
|  | ) | 
|  | .root("foo") | 
|  | .root("bar") | 
|  | .root("baz") | 
|  | .with_config(json!({ | 
|  | "files": { | 
|  | "exclude": ["foo"] | 
|  | } | 
|  | })) | 
|  | .server() | 
|  | .wait_until_workspace_is_loaded(); | 
|  |  | 
|  | server.request::<WorkspaceSymbolRequest>(Default::default(), json!([])); | 
|  |  | 
|  | let server = Project::with_fixture( | 
|  | r#" | 
|  | //- /foo/Cargo.toml | 
|  | [package] | 
|  | name = "foo" | 
|  | version = "0.0.0" | 
|  |  | 
|  | //- /foo/src/lib.rs | 
|  | pub fn foo() {} | 
|  |  | 
|  | //- /bar/Cargo.toml | 
|  | [package] | 
|  | name = "bar" | 
|  | version = "0.0.0" | 
|  |  | 
|  | //- /bar/src/lib.rs | 
|  | pub fn bar() {} | 
|  |  | 
|  | //- /baz/Cargo.toml | 
|  | [package] | 
|  | name = "baz" | 
|  | version = "0.0.0" | 
|  |  | 
|  | //- /baz/src/lib.rs | 
|  | "#, | 
|  | ) | 
|  | .root("foo") | 
|  | .root("bar") | 
|  | .root("baz") | 
|  | .with_config(json!({ | 
|  | "files": { | 
|  | "exclude": ["foo", "bar"] | 
|  | } | 
|  | })) | 
|  | .server() | 
|  | .wait_until_workspace_is_loaded(); | 
|  |  | 
|  | server.request::<WorkspaceSymbolRequest>(Default::default(), json!([])); | 
|  | } |