diff options
-rw-r--r-- | crates/ra_flycheck/src/lib.rs | 64 | ||||
-rw-r--r-- | crates/ra_ide/src/ssr.rs | 116 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop.rs | 6 | ||||
-rw-r--r-- | crates/rust-analyzer/src/world.rs | 4 |
4 files changed, 125 insertions, 65 deletions
diff --git a/crates/ra_flycheck/src/lib.rs b/crates/ra_flycheck/src/lib.rs index 75aece45f..ee09d8de3 100644 --- a/crates/ra_flycheck/src/lib.rs +++ b/crates/ra_flycheck/src/lib.rs | |||
@@ -4,9 +4,9 @@ | |||
4 | mod conv; | 4 | mod conv; |
5 | 5 | ||
6 | use std::{ | 6 | use std::{ |
7 | error, fmt, | 7 | env, error, fmt, |
8 | io::{BufRead, BufReader}, | 8 | io::{BufRead, BufReader}, |
9 | path::{Path, PathBuf}, | 9 | path::PathBuf, |
10 | process::{Command, Stdio}, | 10 | process::{Command, Stdio}, |
11 | time::Instant, | 11 | time::Instant, |
12 | }; | 12 | }; |
@@ -23,10 +23,10 @@ use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic}; | |||
23 | pub use crate::conv::url_from_path_with_drive_lowercasing; | 23 | pub use crate::conv::url_from_path_with_drive_lowercasing; |
24 | 24 | ||
25 | #[derive(Clone, Debug)] | 25 | #[derive(Clone, Debug)] |
26 | pub struct CheckConfig { | 26 | pub struct FlycheckConfig { |
27 | pub args: Vec<String>, | ||
28 | pub command: String, | 27 | pub command: String, |
29 | pub all_targets: bool, | 28 | pub all_targets: bool, |
29 | pub extra_args: Vec<String>, | ||
30 | } | 30 | } |
31 | 31 | ||
32 | /// Flycheck wraps the shared state and communication machinery used for | 32 | /// Flycheck wraps the shared state and communication machinery used for |
@@ -42,12 +42,11 @@ pub struct Flycheck { | |||
42 | } | 42 | } |
43 | 43 | ||
44 | impl Flycheck { | 44 | impl Flycheck { |
45 | pub fn new(config: CheckConfig, workspace_root: PathBuf) -> Flycheck { | 45 | pub fn new(config: FlycheckConfig, workspace_root: PathBuf) -> Flycheck { |
46 | let (task_send, task_recv) = unbounded::<CheckTask>(); | 46 | let (task_send, task_recv) = unbounded::<CheckTask>(); |
47 | let (cmd_send, cmd_recv) = unbounded::<CheckCommand>(); | 47 | let (cmd_send, cmd_recv) = unbounded::<CheckCommand>(); |
48 | let handle = jod_thread::spawn(move || { | 48 | let handle = jod_thread::spawn(move || { |
49 | let mut check = FlycheckThread::new(config, workspace_root); | 49 | FlycheckThread::new(config, workspace_root).run(&task_send, &cmd_recv); |
50 | check.run(&task_send, &cmd_recv); | ||
51 | }); | 50 | }); |
52 | Flycheck { task_recv, cmd_send, handle } | 51 | Flycheck { task_recv, cmd_send, handle } |
53 | } | 52 | } |
@@ -76,7 +75,7 @@ pub enum CheckCommand { | |||
76 | } | 75 | } |
77 | 76 | ||
78 | struct FlycheckThread { | 77 | struct FlycheckThread { |
79 | options: CheckConfig, | 78 | config: FlycheckConfig, |
80 | workspace_root: PathBuf, | 79 | workspace_root: PathBuf, |
81 | last_update_req: Option<Instant>, | 80 | last_update_req: Option<Instant>, |
82 | // XXX: drop order is significant | 81 | // XXX: drop order is significant |
@@ -90,9 +89,9 @@ struct FlycheckThread { | |||
90 | } | 89 | } |
91 | 90 | ||
92 | impl FlycheckThread { | 91 | impl FlycheckThread { |
93 | fn new(options: CheckConfig, workspace_root: PathBuf) -> FlycheckThread { | 92 | fn new(config: FlycheckConfig, workspace_root: PathBuf) -> FlycheckThread { |
94 | FlycheckThread { | 93 | FlycheckThread { |
95 | options, | 94 | config, |
96 | workspace_root, | 95 | workspace_root, |
97 | last_update_req: None, | 96 | last_update_req: None, |
98 | message_recv: never(), | 97 | message_recv: never(), |
@@ -216,27 +215,27 @@ impl FlycheckThread { | |||
216 | self.message_recv = never(); | 215 | self.message_recv = never(); |
217 | self.check_process = None; | 216 | self.check_process = None; |
218 | 217 | ||
219 | let mut args: Vec<String> = vec![ | 218 | let cmd = { |
220 | self.options.command.clone(), | 219 | let mut cmd = Command::new(cargo_binary()); |
221 | "--workspace".to_string(), | 220 | cmd.arg(&self.config.command); |
222 | "--message-format=json".to_string(), | 221 | cmd.args(&["--workspace", "--message-format=json", "--manifest-path"]); |
223 | "--manifest-path".to_string(), | 222 | cmd.arg(self.workspace_root.join("Cargo.toml")); |
224 | format!("{}/Cargo.toml", self.workspace_root.display()), | 223 | if self.config.all_targets { |
225 | ]; | 224 | cmd.arg("--all-targets"); |
226 | if self.options.all_targets { | 225 | } |
227 | args.push("--all-targets".to_string()); | 226 | cmd.args(self.config.extra_args.iter()); |
228 | } | 227 | cmd.current_dir(&self.workspace_root); |
229 | args.extend(self.options.args.iter().cloned()); | 228 | cmd |
229 | }; | ||
230 | 230 | ||
231 | let (message_send, message_recv) = unbounded(); | 231 | let (message_send, message_recv) = unbounded(); |
232 | let workspace_root = self.workspace_root.to_owned(); | ||
233 | self.message_recv = message_recv; | 232 | self.message_recv = message_recv; |
234 | self.check_process = Some(jod_thread::spawn(move || { | 233 | self.check_process = Some(jod_thread::spawn(move || { |
235 | // If we trigger an error here, we will do so in the loop instead, | 234 | // If we trigger an error here, we will do so in the loop instead, |
236 | // which will break out of the loop, and continue the shutdown | 235 | // which will break out of the loop, and continue the shutdown |
237 | let _ = message_send.send(CheckEvent::Begin); | 236 | let _ = message_send.send(CheckEvent::Begin); |
238 | 237 | ||
239 | let res = run_cargo(&args, Some(&workspace_root), &mut |message| { | 238 | let res = run_cargo(cmd, &mut |message| { |
240 | // Skip certain kinds of messages to only spend time on what's useful | 239 | // Skip certain kinds of messages to only spend time on what's useful |
241 | match &message { | 240 | match &message { |
242 | Message::CompilerArtifact(artifact) if artifact.fresh => return true, | 241 | Message::CompilerArtifact(artifact) if artifact.fresh => return true, |
@@ -285,17 +284,11 @@ impl fmt::Display for CargoError { | |||
285 | impl error::Error for CargoError {} | 284 | impl error::Error for CargoError {} |
286 | 285 | ||
287 | fn run_cargo( | 286 | fn run_cargo( |
288 | args: &[String], | 287 | mut command: Command, |
289 | current_dir: Option<&Path>, | ||
290 | on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool, | 288 | on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool, |
291 | ) -> Result<(), CargoError> { | 289 | ) -> Result<(), CargoError> { |
292 | let mut command = Command::new("cargo"); | 290 | dbg!(&command); |
293 | if let Some(current_dir) = current_dir { | ||
294 | command.current_dir(current_dir); | ||
295 | } | ||
296 | |||
297 | let mut child = command | 291 | let mut child = command |
298 | .args(args) | ||
299 | .stdout(Stdio::piped()) | 292 | .stdout(Stdio::piped()) |
300 | .stderr(Stdio::null()) | 293 | .stderr(Stdio::null()) |
301 | .stdin(Stdio::null()) | 294 | .stdin(Stdio::null()) |
@@ -346,9 +339,8 @@ fn run_cargo( | |||
346 | // FIXME: Read the stderr to display the reason, see `read2()` reference in PR comment: | 339 | // FIXME: Read the stderr to display the reason, see `read2()` reference in PR comment: |
347 | // https://github.com/rust-analyzer/rust-analyzer/pull/3632#discussion_r395605298 | 340 | // https://github.com/rust-analyzer/rust-analyzer/pull/3632#discussion_r395605298 |
348 | format!( | 341 | format!( |
349 | "the command produced no valid metadata (exit code: {:?}): cargo {}", | 342 | "the command produced no valid metadata (exit code: {:?}): {:?}", |
350 | exit_code, | 343 | exit_code, command |
351 | args.join(" ") | ||
352 | ) | 344 | ) |
353 | } | 345 | } |
354 | Err(err) => format!("io error: {:?}", err), | 346 | Err(err) => format!("io error: {:?}", err), |
@@ -357,3 +349,7 @@ fn run_cargo( | |||
357 | 349 | ||
358 | Err(CargoError(err_msg)) | 350 | Err(CargoError(err_msg)) |
359 | } | 351 | } |
352 | |||
353 | fn cargo_binary() -> String { | ||
354 | env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()) | ||
355 | } | ||
diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs index 1c9710a5d..1abb891c1 100644 --- a/crates/ra_ide/src/ssr.rs +++ b/crates/ra_ide/src/ssr.rs | |||
@@ -5,7 +5,7 @@ use ra_db::{SourceDatabase, SourceDatabaseExt}; | |||
5 | use ra_ide_db::symbol_index::SymbolsDatabase; | 5 | use ra_ide_db::symbol_index::SymbolsDatabase; |
6 | use ra_ide_db::RootDatabase; | 6 | use ra_ide_db::RootDatabase; |
7 | use ra_syntax::ast::make::try_expr_from_text; | 7 | use ra_syntax::ast::make::try_expr_from_text; |
8 | use ra_syntax::ast::{AstToken, Comment}; | 8 | use ra_syntax::ast::{AstToken, Comment, RecordField, RecordLit}; |
9 | use ra_syntax::{AstNode, SyntaxElement, SyntaxNode}; | 9 | use ra_syntax::{AstNode, SyntaxElement, SyntaxNode}; |
10 | use ra_text_edit::{TextEdit, TextEditBuilder}; | 10 | use ra_text_edit::{TextEdit, TextEditBuilder}; |
11 | use rustc_hash::FxHashMap; | 11 | use rustc_hash::FxHashMap; |
@@ -186,47 +186,102 @@ fn create_name<'a>(name: &str, vars: &'a mut Vec<Var>) -> Result<&'a str, SsrErr | |||
186 | } | 186 | } |
187 | 187 | ||
188 | fn find(pattern: &SsrPattern, code: &SyntaxNode) -> SsrMatches { | 188 | fn find(pattern: &SsrPattern, code: &SyntaxNode) -> SsrMatches { |
189 | fn check_record_lit( | ||
190 | pattern: RecordLit, | ||
191 | code: RecordLit, | ||
192 | placeholders: &[Var], | ||
193 | match_: Match, | ||
194 | ) -> Option<Match> { | ||
195 | let match_ = check_opt_nodes(pattern.path(), code.path(), placeholders, match_)?; | ||
196 | |||
197 | let mut pattern_fields = | ||
198 | pattern.record_field_list().map(|x| x.fields().collect()).unwrap_or(vec![]); | ||
199 | let mut code_fields = | ||
200 | code.record_field_list().map(|x| x.fields().collect()).unwrap_or(vec![]); | ||
201 | |||
202 | if pattern_fields.len() != code_fields.len() { | ||
203 | return None; | ||
204 | } | ||
205 | |||
206 | let by_name = |a: &RecordField, b: &RecordField| { | ||
207 | a.name_ref() | ||
208 | .map(|x| x.syntax().text().to_string()) | ||
209 | .cmp(&b.name_ref().map(|x| x.syntax().text().to_string())) | ||
210 | }; | ||
211 | pattern_fields.sort_by(by_name); | ||
212 | code_fields.sort_by(by_name); | ||
213 | |||
214 | pattern_fields.into_iter().zip(code_fields.into_iter()).fold( | ||
215 | Some(match_), | ||
216 | |accum, (a, b)| { | ||
217 | accum.and_then(|match_| check_opt_nodes(Some(a), Some(b), placeholders, match_)) | ||
218 | }, | ||
219 | ) | ||
220 | } | ||
221 | |||
222 | fn check_opt_nodes( | ||
223 | pattern: Option<impl AstNode>, | ||
224 | code: Option<impl AstNode>, | ||
225 | placeholders: &[Var], | ||
226 | match_: Match, | ||
227 | ) -> Option<Match> { | ||
228 | match (pattern, code) { | ||
229 | (Some(pattern), Some(code)) => check( | ||
230 | &SyntaxElement::from(pattern.syntax().clone()), | ||
231 | &SyntaxElement::from(code.syntax().clone()), | ||
232 | placeholders, | ||
233 | match_, | ||
234 | ), | ||
235 | (None, None) => Some(match_), | ||
236 | _ => None, | ||
237 | } | ||
238 | } | ||
239 | |||
189 | fn check( | 240 | fn check( |
190 | pattern: &SyntaxElement, | 241 | pattern: &SyntaxElement, |
191 | code: &SyntaxElement, | 242 | code: &SyntaxElement, |
192 | placeholders: &[Var], | 243 | placeholders: &[Var], |
193 | mut match_: Match, | 244 | mut match_: Match, |
194 | ) -> Option<Match> { | 245 | ) -> Option<Match> { |
195 | match (pattern, code) { | 246 | match (&pattern, &code) { |
196 | (SyntaxElement::Token(ref pattern), SyntaxElement::Token(ref code)) => { | 247 | (SyntaxElement::Token(pattern), SyntaxElement::Token(code)) => { |
197 | if pattern.text() == code.text() { | 248 | if pattern.text() == code.text() { |
198 | Some(match_) | 249 | Some(match_) |
199 | } else { | 250 | } else { |
200 | None | 251 | None |
201 | } | 252 | } |
202 | } | 253 | } |
203 | (SyntaxElement::Node(ref pattern), SyntaxElement::Node(ref code)) => { | 254 | (SyntaxElement::Node(pattern), SyntaxElement::Node(code)) => { |
204 | if placeholders.iter().any(|n| n.0.as_str() == pattern.text()) { | 255 | if placeholders.iter().any(|n| n.0.as_str() == pattern.text()) { |
205 | match_.binding.insert(Var(pattern.text().to_string()), code.clone()); | 256 | match_.binding.insert(Var(pattern.text().to_string()), code.clone()); |
206 | Some(match_) | 257 | Some(match_) |
207 | } else { | 258 | } else { |
208 | let mut pattern_children = pattern | 259 | if let (Some(pattern), Some(code)) = |
209 | .children_with_tokens() | 260 | (RecordLit::cast(pattern.clone()), RecordLit::cast(code.clone())) |
210 | .filter(|element| !element.kind().is_trivia()); | 261 | { |
211 | let mut code_children = | 262 | check_record_lit(pattern, code, placeholders, match_) |
212 | code.children_with_tokens().filter(|element| !element.kind().is_trivia()); | 263 | } else { |
213 | let new_ignored_comments = code.children_with_tokens().filter_map(|element| { | 264 | let mut pattern_children = pattern |
214 | element.as_token().and_then(|token| Comment::cast(token.clone())) | 265 | .children_with_tokens() |
215 | }); | 266 | .filter(|element| !element.kind().is_trivia()); |
216 | match_.ignored_comments.extend(new_ignored_comments); | 267 | let mut code_children = code |
217 | let match_from_children = pattern_children | 268 | .children_with_tokens() |
218 | .by_ref() | 269 | .filter(|element| !element.kind().is_trivia()); |
219 | .zip(code_children.by_ref()) | 270 | let new_ignored_comments = |
220 | .fold(Some(match_), |accum, (a, b)| { | 271 | code.children_with_tokens().filter_map(|element| { |
221 | accum.and_then(|match_| check(&a, &b, placeholders, match_)) | 272 | element.as_token().and_then(|token| Comment::cast(token.clone())) |
222 | }); | 273 | }); |
223 | match_from_children.and_then(|match_| { | 274 | match_.ignored_comments.extend(new_ignored_comments); |
224 | if pattern_children.count() == 0 && code_children.count() == 0 { | 275 | pattern_children |
225 | Some(match_) | 276 | .by_ref() |
226 | } else { | 277 | .zip(code_children.by_ref()) |
227 | None | 278 | .fold(Some(match_), |accum, (a, b)| { |
228 | } | 279 | accum.and_then(|match_| check(&a, &b, placeholders, match_)) |
229 | }) | 280 | }) |
281 | .filter(|_| { | ||
282 | pattern_children.next().is_none() && code_children.next().is_none() | ||
283 | }) | ||
284 | } | ||
230 | } | 285 | } |
231 | } | 286 | } |
232 | _ => None, | 287 | _ => None, |
@@ -434,4 +489,13 @@ mod tests { | |||
434 | "fn main() { bar(5)/* using 5 */ }", | 489 | "fn main() { bar(5)/* using 5 */ }", |
435 | ) | 490 | ) |
436 | } | 491 | } |
492 | |||
493 | #[test] | ||
494 | fn ssr_struct_lit() { | ||
495 | assert_ssr_transform( | ||
496 | "foo{a: $a:expr, b: $b:expr} ==>> foo::new($a, $b)", | ||
497 | "fn main() { foo{b:2, a:1} }", | ||
498 | "fn main() { foo::new(1, 2) }", | ||
499 | ) | ||
500 | } | ||
437 | } | 501 | } |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 79dc03de4..06122ed95 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -21,7 +21,7 @@ use lsp_types::{ | |||
21 | WorkDoneProgressBegin, WorkDoneProgressCreateParams, WorkDoneProgressEnd, | 21 | WorkDoneProgressBegin, WorkDoneProgressCreateParams, WorkDoneProgressEnd, |
22 | WorkDoneProgressReport, | 22 | WorkDoneProgressReport, |
23 | }; | 23 | }; |
24 | use ra_flycheck::{url_from_path_with_drive_lowercasing, CheckConfig, CheckTask}; | 24 | use ra_flycheck::{url_from_path_with_drive_lowercasing, CheckTask, FlycheckConfig}; |
25 | use ra_ide::{Canceled, FileId, InlayHintsConfig, LibraryData, SourceRootId}; | 25 | use ra_ide::{Canceled, FileId, InlayHintsConfig, LibraryData, SourceRootId}; |
26 | use ra_prof::profile; | 26 | use ra_prof::profile; |
27 | use ra_vfs::{VfsFile, VfsTask, Watch}; | 27 | use ra_vfs::{VfsFile, VfsTask, Watch}; |
@@ -102,10 +102,10 @@ fn get_config( | |||
102 | max_length: config.inlay_hints_max_length, | 102 | max_length: config.inlay_hints_max_length, |
103 | }, | 103 | }, |
104 | check: if config.cargo_watch_enable { | 104 | check: if config.cargo_watch_enable { |
105 | Some(CheckConfig { | 105 | Some(FlycheckConfig { |
106 | args: config.cargo_watch_args.clone(), | ||
107 | command: config.cargo_watch_command.clone(), | 106 | command: config.cargo_watch_command.clone(), |
108 | all_targets: config.cargo_watch_all_targets, | 107 | all_targets: config.cargo_watch_all_targets, |
108 | extra_args: config.cargo_watch_args.clone(), | ||
109 | }) | 109 | }) |
110 | } else { | 110 | } else { |
111 | None | 111 | None |
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs index 7814a682e..2db058eb1 100644 --- a/crates/rust-analyzer/src/world.rs +++ b/crates/rust-analyzer/src/world.rs | |||
@@ -11,7 +11,7 @@ use std::{ | |||
11 | use crossbeam_channel::{unbounded, Receiver}; | 11 | use crossbeam_channel::{unbounded, Receiver}; |
12 | use lsp_types::Url; | 12 | use lsp_types::Url; |
13 | use parking_lot::RwLock; | 13 | use parking_lot::RwLock; |
14 | use ra_flycheck::{url_from_path_with_drive_lowercasing, CheckConfig, Flycheck}; | 14 | use ra_flycheck::{url_from_path_with_drive_lowercasing, Flycheck, FlycheckConfig}; |
15 | use ra_ide::{ | 15 | use ra_ide::{ |
16 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, InlayHintsConfig, LibraryData, | 16 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, InlayHintsConfig, LibraryData, |
17 | SourceRootId, | 17 | SourceRootId, |
@@ -58,7 +58,7 @@ pub struct Config { | |||
58 | pub line_folding_only: bool, | 58 | pub line_folding_only: bool, |
59 | pub inlay_hints: InlayHintsConfig, | 59 | pub inlay_hints: InlayHintsConfig, |
60 | pub rustfmt_args: Vec<String>, | 60 | pub rustfmt_args: Vec<String>, |
61 | pub check: Option<CheckConfig>, | 61 | pub check: Option<FlycheckConfig>, |
62 | pub vscode_lldb: bool, | 62 | pub vscode_lldb: bool, |
63 | pub proc_macro_srv: Option<String>, | 63 | pub proc_macro_srv: Option<String>, |
64 | } | 64 | } |