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,37 @@ impl FlycheckActor {
258
250
}
259
251
260
252
struct CargoHandle {
261
- child : JodChild ,
262
253
#[ allow( unused) ]
263
- thread : jod_thread:: JoinHandle < bool > ,
254
+ thread : jod_thread:: JoinHandle < io :: Result < ( ) > > ,
264
255
receiver : Receiver < CargoMessage > ,
265
256
}
266
257
267
258
impl CargoHandle {
268
- fn spawn ( mut child : JodChild ) -> CargoHandle {
269
- let child_stdout = child. stdout . take ( ) . unwrap ( ) ;
259
+ fn spawn ( command : Command ) -> CargoHandle {
270
260
let ( sender, receiver) = unbounded ( ) ;
271
- let actor = CargoActor :: new ( child_stdout , sender) ;
261
+ let actor = CargoActor :: new ( sender) ;
272
262
let thread = jod_thread:: Builder :: new ( )
273
263
. name ( "CargoHandle" . to_owned ( ) )
274
- . spawn ( move || actor. run ( ) )
264
+ . spawn ( move || actor. run ( command ) )
275
265
. expect ( "failed to spawn thread" ) ;
276
- CargoHandle { child , thread, receiver }
266
+ CargoHandle { thread, receiver }
277
267
}
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 ( ( ) )
268
+
269
+ fn join ( self ) -> io:: Result < ( ) > {
270
+ self . thread . join ( )
295
271
}
296
272
}
297
273
298
274
struct CargoActor {
299
- child_stdout : process:: ChildStdout ,
300
275
sender : Sender < CargoMessage > ,
301
276
}
302
277
303
278
impl CargoActor {
304
- fn new ( child_stdout : process :: ChildStdout , sender : Sender < CargoMessage > ) -> CargoActor {
305
- CargoActor { child_stdout , sender }
279
+ fn new ( sender : Sender < CargoMessage > ) -> CargoActor {
280
+ CargoActor { sender }
306
281
}
307
- fn run ( self ) -> bool {
282
+
283
+ fn run ( self , command : Command ) -> io:: Result < ( ) > {
308
284
// We manually read a line at a time, instead of using serde's
309
285
// stream deserializers, because the deserializer cannot recover
310
286
// from an error, resulting in it getting stuck, because we try to
@@ -313,41 +289,53 @@ impl CargoActor {
313
289
// Because cargo only outputs one JSON object per line, we can
314
290
// simply skip a line if it doesn't parse, which just ignores any
315
291
// 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
292
327
- read_at_least_one_message = true ;
293
+ let mut error = String :: new ( ) ;
294
+ let mut read_at_least_one_message = false ;
295
+ let output = streaming_output (
296
+ command,
297
+ & mut |line| {
298
+ read_at_least_one_message = true ;
328
299
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 ( ) ;
300
+ // Try to deserialize a message from Cargo or Rustc.
301
+ let mut deserializer = serde_json:: Deserializer :: from_str ( & line) ;
302
+ deserializer. disable_recursion_limit ( ) ;
303
+ if let Ok ( message) = JsonMessage :: deserialize ( & mut deserializer) {
304
+ match message {
305
+ // Skip certain kinds of messages to only spend time on what's useful
306
+ JsonMessage :: Cargo ( message) => match message {
307
+ cargo_metadata:: Message :: CompilerArtifact ( artifact)
308
+ if !artifact. fresh =>
309
+ {
310
+ self . sender . send ( CargoMessage :: CompilerArtifact ( artifact) ) . unwrap ( ) ;
311
+ }
312
+ cargo_metadata:: Message :: CompilerMessage ( msg) => {
313
+ self . sender . send ( CargoMessage :: Diagnostic ( msg. message ) ) . unwrap ( ) ;
314
+ }
315
+ _ => ( ) ,
316
+ } ,
317
+ JsonMessage :: Rustc ( message) => {
318
+ self . sender . send ( CargoMessage :: Diagnostic ( message) ) . unwrap ( ) ;
338
319
}
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
320
}
347
321
}
322
+ } ,
323
+ & mut |line| {
324
+ error. push_str ( line) ;
325
+ error. push ( '\n' ) ;
326
+ } ,
327
+ ) ;
328
+ match output {
329
+ Ok ( _) if read_at_least_one_message => Ok ( ( ) ) ,
330
+ Ok ( output) if output. status . success ( ) => {
331
+ Err ( io:: Error :: new ( io:: ErrorKind :: Other , format ! (
332
+ "Cargo watcher failed, the command produced no valid metadata (exit code: {:?})" ,
333
+ output. status
334
+ ) ) )
348
335
}
336
+ Ok ( _) => Err ( io:: Error :: new ( io:: ErrorKind :: Other , error) ) ,
337
+ Err ( e) => Err ( e) ,
349
338
}
350
- read_at_least_one_message
351
339
}
352
340
}
353
341
0 commit comments