aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_cargo_watch/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_cargo_watch/src/lib.rs')
-rw-r--r--crates/ra_cargo_watch/src/lib.rs50
1 files changed, 41 insertions, 9 deletions
diff --git a/crates/ra_cargo_watch/src/lib.rs b/crates/ra_cargo_watch/src/lib.rs
index 7f4c9280c..ea7ddc86b 100644
--- a/crates/ra_cargo_watch/src/lib.rs
+++ b/crates/ra_cargo_watch/src/lib.rs
@@ -7,9 +7,9 @@ use lsp_types::{
7 Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressEnd, 7 Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressEnd,
8 WorkDoneProgressReport, 8 WorkDoneProgressReport,
9}; 9};
10use parking_lot::RwLock;
11use std::{ 10use std::{
12 collections::HashMap, 11 collections::HashMap,
12 io::{BufRead, BufReader},
13 path::PathBuf, 13 path::PathBuf,
14 process::{Command, Stdio}, 14 process::{Command, Stdio},
15 sync::Arc, 15 sync::Arc,
@@ -38,7 +38,7 @@ pub struct CheckOptions {
38#[derive(Debug)] 38#[derive(Debug)]
39pub struct CheckWatcher { 39pub struct CheckWatcher {
40 pub task_recv: Receiver<CheckTask>, 40 pub task_recv: Receiver<CheckTask>,
41 pub state: Arc<RwLock<CheckState>>, 41 pub state: Arc<CheckState>,
42 cmd_send: Option<Sender<CheckCommand>>, 42 cmd_send: Option<Sender<CheckCommand>>,
43 handle: Option<JoinHandle<()>>, 43 handle: Option<JoinHandle<()>>,
44} 44}
@@ -46,7 +46,7 @@ pub struct CheckWatcher {
46impl CheckWatcher { 46impl CheckWatcher {
47 pub fn new(options: &CheckOptions, workspace_root: PathBuf) -> CheckWatcher { 47 pub fn new(options: &CheckOptions, workspace_root: PathBuf) -> CheckWatcher {
48 let options = options.clone(); 48 let options = options.clone();
49 let state = Arc::new(RwLock::new(CheckState::new())); 49 let state = Arc::new(CheckState::new());
50 50
51 let (task_send, task_recv) = unbounded::<CheckTask>(); 51 let (task_send, task_recv) = unbounded::<CheckTask>();
52 let (cmd_send, cmd_recv) = unbounded::<CheckCommand>(); 52 let (cmd_send, cmd_recv) = unbounded::<CheckCommand>();
@@ -59,7 +59,7 @@ impl CheckWatcher {
59 59
60 /// Returns a CheckWatcher that doesn't actually do anything 60 /// Returns a CheckWatcher that doesn't actually do anything
61 pub fn dummy() -> CheckWatcher { 61 pub fn dummy() -> CheckWatcher {
62 let state = Arc::new(RwLock::new(CheckState::new())); 62 let state = Arc::new(CheckState::new());
63 CheckWatcher { task_recv: never(), cmd_send: None, handle: None, state } 63 CheckWatcher { task_recv: never(), cmd_send: None, handle: None, state }
64 } 64 }
65 65
@@ -87,7 +87,7 @@ impl std::ops::Drop for CheckWatcher {
87 } 87 }
88} 88}
89 89
90#[derive(Debug)] 90#[derive(Clone, Debug)]
91pub struct CheckState { 91pub struct CheckState {
92 diagnostic_collection: HashMap<Url, Vec<Diagnostic>>, 92 diagnostic_collection: HashMap<Url, Vec<Diagnostic>>,
93 suggested_fix_collection: HashMap<Url, Vec<SuggestedFix>>, 93 suggested_fix_collection: HashMap<Url, Vec<SuggestedFix>>,
@@ -216,8 +216,10 @@ impl CheckWatcherThread {
216 self.last_update_req.take(); 216 self.last_update_req.take();
217 task_send.send(CheckTask::ClearDiagnostics).unwrap(); 217 task_send.send(CheckTask::ClearDiagnostics).unwrap();
218 218
219 // By replacing the watcher, we drop the previous one which 219 // Replace with a dummy watcher first so we drop the original and wait for completion
220 // causes it to shut down automatically. 220 std::mem::replace(&mut self.watcher, WatchThread::dummy());
221
222 // Then create the actual new watcher
221 self.watcher = WatchThread::new(&self.options, &self.workspace_root); 223 self.watcher = WatchThread::new(&self.options, &self.workspace_root);
222 } 224 }
223 } 225 }
@@ -348,15 +350,45 @@ impl WatchThread {
348 // which will break out of the loop, and continue the shutdown 350 // which will break out of the loop, and continue the shutdown
349 let _ = message_send.send(CheckEvent::Begin); 351 let _ = message_send.send(CheckEvent::Begin);
350 352
351 for message in cargo_metadata::parse_messages(command.stdout.take().unwrap()) { 353 // We manually read a line at a time, instead of using serde's
354 // stream deserializers, because the deserializer cannot recover
355 // from an error, resulting in it getting stuck, because we try to
356 // be resillient against failures.
357 //
358 // Because cargo only outputs one JSON object per line, we can
359 // simply skip a line if it doesn't parse, which just ignores any
360 // erroneus output.
361 let stdout = BufReader::new(command.stdout.take().unwrap());
362 for line in stdout.lines() {
363 let line = match line {
364 Ok(line) => line,
365 Err(err) => {
366 log::error!("Couldn't read line from cargo: {}", err);
367 continue;
368 }
369 };
370
371 let message = serde_json::from_str::<cargo_metadata::Message>(&line);
352 let message = match message { 372 let message = match message {
353 Ok(message) => message, 373 Ok(message) => message,
354 Err(err) => { 374 Err(err) => {
355 log::error!("Invalid json from cargo check, ignoring: {}", err); 375 log::error!(
376 "Invalid json from cargo check, ignoring ({}): {:?} ",
377 err,
378 line
379 );
356 continue; 380 continue;
357 } 381 }
358 }; 382 };
359 383
384 // Skip certain kinds of messages to only spend time on what's useful
385 match &message {
386 Message::CompilerArtifact(artifact) if artifact.fresh => continue,
387 Message::BuildScriptExecuted(_) => continue,
388 Message::Unknown => continue,
389 _ => {}
390 }
391
360 match message_send.send(CheckEvent::Msg(message)) { 392 match message_send.send(CheckEvent::Msg(message)) {
361 Ok(()) => {} 393 Ok(()) => {}
362 Err(_err) => { 394 Err(_err) => {