diff options
Diffstat (limited to 'crates')
29 files changed, 753 insertions, 414 deletions
diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index 4dcab7a61..92ec4f92e 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs | |||
@@ -7,7 +7,7 @@ use std::{ | |||
7 | io::{self, BufReader}, | 7 | io::{self, BufReader}, |
8 | path::PathBuf, | 8 | path::PathBuf, |
9 | process::{Command, Stdio}, | 9 | process::{Command, Stdio}, |
10 | time::Instant, | 10 | time::Duration, |
11 | }; | 11 | }; |
12 | 12 | ||
13 | use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; | 13 | use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; |
@@ -74,9 +74,6 @@ impl FlycheckHandle { | |||
74 | 74 | ||
75 | #[derive(Debug)] | 75 | #[derive(Debug)] |
76 | pub enum Message { | 76 | pub enum Message { |
77 | /// Request a clearing of all cached diagnostics from the check watcher | ||
78 | ClearDiagnostics, | ||
79 | |||
80 | /// Request adding a diagnostic with fixes included to a file | 77 | /// Request adding a diagnostic with fixes included to a file |
81 | AddDiagnostic { workspace_root: PathBuf, diagnostic: Diagnostic }, | 78 | AddDiagnostic { workspace_root: PathBuf, diagnostic: Diagnostic }, |
82 | 79 | ||
@@ -86,9 +83,10 @@ pub enum Message { | |||
86 | 83 | ||
87 | #[derive(Debug)] | 84 | #[derive(Debug)] |
88 | pub enum Progress { | 85 | pub enum Progress { |
89 | Being, | 86 | DidStart, |
90 | DidCheckCrate(String), | 87 | DidCheckCrate(String), |
91 | End, | 88 | DidFinish, |
89 | DidCancel, | ||
92 | } | 90 | } |
93 | 91 | ||
94 | struct Restart; | 92 | struct Restart; |
@@ -97,19 +95,18 @@ struct FlycheckActor { | |||
97 | sender: Box<dyn Fn(Message) + Send>, | 95 | sender: Box<dyn Fn(Message) + Send>, |
98 | config: FlycheckConfig, | 96 | config: FlycheckConfig, |
99 | workspace_root: PathBuf, | 97 | workspace_root: PathBuf, |
100 | last_update_req: Option<Instant>, | ||
101 | /// WatchThread exists to wrap around the communication needed to be able to | 98 | /// WatchThread exists to wrap around the communication needed to be able to |
102 | /// run `cargo check` without blocking. Currently the Rust standard library | 99 | /// run `cargo check` without blocking. Currently the Rust standard library |
103 | /// doesn't provide a way to read sub-process output without blocking, so we | 100 | /// doesn't provide a way to read sub-process output without blocking, so we |
104 | /// have to wrap sub-processes output handling in a thread and pass messages | 101 | /// have to wrap sub-processes output handling in a thread and pass messages |
105 | /// back over a channel. | 102 | /// back over a channel. |
106 | // XXX: drop order is significant | 103 | // XXX: drop order is significant |
107 | check_process: Option<(Receiver<CheckEvent>, jod_thread::JoinHandle)>, | 104 | check_process: Option<(Receiver<cargo_metadata::Message>, jod_thread::JoinHandle)>, |
108 | } | 105 | } |
109 | 106 | ||
110 | enum Event { | 107 | enum Event { |
111 | Restart(Restart), | 108 | Restart(Restart), |
112 | CheckEvent(Option<CheckEvent>), | 109 | CheckEvent(Option<cargo_metadata::Message>), |
113 | } | 110 | } |
114 | 111 | ||
115 | impl FlycheckActor { | 112 | impl FlycheckActor { |
@@ -118,7 +115,7 @@ impl FlycheckActor { | |||
118 | config: FlycheckConfig, | 115 | config: FlycheckConfig, |
119 | workspace_root: PathBuf, | 116 | workspace_root: PathBuf, |
120 | ) -> FlycheckActor { | 117 | ) -> FlycheckActor { |
121 | FlycheckActor { sender, config, workspace_root, last_update_req: None, check_process: None } | 118 | FlycheckActor { sender, config, workspace_root, check_process: None } |
122 | } | 119 | } |
123 | fn next_event(&self, inbox: &Receiver<Restart>) -> Option<Event> { | 120 | fn next_event(&self, inbox: &Receiver<Restart>) -> Option<Event> { |
124 | let check_chan = self.check_process.as_ref().map(|(chan, _thread)| chan); | 121 | let check_chan = self.check_process.as_ref().map(|(chan, _thread)| chan); |
@@ -128,65 +125,48 @@ impl FlycheckActor { | |||
128 | } | 125 | } |
129 | } | 126 | } |
130 | fn run(&mut self, inbox: Receiver<Restart>) { | 127 | fn run(&mut self, inbox: Receiver<Restart>) { |
131 | // If we rerun the thread, we need to discard the previous check results first | ||
132 | self.send(Message::ClearDiagnostics); | ||
133 | self.send(Message::Progress(Progress::End)); | ||
134 | |||
135 | while let Some(event) = self.next_event(&inbox) { | 128 | while let Some(event) = self.next_event(&inbox) { |
136 | match event { | 129 | match event { |
137 | Event::Restart(Restart) => self.last_update_req = Some(Instant::now()), | 130 | Event::Restart(Restart) => { |
131 | while let Ok(Restart) = inbox.recv_timeout(Duration::from_millis(50)) {} | ||
132 | self.cancel_check_process(); | ||
133 | self.check_process = Some(self.start_check_process()); | ||
134 | self.send(Message::Progress(Progress::DidStart)); | ||
135 | } | ||
138 | Event::CheckEvent(None) => { | 136 | Event::CheckEvent(None) => { |
139 | // Watcher finished, replace it with a never channel to | 137 | // Watcher finished, replace it with a never channel to |
140 | // avoid busy-waiting. | 138 | // avoid busy-waiting. |
141 | self.check_process = None; | 139 | assert!(self.check_process.take().is_some()); |
140 | self.send(Message::Progress(Progress::DidFinish)); | ||
142 | } | 141 | } |
143 | Event::CheckEvent(Some(event)) => match event { | 142 | Event::CheckEvent(Some(message)) => match message { |
144 | CheckEvent::Begin => { | 143 | cargo_metadata::Message::CompilerArtifact(msg) => { |
145 | self.send(Message::Progress(Progress::Being)); | ||
146 | } | ||
147 | |||
148 | CheckEvent::End => { | ||
149 | self.send(Message::Progress(Progress::End)); | ||
150 | } | ||
151 | |||
152 | CheckEvent::Msg(cargo_metadata::Message::CompilerArtifact(msg)) => { | ||
153 | self.send(Message::Progress(Progress::DidCheckCrate(msg.target.name))); | 144 | self.send(Message::Progress(Progress::DidCheckCrate(msg.target.name))); |
154 | } | 145 | } |
155 | 146 | ||
156 | CheckEvent::Msg(cargo_metadata::Message::CompilerMessage(msg)) => { | 147 | cargo_metadata::Message::CompilerMessage(msg) => { |
157 | self.send(Message::AddDiagnostic { | 148 | self.send(Message::AddDiagnostic { |
158 | workspace_root: self.workspace_root.clone(), | 149 | workspace_root: self.workspace_root.clone(), |
159 | diagnostic: msg.message, | 150 | diagnostic: msg.message, |
160 | }); | 151 | }); |
161 | } | 152 | } |
162 | 153 | ||
163 | CheckEvent::Msg(cargo_metadata::Message::BuildScriptExecuted(_)) | 154 | cargo_metadata::Message::BuildScriptExecuted(_) |
164 | | CheckEvent::Msg(cargo_metadata::Message::BuildFinished(_)) | 155 | | cargo_metadata::Message::BuildFinished(_) |
165 | | CheckEvent::Msg(cargo_metadata::Message::TextLine(_)) | 156 | | cargo_metadata::Message::TextLine(_) |
166 | | CheckEvent::Msg(cargo_metadata::Message::Unknown) => {} | 157 | | cargo_metadata::Message::Unknown => {} |
167 | }, | 158 | }, |
168 | } | 159 | } |
169 | if self.should_recheck() { | ||
170 | self.last_update_req = None; | ||
171 | self.send(Message::ClearDiagnostics); | ||
172 | self.restart_check_process(); | ||
173 | } | ||
174 | } | 160 | } |
161 | // If we rerun the thread, we need to discard the previous check results first | ||
162 | self.cancel_check_process(); | ||
175 | } | 163 | } |
176 | fn should_recheck(&mut self) -> bool { | 164 | fn cancel_check_process(&mut self) { |
177 | if let Some(_last_update_req) = &self.last_update_req { | 165 | if self.check_process.take().is_some() { |
178 | // We currently only request an update on save, as we need up to | 166 | self.send(Message::Progress(Progress::DidCancel)); |
179 | // date source on disk for cargo check to do it's magic, so we | ||
180 | // don't really need to debounce the requests at this point. | ||
181 | return true; | ||
182 | } | 167 | } |
183 | false | ||
184 | } | 168 | } |
185 | 169 | fn start_check_process(&self) -> (Receiver<cargo_metadata::Message>, jod_thread::JoinHandle) { | |
186 | fn restart_check_process(&mut self) { | ||
187 | // First, clear and cancel the old thread | ||
188 | self.check_process = None; | ||
189 | |||
190 | let mut cmd = match &self.config { | 170 | let mut cmd = match &self.config { |
191 | FlycheckConfig::CargoCommand { | 171 | FlycheckConfig::CargoCommand { |
192 | command, | 172 | command, |
@@ -223,8 +203,6 @@ impl FlycheckActor { | |||
223 | let thread = jod_thread::spawn(move || { | 203 | let thread = jod_thread::spawn(move || { |
224 | // If we trigger an error here, we will do so in the loop instead, | 204 | // If we trigger an error here, we will do so in the loop instead, |
225 | // which will break out of the loop, and continue the shutdown | 205 | // which will break out of the loop, and continue the shutdown |
226 | let _ = message_send.send(CheckEvent::Begin); | ||
227 | |||
228 | let res = run_cargo(cmd, &mut |message| { | 206 | let res = run_cargo(cmd, &mut |message| { |
229 | // Skip certain kinds of messages to only spend time on what's useful | 207 | // Skip certain kinds of messages to only spend time on what's useful |
230 | match &message { | 208 | match &message { |
@@ -237,7 +215,7 @@ impl FlycheckActor { | |||
237 | } | 215 | } |
238 | 216 | ||
239 | // if the send channel was closed, we want to shutdown | 217 | // if the send channel was closed, we want to shutdown |
240 | message_send.send(CheckEvent::Msg(message)).is_ok() | 218 | message_send.send(message).is_ok() |
241 | }); | 219 | }); |
242 | 220 | ||
243 | if let Err(err) = res { | 221 | if let Err(err) = res { |
@@ -245,12 +223,8 @@ impl FlycheckActor { | |||
245 | // to display user-caused misconfiguration errors instead of just logging them here | 223 | // to display user-caused misconfiguration errors instead of just logging them here |
246 | log::error!("Cargo watcher failed {:?}", err); | 224 | log::error!("Cargo watcher failed {:?}", err); |
247 | } | 225 | } |
248 | |||
249 | // We can ignore any error here, as we are already in the progress | ||
250 | // of shutting down. | ||
251 | let _ = message_send.send(CheckEvent::End); | ||
252 | }); | 226 | }); |
253 | self.check_process = Some((message_recv, thread)) | 227 | (message_recv, thread) |
254 | } | 228 | } |
255 | 229 | ||
256 | fn send(&self, check_task: Message) { | 230 | fn send(&self, check_task: Message) { |
@@ -258,12 +232,6 @@ impl FlycheckActor { | |||
258 | } | 232 | } |
259 | } | 233 | } |
260 | 234 | ||
261 | enum CheckEvent { | ||
262 | Begin, | ||
263 | Msg(cargo_metadata::Message), | ||
264 | End, | ||
265 | } | ||
266 | |||
267 | fn run_cargo( | 235 | fn run_cargo( |
268 | mut command: Command, | 236 | mut command: Command, |
269 | on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool, | 237 | on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool, |
diff --git a/crates/ra_assists/src/handlers/change_return_type_to_result.rs b/crates/ra_assists/src/handlers/change_return_type_to_result.rs index c6baa0a57..855baf187 100644 --- a/crates/ra_assists/src/handlers/change_return_type_to_result.rs +++ b/crates/ra_assists/src/handlers/change_return_type_to_result.rs | |||
@@ -4,6 +4,7 @@ use ra_syntax::{ | |||
4 | }; | 4 | }; |
5 | 5 | ||
6 | use crate::{AssistContext, AssistId, Assists}; | 6 | use crate::{AssistContext, AssistId, Assists}; |
7 | use test_utils::mark; | ||
7 | 8 | ||
8 | // Assist: change_return_type_to_result | 9 | // Assist: change_return_type_to_result |
9 | // | 10 | // |
@@ -22,8 +23,13 @@ pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContex | |||
22 | let fn_def = ret_type.syntax().parent().and_then(ast::FnDef::cast)?; | 23 | let fn_def = ret_type.syntax().parent().and_then(ast::FnDef::cast)?; |
23 | 24 | ||
24 | let type_ref = &ret_type.type_ref()?; | 25 | let type_ref = &ret_type.type_ref()?; |
25 | if type_ref.syntax().text().to_string().starts_with("Result<") { | 26 | let ret_type_str = type_ref.syntax().text().to_string(); |
26 | return None; | 27 | let first_part_ret_type = ret_type_str.splitn(2, '<').next(); |
28 | if let Some(ret_type_first_part) = first_part_ret_type { | ||
29 | if ret_type_first_part.ends_with("Result") { | ||
30 | mark::hit!(change_return_type_to_result_simple_return_type_already_result); | ||
31 | return None; | ||
32 | } | ||
27 | } | 33 | } |
28 | 34 | ||
29 | let block_expr = &fn_def.body()?; | 35 | let block_expr = &fn_def.body()?; |
@@ -297,6 +303,29 @@ mod tests { | |||
297 | } | 303 | } |
298 | 304 | ||
299 | #[test] | 305 | #[test] |
306 | fn change_return_type_to_result_simple_return_type_already_result_std() { | ||
307 | check_assist_not_applicable( | ||
308 | change_return_type_to_result, | ||
309 | r#"fn foo() -> std::result::Result<i32<|>, String> { | ||
310 | let test = "test"; | ||
311 | return 42i32; | ||
312 | }"#, | ||
313 | ); | ||
314 | } | ||
315 | |||
316 | #[test] | ||
317 | fn change_return_type_to_result_simple_return_type_already_result() { | ||
318 | mark::check!(change_return_type_to_result_simple_return_type_already_result); | ||
319 | check_assist_not_applicable( | ||
320 | change_return_type_to_result, | ||
321 | r#"fn foo() -> Result<i32<|>, String> { | ||
322 | let test = "test"; | ||
323 | return 42i32; | ||
324 | }"#, | ||
325 | ); | ||
326 | } | ||
327 | |||
328 | #[test] | ||
300 | fn change_return_type_to_result_simple_with_cursor() { | 329 | fn change_return_type_to_result_simple_with_cursor() { |
301 | check_assist( | 330 | check_assist( |
302 | change_return_type_to_result, | 331 | change_return_type_to_result, |
diff --git a/crates/ra_assists/src/handlers/introduce_variable.rs b/crates/ra_assists/src/handlers/extract_variable.rs index 96affe49d..c4150d2bb 100644 --- a/crates/ra_assists/src/handlers/introduce_variable.rs +++ b/crates/ra_assists/src/handlers/extract_variable.rs | |||
@@ -11,7 +11,7 @@ use test_utils::mark; | |||
11 | 11 | ||
12 | use crate::{AssistContext, AssistId, Assists}; | 12 | use crate::{AssistContext, AssistId, Assists}; |
13 | 13 | ||
14 | // Assist: introduce_variable | 14 | // Assist: extract_variable |
15 | // | 15 | // |
16 | // Extracts subexpression into a variable. | 16 | // Extracts subexpression into a variable. |
17 | // | 17 | // |
@@ -27,13 +27,13 @@ use crate::{AssistContext, AssistId, Assists}; | |||
27 | // var_name * 4; | 27 | // var_name * 4; |
28 | // } | 28 | // } |
29 | // ``` | 29 | // ``` |
30 | pub(crate) fn introduce_variable(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 30 | pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
31 | if ctx.frange.range.is_empty() { | 31 | if ctx.frange.range.is_empty() { |
32 | return None; | 32 | return None; |
33 | } | 33 | } |
34 | let node = ctx.covering_element(); | 34 | let node = ctx.covering_element(); |
35 | if node.kind() == COMMENT { | 35 | if node.kind() == COMMENT { |
36 | mark::hit!(introduce_var_in_comment_is_not_applicable); | 36 | mark::hit!(extract_var_in_comment_is_not_applicable); |
37 | return None; | 37 | return None; |
38 | } | 38 | } |
39 | let expr = node.ancestors().find_map(valid_target_expr)?; | 39 | let expr = node.ancestors().find_map(valid_target_expr)?; |
@@ -43,7 +43,7 @@ pub(crate) fn introduce_variable(acc: &mut Assists, ctx: &AssistContext) -> Opti | |||
43 | return None; | 43 | return None; |
44 | } | 44 | } |
45 | let target = expr.syntax().text_range(); | 45 | let target = expr.syntax().text_range(); |
46 | acc.add(AssistId("introduce_variable"), "Extract into variable", target, move |edit| { | 46 | acc.add(AssistId("extract_variable"), "Extract into variable", target, move |edit| { |
47 | let field_shorthand = match expr.syntax().parent().and_then(ast::RecordField::cast) { | 47 | let field_shorthand = match expr.syntax().parent().and_then(ast::RecordField::cast) { |
48 | Some(field) => field.name_ref(), | 48 | Some(field) => field.name_ref(), |
49 | None => None, | 49 | None => None, |
@@ -74,7 +74,7 @@ pub(crate) fn introduce_variable(acc: &mut Assists, ctx: &AssistContext) -> Opti | |||
74 | false | 74 | false |
75 | }; | 75 | }; |
76 | if is_full_stmt { | 76 | if is_full_stmt { |
77 | mark::hit!(test_introduce_var_expr_stmt); | 77 | mark::hit!(test_extract_var_expr_stmt); |
78 | if full_stmt.unwrap().semicolon_token().is_none() { | 78 | if full_stmt.unwrap().semicolon_token().is_none() { |
79 | buf.push_str(";"); | 79 | buf.push_str(";"); |
80 | } | 80 | } |
@@ -133,7 +133,7 @@ fn valid_target_expr(node: SyntaxNode) -> Option<ast::Expr> { | |||
133 | } | 133 | } |
134 | } | 134 | } |
135 | 135 | ||
136 | /// Returns the syntax node which will follow the freshly introduced var | 136 | /// Returns the syntax node which will follow the freshly extractd var |
137 | /// and a boolean indicating whether we have to wrap it within a { } block | 137 | /// and a boolean indicating whether we have to wrap it within a { } block |
138 | /// to produce correct code. | 138 | /// to produce correct code. |
139 | /// It can be a statement, the last in a block expression or a wanna be block | 139 | /// It can be a statement, the last in a block expression or a wanna be block |
@@ -142,7 +142,7 @@ fn anchor_stmt(expr: ast::Expr) -> Option<(SyntaxNode, bool)> { | |||
142 | expr.syntax().ancestors().find_map(|node| { | 142 | expr.syntax().ancestors().find_map(|node| { |
143 | if let Some(expr) = node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.expr()) { | 143 | if let Some(expr) = node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.expr()) { |
144 | if expr.syntax() == &node { | 144 | if expr.syntax() == &node { |
145 | mark::hit!(test_introduce_var_last_expr); | 145 | mark::hit!(test_extract_var_last_expr); |
146 | return Some((node, false)); | 146 | return Some((node, false)); |
147 | } | 147 | } |
148 | } | 148 | } |
@@ -170,9 +170,9 @@ mod tests { | |||
170 | use super::*; | 170 | use super::*; |
171 | 171 | ||
172 | #[test] | 172 | #[test] |
173 | fn test_introduce_var_simple() { | 173 | fn test_extract_var_simple() { |
174 | check_assist( | 174 | check_assist( |
175 | introduce_variable, | 175 | extract_variable, |
176 | r#" | 176 | r#" |
177 | fn foo() { | 177 | fn foo() { |
178 | foo(<|>1 + 1<|>); | 178 | foo(<|>1 + 1<|>); |
@@ -186,16 +186,16 @@ fn foo() { | |||
186 | } | 186 | } |
187 | 187 | ||
188 | #[test] | 188 | #[test] |
189 | fn introduce_var_in_comment_is_not_applicable() { | 189 | fn extract_var_in_comment_is_not_applicable() { |
190 | mark::check!(introduce_var_in_comment_is_not_applicable); | 190 | mark::check!(extract_var_in_comment_is_not_applicable); |
191 | check_assist_not_applicable(introduce_variable, "fn main() { 1 + /* <|>comment<|> */ 1; }"); | 191 | check_assist_not_applicable(extract_variable, "fn main() { 1 + /* <|>comment<|> */ 1; }"); |
192 | } | 192 | } |
193 | 193 | ||
194 | #[test] | 194 | #[test] |
195 | fn test_introduce_var_expr_stmt() { | 195 | fn test_extract_var_expr_stmt() { |
196 | mark::check!(test_introduce_var_expr_stmt); | 196 | mark::check!(test_extract_var_expr_stmt); |
197 | check_assist( | 197 | check_assist( |
198 | introduce_variable, | 198 | extract_variable, |
199 | r#" | 199 | r#" |
200 | fn foo() { | 200 | fn foo() { |
201 | <|>1 + 1<|>; | 201 | <|>1 + 1<|>; |
@@ -206,7 +206,7 @@ fn foo() { | |||
206 | }"#, | 206 | }"#, |
207 | ); | 207 | ); |
208 | check_assist( | 208 | check_assist( |
209 | introduce_variable, | 209 | extract_variable, |
210 | " | 210 | " |
211 | fn foo() { | 211 | fn foo() { |
212 | <|>{ let x = 0; x }<|> | 212 | <|>{ let x = 0; x }<|> |
@@ -221,9 +221,9 @@ fn foo() { | |||
221 | } | 221 | } |
222 | 222 | ||
223 | #[test] | 223 | #[test] |
224 | fn test_introduce_var_part_of_expr_stmt() { | 224 | fn test_extract_var_part_of_expr_stmt() { |
225 | check_assist( | 225 | check_assist( |
226 | introduce_variable, | 226 | extract_variable, |
227 | " | 227 | " |
228 | fn foo() { | 228 | fn foo() { |
229 | <|>1<|> + 1; | 229 | <|>1<|> + 1; |
@@ -237,10 +237,10 @@ fn foo() { | |||
237 | } | 237 | } |
238 | 238 | ||
239 | #[test] | 239 | #[test] |
240 | fn test_introduce_var_last_expr() { | 240 | fn test_extract_var_last_expr() { |
241 | mark::check!(test_introduce_var_last_expr); | 241 | mark::check!(test_extract_var_last_expr); |
242 | check_assist( | 242 | check_assist( |
243 | introduce_variable, | 243 | extract_variable, |
244 | r#" | 244 | r#" |
245 | fn foo() { | 245 | fn foo() { |
246 | bar(<|>1 + 1<|>) | 246 | bar(<|>1 + 1<|>) |
@@ -254,7 +254,7 @@ fn foo() { | |||
254 | "#, | 254 | "#, |
255 | ); | 255 | ); |
256 | check_assist( | 256 | check_assist( |
257 | introduce_variable, | 257 | extract_variable, |
258 | r#" | 258 | r#" |
259 | fn foo() { | 259 | fn foo() { |
260 | <|>bar(1 + 1)<|> | 260 | <|>bar(1 + 1)<|> |
@@ -270,9 +270,9 @@ fn foo() { | |||
270 | } | 270 | } |
271 | 271 | ||
272 | #[test] | 272 | #[test] |
273 | fn test_introduce_var_in_match_arm_no_block() { | 273 | fn test_extract_var_in_match_arm_no_block() { |
274 | check_assist( | 274 | check_assist( |
275 | introduce_variable, | 275 | extract_variable, |
276 | " | 276 | " |
277 | fn main() { | 277 | fn main() { |
278 | let x = true; | 278 | let x = true; |
@@ -295,9 +295,9 @@ fn main() { | |||
295 | } | 295 | } |
296 | 296 | ||
297 | #[test] | 297 | #[test] |
298 | fn test_introduce_var_in_match_arm_with_block() { | 298 | fn test_extract_var_in_match_arm_with_block() { |
299 | check_assist( | 299 | check_assist( |
300 | introduce_variable, | 300 | extract_variable, |
301 | " | 301 | " |
302 | fn main() { | 302 | fn main() { |
303 | let x = true; | 303 | let x = true; |
@@ -327,9 +327,9 @@ fn main() { | |||
327 | } | 327 | } |
328 | 328 | ||
329 | #[test] | 329 | #[test] |
330 | fn test_introduce_var_in_closure_no_block() { | 330 | fn test_extract_var_in_closure_no_block() { |
331 | check_assist( | 331 | check_assist( |
332 | introduce_variable, | 332 | extract_variable, |
333 | " | 333 | " |
334 | fn main() { | 334 | fn main() { |
335 | let lambda = |x: u32| <|>x * 2<|>; | 335 | let lambda = |x: u32| <|>x * 2<|>; |
@@ -344,9 +344,9 @@ fn main() { | |||
344 | } | 344 | } |
345 | 345 | ||
346 | #[test] | 346 | #[test] |
347 | fn test_introduce_var_in_closure_with_block() { | 347 | fn test_extract_var_in_closure_with_block() { |
348 | check_assist( | 348 | check_assist( |
349 | introduce_variable, | 349 | extract_variable, |
350 | " | 350 | " |
351 | fn main() { | 351 | fn main() { |
352 | let lambda = |x: u32| { <|>x * 2<|> }; | 352 | let lambda = |x: u32| { <|>x * 2<|> }; |
@@ -361,9 +361,9 @@ fn main() { | |||
361 | } | 361 | } |
362 | 362 | ||
363 | #[test] | 363 | #[test] |
364 | fn test_introduce_var_path_simple() { | 364 | fn test_extract_var_path_simple() { |
365 | check_assist( | 365 | check_assist( |
366 | introduce_variable, | 366 | extract_variable, |
367 | " | 367 | " |
368 | fn main() { | 368 | fn main() { |
369 | let o = <|>Some(true)<|>; | 369 | let o = <|>Some(true)<|>; |
@@ -379,9 +379,9 @@ fn main() { | |||
379 | } | 379 | } |
380 | 380 | ||
381 | #[test] | 381 | #[test] |
382 | fn test_introduce_var_path_method() { | 382 | fn test_extract_var_path_method() { |
383 | check_assist( | 383 | check_assist( |
384 | introduce_variable, | 384 | extract_variable, |
385 | " | 385 | " |
386 | fn main() { | 386 | fn main() { |
387 | let v = <|>bar.foo()<|>; | 387 | let v = <|>bar.foo()<|>; |
@@ -397,9 +397,9 @@ fn main() { | |||
397 | } | 397 | } |
398 | 398 | ||
399 | #[test] | 399 | #[test] |
400 | fn test_introduce_var_return() { | 400 | fn test_extract_var_return() { |
401 | check_assist( | 401 | check_assist( |
402 | introduce_variable, | 402 | extract_variable, |
403 | " | 403 | " |
404 | fn foo() -> u32 { | 404 | fn foo() -> u32 { |
405 | <|>return 2 + 2<|>; | 405 | <|>return 2 + 2<|>; |
@@ -415,9 +415,9 @@ fn foo() -> u32 { | |||
415 | } | 415 | } |
416 | 416 | ||
417 | #[test] | 417 | #[test] |
418 | fn test_introduce_var_does_not_add_extra_whitespace() { | 418 | fn test_extract_var_does_not_add_extra_whitespace() { |
419 | check_assist( | 419 | check_assist( |
420 | introduce_variable, | 420 | extract_variable, |
421 | " | 421 | " |
422 | fn foo() -> u32 { | 422 | fn foo() -> u32 { |
423 | 423 | ||
@@ -436,7 +436,7 @@ fn foo() -> u32 { | |||
436 | ); | 436 | ); |
437 | 437 | ||
438 | check_assist( | 438 | check_assist( |
439 | introduce_variable, | 439 | extract_variable, |
440 | " | 440 | " |
441 | fn foo() -> u32 { | 441 | fn foo() -> u32 { |
442 | 442 | ||
@@ -453,7 +453,7 @@ fn foo() -> u32 { | |||
453 | ); | 453 | ); |
454 | 454 | ||
455 | check_assist( | 455 | check_assist( |
456 | introduce_variable, | 456 | extract_variable, |
457 | " | 457 | " |
458 | fn foo() -> u32 { | 458 | fn foo() -> u32 { |
459 | let foo = 1; | 459 | let foo = 1; |
@@ -479,9 +479,9 @@ fn foo() -> u32 { | |||
479 | } | 479 | } |
480 | 480 | ||
481 | #[test] | 481 | #[test] |
482 | fn test_introduce_var_break() { | 482 | fn test_extract_var_break() { |
483 | check_assist( | 483 | check_assist( |
484 | introduce_variable, | 484 | extract_variable, |
485 | " | 485 | " |
486 | fn main() { | 486 | fn main() { |
487 | let result = loop { | 487 | let result = loop { |
@@ -501,9 +501,9 @@ fn main() { | |||
501 | } | 501 | } |
502 | 502 | ||
503 | #[test] | 503 | #[test] |
504 | fn test_introduce_var_for_cast() { | 504 | fn test_extract_var_for_cast() { |
505 | check_assist( | 505 | check_assist( |
506 | introduce_variable, | 506 | extract_variable, |
507 | " | 507 | " |
508 | fn main() { | 508 | fn main() { |
509 | let v = <|>0f32 as u32<|>; | 509 | let v = <|>0f32 as u32<|>; |
@@ -519,9 +519,9 @@ fn main() { | |||
519 | } | 519 | } |
520 | 520 | ||
521 | #[test] | 521 | #[test] |
522 | fn introduce_var_field_shorthand() { | 522 | fn extract_var_field_shorthand() { |
523 | check_assist( | 523 | check_assist( |
524 | introduce_variable, | 524 | extract_variable, |
525 | r#" | 525 | r#" |
526 | struct S { | 526 | struct S { |
527 | foo: i32 | 527 | foo: i32 |
@@ -545,22 +545,22 @@ fn main() { | |||
545 | } | 545 | } |
546 | 546 | ||
547 | #[test] | 547 | #[test] |
548 | fn test_introduce_var_for_return_not_applicable() { | 548 | fn test_extract_var_for_return_not_applicable() { |
549 | check_assist_not_applicable(introduce_variable, "fn foo() { <|>return<|>; } "); | 549 | check_assist_not_applicable(extract_variable, "fn foo() { <|>return<|>; } "); |
550 | } | 550 | } |
551 | 551 | ||
552 | #[test] | 552 | #[test] |
553 | fn test_introduce_var_for_break_not_applicable() { | 553 | fn test_extract_var_for_break_not_applicable() { |
554 | check_assist_not_applicable(introduce_variable, "fn main() { loop { <|>break<|>; }; }"); | 554 | check_assist_not_applicable(extract_variable, "fn main() { loop { <|>break<|>; }; }"); |
555 | } | 555 | } |
556 | 556 | ||
557 | // FIXME: This is not quite correct, but good enough(tm) for the sorting heuristic | 557 | // FIXME: This is not quite correct, but good enough(tm) for the sorting heuristic |
558 | #[test] | 558 | #[test] |
559 | fn introduce_var_target() { | 559 | fn extract_var_target() { |
560 | check_assist_target(introduce_variable, "fn foo() -> u32 { <|>return 2 + 2<|>; }", "2 + 2"); | 560 | check_assist_target(extract_variable, "fn foo() -> u32 { <|>return 2 + 2<|>; }", "2 + 2"); |
561 | 561 | ||
562 | check_assist_target( | 562 | check_assist_target( |
563 | introduce_variable, | 563 | extract_variable, |
564 | " | 564 | " |
565 | fn main() { | 565 | fn main() { |
566 | let x = true; | 566 | let x = true; |
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 185428bd5..1745f44a5 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -116,6 +116,7 @@ mod handlers { | |||
116 | mod change_visibility; | 116 | mod change_visibility; |
117 | mod early_return; | 117 | mod early_return; |
118 | mod extract_struct_from_enum_variant; | 118 | mod extract_struct_from_enum_variant; |
119 | mod extract_variable; | ||
119 | mod fill_match_arms; | 120 | mod fill_match_arms; |
120 | mod fix_visibility; | 121 | mod fix_visibility; |
121 | mod flip_binexpr; | 122 | mod flip_binexpr; |
@@ -123,7 +124,6 @@ mod handlers { | |||
123 | mod flip_trait_bound; | 124 | mod flip_trait_bound; |
124 | mod inline_local_variable; | 125 | mod inline_local_variable; |
125 | mod introduce_named_lifetime; | 126 | mod introduce_named_lifetime; |
126 | mod introduce_variable; | ||
127 | mod invert_if; | 127 | mod invert_if; |
128 | mod merge_imports; | 128 | mod merge_imports; |
129 | mod merge_match_arms; | 129 | mod merge_match_arms; |
@@ -157,6 +157,7 @@ mod handlers { | |||
157 | change_visibility::change_visibility, | 157 | change_visibility::change_visibility, |
158 | early_return::convert_to_guarded_return, | 158 | early_return::convert_to_guarded_return, |
159 | extract_struct_from_enum_variant::extract_struct_from_enum_variant, | 159 | extract_struct_from_enum_variant::extract_struct_from_enum_variant, |
160 | extract_variable::extract_variable, | ||
160 | fill_match_arms::fill_match_arms, | 161 | fill_match_arms::fill_match_arms, |
161 | fix_visibility::fix_visibility, | 162 | fix_visibility::fix_visibility, |
162 | flip_binexpr::flip_binexpr, | 163 | flip_binexpr::flip_binexpr, |
@@ -164,7 +165,6 @@ mod handlers { | |||
164 | flip_trait_bound::flip_trait_bound, | 165 | flip_trait_bound::flip_trait_bound, |
165 | inline_local_variable::inline_local_variable, | 166 | inline_local_variable::inline_local_variable, |
166 | introduce_named_lifetime::introduce_named_lifetime, | 167 | introduce_named_lifetime::introduce_named_lifetime, |
167 | introduce_variable::introduce_variable, | ||
168 | invert_if::invert_if, | 168 | invert_if::invert_if, |
169 | merge_imports::merge_imports, | 169 | merge_imports::merge_imports, |
170 | merge_match_arms::merge_match_arms, | 170 | merge_match_arms::merge_match_arms, |
diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs index 40a223727..31ea888c5 100644 --- a/crates/ra_assists/src/tests/generated.rs +++ b/crates/ra_assists/src/tests/generated.rs | |||
@@ -353,6 +353,24 @@ enum A { One(One) } | |||
353 | } | 353 | } |
354 | 354 | ||
355 | #[test] | 355 | #[test] |
356 | fn doctest_extract_variable() { | ||
357 | check_doc_test( | ||
358 | "extract_variable", | ||
359 | r#####" | ||
360 | fn main() { | ||
361 | <|>(1 + 2)<|> * 4; | ||
362 | } | ||
363 | "#####, | ||
364 | r#####" | ||
365 | fn main() { | ||
366 | let $0var_name = (1 + 2); | ||
367 | var_name * 4; | ||
368 | } | ||
369 | "#####, | ||
370 | ) | ||
371 | } | ||
372 | |||
373 | #[test] | ||
356 | fn doctest_fill_match_arms() { | 374 | fn doctest_fill_match_arms() { |
357 | check_doc_test( | 375 | check_doc_test( |
358 | "fill_match_arms", | 376 | "fill_match_arms", |
@@ -492,24 +510,6 @@ impl<'a> Cursor<'a> { | |||
492 | } | 510 | } |
493 | 511 | ||
494 | #[test] | 512 | #[test] |
495 | fn doctest_introduce_variable() { | ||
496 | check_doc_test( | ||
497 | "introduce_variable", | ||
498 | r#####" | ||
499 | fn main() { | ||
500 | <|>(1 + 2)<|> * 4; | ||
501 | } | ||
502 | "#####, | ||
503 | r#####" | ||
504 | fn main() { | ||
505 | let $0var_name = (1 + 2); | ||
506 | var_name * 4; | ||
507 | } | ||
508 | "#####, | ||
509 | ) | ||
510 | } | ||
511 | |||
512 | #[test] | ||
513 | fn doctest_invert_if() { | 513 | fn doctest_invert_if() { |
514 | check_doc_test( | 514 | check_doc_test( |
515 | "invert_if", | 515 | "invert_if", |
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs index 3ced648e5..a7e2e0982 100644 --- a/crates/ra_hir_def/src/body/lower.rs +++ b/crates/ra_hir_def/src/body/lower.rs | |||
@@ -5,7 +5,7 @@ use either::Either; | |||
5 | use hir_expand::{ | 5 | use hir_expand::{ |
6 | hygiene::Hygiene, | 6 | hygiene::Hygiene, |
7 | name::{name, AsName, Name}, | 7 | name::{name, AsName, Name}, |
8 | AstId, HirFileId, MacroDefId, MacroDefKind, | 8 | HirFileId, MacroDefId, MacroDefKind, |
9 | }; | 9 | }; |
10 | use ra_arena::Arena; | 10 | use ra_arena::Arena; |
11 | use ra_syntax::{ | 11 | use ra_syntax::{ |
@@ -27,7 +27,7 @@ use crate::{ | |||
27 | LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, | 27 | LogicOp, MatchArm, Ordering, Pat, PatId, RecordFieldPat, RecordLitField, Statement, |
28 | }, | 28 | }, |
29 | item_scope::BuiltinShadowMode, | 29 | item_scope::BuiltinShadowMode, |
30 | item_tree::{FileItemTreeId, ItemTree, ItemTreeNode}, | 30 | item_tree::{ItemTree, ItemTreeId, ItemTreeNode}, |
31 | path::{GenericArgs, Path}, | 31 | path::{GenericArgs, Path}, |
32 | type_ref::{Mutability, Rawness, TypeRef}, | 32 | type_ref::{Mutability, Rawness, TypeRef}, |
33 | AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId, | 33 | AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId, |
@@ -37,7 +37,7 @@ use crate::{ | |||
37 | use super::{ExprSource, PatSource}; | 37 | use super::{ExprSource, PatSource}; |
38 | use ast::AstChildren; | 38 | use ast::AstChildren; |
39 | use rustc_hash::FxHashMap; | 39 | use rustc_hash::FxHashMap; |
40 | use std::sync::Arc; | 40 | use std::{any::type_name, sync::Arc}; |
41 | 41 | ||
42 | pub(crate) struct LowerCtx { | 42 | pub(crate) struct LowerCtx { |
43 | hygiene: Hygiene, | 43 | hygiene: Hygiene, |
@@ -561,17 +561,30 @@ impl ExprCollector<'_> { | |||
561 | } | 561 | } |
562 | } | 562 | } |
563 | 563 | ||
564 | fn find_inner_item<S: ItemTreeNode>(&self, id: AstId<ast::ModuleItem>) -> FileItemTreeId<S> { | 564 | fn find_inner_item<N: ItemTreeNode>(&self, ast: &N::Source) -> Option<ItemTreeId<N>> { |
565 | let id = self.expander.ast_id(ast); | ||
565 | let tree = &self.item_trees[&id.file_id]; | 566 | let tree = &self.item_trees[&id.file_id]; |
566 | 567 | ||
567 | // FIXME: This probably breaks with `use` items, since they produce multiple item tree nodes | 568 | // FIXME: This probably breaks with `use` items, since they produce multiple item tree nodes |
568 | 569 | ||
569 | // Root file (non-macro). | 570 | // Root file (non-macro). |
570 | tree.all_inner_items() | 571 | let item_tree_id = tree |
572 | .all_inner_items() | ||
571 | .chain(tree.top_level_items().iter().copied()) | 573 | .chain(tree.top_level_items().iter().copied()) |
572 | .filter_map(|mod_item| mod_item.downcast::<S>()) | 574 | .filter_map(|mod_item| mod_item.downcast::<N>()) |
573 | .find(|tree_id| tree[*tree_id].ast_id().upcast() == id.value) | 575 | .find(|tree_id| tree[*tree_id].ast_id().upcast() == id.value.upcast()) |
574 | .unwrap_or_else(|| panic!("couldn't find inner item for {:?}", id)) | 576 | .or_else(|| { |
577 | log::debug!( | ||
578 | "couldn't find inner {} item for {:?} (AST: `{}` - {:?})", | ||
579 | type_name::<N>(), | ||
580 | id, | ||
581 | ast.syntax(), | ||
582 | ast.syntax(), | ||
583 | ); | ||
584 | None | ||
585 | })?; | ||
586 | |||
587 | Some(ItemTreeId::new(id.file_id, item_tree_id)) | ||
575 | } | 588 | } |
576 | 589 | ||
577 | fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId { | 590 | fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId { |
@@ -611,82 +624,45 @@ impl ExprCollector<'_> { | |||
611 | .filter_map(|item| { | 624 | .filter_map(|item| { |
612 | let (def, name): (ModuleDefId, Option<ast::Name>) = match item { | 625 | let (def, name): (ModuleDefId, Option<ast::Name>) = match item { |
613 | ast::ModuleItem::FnDef(def) => { | 626 | ast::ModuleItem::FnDef(def) => { |
614 | let ast_id = self.expander.ast_id(&def); | 627 | let id = self.find_inner_item(&def)?; |
615 | let id = self.find_inner_item(ast_id.map(|id| id.upcast())); | ||
616 | ( | 628 | ( |
617 | FunctionLoc { container: container.into(), id: ast_id.with_value(id) } | 629 | FunctionLoc { container: container.into(), id }.intern(self.db).into(), |
618 | .intern(self.db) | ||
619 | .into(), | ||
620 | def.name(), | 630 | def.name(), |
621 | ) | 631 | ) |
622 | } | 632 | } |
623 | ast::ModuleItem::TypeAliasDef(def) => { | 633 | ast::ModuleItem::TypeAliasDef(def) => { |
624 | let ast_id = self.expander.ast_id(&def); | 634 | let id = self.find_inner_item(&def)?; |
625 | let id = self.find_inner_item(ast_id.map(|id| id.upcast())); | ||
626 | ( | 635 | ( |
627 | TypeAliasLoc { container: container.into(), id: ast_id.with_value(id) } | 636 | TypeAliasLoc { container: container.into(), id }.intern(self.db).into(), |
628 | .intern(self.db) | ||
629 | .into(), | ||
630 | def.name(), | 637 | def.name(), |
631 | ) | 638 | ) |
632 | } | 639 | } |
633 | ast::ModuleItem::ConstDef(def) => { | 640 | ast::ModuleItem::ConstDef(def) => { |
634 | let ast_id = self.expander.ast_id(&def); | 641 | let id = self.find_inner_item(&def)?; |
635 | let id = self.find_inner_item(ast_id.map(|id| id.upcast())); | ||
636 | ( | 642 | ( |
637 | ConstLoc { container: container.into(), id: ast_id.with_value(id) } | 643 | ConstLoc { container: container.into(), id }.intern(self.db).into(), |
638 | .intern(self.db) | ||
639 | .into(), | ||
640 | def.name(), | 644 | def.name(), |
641 | ) | 645 | ) |
642 | } | 646 | } |
643 | ast::ModuleItem::StaticDef(def) => { | 647 | ast::ModuleItem::StaticDef(def) => { |
644 | let ast_id = self.expander.ast_id(&def); | 648 | let id = self.find_inner_item(&def)?; |
645 | let id = self.find_inner_item(ast_id.map(|id| id.upcast())); | 649 | (StaticLoc { container, id }.intern(self.db).into(), def.name()) |
646 | ( | ||
647 | StaticLoc { container, id: ast_id.with_value(id) } | ||
648 | .intern(self.db) | ||
649 | .into(), | ||
650 | def.name(), | ||
651 | ) | ||
652 | } | 650 | } |
653 | ast::ModuleItem::StructDef(def) => { | 651 | ast::ModuleItem::StructDef(def) => { |
654 | let ast_id = self.expander.ast_id(&def); | 652 | let id = self.find_inner_item(&def)?; |
655 | let id = self.find_inner_item(ast_id.map(|id| id.upcast())); | 653 | (StructLoc { container, id }.intern(self.db).into(), def.name()) |
656 | ( | ||
657 | StructLoc { container, id: ast_id.with_value(id) } | ||
658 | .intern(self.db) | ||
659 | .into(), | ||
660 | def.name(), | ||
661 | ) | ||
662 | } | 654 | } |
663 | ast::ModuleItem::EnumDef(def) => { | 655 | ast::ModuleItem::EnumDef(def) => { |
664 | let ast_id = self.expander.ast_id(&def); | 656 | let id = self.find_inner_item(&def)?; |
665 | let id = self.find_inner_item(ast_id.map(|id| id.upcast())); | 657 | (EnumLoc { container, id }.intern(self.db).into(), def.name()) |
666 | ( | ||
667 | EnumLoc { container, id: ast_id.with_value(id) }.intern(self.db).into(), | ||
668 | def.name(), | ||
669 | ) | ||
670 | } | 658 | } |
671 | ast::ModuleItem::UnionDef(def) => { | 659 | ast::ModuleItem::UnionDef(def) => { |
672 | let ast_id = self.expander.ast_id(&def); | 660 | let id = self.find_inner_item(&def)?; |
673 | let id = self.find_inner_item(ast_id.map(|id| id.upcast())); | 661 | (UnionLoc { container, id }.intern(self.db).into(), def.name()) |
674 | ( | ||
675 | UnionLoc { container, id: ast_id.with_value(id) } | ||
676 | .intern(self.db) | ||
677 | .into(), | ||
678 | def.name(), | ||
679 | ) | ||
680 | } | 662 | } |
681 | ast::ModuleItem::TraitDef(def) => { | 663 | ast::ModuleItem::TraitDef(def) => { |
682 | let ast_id = self.expander.ast_id(&def); | 664 | let id = self.find_inner_item(&def)?; |
683 | let id = self.find_inner_item(ast_id.map(|id| id.upcast())); | 665 | (TraitLoc { container, id }.intern(self.db).into(), def.name()) |
684 | ( | ||
685 | TraitLoc { container, id: ast_id.with_value(id) } | ||
686 | .intern(self.db) | ||
687 | .into(), | ||
688 | def.name(), | ||
689 | ) | ||
690 | } | 666 | } |
691 | ast::ModuleItem::ExternBlock(_) => return None, // FIXME: collect from extern blocks | 667 | ast::ModuleItem::ExternBlock(_) => return None, // FIXME: collect from extern blocks |
692 | ast::ModuleItem::ImplDef(_) | 668 | ast::ModuleItem::ImplDef(_) |
diff --git a/crates/ra_hir_def/src/body/scope.rs b/crates/ra_hir_def/src/body/scope.rs index 81397b063..99e876683 100644 --- a/crates/ra_hir_def/src/body/scope.rs +++ b/crates/ra_hir_def/src/body/scope.rs | |||
@@ -337,6 +337,19 @@ fn foo() { | |||
337 | ); | 337 | ); |
338 | } | 338 | } |
339 | 339 | ||
340 | #[test] | ||
341 | fn broken_inner_item() { | ||
342 | do_check( | ||
343 | r" | ||
344 | fn foo() { | ||
345 | trait {} | ||
346 | <|> | ||
347 | } | ||
348 | ", | ||
349 | &[], | ||
350 | ); | ||
351 | } | ||
352 | |||
340 | fn do_check_local_name(ra_fixture: &str, expected_offset: u32) { | 353 | fn do_check_local_name(ra_fixture: &str, expected_offset: u32) { |
341 | let (db, position) = TestDB::with_position(ra_fixture); | 354 | let (db, position) = TestDB::with_position(ra_fixture); |
342 | let file_id = position.file_id; | 355 | let file_id = position.file_id; |
diff --git a/crates/ra_hir_def/src/item_scope.rs b/crates/ra_hir_def/src/item_scope.rs index c81b966c3..4d446c707 100644 --- a/crates/ra_hir_def/src/item_scope.rs +++ b/crates/ra_hir_def/src/item_scope.rs | |||
@@ -4,14 +4,27 @@ | |||
4 | use hir_expand::name::Name; | 4 | use hir_expand::name::Name; |
5 | use once_cell::sync::Lazy; | 5 | use once_cell::sync::Lazy; |
6 | use ra_db::CrateId; | 6 | use ra_db::CrateId; |
7 | use rustc_hash::FxHashMap; | 7 | use rustc_hash::{FxHashMap, FxHashSet}; |
8 | use test_utils::mark; | 8 | use test_utils::mark; |
9 | 9 | ||
10 | use crate::{ | 10 | use crate::{ |
11 | db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId, | 11 | db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId, |
12 | Lookup, MacroDefId, ModuleDefId, TraitId, | 12 | LocalModuleId, Lookup, MacroDefId, ModuleDefId, TraitId, |
13 | }; | 13 | }; |
14 | 14 | ||
15 | #[derive(Copy, Clone)] | ||
16 | pub(crate) enum ImportType { | ||
17 | Glob, | ||
18 | Named, | ||
19 | } | ||
20 | |||
21 | #[derive(Debug, Default)] | ||
22 | pub struct PerNsGlobImports { | ||
23 | types: FxHashSet<(LocalModuleId, Name)>, | ||
24 | values: FxHashSet<(LocalModuleId, Name)>, | ||
25 | macros: FxHashSet<(LocalModuleId, Name)>, | ||
26 | } | ||
27 | |||
15 | #[derive(Debug, Default, PartialEq, Eq)] | 28 | #[derive(Debug, Default, PartialEq, Eq)] |
16 | pub struct ItemScope { | 29 | pub struct ItemScope { |
17 | visible: FxHashMap<Name, PerNs>, | 30 | visible: FxHashMap<Name, PerNs>, |
@@ -127,16 +140,62 @@ impl ItemScope { | |||
127 | let mut changed = false; | 140 | let mut changed = false; |
128 | let existing = self.visible.entry(name).or_default(); | 141 | let existing = self.visible.entry(name).or_default(); |
129 | 142 | ||
143 | if existing.types.is_none() && def.types.is_some() { | ||
144 | existing.types = def.types; | ||
145 | changed = true; | ||
146 | } | ||
147 | |||
148 | if existing.values.is_none() && def.values.is_some() { | ||
149 | existing.values = def.values; | ||
150 | changed = true; | ||
151 | } | ||
152 | |||
153 | if existing.macros.is_none() && def.macros.is_some() { | ||
154 | existing.macros = def.macros; | ||
155 | changed = true; | ||
156 | } | ||
157 | |||
158 | changed | ||
159 | } | ||
160 | |||
161 | pub(crate) fn push_res_with_import( | ||
162 | &mut self, | ||
163 | glob_imports: &mut PerNsGlobImports, | ||
164 | lookup: (LocalModuleId, Name), | ||
165 | def: PerNs, | ||
166 | def_import_type: ImportType, | ||
167 | ) -> bool { | ||
168 | let mut changed = false; | ||
169 | let existing = self.visible.entry(lookup.1.clone()).or_default(); | ||
170 | |||
130 | macro_rules! check_changed { | 171 | macro_rules! check_changed { |
131 | ($changed:ident, $existing:expr, $def:expr) => { | 172 | ( |
132 | match ($existing, $def) { | 173 | $changed:ident, |
174 | ( $existing:ident / $def:ident ) . $field:ident, | ||
175 | $glob_imports:ident [ $lookup:ident ], | ||
176 | $def_import_type:ident | ||
177 | ) => { | ||
178 | match ($existing.$field, $def.$field) { | ||
133 | (None, Some(_)) => { | 179 | (None, Some(_)) => { |
134 | $existing = $def; | 180 | match $def_import_type { |
181 | ImportType::Glob => { | ||
182 | $glob_imports.$field.insert($lookup.clone()); | ||
183 | } | ||
184 | ImportType::Named => { | ||
185 | $glob_imports.$field.remove(&$lookup); | ||
186 | } | ||
187 | } | ||
188 | |||
189 | $existing.$field = $def.$field; | ||
135 | $changed = true; | 190 | $changed = true; |
136 | } | 191 | } |
137 | (Some(e), Some(d)) if e.0 != d.0 => { | 192 | (Some(_), Some(_)) |
193 | if $glob_imports.$field.contains(&$lookup) | ||
194 | && matches!($def_import_type, ImportType::Named) => | ||
195 | { | ||
138 | mark::hit!(import_shadowed); | 196 | mark::hit!(import_shadowed); |
139 | $existing = $def; | 197 | $glob_imports.$field.remove(&$lookup); |
198 | $existing.$field = $def.$field; | ||
140 | $changed = true; | 199 | $changed = true; |
141 | } | 200 | } |
142 | _ => {} | 201 | _ => {} |
@@ -144,9 +203,9 @@ impl ItemScope { | |||
144 | }; | 203 | }; |
145 | } | 204 | } |
146 | 205 | ||
147 | check_changed!(changed, existing.types, def.types); | 206 | check_changed!(changed, (existing / def).types, glob_imports[lookup], def_import_type); |
148 | check_changed!(changed, existing.values, def.values); | 207 | check_changed!(changed, (existing / def).values, glob_imports[lookup], def_import_type); |
149 | check_changed!(changed, existing.macros, def.macros); | 208 | check_changed!(changed, (existing / def).macros, glob_imports[lookup], def_import_type); |
150 | 209 | ||
151 | changed | 210 | changed |
152 | } | 211 | } |
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs index 2ced4f66b..a35ac1024 100644 --- a/crates/ra_hir_def/src/nameres/collector.rs +++ b/crates/ra_hir_def/src/nameres/collector.rs | |||
@@ -20,6 +20,7 @@ use test_utils::mark; | |||
20 | use crate::{ | 20 | use crate::{ |
21 | attr::Attrs, | 21 | attr::Attrs, |
22 | db::DefDatabase, | 22 | db::DefDatabase, |
23 | item_scope::{ImportType, PerNsGlobImports}, | ||
23 | item_tree::{ | 24 | item_tree::{ |
24 | self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, Mod, ModItem, ModKind, StructDefKind, | 25 | self, FileItemTreeId, ItemTree, ItemTreeId, MacroCall, Mod, ModItem, ModKind, StructDefKind, |
25 | }, | 26 | }, |
@@ -80,6 +81,7 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: CrateDefMap) -> Cr | |||
80 | mod_dirs: FxHashMap::default(), | 81 | mod_dirs: FxHashMap::default(), |
81 | cfg_options, | 82 | cfg_options, |
82 | proc_macros, | 83 | proc_macros, |
84 | from_glob_import: Default::default(), | ||
83 | }; | 85 | }; |
84 | collector.collect(); | 86 | collector.collect(); |
85 | collector.finish() | 87 | collector.finish() |
@@ -186,6 +188,7 @@ struct DefCollector<'a> { | |||
186 | mod_dirs: FxHashMap<LocalModuleId, ModDir>, | 188 | mod_dirs: FxHashMap<LocalModuleId, ModDir>, |
187 | cfg_options: &'a CfgOptions, | 189 | cfg_options: &'a CfgOptions, |
188 | proc_macros: Vec<(Name, ProcMacroExpander)>, | 190 | proc_macros: Vec<(Name, ProcMacroExpander)>, |
191 | from_glob_import: PerNsGlobImports, | ||
189 | } | 192 | } |
190 | 193 | ||
191 | impl DefCollector<'_> { | 194 | impl DefCollector<'_> { |
@@ -305,6 +308,7 @@ impl DefCollector<'_> { | |||
305 | self.def_map.root, | 308 | self.def_map.root, |
306 | &[(name, PerNs::macros(macro_, Visibility::Public))], | 309 | &[(name, PerNs::macros(macro_, Visibility::Public))], |
307 | Visibility::Public, | 310 | Visibility::Public, |
311 | ImportType::Named, | ||
308 | ); | 312 | ); |
309 | } | 313 | } |
310 | } | 314 | } |
@@ -330,6 +334,7 @@ impl DefCollector<'_> { | |||
330 | self.def_map.root, | 334 | self.def_map.root, |
331 | &[(name, PerNs::macros(macro_, Visibility::Public))], | 335 | &[(name, PerNs::macros(macro_, Visibility::Public))], |
332 | Visibility::Public, | 336 | Visibility::Public, |
337 | ImportType::Named, | ||
333 | ); | 338 | ); |
334 | } | 339 | } |
335 | 340 | ||
@@ -383,7 +388,6 @@ impl DefCollector<'_> { | |||
383 | let imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); | 388 | let imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); |
384 | for mut directive in imports { | 389 | for mut directive in imports { |
385 | directive.status = self.resolve_import(directive.module_id, &directive.import); | 390 | directive.status = self.resolve_import(directive.module_id, &directive.import); |
386 | |||
387 | match directive.status { | 391 | match directive.status { |
388 | PartialResolvedImport::Indeterminate(_) => { | 392 | PartialResolvedImport::Indeterminate(_) => { |
389 | self.record_resolved_import(&directive); | 393 | self.record_resolved_import(&directive); |
@@ -477,7 +481,7 @@ impl DefCollector<'_> { | |||
477 | .filter(|(_, res)| !res.is_none()) | 481 | .filter(|(_, res)| !res.is_none()) |
478 | .collect::<Vec<_>>(); | 482 | .collect::<Vec<_>>(); |
479 | 483 | ||
480 | self.update(module_id, &items, vis); | 484 | self.update(module_id, &items, vis, ImportType::Glob); |
481 | } else { | 485 | } else { |
482 | // glob import from same crate => we do an initial | 486 | // glob import from same crate => we do an initial |
483 | // import, and then need to propagate any further | 487 | // import, and then need to propagate any further |
@@ -499,7 +503,7 @@ impl DefCollector<'_> { | |||
499 | .filter(|(_, res)| !res.is_none()) | 503 | .filter(|(_, res)| !res.is_none()) |
500 | .collect::<Vec<_>>(); | 504 | .collect::<Vec<_>>(); |
501 | 505 | ||
502 | self.update(module_id, &items, vis); | 506 | self.update(module_id, &items, vis, ImportType::Glob); |
503 | // record the glob import in case we add further items | 507 | // record the glob import in case we add further items |
504 | let glob = self.glob_imports.entry(m.local_id).or_default(); | 508 | let glob = self.glob_imports.entry(m.local_id).or_default(); |
505 | if !glob.iter().any(|(mid, _)| *mid == module_id) { | 509 | if !glob.iter().any(|(mid, _)| *mid == module_id) { |
@@ -529,7 +533,7 @@ impl DefCollector<'_> { | |||
529 | (name, res) | 533 | (name, res) |
530 | }) | 534 | }) |
531 | .collect::<Vec<_>>(); | 535 | .collect::<Vec<_>>(); |
532 | self.update(module_id, &resolutions, vis); | 536 | self.update(module_id, &resolutions, vis, ImportType::Glob); |
533 | } | 537 | } |
534 | Some(d) => { | 538 | Some(d) => { |
535 | log::debug!("glob import {:?} from non-module/enum {:?}", import, d); | 539 | log::debug!("glob import {:?} from non-module/enum {:?}", import, d); |
@@ -555,15 +559,21 @@ impl DefCollector<'_> { | |||
555 | } | 559 | } |
556 | } | 560 | } |
557 | 561 | ||
558 | self.update(module_id, &[(name, def)], vis); | 562 | self.update(module_id, &[(name, def)], vis, ImportType::Named); |
559 | } | 563 | } |
560 | None => mark::hit!(bogus_paths), | 564 | None => mark::hit!(bogus_paths), |
561 | } | 565 | } |
562 | } | 566 | } |
563 | } | 567 | } |
564 | 568 | ||
565 | fn update(&mut self, module_id: LocalModuleId, resolutions: &[(Name, PerNs)], vis: Visibility) { | 569 | fn update( |
566 | self.update_recursive(module_id, resolutions, vis, 0) | 570 | &mut self, |
571 | module_id: LocalModuleId, | ||
572 | resolutions: &[(Name, PerNs)], | ||
573 | vis: Visibility, | ||
574 | import_type: ImportType, | ||
575 | ) { | ||
576 | self.update_recursive(module_id, resolutions, vis, import_type, 0) | ||
567 | } | 577 | } |
568 | 578 | ||
569 | fn update_recursive( | 579 | fn update_recursive( |
@@ -573,6 +583,7 @@ impl DefCollector<'_> { | |||
573 | // All resolutions are imported with this visibility; the visibilies in | 583 | // All resolutions are imported with this visibility; the visibilies in |
574 | // the `PerNs` values are ignored and overwritten | 584 | // the `PerNs` values are ignored and overwritten |
575 | vis: Visibility, | 585 | vis: Visibility, |
586 | import_type: ImportType, | ||
576 | depth: usize, | 587 | depth: usize, |
577 | ) { | 588 | ) { |
578 | if depth > 100 { | 589 | if depth > 100 { |
@@ -582,7 +593,12 @@ impl DefCollector<'_> { | |||
582 | let scope = &mut self.def_map.modules[module_id].scope; | 593 | let scope = &mut self.def_map.modules[module_id].scope; |
583 | let mut changed = false; | 594 | let mut changed = false; |
584 | for (name, res) in resolutions { | 595 | for (name, res) in resolutions { |
585 | changed |= scope.push_res(name.clone(), res.with_visibility(vis)); | 596 | changed |= scope.push_res_with_import( |
597 | &mut self.from_glob_import, | ||
598 | (module_id, name.clone()), | ||
599 | res.with_visibility(vis), | ||
600 | import_type, | ||
601 | ); | ||
586 | } | 602 | } |
587 | 603 | ||
588 | if !changed { | 604 | if !changed { |
@@ -601,7 +617,13 @@ impl DefCollector<'_> { | |||
601 | if !vis.is_visible_from_def_map(&self.def_map, glob_importing_module) { | 617 | if !vis.is_visible_from_def_map(&self.def_map, glob_importing_module) { |
602 | continue; | 618 | continue; |
603 | } | 619 | } |
604 | self.update_recursive(glob_importing_module, resolutions, glob_import_vis, depth + 1); | 620 | self.update_recursive( |
621 | glob_importing_module, | ||
622 | resolutions, | ||
623 | glob_import_vis, | ||
624 | ImportType::Glob, | ||
625 | depth + 1, | ||
626 | ); | ||
605 | } | 627 | } |
606 | } | 628 | } |
607 | 629 | ||
@@ -923,6 +945,7 @@ impl ModCollector<'_, '_> { | |||
923 | self.module_id, | 945 | self.module_id, |
924 | &[(name.clone(), PerNs::from_def(id, vis, has_constructor))], | 946 | &[(name.clone(), PerNs::from_def(id, vis, has_constructor))], |
925 | vis, | 947 | vis, |
948 | ImportType::Named, | ||
926 | ) | 949 | ) |
927 | } | 950 | } |
928 | } | 951 | } |
@@ -1025,7 +1048,12 @@ impl ModCollector<'_, '_> { | |||
1025 | let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: res }; | 1048 | let module = ModuleId { krate: self.def_collector.def_map.krate, local_id: res }; |
1026 | let def: ModuleDefId = module.into(); | 1049 | let def: ModuleDefId = module.into(); |
1027 | self.def_collector.def_map.modules[self.module_id].scope.define_def(def); | 1050 | self.def_collector.def_map.modules[self.module_id].scope.define_def(def); |
1028 | self.def_collector.update(self.module_id, &[(name, PerNs::from_def(def, vis, false))], vis); | 1051 | self.def_collector.update( |
1052 | self.module_id, | ||
1053 | &[(name, PerNs::from_def(def, vis, false))], | ||
1054 | vis, | ||
1055 | ImportType::Named, | ||
1056 | ); | ||
1029 | res | 1057 | res |
1030 | } | 1058 | } |
1031 | 1059 | ||
@@ -1154,6 +1182,7 @@ mod tests { | |||
1154 | mod_dirs: FxHashMap::default(), | 1182 | mod_dirs: FxHashMap::default(), |
1155 | cfg_options: &CfgOptions::default(), | 1183 | cfg_options: &CfgOptions::default(), |
1156 | proc_macros: Default::default(), | 1184 | proc_macros: Default::default(), |
1185 | from_glob_import: Default::default(), | ||
1157 | }; | 1186 | }; |
1158 | collector.collect(); | 1187 | collector.collect(); |
1159 | collector.def_map | 1188 | collector.def_map |
diff --git a/crates/ra_hir_def/src/nameres/tests/globs.rs b/crates/ra_hir_def/src/nameres/tests/globs.rs index 2f440975a..7f3d7509c 100644 --- a/crates/ra_hir_def/src/nameres/tests/globs.rs +++ b/crates/ra_hir_def/src/nameres/tests/globs.rs | |||
@@ -276,3 +276,93 @@ fn glob_shadowed_def() { | |||
276 | "### | 276 | "### |
277 | ); | 277 | ); |
278 | } | 278 | } |
279 | |||
280 | #[test] | ||
281 | fn glob_shadowed_def_reversed() { | ||
282 | let map = def_map( | ||
283 | r###" | ||
284 | //- /lib.rs | ||
285 | mod foo; | ||
286 | mod bar; | ||
287 | |||
288 | use bar::baz; | ||
289 | use foo::*; | ||
290 | |||
291 | use baz::Bar; | ||
292 | |||
293 | //- /foo.rs | ||
294 | pub mod baz { | ||
295 | pub struct Foo; | ||
296 | } | ||
297 | |||
298 | //- /bar.rs | ||
299 | pub mod baz { | ||
300 | pub struct Bar; | ||
301 | } | ||
302 | "###, | ||
303 | ); | ||
304 | assert_snapshot!(map, @r###" | ||
305 | â‹®crate | ||
306 | â‹®Bar: t v | ||
307 | â‹®bar: t | ||
308 | â‹®baz: t | ||
309 | â‹®foo: t | ||
310 | â‹® | ||
311 | â‹®crate::bar | ||
312 | â‹®baz: t | ||
313 | â‹® | ||
314 | â‹®crate::bar::baz | ||
315 | â‹®Bar: t v | ||
316 | â‹® | ||
317 | â‹®crate::foo | ||
318 | â‹®baz: t | ||
319 | â‹® | ||
320 | â‹®crate::foo::baz | ||
321 | â‹®Foo: t v | ||
322 | "### | ||
323 | ); | ||
324 | } | ||
325 | |||
326 | #[test] | ||
327 | fn glob_shadowed_def_dependencies() { | ||
328 | let map = def_map( | ||
329 | r###" | ||
330 | //- /lib.rs | ||
331 | mod a { pub mod foo { pub struct X; } } | ||
332 | mod b { pub use super::a::foo; } | ||
333 | mod c { pub mod foo { pub struct Y; } } | ||
334 | mod d { | ||
335 | use super::c::foo; | ||
336 | use super::b::*; | ||
337 | use foo::Y; | ||
338 | } | ||
339 | "###, | ||
340 | ); | ||
341 | assert_snapshot!(map, @r###" | ||
342 | â‹®crate | ||
343 | â‹®a: t | ||
344 | â‹®b: t | ||
345 | â‹®c: t | ||
346 | â‹®d: t | ||
347 | â‹® | ||
348 | â‹®crate::d | ||
349 | â‹®Y: t v | ||
350 | â‹®foo: t | ||
351 | â‹® | ||
352 | â‹®crate::c | ||
353 | â‹®foo: t | ||
354 | â‹® | ||
355 | â‹®crate::c::foo | ||
356 | â‹®Y: t v | ||
357 | â‹® | ||
358 | â‹®crate::b | ||
359 | â‹®foo: t | ||
360 | â‹® | ||
361 | â‹®crate::a | ||
362 | â‹®foo: t | ||
363 | â‹® | ||
364 | â‹®crate::a::foo | ||
365 | â‹®X: t v | ||
366 | "### | ||
367 | ); | ||
368 | } | ||
diff --git a/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs b/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs index e9a5e4cba..753684201 100644 --- a/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs +++ b/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs | |||
@@ -335,6 +335,22 @@ fn module_resolution_relative_path_2() { | |||
335 | } | 335 | } |
336 | 336 | ||
337 | #[test] | 337 | #[test] |
338 | fn module_resolution_relative_path_outside_root() { | ||
339 | let map = def_map( | ||
340 | r###" | ||
341 | //- /main.rs | ||
342 | |||
343 | #[path="../../../../../outside.rs"] | ||
344 | mod foo; | ||
345 | "###, | ||
346 | ); | ||
347 | |||
348 | assert_snapshot!(map, @r###" | ||
349 | â‹®crate | ||
350 | "###); | ||
351 | } | ||
352 | |||
353 | #[test] | ||
338 | fn module_resolution_explicit_path_mod_rs_2() { | 354 | fn module_resolution_explicit_path_mod_rs_2() { |
339 | let map = def_map( | 355 | let map = def_map( |
340 | r###" | 356 | r###" |
diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs index 8b59a8bd6..c19519cf1 100644 --- a/crates/ra_hir_ty/src/method_resolution.rs +++ b/crates/ra_hir_ty/src/method_resolution.rs | |||
@@ -281,6 +281,7 @@ pub fn iterate_method_candidates<T>( | |||
281 | name, | 281 | name, |
282 | mode, | 282 | mode, |
283 | &mut |ty, item| { | 283 | &mut |ty, item| { |
284 | assert!(slot.is_none()); | ||
284 | slot = callback(ty, item); | 285 | slot = callback(ty, item); |
285 | slot.is_some() | 286 | slot.is_some() |
286 | }, | 287 | }, |
@@ -288,7 +289,7 @@ pub fn iterate_method_candidates<T>( | |||
288 | slot | 289 | slot |
289 | } | 290 | } |
290 | 291 | ||
291 | pub fn iterate_method_candidates_impl( | 292 | fn iterate_method_candidates_impl( |
292 | ty: &Canonical<Ty>, | 293 | ty: &Canonical<Ty>, |
293 | db: &dyn HirDatabase, | 294 | db: &dyn HirDatabase, |
294 | env: Arc<TraitEnvironment>, | 295 | env: Arc<TraitEnvironment>, |
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs index d7ef9add6..7d8197f8b 100644 --- a/crates/ra_hir_ty/src/tests/simple.rs +++ b/crates/ra_hir_ty/src/tests/simple.rs | |||
@@ -1739,6 +1739,52 @@ fn main() { | |||
1739 | assert_eq!(t, "u32"); | 1739 | assert_eq!(t, "u32"); |
1740 | } | 1740 | } |
1741 | 1741 | ||
1742 | // This test is actually testing the shadowing behavior within ra_hir_def. It | ||
1743 | // lives here because the testing infrastructure in ra_hir_def isn't currently | ||
1744 | // capable of asserting the necessary conditions. | ||
1745 | #[test] | ||
1746 | fn should_be_shadowing_imports() { | ||
1747 | let t = type_at( | ||
1748 | r#" | ||
1749 | mod a { | ||
1750 | pub fn foo() -> i8 {0} | ||
1751 | pub struct foo { a: i8 } | ||
1752 | } | ||
1753 | mod b { pub fn foo () -> u8 {0} } | ||
1754 | mod c { pub struct foo { a: u8 } } | ||
1755 | mod d { | ||
1756 | pub use super::a::*; | ||
1757 | pub use super::c::foo; | ||
1758 | pub use super::b::foo; | ||
1759 | } | ||
1760 | |||
1761 | fn main() { | ||
1762 | d::foo()<|>; | ||
1763 | }"#, | ||
1764 | ); | ||
1765 | assert_eq!(t, "u8"); | ||
1766 | |||
1767 | let t = type_at( | ||
1768 | r#" | ||
1769 | mod a { | ||
1770 | pub fn foo() -> i8 {0} | ||
1771 | pub struct foo { a: i8 } | ||
1772 | } | ||
1773 | mod b { pub fn foo () -> u8 {0} } | ||
1774 | mod c { pub struct foo { a: u8 } } | ||
1775 | mod d { | ||
1776 | pub use super::a::*; | ||
1777 | pub use super::c::foo; | ||
1778 | pub use super::b::foo; | ||
1779 | } | ||
1780 | |||
1781 | fn main() { | ||
1782 | d::foo{a:0<|>}; | ||
1783 | }"#, | ||
1784 | ); | ||
1785 | assert_eq!(t, "u8"); | ||
1786 | } | ||
1787 | |||
1742 | #[test] | 1788 | #[test] |
1743 | fn closure_return() { | 1789 | fn closure_return() { |
1744 | assert_snapshot!( | 1790 | assert_snapshot!( |
diff --git a/crates/ra_parser/src/parser.rs b/crates/ra_parser/src/parser.rs index 4f59b0a23..d797f2cc9 100644 --- a/crates/ra_parser/src/parser.rs +++ b/crates/ra_parser/src/parser.rs | |||
@@ -127,17 +127,24 @@ impl<'t> Parser<'t> { | |||
127 | 127 | ||
128 | fn at_composite2(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind) -> bool { | 128 | fn at_composite2(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind) -> bool { |
129 | let t1 = self.token_source.lookahead_nth(n); | 129 | let t1 = self.token_source.lookahead_nth(n); |
130 | if t1.kind != k1 || !t1.is_jointed_to_next { | ||
131 | return false; | ||
132 | } | ||
130 | let t2 = self.token_source.lookahead_nth(n + 1); | 133 | let t2 = self.token_source.lookahead_nth(n + 1); |
131 | t1.kind == k1 && t1.is_jointed_to_next && t2.kind == k2 | 134 | t2.kind == k2 |
132 | } | 135 | } |
133 | 136 | ||
134 | fn at_composite3(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind, k3: SyntaxKind) -> bool { | 137 | fn at_composite3(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind, k3: SyntaxKind) -> bool { |
135 | let t1 = self.token_source.lookahead_nth(n); | 138 | let t1 = self.token_source.lookahead_nth(n); |
139 | if t1.kind != k1 || !t1.is_jointed_to_next { | ||
140 | return false; | ||
141 | } | ||
136 | let t2 = self.token_source.lookahead_nth(n + 1); | 142 | let t2 = self.token_source.lookahead_nth(n + 1); |
143 | if t2.kind != k2 || !t2.is_jointed_to_next { | ||
144 | return false; | ||
145 | } | ||
137 | let t3 = self.token_source.lookahead_nth(n + 2); | 146 | let t3 = self.token_source.lookahead_nth(n + 2); |
138 | (t1.kind == k1 && t1.is_jointed_to_next) | 147 | t3.kind == k3 |
139 | && (t2.kind == k2 && t2.is_jointed_to_next) | ||
140 | && t3.kind == k3 | ||
141 | } | 148 | } |
142 | 149 | ||
143 | /// Checks if the current token is in `kinds`. | 150 | /// Checks if the current token is in `kinds`. |
diff --git a/crates/ra_ssr/src/lib.rs b/crates/ra_ssr/src/lib.rs index da26ee669..8f149e3db 100644 --- a/crates/ra_ssr/src/lib.rs +++ b/crates/ra_ssr/src/lib.rs | |||
@@ -12,7 +12,7 @@ mod tests; | |||
12 | use crate::matching::Match; | 12 | use crate::matching::Match; |
13 | use hir::Semantics; | 13 | use hir::Semantics; |
14 | use ra_db::{FileId, FileRange}; | 14 | use ra_db::{FileId, FileRange}; |
15 | use ra_syntax::{AstNode, SmolStr, SyntaxNode}; | 15 | use ra_syntax::{ast, AstNode, SmolStr, SyntaxNode}; |
16 | use ra_text_edit::TextEdit; | 16 | use ra_text_edit::TextEdit; |
17 | use rustc_hash::FxHashMap; | 17 | use rustc_hash::FxHashMap; |
18 | 18 | ||
@@ -107,6 +107,22 @@ impl<'db> MatchFinder<'db> { | |||
107 | return; | 107 | return; |
108 | } | 108 | } |
109 | } | 109 | } |
110 | // If we've got a macro call, we already tried matching it pre-expansion, which is the only | ||
111 | // way to match the whole macro, now try expanding it and matching the expansion. | ||
112 | if let Some(macro_call) = ast::MacroCall::cast(code.clone()) { | ||
113 | if let Some(expanded) = self.sema.expand(¯o_call) { | ||
114 | if let Some(tt) = macro_call.token_tree() { | ||
115 | // When matching within a macro expansion, we only want to allow matches of | ||
116 | // nodes that originated entirely from within the token tree of the macro call. | ||
117 | // i.e. we don't want to match something that came from the macro itself. | ||
118 | self.find_matches( | ||
119 | &expanded, | ||
120 | &Some(self.sema.original_range(tt.syntax())), | ||
121 | matches_out, | ||
122 | ); | ||
123 | } | ||
124 | } | ||
125 | } | ||
110 | for child in code.children() { | 126 | for child in code.children() { |
111 | self.find_matches(&child, restrict_range, matches_out); | 127 | self.find_matches(&child, restrict_range, matches_out); |
112 | } | 128 | } |
diff --git a/crates/ra_ssr/src/matching.rs b/crates/ra_ssr/src/matching.rs index bdaba9f1b..85420ed3c 100644 --- a/crates/ra_ssr/src/matching.rs +++ b/crates/ra_ssr/src/matching.rs | |||
@@ -343,7 +343,9 @@ impl<'db, 'sema> MatchState<'db, 'sema> { | |||
343 | } | 343 | } |
344 | 344 | ||
345 | /// Outside of token trees, a placeholder can only match a single AST node, whereas in a token | 345 | /// Outside of token trees, a placeholder can only match a single AST node, whereas in a token |
346 | /// tree it can match a sequence of tokens. | 346 | /// tree it can match a sequence of tokens. Note, that this code will only be used when the |
347 | /// pattern matches the macro invocation. For matches within the macro call, we'll already have | ||
348 | /// expanded the macro. | ||
347 | fn attempt_match_token_tree( | 349 | fn attempt_match_token_tree( |
348 | &mut self, | 350 | &mut self, |
349 | match_inputs: &MatchInputs, | 351 | match_inputs: &MatchInputs, |
diff --git a/crates/ra_ssr/src/tests.rs b/crates/ra_ssr/src/tests.rs index 3ee1e74e9..7a3141be8 100644 --- a/crates/ra_ssr/src/tests.rs +++ b/crates/ra_ssr/src/tests.rs | |||
@@ -209,6 +209,11 @@ fn assert_ssr_transform(rule: &str, input: &str, result: &str) { | |||
209 | assert_ssr_transforms(&[rule], input, result); | 209 | assert_ssr_transforms(&[rule], input, result); |
210 | } | 210 | } |
211 | 211 | ||
212 | fn normalize_code(code: &str) -> String { | ||
213 | let (db, file_id) = single_file(code); | ||
214 | db.file_text(file_id).to_string() | ||
215 | } | ||
216 | |||
212 | fn assert_ssr_transforms(rules: &[&str], input: &str, result: &str) { | 217 | fn assert_ssr_transforms(rules: &[&str], input: &str, result: &str) { |
213 | let (db, file_id) = single_file(input); | 218 | let (db, file_id) = single_file(input); |
214 | let mut match_finder = MatchFinder::new(&db); | 219 | let mut match_finder = MatchFinder::new(&db); |
@@ -217,8 +222,13 @@ fn assert_ssr_transforms(rules: &[&str], input: &str, result: &str) { | |||
217 | match_finder.add_rule(rule); | 222 | match_finder.add_rule(rule); |
218 | } | 223 | } |
219 | if let Some(edits) = match_finder.edits_for_file(file_id) { | 224 | if let Some(edits) = match_finder.edits_for_file(file_id) { |
220 | let mut after = input.to_string(); | 225 | // Note, db.file_text is not necessarily the same as `input`, since fixture parsing alters |
226 | // stuff. | ||
227 | let mut after = db.file_text(file_id).to_string(); | ||
221 | edits.apply(&mut after); | 228 | edits.apply(&mut after); |
229 | // Likewise, we need to make sure that whatever transformations fixture parsing applies, | ||
230 | // also get appplied to our expected result. | ||
231 | let result = normalize_code(result); | ||
222 | assert_eq!(after, result); | 232 | assert_eq!(after, result); |
223 | } else { | 233 | } else { |
224 | panic!("No edits were made"); | 234 | panic!("No edits were made"); |
@@ -355,6 +365,18 @@ fn match_nested_method_calls() { | |||
355 | ); | 365 | ); |
356 | } | 366 | } |
357 | 367 | ||
368 | // Make sure that our node matching semantics don't differ within macro calls. | ||
369 | #[test] | ||
370 | fn match_nested_method_calls_with_macro_call() { | ||
371 | assert_matches( | ||
372 | "$a.z().z().z()", | ||
373 | r#" | ||
374 | macro_rules! m1 { ($a:expr) => {$a}; } | ||
375 | fn f() {m1!(h().i().j().z().z().z().d().e())}"#, | ||
376 | &["h().i().j().z().z().z()"], | ||
377 | ); | ||
378 | } | ||
379 | |||
358 | #[test] | 380 | #[test] |
359 | fn match_complex_expr() { | 381 | fn match_complex_expr() { |
360 | let code = "fn f() -> i32 {foo(bar(40, 2), 42)}"; | 382 | let code = "fn f() -> i32 {foo(bar(40, 2), 42)}"; |
@@ -547,3 +569,40 @@ fn multiple_rules() { | |||
547 | "fn f() -> i32 {add_one(add(3, 2))}", | 569 | "fn f() -> i32 {add_one(add(3, 2))}", |
548 | ) | 570 | ) |
549 | } | 571 | } |
572 | |||
573 | #[test] | ||
574 | fn match_within_macro_invocation() { | ||
575 | let code = r#" | ||
576 | macro_rules! foo { | ||
577 | ($a:stmt; $b:expr) => { | ||
578 | $b | ||
579 | }; | ||
580 | } | ||
581 | struct A {} | ||
582 | impl A { | ||
583 | fn bar() {} | ||
584 | } | ||
585 | fn f1() { | ||
586 | let aaa = A {}; | ||
587 | foo!(macro_ignores_this(); aaa.bar()); | ||
588 | } | ||
589 | "#; | ||
590 | assert_matches("$a.bar()", code, &["aaa.bar()"]); | ||
591 | } | ||
592 | |||
593 | #[test] | ||
594 | fn replace_within_macro_expansion() { | ||
595 | assert_ssr_transform( | ||
596 | "$a.foo() ==>> bar($a)", | ||
597 | r#" | ||
598 | macro_rules! macro1 { | ||
599 | ($a:expr) => {$a} | ||
600 | } | ||
601 | fn f() {macro1!(5.x().foo().o2())}"#, | ||
602 | r#" | ||
603 | macro_rules! macro1 { | ||
604 | ($a:expr) => {$a} | ||
605 | } | ||
606 | fn f() {macro1!(bar(5.x()).o2())}"#, | ||
607 | ) | ||
608 | } | ||
diff --git a/crates/ra_toolchain/src/lib.rs b/crates/ra_toolchain/src/lib.rs index 3d2865e09..9916e52c4 100644 --- a/crates/ra_toolchain/src/lib.rs +++ b/crates/ra_toolchain/src/lib.rs | |||
@@ -15,6 +15,10 @@ pub fn rustup() -> PathBuf { | |||
15 | get_path_for_executable("rustup") | 15 | get_path_for_executable("rustup") |
16 | } | 16 | } |
17 | 17 | ||
18 | pub fn rustfmt() -> PathBuf { | ||
19 | get_path_for_executable("rustfmt") | ||
20 | } | ||
21 | |||
18 | /// Return a `PathBuf` to use for the given executable. | 22 | /// Return a `PathBuf` to use for the given executable. |
19 | /// | 23 | /// |
20 | /// E.g., `get_path_for_executable("cargo")` may return just `cargo` if that | 24 | /// E.g., `get_path_for_executable("cargo")` may return just `cargo` if that |
@@ -42,22 +46,23 @@ fn get_path_for_executable(executable_name: &'static str) -> PathBuf { | |||
42 | path.push(".cargo"); | 46 | path.push(".cargo"); |
43 | path.push("bin"); | 47 | path.push("bin"); |
44 | path.push(executable_name); | 48 | path.push(executable_name); |
45 | if path.is_file() { | 49 | if let Some(path) = probe(path) { |
46 | return path; | 50 | return path; |
47 | } | 51 | } |
48 | } | 52 | } |
53 | |||
49 | executable_name.into() | 54 | executable_name.into() |
50 | } | 55 | } |
51 | 56 | ||
52 | fn lookup_in_path(exec: &str) -> bool { | 57 | fn lookup_in_path(exec: &str) -> bool { |
53 | let paths = env::var_os("PATH").unwrap_or_default(); | 58 | let paths = env::var_os("PATH").unwrap_or_default(); |
54 | let mut candidates = env::split_paths(&paths).flat_map(|path| { | 59 | env::split_paths(&paths).map(|path| path.join(exec)).find_map(probe).is_some() |
55 | let candidate = path.join(&exec); | 60 | } |
56 | let with_exe = match env::consts::EXE_EXTENSION { | 61 | |
57 | "" => None, | 62 | fn probe(path: PathBuf) -> Option<PathBuf> { |
58 | it => Some(candidate.with_extension(it)), | 63 | let with_extension = match env::consts::EXE_EXTENSION { |
59 | }; | 64 | "" => None, |
60 | iter::once(candidate).chain(with_exe) | 65 | it => Some(path.with_extension(it)), |
61 | }); | 66 | }; |
62 | candidates.any(|it| it.is_file()) | 67 | iter::once(path).chain(with_extension).find(|it| it.is_file()) |
63 | } | 68 | } |
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 08c67ddd0..122a1605f 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml | |||
@@ -41,6 +41,7 @@ ra_text_edit = { path = "../ra_text_edit" } | |||
41 | vfs = { path = "../vfs" } | 41 | vfs = { path = "../vfs" } |
42 | vfs-notify = { path = "../vfs-notify" } | 42 | vfs-notify = { path = "../vfs-notify" } |
43 | ra_cfg = { path = "../ra_cfg"} | 43 | ra_cfg = { path = "../ra_cfg"} |
44 | ra_toolchain = { path = "../ra_toolchain" } | ||
44 | 45 | ||
45 | # This should only be used in CLI | 46 | # This should only be used in CLI |
46 | ra_db = { path = "../ra_db" } | 47 | ra_db = { path = "../ra_db" } |
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs index f3cdb842b..1cf50b677 100644 --- a/crates/rust-analyzer/src/diagnostics.rs +++ b/crates/rust-analyzer/src/diagnostics.rs | |||
@@ -3,7 +3,6 @@ pub(crate) mod to_proto; | |||
3 | 3 | ||
4 | use std::{collections::HashMap, mem, sync::Arc}; | 4 | use std::{collections::HashMap, mem, sync::Arc}; |
5 | 5 | ||
6 | use lsp_types::{Diagnostic, Range}; | ||
7 | use ra_ide::FileId; | 6 | use ra_ide::FileId; |
8 | use rustc_hash::FxHashSet; | 7 | use rustc_hash::FxHashSet; |
9 | 8 | ||
@@ -19,15 +18,15 @@ pub struct DiagnosticsConfig { | |||
19 | 18 | ||
20 | #[derive(Debug, Default, Clone)] | 19 | #[derive(Debug, Default, Clone)] |
21 | pub(crate) struct DiagnosticCollection { | 20 | pub(crate) struct DiagnosticCollection { |
22 | pub(crate) native: HashMap<FileId, Vec<Diagnostic>>, | 21 | pub(crate) native: HashMap<FileId, Vec<lsp_types::Diagnostic>>, |
23 | pub(crate) check: HashMap<FileId, Vec<Diagnostic>>, | 22 | pub(crate) check: HashMap<FileId, Vec<lsp_types::Diagnostic>>, |
24 | pub(crate) check_fixes: CheckFixes, | 23 | pub(crate) check_fixes: CheckFixes, |
25 | changes: FxHashSet<FileId>, | 24 | changes: FxHashSet<FileId>, |
26 | } | 25 | } |
27 | 26 | ||
28 | #[derive(Debug, Clone)] | 27 | #[derive(Debug, Clone)] |
29 | pub(crate) struct Fix { | 28 | pub(crate) struct Fix { |
30 | pub(crate) range: Range, | 29 | pub(crate) range: lsp_types::Range, |
31 | pub(crate) action: lsp_ext::CodeAction, | 30 | pub(crate) action: lsp_ext::CodeAction, |
32 | } | 31 | } |
33 | 32 | ||
@@ -40,7 +39,7 @@ impl DiagnosticCollection { | |||
40 | pub(crate) fn add_check_diagnostic( | 39 | pub(crate) fn add_check_diagnostic( |
41 | &mut self, | 40 | &mut self, |
42 | file_id: FileId, | 41 | file_id: FileId, |
43 | diagnostic: Diagnostic, | 42 | diagnostic: lsp_types::Diagnostic, |
44 | fixes: Vec<lsp_ext::CodeAction>, | 43 | fixes: Vec<lsp_ext::CodeAction>, |
45 | ) { | 44 | ) { |
46 | let diagnostics = self.check.entry(file_id).or_default(); | 45 | let diagnostics = self.check.entry(file_id).or_default(); |
@@ -59,12 +58,19 @@ impl DiagnosticCollection { | |||
59 | self.changes.insert(file_id); | 58 | self.changes.insert(file_id); |
60 | } | 59 | } |
61 | 60 | ||
62 | pub(crate) fn set_native_diagnostics(&mut self, file_id: FileId, diagnostics: Vec<Diagnostic>) { | 61 | pub(crate) fn set_native_diagnostics( |
62 | &mut self, | ||
63 | file_id: FileId, | ||
64 | diagnostics: Vec<lsp_types::Diagnostic>, | ||
65 | ) { | ||
63 | self.native.insert(file_id, diagnostics); | 66 | self.native.insert(file_id, diagnostics); |
64 | self.changes.insert(file_id); | 67 | self.changes.insert(file_id); |
65 | } | 68 | } |
66 | 69 | ||
67 | pub(crate) fn diagnostics_for(&self, file_id: FileId) -> impl Iterator<Item = &Diagnostic> { | 70 | pub(crate) fn diagnostics_for( |
71 | &self, | ||
72 | file_id: FileId, | ||
73 | ) -> impl Iterator<Item = &lsp_types::Diagnostic> { | ||
68 | let native = self.native.get(&file_id).into_iter().flatten(); | 74 | let native = self.native.get(&file_id).into_iter().flatten(); |
69 | let check = self.check.get(&file_id).into_iter().flatten(); | 75 | let check = self.check.get(&file_id).into_iter().flatten(); |
70 | native.chain(check) | 76 | native.chain(check) |
@@ -78,7 +84,7 @@ impl DiagnosticCollection { | |||
78 | } | 84 | } |
79 | } | 85 | } |
80 | 86 | ||
81 | fn are_diagnostics_equal(left: &Diagnostic, right: &Diagnostic) -> bool { | 87 | fn are_diagnostics_equal(left: &lsp_types::Diagnostic, right: &lsp_types::Diagnostic) -> bool { |
82 | left.source == right.source | 88 | left.source == right.source |
83 | && left.severity == right.severity | 89 | && left.severity == right.severity |
84 | && left.range == right.range | 90 | && left.range == right.range |
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index f379f5ed0..3eed118a9 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs | |||
@@ -167,9 +167,9 @@ fn map_rust_child_diagnostic( | |||
167 | 167 | ||
168 | #[derive(Debug)] | 168 | #[derive(Debug)] |
169 | pub(crate) struct MappedRustDiagnostic { | 169 | pub(crate) struct MappedRustDiagnostic { |
170 | pub location: Location, | 170 | pub(crate) location: Location, |
171 | pub diagnostic: Diagnostic, | 171 | pub(crate) diagnostic: Diagnostic, |
172 | pub fixes: Vec<lsp_ext::CodeAction>, | 172 | pub(crate) fixes: Vec<lsp_ext::CodeAction>, |
173 | } | 173 | } |
174 | 174 | ||
175 | /// Converts a Rust root diagnostic to LSP form | 175 | /// Converts a Rust root diagnostic to LSP form |
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 7533bb319..b8aa1e5b5 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs | |||
@@ -12,6 +12,7 @@ use parking_lot::RwLock; | |||
12 | use ra_db::{CrateId, VfsPath}; | 12 | use ra_db::{CrateId, VfsPath}; |
13 | use ra_ide::{Analysis, AnalysisChange, AnalysisHost, FileId}; | 13 | use ra_ide::{Analysis, AnalysisChange, AnalysisHost, FileId}; |
14 | use ra_project_model::{CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target}; | 14 | use ra_project_model::{CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target}; |
15 | use rustc_hash::{FxHashMap, FxHashSet}; | ||
15 | 16 | ||
16 | use crate::{ | 17 | use crate::{ |
17 | config::Config, | 18 | config::Config, |
@@ -21,12 +22,10 @@ use crate::{ | |||
21 | main_loop::Task, | 22 | main_loop::Task, |
22 | reload::SourceRootConfig, | 23 | reload::SourceRootConfig, |
23 | request_metrics::{LatestRequests, RequestMetrics}, | 24 | request_metrics::{LatestRequests, RequestMetrics}, |
24 | show_message, | ||
25 | thread_pool::TaskPool, | 25 | thread_pool::TaskPool, |
26 | to_proto::url_from_abs_path, | 26 | to_proto::url_from_abs_path, |
27 | Result, | 27 | Result, |
28 | }; | 28 | }; |
29 | use rustc_hash::{FxHashMap, FxHashSet}; | ||
30 | 29 | ||
31 | #[derive(Eq, PartialEq)] | 30 | #[derive(Eq, PartialEq)] |
32 | pub(crate) enum Status { | 31 | pub(crate) enum Status { |
@@ -58,6 +57,7 @@ pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>; | |||
58 | /// Note that this struct has more than on impl in various modules! | 57 | /// Note that this struct has more than on impl in various modules! |
59 | pub(crate) struct GlobalState { | 58 | pub(crate) struct GlobalState { |
60 | sender: Sender<lsp_server::Message>, | 59 | sender: Sender<lsp_server::Message>, |
60 | req_queue: ReqQueue, | ||
61 | pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>, | 61 | pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>, |
62 | pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>, | 62 | pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>, |
63 | pub(crate) flycheck: Option<Handle<FlycheckHandle, Receiver<flycheck::Message>>>, | 63 | pub(crate) flycheck: Option<Handle<FlycheckHandle, Receiver<flycheck::Message>>>, |
@@ -67,7 +67,6 @@ pub(crate) struct GlobalState { | |||
67 | pub(crate) mem_docs: FxHashSet<VfsPath>, | 67 | pub(crate) mem_docs: FxHashSet<VfsPath>, |
68 | pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>, | 68 | pub(crate) vfs: Arc<RwLock<(vfs::Vfs, FxHashMap<FileId, LineEndings>)>>, |
69 | pub(crate) status: Status, | 69 | pub(crate) status: Status, |
70 | pub(crate) req_queue: ReqQueue, | ||
71 | pub(crate) source_root_config: SourceRootConfig, | 70 | pub(crate) source_root_config: SourceRootConfig, |
72 | pub(crate) proc_macro_client: ProcMacroClient, | 71 | pub(crate) proc_macro_client: ProcMacroClient, |
73 | pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>, | 72 | pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>, |
@@ -103,16 +102,16 @@ impl GlobalState { | |||
103 | let analysis_host = AnalysisHost::new(config.lru_capacity); | 102 | let analysis_host = AnalysisHost::new(config.lru_capacity); |
104 | GlobalState { | 103 | GlobalState { |
105 | sender, | 104 | sender, |
105 | req_queue: ReqQueue::default(), | ||
106 | task_pool, | 106 | task_pool, |
107 | loader, | 107 | loader, |
108 | flycheck: None, | ||
108 | config, | 109 | config, |
109 | analysis_host, | 110 | analysis_host, |
110 | flycheck: None, | ||
111 | diagnostics: Default::default(), | 111 | diagnostics: Default::default(), |
112 | mem_docs: FxHashSet::default(), | 112 | mem_docs: FxHashSet::default(), |
113 | vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))), | 113 | vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))), |
114 | status: Status::default(), | 114 | status: Status::default(), |
115 | req_queue: ReqQueue::default(), | ||
116 | source_root_config: SourceRootConfig::default(), | 115 | source_root_config: SourceRootConfig::default(), |
117 | proc_macro_client: ProcMacroClient::dummy(), | 116 | proc_macro_client: ProcMacroClient::dummy(), |
118 | workspaces: Arc::new(Vec::new()), | 117 | workspaces: Arc::new(Vec::new()), |
@@ -169,8 +168,35 @@ impl GlobalState { | |||
169 | } | 168 | } |
170 | } | 169 | } |
171 | 170 | ||
172 | pub(crate) fn send(&mut self, message: lsp_server::Message) { | 171 | pub(crate) fn send_request<R: lsp_types::request::Request>( |
173 | self.sender.send(message).unwrap() | 172 | &mut self, |
173 | params: R::Params, | ||
174 | handler: ReqHandler, | ||
175 | ) { | ||
176 | let request = self.req_queue.outgoing.register(R::METHOD.to_string(), params, handler); | ||
177 | self.send(request.into()); | ||
178 | } | ||
179 | pub(crate) fn complete_request(&mut self, response: lsp_server::Response) { | ||
180 | let handler = self.req_queue.outgoing.complete(response.id.clone()); | ||
181 | handler(self, response) | ||
182 | } | ||
183 | |||
184 | pub(crate) fn send_notification<N: lsp_types::notification::Notification>( | ||
185 | &mut self, | ||
186 | params: N::Params, | ||
187 | ) { | ||
188 | let not = lsp_server::Notification::new(N::METHOD.to_string(), params); | ||
189 | self.send(not.into()); | ||
190 | } | ||
191 | |||
192 | pub(crate) fn register_request( | ||
193 | &mut self, | ||
194 | request: &lsp_server::Request, | ||
195 | request_received: Instant, | ||
196 | ) { | ||
197 | self.req_queue | ||
198 | .incoming | ||
199 | .register(request.id.clone(), (request.method.clone(), request_received)); | ||
174 | } | 200 | } |
175 | pub(crate) fn respond(&mut self, response: lsp_server::Response) { | 201 | pub(crate) fn respond(&mut self, response: lsp_server::Response) { |
176 | if let Some((method, start)) = self.req_queue.incoming.complete(response.id.clone()) { | 202 | if let Some((method, start)) = self.req_queue.incoming.complete(response.id.clone()) { |
@@ -182,8 +208,14 @@ impl GlobalState { | |||
182 | self.send(response.into()); | 208 | self.send(response.into()); |
183 | } | 209 | } |
184 | } | 210 | } |
185 | pub(crate) fn show_message(&self, typ: lsp_types::MessageType, message: String) { | 211 | pub(crate) fn cancel(&mut self, request_id: lsp_server::RequestId) { |
186 | show_message(typ, message, &self.sender) | 212 | if let Some(response) = self.req_queue.incoming.cancel(request_id) { |
213 | self.send(response.into()); | ||
214 | } | ||
215 | } | ||
216 | |||
217 | fn send(&mut self, message: lsp_server::Message) { | ||
218 | self.sender.send(message).unwrap() | ||
187 | } | 219 | } |
188 | } | 220 | } |
189 | 221 | ||
@@ -195,11 +227,7 @@ impl Drop for GlobalState { | |||
195 | 227 | ||
196 | impl GlobalStateSnapshot { | 228 | impl GlobalStateSnapshot { |
197 | pub(crate) fn url_to_file_id(&self, url: &Url) -> Result<FileId> { | 229 | pub(crate) fn url_to_file_id(&self, url: &Url) -> Result<FileId> { |
198 | let path = from_proto::abs_path(url)?; | 230 | url_to_file_id(&self.vfs.read().0, url) |
199 | let path = path.into(); | ||
200 | let res = | ||
201 | self.vfs.read().0.file_id(&path).ok_or_else(|| format!("file not found: {}", path))?; | ||
202 | Ok(res) | ||
203 | } | 231 | } |
204 | 232 | ||
205 | pub(crate) fn file_id_to_url(&self, id: FileId) -> Url { | 233 | pub(crate) fn file_id_to_url(&self, id: FileId) -> Url { |
@@ -213,7 +241,7 @@ impl GlobalStateSnapshot { | |||
213 | pub(crate) fn anchored_path(&self, file_id: FileId, path: &str) -> Url { | 241 | pub(crate) fn anchored_path(&self, file_id: FileId, path: &str) -> Url { |
214 | let mut base = self.vfs.read().0.file_path(file_id); | 242 | let mut base = self.vfs.read().0.file_path(file_id); |
215 | base.pop(); | 243 | base.pop(); |
216 | let path = base.join(path); | 244 | let path = base.join(path).unwrap(); |
217 | let path = path.as_path().unwrap(); | 245 | let path = path.as_path().unwrap(); |
218 | url_from_abs_path(&path) | 246 | url_from_abs_path(&path) |
219 | } | 247 | } |
@@ -239,3 +267,9 @@ pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url { | |||
239 | let path = path.as_path().unwrap(); | 267 | let path = path.as_path().unwrap(); |
240 | url_from_abs_path(&path) | 268 | url_from_abs_path(&path) |
241 | } | 269 | } |
270 | |||
271 | pub(crate) fn url_to_file_id(vfs: &vfs::Vfs, url: &Url) -> Result<FileId> { | ||
272 | let path = from_proto::vfs_path(url)?; | ||
273 | let res = vfs.file_id(&path).ok_or_else(|| format!("file not found: {}", path))?; | ||
274 | Ok(res) | ||
275 | } | ||
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 6c21f25fe..38e3c3324 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -650,7 +650,7 @@ pub(crate) fn handle_formatting( | |||
650 | 650 | ||
651 | let mut rustfmt = match &snap.config.rustfmt { | 651 | let mut rustfmt = match &snap.config.rustfmt { |
652 | RustfmtConfig::Rustfmt { extra_args } => { | 652 | RustfmtConfig::Rustfmt { extra_args } => { |
653 | let mut cmd = process::Command::new("rustfmt"); | 653 | let mut cmd = process::Command::new(ra_toolchain::rustfmt()); |
654 | cmd.args(extra_args); | 654 | cmd.args(extra_args); |
655 | if let Some(&crate_id) = crate_ids.first() { | 655 | if let Some(&crate_id) = crate_ids.first() { |
656 | // Assume all crates are in the same edition | 656 | // Assume all crates are in the same edition |
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs index a24dfe58c..407944d85 100644 --- a/crates/rust-analyzer/src/lib.rs +++ b/crates/rust-analyzer/src/lib.rs | |||
@@ -39,7 +39,7 @@ pub mod config; | |||
39 | use serde::de::DeserializeOwned; | 39 | use serde::de::DeserializeOwned; |
40 | 40 | ||
41 | pub type Result<T, E = Box<dyn std::error::Error + Send + Sync>> = std::result::Result<T, E>; | 41 | pub type Result<T, E = Box<dyn std::error::Error + Send + Sync>> = std::result::Result<T, E>; |
42 | pub use crate::{caps::server_capabilities, lsp_utils::show_message, main_loop::main_loop}; | 42 | pub use crate::{caps::server_capabilities, main_loop::main_loop}; |
43 | use std::fmt; | 43 | use std::fmt; |
44 | 44 | ||
45 | pub fn from_json<T: DeserializeOwned>(what: &'static str, json: serde_json::Value) -> Result<T> { | 45 | pub fn from_json<T: DeserializeOwned>(what: &'static str, json: serde_json::Value) -> Result<T> { |
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs index 35917030c..0bc3ff115 100644 --- a/crates/rust-analyzer/src/lsp_utils.rs +++ b/crates/rust-analyzer/src/lsp_utils.rs | |||
@@ -1,24 +1,11 @@ | |||
1 | //! Utilities for LSP-related boilerplate code. | 1 | //! Utilities for LSP-related boilerplate code. |
2 | use std::{error::Error, ops::Range}; | 2 | use std::{error::Error, ops::Range}; |
3 | 3 | ||
4 | use crossbeam_channel::Sender; | 4 | use lsp_server::Notification; |
5 | use lsp_server::{Message, Notification}; | ||
6 | use ra_db::Canceled; | 5 | use ra_db::Canceled; |
7 | use ra_ide::LineIndex; | 6 | use ra_ide::LineIndex; |
8 | use serde::Serialize; | ||
9 | 7 | ||
10 | use crate::from_proto; | 8 | use crate::{from_proto, global_state::GlobalState}; |
11 | |||
12 | pub fn show_message( | ||
13 | typ: lsp_types::MessageType, | ||
14 | message: impl Into<String>, | ||
15 | sender: &Sender<Message>, | ||
16 | ) { | ||
17 | let message = message.into(); | ||
18 | let params = lsp_types::ShowMessageParams { typ, message }; | ||
19 | let not = notification_new::<lsp_types::notification::ShowMessage>(params); | ||
20 | sender.send(not.into()).unwrap(); | ||
21 | } | ||
22 | 9 | ||
23 | pub(crate) fn is_canceled(e: &(dyn Error + 'static)) -> bool { | 10 | pub(crate) fn is_canceled(e: &(dyn Error + 'static)) -> bool { |
24 | e.downcast_ref::<Canceled>().is_some() | 11 | e.downcast_ref::<Canceled>().is_some() |
@@ -30,12 +17,68 @@ pub(crate) fn notification_is<N: lsp_types::notification::Notification>( | |||
30 | notification.method == N::METHOD | 17 | notification.method == N::METHOD |
31 | } | 18 | } |
32 | 19 | ||
33 | pub(crate) fn notification_new<N>(params: N::Params) -> Notification | 20 | #[derive(Debug, Eq, PartialEq)] |
34 | where | 21 | pub(crate) enum Progress { |
35 | N: lsp_types::notification::Notification, | 22 | Begin, |
36 | N::Params: Serialize, | 23 | Report, |
37 | { | 24 | End, |
38 | Notification::new(N::METHOD.to_string(), params) | 25 | } |
26 | |||
27 | impl Progress { | ||
28 | pub(crate) fn percentage(done: usize, total: usize) -> f64 { | ||
29 | (done as f64 / total.max(1) as f64) * 100.0 | ||
30 | } | ||
31 | } | ||
32 | |||
33 | impl GlobalState { | ||
34 | pub(crate) fn show_message(&mut self, typ: lsp_types::MessageType, message: String) { | ||
35 | let message = message.into(); | ||
36 | self.send_notification::<lsp_types::notification::ShowMessage>( | ||
37 | lsp_types::ShowMessageParams { typ, message }, | ||
38 | ) | ||
39 | } | ||
40 | |||
41 | pub(crate) fn report_progress( | ||
42 | &mut self, | ||
43 | title: &str, | ||
44 | state: Progress, | ||
45 | message: Option<String>, | ||
46 | percentage: Option<f64>, | ||
47 | ) { | ||
48 | if !self.config.client_caps.work_done_progress { | ||
49 | return; | ||
50 | } | ||
51 | let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title)); | ||
52 | let work_done_progress = match state { | ||
53 | Progress::Begin => { | ||
54 | self.send_request::<lsp_types::request::WorkDoneProgressCreate>( | ||
55 | lsp_types::WorkDoneProgressCreateParams { token: token.clone() }, | ||
56 | |_, _| (), | ||
57 | ); | ||
58 | |||
59 | lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin { | ||
60 | title: title.into(), | ||
61 | cancellable: None, | ||
62 | message, | ||
63 | percentage, | ||
64 | }) | ||
65 | } | ||
66 | Progress::Report => { | ||
67 | lsp_types::WorkDoneProgress::Report(lsp_types::WorkDoneProgressReport { | ||
68 | cancellable: None, | ||
69 | message, | ||
70 | percentage, | ||
71 | }) | ||
72 | } | ||
73 | Progress::End => { | ||
74 | lsp_types::WorkDoneProgress::End(lsp_types::WorkDoneProgressEnd { message }) | ||
75 | } | ||
76 | }; | ||
77 | self.send_notification::<lsp_types::notification::Progress>(lsp_types::ProgressParams { | ||
78 | token, | ||
79 | value: lsp_types::ProgressParamsValue::WorkDone(work_done_progress), | ||
80 | }); | ||
81 | } | ||
39 | } | 82 | } |
40 | 83 | ||
41 | pub(crate) fn apply_document_changes( | 84 | pub(crate) fn apply_document_changes( |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 162c0057e..e5194fe41 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -7,7 +7,7 @@ use std::{ | |||
7 | 7 | ||
8 | use crossbeam_channel::{never, select, Receiver}; | 8 | use crossbeam_channel::{never, select, Receiver}; |
9 | use lsp_server::{Connection, Notification, Request, Response}; | 9 | use lsp_server::{Connection, Notification, Request, Response}; |
10 | use lsp_types::{notification::Notification as _, request::Request as _}; | 10 | use lsp_types::notification::Notification as _; |
11 | use ra_db::VfsPath; | 11 | use ra_db::VfsPath; |
12 | use ra_ide::{Canceled, FileId}; | 12 | use ra_ide::{Canceled, FileId}; |
13 | use ra_prof::profile; | 13 | use ra_prof::profile; |
@@ -16,9 +16,9 @@ use crate::{ | |||
16 | config::Config, | 16 | config::Config, |
17 | dispatch::{NotificationDispatcher, RequestDispatcher}, | 17 | dispatch::{NotificationDispatcher, RequestDispatcher}, |
18 | from_proto, | 18 | from_proto, |
19 | global_state::{file_id_to_url, GlobalState, Status}, | 19 | global_state::{file_id_to_url, url_to_file_id, GlobalState, Status}, |
20 | handlers, lsp_ext, | 20 | handlers, lsp_ext, |
21 | lsp_utils::{apply_document_changes, is_canceled, notification_is, notification_new}, | 21 | lsp_utils::{apply_document_changes, is_canceled, notification_is, Progress}, |
22 | Result, | 22 | Result, |
23 | }; | 23 | }; |
24 | 24 | ||
@@ -143,10 +143,7 @@ impl GlobalState { | |||
143 | lsp_server::Message::Notification(not) => { | 143 | lsp_server::Message::Notification(not) => { |
144 | self.on_notification(not)?; | 144 | self.on_notification(not)?; |
145 | } | 145 | } |
146 | lsp_server::Message::Response(resp) => { | 146 | lsp_server::Message::Response(resp) => self.complete_request(resp), |
147 | let handler = self.req_queue.outgoing.complete(resp.id.clone()); | ||
148 | handler(self, resp) | ||
149 | } | ||
150 | }, | 147 | }, |
151 | Event::Task(task) => { | 148 | Event::Task(task) => { |
152 | match task { | 149 | match task { |
@@ -181,18 +178,15 @@ impl GlobalState { | |||
181 | became_ready = true; | 178 | became_ready = true; |
182 | Progress::End | 179 | Progress::End |
183 | }; | 180 | }; |
184 | report_progress( | 181 | self.report_progress( |
185 | self, | ||
186 | "roots scanned", | 182 | "roots scanned", |
187 | state, | 183 | state, |
188 | Some(format!("{}/{}", n_done, n_total)), | 184 | Some(format!("{}/{}", n_done, n_total)), |
189 | Some(percentage(n_done, n_total)), | 185 | Some(Progress::percentage(n_done, n_total)), |
190 | ) | 186 | ) |
191 | } | 187 | } |
192 | }, | 188 | }, |
193 | Event::Flycheck(task) => match task { | 189 | Event::Flycheck(task) => match task { |
194 | flycheck::Message::ClearDiagnostics => self.diagnostics.clear_check(), | ||
195 | |||
196 | flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => { | 190 | flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => { |
197 | let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( | 191 | let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( |
198 | &self.config.diagnostics, | 192 | &self.config.diagnostics, |
@@ -200,31 +194,34 @@ impl GlobalState { | |||
200 | &workspace_root, | 194 | &workspace_root, |
201 | ); | 195 | ); |
202 | for diag in diagnostics { | 196 | for diag in diagnostics { |
203 | let path = from_proto::vfs_path(&diag.location.uri)?; | 197 | match url_to_file_id(&self.vfs.read().0, &diag.location.uri) { |
204 | let file_id = match self.vfs.read().0.file_id(&path) { | 198 | Ok(file_id) => self.diagnostics.add_check_diagnostic( |
205 | Some(file) => FileId(file.0), | 199 | file_id, |
206 | None => { | 200 | diag.diagnostic, |
207 | log::error!( | 201 | diag.fixes, |
208 | "File with cargo diagnostic not found in VFS: {}", | 202 | ), |
209 | path | 203 | Err(err) => { |
210 | ); | 204 | log::error!("File with cargo diagnostic not found in VFS: {}", err); |
211 | return Ok(()); | ||
212 | } | 205 | } |
213 | }; | 206 | }; |
214 | self.diagnostics.add_check_diagnostic(file_id, diag.diagnostic, diag.fixes) | ||
215 | } | 207 | } |
216 | } | 208 | } |
217 | 209 | ||
218 | flycheck::Message::Progress(status) => { | 210 | flycheck::Message::Progress(status) => { |
219 | let (state, message) = match status { | 211 | let (state, message) = match status { |
220 | flycheck::Progress::Being => (Progress::Begin, None), | 212 | flycheck::Progress::DidStart => { |
213 | self.diagnostics.clear_check(); | ||
214 | (Progress::Begin, None) | ||
215 | } | ||
221 | flycheck::Progress::DidCheckCrate(target) => { | 216 | flycheck::Progress::DidCheckCrate(target) => { |
222 | (Progress::Report, Some(target)) | 217 | (Progress::Report, Some(target)) |
223 | } | 218 | } |
224 | flycheck::Progress::End => (Progress::End, None), | 219 | flycheck::Progress::DidFinish | flycheck::Progress::DidCancel => { |
220 | (Progress::End, None) | ||
221 | } | ||
225 | }; | 222 | }; |
226 | 223 | ||
227 | report_progress(self, "cargo check", state, message, None); | 224 | self.report_progress("cargo check", state, message, None); |
228 | } | 225 | } |
229 | }, | 226 | }, |
230 | } | 227 | } |
@@ -250,10 +247,9 @@ impl GlobalState { | |||
250 | for file_id in diagnostic_changes { | 247 | for file_id in diagnostic_changes { |
251 | let url = file_id_to_url(&self.vfs.read().0, file_id); | 248 | let url = file_id_to_url(&self.vfs.read().0, file_id); |
252 | let diagnostics = self.diagnostics.diagnostics_for(file_id).cloned().collect(); | 249 | let diagnostics = self.diagnostics.diagnostics_for(file_id).cloned().collect(); |
253 | let params = | 250 | self.send_notification::<lsp_types::notification::PublishDiagnostics>( |
254 | lsp_types::PublishDiagnosticsParams { uri: url, diagnostics, version: None }; | 251 | lsp_types::PublishDiagnosticsParams { uri: url, diagnostics, version: None }, |
255 | let not = notification_new::<lsp_types::notification::PublishDiagnostics>(params); | 252 | ); |
256 | self.send(not.into()); | ||
257 | } | 253 | } |
258 | } | 254 | } |
259 | 255 | ||
@@ -271,7 +267,7 @@ impl GlobalState { | |||
271 | } | 267 | } |
272 | 268 | ||
273 | fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> { | 269 | fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> { |
274 | self.req_queue.incoming.register(req.id.clone(), (req.method.clone(), request_received)); | 270 | self.register_request(&req, request_received); |
275 | 271 | ||
276 | RequestDispatcher { req: Some(req), global_state: self } | 272 | RequestDispatcher { req: Some(req), global_state: self } |
277 | .on_sync::<lsp_ext::CollectGarbage>(|s, ()| Ok(s.analysis_host.collect_garbage()))? | 273 | .on_sync::<lsp_ext::CollectGarbage>(|s, ()| Ok(s.analysis_host.collect_garbage()))? |
@@ -335,9 +331,7 @@ impl GlobalState { | |||
335 | lsp_types::NumberOrString::Number(id) => id.into(), | 331 | lsp_types::NumberOrString::Number(id) => id.into(), |
336 | lsp_types::NumberOrString::String(id) => id.into(), | 332 | lsp_types::NumberOrString::String(id) => id.into(), |
337 | }; | 333 | }; |
338 | if let Some(response) = this.req_queue.incoming.cancel(id) { | 334 | this.cancel(id); |
339 | this.send(response.into()); | ||
340 | } | ||
341 | Ok(()) | 335 | Ok(()) |
342 | })? | 336 | })? |
343 | .on::<lsp_types::notification::DidOpenTextDocument>(|this, params| { | 337 | .on::<lsp_types::notification::DidOpenTextDocument>(|this, params| { |
@@ -372,13 +366,13 @@ impl GlobalState { | |||
372 | this.loader.handle.invalidate(path.to_path_buf()); | 366 | this.loader.handle.invalidate(path.to_path_buf()); |
373 | } | 367 | } |
374 | } | 368 | } |
375 | let params = lsp_types::PublishDiagnosticsParams { | 369 | this.send_notification::<lsp_types::notification::PublishDiagnostics>( |
376 | uri: params.text_document.uri, | 370 | lsp_types::PublishDiagnosticsParams { |
377 | diagnostics: Vec::new(), | 371 | uri: params.text_document.uri, |
378 | version: None, | 372 | diagnostics: Vec::new(), |
379 | }; | 373 | version: None, |
380 | let not = notification_new::<lsp_types::notification::PublishDiagnostics>(params); | 374 | }, |
381 | this.send(not.into()); | 375 | ); |
382 | Ok(()) | 376 | Ok(()) |
383 | })? | 377 | })? |
384 | .on::<lsp_types::notification::DidSaveTextDocument>(|this, _params| { | 378 | .on::<lsp_types::notification::DidSaveTextDocument>(|this, _params| { |
@@ -390,8 +384,7 @@ impl GlobalState { | |||
390 | .on::<lsp_types::notification::DidChangeConfiguration>(|this, _params| { | 384 | .on::<lsp_types::notification::DidChangeConfiguration>(|this, _params| { |
391 | // As stated in https://github.com/microsoft/language-server-protocol/issues/676, | 385 | // As stated in https://github.com/microsoft/language-server-protocol/issues/676, |
392 | // this notification's parameters should be ignored and the actual config queried separately. | 386 | // this notification's parameters should be ignored and the actual config queried separately. |
393 | let request = this.req_queue.outgoing.register( | 387 | this.send_request::<lsp_types::request::WorkspaceConfiguration>( |
394 | lsp_types::request::WorkspaceConfiguration::METHOD.to_string(), | ||
395 | lsp_types::ConfigurationParams { | 388 | lsp_types::ConfigurationParams { |
396 | items: vec![lsp_types::ConfigurationItem { | 389 | items: vec![lsp_types::ConfigurationItem { |
397 | scope_uri: None, | 390 | scope_uri: None, |
@@ -419,7 +412,6 @@ impl GlobalState { | |||
419 | } | 412 | } |
420 | }, | 413 | }, |
421 | ); | 414 | ); |
422 | this.send(request.into()); | ||
423 | 415 | ||
424 | return Ok(()); | 416 | return Ok(()); |
425 | })? | 417 | })? |
@@ -467,60 +459,3 @@ impl GlobalState { | |||
467 | }); | 459 | }); |
468 | } | 460 | } |
469 | } | 461 | } |
470 | |||
471 | #[derive(Eq, PartialEq)] | ||
472 | enum Progress { | ||
473 | Begin, | ||
474 | Report, | ||
475 | End, | ||
476 | } | ||
477 | |||
478 | fn percentage(done: usize, total: usize) -> f64 { | ||
479 | (done as f64 / total.max(1) as f64) * 100.0 | ||
480 | } | ||
481 | |||
482 | fn report_progress( | ||
483 | global_state: &mut GlobalState, | ||
484 | title: &str, | ||
485 | state: Progress, | ||
486 | message: Option<String>, | ||
487 | percentage: Option<f64>, | ||
488 | ) { | ||
489 | if !global_state.config.client_caps.work_done_progress { | ||
490 | return; | ||
491 | } | ||
492 | let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title)); | ||
493 | let work_done_progress = match state { | ||
494 | Progress::Begin => { | ||
495 | let work_done_progress_create = global_state.req_queue.outgoing.register( | ||
496 | lsp_types::request::WorkDoneProgressCreate::METHOD.to_string(), | ||
497 | lsp_types::WorkDoneProgressCreateParams { token: token.clone() }, | ||
498 | |_, _| (), | ||
499 | ); | ||
500 | global_state.send(work_done_progress_create.into()); | ||
501 | |||
502 | lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin { | ||
503 | title: title.into(), | ||
504 | cancellable: None, | ||
505 | message, | ||
506 | percentage, | ||
507 | }) | ||
508 | } | ||
509 | Progress::Report => { | ||
510 | lsp_types::WorkDoneProgress::Report(lsp_types::WorkDoneProgressReport { | ||
511 | cancellable: None, | ||
512 | message, | ||
513 | percentage, | ||
514 | }) | ||
515 | } | ||
516 | Progress::End => { | ||
517 | lsp_types::WorkDoneProgress::End(lsp_types::WorkDoneProgressEnd { message }) | ||
518 | } | ||
519 | }; | ||
520 | let notification = | ||
521 | notification_new::<lsp_types::notification::Progress>(lsp_types::ProgressParams { | ||
522 | token, | ||
523 | value: lsp_types::ProgressParamsValue::WorkDone(work_done_progress), | ||
524 | }); | ||
525 | global_state.send(notification.into()); | ||
526 | } | ||
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index a22d3e262..ec71f8b29 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs | |||
@@ -1,9 +1,8 @@ | |||
1 | //! Project loading & configuration updates | 1 | //! Project loading & configuration updates |
2 | use std::sync::Arc; | 2 | use std::{mem, sync::Arc}; |
3 | 3 | ||
4 | use crossbeam_channel::unbounded; | 4 | use crossbeam_channel::unbounded; |
5 | use flycheck::FlycheckHandle; | 5 | use flycheck::FlycheckHandle; |
6 | use lsp_types::request::Request; | ||
7 | use ra_db::{CrateGraph, SourceRoot, VfsPath}; | 6 | use ra_db::{CrateGraph, SourceRoot, VfsPath}; |
8 | use ra_ide::AnalysisChange; | 7 | use ra_ide::AnalysisChange; |
9 | use ra_project_model::{PackageRoot, ProcMacroClient, ProjectWorkspace}; | 8 | use ra_project_model::{PackageRoot, ProcMacroClient, ProjectWorkspace}; |
@@ -15,12 +14,14 @@ use crate::{ | |||
15 | }; | 14 | }; |
16 | 15 | ||
17 | impl GlobalState { | 16 | impl GlobalState { |
18 | pub(crate) fn update_configuration(&mut self, new_config: Config) { | 17 | pub(crate) fn update_configuration(&mut self, config: Config) { |
19 | self.analysis_host.update_lru_capacity(new_config.lru_capacity); | 18 | let old_config = mem::replace(&mut self.config, config); |
20 | if new_config.flycheck != self.config.flycheck { | 19 | if self.config.lru_capacity != old_config.lru_capacity { |
20 | self.analysis_host.update_lru_capacity(old_config.lru_capacity); | ||
21 | } | ||
22 | if self.config.flycheck != old_config.flycheck { | ||
21 | self.reload_flycheck(); | 23 | self.reload_flycheck(); |
22 | } | 24 | } |
23 | self.config = new_config; | ||
24 | } | 25 | } |
25 | pub(crate) fn reload(&mut self) { | 26 | pub(crate) fn reload(&mut self) { |
26 | let workspaces = { | 27 | let workspaces = { |
@@ -36,27 +37,31 @@ impl GlobalState { | |||
36 | self.config | 37 | self.config |
37 | .linked_projects | 38 | .linked_projects |
38 | .iter() | 39 | .iter() |
39 | .filter_map(|project| match project { | 40 | .map(|project| match project { |
40 | LinkedProject::ProjectManifest(manifest) => { | 41 | LinkedProject::ProjectManifest(manifest) => { |
41 | ra_project_model::ProjectWorkspace::load( | 42 | ra_project_model::ProjectWorkspace::load( |
42 | manifest.clone(), | 43 | manifest.clone(), |
43 | &self.config.cargo, | 44 | &self.config.cargo, |
44 | self.config.with_sysroot, | 45 | self.config.with_sysroot, |
45 | ) | 46 | ) |
46 | .map_err(|err| { | ||
47 | log::error!("failed to load workspace: {:#}", err); | ||
48 | self.show_message( | ||
49 | lsp_types::MessageType::Error, | ||
50 | format!("rust-analyzer failed to load workspace: {:#}", err), | ||
51 | ); | ||
52 | }) | ||
53 | .ok() | ||
54 | } | 47 | } |
55 | LinkedProject::InlineJsonProject(it) => { | 48 | LinkedProject::InlineJsonProject(it) => { |
56 | Some(ra_project_model::ProjectWorkspace::Json { project: it.clone() }) | 49 | Ok(ra_project_model::ProjectWorkspace::Json { project: it.clone() }) |
57 | } | 50 | } |
58 | }) | 51 | }) |
59 | .collect::<Vec<_>>() | 52 | .collect::<Vec<_>>() |
53 | .into_iter() | ||
54 | .filter_map(|res| { | ||
55 | res.map_err(|err| { | ||
56 | log::error!("failed to load workspace: {:#}", err); | ||
57 | self.show_message( | ||
58 | lsp_types::MessageType::Error, | ||
59 | format!("rust-analyzer failed to load workspace: {:#}", err), | ||
60 | ); | ||
61 | }) | ||
62 | .ok() | ||
63 | }) | ||
64 | .collect::<Vec<_>>() | ||
60 | }; | 65 | }; |
61 | 66 | ||
62 | if let FilesWatcher::Client = self.config.files.watcher { | 67 | if let FilesWatcher::Client = self.config.files.watcher { |
@@ -74,13 +79,10 @@ impl GlobalState { | |||
74 | method: "workspace/didChangeWatchedFiles".to_string(), | 79 | method: "workspace/didChangeWatchedFiles".to_string(), |
75 | register_options: Some(serde_json::to_value(registration_options).unwrap()), | 80 | register_options: Some(serde_json::to_value(registration_options).unwrap()), |
76 | }; | 81 | }; |
77 | let params = lsp_types::RegistrationParams { registrations: vec![registration] }; | 82 | self.send_request::<lsp_types::request::RegisterCapability>( |
78 | let request = self.req_queue.outgoing.register( | 83 | lsp_types::RegistrationParams { registrations: vec![registration] }, |
79 | lsp_types::request::RegisterCapability::METHOD.to_string(), | ||
80 | params, | ||
81 | |_, _| (), | 84 | |_, _| (), |
82 | ); | 85 | ); |
83 | self.send(request.into()); | ||
84 | } | 86 | } |
85 | 87 | ||
86 | let mut change = AnalysisChange::new(); | 88 | let mut change = AnalysisChange::new(); |
diff --git a/crates/vfs/src/file_set.rs b/crates/vfs/src/file_set.rs index 0173f7464..d0ddeafe7 100644 --- a/crates/vfs/src/file_set.rs +++ b/crates/vfs/src/file_set.rs | |||
@@ -18,7 +18,7 @@ impl FileSet { | |||
18 | pub fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> { | 18 | pub fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> { |
19 | let mut base = self.paths[&anchor].clone(); | 19 | let mut base = self.paths[&anchor].clone(); |
20 | base.pop(); | 20 | base.pop(); |
21 | let path = base.join(path); | 21 | let path = base.join(path)?; |
22 | let res = self.files.get(&path).copied(); | 22 | let res = self.files.get(&path).copied(); |
23 | res | 23 | res |
24 | } | 24 | } |
diff --git a/crates/vfs/src/vfs_path.rs b/crates/vfs/src/vfs_path.rs index 940f91d0e..dc3031ada 100644 --- a/crates/vfs/src/vfs_path.rs +++ b/crates/vfs/src/vfs_path.rs | |||
@@ -22,15 +22,15 @@ impl VfsPath { | |||
22 | VfsPathRepr::VirtualPath(_) => None, | 22 | VfsPathRepr::VirtualPath(_) => None, |
23 | } | 23 | } |
24 | } | 24 | } |
25 | pub fn join(&self, path: &str) -> VfsPath { | 25 | pub fn join(&self, path: &str) -> Option<VfsPath> { |
26 | match &self.0 { | 26 | match &self.0 { |
27 | VfsPathRepr::PathBuf(it) => { | 27 | VfsPathRepr::PathBuf(it) => { |
28 | let res = it.join(path).normalize(); | 28 | let res = it.join(path).normalize(); |
29 | VfsPath(VfsPathRepr::PathBuf(res)) | 29 | Some(VfsPath(VfsPathRepr::PathBuf(res))) |
30 | } | 30 | } |
31 | VfsPathRepr::VirtualPath(it) => { | 31 | VfsPathRepr::VirtualPath(it) => { |
32 | let res = it.join(path); | 32 | let res = it.join(path)?; |
33 | VfsPath(VfsPathRepr::VirtualPath(res)) | 33 | Some(VfsPath(VfsPathRepr::VirtualPath(res))) |
34 | } | 34 | } |
35 | } | 35 | } |
36 | } | 36 | } |
@@ -101,13 +101,15 @@ impl VirtualPath { | |||
101 | self.0 = self.0[..pos].to_string(); | 101 | self.0 = self.0[..pos].to_string(); |
102 | true | 102 | true |
103 | } | 103 | } |
104 | fn join(&self, mut path: &str) -> VirtualPath { | 104 | fn join(&self, mut path: &str) -> Option<VirtualPath> { |
105 | let mut res = self.clone(); | 105 | let mut res = self.clone(); |
106 | while path.starts_with("../") { | 106 | while path.starts_with("../") { |
107 | assert!(res.pop()); | 107 | if !res.pop() { |
108 | return None; | ||
109 | } | ||
108 | path = &path["../".len()..] | 110 | path = &path["../".len()..] |
109 | } | 111 | } |
110 | res.0 = format!("{}/{}", res.0, path); | 112 | res.0 = format!("{}/{}", res.0, path); |
111 | res | 113 | Some(res) |
112 | } | 114 | } |
113 | } | 115 | } |