aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_flycheck/src/lib.rs64
-rw-r--r--crates/ra_ide/src/ssr.rs116
-rw-r--r--crates/rust-analyzer/src/main_loop.rs6
-rw-r--r--crates/rust-analyzer/src/world.rs4
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 @@
4mod conv; 4mod conv;
5 5
6use std::{ 6use 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};
23pub use crate::conv::url_from_path_with_drive_lowercasing; 23pub use crate::conv::url_from_path_with_drive_lowercasing;
24 24
25#[derive(Clone, Debug)] 25#[derive(Clone, Debug)]
26pub struct CheckConfig { 26pub 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
44impl Flycheck { 44impl 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
78struct FlycheckThread { 77struct 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
92impl FlycheckThread { 91impl 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 {
285impl error::Error for CargoError {} 284impl error::Error for CargoError {}
286 285
287fn run_cargo( 286fn 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
353fn 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};
5use ra_ide_db::symbol_index::SymbolsDatabase; 5use ra_ide_db::symbol_index::SymbolsDatabase;
6use ra_ide_db::RootDatabase; 6use ra_ide_db::RootDatabase;
7use ra_syntax::ast::make::try_expr_from_text; 7use ra_syntax::ast::make::try_expr_from_text;
8use ra_syntax::ast::{AstToken, Comment}; 8use ra_syntax::ast::{AstToken, Comment, RecordField, RecordLit};
9use ra_syntax::{AstNode, SyntaxElement, SyntaxNode}; 9use ra_syntax::{AstNode, SyntaxElement, SyntaxNode};
10use ra_text_edit::{TextEdit, TextEditBuilder}; 10use ra_text_edit::{TextEdit, TextEditBuilder};
11use rustc_hash::FxHashMap; 11use 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
188fn find(pattern: &SsrPattern, code: &SyntaxNode) -> SsrMatches { 188fn 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};
24use ra_flycheck::{url_from_path_with_drive_lowercasing, CheckConfig, CheckTask}; 24use ra_flycheck::{url_from_path_with_drive_lowercasing, CheckTask, FlycheckConfig};
25use ra_ide::{Canceled, FileId, InlayHintsConfig, LibraryData, SourceRootId}; 25use ra_ide::{Canceled, FileId, InlayHintsConfig, LibraryData, SourceRootId};
26use ra_prof::profile; 26use ra_prof::profile;
27use ra_vfs::{VfsFile, VfsTask, Watch}; 27use 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::{
11use crossbeam_channel::{unbounded, Receiver}; 11use crossbeam_channel::{unbounded, Receiver};
12use lsp_types::Url; 12use lsp_types::Url;
13use parking_lot::RwLock; 13use parking_lot::RwLock;
14use ra_flycheck::{url_from_path_with_drive_lowercasing, CheckConfig, Flycheck}; 14use ra_flycheck::{url_from_path_with_drive_lowercasing, Flycheck, FlycheckConfig};
15use ra_ide::{ 15use 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}