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_no_project() {
63 if skip_slow_tests() {
75 server.wait_until_workspace_is_loaded();
76 server.request::<Runnables>(
77 RunnablesParams { text_document: server.doc_id("lib.rs"), position: None },
81 "extraArgs": [ "foo", "--nocapture" ],
83 "env": { "RUST_BACKTRACE": "short" },
87 "end": { "character": 1, "line": 2 },
88 "start": { "character": 0, "line": 0 }
92 "args": ["check", "--workspace"],
97 "label": "cargo check --workspace",
99 "end": { "character": 0, "line": 0 },
100 "start": { "character": 0, "line": 0 }
108 fn test_runnables_project() {
109 if skip_slow_tests() {
122 //- foo/tests/spam.rs
135 let server = Project::with_fixture(code).root("foo").root("bar").server();
137 server.wait_until_workspace_is_loaded();
138 server.request::<Runnables>(
139 RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None },
142 "args": [ "test", "--package", "foo", "--test", "spam" ],
143 "extraArgs": [ "test_eggs", "--exact", "--nocapture" ],
145 "env": { "RUST_BACKTRACE": "short" },
146 "label": "test test_eggs",
148 "end": { "character": 17, "line": 1 },
149 "start": { "character": 0, "line": 0 }
151 "cwd": server.path().join("foo")
154 "args": [ "check", "--package", "foo" ],
158 "label": "cargo check -p foo",
160 "end": { "character": 0, "line": 0 },
161 "start": { "character": 0, "line": 0 }
163 "cwd": server.path().join("foo")
166 "args": [ "test", "--package", "foo" ],
170 "label": "cargo test -p foo",
172 "end": { "character": 0, "line": 0 },
173 "start": { "character": 0, "line": 0 }
175 "cwd": server.path().join("foo")
182 fn test_format_document() {
183 if skip_slow_tests() {
187 let server = project(
200 pub use std::collections::HashMap;
203 server.wait_until_workspace_is_loaded();
205 server.request::<Formatting>(
206 DocumentFormattingParams {
207 text_document: server.doc_id("src/lib.rs"),
208 options: FormattingOptions {
210 insert_spaces: false,
211 insert_final_newline: None,
212 trim_final_newlines: None,
213 trim_trailing_whitespace: None,
214 properties: HashMap::new(),
216 work_done_progress_params: WorkDoneProgressParams::default(),
220 "newText": r#"mod bar;
224 pub use std::collections::HashMap;
242 fn test_format_document_2018() {
243 if skip_slow_tests() {
247 let server = project(
264 pub use std::collections::HashMap;
267 server.wait_until_workspace_is_loaded();
269 server.request::<Formatting>(
270 DocumentFormattingParams {
271 text_document: server.doc_id("src/lib.rs"),
272 options: FormattingOptions {
274 insert_spaces: false,
275 properties: HashMap::new(),
276 insert_final_newline: None,
277 trim_final_newlines: None,
278 trim_trailing_whitespace: None,
280 work_done_progress_params: WorkDoneProgressParams::default(),
284 "newText": r#"mod bar;
290 pub use std::collections::HashMap;
308 fn test_missing_module_code_action() {
309 if skip_slow_tests() {
313 let server = project(
326 server.wait_until_workspace_is_loaded();
327 let empty_context = || CodeActionContext { diagnostics: Vec::new(), only: None };
328 server.request::<CodeActionRequest>(
330 text_document: server.doc_id("src/lib.rs"),
331 range: Range::new(Position::new(0, 4), Position::new(0, 7)),
332 context: empty_context(),
333 partial_result_params: PartialResultParams::default(),
334 work_done_progress_params: WorkDoneProgressParams::default(),
341 "cursorPosition": null,
342 "label": "Create module",
347 "uri": "file:///[..]/src/bar.rs"
353 "command": "rust-analyzer.applySourceChange",
354 "title": "Create module"
356 "title": "Create module"
361 server.request::<CodeActionRequest>(
363 text_document: server.doc_id("src/lib.rs"),
364 range: Range::new(Position::new(2, 4), Position::new(2, 7)),
365 context: empty_context(),
366 partial_result_params: PartialResultParams::default(),
367 work_done_progress_params: WorkDoneProgressParams::default(),
374 fn test_missing_module_code_action_in_json_project() {
375 if skip_slow_tests() {
379 let tmp_dir = TempDir::new().unwrap();
381 let path = tmp_dir.path();
383 let project = json!({
386 "root_module": path.join("src/lib.rs"),
396 //- rust-project.json
404 PROJECT = project.to_string(),
407 let server = Project::with_fixture(&code).tmp_dir(tmp_dir).server();
409 server.wait_until_workspace_is_loaded();
410 let empty_context = || CodeActionContext { diagnostics: Vec::new(), only: None };
411 server.request::<CodeActionRequest>(
413 text_document: server.doc_id("src/lib.rs"),
414 range: Range::new(Position::new(0, 4), Position::new(0, 7)),
415 context: empty_context(),
416 partial_result_params: PartialResultParams::default(),
417 work_done_progress_params: WorkDoneProgressParams::default(),
424 "cursorPosition": null,
425 "label": "Create module",
430 "uri": "file:///[..]/src/bar.rs"
436 "command": "rust-analyzer.applySourceChange",
437 "title": "Create module"
439 "title": "Create module"
444 server.request::<CodeActionRequest>(
446 text_document: server.doc_id("src/lib.rs"),
447 range: Range::new(Position::new(2, 4), Position::new(2, 7)),
448 context: empty_context(),
449 partial_result_params: PartialResultParams::default(),
450 work_done_progress_params: WorkDoneProgressParams::default(),
457 fn diagnostics_dont_block_typing() {
458 if skip_slow_tests() {
462 let librs: String = (0..10).map(|i| format!("mod m{};", i)).collect();
463 let libs: String = (0..10).map(|i| format!("//- src/m{}.rs\nfn foo() {{}}\n\n", i)).collect();
464 let server = Project::with_fixture(&format!(
483 server.wait_until_workspace_is_loaded();
485 server.notification::<DidOpenTextDocument>(DidOpenTextDocumentParams {
486 text_document: TextDocumentItem {
487 uri: server.doc_id(&format!("src/m{}.rs", i)).uri,
488 language_id: "rust".to_string(),
490 text: "/// Docs\nfn foo() {}".to_string(),
494 let start = std::time::Instant::now();
495 server.request::<OnEnter>(
496 TextDocumentPositionParams {
497 text_document: server.doc_id("src/m0.rs"),
498 position: Position { line: 0, character: 5 },
502 "position": { "character": 4, "line": 1 },
503 "textDocument": { "uri": "file:///[..]src/m0.rs" }
513 "end": { "character": 5, "line": 0 },
514 "start": { "character": 5, "line": 0 }
518 "textDocument": { "uri": "file:///[..]src/m0.rs", "version": null }
524 let elapsed = start.elapsed();
525 assert!(elapsed.as_millis() < 2000, "typing enter took {:?}", elapsed);
529 fn preserves_dos_line_endings() {
530 if skip_slow_tests() {
534 let server = Project::with_fixture(
542 /// Some Docs\r\nfn main() {}
547 server.request::<OnEnter>(
548 TextDocumentPositionParams {
549 text_document: server.doc_id("src/main.rs"),
550 position: Position { line: 0, character: 8 },
554 "position": { "line": 1, "character": 4 },
555 "textDocument": { "uri": "file:///[..]src/main.rs" }
563 "newText": "\r\n/// ",
565 "end": { "line": 0, "character": 8 },
566 "start": { "line": 0, "character": 8 }
570 "textDocument": { "uri": "file:///[..]src/main.rs", "version": null }
579 fn out_dirs_check() {
580 if skip_slow_tests() {
584 let server = Project::with_fixture(
592 use std::{env, fs, path::Path};
595 let out_dir = env::var_os("OUT_DIR").unwrap();
596 let dest_path = Path::new(&out_dir).join("hello.rs");
599 r#"pub fn message() -> &'static str { "Hello, World!" }"#,
602 println!("cargo:rustc-cfg=atom_cfg");
603 println!("cargo:rustc-cfg=featlike=\"set\"");
604 println!("cargo:rerun-if-changed=build.rs");
607 include!(concat!(env!("OUT_DIR"), "/hello.rs"));
613 #[cfg(featlike = "set")]
615 #[cfg(featlike = "not_set")]
624 fn main() { message(); }
627 .with_config(|config| {
628 config.cargo.load_out_dirs_from_check = true;
631 server.wait_until_workspace_is_loaded();
632 let res = server.send_request::<GotoDefinition>(GotoDefinitionParams {
633 text_document_position_params: TextDocumentPositionParams::new(
634 server.doc_id("src/main.rs"),
635 Position::new(14, 8),
637 work_done_progress_params: Default::default(),
638 partial_result_params: Default::default(),
640 assert!(format!("{}", res).contains("hello.rs"));
641 server.request::<GotoTypeDefinition>(
642 GotoDefinitionParams {
643 text_document_position_params: TextDocumentPositionParams::new(
644 server.doc_id("src/main.rs"),
645 Position::new(12, 9),
647 work_done_progress_params: Default::default(),
648 partial_result_params: Default::default(),
651 "originSelectionRange": {
671 "targetSelectionRange": {
681 "targetUri": "file:///[..]src/main.rs"
684 server.request::<GotoTypeDefinition>(
685 GotoDefinitionParams {
686 text_document_position_params: TextDocumentPositionParams::new(
687 server.doc_id("src/main.rs"),
688 Position::new(13, 9),
690 work_done_progress_params: Default::default(),
691 partial_result_params: Default::default(),
694 "originSelectionRange": {
714 "targetSelectionRange": {
724 "targetUri": "file:///[..]src/main.rs"
730 fn resolve_proc_macro() {
731 if skip_slow_tests() {
734 let server = Project::with_fixture(
742 bar = {path = "../bar"}
765 extern crate proc_macro;
766 use proc_macro::{Delimiter, Group, Ident, Span, TokenStream, TokenTree};
769 TokenTree::from(Ident::new($n, Span::call_site()))
772 TokenTree::from(Group::new(Delimiter::Brace, TokenStream::new()))
775 TokenTree::from(Group::new(Delimiter::Parenthesis, TokenStream::new()))
778 #[proc_macro_derive(Bar)]
779 pub fn foo(_input: TokenStream) -> TokenStream {
780 // We hard code the output here for preventing to use any deps
781 let mut res = TokenStream::new();
783 // impl Bar for Foo { fn bar() {} }
784 let mut tokens = vec![t!("impl"), t!("Bar"), t!("for"), t!("Foo")];
785 let mut fn_stream = TokenStream::new();
786 fn_stream.extend(vec![t!("fn"), t!("bar"), t!(()), t!({})]);
787 tokens.push(Group::new(Delimiter::Brace, fn_stream).into());
794 .with_config(|config| {
795 let macro_srv_path = PathBuf::from(env!("CARGO_BIN_EXE_rust-analyzer"));
797 config.cargo.load_out_dirs_from_check = true;
798 config.proc_macro_srv = Some((macro_srv_path, vec!["proc-macro".into()]));
803 server.wait_until_workspace_is_loaded();
804 let res = server.send_request::<HoverRequest>(HoverParams {
805 text_document_position_params: TextDocumentPositionParams::new(
806 server.doc_id("foo/src/main.rs"),
809 work_done_progress_params: Default::default(),
812 let value = res.get("contents").unwrap().get("value").unwrap().to_string();
813 assert_eq!(value, r#""```rust\nfoo::Bar\nfn bar()\n```""#)