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:: { fmt, io, process:: Command , time:: Duration } ;
5
+ use std:: {
6
+ fmt, io,
7
+ process:: { ChildStderr , ChildStdout , Command , Stdio } ,
8
+ time:: Duration ,
9
+ } ;
6
10
7
11
use crossbeam_channel:: { never, select, unbounded, Receiver , Sender } ;
8
12
use paths:: AbsPathBuf ;
9
13
use serde:: Deserialize ;
10
- use stdx:: process:: streaming_output;
14
+ use stdx:: { process:: streaming_output, JodChild } ;
11
15
12
16
pub use cargo_metadata:: diagnostic:: {
13
17
Applicability , Diagnostic , DiagnosticCode , DiagnosticLevel , DiagnosticSpan ,
@@ -153,14 +157,24 @@ impl FlycheckActor {
153
157
while let Some ( event) = self . next_event ( & inbox) {
154
158
match event {
155
159
Event :: Restart ( Restart ) => {
160
+ // Drop and cancel the previously spawned process
161
+ self . cargo_handle . take ( ) ;
156
162
while let Ok ( Restart ) = inbox. recv_timeout ( Duration :: from_millis ( 50 ) ) { }
157
163
158
164
self . cancel_check_process ( ) ;
159
165
160
166
let command = self . check_command ( ) ;
161
- tracing:: info!( "restart flycheck {:?}" , command) ;
162
- self . cargo_handle = Some ( CargoHandle :: spawn ( command) ) ;
163
- self . progress ( Progress :: DidStart ) ;
167
+ let command_f = format ! ( "restart flycheck {command:?}" ) ;
168
+ match CargoHandle :: spawn ( command) {
169
+ Ok ( cargo_handle) => {
170
+ tracing:: info!( "{}" , command_f) ;
171
+ self . cargo_handle = Some ( cargo_handle) ;
172
+ self . progress ( Progress :: DidStart ) ;
173
+ }
174
+ Err ( e) => {
175
+ tracing:: error!( "{command_f} failed: {e:?}" , ) ;
176
+ }
177
+ }
164
178
}
165
179
Event :: CheckEvent ( None ) => {
166
180
// Watcher finished, replace it with a never channel to
@@ -249,37 +263,56 @@ impl FlycheckActor {
249
263
}
250
264
}
251
265
266
+ /// A handle to a cargo process used for fly-checking.
252
267
struct CargoHandle {
253
- thread : jod_thread:: JoinHandle < io:: Result < ( ) > > ,
268
+ child : JodChild ,
269
+ thread : jod_thread:: JoinHandle < io:: Result < ( bool , String ) > > ,
254
270
receiver : Receiver < CargoMessage > ,
255
271
}
256
272
257
273
impl CargoHandle {
258
- fn spawn ( command : Command ) -> CargoHandle {
274
+ fn spawn ( mut command : Command ) -> std:: io:: Result < CargoHandle > {
275
+ command. stdout ( Stdio :: piped ( ) ) . stderr ( Stdio :: null ( ) ) . stdin ( Stdio :: null ( ) ) ;
276
+ let mut child = JodChild :: spawn ( command) ?;
277
+
278
+ let stdout = child. stdout . take ( ) . unwrap ( ) ;
279
+ let stderr = child. stderr . take ( ) . unwrap ( ) ;
280
+
259
281
let ( sender, receiver) = unbounded ( ) ;
260
- let actor = CargoActor :: new ( sender) ;
282
+ let actor = CargoActor :: new ( sender, stdout , stderr ) ;
261
283
let thread = jod_thread:: Builder :: new ( )
262
284
. name ( "CargoHandle" . to_owned ( ) )
263
- . spawn ( move || actor. run ( command ) )
285
+ . spawn ( move || actor. run ( ) )
264
286
. expect ( "failed to spawn thread" ) ;
265
- CargoHandle { thread, receiver }
287
+ Ok ( CargoHandle { child , thread, receiver } )
266
288
}
267
289
268
290
fn join ( self ) -> io:: Result < ( ) > {
269
- self . thread . join ( )
291
+ let exit_status = self . child . wait ( ) ?;
292
+ let ( read_at_least_one_message, error) = self . thread . join ( ) ?;
293
+ if read_at_least_one_message || exit_status. success ( ) {
294
+ Ok ( ( ) )
295
+ } else {
296
+ Err ( io:: Error :: new ( io:: ErrorKind :: Other , format ! (
297
+ "Cargo watcher failed, the command produced no valid metadata (exit code: {:?}):\n {}" ,
298
+ exit_status, error
299
+ ) ) )
300
+ }
270
301
}
271
302
}
272
303
273
304
struct CargoActor {
274
305
sender : Sender < CargoMessage > ,
306
+ stdout : ChildStdout ,
307
+ stderr : ChildStderr ,
275
308
}
276
309
277
310
impl CargoActor {
278
- fn new ( sender : Sender < CargoMessage > ) -> CargoActor {
279
- CargoActor { sender }
311
+ fn new ( sender : Sender < CargoMessage > , stdout : ChildStdout , stderr : ChildStderr ) -> CargoActor {
312
+ CargoActor { sender, stdout , stderr }
280
313
}
281
314
282
- fn run ( self , command : Command ) -> io:: Result < ( ) > {
315
+ fn run ( self ) -> io:: Result < ( bool , String ) > {
283
316
// We manually read a line at a time, instead of using serde's
284
317
// stream deserializers, because the deserializer cannot recover
285
318
// from an error, resulting in it getting stuck, because we try to
@@ -292,7 +325,8 @@ impl CargoActor {
292
325
let mut error = String :: new ( ) ;
293
326
let mut read_at_least_one_message = false ;
294
327
let output = streaming_output (
295
- command,
328
+ self . stdout ,
329
+ self . stderr ,
296
330
& mut |line| {
297
331
read_at_least_one_message = true ;
298
332
@@ -325,14 +359,7 @@ impl CargoActor {
325
359
} ,
326
360
) ;
327
361
match output {
328
- Ok ( _) if read_at_least_one_message => Ok ( ( ) ) ,
329
- Ok ( output) if output. status . success ( ) => Ok ( ( ) ) ,
330
- Ok ( output) => {
331
- Err ( io:: Error :: new ( io:: ErrorKind :: Other , format ! (
332
- "Cargo watcher failed, the command produced no valid metadata (exit code: {:?}):\n {}" ,
333
- output. status, error
334
- ) ) )
335
- }
362
+ Ok ( _) => Ok ( ( read_at_least_one_message, error) ) ,
336
363
Err ( e) => Err ( io:: Error :: new ( e. kind ( ) , format ! ( "{:?}: {}" , e, error) ) ) ,
337
364
}
338
365
}
0 commit comments