diff options
Diffstat (limited to 'crates')
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> { | |||
14 | impl<T, V> ArenaMap<Idx<T>, V> { | 14 | impl<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 | |||
3 | use std::iter; | 1 | use std::iter; |
4 | 2 | ||
5 | use hir::{Adt, HasSource, ModuleDef, Semantics}; | 3 | use hir::{Adt, HasSource, ModuleDef, Semantics}; |
6 | use itertools::Itertools; | 4 | use itertools::Itertools; |
7 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; | ||
8 | 7 | ||
9 | use crate::{Assist, AssistCtx, AssistId}; | 8 | use crate::{Assist, AssistCtx, AssistId}; |
10 | use ra_syntax::ast::{self, edit::IndentLevel, make, AstNode, NameOwner}; | ||
11 | |||
12 | use 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] |
2 | edition = "2018" | 2 | edition = "2018" |
3 | name = "ra_cargo_watch" | 3 | name = "ra_flycheck" |
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["rust-analyzer developers"] |
6 | 6 | ||
@@ -10,6 +10,7 @@ lsp-types = { version = "0.73.0", features = ["proposed"] } | |||
10 | log = "0.4.8" | 10 | log = "0.4.8" |
11 | cargo_metadata = "0.9.1" | 11 | cargo_metadata = "0.9.1" |
12 | serde_json = "1.0.48" | 12 | serde_json = "1.0.48" |
13 | jod-thread = "0.1.1" | ||
13 | 14 | ||
14 | [dev-dependencies] | 15 | [dev-dependencies] |
15 | insta = "0.15.0" | 16 | insta = "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 | --- |
2 | source: crates/ra_cargo_watch/src/conv/test.rs | 2 | source: crates/ra_flycheck/src/conv/test.rs |
3 | expression: diag | 3 | expression: 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 | --- |
2 | source: crates/ra_cargo_watch/src/conv/test.rs | 2 | source: crates/ra_flycheck/src/conv/test.rs |
3 | expression: diag | 3 | expression: 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 | --- |
2 | source: crates/ra_cargo_watch/src/conv/test.rs | 2 | source: crates/ra_flycheck/src/conv/test.rs |
3 | expression: diag | 3 | expression: 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 | --- |
2 | source: crates/ra_cargo_watch/src/conv/test.rs | 2 | source: crates/ra_flycheck/src/conv/test.rs |
3 | expression: diag | 3 | expression: 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 | --- |
2 | source: crates/ra_cargo_watch/src/conv/test.rs | 2 | source: crates/ra_flycheck/src/conv/test.rs |
3 | expression: diag | 3 | expression: 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 | --- |
2 | source: crates/ra_cargo_watch/src/conv/test.rs | 2 | source: crates/ra_flycheck/src/conv/test.rs |
3 | expression: diag | 3 | expression: 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 | --- |
2 | source: crates/ra_cargo_watch/src/conv/test.rs | 2 | source: crates/ra_flycheck/src/conv/test.rs |
3 | expression: diag | 3 | expression: 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 | --- |
2 | source: crates/ra_cargo_watch/src/conv/test.rs | 2 | source: crates/ra_flycheck/src/conv/test.rs |
3 | expression: diag | 3 | expression: 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. |
4 | use cargo_metadata::Message; | 4 | mod conv; |
5 | use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender}; | 5 | |
6 | use lsp_types::{ | ||
7 | CodeAction, CodeActionOrCommand, Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin, | ||
8 | WorkDoneProgressEnd, WorkDoneProgressReport, | ||
9 | }; | ||
10 | use std::{ | 6 | use 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 | ||
19 | mod conv; | 14 | use cargo_metadata::Message; |
15 | use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender}; | ||
16 | use lsp_types::{ | ||
17 | CodeAction, CodeActionOrCommand, Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin, | ||
18 | WorkDoneProgressEnd, WorkDoneProgressReport, | ||
19 | }; | ||
20 | 20 | ||
21 | use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic}; | 21 | use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic}; |
22 | 22 | ||
23 | pub use crate::conv::url_from_path_with_drive_lowercasing; | 23 | pub use crate::conv::url_from_path_with_drive_lowercasing; |
24 | 24 | ||
25 | #[derive(Clone, Debug)] | 25 | #[derive(Clone, Debug)] |
26 | pub struct CheckOptions { | 26 | pub 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)] |
38 | pub struct CheckWatcher { | 37 | pub 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 | ||
44 | impl CheckWatcher { | 44 | impl 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 | |||
70 | impl 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 | ||
103 | struct CheckWatcherThread { | 78 | struct 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 | ||
110 | impl CheckWatcherThread { | 92 | impl 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. | ||
239 | struct WatchThread { | ||
240 | handle: Option<JoinHandle<()>>, | ||
241 | message_recv: Receiver<CheckEvent>, | ||
242 | } | ||
243 | |||
244 | enum CheckEvent { | 271 | enum 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 | } |
258 | impl error::Error for CargoError {} | 285 | impl error::Error for CargoError {} |
259 | 286 | ||
260 | pub fn run_cargo( | 287 | fn 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 | |||
334 | impl 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 | |||
390 | impl 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 | ||
12 | macro_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 | |||
12 | impl ProcMacroExpander { | 21 | impl 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 | |||
46 | fn 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)] |
37 | pub struct CompletionOptions { | 37 | pub 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 | ||
43 | impl Default for CompletionOptions { | 43 | impl 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 { | |||
75 | pub(crate) fn completions( | 75 | pub(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 | ||
17 | pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | 17 | pub(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 | }; |
12 | use ra_text_edit::AtomTextEdit; | 12 | use ra_text_edit::AtomTextEdit; |
13 | 13 | ||
14 | use crate::{completion::CompletionOptions, FilePosition}; | 14 | use 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}; | |||
19 | pub(crate) struct CompletionContext<'a> { | 19 | pub(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 | ||
3 | use crate::{ | 3 | use 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 | ||
9 | pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> { | 9 | pub(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 | ||
13 | pub(crate) fn do_completion_with_options( | 13 | pub(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::{ | |||
11 | use crate::{FileId, FunctionSignature}; | 11 | use crate::{FileId, FunctionSignature}; |
12 | 12 | ||
13 | #[derive(Clone, Debug, PartialEq, Eq)] | 13 | #[derive(Clone, Debug, PartialEq, Eq)] |
14 | pub struct InlayHintsOptions { | 14 | pub 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 | ||
21 | impl Default for InlayHintsOptions { | 21 | impl 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 { | |||
41 | pub(crate) fn inlay_hints( | 41 | pub(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( | |||
68 | fn get_chaining_hints( | 68 | fn 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( | |||
108 | fn get_param_name_hints( | 108 | fn 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( | |||
148 | fn get_bind_pat_hints( | 148 | fn 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)] |
272 | mod tests { | 272 | mod 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; | |||
62 | pub use crate::{ | 62 | pub 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 | ||
126 | impl salsa::ParallelDatabase for RootDatabase { | 130 | impl 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] |
12 | ra_tt = { path = "../ra_tt" } | 12 | ra_tt = { path = "../ra_tt" } |
13 | serde = { version = "1.0", features = ["derive"] } | ||
14 | serde_json = "1.0" | ||
15 | log = "0.4.8" | ||
16 | crossbeam-channel = "0.4.0" | ||
17 | jod-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 | ||
8 | mod rpc; | ||
9 | mod process; | ||
10 | pub mod msg; | ||
11 | |||
12 | use process::{ProcMacroProcessSrv, ProcMacroProcessThread}; | ||
8 | use ra_tt::{SmolStr, Subtree}; | 13 | use ra_tt::{SmolStr, Subtree}; |
14 | use rpc::ProcMacroKind; | ||
9 | use std::{ | 15 | use 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)] | 20 | pub use rpc::{ExpansionResult, ExpansionTask}; |
21 | |||
22 | #[derive(Debug, Clone)] | ||
15 | pub struct ProcMacroProcessExpander { | 23 | pub struct ProcMacroProcessExpander { |
16 | process: Arc<ProcMacroProcessSrv>, | 24 | process: Arc<ProcMacroProcessSrv>, |
25 | dylib_path: PathBuf, | ||
17 | name: SmolStr, | 26 | name: SmolStr, |
18 | } | 27 | } |
19 | 28 | ||
29 | impl Eq for ProcMacroProcessExpander {} | ||
30 | impl 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 | |||
20 | impl ra_tt::TokenExpander for ProcMacroProcessExpander { | 38 | impl 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)] |
32 | pub struct ProcMacroProcessSrv { | 49 | enum 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)] |
37 | pub enum ProcMacroClient { | 55 | pub struct ProcMacroClient { |
38 | Process { process: Arc<ProcMacroProcessSrv> }, | 56 | kind: ProcMacroClientKind, |
39 | Dummy, | ||
40 | } | 57 | } |
41 | 58 | ||
42 | impl ProcMacroClient { | 59 | impl 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 | |||
3 | use std::{ | ||
4 | convert::TryFrom, | ||
5 | io::{self, BufRead, Write}, | ||
6 | }; | ||
7 | |||
8 | use crate::{ | ||
9 | rpc::{ListMacrosResult, ListMacrosTask}, | ||
10 | ExpansionResult, ExpansionTask, | ||
11 | }; | ||
12 | use serde::{de::DeserializeOwned, Deserialize, Serialize}; | ||
13 | |||
14 | #[derive(Debug, Serialize, Deserialize, Clone)] | ||
15 | pub enum Request { | ||
16 | ListMacro(ListMacrosTask), | ||
17 | ExpansionMacro(ExpansionTask), | ||
18 | } | ||
19 | |||
20 | #[derive(Debug, Serialize, Deserialize, Clone)] | ||
21 | pub enum Response { | ||
22 | Error(ResponseError), | ||
23 | ListMacro(ListMacrosResult), | ||
24 | ExpansionMacro(ExpansionResult), | ||
25 | } | ||
26 | |||
27 | macro_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 | |||
41 | impl_try_from_response!(ListMacrosResult, ListMacro); | ||
42 | impl_try_from_response!(ExpansionResult, ExpansionMacro); | ||
43 | |||
44 | #[derive(Debug, Serialize, Deserialize, Clone)] | ||
45 | pub struct ResponseError { | ||
46 | pub code: ErrorCode, | ||
47 | pub message: String, | ||
48 | } | ||
49 | |||
50 | #[derive(Debug, Serialize, Deserialize, Clone)] | ||
51 | pub enum ErrorCode { | ||
52 | ServerErrorEnd, | ||
53 | ExpansionError, | ||
54 | } | ||
55 | |||
56 | pub 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 | |||
71 | impl Message for Request {} | ||
72 | impl Message for Response {} | ||
73 | |||
74 | fn 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 | |||
87 | fn 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 | |||
3 | use crossbeam_channel::{bounded, Receiver, Sender}; | ||
4 | use ra_tt::Subtree; | ||
5 | |||
6 | use crate::msg::{ErrorCode, Message, Request, Response, ResponseError}; | ||
7 | use crate::rpc::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask, ProcMacroKind}; | ||
8 | |||
9 | use io::{BufRead, BufReader}; | ||
10 | use 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)] | ||
19 | pub(crate) struct ProcMacroProcessSrv { | ||
20 | inner: Option<Weak<Sender<Task>>>, | ||
21 | } | ||
22 | |||
23 | #[derive(Debug)] | ||
24 | pub(crate) struct ProcMacroProcessThread { | ||
25 | // XXX: drop order is significant | ||
26 | sender: Arc<Sender<Task>>, | ||
27 | handle: jod_thread::JoinHandle<()>, | ||
28 | } | ||
29 | |||
30 | struct Task { | ||
31 | req: Request, | ||
32 | result_tx: Sender<Option<Response>>, | ||
33 | } | ||
34 | |||
35 | struct Process { | ||
36 | path: PathBuf, | ||
37 | child: Child, | ||
38 | } | ||
39 | |||
40 | impl Drop for Process { | ||
41 | fn drop(&mut self) { | ||
42 | let _ = self.child.kill(); | ||
43 | } | ||
44 | } | ||
45 | |||
46 | impl 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 | |||
76 | impl 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 | |||
157 | fn 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 | |||
189 | fn 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 | |||
9 | use ra_tt::{ | ||
10 | Delimiter, DelimiterKind, Ident, Leaf, Literal, Punct, SmolStr, Spacing, Subtree, TokenId, | ||
11 | TokenTree, | ||
12 | }; | ||
13 | use serde::{Deserialize, Serialize}; | ||
14 | use std::path::PathBuf; | ||
15 | |||
16 | #[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] | ||
17 | pub struct ListMacrosTask { | ||
18 | pub lib: PathBuf, | ||
19 | } | ||
20 | |||
21 | #[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] | ||
22 | pub enum ProcMacroKind { | ||
23 | CustomDerive, | ||
24 | FuncLike, | ||
25 | Attr, | ||
26 | } | ||
27 | |||
28 | #[derive(Clone, Eq, PartialEq, Debug, Default, Serialize, Deserialize)] | ||
29 | pub struct ListMacrosResult { | ||
30 | pub macros: Vec<(String, ProcMacroKind)>, | ||
31 | } | ||
32 | |||
33 | #[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] | ||
34 | pub 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)] | ||
56 | pub struct ExpansionResult { | ||
57 | #[serde(with = "SubtreeDef")] | ||
58 | pub expansion: Subtree, | ||
59 | } | ||
60 | |||
61 | #[derive(Serialize, Deserialize)] | ||
62 | #[serde(remote = "DelimiterKind")] | ||
63 | enum DelimiterKindDef { | ||
64 | Parenthesis, | ||
65 | Brace, | ||
66 | Bracket, | ||
67 | } | ||
68 | |||
69 | #[derive(Serialize, Deserialize)] | ||
70 | #[serde(remote = "TokenId")] | ||
71 | struct TokenIdDef(u32); | ||
72 | |||
73 | #[derive(Serialize, Deserialize)] | ||
74 | #[serde(remote = "Delimiter")] | ||
75 | struct 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")] | ||
84 | struct 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")] | ||
93 | enum 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")] | ||
102 | enum 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")] | ||
113 | struct LiteralDef { | ||
114 | pub text: SmolStr, | ||
115 | #[serde(with = "TokenIdDef")] | ||
116 | pub id: TokenId, | ||
117 | } | ||
118 | |||
119 | #[derive(Serialize, Deserialize)] | ||
120 | #[serde(remote = "Punct")] | ||
121 | struct 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")] | ||
131 | enum SpacingDef { | ||
132 | Alone, | ||
133 | Joint, | ||
134 | } | ||
135 | |||
136 | #[derive(Serialize, Deserialize)] | ||
137 | #[serde(remote = "Ident")] | ||
138 | struct IdentDef { | ||
139 | pub text: SmolStr, | ||
140 | #[serde(with = "TokenIdDef")] | ||
141 | pub id: TokenId, | ||
142 | } | ||
143 | |||
144 | mod 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 | |||
168 | mod 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 | |||
192 | mod 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)] | ||
224 | mod 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"))] | ||
343 | pub fn print_backtrace() { | ||
344 | eprintln!( | ||
345 | r#"enable the backtrace feature: | ||
346 | ra_prof = {{ path = "../ra_prof", features = [ "backtrace"] }} | ||
347 | "# | ||
348 | ); | ||
349 | } | ||
342 | 350 | ||
343 | thread_local!(static IN_SCOPE: RefCell<bool> = RefCell::new(false)); | 351 | thread_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" | |||
16 | ra_arena = { path = "../ra_arena" } | 16 | ra_arena = { path = "../ra_arena" } |
17 | ra_db = { path = "../ra_db" } | 17 | ra_db = { path = "../ra_db" } |
18 | ra_cfg = { path = "../ra_cfg" } | 18 | ra_cfg = { path = "../ra_cfg" } |
19 | ra_cargo_watch = { path = "../ra_cargo_watch" } | ||
20 | ra_proc_macro = { path = "../ra_proc_macro" } | 19 | ra_proc_macro = { path = "../ra_proc_macro" } |
21 | 20 | ||
22 | serde = { version = "1.0.104", features = ["derive"] } | 21 | serde = { 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 | ||
3 | use std::{ | 3 | use 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 | ||
8 | use anyhow::{Context, Result}; | 11 | use anyhow::{Context, Result}; |
9 | use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}; | 12 | use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}; |
10 | use ra_arena::{Arena, Idx}; | 13 | use ra_arena::{Arena, Idx}; |
11 | use ra_cargo_watch::run_cargo; | ||
12 | use ra_db::Edition; | 14 | use ra_db::Edition; |
13 | use rustc_hash::FxHashMap; | 15 | use rustc_hash::FxHashMap; |
14 | use serde::Deserialize; | 16 | use serde::Deserialize; |
@@ -75,6 +77,7 @@ pub type Target = Idx<TargetData>; | |||
75 | 77 | ||
76 | #[derive(Debug, Clone)] | 78 | #[derive(Debug, Clone)] |
77 | pub struct PackageData { | 79 | pub 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 | ||
260 | pub fn load_extern_resources(cargo_toml: &Path, cargo_features: &CargoFeatures) -> ExternResources { | 276 | pub 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); | 324 | fn 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 | 331 | fn 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 | ||
49 | fn make_multiline<N>(node: N) -> N | ||
50 | where | ||
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 | |||
49 | impl ast::ItemList { | 81 | impl 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 | ||
115 | impl ast::RecordFieldList { | 118 | impl ast::RecordFieldList { |
@@ -334,6 +337,80 @@ impl ast::UseTree { | |||
334 | } | 337 | } |
335 | } | 338 | } |
336 | 339 | ||
340 | impl 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] |
338 | pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { | 415 | pub 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 | |||
3 | fn 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 @@ | |||
1 | SOURCE_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 { | |||
189 | pub mod buffer; | 189 | pub mod buffer; |
190 | 190 | ||
191 | #[derive(Debug, PartialEq, Eq)] | 191 | #[derive(Debug, PartialEq, Eq)] |
192 | pub enum ExpansionError {} | 192 | pub enum ExpansionError { |
193 | IOError(String), | ||
194 | JsonError(String), | ||
195 | Unknown(String), | ||
196 | ExpansionError(String), | ||
197 | } | ||
193 | 198 | ||
194 | pub trait TokenExpander: Debug + Send + Sync + RefUnwindSafe { | 199 | pub 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" | |||
33 | stdx = { path = "../stdx" } | 33 | stdx = { path = "../stdx" } |
34 | 34 | ||
35 | lsp-server = "0.3.1" | 35 | lsp-server = "0.3.1" |
36 | ra_cargo_watch = { path = "../ra_cargo_watch" } | 36 | ra_flycheck = { path = "../ra_flycheck" } |
37 | ra_ide = { path = "../ra_ide" } | 37 | ra_ide = { path = "../ra_ide" } |
38 | ra_prof = { path = "../ra_prof" } | 38 | ra_prof = { path = "../ra_prof" } |
39 | ra_project_model = { path = "../ra_project_model" } | 39 | ra_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 | }; |
15 | use ra_ide::{Analysis, AnalysisChange, AnalysisHost, CompletionOptions, FilePosition, LineCol}; | 15 | use ra_ide::{Analysis, AnalysisChange, AnalysisHost, CompletionConfig, FilePosition, LineCol}; |
16 | 16 | ||
17 | use crate::cli::{load_cargo::load_cargo, Verbosity}; | 17 | use 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 | ||
17 | use crossbeam_channel::{select, unbounded, RecvError, Sender}; | 17 | use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; |
18 | use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; | 18 | use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; |
19 | use lsp_types::{ | 19 | use lsp_types::{ |
20 | ClientCapabilities, NumberOrString, WorkDoneProgress, WorkDoneProgressBegin, | 20 | ClientCapabilities, NumberOrString, TextDocumentClientCapabilities, WorkDoneProgress, |
21 | WorkDoneProgressCreateParams, WorkDoneProgressEnd, WorkDoneProgressReport, | 21 | WorkDoneProgressBegin, WorkDoneProgressCreateParams, WorkDoneProgressEnd, |
22 | WorkDoneProgressReport, | ||
22 | }; | 23 | }; |
23 | use ra_cargo_watch::{url_from_path_with_drive_lowercasing, CheckOptions, CheckTask}; | 24 | use ra_flycheck::{url_from_path_with_drive_lowercasing, CheckConfig, CheckTask}; |
24 | use ra_ide::{Canceled, FileId, InlayHintsOptions, LibraryData, SourceRootId}; | 25 | use ra_ide::{Canceled, FileId, InlayHintsConfig, LibraryData, SourceRootId}; |
25 | use ra_prof::profile; | 26 | use ra_prof::profile; |
26 | use ra_vfs::{VfsFile, VfsTask, Watch}; | 27 | use ra_vfs::{VfsFile, VfsTask, Watch}; |
27 | use relative_path::RelativePathBuf; | 28 | use 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 | }; |
44 | use req::ConfigurationParams; | ||
43 | 45 | ||
44 | #[derive(Debug)] | 46 | #[derive(Debug)] |
45 | pub struct LspError { | 47 | pub struct LspError { |
@@ -63,6 +65,57 @@ impl fmt::Display for LspError { | |||
63 | 65 | ||
64 | impl Error for LspError {} | 66 | impl Error for LspError {} |
65 | 67 | ||
68 | fn 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 | |||
84 | fn 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 | |||
66 | pub fn main_loop( | 119 | pub 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 | ||
339 | impl LoopState { | 357 | impl 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( | |||
556 | fn on_notification( | 610 | fn 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 | ||
709 | fn send_startup_progress( | 774 | fn 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 | ||
760 | fn 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 | ||
768 | struct PoolDispatcher<'a> { | 825 | struct 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 | }; |
21 | use ra_ide::{ | 21 | use 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 | }; |
25 | use ra_prof::profile; | 25 | use 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 | ||
7 | pub use lsp_types::{ | 7 | pub 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 | ||
3 | use std::ops; | 3 | use std::ops; |
4 | 4 | ||
5 | use lsp_types::{Range, SemanticToken, SemanticTokenModifier, SemanticTokenType}; | 5 | use lsp_types::{Range, SemanticToken, SemanticTokenModifier, SemanticTokenType, SemanticTokens}; |
6 | 6 | ||
7 | pub(crate) const ATTRIBUTE: SemanticTokenType = SemanticTokenType::new("attribute"); | 7 | pub(crate) const ATTRIBUTE: SemanticTokenType = SemanticTokenType::new("attribute"); |
8 | pub(crate) const BUILTIN_TYPE: SemanticTokenType = SemanticTokenType::new("builtinType"); | 8 | pub(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::{ | |||
11 | use crossbeam_channel::{unbounded, Receiver}; | 11 | use crossbeam_channel::{unbounded, Receiver}; |
12 | use lsp_types::Url; | 12 | use lsp_types::Url; |
13 | use parking_lot::RwLock; | 13 | use parking_lot::RwLock; |
14 | use ra_cargo_watch::{url_from_path_with_drive_lowercasing, CheckOptions, CheckWatcher}; | 14 | use ra_flycheck::{url_from_path_with_drive_lowercasing, CheckConfig, Flycheck}; |
15 | use ra_ide::{ | 15 | use ra_ide::{ |
16 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, InlayHintsOptions, LibraryData, | 16 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, InlayHintsConfig, LibraryData, |
17 | SourceRootId, | 17 | SourceRootId, |
18 | }; | 18 | }; |
19 | use ra_project_model::{get_rustc_cfg_options, ProcMacroClient, ProjectWorkspace}; | 19 | use ra_project_model::{get_rustc_cfg_options, ProcMacroClient, ProjectWorkspace}; |
@@ -31,15 +31,36 @@ use crate::{ | |||
31 | use ra_db::ExternSourceId; | 31 | use ra_db::ExternSourceId; |
32 | use rustc_hash::{FxHashMap, FxHashSet}; | 32 | use rustc_hash::{FxHashMap, FxHashSet}; |
33 | 33 | ||
34 | fn 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)] |
35 | pub struct Options { | 55 | pub 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)] |
51 | pub struct WorldState { | 72 | pub 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. |
67 | pub struct WorldSnapshot { | 86 | pub 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 | }; |
10 | use rust_analyzer::req::{ | 10 | use 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 | }; |
14 | use serde_json::json; | 14 | use serde_json::json; |
15 | use tempfile::TempDir; | 15 | use tempfile::TempDir; |
@@ -581,3 +581,47 @@ version = \"0.0.0\" | |||
581 | }), | 581 | }), |
582 | ); | 582 | ); |
583 | } | 583 | } |
584 | |||
585 | #[test] | ||
586 | fn 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] | ||
595 | name = "foo" | ||
596 | version = "0.0.0" | ||
597 | |||
598 | //- build.rs | ||
599 | use std::{env, fs, path::Path}; | ||
600 | |||
601 | fn 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 | ||
612 | include!(concat!(env!("OUT_DIR"), "/hello.rs")); | ||
613 | |||
614 | fn 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 | ||
32 | impl<'a> Project<'a> { | 33 | impl<'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 { | |||
83 | pub struct Server { | 96 | pub 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 | ||
91 | impl Server { | 105 | impl 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] |
7 | macro_rules! format_to { | 7 | macro_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 | }; |