@@ -9,8 +9,8 @@ use lsp_types::{
99} ;
1010use std:: {
1111 io:: { BufRead , BufReader } ,
12- path:: PathBuf ,
13- process:: { Command , Stdio } ,
12+ path:: { Path , PathBuf } ,
13+ process:: { Child , Command , Stdio } ,
1414 thread:: JoinHandle ,
1515 time:: Instant ,
1616} ;
@@ -246,102 +246,119 @@ enum CheckEvent {
246246 End ,
247247}
248248
249+ pub fn run_cargo (
250+ args : & [ String ] ,
251+ current_dir : Option < & Path > ,
252+ on_message : & mut dyn FnMut ( cargo_metadata:: Message ) -> bool ,
253+ ) -> Child {
254+ let mut command = Command :: new ( "cargo" ) ;
255+ if let Some ( current_dir) = current_dir {
256+ command. current_dir ( current_dir) ;
257+ }
258+
259+ let mut child = command
260+ . args ( args)
261+ . stdout ( Stdio :: piped ( ) )
262+ . stderr ( Stdio :: null ( ) )
263+ . stdin ( Stdio :: null ( ) )
264+ . spawn ( )
265+ . expect ( "couldn't launch cargo" ) ;
266+
267+ // We manually read a line at a time, instead of using serde's
268+ // stream deserializers, because the deserializer cannot recover
269+ // from an error, resulting in it getting stuck, because we try to
270+ // be resillient against failures.
271+ //
272+ // Because cargo only outputs one JSON object per line, we can
273+ // simply skip a line if it doesn't parse, which just ignores any
274+ // erroneus output.
275+ let stdout = BufReader :: new ( child. stdout . take ( ) . unwrap ( ) ) ;
276+ for line in stdout. lines ( ) {
277+ let line = match line {
278+ Ok ( line) => line,
279+ Err ( err) => {
280+ log:: error!( "Couldn't read line from cargo: {}" , err) ;
281+ continue ;
282+ }
283+ } ;
284+
285+ let message = serde_json:: from_str :: < cargo_metadata:: Message > ( & line) ;
286+ let message = match message {
287+ Ok ( message) => message,
288+ Err ( err) => {
289+ log:: error!( "Invalid json from cargo check, ignoring ({}): {:?} " , err, line) ;
290+ continue ;
291+ }
292+ } ;
293+
294+ if !on_message ( message) {
295+ break ;
296+ }
297+ }
298+
299+ child
300+ }
301+
249302impl WatchThread {
250303 fn dummy ( ) -> WatchThread {
251304 WatchThread { handle : None , message_recv : never ( ) }
252305 }
253306
254- fn new ( options : & CheckOptions , workspace_root : & PathBuf ) -> WatchThread {
307+ fn new ( options : & CheckOptions , workspace_root : & Path ) -> WatchThread {
255308 let mut args: Vec < String > = vec ! [
256309 options. command. clone( ) ,
257310 "--workspace" . to_string( ) ,
258311 "--message-format=json" . to_string( ) ,
259312 "--manifest-path" . to_string( ) ,
260- format!( "{}/Cargo.toml" , workspace_root. to_string_lossy ( ) ) ,
313+ format!( "{}/Cargo.toml" , workspace_root. display ( ) ) ,
261314 ] ;
262315 if options. all_targets {
263316 args. push ( "--all-targets" . to_string ( ) ) ;
264317 }
265318 args. extend ( options. args . iter ( ) . cloned ( ) ) ;
266319
267320 let ( message_send, message_recv) = unbounded ( ) ;
268- let enabled = options. enable ;
269- let handle = std:: thread:: spawn ( move || {
270- if !enabled {
271- return ;
272- }
273-
274- let mut command = Command :: new ( "cargo" )
275- . args ( & args)
276- . stdout ( Stdio :: piped ( ) )
277- . stderr ( Stdio :: null ( ) )
278- . stdin ( Stdio :: null ( ) )
279- . spawn ( )
280- . expect ( "couldn't launch cargo" ) ;
281-
282- // If we trigger an error here, we will do so in the loop instead,
283- // which will break out of the loop, and continue the shutdown
284- let _ = message_send. send ( CheckEvent :: Begin ) ;
285-
286- // We manually read a line at a time, instead of using serde's
287- // stream deserializers, because the deserializer cannot recover
288- // from an error, resulting in it getting stuck, because we try to
289- // be resillient against failures.
290- //
291- // Because cargo only outputs one JSON object per line, we can
292- // simply skip a line if it doesn't parse, which just ignores any
293- // erroneus output.
294- let stdout = BufReader :: new ( command. stdout . take ( ) . unwrap ( ) ) ;
295- for line in stdout. lines ( ) {
296- let line = match line {
297- Ok ( line) => line,
298- Err ( err) => {
299- log:: error!( "Couldn't read line from cargo: {}" , err) ;
300- continue ;
301- }
302- } ;
303-
304- let message = serde_json:: from_str :: < cargo_metadata:: Message > ( & line) ;
305- let message = match message {
306- Ok ( message) => message,
307- Err ( err) => {
308- log:: error!(
309- "Invalid json from cargo check, ignoring ({}): {:?} " ,
310- err,
311- line
312- ) ;
313- continue ;
321+ let workspace_root = workspace_root. to_owned ( ) ;
322+ let handle = if options. enable {
323+ Some ( std:: thread:: spawn ( move || {
324+ // If we trigger an error here, we will do so in the loop instead,
325+ // which will break out of the loop, and continue the shutdown
326+ let _ = message_send. send ( CheckEvent :: Begin ) ;
327+
328+ let mut child = run_cargo ( & args, Some ( & workspace_root) , & mut |message| {
329+ // Skip certain kinds of messages to only spend time on what's useful
330+ match & message {
331+ Message :: CompilerArtifact ( artifact) if artifact. fresh => return true ,
332+ Message :: BuildScriptExecuted ( _) => return true ,
333+ Message :: Unknown => return true ,
334+ _ => { }
314335 }
315- } ;
316-
317- // Skip certain kinds of messages to only spend time on what's useful
318- match & message {
319- Message :: CompilerArtifact ( artifact) if artifact. fresh => continue ,
320- Message :: BuildScriptExecuted ( _) => continue ,
321- Message :: Unknown => continue ,
322- _ => { }
323- }
324336
325- match message_send. send ( CheckEvent :: Msg ( message) ) {
326- Ok ( ( ) ) => { }
327- Err ( _err) => {
328- // The send channel was closed, so we want to shutdown
329- break ;
330- }
331- }
332- }
333-
334- // We can ignore any error here, as we are already in the progress
335- // of shutting down.
336- let _ = message_send. send ( CheckEvent :: End ) ;
337-
338- // It is okay to ignore the result, as it only errors if the process is already dead
339- let _ = command. kill ( ) ;
340-
341- // Again, we don't care about the exit status so just ignore the result
342- let _ = command. wait ( ) ;
343- } ) ;
344- WatchThread { handle : Some ( handle) , message_recv }
337+ match message_send. send ( CheckEvent :: Msg ( message) ) {
338+ Ok ( ( ) ) => { }
339+ Err ( _err) => {
340+ // The send channel was closed, so we want to shutdown
341+ return false ;
342+ }
343+ } ;
344+
345+ true
346+ } ) ;
347+
348+ // We can ignore any error here, as we are already in the progress
349+ // of shutting down.
350+ let _ = message_send. send ( CheckEvent :: End ) ;
351+
352+ // It is okay to ignore the result, as it only errors if the process is already dead
353+ let _ = child. kill ( ) ;
354+
355+ // Again, we don't care about the exit status so just ignore the result
356+ let _ = child. wait ( ) ;
357+ } ) )
358+ } else {
359+ None
360+ } ;
361+ WatchThread { handle, message_recv }
345362 }
346363}
347364
0 commit comments