3 use std::{collections::HashMap, path::PathBuf, time::Instant};
6 CodeActionContext, DidOpenTextDocumentParams, DocumentFormattingParams, FormattingOptions,
7 GotoDefinitionParams, HoverParams, PartialResultParams, Position, Range, TextDocumentItem,
8 TextDocumentPositionParams, WorkDoneProgressParams,
10 use rust_analyzer::req::{
11 CodeActionParams, CodeActionRequest, Completion, CompletionParams, DidOpenTextDocument,
12 Formatting, GotoDefinition, GotoTypeDefinition, HoverRequest, OnEnter, Runnables,
16 use tempfile::TempDir;
17 use test_utils::skip_slow_tests;
19 use crate::support::{project, Project};
21 const PROFILE: &str = "";
22 // const PROFILE: &'static str = "*@3>100";
25 fn completes_items_from_standard_library() {
26 if skip_slow_tests() {
30 let project_start = Instant::now();
31 let server = Project::with_fixture(
39 use std::collections::Spam;
44 server.wait_until_workspace_is_loaded();
45 eprintln!("loading took {:?}", project_start.elapsed());
46 let completion_start = Instant::now();
47 let res = server.send_request::<Completion>(CompletionParams {
48 text_document_position: TextDocumentPositionParams::new(
49 server.doc_id("src/lib.rs"),
53 partial_result_params: PartialResultParams::default(),
54 work_done_progress_params: WorkDoneProgressParams::default(),
56 assert!(format!("{}", res).contains("HashMap"));
57 eprintln!("completion took {:?}", completion_start.elapsed());
61 fn test_runnables_no_project() {
62 if skip_slow_tests() {
74 server.wait_until_workspace_is_loaded();
75 server.request::<Runnables>(
76 RunnablesParams { text_document: server.doc_id("lib.rs"), position: None },
80 "extraArgs": [ "foo", "--nocapture" ],
82 "env": { "RUST_BACKTRACE": "short" },
86 "end": { "character": 1, "line": 2 },
87 "start": { "character": 0, "line": 0 }
91 "args": ["check", "--workspace"],
96 "label": "cargo check --workspace",
98 "end": { "character": 0, "line": 0 },
99 "start": { "character": 0, "line": 0 }
107 fn test_runnables_project() {
108 if skip_slow_tests() {
121 //- foo/tests/spam.rs
134 let server = Project::with_fixture(code).root("foo").root("bar").server();
136 server.wait_until_workspace_is_loaded();
137 server.request::<Runnables>(
138 RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None },
141 "args": [ "test", "--package", "foo", "--test", "spam" ],
142 "extraArgs": [ "test_eggs", "--exact", "--nocapture" ],
144 "env": { "RUST_BACKTRACE": "short" },
145 "label": "test test_eggs",
147 "end": { "character": 17, "line": 1 },
148 "start": { "character": 0, "line": 0 }
150 "cwd": server.path().join("foo")
153 "args": [ "check", "--package", "foo" ],
157 "label": "cargo check -p foo",
159 "end": { "character": 0, "line": 0 },
160 "start": { "character": 0, "line": 0 }
162 "cwd": server.path().join("foo")
165 "args": [ "test", "--package", "foo" ],
169 "label": "cargo test -p foo",
171 "end": { "character": 0, "line": 0 },
172 "start": { "character": 0, "line": 0 }
174 "cwd": server.path().join("foo")
181 fn test_format_document() {
182 if skip_slow_tests() {
186 let server = project(
199 pub use std::collections::HashMap;
202 server.wait_until_workspace_is_loaded();
204 server.request::<Formatting>(
205 DocumentFormattingParams {
206 text_document: server.doc_id("src/lib.rs"),
207 options: FormattingOptions {
209 insert_spaces: false,
210 insert_final_newline: None,
211 trim_final_newlines: None,
212 trim_trailing_whitespace: None,
213 properties: HashMap::new(),
215 work_done_progress_params: WorkDoneProgressParams::default(),
219 "newText": r#"mod bar;
223 pub use std::collections::HashMap;
241 fn test_format_document_2018() {
242 if skip_slow_tests() {
246 let server = project(
263 pub use std::collections::HashMap;
266 server.wait_until_workspace_is_loaded();
268 server.request::<Formatting>(
269 DocumentFormattingParams {
270 text_document: server.doc_id("src/lib.rs"),
271 options: FormattingOptions {
273 insert_spaces: false,
274 properties: HashMap::new(),
275 insert_final_newline: None,
276 trim_final_newlines: None,
277 trim_trailing_whitespace: None,
279 work_done_progress_params: WorkDoneProgressParams::default(),
283 "newText": r#"mod bar;
289 pub use std::collections::HashMap;
307 fn test_missing_module_code_action() {
308 if skip_slow_tests() {
312 let server = project(
325 server.wait_until_workspace_is_loaded();
326 let empty_context = || CodeActionContext { diagnostics: Vec::new(), only: None };
327 server.request::<CodeActionRequest>(
329 text_document: server.doc_id("src/lib.rs"),
330 range: Range::new(Position::new(0, 4), Position::new(0, 7)),
331 context: empty_context(),
332 partial_result_params: PartialResultParams::default(),
333 work_done_progress_params: WorkDoneProgressParams::default(),
340 "cursorPosition": null,
341 "label": "Create module",
346 "uri": "file:///[..]/src/bar.rs"
352 "command": "rust-analyzer.applySourceChange",
353 "title": "Create module"
355 "title": "Create module"
360 server.request::<CodeActionRequest>(
362 text_document: server.doc_id("src/lib.rs"),
363 range: Range::new(Position::new(2, 4), Position::new(2, 7)),
364 context: empty_context(),
365 partial_result_params: PartialResultParams::default(),
366 work_done_progress_params: WorkDoneProgressParams::default(),
373 fn test_missing_module_code_action_in_json_project() {
374 if skip_slow_tests() {
378 let tmp_dir = TempDir::new().unwrap();
380 let path = tmp_dir.path();
382 let project = json!({
385 "root_module": path.join("src/lib.rs"),
395 //- rust-project.json
403 PROJECT = project.to_string(),
406 let server = Project::with_fixture(&code).tmp_dir(tmp_dir).server();
408 server.wait_until_workspace_is_loaded();
409 let empty_context = || CodeActionContext { diagnostics: Vec::new(), only: None };
410 server.request::<CodeActionRequest>(
412 text_document: server.doc_id("src/lib.rs"),
413 range: Range::new(Position::new(0, 4), Position::new(0, 7)),
414 context: empty_context(),
415 partial_result_params: PartialResultParams::default(),
416 work_done_progress_params: WorkDoneProgressParams::default(),
423 "cursorPosition": null,
424 "label": "Create module",
429 "uri": "file:///[..]/src/bar.rs"
435 "command": "rust-analyzer.applySourceChange",
436 "title": "Create module"
438 "title": "Create module"
443 server.request::<CodeActionRequest>(
445 text_document: server.doc_id("src/lib.rs"),
446 range: Range::new(Position::new(2, 4), Position::new(2, 7)),
447 context: empty_context(),
448 partial_result_params: PartialResultParams::default(),
449 work_done_progress_params: WorkDoneProgressParams::default(),
456 fn diagnostics_dont_block_typing() {
457 if skip_slow_tests() {
461 let librs: String = (0..10).map(|i| format!("mod m{};", i)).collect();
462 let libs: String = (0..10).map(|i| format!("//- src/m{}.rs\nfn foo() {{}}\n\n", i)).collect();
463 let server = Project::with_fixture(&format!(
482 server.wait_until_workspace_is_loaded();
484 server.notification::<DidOpenTextDocument>(DidOpenTextDocumentParams {
485 text_document: TextDocumentItem {
486 uri: server.doc_id(&format!("src/m{}.rs", i)).uri,
487 language_id: "rust".to_string(),
489 text: "/// Docs\nfn foo() {}".to_string(),
493 let start = std::time::Instant::now();
494 server.request::<OnEnter>(
495 TextDocumentPositionParams {
496 text_document: server.doc_id("src/m0.rs"),
497 position: Position { line: 0, character: 5 },
501 "position": { "character": 4, "line": 1 },
502 "textDocument": { "uri": "file:///[..]src/m0.rs" }
512 "end": { "character": 5, "line": 0 },
513 "start": { "character": 5, "line": 0 }
517 "textDocument": { "uri": "file:///[..]src/m0.rs", "version": null }
523 let elapsed = start.elapsed();
524 assert!(elapsed.as_millis() < 2000, "typing enter took {:?}", elapsed);
528 fn preserves_dos_line_endings() {
529 if skip_slow_tests() {
533 let server = Project::with_fixture(
541 /// Some Docs\r\nfn main() {}
546 server.request::<OnEnter>(
547 TextDocumentPositionParams {
548 text_document: server.doc_id("src/main.rs"),
549 position: Position { line: 0, character: 8 },
553 "position": { "line": 1, "character": 4 },
554 "textDocument": { "uri": "file:///[..]src/main.rs" }
562 "newText": "\r\n/// ",
564 "end": { "line": 0, "character": 8 },
565 "start": { "line": 0, "character": 8 }
569 "textDocument": { "uri": "file:///[..]src/main.rs", "version": null }
578 fn out_dirs_check() {
579 if skip_slow_tests() {
583 let server = Project::with_fixture(
591 use std::{env, fs, path::Path};
594 let out_dir = env::var_os("OUT_DIR").unwrap();
595 let dest_path = Path::new(&out_dir).join("hello.rs");
598 r#"pub fn message() -> &'static str { "Hello, World!" }"#,
601 println!("cargo:rustc-cfg=atom_cfg");
602 println!("cargo:rustc-cfg=featlike=\"set\"");
603 println!("cargo:rerun-if-changed=build.rs");
606 include!(concat!(env!("OUT_DIR"), "/hello.rs"));
612 #[cfg(featlike = "set")]
614 #[cfg(featlike = "not_set")]
623 fn main() { message(); }
626 .with_config(|config| {
627 config.cargo.load_out_dirs_from_check = true;
630 server.wait_until_workspace_is_loaded();
631 let res = server.send_request::<GotoDefinition>(GotoDefinitionParams {
632 text_document_position_params: TextDocumentPositionParams::new(
633 server.doc_id("src/main.rs"),
634 Position::new(14, 8),
636 work_done_progress_params: Default::default(),
637 partial_result_params: Default::default(),
639 assert!(format!("{}", res).contains("hello.rs"));
640 server.request::<GotoTypeDefinition>(
641 GotoDefinitionParams {
642 text_document_position_params: TextDocumentPositionParams::new(
643 server.doc_id("src/main.rs"),
644 Position::new(12, 9),
646 work_done_progress_params: Default::default(),
647 partial_result_params: Default::default(),
650 "originSelectionRange": {
670 "targetSelectionRange": {
680 "targetUri": "file:///[..]src/main.rs"
683 server.request::<GotoTypeDefinition>(
684 GotoDefinitionParams {
685 text_document_position_params: TextDocumentPositionParams::new(
686 server.doc_id("src/main.rs"),
687 Position::new(13, 9),
689 work_done_progress_params: Default::default(),
690 partial_result_params: Default::default(),
693 "originSelectionRange": {
713 "targetSelectionRange": {
723 "targetUri": "file:///[..]src/main.rs"
729 fn resolve_proc_macro() {
730 if skip_slow_tests() {
733 let server = Project::with_fixture(
741 bar = {path = "../bar"}
764 extern crate proc_macro;
765 use proc_macro::{Delimiter, Group, Ident, Span, TokenStream, TokenTree};
768 TokenTree::from(Ident::new($n, Span::call_site()))
771 TokenTree::from(Group::new(Delimiter::Brace, TokenStream::new()))
774 TokenTree::from(Group::new(Delimiter::Parenthesis, TokenStream::new()))
777 #[proc_macro_derive(Bar)]
778 pub fn foo(_input: TokenStream) -> TokenStream {
779 // We hard code the output here for preventing to use any deps
780 let mut res = TokenStream::new();
782 // impl Bar for Foo { fn bar() {} }
783 let mut tokens = vec![t!("impl"), t!("Bar"), t!("for"), t!("Foo")];
784 let mut fn_stream = TokenStream::new();
785 fn_stream.extend(vec![t!("fn"), t!("bar"), t!(()), t!({})]);
786 tokens.push(Group::new(Delimiter::Brace, fn_stream).into());
793 .with_config(|config| {
794 let macro_srv_path = PathBuf::from(env!("CARGO_BIN_EXE_rust-analyzer"));
796 config.cargo.load_out_dirs_from_check = true;
797 config.proc_macro_srv = Some((macro_srv_path, vec!["proc-macro".into()]));
802 server.wait_until_workspace_is_loaded();
803 let res = server.send_request::<HoverRequest>(HoverParams {
804 text_document_position_params: TextDocumentPositionParams::new(
805 server.doc_id("foo/src/main.rs"),
808 work_done_progress_params: Default::default(),
811 let value = res.get("contents").unwrap().get("value").unwrap().to_string();
812 assert_eq!(value, r#""```rust\nfoo::Bar\nfn bar()\n```""#)