aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_arena/src/map.rs10
-rw-r--r--crates/ra_assists/src/handlers/fill_match_arms.rs100
-rw-r--r--crates/ra_flycheck/Cargo.toml (renamed from crates/ra_cargo_watch/Cargo.toml)3
-rw-r--r--crates/ra_flycheck/src/conv.rs (renamed from crates/ra_cargo_watch/src/conv.rs)0
-rw-r--r--crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_clippy_pass_by_ref.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap)2
-rw-r--r--crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_handles_macro_location.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_handles_macro_location.snap)2
-rw-r--r--crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_macro_compiler_error.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_macro_compiler_error.snap)2
-rw-r--r--crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_multi_line_fix.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_multi_line_fix.snap)2
-rw-r--r--crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_incompatible_type_for_trait.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_incompatible_type_for_trait.snap)2
-rw-r--r--crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_mismatched_type.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_mismatched_type.snap)2
-rw-r--r--crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_unused_variable.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_unused_variable.snap)2
-rw-r--r--crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_wrong_number_of_parameters.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_wrong_number_of_parameters.snap)2
-rw-r--r--crates/ra_flycheck/src/conv/test.rs (renamed from crates/ra_cargo_watch/src/conv/test.rs)0
-rw-r--r--crates/ra_flycheck/src/lib.rs (renamed from crates/ra_cargo_watch/src/lib.rs)248
-rw-r--r--crates/ra_hir_expand/src/proc_macro.rs27
-rw-r--r--crates/ra_hir_ty/src/display.rs44
-rw-r--r--crates/ra_ide/src/completion.rs10
-rw-r--r--crates/ra_ide/src/completion/complete_postfix.rs2
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs8
-rw-r--r--crates/ra_ide/src/completion/presentation.rs12
-rw-r--r--crates/ra_ide/src/completion/test_utils.rs6
-rw-r--r--crates/ra_ide/src/inlay_hints.rs68
-rw-r--r--crates/ra_ide/src/lib.rs17
-rw-r--r--crates/ra_ide_db/src/lib.rs12
-rw-r--r--crates/ra_proc_macro/Cargo.toml5
-rw-r--r--crates/ra_proc_macro/src/lib.rs85
-rw-r--r--crates/ra_proc_macro/src/msg.rs93
-rw-r--r--crates/ra_proc_macro/src/process.rs196
-rw-r--r--crates/ra_proc_macro/src/rpc.rs266
-rw-r--r--crates/ra_prof/src/lib.rs8
-rw-r--r--crates/ra_project_model/Cargo.toml1
-rw-r--r--crates/ra_project_model/src/cargo_workspace.rs91
-rw-r--r--crates/ra_syntax/src/ast/edit.rs141
-rw-r--r--crates/ra_syntax/src/parsing/text_tree_sink.rs13
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0065_comment_newline.rs3
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0065_comment_newline.txt17
-rw-r--r--crates/ra_tt/src/lib.rs7
-rw-r--r--crates/rust-analyzer/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/cargo_target_spec.rs2
-rw-r--r--crates/rust-analyzer/src/cli/analysis_bench.rs4
-rw-r--r--crates/rust-analyzer/src/conv.rs2
-rw-r--r--crates/rust-analyzer/src/main_loop.rs247
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs20
-rw-r--r--crates/rust-analyzer/src/req.rs2
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs6
-rw-r--r--crates/rust-analyzer/src/world.rs101
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/main.rs46
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/support.rs25
-rw-r--r--crates/stdx/src/lib.rs2
49 files changed, 1464 insertions, 504 deletions
diff --git a/crates/ra_arena/src/map.rs b/crates/ra_arena/src/map.rs
index 5e764113d..0f33907c0 100644
--- a/crates/ra_arena/src/map.rs
+++ b/crates/ra_arena/src/map.rs
@@ -14,14 +14,8 @@ pub struct ArenaMap<ID, V> {
14impl<T, V> ArenaMap<Idx<T>, V> { 14impl<T, V> ArenaMap<Idx<T>, V> {
15 pub fn insert(&mut self, id: Idx<T>, t: V) { 15 pub fn insert(&mut self, id: Idx<T>, t: V) {
16 let idx = Self::to_idx(id); 16 let idx = Self::to_idx(id);
17 if self.v.capacity() <= idx { 17
18 self.v.reserve(idx + 1 - self.v.capacity()); 18 self.v.resize_with((idx + 1).max(self.v.len()), || None);
19 }
20 if self.v.len() <= idx {
21 while self.v.len() <= idx {
22 self.v.push(None);
23 }
24 }
25 self.v[idx] = Some(t); 19 self.v[idx] = Some(t);
26 } 20 }
27 21
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs
index add82e5b1..8d1af9933 100644
--- a/crates/ra_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ra_assists/src/handlers/fill_match_arms.rs
@@ -1,15 +1,11 @@
1//! FIXME: write short doc here
2
3use std::iter; 1use std::iter;
4 2
5use hir::{Adt, HasSource, ModuleDef, Semantics}; 3use hir::{Adt, HasSource, ModuleDef, Semantics};
6use itertools::Itertools; 4use itertools::Itertools;
7use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat};
8 7
9use crate::{Assist, AssistCtx, AssistId}; 8use crate::{Assist, AssistCtx, AssistId};
10use ra_syntax::ast::{self, edit::IndentLevel, make, AstNode, NameOwner};
11
12use ast::{MatchArm, Pat};
13 9
14// Assist: fill_match_arms 10// Assist: fill_match_arms
15// 11//
@@ -97,10 +93,7 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
97 } 93 }
98 94
99 ctx.add_assist(AssistId("fill_match_arms"), "Fill match arms", |edit| { 95 ctx.add_assist(AssistId("fill_match_arms"), "Fill match arms", |edit| {
100 arms.extend(missing_arms); 96 let new_arm_list = match_arm_list.remove_placeholder().append_arms(missing_arms);
101
102 let indent_level = IndentLevel::from_node(match_arm_list.syntax());
103 let new_arm_list = indent_level.increase_indent(make::match_arm_list(arms));
104 97
105 edit.target(match_expr.syntax().text_range()); 98 edit.target(match_expr.syntax().text_range());
106 edit.set_cursor(expr.syntax().text_range().start()); 99 edit.set_cursor(expr.syntax().text_range().start());
@@ -655,4 +648,93 @@ mod tests {
655 "#, 648 "#,
656 ); 649 );
657 } 650 }
651
652 #[test]
653 fn fill_match_arms_preserves_comments() {
654 check_assist(
655 fill_match_arms,
656 r#"
657 enum A {
658 One,
659 Two,
660 }
661 fn foo(a: A) {
662 match a {
663 // foo bar baz<|>
664 A::One => {}
665 // This is where the rest should be
666 }
667 }
668 "#,
669 r#"
670 enum A {
671 One,
672 Two,
673 }
674 fn foo(a: A) {
675 match <|>a {
676 // foo bar baz
677 A::One => {}
678 // This is where the rest should be
679 A::Two => {}
680 }
681 }
682 "#,
683 );
684 }
685
686 #[test]
687 fn fill_match_arms_preserves_comments_empty() {
688 check_assist(
689 fill_match_arms,
690 r#"
691 enum A {
692 One,
693 Two,
694 }
695 fn foo(a: A) {
696 match a {
697 // foo bar baz<|>
698 }
699 }
700 "#,
701 r#"
702 enum A {
703 One,
704 Two,
705 }
706 fn foo(a: A) {
707 match <|>a {
708 // foo bar baz
709 A::One => {}
710 A::Two => {}
711 }
712 }
713 "#,
714 );
715 }
716
717 #[test]
718 fn fill_match_arms_placeholder() {
719 check_assist(
720 fill_match_arms,
721 r#"
722 enum A { One, Two, }
723 fn foo(a: A) {
724 match a<|> {
725 _ => (),
726 }
727 }
728 "#,
729 r#"
730 enum A { One, Two, }
731 fn foo(a: A) {
732 match <|>a {
733 A::One => {}
734 A::Two => {}
735 }
736 }
737 "#,
738 );
739 }
658} 740}
diff --git a/crates/ra_cargo_watch/Cargo.toml b/crates/ra_flycheck/Cargo.toml
index 741345a21..c9a9ddc12 100644
--- a/crates/ra_cargo_watch/Cargo.toml
+++ b/crates/ra_flycheck/Cargo.toml
@@ -1,6 +1,6 @@
1[package] 1[package]
2edition = "2018" 2edition = "2018"
3name = "ra_cargo_watch" 3name = "ra_flycheck"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6 6
@@ -10,6 +10,7 @@ lsp-types = { version = "0.73.0", features = ["proposed"] }
10log = "0.4.8" 10log = "0.4.8"
11cargo_metadata = "0.9.1" 11cargo_metadata = "0.9.1"
12serde_json = "1.0.48" 12serde_json = "1.0.48"
13jod-thread = "0.1.1"
13 14
14[dev-dependencies] 15[dev-dependencies]
15insta = "0.15.0" 16insta = "0.15.0"
diff --git a/crates/ra_cargo_watch/src/conv.rs b/crates/ra_flycheck/src/conv.rs
index 817543deb..817543deb 100644
--- a/crates/ra_cargo_watch/src/conv.rs
+++ b/crates/ra_flycheck/src/conv.rs
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_clippy_pass_by_ref.snap
index a59fa84fa..4c9db0385 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap
+++ b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_clippy_pass_by_ref.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_flycheck/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_handles_macro_location.snap b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_handles_macro_location.snap
index 61ae0c9ae..7cde4d867 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_handles_macro_location.snap
+++ b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_handles_macro_location.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_flycheck/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_macro_compiler_error.snap b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_macro_compiler_error.snap
index 641da1a58..1cc37e087 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_macro_compiler_error.snap
+++ b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_macro_compiler_error.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_flycheck/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_multi_line_fix.snap b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_multi_line_fix.snap
index 0557a2e79..615ed8378 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_multi_line_fix.snap
+++ b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_multi_line_fix.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_flycheck/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_incompatible_type_for_trait.snap b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_incompatible_type_for_trait.snap
index 754bc33a4..0df0fce18 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_incompatible_type_for_trait.snap
+++ b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_incompatible_type_for_trait.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_flycheck/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_mismatched_type.snap b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_mismatched_type.snap
index 78b7f7cc8..28ebcb3b3 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_mismatched_type.snap
+++ b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_mismatched_type.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_flycheck/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_unused_variable.snap b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_unused_variable.snap
index 5989ed202..5e0873281 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_unused_variable.snap
+++ b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_unused_variable.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_flycheck/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_wrong_number_of_parameters.snap b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_wrong_number_of_parameters.snap
index e34b546dc..e500d3cd6 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_wrong_number_of_parameters.snap
+++ b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_wrong_number_of_parameters.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_flycheck/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_cargo_watch/src/conv/test.rs b/crates/ra_flycheck/src/conv/test.rs
index 4e81455ca..4e81455ca 100644
--- a/crates/ra_cargo_watch/src/conv/test.rs
+++ b/crates/ra_flycheck/src/conv/test.rs
diff --git a/crates/ra_cargo_watch/src/lib.rs b/crates/ra_flycheck/src/lib.rs
index 7c525c430..75aece45f 100644
--- a/crates/ra_cargo_watch/src/lib.rs
+++ b/crates/ra_flycheck/src/lib.rs
@@ -1,85 +1,60 @@
1//! cargo_check provides the functionality needed to run `cargo check` or 1//! cargo_check provides the functionality needed to run `cargo check` or
2//! another compatible command (f.x. clippy) in a background thread and provide 2//! another compatible command (f.x. clippy) in a background thread and provide
3//! LSP diagnostics based on the output of the command. 3//! LSP diagnostics based on the output of the command.
4use cargo_metadata::Message; 4mod conv;
5use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender}; 5
6use lsp_types::{
7 CodeAction, CodeActionOrCommand, Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin,
8 WorkDoneProgressEnd, WorkDoneProgressReport,
9};
10use std::{ 6use std::{
11 error, fmt, 7 error, fmt,
12 io::{BufRead, BufReader}, 8 io::{BufRead, BufReader},
13 path::{Path, PathBuf}, 9 path::{Path, PathBuf},
14 process::{Command, Stdio}, 10 process::{Command, Stdio},
15 thread::JoinHandle,
16 time::Instant, 11 time::Instant,
17}; 12};
18 13
19mod conv; 14use cargo_metadata::Message;
15use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender};
16use lsp_types::{
17 CodeAction, CodeActionOrCommand, Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin,
18 WorkDoneProgressEnd, WorkDoneProgressReport,
19};
20 20
21use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic}; 21use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic};
22 22
23pub use crate::conv::url_from_path_with_drive_lowercasing; 23pub use crate::conv::url_from_path_with_drive_lowercasing;
24 24
25#[derive(Clone, Debug)] 25#[derive(Clone, Debug)]
26pub struct CheckOptions { 26pub struct CheckConfig {
27 pub enable: bool,
28 pub args: Vec<String>, 27 pub args: Vec<String>,
29 pub command: String, 28 pub command: String,
30 pub all_targets: bool, 29 pub all_targets: bool,
31} 30}
32 31
33/// CheckWatcher wraps the shared state and communication machinery used for 32/// Flycheck wraps the shared state and communication machinery used for
34/// running `cargo check` (or other compatible command) and providing 33/// running `cargo check` (or other compatible command) and providing
35/// diagnostics based on the output. 34/// diagnostics based on the output.
36/// The spawned thread is shut down when this struct is dropped. 35/// The spawned thread is shut down when this struct is dropped.
37#[derive(Debug)] 36#[derive(Debug)]
38pub struct CheckWatcher { 37pub struct Flycheck {
38 // XXX: drop order is significant
39 cmd_send: Sender<CheckCommand>,
40 handle: jod_thread::JoinHandle<()>,
39 pub task_recv: Receiver<CheckTask>, 41 pub task_recv: Receiver<CheckTask>,
40 cmd_send: Option<Sender<CheckCommand>>,
41 handle: Option<JoinHandle<()>>,
42} 42}
43 43
44impl CheckWatcher { 44impl Flycheck {
45 pub fn new(options: &CheckOptions, workspace_root: PathBuf) -> CheckWatcher { 45 pub fn new(config: CheckConfig, workspace_root: PathBuf) -> Flycheck {
46 let options = options.clone();
47
48 let (task_send, task_recv) = unbounded::<CheckTask>(); 46 let (task_send, task_recv) = unbounded::<CheckTask>();
49 let (cmd_send, cmd_recv) = unbounded::<CheckCommand>(); 47 let (cmd_send, cmd_recv) = unbounded::<CheckCommand>();
50 let handle = std::thread::spawn(move || { 48 let handle = jod_thread::spawn(move || {
51 let mut check = CheckWatcherThread::new(options, workspace_root); 49 let mut check = FlycheckThread::new(config, workspace_root);
52 check.run(&task_send, &cmd_recv); 50 check.run(&task_send, &cmd_recv);
53 }); 51 });
54 CheckWatcher { task_recv, cmd_send: Some(cmd_send), handle: Some(handle) } 52 Flycheck { task_recv, cmd_send, handle }
55 }
56
57 /// Returns a CheckWatcher that doesn't actually do anything
58 pub fn dummy() -> CheckWatcher {
59 CheckWatcher { task_recv: never(), cmd_send: None, handle: None }
60 } 53 }
61 54
62 /// Schedule a re-start of the cargo check worker. 55 /// Schedule a re-start of the cargo check worker.
63 pub fn update(&self) { 56 pub fn update(&self) {
64 if let Some(cmd_send) = &self.cmd_send { 57 self.cmd_send.send(CheckCommand::Update).unwrap();
65 cmd_send.send(CheckCommand::Update).unwrap();
66 }
67 }
68}
69
70impl std::ops::Drop for CheckWatcher {
71 fn drop(&mut self) {
72 if let Some(handle) = self.handle.take() {
73 // Take the sender out of the option
74 let cmd_send = self.cmd_send.take();
75
76 // Dropping the sender finishes the thread loop
77 drop(cmd_send);
78
79 // Join the thread, it should finish shortly. We don't really care
80 // whether it panicked, so it is safe to ignore the result
81 let _ = handle.join();
82 }
83 } 58 }
84} 59}
85 60
@@ -100,24 +75,35 @@ pub enum CheckCommand {
100 Update, 75 Update,
101} 76}
102 77
103struct CheckWatcherThread { 78struct FlycheckThread {
104 options: CheckOptions, 79 options: CheckConfig,
105 workspace_root: PathBuf, 80 workspace_root: PathBuf,
106 watcher: WatchThread,
107 last_update_req: Option<Instant>, 81 last_update_req: Option<Instant>,
82 // XXX: drop order is significant
83 message_recv: Receiver<CheckEvent>,
84 /// WatchThread exists to wrap around the communication needed to be able to
85 /// run `cargo check` without blocking. Currently the Rust standard library
86 /// doesn't provide a way to read sub-process output without blocking, so we
87 /// have to wrap sub-processes output handling in a thread and pass messages
88 /// back over a channel.
89 check_process: Option<jod_thread::JoinHandle<()>>,
108} 90}
109 91
110impl CheckWatcherThread { 92impl FlycheckThread {
111 fn new(options: CheckOptions, workspace_root: PathBuf) -> CheckWatcherThread { 93 fn new(options: CheckConfig, workspace_root: PathBuf) -> FlycheckThread {
112 CheckWatcherThread { 94 FlycheckThread {
113 options, 95 options,
114 workspace_root, 96 workspace_root,
115 watcher: WatchThread::dummy(),
116 last_update_req: None, 97 last_update_req: None,
98 message_recv: never(),
99 check_process: None,
117 } 100 }
118 } 101 }
119 102
120 fn run(&mut self, task_send: &Sender<CheckTask>, cmd_recv: &Receiver<CheckCommand>) { 103 fn run(&mut self, task_send: &Sender<CheckTask>, cmd_recv: &Receiver<CheckCommand>) {
104 // If we rerun the thread, we need to discard the previous check results first
105 self.clean_previous_results(task_send);
106
121 loop { 107 loop {
122 select! { 108 select! {
123 recv(&cmd_recv) -> cmd => match cmd { 109 recv(&cmd_recv) -> cmd => match cmd {
@@ -127,29 +113,32 @@ impl CheckWatcherThread {
127 break; 113 break;
128 }, 114 },
129 }, 115 },
130 recv(self.watcher.message_recv) -> msg => match msg { 116 recv(self.message_recv) -> msg => match msg {
131 Ok(msg) => self.handle_message(msg, task_send), 117 Ok(msg) => self.handle_message(msg, task_send),
132 Err(RecvError) => { 118 Err(RecvError) => {
133 // Watcher finished, replace it with a never channel to 119 // Watcher finished, replace it with a never channel to
134 // avoid busy-waiting. 120 // avoid busy-waiting.
135 std::mem::replace(&mut self.watcher.message_recv, never()); 121 self.message_recv = never();
122 self.check_process = None;
136 }, 123 },
137 } 124 }
138 }; 125 };
139 126
140 if self.should_recheck() { 127 if self.should_recheck() {
141 self.last_update_req.take(); 128 self.last_update_req = None;
142 task_send.send(CheckTask::ClearDiagnostics).unwrap(); 129 task_send.send(CheckTask::ClearDiagnostics).unwrap();
143 130 self.restart_check_process();
144 // Replace with a dummy watcher first so we drop the original and wait for completion
145 std::mem::replace(&mut self.watcher, WatchThread::dummy());
146
147 // Then create the actual new watcher
148 self.watcher = WatchThread::new(&self.options, &self.workspace_root);
149 } 131 }
150 } 132 }
151 } 133 }
152 134
135 fn clean_previous_results(&self, task_send: &Sender<CheckTask>) {
136 task_send.send(CheckTask::ClearDiagnostics).unwrap();
137 task_send
138 .send(CheckTask::Status(WorkDoneProgress::End(WorkDoneProgressEnd { message: None })))
139 .unwrap();
140 }
141
153 fn should_recheck(&mut self) -> bool { 142 fn should_recheck(&mut self) -> bool {
154 if let Some(_last_update_req) = &self.last_update_req { 143 if let Some(_last_update_req) = &self.last_update_req {
155 // We currently only request an update on save, as we need up to 144 // We currently only request an update on save, as we need up to
@@ -221,6 +210,56 @@ impl CheckWatcherThread {
221 CheckEvent::Msg(Message::Unknown) => {} 210 CheckEvent::Msg(Message::Unknown) => {}
222 } 211 }
223 } 212 }
213
214 fn restart_check_process(&mut self) {
215 // First, clear and cancel the old thread
216 self.message_recv = never();
217 self.check_process = None;
218
219 let mut args: Vec<String> = vec![
220 self.options.command.clone(),
221 "--workspace".to_string(),
222 "--message-format=json".to_string(),
223 "--manifest-path".to_string(),
224 format!("{}/Cargo.toml", self.workspace_root.display()),
225 ];
226 if self.options.all_targets {
227 args.push("--all-targets".to_string());
228 }
229 args.extend(self.options.args.iter().cloned());
230
231 let (message_send, message_recv) = unbounded();
232 let workspace_root = self.workspace_root.to_owned();
233 self.message_recv = message_recv;
234 self.check_process = Some(jod_thread::spawn(move || {
235 // If we trigger an error here, we will do so in the loop instead,
236 // which will break out of the loop, and continue the shutdown
237 let _ = message_send.send(CheckEvent::Begin);
238
239 let res = run_cargo(&args, Some(&workspace_root), &mut |message| {
240 // Skip certain kinds of messages to only spend time on what's useful
241 match &message {
242 Message::CompilerArtifact(artifact) if artifact.fresh => return true,
243 Message::BuildScriptExecuted(_) => return true,
244 Message::Unknown => return true,
245 _ => {}
246 }
247
248 // if the send channel was closed, we want to shutdown
249 message_send.send(CheckEvent::Msg(message)).is_ok()
250 });
251
252 if let Err(err) = res {
253 // FIXME: make the `message_send` to be `Sender<Result<CheckEvent, CargoError>>`
254 // to display user-caused misconfiguration errors instead of just logging them here
255 log::error!("Cargo watcher failed {:?}", err);
256 }
257
258 // We can ignore any error here, as we are already in the progress
259 // of shutting down.
260 let _ = message_send.send(CheckEvent::End);
261 }))
262 }
224} 263}
225 264
226#[derive(Debug)] 265#[derive(Debug)]
@@ -229,18 +268,6 @@ pub struct DiagnosticWithFixes {
229 fixes: Vec<CodeAction>, 268 fixes: Vec<CodeAction>,
230} 269}
231 270
232/// WatchThread exists to wrap around the communication needed to be able to
233/// run `cargo check` without blocking. Currently the Rust standard library
234/// doesn't provide a way to read sub-process output without blocking, so we
235/// have to wrap sub-processes output handling in a thread and pass messages
236/// back over a channel.
237/// The correct way to dispose of the thread is to drop it, on which the
238/// sub-process will be killed, and the thread will be joined.
239struct WatchThread {
240 handle: Option<JoinHandle<()>>,
241 message_recv: Receiver<CheckEvent>,
242}
243
244enum CheckEvent { 271enum CheckEvent {
245 Begin, 272 Begin,
246 Msg(cargo_metadata::Message), 273 Msg(cargo_metadata::Message),
@@ -257,7 +284,7 @@ impl fmt::Display for CargoError {
257} 284}
258impl error::Error for CargoError {} 285impl error::Error for CargoError {}
259 286
260pub fn run_cargo( 287fn run_cargo(
261 args: &[String], 288 args: &[String],
262 current_dir: Option<&Path>, 289 current_dir: Option<&Path>,
263 on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool, 290 on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool,
@@ -330,76 +357,3 @@ pub fn run_cargo(
330 357
331 Err(CargoError(err_msg)) 358 Err(CargoError(err_msg))
332} 359}
333
334impl WatchThread {
335 fn dummy() -> WatchThread {
336 WatchThread { handle: None, message_recv: never() }
337 }
338
339 fn new(options: &CheckOptions, workspace_root: &Path) -> WatchThread {
340 let mut args: Vec<String> = vec![
341 options.command.clone(),
342 "--workspace".to_string(),
343 "--message-format=json".to_string(),
344 "--manifest-path".to_string(),
345 format!("{}/Cargo.toml", workspace_root.display()),
346 ];
347 if options.all_targets {
348 args.push("--all-targets".to_string());
349 }
350 args.extend(options.args.iter().cloned());
351
352 let (message_send, message_recv) = unbounded();
353 let workspace_root = workspace_root.to_owned();
354 let handle = if options.enable {
355 Some(std::thread::spawn(move || {
356 // If we trigger an error here, we will do so in the loop instead,
357 // which will break out of the loop, and continue the shutdown
358 let _ = message_send.send(CheckEvent::Begin);
359
360 let res = run_cargo(&args, Some(&workspace_root), &mut |message| {
361 // Skip certain kinds of messages to only spend time on what's useful
362 match &message {
363 Message::CompilerArtifact(artifact) if artifact.fresh => return true,
364 Message::BuildScriptExecuted(_) => return true,
365 Message::Unknown => return true,
366 _ => {}
367 }
368
369 // if the send channel was closed, we want to shutdown
370 message_send.send(CheckEvent::Msg(message)).is_ok()
371 });
372
373 if let Err(err) = res {
374 // FIXME: make the `message_send` to be `Sender<Result<CheckEvent, CargoError>>`
375 // to display user-caused misconfiguration errors instead of just logging them here
376 log::error!("Cargo watcher failed {:?}", err);
377 }
378
379 // We can ignore any error here, as we are already in the progress
380 // of shutting down.
381 let _ = message_send.send(CheckEvent::End);
382 }))
383 } else {
384 None
385 };
386 WatchThread { handle, message_recv }
387 }
388}
389
390impl std::ops::Drop for WatchThread {
391 fn drop(&mut self) {
392 if let Some(handle) = self.handle.take() {
393 // Replace our reciever with dummy one, so we can drop and close the
394 // one actually communicating with the thread
395 let recv = std::mem::replace(&mut self.message_recv, never());
396
397 // Dropping the original reciever initiates thread sub-process shutdown
398 drop(recv);
399
400 // Join the thread, it should finish shortly. We don't really care
401 // whether it panicked, so it is safe to ignore the result
402 let _ = handle.join();
403 }
404 }
405}
diff --git a/crates/ra_hir_expand/src/proc_macro.rs b/crates/ra_hir_expand/src/proc_macro.rs
index 4d270e0de..97d1208ec 100644
--- a/crates/ra_hir_expand/src/proc_macro.rs
+++ b/crates/ra_hir_expand/src/proc_macro.rs
@@ -9,6 +9,15 @@ pub struct ProcMacroExpander {
9 proc_macro_id: ProcMacroId, 9 proc_macro_id: ProcMacroId,
10} 10}
11 11
12macro_rules! err {
13 ($fmt:literal, $($tt:tt),*) => {
14 mbe::ExpandError::ProcMacroError(tt::ExpansionError::Unknown(format!($fmt, $($tt),*)))
15 };
16 ($fmt:literal) => {
17 mbe::ExpandError::ProcMacroError(tt::ExpansionError::Unknown($fmt.to_string()))
18 }
19}
20
12impl ProcMacroExpander { 21impl ProcMacroExpander {
13 pub fn new(krate: CrateId, proc_macro_id: ProcMacroId) -> ProcMacroExpander { 22 pub fn new(krate: CrateId, proc_macro_id: ProcMacroId) -> ProcMacroExpander {
14 ProcMacroExpander { krate, proc_macro_id } 23 ProcMacroExpander { krate, proc_macro_id }
@@ -25,8 +34,24 @@ impl ProcMacroExpander {
25 .proc_macro 34 .proc_macro
26 .get(self.proc_macro_id.0 as usize) 35 .get(self.proc_macro_id.0 as usize)
27 .clone() 36 .clone()
28 .ok_or_else(|| mbe::ExpandError::ConversionError)?; 37 .ok_or_else(|| err!("No derive macro found."))?;
38
39 let tt = remove_derive_atr(tt, &proc_macro.name)
40 .ok_or_else(|| err!("Fail to remove derive for custom derive"))?;
29 41
30 proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from) 42 proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from)
31 } 43 }
32} 44}
45
46fn remove_derive_atr(tt: &tt::Subtree, _name: &str) -> Option<tt::Subtree> {
47 // FIXME: proper handle the remove derive
48 // We assume the first 2 tokens are #[derive(name)]
49 if tt.token_trees.len() > 2 {
50 let mut tt = tt.clone();
51 tt.token_trees.remove(0);
52 tt.token_trees.remove(0);
53 return Some(tt);
54 }
55
56 None
57}
diff --git a/crates/ra_hir_ty/src/display.rs b/crates/ra_hir_ty/src/display.rs
index a6ef44a31..13ecd537a 100644
--- a/crates/ra_hir_ty/src/display.rs
+++ b/crates/ra_hir_ty/src/display.rs
@@ -159,20 +159,13 @@ impl HirDisplay for ApplicationTy {
159 } 159 }
160 TypeCtor::FnDef(def) => { 160 TypeCtor::FnDef(def) => {
161 let sig = f.db.callable_item_signature(def).subst(&self.parameters); 161 let sig = f.db.callable_item_signature(def).subst(&self.parameters);
162 let name = match def { 162 match def {
163 CallableDef::FunctionId(ff) => f.db.function_data(ff).name.clone(), 163 CallableDef::FunctionId(ff) => write!(f, "fn {}", f.db.function_data(ff).name)?,
164 CallableDef::StructId(s) => f.db.struct_data(s).name.clone(), 164 CallableDef::StructId(s) => write!(f, "{}", f.db.struct_data(s).name)?,
165 CallableDef::EnumVariantId(e) => { 165 CallableDef::EnumVariantId(e) => {
166 let enum_data = f.db.enum_data(e.parent); 166 write!(f, "{}", f.db.enum_data(e.parent).variants[e.local_id].name)?
167 enum_data.variants[e.local_id].name.clone()
168 } 167 }
169 }; 168 };
170 match def {
171 CallableDef::FunctionId(_) => write!(f, "fn {}", name)?,
172 CallableDef::StructId(_) | CallableDef::EnumVariantId(_) => {
173 write!(f, "{}", name)?
174 }
175 }
176 if self.parameters.len() > 0 { 169 if self.parameters.len() > 0 {
177 let generics = generics(f.db.upcast(), def.into()); 170 let generics = generics(f.db.upcast(), def.into());
178 let (parent_params, self_param, type_params, _impl_trait_params) = 171 let (parent_params, self_param, type_params, _impl_trait_params) =
@@ -197,8 +190,6 @@ impl HirDisplay for ApplicationTy {
197 }; 190 };
198 write!(f, "{}", name)?; 191 write!(f, "{}", name)?;
199 if self.parameters.len() > 0 { 192 if self.parameters.len() > 0 {
200 write!(f, "<")?;
201
202 let mut non_default_parameters = Vec::with_capacity(self.parameters.len()); 193 let mut non_default_parameters = Vec::with_capacity(self.parameters.len());
203 let parameters_to_write = if f.omit_verbose_types() { 194 let parameters_to_write = if f.omit_verbose_types() {
204 match self 195 match self
@@ -207,8 +198,8 @@ impl HirDisplay for ApplicationTy {
207 .map(|generic_def_id| f.db.generic_defaults(generic_def_id)) 198 .map(|generic_def_id| f.db.generic_defaults(generic_def_id))
208 .filter(|defaults| !defaults.is_empty()) 199 .filter(|defaults| !defaults.is_empty())
209 { 200 {
210 Option::None => self.parameters.0.as_ref(), 201 None => self.parameters.0.as_ref(),
211 Option::Some(default_parameters) => { 202 Some(default_parameters) => {
212 for (i, parameter) in self.parameters.iter().enumerate() { 203 for (i, parameter) in self.parameters.iter().enumerate() {
213 match (parameter, default_parameters.get(i)) { 204 match (parameter, default_parameters.get(i)) {
214 (&Ty::Unknown, _) | (_, None) => { 205 (&Ty::Unknown, _) | (_, None) => {
@@ -228,7 +219,7 @@ impl HirDisplay for ApplicationTy {
228 } else { 219 } else {
229 self.parameters.0.as_ref() 220 self.parameters.0.as_ref()
230 }; 221 };
231 222 write!(f, "<")?;
232 f.write_joined(parameters_to_write, ", ")?; 223 f.write_joined(parameters_to_write, ", ")?;
233 write!(f, ">")?; 224 write!(f, ">")?;
234 } 225 }
@@ -238,9 +229,9 @@ impl HirDisplay for ApplicationTy {
238 AssocContainerId::TraitId(it) => it, 229 AssocContainerId::TraitId(it) => it,
239 _ => panic!("not an associated type"), 230 _ => panic!("not an associated type"),
240 }; 231 };
241 let trait_name = f.db.trait_data(trait_).name.clone(); 232 let trait_ = f.db.trait_data(trait_);
242 let name = f.db.type_alias_data(type_alias).name.clone(); 233 let type_alias = f.db.type_alias_data(type_alias);
243 write!(f, "{}::{}", trait_name, name)?; 234 write!(f, "{}::{}", trait_.name, type_alias.name)?;
244 if self.parameters.len() > 0 { 235 if self.parameters.len() > 0 {
245 write!(f, "<")?; 236 write!(f, "<")?;
246 f.write_joined(&*self.parameters.0, ", ")?; 237 f.write_joined(&*self.parameters.0, ", ")?;
@@ -273,8 +264,8 @@ impl HirDisplay for ProjectionTy {
273 return write!(f, "{}", TYPE_HINT_TRUNCATION); 264 return write!(f, "{}", TYPE_HINT_TRUNCATION);
274 } 265 }
275 266
276 let trait_name = f.db.trait_data(self.trait_(f.db)).name.clone(); 267 let trait_ = f.db.trait_data(self.trait_(f.db));
277 write!(f, "<{} as {}", self.parameters[0].display(f.db), trait_name,)?; 268 write!(f, "<{} as {}", self.parameters[0].display(f.db), trait_.name)?;
278 if self.parameters.len() > 1 { 269 if self.parameters.len() > 1 {
279 write!(f, "<")?; 270 write!(f, "<")?;
280 f.write_joined(&self.parameters[1..], ", ")?; 271 f.write_joined(&self.parameters[1..], ", ")?;
@@ -319,7 +310,7 @@ impl HirDisplay for Ty {
319 Ty::Opaque(_) => write!(f, "impl ")?, 310 Ty::Opaque(_) => write!(f, "impl ")?,
320 _ => unreachable!(), 311 _ => unreachable!(),
321 }; 312 };
322 write_bounds_like_dyn_trait(&predicates, f)?; 313 write_bounds_like_dyn_trait(predicates, f)?;
323 } 314 }
324 Ty::Unknown => write!(f, "{{unknown}}")?, 315 Ty::Unknown => write!(f, "{{unknown}}")?,
325 Ty::Infer(..) => write!(f, "_")?, 316 Ty::Infer(..) => write!(f, "_")?,
@@ -352,7 +343,7 @@ fn write_bounds_like_dyn_trait(
352 // We assume that the self type is $0 (i.e. the 343 // We assume that the self type is $0 (i.e. the
353 // existential) here, which is the only thing that's 344 // existential) here, which is the only thing that's
354 // possible in actual Rust, and hence don't print it 345 // possible in actual Rust, and hence don't print it
355 write!(f, "{}", f.db.trait_data(trait_ref.trait_).name.clone())?; 346 write!(f, "{}", f.db.trait_data(trait_ref.trait_).name)?;
356 if trait_ref.substs.len() > 1 { 347 if trait_ref.substs.len() > 1 {
357 write!(f, "<")?; 348 write!(f, "<")?;
358 f.write_joined(&trait_ref.substs[1..], ", ")?; 349 f.write_joined(&trait_ref.substs[1..], ", ")?;
@@ -369,9 +360,8 @@ fn write_bounds_like_dyn_trait(
369 write!(f, "<")?; 360 write!(f, "<")?;
370 angle_open = true; 361 angle_open = true;
371 } 362 }
372 let name = 363 let type_alias = f.db.type_alias_data(projection_pred.projection_ty.associated_ty);
373 f.db.type_alias_data(projection_pred.projection_ty.associated_ty).name.clone(); 364 write!(f, "{} = ", type_alias.name)?;
374 write!(f, "{} = ", name)?;
375 projection_pred.ty.hir_fmt(f)?; 365 projection_pred.ty.hir_fmt(f)?;
376 } 366 }
377 GenericPredicate::Error => { 367 GenericPredicate::Error => {
@@ -405,7 +395,7 @@ impl TraitRef {
405 } else { 395 } else {
406 write!(f, ": ")?; 396 write!(f, ": ")?;
407 } 397 }
408 write!(f, "{}", f.db.trait_data(self.trait_).name.clone())?; 398 write!(f, "{}", f.db.trait_data(self.trait_).name)?;
409 if self.substs.len() > 1 { 399 if self.substs.len() > 1 {
410 write!(f, "<")?; 400 write!(f, "<")?;
411 f.write_joined(&self.substs[1..], ", ")?; 401 f.write_joined(&self.substs[1..], ", ")?;
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index cd0757be5..b683572fb 100644
--- a/crates/ra_ide/src/completion.rs
+++ b/crates/ra_ide/src/completion.rs
@@ -34,15 +34,15 @@ pub use crate::completion::completion_item::{
34}; 34};
35 35
36#[derive(Clone, Debug, PartialEq, Eq)] 36#[derive(Clone, Debug, PartialEq, Eq)]
37pub struct CompletionOptions { 37pub struct CompletionConfig {
38 pub enable_postfix_completions: bool, 38 pub enable_postfix_completions: bool,
39 pub add_call_parenthesis: bool, 39 pub add_call_parenthesis: bool,
40 pub add_call_argument_snippets: bool, 40 pub add_call_argument_snippets: bool,
41} 41}
42 42
43impl Default for CompletionOptions { 43impl Default for CompletionConfig {
44 fn default() -> Self { 44 fn default() -> Self {
45 CompletionOptions { 45 CompletionConfig {
46 enable_postfix_completions: true, 46 enable_postfix_completions: true,
47 add_call_parenthesis: true, 47 add_call_parenthesis: true,
48 add_call_argument_snippets: true, 48 add_call_argument_snippets: true,
@@ -75,9 +75,9 @@ impl Default for CompletionOptions {
75pub(crate) fn completions( 75pub(crate) fn completions(
76 db: &RootDatabase, 76 db: &RootDatabase,
77 position: FilePosition, 77 position: FilePosition,
78 options: &CompletionOptions, 78 config: &CompletionConfig,
79) -> Option<Completions> { 79) -> Option<Completions> {
80 let ctx = CompletionContext::new(db, position, options)?; 80 let ctx = CompletionContext::new(db, position, config)?;
81 81
82 let mut acc = Completions::default(); 82 let mut acc = Completions::default();
83 83
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs
index 0a00054b2..29c2881c6 100644
--- a/crates/ra_ide/src/completion/complete_postfix.rs
+++ b/crates/ra_ide/src/completion/complete_postfix.rs
@@ -15,7 +15,7 @@ use crate::{
15}; 15};
16 16
17pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { 17pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
18 if !ctx.options.enable_postfix_completions { 18 if !ctx.config.enable_postfix_completions {
19 return; 19 return;
20 } 20 }
21 21
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index 319e33b61..fdc0da2c5 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -11,7 +11,7 @@ use ra_syntax::{
11}; 11};
12use ra_text_edit::AtomTextEdit; 12use ra_text_edit::AtomTextEdit;
13 13
14use crate::{completion::CompletionOptions, FilePosition}; 14use crate::{completion::CompletionConfig, FilePosition};
15 15
16/// `CompletionContext` is created early during completion to figure out, where 16/// `CompletionContext` is created early during completion to figure out, where
17/// exactly is the cursor, syntax-wise. 17/// exactly is the cursor, syntax-wise.
@@ -19,7 +19,7 @@ use crate::{completion::CompletionOptions, FilePosition};
19pub(crate) struct CompletionContext<'a> { 19pub(crate) struct CompletionContext<'a> {
20 pub(super) sema: Semantics<'a, RootDatabase>, 20 pub(super) sema: Semantics<'a, RootDatabase>,
21 pub(super) db: &'a RootDatabase, 21 pub(super) db: &'a RootDatabase,
22 pub(super) options: &'a CompletionOptions, 22 pub(super) config: &'a CompletionConfig,
23 pub(super) offset: TextUnit, 23 pub(super) offset: TextUnit,
24 /// The token before the cursor, in the original file. 24 /// The token before the cursor, in the original file.
25 pub(super) original_token: SyntaxToken, 25 pub(super) original_token: SyntaxToken,
@@ -61,7 +61,7 @@ impl<'a> CompletionContext<'a> {
61 pub(super) fn new( 61 pub(super) fn new(
62 db: &'a RootDatabase, 62 db: &'a RootDatabase,
63 position: FilePosition, 63 position: FilePosition,
64 options: &'a CompletionOptions, 64 config: &'a CompletionConfig,
65 ) -> Option<CompletionContext<'a>> { 65 ) -> Option<CompletionContext<'a>> {
66 let sema = Semantics::new(db); 66 let sema = Semantics::new(db);
67 67
@@ -85,7 +85,7 @@ impl<'a> CompletionContext<'a> {
85 let mut ctx = CompletionContext { 85 let mut ctx = CompletionContext {
86 sema, 86 sema,
87 db, 87 db,
88 options, 88 config,
89 original_token, 89 original_token,
90 token, 90 token,
91 offset: position.offset, 91 offset: position.offset,
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 60f1b83f3..1c7c0924d 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -106,7 +106,7 @@ impl Completions {
106 }; 106 };
107 107
108 // Add `<>` for generic types 108 // Add `<>` for generic types
109 if ctx.is_path_type && !ctx.has_type_args && ctx.options.add_call_parenthesis { 109 if ctx.is_path_type && !ctx.has_type_args && ctx.config.add_call_parenthesis {
110 let has_non_default_type_params = match resolution { 110 let has_non_default_type_params = match resolution {
111 ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db), 111 ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db),
112 ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db), 112 ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db),
@@ -211,14 +211,14 @@ impl Completions {
211 .detail(function_signature.to_string()); 211 .detail(function_signature.to_string());
212 212
213 // If not an import, add parenthesis automatically. 213 // If not an import, add parenthesis automatically.
214 if ctx.use_item_syntax.is_none() && !ctx.is_call && ctx.options.add_call_parenthesis { 214 if ctx.use_item_syntax.is_none() && !ctx.is_call && ctx.config.add_call_parenthesis {
215 tested_by!(inserts_parens_for_function_calls); 215 tested_by!(inserts_parens_for_function_calls);
216 216
217 let (snippet, label) = if params.is_empty() || has_self_param && params.len() == 1 { 217 let (snippet, label) = if params.is_empty() || has_self_param && params.len() == 1 {
218 (format!("{}()$0", name), format!("{}()", name)) 218 (format!("{}()$0", name), format!("{}()", name))
219 } else { 219 } else {
220 builder = builder.trigger_call_info(); 220 builder = builder.trigger_call_info();
221 let snippet = if ctx.options.add_call_argument_snippets { 221 let snippet = if ctx.config.add_call_argument_snippets {
222 let to_skip = if has_self_param { 1 } else { 0 }; 222 let to_skip = if has_self_param { 1 } else { 0 };
223 let function_params_snippet = function_signature 223 let function_params_snippet = function_signature
224 .parameter_names 224 .parameter_names
@@ -311,7 +311,7 @@ mod tests {
311 311
312 use crate::completion::{ 312 use crate::completion::{
313 test_utils::{do_completion, do_completion_with_options}, 313 test_utils::{do_completion, do_completion_with_options},
314 CompletionItem, CompletionKind, CompletionOptions, 314 CompletionConfig, CompletionItem, CompletionKind,
315 }; 315 };
316 316
317 fn do_reference_completion(ra_fixture: &str) -> Vec<CompletionItem> { 317 fn do_reference_completion(ra_fixture: &str) -> Vec<CompletionItem> {
@@ -320,7 +320,7 @@ mod tests {
320 320
321 fn do_reference_completion_with_options( 321 fn do_reference_completion_with_options(
322 ra_fixture: &str, 322 ra_fixture: &str,
323 options: CompletionOptions, 323 options: CompletionConfig,
324 ) -> Vec<CompletionItem> { 324 ) -> Vec<CompletionItem> {
325 do_completion_with_options(ra_fixture, CompletionKind::Reference, &options) 325 do_completion_with_options(ra_fixture, CompletionKind::Reference, &options)
326 } 326 }
@@ -589,7 +589,7 @@ mod tests {
589 s.f<|> 589 s.f<|>
590 } 590 }
591 ", 591 ",
592 CompletionOptions { 592 CompletionConfig {
593 add_call_argument_snippets: false, 593 add_call_argument_snippets: false,
594 .. Default::default() 594 .. Default::default()
595 } 595 }
diff --git a/crates/ra_ide/src/completion/test_utils.rs b/crates/ra_ide/src/completion/test_utils.rs
index 136857315..eb90b5279 100644
--- a/crates/ra_ide/src/completion/test_utils.rs
+++ b/crates/ra_ide/src/completion/test_utils.rs
@@ -1,19 +1,19 @@
1//! Runs completion for testing purposes. 1//! Runs completion for testing purposes.
2 2
3use crate::{ 3use crate::{
4 completion::{completion_item::CompletionKind, CompletionOptions}, 4 completion::{completion_item::CompletionKind, CompletionConfig},
5 mock_analysis::{analysis_and_position, single_file_with_position}, 5 mock_analysis::{analysis_and_position, single_file_with_position},
6 CompletionItem, 6 CompletionItem,
7}; 7};
8 8
9pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> { 9pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> {
10 do_completion_with_options(code, kind, &CompletionOptions::default()) 10 do_completion_with_options(code, kind, &CompletionConfig::default())
11} 11}
12 12
13pub(crate) fn do_completion_with_options( 13pub(crate) fn do_completion_with_options(
14 code: &str, 14 code: &str,
15 kind: CompletionKind, 15 kind: CompletionKind,
16 options: &CompletionOptions, 16 options: &CompletionConfig,
17) -> Vec<CompletionItem> { 17) -> Vec<CompletionItem> {
18 let (analysis, position) = if code.contains("//-") { 18 let (analysis, position) = if code.contains("//-") {
19 analysis_and_position(code) 19 analysis_and_position(code)
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index f4f0751c0..d06fc03d3 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -11,14 +11,14 @@ use ra_syntax::{
11use crate::{FileId, FunctionSignature}; 11use crate::{FileId, FunctionSignature};
12 12
13#[derive(Clone, Debug, PartialEq, Eq)] 13#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct InlayHintsOptions { 14pub struct InlayHintsConfig {
15 pub type_hints: bool, 15 pub type_hints: bool,
16 pub parameter_hints: bool, 16 pub parameter_hints: bool,
17 pub chaining_hints: bool, 17 pub chaining_hints: bool,
18 pub max_length: Option<usize>, 18 pub max_length: Option<usize>,
19} 19}
20 20
21impl Default for InlayHintsOptions { 21impl Default for InlayHintsConfig {
22 fn default() -> Self { 22 fn default() -> Self {
23 Self { type_hints: true, parameter_hints: true, chaining_hints: true, max_length: None } 23 Self { type_hints: true, parameter_hints: true, chaining_hints: true, max_length: None }
24 } 24 }
@@ -41,7 +41,7 @@ pub struct InlayHint {
41pub(crate) fn inlay_hints( 41pub(crate) fn inlay_hints(
42 db: &RootDatabase, 42 db: &RootDatabase,
43 file_id: FileId, 43 file_id: FileId,
44 options: &InlayHintsOptions, 44 config: &InlayHintsConfig,
45) -> Vec<InlayHint> { 45) -> Vec<InlayHint> {
46 let _p = profile("inlay_hints"); 46 let _p = profile("inlay_hints");
47 let sema = Semantics::new(db); 47 let sema = Semantics::new(db);
@@ -50,14 +50,14 @@ pub(crate) fn inlay_hints(
50 let mut res = Vec::new(); 50 let mut res = Vec::new();
51 for node in file.syntax().descendants() { 51 for node in file.syntax().descendants() {
52 if let Some(expr) = ast::Expr::cast(node.clone()) { 52 if let Some(expr) = ast::Expr::cast(node.clone()) {
53 get_chaining_hints(&mut res, &sema, options, expr); 53 get_chaining_hints(&mut res, &sema, config, expr);
54 } 54 }
55 55
56 match_ast! { 56 match_ast! {
57 match node { 57 match node {
58 ast::CallExpr(it) => { get_param_name_hints(&mut res, &sema, options, ast::Expr::from(it)); }, 58 ast::CallExpr(it) => { get_param_name_hints(&mut res, &sema, config, ast::Expr::from(it)); },
59 ast::MethodCallExpr(it) => { get_param_name_hints(&mut res, &sema, options, ast::Expr::from(it)); }, 59 ast::MethodCallExpr(it) => { get_param_name_hints(&mut res, &sema, config, ast::Expr::from(it)); },
60 ast::BindPat(it) => { get_bind_pat_hints(&mut res, &sema, options, it); }, 60 ast::BindPat(it) => { get_bind_pat_hints(&mut res, &sema, config, it); },
61 _ => (), 61 _ => (),
62 } 62 }
63 } 63 }
@@ -68,10 +68,10 @@ pub(crate) fn inlay_hints(
68fn get_chaining_hints( 68fn get_chaining_hints(
69 acc: &mut Vec<InlayHint>, 69 acc: &mut Vec<InlayHint>,
70 sema: &Semantics<RootDatabase>, 70 sema: &Semantics<RootDatabase>,
71 options: &InlayHintsOptions, 71 config: &InlayHintsConfig,
72 expr: ast::Expr, 72 expr: ast::Expr,
73) -> Option<()> { 73) -> Option<()> {
74 if !options.chaining_hints { 74 if !config.chaining_hints {
75 return None; 75 return None;
76 } 76 }
77 77
@@ -95,7 +95,7 @@ fn get_chaining_hints(
95 let next = tokens.next()?.kind(); 95 let next = tokens.next()?.kind();
96 let next_next = tokens.next()?.kind(); 96 let next_next = tokens.next()?.kind();
97 if next == SyntaxKind::WHITESPACE && next_next == SyntaxKind::DOT { 97 if next == SyntaxKind::WHITESPACE && next_next == SyntaxKind::DOT {
98 let label = ty.display_truncated(sema.db, options.max_length).to_string(); 98 let label = ty.display_truncated(sema.db, config.max_length).to_string();
99 acc.push(InlayHint { 99 acc.push(InlayHint {
100 range: expr.syntax().text_range(), 100 range: expr.syntax().text_range(),
101 kind: InlayKind::ChainingHint, 101 kind: InlayKind::ChainingHint,
@@ -108,10 +108,10 @@ fn get_chaining_hints(
108fn get_param_name_hints( 108fn get_param_name_hints(
109 acc: &mut Vec<InlayHint>, 109 acc: &mut Vec<InlayHint>,
110 sema: &Semantics<RootDatabase>, 110 sema: &Semantics<RootDatabase>,
111 options: &InlayHintsOptions, 111 config: &InlayHintsConfig,
112 expr: ast::Expr, 112 expr: ast::Expr,
113) -> Option<()> { 113) -> Option<()> {
114 if !options.parameter_hints { 114 if !config.parameter_hints {
115 return None; 115 return None;
116 } 116 }
117 117
@@ -148,10 +148,10 @@ fn get_param_name_hints(
148fn get_bind_pat_hints( 148fn get_bind_pat_hints(
149 acc: &mut Vec<InlayHint>, 149 acc: &mut Vec<InlayHint>,
150 sema: &Semantics<RootDatabase>, 150 sema: &Semantics<RootDatabase>,
151 options: &InlayHintsOptions, 151 config: &InlayHintsConfig,
152 pat: ast::BindPat, 152 pat: ast::BindPat,
153) -> Option<()> { 153) -> Option<()> {
154 if !options.type_hints { 154 if !config.type_hints {
155 return None; 155 return None;
156 } 156 }
157 157
@@ -164,7 +164,7 @@ fn get_bind_pat_hints(
164 acc.push(InlayHint { 164 acc.push(InlayHint {
165 range: pat.syntax().text_range(), 165 range: pat.syntax().text_range(),
166 kind: InlayKind::TypeHint, 166 kind: InlayKind::TypeHint,
167 label: ty.display_truncated(sema.db, options.max_length).to_string().into(), 167 label: ty.display_truncated(sema.db, config.max_length).to_string().into(),
168 }); 168 });
169 Some(()) 169 Some(())
170} 170}
@@ -270,7 +270,7 @@ fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<
270 270
271#[cfg(test)] 271#[cfg(test)]
272mod tests { 272mod tests {
273 use crate::inlay_hints::InlayHintsOptions; 273 use crate::inlay_hints::InlayHintsConfig;
274 use insta::assert_debug_snapshot; 274 use insta::assert_debug_snapshot;
275 275
276 use crate::mock_analysis::single_file; 276 use crate::mock_analysis::single_file;
@@ -284,7 +284,7 @@ mod tests {
284 let _x = foo(4, 4); 284 let _x = foo(4, 4);
285 }"#, 285 }"#,
286 ); 286 );
287 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: true, type_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###" 287 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: true, type_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"
288 [ 288 [
289 InlayHint { 289 InlayHint {
290 range: [106; 107), 290 range: [106; 107),
@@ -308,7 +308,7 @@ mod tests {
308 let _x = foo(4, 4); 308 let _x = foo(4, 4);
309 }"#, 309 }"#,
310 ); 310 );
311 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: false, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"[]"###); 311 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ type_hints: false, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"[]"###);
312 } 312 }
313 313
314 #[test] 314 #[test]
@@ -320,7 +320,7 @@ mod tests {
320 let _x = foo(4, 4); 320 let _x = foo(4, 4);
321 }"#, 321 }"#,
322 ); 322 );
323 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: true, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###" 323 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ type_hints: true, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"
324 [ 324 [
325 InlayHint { 325 InlayHint {
326 range: [97; 99), 326 range: [97; 99),
@@ -344,7 +344,7 @@ fn main() {
344}"#, 344}"#,
345 ); 345 );
346 346
347 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 347 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
348 [ 348 [
349 InlayHint { 349 InlayHint {
350 range: [69; 71), 350 range: [69; 71),
@@ -401,7 +401,7 @@ fn main() {
401}"#, 401}"#,
402 ); 402 );
403 403
404 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 404 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
405 [ 405 [
406 InlayHint { 406 InlayHint {
407 range: [193; 197), 407 range: [193; 197),
@@ -481,7 +481,7 @@ fn main() {
481}"#, 481}"#,
482 ); 482 );
483 483
484 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 484 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
485 [ 485 [
486 InlayHint { 486 InlayHint {
487 range: [21; 30), 487 range: [21; 30),
@@ -545,7 +545,7 @@ fn main() {
545}"#, 545}"#,
546 ); 546 );
547 547
548 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 548 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
549 [ 549 [
550 InlayHint { 550 InlayHint {
551 range: [21; 30), 551 range: [21; 30),
@@ -595,7 +595,7 @@ fn main() {
595}"#, 595}"#,
596 ); 596 );
597 597
598 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 598 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
599 [ 599 [
600 InlayHint { 600 InlayHint {
601 range: [188; 192), 601 range: [188; 192),
@@ -690,7 +690,7 @@ fn main() {
690}"#, 690}"#,
691 ); 691 );
692 692
693 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 693 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
694 [ 694 [
695 InlayHint { 695 InlayHint {
696 range: [188; 192), 696 range: [188; 192),
@@ -785,7 +785,7 @@ fn main() {
785}"#, 785}"#,
786 ); 786 );
787 787
788 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 788 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
789 [ 789 [
790 InlayHint { 790 InlayHint {
791 range: [252; 256), 791 range: [252; 256),
@@ -857,7 +857,7 @@ fn main() {
857}"#, 857}"#,
858 ); 858 );
859 859
860 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions { max_length: Some(8), ..Default::default() }).unwrap(), @r###" 860 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
861 [ 861 [
862 InlayHint { 862 InlayHint {
863 range: [74; 75), 863 range: [74; 75),
@@ -945,7 +945,7 @@ fn main() {
945}"#, 945}"#,
946 ); 946 );
947 947
948 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 948 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
949 [ 949 [
950 InlayHint { 950 InlayHint {
951 range: [798; 809), 951 range: [798; 809),
@@ -1067,7 +1067,7 @@ fn main() {
1067}"#, 1067}"#,
1068 ); 1068 );
1069 1069
1070 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions { max_length: Some(8), ..Default::default() }).unwrap(), @r###" 1070 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
1071 [] 1071 []
1072 "### 1072 "###
1073 ); 1073 );
@@ -1093,7 +1093,7 @@ fn main() {
1093}"#, 1093}"#,
1094 ); 1094 );
1095 1095
1096 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions { max_length: Some(8), ..Default::default() }).unwrap(), @r###" 1096 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
1097 [] 1097 []
1098 "### 1098 "###
1099 ); 1099 );
@@ -1115,7 +1115,7 @@ fn main() {
1115 .into_c(); 1115 .into_c();
1116 }"#, 1116 }"#,
1117 ); 1117 );
1118 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" 1118 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1119 [ 1119 [
1120 InlayHint { 1120 InlayHint {
1121 range: [232; 269), 1121 range: [232; 269),
@@ -1144,7 +1144,7 @@ fn main() {
1144 let c = A(B(C)).into_b().into_c(); 1144 let c = A(B(C)).into_b().into_c();
1145 }"#, 1145 }"#,
1146 ); 1146 );
1147 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"[]"###); 1147 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"[]"###);
1148 } 1148 }
1149 1149
1150 #[test] 1150 #[test]
@@ -1162,7 +1162,7 @@ fn main() {
1162 .0; 1162 .0;
1163 }"#, 1163 }"#,
1164 ); 1164 );
1165 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" 1165 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1166 [ 1166 [
1167 InlayHint { 1167 InlayHint {
1168 range: [150; 221), 1168 range: [150; 221),
@@ -1204,7 +1204,7 @@ fn main() {
1204 .into_c(); 1204 .into_c();
1205 }"#, 1205 }"#,
1206 ); 1206 );
1207 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" 1207 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1208 [ 1208 [
1209 InlayHint { 1209 InlayHint {
1210 range: [403; 452), 1210 range: [403; 452),
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index e43414985..285381086 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -62,13 +62,13 @@ use crate::display::ToNav;
62pub use crate::{ 62pub use crate::{
63 assists::{Assist, AssistId}, 63 assists::{Assist, AssistId},
64 call_hierarchy::CallItem, 64 call_hierarchy::CallItem,
65 completion::{CompletionItem, CompletionItemKind, CompletionOptions, InsertTextFormat}, 65 completion::{CompletionConfig, CompletionItem, CompletionItemKind, InsertTextFormat},
66 diagnostics::Severity, 66 diagnostics::Severity,
67 display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, 67 display::{file_structure, FunctionSignature, NavigationTarget, StructureNode},
68 expand_macro::ExpandedMacro, 68 expand_macro::ExpandedMacro,
69 folding_ranges::{Fold, FoldKind}, 69 folding_ranges::{Fold, FoldKind},
70 hover::HoverResult, 70 hover::HoverResult,
71 inlay_hints::{InlayHint, InlayHintsOptions, InlayKind}, 71 inlay_hints::{InlayHint, InlayHintsConfig, InlayKind},
72 references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult}, 72 references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult},
73 runnables::{Runnable, RunnableKind, TestId}, 73 runnables::{Runnable, RunnableKind, TestId},
74 source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, 74 source_change::{FileSystemEdit, SourceChange, SourceFileEdit},
@@ -138,6 +138,11 @@ impl AnalysisHost {
138 pub fn new(lru_capacity: Option<usize>) -> AnalysisHost { 138 pub fn new(lru_capacity: Option<usize>) -> AnalysisHost {
139 AnalysisHost { db: RootDatabase::new(lru_capacity) } 139 AnalysisHost { db: RootDatabase::new(lru_capacity) }
140 } 140 }
141
142 pub fn update_lru_capacity(&mut self, lru_capacity: Option<usize>) {
143 self.db.update_lru_capacity(lru_capacity);
144 }
145
141 /// Returns a snapshot of the current state, which you can query for 146 /// Returns a snapshot of the current state, which you can query for
142 /// semantic information. 147 /// semantic information.
143 pub fn analysis(&self) -> Analysis { 148 pub fn analysis(&self) -> Analysis {
@@ -320,9 +325,9 @@ impl Analysis {
320 pub fn inlay_hints( 325 pub fn inlay_hints(
321 &self, 326 &self,
322 file_id: FileId, 327 file_id: FileId,
323 inlay_hint_opts: &InlayHintsOptions, 328 config: &InlayHintsConfig,
324 ) -> Cancelable<Vec<InlayHint>> { 329 ) -> Cancelable<Vec<InlayHint>> {
325 self.with_db(|db| inlay_hints::inlay_hints(db, file_id, inlay_hint_opts)) 330 self.with_db(|db| inlay_hints::inlay_hints(db, file_id, config))
326 } 331 }
327 332
328 /// Returns the set of folding ranges. 333 /// Returns the set of folding ranges.
@@ -445,9 +450,9 @@ impl Analysis {
445 pub fn completions( 450 pub fn completions(
446 &self, 451 &self,
447 position: FilePosition, 452 position: FilePosition,
448 options: &CompletionOptions, 453 config: &CompletionConfig,
449 ) -> Cancelable<Option<Vec<CompletionItem>>> { 454 ) -> Cancelable<Option<Vec<CompletionItem>>> {
450 self.with_db(|db| completion::completions(db, position, options).map(Into::into)) 455 self.with_db(|db| completion::completions(db, position, config).map(Into::into))
451 } 456 }
452 457
453 /// Computes assists (aka code actions aka intentions) for the given 458 /// Computes assists (aka code actions aka intentions) for the given
diff --git a/crates/ra_ide_db/src/lib.rs b/crates/ra_ide_db/src/lib.rs
index 4faeefa8d..e6f2d36e9 100644
--- a/crates/ra_ide_db/src/lib.rs
+++ b/crates/ra_ide_db/src/lib.rs
@@ -115,12 +115,16 @@ impl RootDatabase {
115 db.set_crate_graph_with_durability(Default::default(), Durability::HIGH); 115 db.set_crate_graph_with_durability(Default::default(), Durability::HIGH);
116 db.set_local_roots_with_durability(Default::default(), Durability::HIGH); 116 db.set_local_roots_with_durability(Default::default(), Durability::HIGH);
117 db.set_library_roots_with_durability(Default::default(), Durability::HIGH); 117 db.set_library_roots_with_durability(Default::default(), Durability::HIGH);
118 let lru_capacity = lru_capacity.unwrap_or(ra_db::DEFAULT_LRU_CAP); 118 db.update_lru_capacity(lru_capacity);
119 db.query_mut(ra_db::ParseQuery).set_lru_capacity(lru_capacity);
120 db.query_mut(hir::db::ParseMacroQuery).set_lru_capacity(lru_capacity);
121 db.query_mut(hir::db::MacroExpandQuery).set_lru_capacity(lru_capacity);
122 db 119 db
123 } 120 }
121
122 pub fn update_lru_capacity(&mut self, lru_capacity: Option<usize>) {
123 let lru_capacity = lru_capacity.unwrap_or(ra_db::DEFAULT_LRU_CAP);
124 self.query_mut(ra_db::ParseQuery).set_lru_capacity(lru_capacity);
125 self.query_mut(hir::db::ParseMacroQuery).set_lru_capacity(lru_capacity);
126 self.query_mut(hir::db::MacroExpandQuery).set_lru_capacity(lru_capacity);
127 }
124} 128}
125 129
126impl salsa::ParallelDatabase for RootDatabase { 130impl salsa::ParallelDatabase for RootDatabase {
diff --git a/crates/ra_proc_macro/Cargo.toml b/crates/ra_proc_macro/Cargo.toml
index bc2c37296..d009ceb82 100644
--- a/crates/ra_proc_macro/Cargo.toml
+++ b/crates/ra_proc_macro/Cargo.toml
@@ -10,3 +10,8 @@ doctest = false
10 10
11[dependencies] 11[dependencies]
12ra_tt = { path = "../ra_tt" } 12ra_tt = { path = "../ra_tt" }
13serde = { version = "1.0", features = ["derive"] }
14serde_json = "1.0"
15log = "0.4.8"
16crossbeam-channel = "0.4.0"
17jod-thread = "0.1.1"
diff --git a/crates/ra_proc_macro/src/lib.rs b/crates/ra_proc_macro/src/lib.rs
index 5e21dd487..51fbb046a 100644
--- a/crates/ra_proc_macro/src/lib.rs
+++ b/crates/ra_proc_macro/src/lib.rs
@@ -5,55 +5,104 @@
5//! is used to provide basic infrastructure for communication between two 5//! is used to provide basic infrastructure for communication between two
6//! processes: Client (RA itself), Server (the external program) 6//! processes: Client (RA itself), Server (the external program)
7 7
8mod rpc;
9mod process;
10pub mod msg;
11
12use process::{ProcMacroProcessSrv, ProcMacroProcessThread};
8use ra_tt::{SmolStr, Subtree}; 13use ra_tt::{SmolStr, Subtree};
14use rpc::ProcMacroKind;
9use std::{ 15use std::{
10 path::{Path, PathBuf}, 16 path::{Path, PathBuf},
11 sync::Arc, 17 sync::Arc,
12}; 18};
13 19
14#[derive(Debug, Clone, PartialEq, Eq)] 20pub use rpc::{ExpansionResult, ExpansionTask};
21
22#[derive(Debug, Clone)]
15pub struct ProcMacroProcessExpander { 23pub struct ProcMacroProcessExpander {
16 process: Arc<ProcMacroProcessSrv>, 24 process: Arc<ProcMacroProcessSrv>,
25 dylib_path: PathBuf,
17 name: SmolStr, 26 name: SmolStr,
18} 27}
19 28
29impl Eq for ProcMacroProcessExpander {}
30impl PartialEq for ProcMacroProcessExpander {
31 fn eq(&self, other: &Self) -> bool {
32 self.name == other.name
33 && self.dylib_path == other.dylib_path
34 && Arc::ptr_eq(&self.process, &other.process)
35 }
36}
37
20impl ra_tt::TokenExpander for ProcMacroProcessExpander { 38impl ra_tt::TokenExpander for ProcMacroProcessExpander {
21 fn expand( 39 fn expand(
22 &self, 40 &self,
23 _subtree: &Subtree, 41 subtree: &Subtree,
24 _attr: Option<&Subtree>, 42 _attr: Option<&Subtree>,
25 ) -> Result<Subtree, ra_tt::ExpansionError> { 43 ) -> Result<Subtree, ra_tt::ExpansionError> {
26 // FIXME: do nothing for now 44 self.process.custom_derive(&self.dylib_path, subtree, &self.name)
27 Ok(Subtree::default())
28 } 45 }
29} 46}
30 47
31#[derive(Debug, Clone, PartialEq, Eq)] 48#[derive(Debug)]
32pub struct ProcMacroProcessSrv { 49enum ProcMacroClientKind {
33 path: PathBuf, 50 Process { process: Arc<ProcMacroProcessSrv>, thread: ProcMacroProcessThread },
51 Dummy,
34} 52}
35 53
36#[derive(Debug, Clone, PartialEq, Eq)] 54#[derive(Debug)]
37pub enum ProcMacroClient { 55pub struct ProcMacroClient {
38 Process { process: Arc<ProcMacroProcessSrv> }, 56 kind: ProcMacroClientKind,
39 Dummy,
40} 57}
41 58
42impl ProcMacroClient { 59impl ProcMacroClient {
43 pub fn extern_process(process_path: &Path) -> ProcMacroClient { 60 pub fn extern_process(process_path: &Path) -> Result<ProcMacroClient, std::io::Error> {
44 let process = ProcMacroProcessSrv { path: process_path.into() }; 61 let (thread, process) = ProcMacroProcessSrv::run(process_path)?;
45 ProcMacroClient::Process { process: Arc::new(process) } 62 Ok(ProcMacroClient {
63 kind: ProcMacroClientKind::Process { process: Arc::new(process), thread },
64 })
46 } 65 }
47 66
48 pub fn dummy() -> ProcMacroClient { 67 pub fn dummy() -> ProcMacroClient {
49 ProcMacroClient::Dummy 68 ProcMacroClient { kind: ProcMacroClientKind::Dummy }
50 } 69 }
51 70
52 pub fn by_dylib_path( 71 pub fn by_dylib_path(
53 &self, 72 &self,
54 _dylib_path: &Path, 73 dylib_path: &Path,
55 ) -> Vec<(SmolStr, Arc<dyn ra_tt::TokenExpander>)> { 74 ) -> Vec<(SmolStr, Arc<dyn ra_tt::TokenExpander>)> {
56 // FIXME: return empty for now 75 match &self.kind {
57 vec![] 76 ProcMacroClientKind::Dummy => vec![],
77 ProcMacroClientKind::Process { process, .. } => {
78 let macros = match process.find_proc_macros(dylib_path) {
79 Err(err) => {
80 eprintln!("Fail to find proc macro. Error: {:#?}", err);
81 return vec![];
82 }
83 Ok(macros) => macros,
84 };
85
86 macros
87 .into_iter()
88 .filter_map(|(name, kind)| {
89 // FIXME: Support custom derive only for now.
90 match kind {
91 ProcMacroKind::CustomDerive => {
92 let name = SmolStr::new(&name);
93 let expander: Arc<dyn ra_tt::TokenExpander> =
94 Arc::new(ProcMacroProcessExpander {
95 process: process.clone(),
96 name: name.clone(),
97 dylib_path: dylib_path.into(),
98 });
99 Some((name, expander))
100 }
101 _ => None,
102 }
103 })
104 .collect()
105 }
106 }
58 } 107 }
59} 108}
diff --git a/crates/ra_proc_macro/src/msg.rs b/crates/ra_proc_macro/src/msg.rs
new file mode 100644
index 000000000..aa95bcc8f
--- /dev/null
+++ b/crates/ra_proc_macro/src/msg.rs
@@ -0,0 +1,93 @@
1//! Defines messages for cross-process message based on `ndjson` wire protocol
2
3use std::{
4 convert::TryFrom,
5 io::{self, BufRead, Write},
6};
7
8use crate::{
9 rpc::{ListMacrosResult, ListMacrosTask},
10 ExpansionResult, ExpansionTask,
11};
12use serde::{de::DeserializeOwned, Deserialize, Serialize};
13
14#[derive(Debug, Serialize, Deserialize, Clone)]
15pub enum Request {
16 ListMacro(ListMacrosTask),
17 ExpansionMacro(ExpansionTask),
18}
19
20#[derive(Debug, Serialize, Deserialize, Clone)]
21pub enum Response {
22 Error(ResponseError),
23 ListMacro(ListMacrosResult),
24 ExpansionMacro(ExpansionResult),
25}
26
27macro_rules! impl_try_from_response {
28 ($ty:ty, $tag:ident) => {
29 impl TryFrom<Response> for $ty {
30 type Error = &'static str;
31 fn try_from(value: Response) -> Result<Self, Self::Error> {
32 match value {
33 Response::$tag(res) => Ok(res),
34 _ => Err("Fail to convert from response"),
35 }
36 }
37 }
38 };
39}
40
41impl_try_from_response!(ListMacrosResult, ListMacro);
42impl_try_from_response!(ExpansionResult, ExpansionMacro);
43
44#[derive(Debug, Serialize, Deserialize, Clone)]
45pub struct ResponseError {
46 pub code: ErrorCode,
47 pub message: String,
48}
49
50#[derive(Debug, Serialize, Deserialize, Clone)]
51pub enum ErrorCode {
52 ServerErrorEnd,
53 ExpansionError,
54}
55
56pub trait Message: Sized + Serialize + DeserializeOwned {
57 fn read(r: &mut impl BufRead) -> io::Result<Option<Self>> {
58 let text = match read_json(r)? {
59 None => return Ok(None),
60 Some(text) => text,
61 };
62 let msg = serde_json::from_str(&text)?;
63 Ok(Some(msg))
64 }
65 fn write(self, w: &mut impl Write) -> io::Result<()> {
66 let text = serde_json::to_string(&self)?;
67 write_json(w, &text)
68 }
69}
70
71impl Message for Request {}
72impl Message for Response {}
73
74fn read_json(inp: &mut impl BufRead) -> io::Result<Option<String>> {
75 let mut buf = String::new();
76 if inp.read_line(&mut buf)? == 0 {
77 return Ok(None);
78 }
79 // Remove ending '\n'
80 let buf = &buf[..buf.len() - 1];
81 if buf.is_empty() {
82 return Ok(None);
83 }
84 Ok(Some(buf.to_string()))
85}
86
87fn write_json(out: &mut impl Write, msg: &str) -> io::Result<()> {
88 log::debug!("> {}", msg);
89 out.write_all(msg.as_bytes())?;
90 out.write_all(b"\n")?;
91 out.flush()?;
92 Ok(())
93}
diff --git a/crates/ra_proc_macro/src/process.rs b/crates/ra_proc_macro/src/process.rs
new file mode 100644
index 000000000..e8c85be38
--- /dev/null
+++ b/crates/ra_proc_macro/src/process.rs
@@ -0,0 +1,196 @@
1//! Handle process life-time and message passing for proc-macro client
2
3use crossbeam_channel::{bounded, Receiver, Sender};
4use ra_tt::Subtree;
5
6use crate::msg::{ErrorCode, Message, Request, Response, ResponseError};
7use crate::rpc::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask, ProcMacroKind};
8
9use io::{BufRead, BufReader};
10use std::{
11 convert::{TryFrom, TryInto},
12 io::{self, Write},
13 path::{Path, PathBuf},
14 process::{Child, Command, Stdio},
15 sync::{Arc, Weak},
16};
17
18#[derive(Debug, Default)]
19pub(crate) struct ProcMacroProcessSrv {
20 inner: Option<Weak<Sender<Task>>>,
21}
22
23#[derive(Debug)]
24pub(crate) struct ProcMacroProcessThread {
25 // XXX: drop order is significant
26 sender: Arc<Sender<Task>>,
27 handle: jod_thread::JoinHandle<()>,
28}
29
30struct Task {
31 req: Request,
32 result_tx: Sender<Option<Response>>,
33}
34
35struct Process {
36 path: PathBuf,
37 child: Child,
38}
39
40impl Drop for Process {
41 fn drop(&mut self) {
42 let _ = self.child.kill();
43 }
44}
45
46impl Process {
47 fn run(process_path: &Path) -> Result<Process, io::Error> {
48 let child = Command::new(process_path.clone())
49 .stdin(Stdio::piped())
50 .stdout(Stdio::piped())
51 .stderr(Stdio::null())
52 .spawn()?;
53
54 Ok(Process { path: process_path.into(), child })
55 }
56
57 fn restart(&mut self) -> Result<(), io::Error> {
58 let _ = self.child.kill();
59 self.child = Command::new(self.path.clone())
60 .stdin(Stdio::piped())
61 .stdout(Stdio::piped())
62 .stderr(Stdio::null())
63 .spawn()?;
64 Ok(())
65 }
66
67 fn stdio(&mut self) -> Option<(impl Write, impl BufRead)> {
68 let stdin = self.child.stdin.take()?;
69 let stdout = self.child.stdout.take()?;
70 let read = BufReader::new(stdout);
71
72 Some((stdin, read))
73 }
74}
75
76impl ProcMacroProcessSrv {
77 pub fn run(
78 process_path: &Path,
79 ) -> Result<(ProcMacroProcessThread, ProcMacroProcessSrv), io::Error> {
80 let process = Process::run(process_path)?;
81
82 let (task_tx, task_rx) = bounded(0);
83 let handle = jod_thread::spawn(move || {
84 client_loop(task_rx, process);
85 });
86
87 let task_tx = Arc::new(task_tx);
88 let srv = ProcMacroProcessSrv { inner: Some(Arc::downgrade(&task_tx)) };
89 let thread = ProcMacroProcessThread { handle, sender: task_tx };
90
91 Ok((thread, srv))
92 }
93
94 pub fn find_proc_macros(
95 &self,
96 dylib_path: &Path,
97 ) -> Result<Vec<(String, ProcMacroKind)>, ra_tt::ExpansionError> {
98 let task = ListMacrosTask { lib: dylib_path.to_path_buf() };
99
100 let result: ListMacrosResult = self.send_task(Request::ListMacro(task))?;
101 Ok(result.macros)
102 }
103
104 pub fn custom_derive(
105 &self,
106 dylib_path: &Path,
107 subtree: &Subtree,
108 derive_name: &str,
109 ) -> Result<Subtree, ra_tt::ExpansionError> {
110 let task = ExpansionTask {
111 macro_body: subtree.clone(),
112 macro_name: derive_name.to_string(),
113 attributes: None,
114 lib: dylib_path.to_path_buf(),
115 };
116
117 let result: ExpansionResult = self.send_task(Request::ExpansionMacro(task))?;
118 Ok(result.expansion)
119 }
120
121 pub fn send_task<R>(&self, req: Request) -> Result<R, ra_tt::ExpansionError>
122 where
123 R: TryFrom<Response, Error = &'static str>,
124 {
125 let sender = match &self.inner {
126 None => return Err(ra_tt::ExpansionError::Unknown("No sender is found.".to_string())),
127 Some(it) => it,
128 };
129
130 let (result_tx, result_rx) = bounded(0);
131 let sender = match sender.upgrade() {
132 None => {
133 return Err(ra_tt::ExpansionError::Unknown("Proc macro process is closed.".into()))
134 }
135 Some(it) => it,
136 };
137 sender.send(Task { req: req.into(), result_tx }).unwrap();
138 let res = result_rx
139 .recv()
140 .map_err(|_| ra_tt::ExpansionError::Unknown("Proc macro thread is closed.".into()))?;
141
142 match res {
143 Some(Response::Error(err)) => {
144 return Err(ra_tt::ExpansionError::ExpansionError(err.message));
145 }
146 Some(res) => Ok(res.try_into().map_err(|err| {
147 ra_tt::ExpansionError::Unknown(format!(
148 "Fail to get response, reason : {:#?} ",
149 err
150 ))
151 })?),
152 None => Err(ra_tt::ExpansionError::Unknown("Empty result".into())),
153 }
154 }
155}
156
157fn client_loop(task_rx: Receiver<Task>, mut process: Process) {
158 let (mut stdin, mut stdout) = match process.stdio() {
159 None => return,
160 Some(it) => it,
161 };
162
163 for task in task_rx {
164 let Task { req, result_tx } = task;
165
166 match send_request(&mut stdin, &mut stdout, req) {
167 Ok(res) => result_tx.send(res).unwrap(),
168 Err(_err) => {
169 let res = Response::Error(ResponseError {
170 code: ErrorCode::ServerErrorEnd,
171 message: "Server closed".into(),
172 });
173 result_tx.send(res.into()).unwrap();
174 // Restart the process
175 if process.restart().is_err() {
176 break;
177 }
178 let stdio = match process.stdio() {
179 None => break,
180 Some(it) => it,
181 };
182 stdin = stdio.0;
183 stdout = stdio.1;
184 }
185 }
186 }
187}
188
189fn send_request(
190 mut writer: &mut impl Write,
191 mut reader: &mut impl BufRead,
192 req: Request,
193) -> Result<Option<Response>, io::Error> {
194 req.write(&mut writer)?;
195 Ok(Response::read(&mut reader)?)
196}
diff --git a/crates/ra_proc_macro/src/rpc.rs b/crates/ra_proc_macro/src/rpc.rs
new file mode 100644
index 000000000..66b3f55db
--- /dev/null
+++ b/crates/ra_proc_macro/src/rpc.rs
@@ -0,0 +1,266 @@
1//! Data struture serialization related stuffs for RPC
2//!
3//! Define all necessary rpc serialization data structure,
4//! which include ra_tt related data and some task messages.
5//! Although adding Serialize and Deserialize trait to ra_tt directly seem to be much easier,
6//! we deliberately duplicate the ra_tt struct with #[serde(with = "XXDef")]
7//! for separation of code responsibility.
8
9use ra_tt::{
10 Delimiter, DelimiterKind, Ident, Leaf, Literal, Punct, SmolStr, Spacing, Subtree, TokenId,
11 TokenTree,
12};
13use serde::{Deserialize, Serialize};
14use std::path::PathBuf;
15
16#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
17pub struct ListMacrosTask {
18 pub lib: PathBuf,
19}
20
21#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
22pub enum ProcMacroKind {
23 CustomDerive,
24 FuncLike,
25 Attr,
26}
27
28#[derive(Clone, Eq, PartialEq, Debug, Default, Serialize, Deserialize)]
29pub struct ListMacrosResult {
30 pub macros: Vec<(String, ProcMacroKind)>,
31}
32
33#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
34pub struct ExpansionTask {
35 /// Argument of macro call.
36 ///
37 /// In custom derive that would be a struct or enum; in attribute-like macro - underlying
38 /// item; in function-like macro - the macro body.
39 #[serde(with = "SubtreeDef")]
40 pub macro_body: Subtree,
41
42 /// Names of macros to expand.
43 ///
44 /// In custom derive those are names of derived traits (`Serialize`, `Getters`, etc.). In
45 /// attribute-like and functiona-like macros - single name of macro itself (`show_streams`).
46 pub macro_name: String,
47
48 /// Possible attributes for the attribute-like macros.
49 #[serde(with = "opt_subtree_def")]
50 pub attributes: Option<Subtree>,
51
52 pub lib: PathBuf,
53}
54
55#[derive(Clone, Eq, PartialEq, Debug, Default, Serialize, Deserialize)]
56pub struct ExpansionResult {
57 #[serde(with = "SubtreeDef")]
58 pub expansion: Subtree,
59}
60
61#[derive(Serialize, Deserialize)]
62#[serde(remote = "DelimiterKind")]
63enum DelimiterKindDef {
64 Parenthesis,
65 Brace,
66 Bracket,
67}
68
69#[derive(Serialize, Deserialize)]
70#[serde(remote = "TokenId")]
71struct TokenIdDef(u32);
72
73#[derive(Serialize, Deserialize)]
74#[serde(remote = "Delimiter")]
75struct DelimiterDef {
76 #[serde(with = "TokenIdDef")]
77 pub id: TokenId,
78 #[serde(with = "DelimiterKindDef")]
79 pub kind: DelimiterKind,
80}
81
82#[derive(Serialize, Deserialize)]
83#[serde(remote = "Subtree")]
84struct SubtreeDef {
85 #[serde(default, with = "opt_delimiter_def")]
86 pub delimiter: Option<Delimiter>,
87 #[serde(with = "vec_token_tree")]
88 pub token_trees: Vec<TokenTree>,
89}
90
91#[derive(Serialize, Deserialize)]
92#[serde(remote = "TokenTree")]
93enum TokenTreeDef {
94 #[serde(with = "LeafDef")]
95 Leaf(Leaf),
96 #[serde(with = "SubtreeDef")]
97 Subtree(Subtree),
98}
99
100#[derive(Serialize, Deserialize)]
101#[serde(remote = "Leaf")]
102enum LeafDef {
103 #[serde(with = "LiteralDef")]
104 Literal(Literal),
105 #[serde(with = "PunctDef")]
106 Punct(Punct),
107 #[serde(with = "IdentDef")]
108 Ident(Ident),
109}
110
111#[derive(Serialize, Deserialize)]
112#[serde(remote = "Literal")]
113struct LiteralDef {
114 pub text: SmolStr,
115 #[serde(with = "TokenIdDef")]
116 pub id: TokenId,
117}
118
119#[derive(Serialize, Deserialize)]
120#[serde(remote = "Punct")]
121struct PunctDef {
122 pub char: char,
123 #[serde(with = "SpacingDef")]
124 pub spacing: Spacing,
125 #[serde(with = "TokenIdDef")]
126 pub id: TokenId,
127}
128
129#[derive(Serialize, Deserialize)]
130#[serde(remote = "Spacing")]
131enum SpacingDef {
132 Alone,
133 Joint,
134}
135
136#[derive(Serialize, Deserialize)]
137#[serde(remote = "Ident")]
138struct IdentDef {
139 pub text: SmolStr,
140 #[serde(with = "TokenIdDef")]
141 pub id: TokenId,
142}
143
144mod opt_delimiter_def {
145 use super::{Delimiter, DelimiterDef};
146 use serde::{Deserialize, Deserializer, Serialize, Serializer};
147
148 pub fn serialize<S>(value: &Option<Delimiter>, serializer: S) -> Result<S::Ok, S::Error>
149 where
150 S: Serializer,
151 {
152 #[derive(Serialize)]
153 struct Helper<'a>(#[serde(with = "DelimiterDef")] &'a Delimiter);
154 value.as_ref().map(Helper).serialize(serializer)
155 }
156
157 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Delimiter>, D::Error>
158 where
159 D: Deserializer<'de>,
160 {
161 #[derive(Deserialize)]
162 struct Helper(#[serde(with = "DelimiterDef")] Delimiter);
163 let helper = Option::deserialize(deserializer)?;
164 Ok(helper.map(|Helper(external)| external))
165 }
166}
167
168mod opt_subtree_def {
169 use super::{Subtree, SubtreeDef};
170 use serde::{Deserialize, Deserializer, Serialize, Serializer};
171
172 pub fn serialize<S>(value: &Option<Subtree>, serializer: S) -> Result<S::Ok, S::Error>
173 where
174 S: Serializer,
175 {
176 #[derive(Serialize)]
177 struct Helper<'a>(#[serde(with = "SubtreeDef")] &'a Subtree);
178 value.as_ref().map(Helper).serialize(serializer)
179 }
180
181 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Subtree>, D::Error>
182 where
183 D: Deserializer<'de>,
184 {
185 #[derive(Deserialize)]
186 struct Helper(#[serde(with = "SubtreeDef")] Subtree);
187 let helper = Option::deserialize(deserializer)?;
188 Ok(helper.map(|Helper(external)| external))
189 }
190}
191
192mod vec_token_tree {
193 use super::{TokenTree, TokenTreeDef};
194 use serde::{ser::SerializeSeq, Deserialize, Deserializer, Serialize, Serializer};
195
196 pub fn serialize<S>(value: &Vec<TokenTree>, serializer: S) -> Result<S::Ok, S::Error>
197 where
198 S: Serializer,
199 {
200 #[derive(Serialize)]
201 struct Helper<'a>(#[serde(with = "TokenTreeDef")] &'a TokenTree);
202
203 let items: Vec<_> = value.iter().map(Helper).collect();
204 let mut seq = serializer.serialize_seq(Some(items.len()))?;
205 for element in items {
206 seq.serialize_element(&element)?;
207 }
208 seq.end()
209 }
210
211 pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<TokenTree>, D::Error>
212 where
213 D: Deserializer<'de>,
214 {
215 #[derive(Deserialize)]
216 struct Helper(#[serde(with = "TokenTreeDef")] TokenTree);
217
218 let helper = Vec::deserialize(deserializer)?;
219 Ok(helper.into_iter().map(|Helper(external)| external).collect())
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226
227 fn fixture_token_tree() -> Subtree {
228 let mut subtree = Subtree::default();
229 subtree
230 .token_trees
231 .push(TokenTree::Leaf(Ident { text: "struct".into(), id: TokenId(0) }.into()));
232 subtree
233 .token_trees
234 .push(TokenTree::Leaf(Ident { text: "Foo".into(), id: TokenId(1) }.into()));
235 subtree.token_trees.push(TokenTree::Subtree(
236 Subtree {
237 delimiter: Some(Delimiter { id: TokenId(2), kind: DelimiterKind::Brace }),
238 token_trees: vec![],
239 }
240 .into(),
241 ));
242 subtree
243 }
244
245 #[test]
246 fn test_proc_macro_rpc_works() {
247 let tt = fixture_token_tree();
248 let task = ExpansionTask {
249 macro_body: tt.clone(),
250 macro_name: Default::default(),
251 attributes: None,
252 lib: Default::default(),
253 };
254
255 let json = serde_json::to_string(&task).unwrap();
256 let back: ExpansionTask = serde_json::from_str(&json).unwrap();
257
258 assert_eq!(task.macro_body, back.macro_body);
259
260 let result = ExpansionResult { expansion: tt.clone() };
261 let json = serde_json::to_string(&result).unwrap();
262 let back: ExpansionResult = serde_json::from_str(&json).unwrap();
263
264 assert_eq!(result, back);
265 }
266}
diff --git a/crates/ra_prof/src/lib.rs b/crates/ra_prof/src/lib.rs
index 9e167db96..00ea3a9b0 100644
--- a/crates/ra_prof/src/lib.rs
+++ b/crates/ra_prof/src/lib.rs
@@ -339,6 +339,14 @@ pub fn print_backtrace() {
339 let bt = backtrace::Backtrace::new(); 339 let bt = backtrace::Backtrace::new();
340 eprintln!("{:?}", bt); 340 eprintln!("{:?}", bt);
341} 341}
342#[cfg(not(feature = "backtrace"))]
343pub fn print_backtrace() {
344 eprintln!(
345 r#"enable the backtrace feature:
346 ra_prof = {{ path = "../ra_prof", features = [ "backtrace"] }}
347"#
348 );
349}
342 350
343thread_local!(static IN_SCOPE: RefCell<bool> = RefCell::new(false)); 351thread_local!(static IN_SCOPE: RefCell<bool> = RefCell::new(false));
344 352
diff --git a/crates/ra_project_model/Cargo.toml b/crates/ra_project_model/Cargo.toml
index cdcdd63c9..b10644b4b 100644
--- a/crates/ra_project_model/Cargo.toml
+++ b/crates/ra_project_model/Cargo.toml
@@ -16,7 +16,6 @@ cargo_metadata = "0.9.1"
16ra_arena = { path = "../ra_arena" } 16ra_arena = { path = "../ra_arena" }
17ra_db = { path = "../ra_db" } 17ra_db = { path = "../ra_db" }
18ra_cfg = { path = "../ra_cfg" } 18ra_cfg = { path = "../ra_cfg" }
19ra_cargo_watch = { path = "../ra_cargo_watch" }
20ra_proc_macro = { path = "../ra_proc_macro" } 19ra_proc_macro = { path = "../ra_proc_macro" }
21 20
22serde = { version = "1.0.104", features = ["derive"] } 21serde = { version = "1.0.104", features = ["derive"] }
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs
index 291594e2a..f4fd6ad28 100644
--- a/crates/ra_project_model/src/cargo_workspace.rs
+++ b/crates/ra_project_model/src/cargo_workspace.rs
@@ -1,14 +1,16 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use std::{ 3use std::{
4 env,
5 ffi::OsStr,
4 ops, 6 ops,
5 path::{Path, PathBuf}, 7 path::{Path, PathBuf},
8 process::Command,
6}; 9};
7 10
8use anyhow::{Context, Result}; 11use anyhow::{Context, Result};
9use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}; 12use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId};
10use ra_arena::{Arena, Idx}; 13use ra_arena::{Arena, Idx};
11use ra_cargo_watch::run_cargo;
12use ra_db::Edition; 14use ra_db::Edition;
13use rustc_hash::FxHashMap; 15use rustc_hash::FxHashMap;
14use serde::Deserialize; 16use serde::Deserialize;
@@ -75,6 +77,7 @@ pub type Target = Idx<TargetData>;
75 77
76#[derive(Debug, Clone)] 78#[derive(Debug, Clone)]
77pub struct PackageData { 79pub struct PackageData {
80 pub id: String,
78 pub name: String, 81 pub name: String,
79 pub manifest: PathBuf, 82 pub manifest: PathBuf,
80 pub targets: Vec<Target>, 83 pub targets: Vec<Target>,
@@ -161,7 +164,7 @@ impl CargoWorkspace {
161 let mut out_dir_by_id = FxHashMap::default(); 164 let mut out_dir_by_id = FxHashMap::default();
162 let mut proc_macro_dylib_paths = FxHashMap::default(); 165 let mut proc_macro_dylib_paths = FxHashMap::default();
163 if cargo_features.load_out_dirs_from_check { 166 if cargo_features.load_out_dirs_from_check {
164 let resources = load_extern_resources(cargo_toml, cargo_features); 167 let resources = load_extern_resources(cargo_toml, cargo_features)?;
165 out_dir_by_id = resources.out_dirs; 168 out_dir_by_id = resources.out_dirs;
166 proc_macro_dylib_paths = resources.proc_dylib_paths; 169 proc_macro_dylib_paths = resources.proc_dylib_paths;
167 } 170 }
@@ -180,6 +183,7 @@ impl CargoWorkspace {
180 .with_context(|| format!("Failed to parse edition {}", edition))?; 183 .with_context(|| format!("Failed to parse edition {}", edition))?;
181 let pkg = packages.alloc(PackageData { 184 let pkg = packages.alloc(PackageData {
182 name, 185 name,
186 id: id.to_string(),
183 manifest: manifest_path, 187 manifest: manifest_path,
184 targets: Vec::new(), 188 targets: Vec::new(),
185 is_member, 189 is_member,
@@ -249,6 +253,18 @@ impl CargoWorkspace {
249 pub fn workspace_root(&self) -> &Path { 253 pub fn workspace_root(&self) -> &Path {
250 &self.workspace_root 254 &self.workspace_root
251 } 255 }
256
257 pub fn package_flag(&self, package: &PackageData) -> String {
258 if self.is_unique(&*package.name) {
259 package.name.clone()
260 } else {
261 package.id.clone()
262 }
263 }
264
265 fn is_unique(&self, name: &str) -> bool {
266 self.packages.iter().filter(|(_, v)| v.name == name).count() == 1
267 }
252} 268}
253 269
254#[derive(Debug, Clone, Default)] 270#[derive(Debug, Clone, Default)]
@@ -257,48 +273,61 @@ pub struct ExternResources {
257 proc_dylib_paths: FxHashMap<PackageId, PathBuf>, 273 proc_dylib_paths: FxHashMap<PackageId, PathBuf>,
258} 274}
259 275
260pub fn load_extern_resources(cargo_toml: &Path, cargo_features: &CargoFeatures) -> ExternResources { 276pub fn load_extern_resources(
261 let mut args: Vec<String> = vec![ 277 cargo_toml: &Path,
262 "check".to_string(), 278 cargo_features: &CargoFeatures,
263 "--message-format=json".to_string(), 279) -> Result<ExternResources> {
264 "--manifest-path".to_string(), 280 let mut cmd = Command::new(cargo_binary());
265 cargo_toml.display().to_string(), 281 cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml);
266 ];
267
268 if cargo_features.all_features { 282 if cargo_features.all_features {
269 args.push("--all-features".to_string()); 283 cmd.arg("--all-features");
270 } else if cargo_features.no_default_features { 284 } else if cargo_features.no_default_features {
271 // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures` 285 // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
272 // https://github.com/oli-obk/cargo_metadata/issues/79 286 // https://github.com/oli-obk/cargo_metadata/issues/79
273 args.push("--no-default-features".to_string()); 287 cmd.arg("--no-default-features");
274 } else { 288 } else {
275 args.extend(cargo_features.features.iter().cloned()); 289 cmd.args(&cargo_features.features);
276 } 290 }
277 291
278 let mut acc = ExternResources::default(); 292 let output = cmd.output()?;
279 let res = run_cargo(&args, cargo_toml.parent(), &mut |message| {
280 match message {
281 Message::BuildScriptExecuted(BuildScript { package_id, out_dir, .. }) => {
282 acc.out_dirs.insert(package_id, out_dir);
283 }
284 293
285 Message::CompilerArtifact(message) => { 294 let mut res = ExternResources::default();
286 if message.target.kind.contains(&"proc-macro".to_string()) { 295
287 let package_id = message.package_id; 296 let stdout = String::from_utf8(output.stdout)?;
288 if let Some(filename) = message.filenames.get(0) { 297 for line in stdout.lines() {
289 acc.proc_dylib_paths.insert(package_id, filename.clone()); 298 if let Ok(message) = serde_json::from_str::<cargo_metadata::Message>(&line) {
299 match message {
300 Message::BuildScriptExecuted(BuildScript { package_id, out_dir, .. }) => {
301 res.out_dirs.insert(package_id, out_dir);
302 }
303
304 Message::CompilerArtifact(message) => {
305 if message.target.kind.contains(&"proc-macro".to_string()) {
306 let package_id = message.package_id;
307 // Skip rmeta file
308 if let Some(filename) =
309 message.filenames.iter().filter(|name| is_dylib(name)).next()
310 {
311 res.proc_dylib_paths.insert(package_id, filename.clone());
312 }
290 } 313 }
291 } 314 }
315 Message::CompilerMessage(_) => (),
316 Message::Unknown => (),
292 } 317 }
293 Message::CompilerMessage(_) => (),
294 Message::Unknown => (),
295 } 318 }
296 true 319 }
297 }); 320 Ok(res)
321}
298 322
299 if let Err(err) = res { 323// FIXME: File a better way to know if it is a dylib
300 log::error!("Failed to load outdirs: {:?}", err); 324fn is_dylib(path: &Path) -> bool {
325 match path.extension().and_then(OsStr::to_str).map(|it| it.to_string().to_lowercase()) {
326 None => false,
327 Some(ext) => matches!(ext.as_str(), "dll" | "dylib" | "so"),
301 } 328 }
329}
302 330
303 acc 331fn cargo_binary() -> String {
332 env::var("CARGO").unwrap_or_else(|_| "cargo".to_string())
304} 333}
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs
index 2304e00cf..b69cae234 100644
--- a/crates/ra_syntax/src/ast/edit.rs
+++ b/crates/ra_syntax/src/ast/edit.rs
@@ -46,14 +46,46 @@ impl ast::FnDef {
46 } 46 }
47} 47}
48 48
49fn make_multiline<N>(node: N) -> N
50where
51 N: AstNode + Clone,
52{
53 let l_curly = match node.syntax().children_with_tokens().find(|it| it.kind() == T!['{']) {
54 Some(it) => it,
55 None => return node,
56 };
57 let sibling = match l_curly.next_sibling_or_token() {
58 Some(it) => it,
59 None => return node,
60 };
61 let existing_ws = match sibling.as_token() {
62 None => None,
63 Some(tok) if tok.kind() != WHITESPACE => None,
64 Some(ws) => {
65 if ws.text().contains('\n') {
66 return node;
67 }
68 Some(ws.clone())
69 }
70 };
71
72 let indent = leading_indent(node.syntax()).unwrap_or_default();
73 let ws = tokens::WsBuilder::new(&format!("\n{}", indent));
74 let to_insert = iter::once(ws.ws().into());
75 match existing_ws {
76 None => node.insert_children(InsertPosition::After(l_curly), to_insert),
77 Some(ws) => node.replace_children(single_node(ws), to_insert),
78 }
79}
80
49impl ast::ItemList { 81impl ast::ItemList {
50 #[must_use] 82 #[must_use]
51 pub fn append_items(&self, items: impl Iterator<Item = ast::ImplItem>) -> ast::ItemList { 83 pub fn append_items(&self, items: impl IntoIterator<Item = ast::ImplItem>) -> ast::ItemList {
52 let mut res = self.clone(); 84 let mut res = self.clone();
53 if !self.syntax().text().contains_char('\n') { 85 if !self.syntax().text().contains_char('\n') {
54 res = res.make_multiline(); 86 res = make_multiline(res);
55 } 87 }
56 items.for_each(|it| res = res.append_item(it)); 88 items.into_iter().for_each(|it| res = res.append_item(it));
57 res 89 res
58 } 90 }
59 91
@@ -81,35 +113,6 @@ impl ast::ItemList {
81 fn l_curly(&self) -> Option<SyntaxElement> { 113 fn l_curly(&self) -> Option<SyntaxElement> {
82 self.syntax().children_with_tokens().find(|it| it.kind() == T!['{']) 114 self.syntax().children_with_tokens().find(|it| it.kind() == T!['{'])
83 } 115 }
84
85 fn make_multiline(&self) -> ast::ItemList {
86 let l_curly = match self.syntax().children_with_tokens().find(|it| it.kind() == T!['{']) {
87 Some(it) => it,
88 None => return self.clone(),
89 };
90 let sibling = match l_curly.next_sibling_or_token() {
91 Some(it) => it,
92 None => return self.clone(),
93 };
94 let existing_ws = match sibling.as_token() {
95 None => None,
96 Some(tok) if tok.kind() != WHITESPACE => None,
97 Some(ws) => {
98 if ws.text().contains('\n') {
99 return self.clone();
100 }
101 Some(ws.clone())
102 }
103 };
104
105 let indent = leading_indent(self.syntax()).unwrap_or_default();
106 let ws = tokens::WsBuilder::new(&format!("\n{}", indent));
107 let to_insert = iter::once(ws.ws().into());
108 match existing_ws {
109 None => self.insert_children(InsertPosition::After(l_curly), to_insert),
110 Some(ws) => self.replace_children(single_node(ws), to_insert),
111 }
112 }
113} 116}
114 117
115impl ast::RecordFieldList { 118impl ast::RecordFieldList {
@@ -334,6 +337,80 @@ impl ast::UseTree {
334 } 337 }
335} 338}
336 339
340impl ast::MatchArmList {
341 #[must_use]
342 pub fn append_arms(&self, items: impl IntoIterator<Item = ast::MatchArm>) -> ast::MatchArmList {
343 let mut res = self.clone();
344 res = res.strip_if_only_whitespace();
345 if !res.syntax().text().contains_char('\n') {
346 res = make_multiline(res);
347 }
348 items.into_iter().for_each(|it| res = res.append_arm(it));
349 res
350 }
351
352 fn strip_if_only_whitespace(&self) -> ast::MatchArmList {
353 let mut iter = self.syntax().children_with_tokens().skip_while(|it| it.kind() != T!['{']);
354 iter.next(); // Eat the curly
355 let mut inner = iter.take_while(|it| it.kind() != T!['}']);
356 if !inner.clone().all(|it| it.kind() == WHITESPACE) {
357 return self.clone();
358 }
359 let start = match inner.next() {
360 Some(s) => s,
361 None => return self.clone(),
362 };
363 let end = match inner.last() {
364 Some(s) => s,
365 None => start.clone(),
366 };
367 self.replace_children(start..=end, &mut iter::empty())
368 }
369
370 #[must_use]
371 pub fn remove_placeholder(&self) -> ast::MatchArmList {
372 let placeholder =
373 self.arms().find(|arm| matches!(arm.pat(), Some(ast::Pat::PlaceholderPat(_))));
374 if let Some(placeholder) = placeholder {
375 self.remove_arm(&placeholder)
376 } else {
377 self.clone()
378 }
379 }
380
381 #[must_use]
382 fn remove_arm(&self, arm: &ast::MatchArm) -> ast::MatchArmList {
383 let start = arm.syntax().clone();
384 let end = if let Some(comma) = start
385 .siblings_with_tokens(Direction::Next)
386 .skip(1)
387 .skip_while(|it| it.kind().is_trivia())
388 .next()
389 .filter(|it| it.kind() == T![,])
390 {
391 comma
392 } else {
393 start.clone().into()
394 };
395 self.replace_children(start.into()..=end, None)
396 }
397
398 #[must_use]
399 pub fn append_arm(&self, item: ast::MatchArm) -> ast::MatchArmList {
400 let r_curly = match self.syntax().children_with_tokens().find(|it| it.kind() == T!['}']) {
401 Some(t) => t,
402 None => return self.clone(),
403 };
404 let position = InsertPosition::Before(r_curly.into());
405 let arm_ws = tokens::WsBuilder::new(" ");
406 let match_indent = &leading_indent(self.syntax()).unwrap_or_default();
407 let match_ws = tokens::WsBuilder::new(&format!("\n{}", match_indent));
408 let to_insert: ArrayVec<[SyntaxElement; 3]> =
409 [arm_ws.ws().into(), item.syntax().clone().into(), match_ws.ws().into()].into();
410 self.insert_children(position, to_insert)
411 }
412}
413
337#[must_use] 414#[must_use]
338pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { 415pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N {
339 N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap() 416 N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap()
diff --git a/crates/ra_syntax/src/parsing/text_tree_sink.rs b/crates/ra_syntax/src/parsing/text_tree_sink.rs
index dd202601d..87bb21cd9 100644
--- a/crates/ra_syntax/src/parsing/text_tree_sink.rs
+++ b/crates/ra_syntax/src/parsing/text_tree_sink.rs
@@ -149,10 +149,21 @@ fn n_attached_trivias<'a>(
149 MACRO_CALL | CONST_DEF | TYPE_ALIAS_DEF | STRUCT_DEF | ENUM_DEF | ENUM_VARIANT | FN_DEF 149 MACRO_CALL | CONST_DEF | TYPE_ALIAS_DEF | STRUCT_DEF | ENUM_DEF | ENUM_VARIANT | FN_DEF
150 | TRAIT_DEF | MODULE | RECORD_FIELD_DEF | STATIC_DEF => { 150 | TRAIT_DEF | MODULE | RECORD_FIELD_DEF | STATIC_DEF => {
151 let mut res = 0; 151 let mut res = 0;
152 for (i, (kind, text)) in trivias.enumerate() { 152 let mut trivias = trivias.enumerate().peekable();
153
154 while let Some((i, (kind, text))) = trivias.next() {
153 match kind { 155 match kind {
154 WHITESPACE => { 156 WHITESPACE => {
155 if text.contains("\n\n") { 157 if text.contains("\n\n") {
158 // we check whether the next token is a doc-comment
159 // and skip the whitespace in this case
160 if let Some((peek_kind, peek_text)) =
161 trivias.peek().map(|(_, pair)| pair)
162 {
163 if *peek_kind == COMMENT && peek_text.starts_with("///") {
164 continue;
165 }
166 }
156 break; 167 break;
157 } 168 }
158 } 169 }
diff --git a/crates/ra_syntax/test_data/parser/ok/0065_comment_newline.rs b/crates/ra_syntax/test_data/parser/ok/0065_comment_newline.rs
new file mode 100644
index 000000000..1fafe216b
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/ok/0065_comment_newline.rs
@@ -0,0 +1,3 @@
1/// Example
2
3fn test() {}
diff --git a/crates/ra_syntax/test_data/parser/ok/0065_comment_newline.txt b/crates/ra_syntax/test_data/parser/ok/0065_comment_newline.txt
new file mode 100644
index 000000000..91d0c3736
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/ok/0065_comment_newline.txt
@@ -0,0 +1,17 @@
1SOURCE_FILE@[0; 26)
2 FN_DEF@[0; 25)
3 COMMENT@[0; 11) "/// Example"
4 WHITESPACE@[11; 13) "\n\n"
5 FN_KW@[13; 15) "fn"
6 WHITESPACE@[15; 16) " "
7 NAME@[16; 20)
8 IDENT@[16; 20) "test"
9 PARAM_LIST@[20; 22)
10 L_PAREN@[20; 21) "("
11 R_PAREN@[21; 22) ")"
12 WHITESPACE@[22; 23) " "
13 BLOCK_EXPR@[23; 25)
14 BLOCK@[23; 25)
15 L_CURLY@[23; 24) "{"
16 R_CURLY@[24; 25) "}"
17 WHITESPACE@[25; 26) "\n"
diff --git a/crates/ra_tt/src/lib.rs b/crates/ra_tt/src/lib.rs
index 1015ce0a6..bd484aa30 100644
--- a/crates/ra_tt/src/lib.rs
+++ b/crates/ra_tt/src/lib.rs
@@ -189,7 +189,12 @@ impl Subtree {
189pub mod buffer; 189pub mod buffer;
190 190
191#[derive(Debug, PartialEq, Eq)] 191#[derive(Debug, PartialEq, Eq)]
192pub enum ExpansionError {} 192pub enum ExpansionError {
193 IOError(String),
194 JsonError(String),
195 Unknown(String),
196 ExpansionError(String),
197}
193 198
194pub trait TokenExpander: Debug + Send + Sync + RefUnwindSafe { 199pub trait TokenExpander: Debug + Send + Sync + RefUnwindSafe {
195 fn expand(&self, subtree: &Subtree, attrs: Option<&Subtree>) 200 fn expand(&self, subtree: &Subtree, attrs: Option<&Subtree>)
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 8fe6799d2..f5f773432 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -33,7 +33,7 @@ threadpool = "1.7.1"
33stdx = { path = "../stdx" } 33stdx = { path = "../stdx" }
34 34
35lsp-server = "0.3.1" 35lsp-server = "0.3.1"
36ra_cargo_watch = { path = "../ra_cargo_watch" } 36ra_flycheck = { path = "../ra_flycheck" }
37ra_ide = { path = "../ra_ide" } 37ra_ide = { path = "../ra_ide" }
38ra_prof = { path = "../ra_prof" } 38ra_prof = { path = "../ra_prof" }
39ra_project_model = { path = "../ra_project_model" } 39ra_project_model = { path = "../ra_project_model" }
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs
index f87bdcec5..942c30328 100644
--- a/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -77,7 +77,7 @@ impl CargoTargetSpec {
77 ProjectWorkspace::Cargo { cargo, .. } => { 77 ProjectWorkspace::Cargo { cargo, .. } => {
78 let tgt = cargo.target_by_root(&path)?; 78 let tgt = cargo.target_by_root(&path)?;
79 Some(CargoTargetSpec { 79 Some(CargoTargetSpec {
80 package: cargo[cargo[tgt].package].name.clone(), 80 package: cargo.package_flag(&cargo[cargo[tgt].package]),
81 target: cargo[tgt].name.clone(), 81 target: cargo[tgt].name.clone(),
82 target_kind: cargo[tgt].kind, 82 target_kind: cargo[tgt].kind,
83 }) 83 })
diff --git a/crates/rust-analyzer/src/cli/analysis_bench.rs b/crates/rust-analyzer/src/cli/analysis_bench.rs
index 7164b0ade..7667873d5 100644
--- a/crates/rust-analyzer/src/cli/analysis_bench.rs
+++ b/crates/rust-analyzer/src/cli/analysis_bench.rs
@@ -12,7 +12,7 @@ use ra_db::{
12 salsa::{Database, Durability}, 12 salsa::{Database, Durability},
13 FileId, SourceDatabaseExt, 13 FileId, SourceDatabaseExt,
14}; 14};
15use ra_ide::{Analysis, AnalysisChange, AnalysisHost, CompletionOptions, FilePosition, LineCol}; 15use ra_ide::{Analysis, AnalysisChange, AnalysisHost, CompletionConfig, FilePosition, LineCol};
16 16
17use crate::cli::{load_cargo::load_cargo, Verbosity}; 17use crate::cli::{load_cargo::load_cargo, Verbosity};
18 18
@@ -102,7 +102,7 @@ pub fn analysis_bench(
102 let file_position = FilePosition { file_id, offset }; 102 let file_position = FilePosition { file_id, offset };
103 103
104 if is_completion { 104 if is_completion {
105 let options = CompletionOptions::default(); 105 let options = CompletionConfig::default();
106 let res = do_work(&mut host, file_id, |analysis| { 106 let res = do_work(&mut host, file_id, |analysis| {
107 analysis.completions(file_position, &options) 107 analysis.completions(file_position, &options)
108 }); 108 });
diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs
index 6edc03fe0..e8dc953c3 100644
--- a/crates/rust-analyzer/src/conv.rs
+++ b/crates/rust-analyzer/src/conv.rs
@@ -579,7 +579,7 @@ impl TryConvWith<&WorldSnapshot> for (FileId, RangeInfo<Vec<NavigationTarget>>)
579 .into_iter() 579 .into_iter()
580 .map(|nav| (file_id, RangeInfo::new(range, nav))) 580 .map(|nav| (file_id, RangeInfo::new(range, nav)))
581 .try_conv_with_to_vec(world)?; 581 .try_conv_with_to_vec(world)?;
582 if world.options.supports_location_link { 582 if world.config.supports_location_link {
583 Ok(links.into()) 583 Ok(links.into())
584 } else { 584 } else {
585 let locations: Vec<Location> = links 585 let locations: Vec<Location> = links
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 7825b0077..79dc03de4 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -14,14 +14,15 @@ use std::{
14 time::{Duration, Instant}, 14 time::{Duration, Instant},
15}; 15};
16 16
17use crossbeam_channel::{select, unbounded, RecvError, Sender}; 17use crossbeam_channel::{never, select, unbounded, RecvError, Sender};
18use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; 18use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response};
19use lsp_types::{ 19use lsp_types::{
20 ClientCapabilities, NumberOrString, WorkDoneProgress, WorkDoneProgressBegin, 20 ClientCapabilities, NumberOrString, TextDocumentClientCapabilities, WorkDoneProgress,
21 WorkDoneProgressCreateParams, WorkDoneProgressEnd, WorkDoneProgressReport, 21 WorkDoneProgressBegin, WorkDoneProgressCreateParams, WorkDoneProgressEnd,
22 WorkDoneProgressReport,
22}; 23};
23use ra_cargo_watch::{url_from_path_with_drive_lowercasing, CheckOptions, CheckTask}; 24use ra_flycheck::{url_from_path_with_drive_lowercasing, CheckConfig, CheckTask};
24use ra_ide::{Canceled, FileId, InlayHintsOptions, LibraryData, SourceRootId}; 25use ra_ide::{Canceled, FileId, InlayHintsConfig, LibraryData, SourceRootId};
25use ra_prof::profile; 26use ra_prof::profile;
26use ra_vfs::{VfsFile, VfsTask, Watch}; 27use ra_vfs::{VfsFile, VfsTask, Watch};
27use relative_path::RelativePathBuf; 28use relative_path::RelativePathBuf;
@@ -37,9 +38,10 @@ use crate::{
37 subscriptions::Subscriptions, 38 subscriptions::Subscriptions,
38 }, 39 },
39 req, 40 req,
40 world::{Options, WorldSnapshot, WorldState}, 41 world::{Config, WorldSnapshot, WorldState},
41 Result, ServerConfig, 42 Result, ServerConfig,
42}; 43};
44use req::ConfigurationParams;
43 45
44#[derive(Debug)] 46#[derive(Debug)]
45pub struct LspError { 47pub struct LspError {
@@ -63,6 +65,57 @@ impl fmt::Display for LspError {
63 65
64impl Error for LspError {} 66impl Error for LspError {}
65 67
68fn get_feature_flags(config: &ServerConfig, connection: &Connection) -> FeatureFlags {
69 let mut ff = FeatureFlags::default();
70 for (flag, &value) in &config.feature_flags {
71 if ff.set(flag.as_str(), value).is_err() {
72 log::error!("unknown feature flag: {:?}", flag);
73 show_message(
74 req::MessageType::Error,
75 format!("unknown feature flag: {:?}", flag),
76 &connection.sender,
77 );
78 }
79 }
80 log::info!("feature_flags: {:#?}", ff);
81 ff
82}
83
84fn get_config(
85 config: &ServerConfig,
86 text_document_caps: Option<&TextDocumentClientCapabilities>,
87) -> Config {
88 Config {
89 publish_decorations: config.publish_decorations,
90 supports_location_link: text_document_caps
91 .and_then(|it| it.definition)
92 .and_then(|it| it.link_support)
93 .unwrap_or(false),
94 line_folding_only: text_document_caps
95 .and_then(|it| it.folding_range.as_ref())
96 .and_then(|it| it.line_folding_only)
97 .unwrap_or(false),
98 inlay_hints: InlayHintsConfig {
99 type_hints: config.inlay_hints_type,
100 parameter_hints: config.inlay_hints_parameter,
101 chaining_hints: config.inlay_hints_chaining,
102 max_length: config.inlay_hints_max_length,
103 },
104 check: if config.cargo_watch_enable {
105 Some(CheckConfig {
106 args: config.cargo_watch_args.clone(),
107 command: config.cargo_watch_command.clone(),
108 all_targets: config.cargo_watch_all_targets,
109 })
110 } else {
111 None
112 },
113 rustfmt_args: config.rustfmt_args.clone(),
114 vscode_lldb: config.vscode_lldb,
115 proc_macro_srv: None, // FIXME: get this from config
116 }
117}
118
66pub fn main_loop( 119pub fn main_loop(
67 ws_roots: Vec<PathBuf>, 120 ws_roots: Vec<PathBuf>,
68 client_caps: ClientCapabilities, 121 client_caps: ClientCapabilities,
@@ -90,23 +143,10 @@ pub fn main_loop(
90 SetThreadPriority(thread, thread_priority_above_normal); 143 SetThreadPriority(thread, thread_priority_above_normal);
91 } 144 }
92 145
146 let text_document_caps = client_caps.text_document.as_ref();
93 let mut loop_state = LoopState::default(); 147 let mut loop_state = LoopState::default();
94 let mut world_state = { 148 let mut world_state = {
95 let feature_flags = { 149 let feature_flags = get_feature_flags(&config, &connection);
96 let mut ff = FeatureFlags::default();
97 for (flag, value) in config.feature_flags {
98 if ff.set(flag.as_str(), value).is_err() {
99 log::error!("unknown feature flag: {:?}", flag);
100 show_message(
101 req::MessageType::Error,
102 format!("unknown feature flag: {:?}", flag),
103 &connection.sender,
104 );
105 }
106 }
107 ff
108 };
109 log::info!("feature_flags: {:#?}", feature_flags);
110 150
111 // FIXME: support dynamic workspace loading. 151 // FIXME: support dynamic workspace loading.
112 let workspaces = { 152 let workspaces = {
@@ -168,46 +208,20 @@ pub fn main_loop(
168 connection.sender.send(request.into()).unwrap(); 208 connection.sender.send(request.into()).unwrap();
169 } 209 }
170 210
171 let options = {
172 let text_document_caps = client_caps.text_document.as_ref();
173 Options {
174 publish_decorations: config.publish_decorations,
175 supports_location_link: text_document_caps
176 .and_then(|it| it.definition)
177 .and_then(|it| it.link_support)
178 .unwrap_or(false),
179 line_folding_only: text_document_caps
180 .and_then(|it| it.folding_range.as_ref())
181 .and_then(|it| it.line_folding_only)
182 .unwrap_or(false),
183 inlay_hints: InlayHintsOptions {
184 type_hints: config.inlay_hints_type,
185 parameter_hints: config.inlay_hints_parameter,
186 chaining_hints: config.inlay_hints_chaining,
187 max_length: config.inlay_hints_max_length,
188 },
189 cargo_watch: CheckOptions {
190 enable: config.cargo_watch_enable,
191 args: config.cargo_watch_args,
192 command: config.cargo_watch_command,
193 all_targets: config.cargo_watch_all_targets,
194 },
195 rustfmt_args: config.rustfmt_args,
196 vscode_lldb: config.vscode_lldb,
197 }
198 };
199
200 WorldState::new( 211 WorldState::new(
201 ws_roots, 212 ws_roots,
202 workspaces, 213 workspaces,
203 config.lru_capacity, 214 config.lru_capacity,
204 &globs, 215 &globs,
205 Watch(!config.use_client_watching), 216 Watch(!config.use_client_watching),
206 options, 217 get_config(&config, text_document_caps),
207 feature_flags, 218 feature_flags,
208 ) 219 )
209 }; 220 };
210 221
222 loop_state.roots_total = world_state.vfs.read().n_roots();
223 loop_state.roots_scanned = 0;
224
211 let pool = ThreadPool::default(); 225 let pool = ThreadPool::default();
212 let (task_sender, task_receiver) = unbounded::<Task>(); 226 let (task_sender, task_receiver) = unbounded::<Task>();
213 let (libdata_sender, libdata_receiver) = unbounded::<LibraryData>(); 227 let (libdata_sender, libdata_receiver) = unbounded::<LibraryData>();
@@ -229,7 +243,7 @@ pub fn main_loop(
229 Err(RecvError) => return Err("vfs died".into()), 243 Err(RecvError) => return Err("vfs died".into()),
230 }, 244 },
231 recv(libdata_receiver) -> data => Event::Lib(data.unwrap()), 245 recv(libdata_receiver) -> data => Event::Lib(data.unwrap()),
232 recv(world_state.check_watcher.task_recv) -> task => match task { 246 recv(world_state.flycheck.as_ref().map_or(&never(), |it| &it.task_recv)) -> task => match task {
233 Ok(task) => Event::CheckWatcher(task), 247 Ok(task) => Event::CheckWatcher(task),
234 Err(RecvError) => return Err("check watcher died".into()), 248 Err(RecvError) => return Err("check watcher died".into()),
235 } 249 }
@@ -244,6 +258,7 @@ pub fn main_loop(
244 &task_sender, 258 &task_sender,
245 &libdata_sender, 259 &libdata_sender,
246 &connection, 260 &connection,
261 text_document_caps,
247 &mut world_state, 262 &mut world_state,
248 &mut loop_state, 263 &mut loop_state,
249 event, 264 event,
@@ -333,7 +348,10 @@ struct LoopState {
333 in_flight_libraries: usize, 348 in_flight_libraries: usize,
334 pending_libraries: Vec<(SourceRootId, Vec<(FileId, RelativePathBuf, Arc<String>)>)>, 349 pending_libraries: Vec<(SourceRootId, Vec<(FileId, RelativePathBuf, Arc<String>)>)>,
335 workspace_loaded: bool, 350 workspace_loaded: bool,
336 roots_scanned_progress: Option<usize>, 351 roots_progress_reported: Option<usize>,
352 roots_scanned: usize,
353 roots_total: usize,
354 configuration_request_id: Option<RequestId>,
337} 355}
338 356
339impl LoopState { 357impl LoopState {
@@ -351,6 +369,7 @@ fn loop_turn(
351 task_sender: &Sender<Task>, 369 task_sender: &Sender<Task>,
352 libdata_sender: &Sender<LibraryData>, 370 libdata_sender: &Sender<LibraryData>,
353 connection: &Connection, 371 connection: &Connection,
372 text_document_caps: Option<&TextDocumentClientCapabilities>,
354 world_state: &mut WorldState, 373 world_state: &mut WorldState,
355 loop_state: &mut LoopState, 374 loop_state: &mut LoopState,
356 event: Event, 375 event: Event,
@@ -377,6 +396,7 @@ fn loop_turn(
377 world_state.add_lib(lib); 396 world_state.add_lib(lib);
378 world_state.maybe_collect_garbage(); 397 world_state.maybe_collect_garbage();
379 loop_state.in_flight_libraries -= 1; 398 loop_state.in_flight_libraries -= 1;
399 loop_state.roots_scanned += 1;
380 } 400 }
381 Event::CheckWatcher(task) => on_check_task(task, world_state, task_sender)?, 401 Event::CheckWatcher(task) => on_check_task(task, world_state, task_sender)?,
382 Event::Msg(msg) => match msg { 402 Event::Msg(msg) => match msg {
@@ -390,25 +410,53 @@ fn loop_turn(
390 req, 410 req,
391 )?, 411 )?,
392 Message::Notification(not) => { 412 Message::Notification(not) => {
393 on_notification( 413 on_notification(&connection.sender, world_state, loop_state, not)?;
394 &connection.sender,
395 world_state,
396 &mut loop_state.pending_requests,
397 &mut loop_state.subscriptions,
398 not,
399 )?;
400 } 414 }
401 Message::Response(resp) => { 415 Message::Response(resp) => {
402 let removed = loop_state.pending_responses.remove(&resp.id); 416 let removed = loop_state.pending_responses.remove(&resp.id);
403 if !removed { 417 if !removed {
404 log::error!("unexpected response: {:?}", resp) 418 log::error!("unexpected response: {:?}", resp)
405 } 419 }
420
421 if Some(&resp.id) == loop_state.configuration_request_id.as_ref() {
422 loop_state.configuration_request_id = None;
423 log::debug!("config update response: '{:?}", resp);
424 let Response { error, result, .. } = resp;
425
426 match (
427 error,
428 result.map(|result| serde_json::from_value::<Vec<ServerConfig>>(result)),
429 ) {
430 (Some(err), _) => {
431 log::error!("failed to fetch the server settings: {:?}", err)
432 }
433 (None, Some(Ok(new_config))) => {
434 let new_config = new_config
435 .first()
436 .expect(
437 "the client is expected to always send a non-empty config data",
438 )
439 .to_owned();
440 world_state.update_configuration(
441 new_config.lru_capacity,
442 get_config(&new_config, text_document_caps),
443 get_feature_flags(&new_config, connection),
444 );
445 }
446 (None, Some(Err(e))) => {
447 log::error!("failed to parse client config response: {}", e)
448 }
449 (None, None) => {
450 log::error!("received empty server settings response from the client")
451 }
452 }
453 }
406 } 454 }
407 }, 455 },
408 }; 456 };
409 457
410 let mut state_changed = false; 458 let mut state_changed = false;
411 if let Some(changes) = world_state.process_changes() { 459 if let Some(changes) = world_state.process_changes(&mut loop_state.roots_scanned) {
412 state_changed = true; 460 state_changed = true;
413 loop_state.pending_libraries.extend(changes); 461 loop_state.pending_libraries.extend(changes);
414 } 462 }
@@ -427,28 +475,34 @@ fn loop_turn(
427 }); 475 });
428 } 476 }
429 477
478 let show_progress = !loop_state.workspace_loaded
479 && world_state.feature_flags.get("notifications.workspace-loaded");
480
430 if !loop_state.workspace_loaded 481 if !loop_state.workspace_loaded
431 && world_state.roots_to_scan == 0 482 && loop_state.roots_scanned == loop_state.roots_total
432 && loop_state.pending_libraries.is_empty() 483 && loop_state.pending_libraries.is_empty()
433 && loop_state.in_flight_libraries == 0 484 && loop_state.in_flight_libraries == 0
434 { 485 {
435 loop_state.workspace_loaded = true; 486 loop_state.workspace_loaded = true;
436 world_state.check_watcher.update(); 487 if let Some(flycheck) = &world_state.flycheck {
488 flycheck.update();
489 }
437 pool.execute({ 490 pool.execute({
438 let subs = loop_state.subscriptions.subscriptions(); 491 let subs = loop_state.subscriptions.subscriptions();
439 let snap = world_state.snapshot(); 492 let snap = world_state.snapshot();
440 move || snap.analysis().prime_caches(subs).unwrap_or_else(|_: Canceled| ()) 493 move || snap.analysis().prime_caches(subs).unwrap_or_else(|_: Canceled| ())
441 }); 494 });
442 send_startup_progress(&connection.sender, loop_state, world_state); 495 }
443 } else if !loop_state.workspace_loaded { 496
444 send_startup_progress(&connection.sender, loop_state, world_state); 497 if show_progress {
498 send_startup_progress(&connection.sender, loop_state);
445 } 499 }
446 500
447 if state_changed { 501 if state_changed {
448 update_file_notifications_on_threadpool( 502 update_file_notifications_on_threadpool(
449 pool, 503 pool,
450 world_state.snapshot(), 504 world_state.snapshot(),
451 world_state.options.publish_decorations, 505 world_state.config.publish_decorations,
452 task_sender.clone(), 506 task_sender.clone(),
453 loop_state.subscriptions.subscriptions(), 507 loop_state.subscriptions.subscriptions(),
454 ) 508 )
@@ -556,8 +610,7 @@ fn on_request(
556fn on_notification( 610fn on_notification(
557 msg_sender: &Sender<Message>, 611 msg_sender: &Sender<Message>,
558 state: &mut WorldState, 612 state: &mut WorldState,
559 pending_requests: &mut PendingRequests, 613 loop_state: &mut LoopState,
560 subs: &mut Subscriptions,
561 not: Notification, 614 not: Notification,
562) -> Result<()> { 615) -> Result<()> {
563 let not = match notification_cast::<req::Cancel>(not) { 616 let not = match notification_cast::<req::Cancel>(not) {
@@ -566,7 +619,7 @@ fn on_notification(
566 NumberOrString::Number(id) => id.into(), 619 NumberOrString::Number(id) => id.into(),
567 NumberOrString::String(id) => id.into(), 620 NumberOrString::String(id) => id.into(),
568 }; 621 };
569 if pending_requests.cancel(&id) { 622 if loop_state.pending_requests.cancel(&id) {
570 let response = Response::new_err( 623 let response = Response::new_err(
571 id, 624 id,
572 ErrorCode::RequestCanceled as i32, 625 ErrorCode::RequestCanceled as i32,
@@ -585,7 +638,7 @@ fn on_notification(
585 if let Some(file_id) = 638 if let Some(file_id) =
586 state.vfs.write().add_file_overlay(&path, params.text_document.text) 639 state.vfs.write().add_file_overlay(&path, params.text_document.text)
587 { 640 {
588 subs.add_sub(FileId(file_id.0)); 641 loop_state.subscriptions.add_sub(FileId(file_id.0));
589 } 642 }
590 return Ok(()); 643 return Ok(());
591 } 644 }
@@ -604,7 +657,9 @@ fn on_notification(
604 }; 657 };
605 let not = match notification_cast::<req::DidSaveTextDocument>(not) { 658 let not = match notification_cast::<req::DidSaveTextDocument>(not) {
606 Ok(_params) => { 659 Ok(_params) => {
607 state.check_watcher.update(); 660 if let Some(flycheck) = &state.flycheck {
661 flycheck.update();
662 }
608 return Ok(()); 663 return Ok(());
609 } 664 }
610 Err(not) => not, 665 Err(not) => not,
@@ -614,7 +669,7 @@ fn on_notification(
614 let uri = params.text_document.uri; 669 let uri = params.text_document.uri;
615 let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; 670 let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?;
616 if let Some(file_id) = state.vfs.write().remove_file_overlay(path.as_path()) { 671 if let Some(file_id) = state.vfs.write().remove_file_overlay(path.as_path()) {
617 subs.remove_sub(FileId(file_id.0)); 672 loop_state.subscriptions.remove_sub(FileId(file_id.0));
618 } 673 }
619 let params = 674 let params =
620 req::PublishDiagnosticsParams { uri, diagnostics: Vec::new(), version: None }; 675 req::PublishDiagnosticsParams { uri, diagnostics: Vec::new(), version: None };
@@ -625,7 +680,17 @@ fn on_notification(
625 Err(not) => not, 680 Err(not) => not,
626 }; 681 };
627 let not = match notification_cast::<req::DidChangeConfiguration>(not) { 682 let not = match notification_cast::<req::DidChangeConfiguration>(not) {
628 Ok(_params) => { 683 Ok(_) => {
684 // As stated in https://github.com/microsoft/language-server-protocol/issues/676,
685 // this notification's parameters should be ignored and the actual config queried separately.
686 let request_id = loop_state.next_request_id();
687 let request = request_new::<req::WorkspaceConfiguration>(
688 request_id.clone(),
689 ConfigurationParams::default(),
690 );
691 msg_sender.send(request.into())?;
692 loop_state.configuration_request_id = Some(request_id);
693
629 return Ok(()); 694 return Ok(());
630 } 695 }
631 Err(not) => not, 696 Err(not) => not,
@@ -706,21 +771,13 @@ fn on_diagnostic_task(task: DiagnosticTask, msg_sender: &Sender<Message>, state:
706 } 771 }
707} 772}
708 773
709fn send_startup_progress( 774fn send_startup_progress(sender: &Sender<Message>, loop_state: &mut LoopState) {
710 sender: &Sender<Message>, 775 let total: usize = loop_state.roots_total;
711 loop_state: &mut LoopState, 776 let prev = loop_state.roots_progress_reported;
712 world_state: &WorldState, 777 let progress = loop_state.roots_scanned;
713) { 778 loop_state.roots_progress_reported = Some(progress);
714 if !world_state.feature_flags.get("notifications.workspace-loaded") {
715 return;
716 }
717 779
718 let total: usize = world_state.workspaces.iter().map(|it| it.n_packages()).sum(); 780 match (prev, loop_state.workspace_loaded) {
719 let prev_progress = loop_state.roots_scanned_progress;
720 let progress = total - world_state.roots_to_scan;
721 loop_state.roots_scanned_progress = Some(progress);
722
723 match (prev_progress, loop_state.workspace_loaded) {
724 (None, false) => { 781 (None, false) => {
725 let work_done_progress_create = request_new::<req::WorkDoneProgressCreate>( 782 let work_done_progress_create = request_new::<req::WorkDoneProgressCreate>(
726 loop_state.next_request_id(), 783 loop_state.next_request_id(),
@@ -755,14 +812,14 @@ fn send_startup_progress(
755 ), 812 ),
756 _ => {} 813 _ => {}
757 } 814 }
758}
759 815
760fn send_startup_progress_notif(sender: &Sender<Message>, work_done_progress: WorkDoneProgress) { 816 fn send_startup_progress_notif(sender: &Sender<Message>, work_done_progress: WorkDoneProgress) {
761 let notif = notification_new::<req::Progress>(req::ProgressParams { 817 let notif = notification_new::<req::Progress>(req::ProgressParams {
762 token: req::ProgressToken::String("rustAnalyzer/startup".into()), 818 token: req::ProgressToken::String("rustAnalyzer/startup".into()),
763 value: req::ProgressParamsValue::WorkDone(work_done_progress), 819 value: req::ProgressParamsValue::WorkDone(work_done_progress),
764 }); 820 });
765 sender.send(notif.into()).unwrap(); 821 sender.send(notif.into()).unwrap();
822 }
766} 823}
767 824
768struct PoolDispatcher<'a> { 825struct PoolDispatcher<'a> {
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index 12f8ca297..d5cb5d137 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -14,12 +14,12 @@ use lsp_types::{
14 CodeAction, CodeActionResponse, CodeLens, Command, CompletionItem, Diagnostic, 14 CodeAction, CodeActionResponse, CodeLens, Command, CompletionItem, Diagnostic,
15 DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams, 15 DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams,
16 Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, PrepareRenameResponse, 16 Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, PrepareRenameResponse,
17 Range, RenameParams, SemanticTokens, SemanticTokensParams, SemanticTokensRangeParams, 17 Range, RenameParams, SemanticTokensParams, SemanticTokensRangeParams,
18 SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, 18 SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, TextDocumentIdentifier,
19 TextEdit, WorkspaceEdit, 19 TextEdit, WorkspaceEdit,
20}; 20};
21use ra_ide::{ 21use ra_ide::{
22 Assist, AssistId, CompletionOptions, FileId, FilePosition, FileRange, Query, RangeInfo, 22 Assist, AssistId, CompletionConfig, FileId, FilePosition, FileRange, Query, RangeInfo,
23 Runnable, RunnableKind, SearchScope, 23 Runnable, RunnableKind, SearchScope,
24}; 24};
25use ra_prof::profile; 25use ra_prof::profile;
@@ -425,7 +425,7 @@ pub fn handle_completion(
425 return Ok(None); 425 return Ok(None);
426 } 426 }
427 427
428 let options = CompletionOptions { 428 let config = CompletionConfig {
429 enable_postfix_completions: world.feature_flags.get("completion.enable-postfix"), 429 enable_postfix_completions: world.feature_flags.get("completion.enable-postfix"),
430 add_call_parenthesis: world.feature_flags.get("completion.insertion.add-call-parenthesis"), 430 add_call_parenthesis: world.feature_flags.get("completion.insertion.add-call-parenthesis"),
431 add_call_argument_snippets: world 431 add_call_argument_snippets: world
@@ -433,7 +433,7 @@ pub fn handle_completion(
433 .get("completion.insertion.add-argument-snippets"), 433 .get("completion.insertion.add-argument-snippets"),
434 }; 434 };
435 435
436 let items = match world.analysis().completions(position, &options)? { 436 let items = match world.analysis().completions(position, &config)? {
437 None => return Ok(None), 437 None => return Ok(None),
438 Some(items) => items, 438 Some(items) => items,
439 }; 439 };
@@ -457,7 +457,7 @@ pub fn handle_folding_range(
457 let ctx = FoldConvCtx { 457 let ctx = FoldConvCtx {
458 text: &text, 458 text: &text,
459 line_index: &line_index, 459 line_index: &line_index,
460 line_folding_only: world.options.line_folding_only, 460 line_folding_only: world.config.line_folding_only,
461 }; 461 };
462 let res = Some(folds.into_iter().map_conv_with(&ctx).collect()); 462 let res = Some(folds.into_iter().map_conv_with(&ctx).collect());
463 Ok(res) 463 Ok(res)
@@ -611,7 +611,7 @@ pub fn handle_formatting(
611 let end_position = TextUnit::of_str(&file).conv_with(&file_line_index); 611 let end_position = TextUnit::of_str(&file).conv_with(&file_line_index);
612 612
613 let mut rustfmt = process::Command::new("rustfmt"); 613 let mut rustfmt = process::Command::new("rustfmt");
614 rustfmt.args(&world.options.rustfmt_args); 614 rustfmt.args(&world.config.rustfmt_args);
615 if let Some(&crate_id) = crate_ids.first() { 615 if let Some(&crate_id) = crate_ids.first() {
616 // Assume all crates are in the same edition 616 // Assume all crates are in the same edition
617 let edition = world.analysis().crate_edition(crate_id)?; 617 let edition = world.analysis().crate_edition(crate_id)?;
@@ -815,7 +815,7 @@ pub fn handle_code_lens(
815 }; 815 };
816 lenses.push(lens); 816 lenses.push(lens);
817 817
818 if world.options.vscode_lldb { 818 if world.config.vscode_lldb {
819 if r.args[0] == "run" { 819 if r.args[0] == "run" {
820 r.args[0] = "build".into(); 820 r.args[0] = "build".into();
821 } else { 821 } else {
@@ -1028,7 +1028,7 @@ pub fn handle_inlay_hints(
1028 let analysis = world.analysis(); 1028 let analysis = world.analysis();
1029 let line_index = analysis.file_line_index(file_id)?; 1029 let line_index = analysis.file_line_index(file_id)?;
1030 Ok(analysis 1030 Ok(analysis
1031 .inlay_hints(file_id, &world.options.inlay_hints)? 1031 .inlay_hints(file_id, &world.config.inlay_hints)?
1032 .into_iter() 1032 .into_iter()
1033 .map_conv_with(&line_index) 1033 .map_conv_with(&line_index)
1034 .collect()) 1034 .collect())
@@ -1145,7 +1145,7 @@ pub fn handle_semantic_tokens(
1145 } 1145 }
1146 } 1146 }
1147 1147
1148 let tokens = SemanticTokens { data: builder.build(), ..Default::default() }; 1148 let tokens = builder.build();
1149 1149
1150 Ok(Some(tokens.into())) 1150 Ok(Some(tokens.into()))
1151} 1151}
@@ -1166,7 +1166,7 @@ pub fn handle_semantic_tokens_range(
1166 builder.push(highlight_range.range.conv_with(&line_index), token_type, token_modifiers); 1166 builder.push(highlight_range.range.conv_with(&line_index), token_type, token_modifiers);
1167 } 1167 }
1168 1168
1169 let tokens = SemanticTokens { data: builder.build(), ..Default::default() }; 1169 let tokens = builder.build();
1170 1170
1171 Ok(Some(tokens.into())) 1171 Ok(Some(tokens.into()))
1172} 1172}
diff --git a/crates/rust-analyzer/src/req.rs b/crates/rust-analyzer/src/req.rs
index 8557294f6..994f0ed61 100644
--- a/crates/rust-analyzer/src/req.rs
+++ b/crates/rust-analyzer/src/req.rs
@@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
6 6
7pub use lsp_types::{ 7pub use lsp_types::{
8 notification::*, request::*, ApplyWorkspaceEditParams, CodeActionParams, CodeLens, 8 notification::*, request::*, ApplyWorkspaceEditParams, CodeActionParams, CodeLens,
9 CodeLensParams, CompletionParams, CompletionResponse, DiagnosticTag, 9 CodeLensParams, CompletionParams, CompletionResponse, ConfigurationParams, DiagnosticTag,
10 DidChangeConfigurationParams, DidChangeWatchedFilesParams, 10 DidChangeConfigurationParams, DidChangeWatchedFilesParams,
11 DidChangeWatchedFilesRegistrationOptions, DocumentOnTypeFormattingParams, DocumentSymbolParams, 11 DidChangeWatchedFilesRegistrationOptions, DocumentOnTypeFormattingParams, DocumentSymbolParams,
12 DocumentSymbolResponse, FileSystemWatcher, Hover, InitializeResult, MessageType, 12 DocumentSymbolResponse, FileSystemWatcher, Hover, InitializeResult, MessageType,
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index d9ba77050..2a66bbfd8 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -2,7 +2,7 @@
2 2
3use std::ops; 3use std::ops;
4 4
5use lsp_types::{Range, SemanticToken, SemanticTokenModifier, SemanticTokenType}; 5use lsp_types::{Range, SemanticToken, SemanticTokenModifier, SemanticTokenType, SemanticTokens};
6 6
7pub(crate) const ATTRIBUTE: SemanticTokenType = SemanticTokenType::new("attribute"); 7pub(crate) const ATTRIBUTE: SemanticTokenType = SemanticTokenType::new("attribute");
8pub(crate) const BUILTIN_TYPE: SemanticTokenType = SemanticTokenType::new("builtinType"); 8pub(crate) const BUILTIN_TYPE: SemanticTokenType = SemanticTokenType::new("builtinType");
@@ -109,8 +109,8 @@ impl SemanticTokensBuilder {
109 self.prev_char = range.start.character as u32; 109 self.prev_char = range.start.character as u32;
110 } 110 }
111 111
112 pub fn build(self) -> Vec<SemanticToken> { 112 pub fn build(self) -> SemanticTokens {
113 self.data 113 SemanticTokens { result_id: None, data: self.data }
114 } 114 }
115} 115}
116 116
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs
index 64a7b907e..7814a682e 100644
--- a/crates/rust-analyzer/src/world.rs
+++ b/crates/rust-analyzer/src/world.rs
@@ -11,9 +11,9 @@ use std::{
11use crossbeam_channel::{unbounded, Receiver}; 11use crossbeam_channel::{unbounded, Receiver};
12use lsp_types::Url; 12use lsp_types::Url;
13use parking_lot::RwLock; 13use parking_lot::RwLock;
14use ra_cargo_watch::{url_from_path_with_drive_lowercasing, CheckOptions, CheckWatcher}; 14use ra_flycheck::{url_from_path_with_drive_lowercasing, CheckConfig, Flycheck};
15use ra_ide::{ 15use ra_ide::{
16 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, InlayHintsOptions, LibraryData, 16 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, InlayHintsConfig, LibraryData,
17 SourceRootId, 17 SourceRootId,
18}; 18};
19use ra_project_model::{get_rustc_cfg_options, ProcMacroClient, ProjectWorkspace}; 19use ra_project_model::{get_rustc_cfg_options, ProcMacroClient, ProjectWorkspace};
@@ -31,15 +31,36 @@ use crate::{
31use ra_db::ExternSourceId; 31use ra_db::ExternSourceId;
32use rustc_hash::{FxHashMap, FxHashSet}; 32use rustc_hash::{FxHashMap, FxHashSet};
33 33
34fn create_flycheck(workspaces: &[ProjectWorkspace], config: &Config) -> Option<Flycheck> {
35 let check_config = config.check.as_ref()?;
36
37 // FIXME: Figure out the multi-workspace situation
38 workspaces
39 .iter()
40 .find_map(|w| match w {
41 ProjectWorkspace::Cargo { cargo, .. } => Some(cargo),
42 ProjectWorkspace::Json { .. } => None,
43 })
44 .map(|cargo| {
45 let cargo_project_root = cargo.workspace_root().to_path_buf();
46 Some(Flycheck::new(check_config.clone(), cargo_project_root))
47 })
48 .unwrap_or_else(|| {
49 log::warn!("Cargo check watching only supported for cargo workspaces, disabling");
50 None
51 })
52}
53
34#[derive(Debug, Clone)] 54#[derive(Debug, Clone)]
35pub struct Options { 55pub struct Config {
36 pub publish_decorations: bool, 56 pub publish_decorations: bool,
37 pub supports_location_link: bool, 57 pub supports_location_link: bool,
38 pub line_folding_only: bool, 58 pub line_folding_only: bool,
39 pub inlay_hints: InlayHintsOptions, 59 pub inlay_hints: InlayHintsConfig,
40 pub rustfmt_args: Vec<String>, 60 pub rustfmt_args: Vec<String>,
41 pub cargo_watch: CheckOptions, 61 pub check: Option<CheckConfig>,
42 pub vscode_lldb: bool, 62 pub vscode_lldb: bool,
63 pub proc_macro_srv: Option<String>,
43} 64}
44 65
45/// `WorldState` is the primary mutable state of the language server 66/// `WorldState` is the primary mutable state of the language server
@@ -49,23 +70,21 @@ pub struct Options {
49/// incremental salsa database. 70/// incremental salsa database.
50#[derive(Debug)] 71#[derive(Debug)]
51pub struct WorldState { 72pub struct WorldState {
52 pub options: Options, 73 pub config: Config,
53 pub feature_flags: Arc<FeatureFlags>, 74 pub feature_flags: Arc<FeatureFlags>,
54 //FIXME: this belongs to `LoopState` rather than to `WorldState`
55 pub roots_to_scan: usize,
56 pub roots: Vec<PathBuf>, 75 pub roots: Vec<PathBuf>,
57 pub workspaces: Arc<Vec<ProjectWorkspace>>, 76 pub workspaces: Arc<Vec<ProjectWorkspace>>,
58 pub analysis_host: AnalysisHost, 77 pub analysis_host: AnalysisHost,
59 pub vfs: Arc<RwLock<Vfs>>, 78 pub vfs: Arc<RwLock<Vfs>>,
60 pub task_receiver: Receiver<VfsTask>, 79 pub task_receiver: Receiver<VfsTask>,
61 pub latest_requests: Arc<RwLock<LatestRequests>>, 80 pub latest_requests: Arc<RwLock<LatestRequests>>,
62 pub check_watcher: CheckWatcher, 81 pub flycheck: Option<Flycheck>,
63 pub diagnostics: DiagnosticCollection, 82 pub diagnostics: DiagnosticCollection,
64} 83}
65 84
66/// An immutable snapshot of the world's state at a point in time. 85/// An immutable snapshot of the world's state at a point in time.
67pub struct WorldSnapshot { 86pub struct WorldSnapshot {
68 pub options: Options, 87 pub config: Config,
69 pub feature_flags: Arc<FeatureFlags>, 88 pub feature_flags: Arc<FeatureFlags>,
70 pub workspaces: Arc<Vec<ProjectWorkspace>>, 89 pub workspaces: Arc<Vec<ProjectWorkspace>>,
71 pub analysis: Analysis, 90 pub analysis: Analysis,
@@ -81,7 +100,7 @@ impl WorldState {
81 lru_capacity: Option<usize>, 100 lru_capacity: Option<usize>,
82 exclude_globs: &[Glob], 101 exclude_globs: &[Glob],
83 watch: Watch, 102 watch: Watch,
84 options: Options, 103 config: Config,
85 feature_flags: FeatureFlags, 104 feature_flags: FeatureFlags,
86 ) -> WorldState { 105 ) -> WorldState {
87 let mut change = AnalysisChange::new(); 106 let mut change = AnalysisChange::new();
@@ -123,7 +142,7 @@ impl WorldState {
123 let (task_sender, task_receiver) = unbounded(); 142 let (task_sender, task_receiver) = unbounded();
124 let task_sender = Box::new(move |t| task_sender.send(t).unwrap()); 143 let task_sender = Box::new(move |t| task_sender.send(t).unwrap());
125 let (mut vfs, vfs_roots) = Vfs::new(roots, task_sender, watch); 144 let (mut vfs, vfs_roots) = Vfs::new(roots, task_sender, watch);
126 let roots_to_scan = vfs_roots.len(); 145
127 for r in vfs_roots { 146 for r in vfs_roots {
128 let vfs_root_path = vfs.root2path(r); 147 let vfs_root_path = vfs.root2path(r);
129 let is_local = folder_roots.iter().any(|it| vfs_root_path.starts_with(it)); 148 let is_local = folder_roots.iter().any(|it| vfs_root_path.starts_with(it));
@@ -151,8 +170,23 @@ impl WorldState {
151 vfs_file.map(|f| FileId(f.0)) 170 vfs_file.map(|f| FileId(f.0))
152 }; 171 };
153 172
154 let proc_macro_client = 173 let proc_macro_client = match &config.proc_macro_srv {
155 ProcMacroClient::extern_process(std::path::Path::new("ra_proc_macro_srv")); 174 None => ProcMacroClient::dummy(),
175 Some(srv) => {
176 let path = Path::new(&srv);
177 match ProcMacroClient::extern_process(path) {
178 Ok(it) => it,
179 Err(err) => {
180 log::error!(
181 "Fail to run ra_proc_macro_srv from path {}, error : {}",
182 path.to_string_lossy(),
183 err
184 );
185 ProcMacroClient::dummy()
186 }
187 }
188 }
189 };
156 190
157 workspaces 191 workspaces
158 .iter() 192 .iter()
@@ -169,43 +203,41 @@ impl WorldState {
169 }); 203 });
170 change.set_crate_graph(crate_graph); 204 change.set_crate_graph(crate_graph);
171 205
172 // FIXME: Figure out the multi-workspace situation 206 let flycheck = create_flycheck(&workspaces, &config);
173 let check_watcher = workspaces
174 .iter()
175 .find_map(|w| match w {
176 ProjectWorkspace::Cargo { cargo, .. } => Some(cargo),
177 ProjectWorkspace::Json { .. } => None,
178 })
179 .map(|cargo| {
180 let cargo_project_root = cargo.workspace_root().to_path_buf();
181 CheckWatcher::new(&options.cargo_watch, cargo_project_root)
182 })
183 .unwrap_or_else(|| {
184 log::warn!("Cargo check watching only supported for cargo workspaces, disabling");
185 CheckWatcher::dummy()
186 });
187 207
188 let mut analysis_host = AnalysisHost::new(lru_capacity); 208 let mut analysis_host = AnalysisHost::new(lru_capacity);
189 analysis_host.apply_change(change); 209 analysis_host.apply_change(change);
190 WorldState { 210 WorldState {
191 options, 211 config: config,
192 feature_flags: Arc::new(feature_flags), 212 feature_flags: Arc::new(feature_flags),
193 roots_to_scan,
194 roots: folder_roots, 213 roots: folder_roots,
195 workspaces: Arc::new(workspaces), 214 workspaces: Arc::new(workspaces),
196 analysis_host, 215 analysis_host,
197 vfs: Arc::new(RwLock::new(vfs)), 216 vfs: Arc::new(RwLock::new(vfs)),
198 task_receiver, 217 task_receiver,
199 latest_requests: Default::default(), 218 latest_requests: Default::default(),
200 check_watcher, 219 flycheck,
201 diagnostics: Default::default(), 220 diagnostics: Default::default(),
202 } 221 }
203 } 222 }
204 223
224 pub fn update_configuration(
225 &mut self,
226 lru_capacity: Option<usize>,
227 config: Config,
228 feature_flags: FeatureFlags,
229 ) {
230 self.feature_flags = Arc::new(feature_flags);
231 self.analysis_host.update_lru_capacity(lru_capacity);
232 self.flycheck = create_flycheck(&self.workspaces, &config);
233 self.config = config;
234 }
235
205 /// Returns a vec of libraries 236 /// Returns a vec of libraries
206 /// FIXME: better API here 237 /// FIXME: better API here
207 pub fn process_changes( 238 pub fn process_changes(
208 &mut self, 239 &mut self,
240 roots_scanned: &mut usize,
209 ) -> Option<Vec<(SourceRootId, Vec<(FileId, RelativePathBuf, Arc<String>)>)>> { 241 ) -> Option<Vec<(SourceRootId, Vec<(FileId, RelativePathBuf, Arc<String>)>)>> {
210 let changes = self.vfs.write().commit_changes(); 242 let changes = self.vfs.write().commit_changes();
211 if changes.is_empty() { 243 if changes.is_empty() {
@@ -219,7 +251,7 @@ impl WorldState {
219 let root_path = self.vfs.read().root2path(root); 251 let root_path = self.vfs.read().root2path(root);
220 let is_local = self.roots.iter().any(|r| root_path.starts_with(r)); 252 let is_local = self.roots.iter().any(|r| root_path.starts_with(r));
221 if is_local { 253 if is_local {
222 self.roots_to_scan -= 1; 254 *roots_scanned += 1;
223 for (file, path, text) in files { 255 for (file, path, text) in files {
224 change.add_file(SourceRootId(root.0), FileId(file.0), path, text); 256 change.add_file(SourceRootId(root.0), FileId(file.0), path, text);
225 } 257 }
@@ -247,7 +279,6 @@ impl WorldState {
247 } 279 }
248 280
249 pub fn add_lib(&mut self, data: LibraryData) { 281 pub fn add_lib(&mut self, data: LibraryData) {
250 self.roots_to_scan -= 1;
251 let mut change = AnalysisChange::new(); 282 let mut change = AnalysisChange::new();
252 change.add_library(data); 283 change.add_library(data);
253 self.analysis_host.apply_change(change); 284 self.analysis_host.apply_change(change);
@@ -255,7 +286,7 @@ impl WorldState {
255 286
256 pub fn snapshot(&self) -> WorldSnapshot { 287 pub fn snapshot(&self) -> WorldSnapshot {
257 WorldSnapshot { 288 WorldSnapshot {
258 options: self.options.clone(), 289 config: self.config.clone(),
259 feature_flags: Arc::clone(&self.feature_flags), 290 feature_flags: Arc::clone(&self.feature_flags),
260 workspaces: Arc::clone(&self.workspaces), 291 workspaces: Arc::clone(&self.workspaces),
261 analysis: self.analysis_host.analysis(), 292 analysis: self.analysis_host.analysis(),
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs
index 145429571..5af5eaad2 100644
--- a/crates/rust-analyzer/tests/heavy_tests/main.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/main.rs
@@ -9,7 +9,7 @@ use lsp_types::{
9}; 9};
10use rust_analyzer::req::{ 10use rust_analyzer::req::{
11 CodeActionParams, CodeActionRequest, Completion, CompletionParams, DidOpenTextDocument, 11 CodeActionParams, CodeActionRequest, Completion, CompletionParams, DidOpenTextDocument,
12 Formatting, OnEnter, Runnables, RunnablesParams, 12 Formatting, GotoDefinition, OnEnter, Runnables, RunnablesParams,
13}; 13};
14use serde_json::json; 14use serde_json::json;
15use tempfile::TempDir; 15use tempfile::TempDir;
@@ -581,3 +581,47 @@ version = \"0.0.0\"
581 }), 581 }),
582 ); 582 );
583} 583}
584
585#[test]
586fn resolve_include_concat_env() {
587 if skip_slow_tests() {
588 return;
589 }
590
591 let server = Project::with_fixture(
592 r###"
593//- Cargo.toml
594[package]
595name = "foo"
596version = "0.0.0"
597
598//- build.rs
599use std::{env, fs, path::Path};
600
601fn main() {
602 let out_dir = env::var_os("OUT_DIR").unwrap();
603 let dest_path = Path::new(&out_dir).join("hello.rs");
604 fs::write(
605 &dest_path,
606 r#"pub fn message() -> &'static str { "Hello, World!" }"#,
607 )
608 .unwrap();
609 println!("cargo:rerun-if-changed=build.rs");
610}
611//- src/main.rs
612include!(concat!(env!("OUT_DIR"), "/hello.rs"));
613
614fn main() { message(); }
615"###,
616 )
617 .with_config(|config| {
618 config.cargo_features.load_out_dirs_from_check = true;
619 })
620 .server();
621 server.wait_until_workspace_is_loaded();
622 let res = server.send_request::<GotoDefinition>(TextDocumentPositionParams::new(
623 server.doc_id("src/main.rs"),
624 Position::new(2, 15),
625 ));
626 assert!(format!("{}", res).contains("hello.rs"));
627}
diff --git a/crates/rust-analyzer/tests/heavy_tests/support.rs b/crates/rust-analyzer/tests/heavy_tests/support.rs
index 1d7062bdf..d8bed6d7f 100644
--- a/crates/rust-analyzer/tests/heavy_tests/support.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/support.rs
@@ -27,11 +27,12 @@ pub struct Project<'a> {
27 with_sysroot: bool, 27 with_sysroot: bool,
28 tmp_dir: Option<TempDir>, 28 tmp_dir: Option<TempDir>,
29 roots: Vec<PathBuf>, 29 roots: Vec<PathBuf>,
30 config: Option<Box<dyn Fn(&mut ServerConfig)>>,
30} 31}
31 32
32impl<'a> Project<'a> { 33impl<'a> Project<'a> {
33 pub fn with_fixture(fixture: &str) -> Project { 34 pub fn with_fixture(fixture: &str) -> Project {
34 Project { fixture, tmp_dir: None, roots: vec![], with_sysroot: false } 35 Project { fixture, tmp_dir: None, roots: vec![], with_sysroot: false, config: None }
35 } 36 }
36 37
37 pub fn tmp_dir(mut self, tmp_dir: TempDir) -> Project<'a> { 38 pub fn tmp_dir(mut self, tmp_dir: TempDir) -> Project<'a> {
@@ -49,6 +50,11 @@ impl<'a> Project<'a> {
49 self 50 self
50 } 51 }
51 52
53 pub fn with_config(mut self, config: impl Fn(&mut ServerConfig) + 'static) -> Project<'a> {
54 self.config = Some(Box::new(config));
55 self
56 }
57
52 pub fn server(self) -> Server { 58 pub fn server(self) -> Server {
53 let tmp_dir = self.tmp_dir.unwrap_or_else(|| TempDir::new().unwrap()); 59 let tmp_dir = self.tmp_dir.unwrap_or_else(|| TempDir::new().unwrap());
54 static INIT: Once = Once::new(); 60 static INIT: Once = Once::new();
@@ -72,7 +78,14 @@ impl<'a> Project<'a> {
72 78
73 let roots = self.roots.into_iter().map(|root| tmp_dir.path().join(root)).collect(); 79 let roots = self.roots.into_iter().map(|root| tmp_dir.path().join(root)).collect();
74 80
75 Server::new(tmp_dir, self.with_sysroot, roots, paths) 81 let mut config =
82 ServerConfig { with_sysroot: self.with_sysroot, ..ServerConfig::default() };
83
84 if let Some(f) = &self.config {
85 f(&mut config)
86 }
87
88 Server::new(tmp_dir, config, roots, paths)
76 } 89 }
77} 90}
78 91
@@ -83,15 +96,16 @@ pub fn project(fixture: &str) -> Server {
83pub struct Server { 96pub struct Server {
84 req_id: Cell<u64>, 97 req_id: Cell<u64>,
85 messages: RefCell<Vec<Message>>, 98 messages: RefCell<Vec<Message>>,
86 dir: TempDir,
87 _thread: jod_thread::JoinHandle<()>, 99 _thread: jod_thread::JoinHandle<()>,
88 client: Connection, 100 client: Connection,
101 /// XXX: remove the tempdir last
102 dir: TempDir,
89} 103}
90 104
91impl Server { 105impl Server {
92 fn new( 106 fn new(
93 dir: TempDir, 107 dir: TempDir,
94 with_sysroot: bool, 108 config: ServerConfig,
95 roots: Vec<PathBuf>, 109 roots: Vec<PathBuf>,
96 files: Vec<(PathBuf, String)>, 110 files: Vec<(PathBuf, String)>,
97 ) -> Server { 111 ) -> Server {
@@ -117,7 +131,7 @@ impl Server {
117 window: None, 131 window: None,
118 experimental: None, 132 experimental: None,
119 }, 133 },
120 ServerConfig { with_sysroot, ..ServerConfig::default() }, 134 config,
121 connection, 135 connection,
122 ) 136 )
123 .unwrap() 137 .unwrap()
@@ -187,6 +201,7 @@ impl Server {
187 self.client.sender.send(r.into()).unwrap(); 201 self.client.sender.send(r.into()).unwrap();
188 while let Some(msg) = self.recv() { 202 while let Some(msg) = self.recv() {
189 match msg { 203 match msg {
204 Message::Request(req) if req.method == "window/workDoneProgress/create" => (),
190 Message::Request(req) => panic!("unexpected request: {:?}", req), 205 Message::Request(req) => panic!("unexpected request: {:?}", req),
191 Message::Notification(_) => (), 206 Message::Notification(_) => (),
192 Message::Response(res) => { 207 Message::Response(res) => {
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index 8492c17af..d2efa2236 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -5,7 +5,7 @@ use std::{cell::Cell, fmt};
5/// Appends formatted string to a `String`. 5/// Appends formatted string to a `String`.
6#[macro_export] 6#[macro_export]
7macro_rules! format_to { 7macro_rules! format_to {
8 (&buf:expr) => (); 8 ($buf:expr) => ();
9 ($buf:expr, $lit:literal $($arg:tt)*) => { 9 ($buf:expr, $lit:literal $($arg:tt)*) => {
10 { use ::std::fmt::Write as _; let _ = ::std::write!($buf, $lit $($arg)*); } 10 { use ::std::fmt::Write as _; let _ = ::std::write!($buf, $lit $($arg)*); }
11 }; 11 };