3 use std::{collections::HashMap, path::PathBuf, time::Instant};
6 notification::DidOpenTextDocument,
8 CodeActionRequest, Completion, Formatting, GotoDefinition, GotoTypeDefinition, HoverRequest,
10 CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams,
11 DocumentFormattingParams, FormattingOptions, GotoDefinitionParams, HoverParams,
12 PartialResultParams, Position, Range, TextDocumentItem, TextDocumentPositionParams,
13 WorkDoneProgressParams,
15 use rust_analyzer::lsp_ext::{OnEnter, Runnables, RunnablesParams};
17 use tempfile::TempDir;
18 use test_utils::skip_slow_tests;
20 use crate::support::{project, Project};
22 const PROFILE: &str = "";
23 // const PROFILE: &'static str = "*@3>100";
26 fn completes_items_from_standard_library() {
27 if skip_slow_tests() {
31 let project_start = Instant::now();
32 let server = Project::with_fixture(
40 use std::collections::Spam;
45 server.wait_until_workspace_is_loaded();
46 eprintln!("loading took {:?}", project_start.elapsed());
47 let completion_start = Instant::now();
48 let res = server.send_request::<Completion>(CompletionParams {
49 text_document_position: TextDocumentPositionParams::new(
50 server.doc_id("src/lib.rs"),
54 partial_result_params: PartialResultParams::default(),
55 work_done_progress_params: WorkDoneProgressParams::default(),
57 assert!(format!("{}", res).contains("HashMap"));
58 eprintln!("completion took {:?}", completion_start.elapsed());
62 fn test_runnables_project() {
63 if skip_slow_tests() {
89 let server = Project::with_fixture(code).root("foo").root("bar").server();
91 server.wait_until_workspace_is_loaded();
92 server.request::<Runnables>(
93 RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None },
97 "cargoArgs": ["test", "--package", "foo", "--test", "spam"],
98 "executableArgs": ["test_eggs", "--exact", "--nocapture"],
99 "workspaceRoot": server.path().join("foo")
102 "label": "test test_eggs",
105 "end": { "character": 17, "line": 1 },
106 "start": { "character": 0, "line": 0 }
108 "targetSelectionRange": {
109 "end": { "character": 12, "line": 1 },
110 "start": { "character": 3, "line": 1 }
112 "targetUri": "file:///[..]/tests/spam.rs"
117 "cargoArgs": ["check", "--package", "foo"],
118 "executableArgs": [],
119 "workspaceRoot": server.path().join("foo")
122 "label": "cargo check -p foo"
126 "cargoArgs": ["test", "--package", "foo"],
127 "executableArgs": [],
128 "workspaceRoot": server.path().join("foo")
131 "label": "cargo test -p foo"
138 fn test_format_document() {
139 if skip_slow_tests() {
143 let server = project(
156 pub use std::collections::HashMap;
159 server.wait_until_workspace_is_loaded();
161 server.request::<Formatting>(
162 DocumentFormattingParams {
163 text_document: server.doc_id("src/lib.rs"),
164 options: FormattingOptions {
166 insert_spaces: false,
167 insert_final_newline: None,
168 trim_final_newlines: None,
169 trim_trailing_whitespace: None,
170 properties: HashMap::new(),
172 work_done_progress_params: WorkDoneProgressParams::default(),
176 "newText": r#"mod bar;
180 pub use std::collections::HashMap;
198 fn test_format_document_2018() {
199 if skip_slow_tests() {
203 let server = project(
220 pub use std::collections::HashMap;
223 server.wait_until_workspace_is_loaded();
225 server.request::<Formatting>(
226 DocumentFormattingParams {
227 text_document: server.doc_id("src/lib.rs"),
228 options: FormattingOptions {
230 insert_spaces: false,
231 properties: HashMap::new(),
232 insert_final_newline: None,
233 trim_final_newlines: None,
234 trim_trailing_whitespace: None,
236 work_done_progress_params: WorkDoneProgressParams::default(),
240 "newText": r#"mod bar;
246 pub use std::collections::HashMap;
264 fn test_missing_module_code_action() {
265 if skip_slow_tests() {
269 let server = project(
282 server.wait_until_workspace_is_loaded();
283 let empty_context = || CodeActionContext { diagnostics: Vec::new(), only: None };
284 server.request::<CodeActionRequest>(
286 text_document: server.doc_id("src/lib.rs"),
287 range: Range::new(Position::new(0, 4), Position::new(0, 7)),
288 context: empty_context(),
289 partial_result_params: PartialResultParams::default(),
290 work_done_progress_params: WorkDoneProgressParams::default(),
297 "uri": "file:///[..]/src/bar.rs"
302 "title": "Create module"
306 server.request::<CodeActionRequest>(
308 text_document: server.doc_id("src/lib.rs"),
309 range: Range::new(Position::new(2, 4), Position::new(2, 7)),
310 context: empty_context(),
311 partial_result_params: PartialResultParams::default(),
312 work_done_progress_params: WorkDoneProgressParams::default(),
319 fn test_missing_module_code_action_in_json_project() {
320 if skip_slow_tests() {
324 let tmp_dir = TempDir::new().unwrap();
326 let path = tmp_dir.path();
328 let project = json!({
331 "root_module": path.join("src/lib.rs"),
334 "cfg": [ "cfg_atom_1", "feature=cfg_1"],
340 //- rust-project.json
348 PROJECT = project.to_string(),
351 let server = Project::with_fixture(&code).tmp_dir(tmp_dir).server();
353 server.wait_until_workspace_is_loaded();
354 let empty_context = || CodeActionContext { diagnostics: Vec::new(), only: None };
355 server.request::<CodeActionRequest>(
357 text_document: server.doc_id("src/lib.rs"),
358 range: Range::new(Position::new(0, 4), Position::new(0, 7)),
359 context: empty_context(),
360 partial_result_params: PartialResultParams::default(),
361 work_done_progress_params: WorkDoneProgressParams::default(),
368 "uri": "file://[..]/src/bar.rs"
373 "title": "Create module"
377 server.request::<CodeActionRequest>(
379 text_document: server.doc_id("src/lib.rs"),
380 range: Range::new(Position::new(2, 4), Position::new(2, 7)),
381 context: empty_context(),
382 partial_result_params: PartialResultParams::default(),
383 work_done_progress_params: WorkDoneProgressParams::default(),
390 fn diagnostics_dont_block_typing() {
391 if skip_slow_tests() {
395 let librs: String = (0..10).map(|i| format!("mod m{};", i)).collect();
396 let libs: String = (0..10).map(|i| format!("//- src/m{}.rs\nfn foo() {{}}\n\n", i)).collect();
397 let server = Project::with_fixture(&format!(
416 server.wait_until_workspace_is_loaded();
418 server.notification::<DidOpenTextDocument>(DidOpenTextDocumentParams {
419 text_document: TextDocumentItem {
420 uri: server.doc_id(&format!("src/m{}.rs", i)).uri,
421 language_id: "rust".to_string(),
423 text: "/// Docs\nfn foo() {}".to_string(),
427 let start = std::time::Instant::now();
428 server.request::<OnEnter>(
429 TextDocumentPositionParams {
430 text_document: server.doc_id("src/m0.rs"),
431 position: Position { line: 0, character: 5 },
434 "insertTextFormat": 2,
435 "newText": "\n/// $0",
437 "end": { "character": 5, "line": 0 },
438 "start": { "character": 5, "line": 0 }
442 let elapsed = start.elapsed();
443 assert!(elapsed.as_millis() < 2000, "typing enter took {:?}", elapsed);
447 fn preserves_dos_line_endings() {
448 if skip_slow_tests() {
452 let server = Project::with_fixture(
460 /// Some Docs\r\nfn main() {}
465 server.request::<OnEnter>(
466 TextDocumentPositionParams {
467 text_document: server.doc_id("src/main.rs"),
468 position: Position { line: 0, character: 8 },
471 "insertTextFormat": 2,
472 "newText": "\r\n/// $0",
474 "end": { "line": 0, "character": 8 },
475 "start": { "line": 0, "character": 8 }
482 fn out_dirs_check() {
483 if skip_slow_tests() {
487 let server = Project::with_fixture(
495 use std::{env, fs, path::Path};
498 let out_dir = env::var_os("OUT_DIR").unwrap();
499 let dest_path = Path::new(&out_dir).join("hello.rs");
502 r#"pub fn message() -> &'static str { "Hello, World!" }"#,
505 println!("cargo:rustc-cfg=atom_cfg");
506 println!("cargo:rustc-cfg=featlike=\"set\"");
507 println!("cargo:rerun-if-changed=build.rs");
510 include!(concat!(env!("OUT_DIR"), "/hello.rs"));
516 #[cfg(featlike = "set")]
518 #[cfg(featlike = "not_set")]
527 fn main() { message(); }
530 .with_config(|config| {
531 config.cargo.load_out_dirs_from_check = true;
534 server.wait_until_workspace_is_loaded();
535 let res = server.send_request::<GotoDefinition>(GotoDefinitionParams {
536 text_document_position_params: TextDocumentPositionParams::new(
537 server.doc_id("src/main.rs"),
538 Position::new(14, 8),
540 work_done_progress_params: Default::default(),
541 partial_result_params: Default::default(),
543 assert!(format!("{}", res).contains("hello.rs"));
544 server.request::<GotoTypeDefinition>(
545 GotoDefinitionParams {
546 text_document_position_params: TextDocumentPositionParams::new(
547 server.doc_id("src/main.rs"),
548 Position::new(12, 9),
550 work_done_progress_params: Default::default(),
551 partial_result_params: Default::default(),
554 "originSelectionRange": {
574 "targetSelectionRange": {
584 "targetUri": "file:///[..]src/main.rs"
587 server.request::<GotoTypeDefinition>(
588 GotoDefinitionParams {
589 text_document_position_params: TextDocumentPositionParams::new(
590 server.doc_id("src/main.rs"),
591 Position::new(13, 9),
593 work_done_progress_params: Default::default(),
594 partial_result_params: Default::default(),
597 "originSelectionRange": {
617 "targetSelectionRange": {
627 "targetUri": "file:///[..]src/main.rs"
633 fn resolve_proc_macro() {
634 if skip_slow_tests() {
637 let server = Project::with_fixture(
645 bar = {path = "../bar"}
668 extern crate proc_macro;
669 use proc_macro::{Delimiter, Group, Ident, Span, TokenStream, TokenTree};
672 TokenTree::from(Ident::new($n, Span::call_site()))
675 TokenTree::from(Group::new(Delimiter::Brace, TokenStream::new()))
678 TokenTree::from(Group::new(Delimiter::Parenthesis, TokenStream::new()))
681 #[proc_macro_derive(Bar)]
682 pub fn foo(_input: TokenStream) -> TokenStream {
683 // We hard code the output here for preventing to use any deps
684 let mut res = TokenStream::new();
686 // impl Bar for Foo { fn bar() {} }
687 let mut tokens = vec![t!("impl"), t!("Bar"), t!("for"), t!("Foo")];
688 let mut fn_stream = TokenStream::new();
689 fn_stream.extend(vec![t!("fn"), t!("bar"), t!(()), t!({})]);
690 tokens.push(Group::new(Delimiter::Brace, fn_stream).into());
697 .with_config(|config| {
698 let macro_srv_path = PathBuf::from(env!("CARGO_BIN_EXE_rust-analyzer"));
700 config.cargo.load_out_dirs_from_check = true;
701 config.proc_macro_srv = Some((macro_srv_path, vec!["proc-macro".into()]));
706 server.wait_until_workspace_is_loaded();
707 let res = server.send_request::<HoverRequest>(HoverParams {
708 text_document_position_params: TextDocumentPositionParams::new(
709 server.doc_id("foo/src/main.rs"),
712 work_done_progress_params: Default::default(),
715 let value = res.get("contents").unwrap().get("value").unwrap().to_string();
716 assert_eq!(value, r#""```rust\nfoo::Bar\n```\n\n```rust\nfn bar()\n```""#)
720 fn test_client_support_hover_actions() {
721 if skip_slow_tests() {
725 let server = Project::with_fixture(
744 .with_config(|config| {
745 config.client_caps.hover_actions = true;
749 server.wait_until_workspace_is_loaded();
751 // has 1 implementation
752 server.request::<HoverRequest>(
754 text_document_position_params: TextDocumentPositionParams::new(
755 server.doc_id("src/lib.rs"),
758 work_done_progress_params: Default::default(),
764 "file:///[..]src/lib.rs",
770 "range": { "end": { "character": 1, "line": 8 }, "start": { "character": 0, "line": 4 } },
771 "uri": "file:///[..]src/lib.rs"
774 "command": "rust-analyzer.showReferences",
775 "title": "1 implementation",
776 "tooltip": "Go to implementations"
779 "contents": { "kind": "markdown", "value": "```rust\nfoo\n```\n\n```rust\nstruct Foo\n```" },
780 "range": { "end": { "character": 10, "line": 0 }, "start": { "character": 7, "line": 0 } }
785 server.request::<HoverRequest>(
787 text_document_position_params: TextDocumentPositionParams::new(
788 server.doc_id("src/lib.rs"),
791 work_done_progress_params: Default::default(),
796 // no implementations
797 server.request::<HoverRequest>(
799 text_document_position_params: TextDocumentPositionParams::new(
800 server.doc_id("src/lib.rs"),
801 Position::new(2, 12),
803 work_done_progress_params: Default::default(),
809 "file:///[..]src/lib.rs",
810 { "character": 7, "line": 2 },
813 "command": "rust-analyzer.showReferences",
814 "title": "0 implementations",
815 "tooltip": "Go to implementations"
818 "contents": { "kind": "markdown", "value": "```rust\nfoo\n```\n\n```rust\nstruct NoImpl\n```" },
819 "range": { "end": { "character": 13, "line": 2 }, "start": { "character": 7, "line": 2 } }
825 fn test_client_does_not_support_hover_actions() {
826 if skip_slow_tests() {
830 let server = Project::with_fixture(
849 .with_config(|config| {
850 config.client_caps.hover_actions = false;
854 server.wait_until_workspace_is_loaded();
856 // has 1 implementation
857 server.request::<HoverRequest>(
859 text_document_position_params: TextDocumentPositionParams::new(
860 server.doc_id("src/lib.rs"),
863 work_done_progress_params: Default::default(),
866 "contents": { "kind": "markdown", "value": "```rust\nfoo\n```\n\n```rust\nstruct Foo\n```" },
867 "range": { "end": { "character": 10, "line": 0 }, "start": { "character": 7, "line": 0 } }
872 server.request::<HoverRequest>(
874 text_document_position_params: TextDocumentPositionParams::new(
875 server.doc_id("src/lib.rs"),
878 work_done_progress_params: Default::default(),
883 // no implementations
884 server.request::<HoverRequest>(
886 text_document_position_params: TextDocumentPositionParams::new(
887 server.doc_id("src/lib.rs"),
888 Position::new(2, 12),
890 work_done_progress_params: Default::default(),
893 "contents": { "kind": "markdown", "value": "```rust\nfoo\n```\n\n```rust\nstruct NoImpl\n```" },
894 "range": { "end": { "character": 13, "line": 2 }, "start": { "character": 7, "line": 2 } }