2
2
//! another compatible command (f.x. clippy) in a background thread and provide
3
3
//! LSP diagnostics based on the output of the command.
4
4
5
- use std:: {
6
- fmt,
7
- io:: { self , BufRead , BufReader } ,
8
- process:: { self , Command , Stdio } ,
9
- time:: Duration ,
10
- } ;
5
+ use std:: { fmt, io, process:: Command , time:: Duration } ;
11
6
12
7
use crossbeam_channel:: { never, select, unbounded, Receiver , Sender } ;
13
8
use paths:: AbsPathBuf ;
14
9
use serde:: Deserialize ;
15
- use stdx:: JodChild ;
10
+ use stdx:: process :: streaming_output ;
16
11
17
12
pub use cargo_metadata:: diagnostic:: {
18
13
Applicability , Diagnostic , DiagnosticCode , DiagnosticLevel , DiagnosticSpan ,
@@ -162,13 +157,10 @@ impl FlycheckActor {
162
157
163
158
self . cancel_check_process ( ) ;
164
159
165
- let mut command = self . check_command ( ) ;
160
+ let command = self . check_command ( ) ;
166
161
tracing:: info!( "restart flycheck {:?}" , command) ;
167
- command. stdout ( Stdio :: piped ( ) ) . stderr ( Stdio :: null ( ) ) . stdin ( Stdio :: null ( ) ) ;
168
- if let Ok ( child) = command. spawn ( ) . map ( JodChild ) {
169
- self . cargo_handle = Some ( CargoHandle :: spawn ( child) ) ;
170
- self . progress ( Progress :: DidStart ) ;
171
- }
162
+ self . cargo_handle = Some ( CargoHandle :: spawn ( command) ) ;
163
+ self . progress ( Progress :: DidStart ) ;
172
164
}
173
165
Event :: CheckEvent ( None ) => {
174
166
// Watcher finished, replace it with a never channel to
@@ -258,53 +250,36 @@ impl FlycheckActor {
258
250
}
259
251
260
252
struct CargoHandle {
261
- child : JodChild ,
262
- #[ allow( unused) ]
263
- thread : jod_thread:: JoinHandle < bool > ,
253
+ thread : jod_thread:: JoinHandle < io:: Result < ( ) > > ,
264
254
receiver : Receiver < CargoMessage > ,
265
255
}
266
256
267
257
impl CargoHandle {
268
- fn spawn ( mut child : JodChild ) -> CargoHandle {
269
- let child_stdout = child. stdout . take ( ) . unwrap ( ) ;
258
+ fn spawn ( command : Command ) -> CargoHandle {
270
259
let ( sender, receiver) = unbounded ( ) ;
271
- let actor = CargoActor :: new ( child_stdout , sender) ;
260
+ let actor = CargoActor :: new ( sender) ;
272
261
let thread = jod_thread:: Builder :: new ( )
273
262
. name ( "CargoHandle" . to_owned ( ) )
274
- . spawn ( move || actor. run ( ) )
263
+ . spawn ( move || actor. run ( command ) )
275
264
. expect ( "failed to spawn thread" ) ;
276
- CargoHandle { child , thread, receiver }
265
+ CargoHandle { thread, receiver }
277
266
}
278
- fn join ( mut self ) -> io:: Result < ( ) > {
279
- // It is okay to ignore the result, as it only errors if the process is already dead
280
- let _ = self . child . kill ( ) ;
281
- let exit_status = self . child . wait ( ) ?;
282
- let read_at_least_one_message = self . thread . join ( ) ;
283
- if !exit_status. success ( ) && !read_at_least_one_message {
284
- // FIXME: Read the stderr to display the reason, see `read2()` reference in PR comment:
285
- // https://github.com/rust-analyzer/rust-analyzer/pull/3632#discussion_r395605298
286
- return Err ( io:: Error :: new (
287
- io:: ErrorKind :: Other ,
288
- format ! (
289
- "Cargo watcher failed, the command produced no valid metadata (exit code: {:?})" ,
290
- exit_status
291
- ) ,
292
- ) ) ;
293
- }
294
- Ok ( ( ) )
267
+
268
+ fn join ( self ) -> io:: Result < ( ) > {
269
+ self . thread . join ( )
295
270
}
296
271
}
297
272
298
273
struct CargoActor {
299
- child_stdout : process:: ChildStdout ,
300
274
sender : Sender < CargoMessage > ,
301
275
}
302
276
303
277
impl CargoActor {
304
- fn new ( child_stdout : process :: ChildStdout , sender : Sender < CargoMessage > ) -> CargoActor {
305
- CargoActor { child_stdout , sender }
278
+ fn new ( sender : Sender < CargoMessage > ) -> CargoActor {
279
+ CargoActor { sender }
306
280
}
307
- fn run ( self ) -> bool {
281
+
282
+ fn run ( self , command : Command ) -> io:: Result < ( ) > {
308
283
// We manually read a line at a time, instead of using serde's
309
284
// stream deserializers, because the deserializer cannot recover
310
285
// from an error, resulting in it getting stuck, because we try to
@@ -313,41 +288,53 @@ impl CargoActor {
313
288
// Because cargo only outputs one JSON object per line, we can
314
289
// simply skip a line if it doesn't parse, which just ignores any
315
290
// erroneus output.
316
- let stdout = BufReader :: new ( self . child_stdout ) ;
317
- let mut read_at_least_one_message = false ;
318
- for message in stdout. lines ( ) {
319
- let message = match message {
320
- Ok ( message) => message,
321
- Err ( err) => {
322
- tracing:: error!( "Invalid json from cargo check, ignoring ({})" , err) ;
323
- continue ;
324
- }
325
- } ;
326
291
327
- read_at_least_one_message = true ;
292
+ let mut error = String :: new ( ) ;
293
+ let mut read_at_least_one_message = false ;
294
+ let output = streaming_output (
295
+ command,
296
+ & mut |line| {
297
+ read_at_least_one_message = true ;
328
298
329
- // Try to deserialize a message from Cargo or Rustc.
330
- let mut deserializer = serde_json:: Deserializer :: from_str ( & message) ;
331
- deserializer. disable_recursion_limit ( ) ;
332
- if let Ok ( message) = JsonMessage :: deserialize ( & mut deserializer) {
333
- match message {
334
- // Skip certain kinds of messages to only spend time on what's useful
335
- JsonMessage :: Cargo ( message) => match message {
336
- cargo_metadata:: Message :: CompilerArtifact ( artifact) if !artifact. fresh => {
337
- self . sender . send ( CargoMessage :: CompilerArtifact ( artifact) ) . unwrap ( ) ;
299
+ // Try to deserialize a message from Cargo or Rustc.
300
+ let mut deserializer = serde_json:: Deserializer :: from_str ( & line) ;
301
+ deserializer. disable_recursion_limit ( ) ;
302
+ if let Ok ( message) = JsonMessage :: deserialize ( & mut deserializer) {
303
+ match message {
304
+ // Skip certain kinds of messages to only spend time on what's useful
305
+ JsonMessage :: Cargo ( message) => match message {
306
+ cargo_metadata:: Message :: CompilerArtifact ( artifact)
307
+ if !artifact. fresh =>
308
+ {
309
+ self . sender . send ( CargoMessage :: CompilerArtifact ( artifact) ) . unwrap ( ) ;
310
+ }
311
+ cargo_metadata:: Message :: CompilerMessage ( msg) => {
312
+ self . sender . send ( CargoMessage :: Diagnostic ( msg. message ) ) . unwrap ( ) ;
313
+ }
314
+ _ => ( ) ,
315
+ } ,
316
+ JsonMessage :: Rustc ( message) => {
317
+ self . sender . send ( CargoMessage :: Diagnostic ( message) ) . unwrap ( ) ;
338
318
}
339
- cargo_metadata:: Message :: CompilerMessage ( msg) => {
340
- self . sender . send ( CargoMessage :: Diagnostic ( msg. message ) ) . unwrap ( ) ;
341
- }
342
- _ => ( ) ,
343
- } ,
344
- JsonMessage :: Rustc ( message) => {
345
- self . sender . send ( CargoMessage :: Diagnostic ( message) ) . unwrap ( ) ;
346
319
}
347
320
}
321
+ } ,
322
+ & mut |line| {
323
+ error. push_str ( line) ;
324
+ error. push ( '\n' ) ;
325
+ } ,
326
+ ) ;
327
+ match output {
328
+ Ok ( _) if read_at_least_one_message => Ok ( ( ) ) ,
329
+ Ok ( output) if output. status . success ( ) => {
330
+ Err ( io:: Error :: new ( io:: ErrorKind :: Other , format ! (
331
+ "Cargo watcher failed, the command produced no valid metadata (exit code: {:?})" ,
332
+ output. status
333
+ ) ) )
348
334
}
335
+ Ok ( _) => Err ( io:: Error :: new ( io:: ErrorKind :: Other , error) ) ,
336
+ Err ( e) => Err ( e) ,
349
337
}
350
- read_at_least_one_message
351
338
}
352
339
}
353
340
0 commit comments