3 use std::{collections::HashMap, path::PathBuf, time::Instant};
6 CodeActionContext, DidOpenTextDocumentParams, DocumentFormattingParams, FormattingOptions,
7 PartialResultParams, Position, Range, TextDocumentItem, TextDocumentPositionParams,
8 WorkDoneProgressParams,
10 use rust_analyzer::req::{
11 CodeActionParams, CodeActionRequest, Completion, CompletionParams, DidOpenTextDocument,
12 Formatting, GotoDefinition, HoverRequest, OnEnter, Runnables, RunnablesParams,
15 use tempfile::TempDir;
16 use test_utils::skip_slow_tests;
18 use crate::support::{project, Project};
20 const PROFILE: &str = "";
21 // const PROFILE: &'static str = "*@3>100";
24 fn completes_items_from_standard_library() {
25 if skip_slow_tests() {
29 let project_start = Instant::now();
30 let server = Project::with_fixture(
38 use std::collections::Spam;
43 server.wait_until_workspace_is_loaded();
44 eprintln!("loading took {:?}", project_start.elapsed());
45 let completion_start = Instant::now();
46 let res = server.send_request::<Completion>(CompletionParams {
47 text_document_position: TextDocumentPositionParams::new(
48 server.doc_id("src/lib.rs"),
52 partial_result_params: PartialResultParams::default(),
53 work_done_progress_params: WorkDoneProgressParams::default(),
55 assert!(format!("{}", res).contains("HashMap"));
56 eprintln!("completion took {:?}", completion_start.elapsed());
60 fn test_runnables_no_project() {
61 if skip_slow_tests() {
73 server.wait_until_workspace_is_loaded();
74 server.request::<Runnables>(
75 RunnablesParams { text_document: server.doc_id("lib.rs"), position: None },
79 "extraArgs": [ "foo", "--nocapture" ],
81 "env": { "RUST_BACKTRACE": "short" },
85 "end": { "character": 1, "line": 2 },
86 "start": { "character": 0, "line": 0 }
90 "args": ["check", "--workspace"],
95 "label": "cargo check --workspace",
97 "end": { "character": 0, "line": 0 },
98 "start": { "character": 0, "line": 0 }
106 fn test_runnables_project() {
107 if skip_slow_tests() {
120 //- foo/tests/spam.rs
133 let server = Project::with_fixture(code).root("foo").root("bar").server();
135 server.wait_until_workspace_is_loaded();
136 server.request::<Runnables>(
137 RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None },
140 "args": [ "test", "--package", "foo", "--test", "spam" ],
141 "extraArgs": [ "test_eggs", "--exact", "--nocapture" ],
143 "env": { "RUST_BACKTRACE": "short" },
144 "label": "test test_eggs",
146 "end": { "character": 17, "line": 1 },
147 "start": { "character": 0, "line": 0 }
149 "cwd": server.path().join("foo")
152 "args": [ "check", "--package", "foo", "--test", "spam" ],
156 "label": "cargo check -p foo",
158 "end": { "character": 0, "line": 0 },
159 "start": { "character": 0, "line": 0 }
161 "cwd": server.path().join("foo")
164 "args": [ "test", "--package", "foo", "--test", "spam" ],
168 "label": "cargo test -p foo",
170 "end": { "character": 0, "line": 0 },
171 "start": { "character": 0, "line": 0 }
173 "cwd": server.path().join("foo")
180 fn test_format_document() {
181 if skip_slow_tests() {
185 let server = project(
198 pub use std::collections::HashMap;
201 server.wait_until_workspace_is_loaded();
203 server.request::<Formatting>(
204 DocumentFormattingParams {
205 text_document: server.doc_id("src/lib.rs"),
206 options: FormattingOptions {
208 insert_spaces: false,
209 insert_final_newline: None,
210 trim_final_newlines: None,
211 trim_trailing_whitespace: None,
212 properties: HashMap::new(),
214 work_done_progress_params: WorkDoneProgressParams::default(),
218 "newText": r#"mod bar;
222 pub use std::collections::HashMap;
240 fn test_format_document_2018() {
241 if skip_slow_tests() {
245 let server = project(
262 pub use std::collections::HashMap;
265 server.wait_until_workspace_is_loaded();
267 server.request::<Formatting>(
268 DocumentFormattingParams {
269 text_document: server.doc_id("src/lib.rs"),
270 options: FormattingOptions {
272 insert_spaces: false,
273 properties: HashMap::new(),
274 insert_final_newline: None,
275 trim_final_newlines: None,
276 trim_trailing_whitespace: None,
278 work_done_progress_params: WorkDoneProgressParams::default(),
282 "newText": r#"mod bar;
288 pub use std::collections::HashMap;
306 fn test_missing_module_code_action() {
307 if skip_slow_tests() {
311 let server = project(
324 server.wait_until_workspace_is_loaded();
325 let empty_context = || CodeActionContext { diagnostics: Vec::new(), only: None };
326 server.request::<CodeActionRequest>(
328 text_document: server.doc_id("src/lib.rs"),
329 range: Range::new(Position::new(0, 4), Position::new(0, 7)),
330 context: empty_context(),
331 partial_result_params: PartialResultParams::default(),
332 work_done_progress_params: WorkDoneProgressParams::default(),
339 "cursorPosition": null,
340 "label": "create module",
345 "uri": "file:///[..]/src/bar.rs"
351 "command": "rust-analyzer.applySourceChange",
352 "title": "create module"
354 "title": "create module"
359 server.request::<CodeActionRequest>(
361 text_document: server.doc_id("src/lib.rs"),
362 range: Range::new(Position::new(2, 4), Position::new(2, 7)),
363 context: empty_context(),
364 partial_result_params: PartialResultParams::default(),
365 work_done_progress_params: WorkDoneProgressParams::default(),
372 fn test_missing_module_code_action_in_json_project() {
373 if skip_slow_tests() {
377 let tmp_dir = TempDir::new().unwrap();
379 let path = tmp_dir.path();
381 let project = json!({
384 "root_module": path.join("src/lib.rs"),
394 //- rust-project.json
402 PROJECT = project.to_string(),
405 let server = Project::with_fixture(&code).tmp_dir(tmp_dir).server();
407 server.wait_until_workspace_is_loaded();
408 let empty_context = || CodeActionContext { diagnostics: Vec::new(), only: None };
409 server.request::<CodeActionRequest>(
411 text_document: server.doc_id("src/lib.rs"),
412 range: Range::new(Position::new(0, 4), Position::new(0, 7)),
413 context: empty_context(),
414 partial_result_params: PartialResultParams::default(),
415 work_done_progress_params: WorkDoneProgressParams::default(),
422 "cursorPosition": null,
423 "label": "create module",
428 "uri": "file:///[..]/src/bar.rs"
434 "command": "rust-analyzer.applySourceChange",
435 "title": "create module"
437 "title": "create module"
442 server.request::<CodeActionRequest>(
444 text_document: server.doc_id("src/lib.rs"),
445 range: Range::new(Position::new(2, 4), Position::new(2, 7)),
446 context: empty_context(),
447 partial_result_params: PartialResultParams::default(),
448 work_done_progress_params: WorkDoneProgressParams::default(),
455 fn diagnostics_dont_block_typing() {
456 if skip_slow_tests() {
460 let librs: String = (0..10).map(|i| format!("mod m{};", i)).collect();
461 let libs: String = (0..10).map(|i| format!("//- src/m{}.rs\nfn foo() {{}}\n\n", i)).collect();
462 let server = Project::with_fixture(&format!(
481 server.wait_until_workspace_is_loaded();
483 server.notification::<DidOpenTextDocument>(DidOpenTextDocumentParams {
484 text_document: TextDocumentItem {
485 uri: server.doc_id(&format!("src/m{}.rs", i)).uri,
486 language_id: "rust".to_string(),
488 text: "/// Docs\nfn foo() {}".to_string(),
492 let start = std::time::Instant::now();
493 server.request::<OnEnter>(
494 TextDocumentPositionParams {
495 text_document: server.doc_id("src/m0.rs"),
496 position: Position { line: 0, character: 5 },
500 "position": { "character": 4, "line": 1 },
501 "textDocument": { "uri": "file:///[..]src/m0.rs" }
511 "end": { "character": 5, "line": 0 },
512 "start": { "character": 5, "line": 0 }
516 "textDocument": { "uri": "file:///[..]src/m0.rs", "version": null }
522 let elapsed = start.elapsed();
523 assert!(elapsed.as_millis() < 2000, "typing enter took {:?}", elapsed);
527 fn preserves_dos_line_endings() {
528 if skip_slow_tests() {
532 let server = Project::with_fixture(
540 /// Some Docs\r\nfn main() {}
545 server.request::<OnEnter>(
546 TextDocumentPositionParams {
547 text_document: server.doc_id("src/main.rs"),
548 position: Position { line: 0, character: 8 },
552 "position": { "line": 1, "character": 4 },
553 "textDocument": { "uri": "file:///[..]src/main.rs" }
561 "newText": "\r\n/// ",
563 "end": { "line": 0, "character": 8 },
564 "start": { "line": 0, "character": 8 }
568 "textDocument": { "uri": "file:///[..]src/main.rs", "version": null }
577 fn resolve_include_concat_env() {
578 if skip_slow_tests() {
582 let server = Project::with_fixture(
590 use std::{env, fs, path::Path};
593 let out_dir = env::var_os("OUT_DIR").unwrap();
594 let dest_path = Path::new(&out_dir).join("hello.rs");
597 r#"pub fn message() -> &'static str { "Hello, World!" }"#,
600 println!("cargo:rerun-if-changed=build.rs");
603 include!(concat!(env!("OUT_DIR"), "/hello.rs"));
605 fn main() { message(); }
608 .with_config(|config| {
609 config.cargo.load_out_dirs_from_check = true;
612 server.wait_until_workspace_is_loaded();
613 let res = server.send_request::<GotoDefinition>(TextDocumentPositionParams::new(
614 server.doc_id("src/main.rs"),
615 Position::new(2, 15),
617 assert!(format!("{}", res).contains("hello.rs"));
621 fn resolve_proc_macro() {
622 if skip_slow_tests() {
625 let server = Project::with_fixture(
633 bar = {path = "../bar"}
656 extern crate proc_macro;
657 use proc_macro::{Delimiter, Group, Ident, Span, TokenStream, TokenTree};
660 TokenTree::from(Ident::new($n, Span::call_site()))
663 TokenTree::from(Group::new(Delimiter::Brace, TokenStream::new()))
666 TokenTree::from(Group::new(Delimiter::Parenthesis, TokenStream::new()))
669 #[proc_macro_derive(Bar)]
670 pub fn foo(_input: TokenStream) -> TokenStream {
671 // We hard code the output here for preventing to use any deps
672 let mut res = TokenStream::new();
674 // impl Bar for Foo { fn bar() {} }
675 let mut tokens = vec![t!("impl"), t!("Bar"), t!("for"), t!("Foo")];
676 let mut fn_stream = TokenStream::new();
677 fn_stream.extend(vec![t!("fn"), t!("bar"), t!(()), t!({})]);
678 tokens.push(Group::new(Delimiter::Brace, fn_stream).into());
685 .with_config(|config| {
686 let macro_srv_path = PathBuf::from(env!("CARGO_BIN_EXE_rust-analyzer"));
688 config.cargo.load_out_dirs_from_check = true;
689 config.proc_macro_srv = Some((macro_srv_path, vec!["proc-macro".into()]));
694 server.wait_until_workspace_is_loaded();
695 let res = server.send_request::<HoverRequest>(TextDocumentPositionParams::new(
696 server.doc_id("foo/src/main.rs"),
700 let value = res.get("contents").unwrap().get("value").unwrap().to_string();
701 assert_eq!(value, r#""```rust\nfoo::Bar\nfn bar()\n```""#)