3 use std::{collections::HashMap, path::PathBuf, time::Instant};
6 notification::DidOpenTextDocument,
7 request::{CodeActionRequest, Completion, Formatting, GotoTypeDefinition, HoverRequest},
8 CodeActionContext, CodeActionParams, CompletionParams, DidOpenTextDocumentParams,
9 DocumentFormattingParams, FormattingOptions, GotoDefinitionParams, HoverParams,
10 PartialResultParams, Position, Range, TextDocumentItem, TextDocumentPositionParams,
11 WorkDoneProgressParams,
13 use rust_analyzer::lsp_ext::{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_project() {
61 if skip_slow_tests() {
74 //- /foo/tests/spam.rs
87 let server = Project::with_fixture(code).root("foo").root("bar").server();
89 server.wait_until_workspace_is_loaded();
90 server.request::<Runnables>(
91 RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None },
95 "cargoArgs": ["test", "--package", "foo", "--test", "spam"],
96 "executableArgs": ["test_eggs", "--exact", "--nocapture"],
97 "workspaceRoot": server.path().join("foo")
100 "label": "test test_eggs",
103 "end": { "character": 17, "line": 1 },
104 "start": { "character": 0, "line": 0 }
106 "targetSelectionRange": {
107 "end": { "character": 12, "line": 1 },
108 "start": { "character": 3, "line": 1 }
110 "targetUri": "file:///[..]/tests/spam.rs"
115 "cargoArgs": ["check", "--package", "foo"],
116 "executableArgs": [],
117 "workspaceRoot": server.path().join("foo")
120 "label": "cargo check -p foo"
124 "cargoArgs": ["test", "--package", "foo"],
125 "executableArgs": [],
126 "workspaceRoot": server.path().join("foo")
129 "label": "cargo test -p foo"
136 fn test_format_document() {
137 if skip_slow_tests() {
141 let server = project(
154 pub use std::collections::HashMap;
157 server.wait_until_workspace_is_loaded();
159 server.request::<Formatting>(
160 DocumentFormattingParams {
161 text_document: server.doc_id("src/lib.rs"),
162 options: FormattingOptions {
164 insert_spaces: false,
165 insert_final_newline: None,
166 trim_final_newlines: None,
167 trim_trailing_whitespace: None,
168 properties: HashMap::new(),
170 work_done_progress_params: WorkDoneProgressParams::default(),
174 "newText": r#"mod bar;
178 pub use std::collections::HashMap;
196 fn test_format_document_2018() {
197 if skip_slow_tests() {
201 let server = project(
218 pub use std::collections::HashMap;
221 server.wait_until_workspace_is_loaded();
223 server.request::<Formatting>(
224 DocumentFormattingParams {
225 text_document: server.doc_id("src/lib.rs"),
226 options: FormattingOptions {
228 insert_spaces: false,
229 properties: HashMap::new(),
230 insert_final_newline: None,
231 trim_final_newlines: None,
232 trim_trailing_whitespace: None,
234 work_done_progress_params: WorkDoneProgressParams::default(),
238 "newText": r#"mod bar;
244 pub use std::collections::HashMap;
262 fn test_missing_module_code_action() {
263 if skip_slow_tests() {
267 let server = project(
280 server.wait_until_workspace_is_loaded();
281 let empty_context = || CodeActionContext { diagnostics: Vec::new(), only: None };
282 server.request::<CodeActionRequest>(
284 text_document: server.doc_id("src/lib.rs"),
285 range: Range::new(Position::new(0, 4), Position::new(0, 7)),
286 context: empty_context(),
287 partial_result_params: PartialResultParams::default(),
288 work_done_progress_params: WorkDoneProgressParams::default(),
295 "uri": "file:///[..]/src/bar.rs"
300 "title": "Create module"
304 server.request::<CodeActionRequest>(
306 text_document: server.doc_id("src/lib.rs"),
307 range: Range::new(Position::new(2, 4), Position::new(2, 7)),
308 context: empty_context(),
309 partial_result_params: PartialResultParams::default(),
310 work_done_progress_params: WorkDoneProgressParams::default(),
317 fn test_missing_module_code_action_in_json_project() {
318 if skip_slow_tests() {
322 let tmp_dir = TempDir::new().unwrap();
324 let path = tmp_dir.path();
326 let project = json!({
329 "root_module": path.join("src/lib.rs"),
332 "cfg": [ "cfg_atom_1", "feature=cfg_1"],
338 //- /rust-project.json
346 PROJECT = project.to_string(),
349 let server = Project::with_fixture(&code).tmp_dir(tmp_dir).server();
351 server.wait_until_workspace_is_loaded();
352 let empty_context = || CodeActionContext { diagnostics: Vec::new(), only: None };
353 server.request::<CodeActionRequest>(
355 text_document: server.doc_id("src/lib.rs"),
356 range: Range::new(Position::new(0, 4), Position::new(0, 7)),
357 context: empty_context(),
358 partial_result_params: PartialResultParams::default(),
359 work_done_progress_params: WorkDoneProgressParams::default(),
366 "uri": "file://[..]/src/bar.rs"
371 "title": "Create module"
375 server.request::<CodeActionRequest>(
377 text_document: server.doc_id("src/lib.rs"),
378 range: Range::new(Position::new(2, 4), Position::new(2, 7)),
379 context: empty_context(),
380 partial_result_params: PartialResultParams::default(),
381 work_done_progress_params: WorkDoneProgressParams::default(),
388 fn diagnostics_dont_block_typing() {
389 if skip_slow_tests() {
393 let librs: String = (0..10).map(|i| format!("mod m{};", i)).collect();
394 let libs: String = (0..10).map(|i| format!("//- /src/m{}.rs\nfn foo() {{}}\n\n", i)).collect();
395 let server = Project::with_fixture(&format!(
414 server.wait_until_workspace_is_loaded();
416 server.notification::<DidOpenTextDocument>(DidOpenTextDocumentParams {
417 text_document: TextDocumentItem {
418 uri: server.doc_id(&format!("src/m{}.rs", i)).uri,
419 language_id: "rust".to_string(),
421 text: "/// Docs\nfn foo() {}".to_string(),
425 let start = std::time::Instant::now();
426 server.request::<OnEnter>(
427 TextDocumentPositionParams {
428 text_document: server.doc_id("src/m0.rs"),
429 position: Position { line: 0, character: 5 },
432 "insertTextFormat": 2,
433 "newText": "\n/// $0",
435 "end": { "character": 5, "line": 0 },
436 "start": { "character": 5, "line": 0 }
440 let elapsed = start.elapsed();
441 assert!(elapsed.as_millis() < 2000, "typing enter took {:?}", elapsed);
445 fn preserves_dos_line_endings() {
446 if skip_slow_tests() {
450 let server = Project::with_fixture(
458 /// Some Docs\r\nfn main() {}
463 server.request::<OnEnter>(
464 TextDocumentPositionParams {
465 text_document: server.doc_id("src/main.rs"),
466 position: Position { line: 0, character: 8 },
469 "insertTextFormat": 2,
470 "newText": "\r\n/// $0",
472 "end": { "line": 0, "character": 8 },
473 "start": { "line": 0, "character": 8 }
480 fn out_dirs_check() {
481 if skip_slow_tests() {
485 let server = Project::with_fixture(
493 use std::{env, fs, path::Path};
496 let out_dir = env::var_os("OUT_DIR").unwrap();
497 let dest_path = Path::new(&out_dir).join("hello.rs");
500 r#"pub fn message() -> &'static str { "Hello, World!" }"#,
503 println!("cargo:rustc-cfg=atom_cfg");
504 println!("cargo:rustc-cfg=featlike=\"set\"");
505 println!("cargo:rerun-if-changed=build.rs");
508 #[rustc_builtin_macro] macro_rules! include {}
509 #[rustc_builtin_macro] macro_rules! concat {}
510 #[rustc_builtin_macro] macro_rules! env {}
512 include!(concat!(env!("OUT_DIR"), "/hello.rs"));
518 #[cfg(featlike = "set")]
520 #[cfg(featlike = "not_set")]
526 let should_be_str = 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::<HoverRequest>(HoverParams {
536 text_document_position_params: TextDocumentPositionParams::new(
537 server.doc_id("src/main.rs"),
538 Position::new(18, 10),
540 work_done_progress_params: Default::default(),
542 assert!(res.to_string().contains("&str"));
543 server.request::<GotoTypeDefinition>(
544 GotoDefinitionParams {
545 text_document_position_params: TextDocumentPositionParams::new(
546 server.doc_id("src/main.rs"),
547 Position::new(16, 9),
549 work_done_progress_params: Default::default(),
550 partial_result_params: Default::default(),
553 "originSelectionRange": {
554 "end": { "character": 10, "line": 16 },
555 "start": { "character": 8, "line": 16 }
558 "end": { "character": 9, "line": 7 },
559 "start": { "character": 0, "line": 6 }
561 "targetSelectionRange": {
562 "end": { "character": 8, "line": 7 },
563 "start": { "character": 7, "line": 7 }
565 "targetUri": "file:///[..]src/main.rs"
568 server.request::<GotoTypeDefinition>(
569 GotoDefinitionParams {
570 text_document_position_params: TextDocumentPositionParams::new(
571 server.doc_id("src/main.rs"),
572 Position::new(17, 9),
574 work_done_progress_params: Default::default(),
575 partial_result_params: Default::default(),
578 "originSelectionRange": {
579 "end": { "character": 10, "line": 17 },
580 "start": { "character": 8, "line": 17 }
583 "end": { "character": 9, "line": 11 },
584 "start": { "character": 0, "line":10 }
586 "targetSelectionRange": {
587 "end": { "character": 8, "line": 11 },
588 "start": { "character": 7, "line": 11 }
590 "targetUri": "file:///[..]src/main.rs"
596 fn resolve_proc_macro() {
597 if skip_slow_tests() {
600 let server = Project::with_fixture(
608 bar = {path = "../bar"}
631 extern crate proc_macro;
632 use proc_macro::{Delimiter, Group, Ident, Span, TokenStream, TokenTree};
635 TokenTree::from(Ident::new($n, Span::call_site()))
638 TokenTree::from(Group::new(Delimiter::Brace, TokenStream::new()))
641 TokenTree::from(Group::new(Delimiter::Parenthesis, TokenStream::new()))
644 #[proc_macro_derive(Bar)]
645 pub fn foo(_input: TokenStream) -> TokenStream {
646 // We hard code the output here for preventing to use any deps
647 let mut res = TokenStream::new();
649 // impl Bar for Foo { fn bar() {} }
650 let mut tokens = vec![t!("impl"), t!("Bar"), t!("for"), t!("Foo")];
651 let mut fn_stream = TokenStream::new();
652 fn_stream.extend(vec![t!("fn"), t!("bar"), t!(()), t!({})]);
653 tokens.push(Group::new(Delimiter::Brace, fn_stream).into());
660 .with_config(|config| {
661 let macro_srv_path = PathBuf::from(env!("CARGO_BIN_EXE_rust-analyzer"));
663 config.cargo.load_out_dirs_from_check = true;
664 config.proc_macro_srv = Some((macro_srv_path, vec!["proc-macro".into()]));
669 server.wait_until_workspace_is_loaded();
670 let res = server.send_request::<HoverRequest>(HoverParams {
671 text_document_position_params: TextDocumentPositionParams::new(
672 server.doc_id("foo/src/main.rs"),
675 work_done_progress_params: Default::default(),
678 let value = res.get("contents").unwrap().get("value").unwrap().to_string();
679 assert_eq!(value, r#""```rust\nfoo::Bar\n```\n\n```rust\nfn bar()\n```""#)