1
1
//! This module is responsible for implementing handlers for Language Server
2
2
//! Protocol. This module specifically handles notifications.
3
3
4
- use std:: ops:: { Deref , Not as _} ;
4
+ use std:: {
5
+ ops:: { Deref , Not as _} ,
6
+ panic:: UnwindSafe ,
7
+ } ;
5
8
6
- use ide_db:: base_db:: salsa:: Cancelled ;
7
9
use itertools:: Itertools ;
8
10
use lsp_types:: {
9
11
CancelParams , DidChangeConfigurationParams , DidChangeTextDocumentParams ,
@@ -16,7 +18,7 @@ use vfs::{AbsPathBuf, ChangeKind, VfsPath};
16
18
17
19
use crate :: {
18
20
config:: { Config , ConfigChange } ,
19
- flycheck:: Target ,
21
+ flycheck:: { InvocationStrategy , Target } ,
20
22
global_state:: { FetchWorkspaceRequest , GlobalState } ,
21
23
lsp:: { from_proto, utils:: apply_document_changes} ,
22
24
lsp_ext:: { self , RunFlycheckParams } ,
@@ -301,124 +303,165 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool {
301
303
let file_id = state. vfs . read ( ) . 0 . file_id ( & vfs_path) ;
302
304
if let Some ( ( file_id, vfs:: FileExcluded :: No ) ) = file_id {
303
305
let world = state. snapshot ( ) ;
304
- let invocation_strategy_once = state. config . flycheck ( None ) . invocation_strategy_once ( ) ;
306
+ let invocation_strategy = state. config . flycheck ( None ) . invocation_strategy ( ) ;
305
307
let may_flycheck_workspace = state. config . flycheck_workspace ( None ) ;
306
- let mut updated = false ;
307
- let task = move || -> std:: result:: Result < ( ) , Cancelled > {
308
- if invocation_strategy_once {
309
- let saved_file = vfs_path. as_path ( ) . map ( |p| p. to_owned ( ) ) ;
310
- world. flycheck [ 0 ] . restart_workspace ( saved_file) ;
311
- }
312
308
313
- let target = TargetSpec :: for_file ( & world, file_id) ?. and_then ( |it| {
314
- let tgt_kind = it. target_kind ( ) ;
315
- let ( tgt_name, root, package) = match it {
316
- TargetSpec :: Cargo ( c) => ( c. target , c. workspace_root , c. package ) ,
317
- _ => return None ,
318
- } ;
319
-
320
- let tgt = match tgt_kind {
321
- project_model:: TargetKind :: Bin => Target :: Bin ( tgt_name) ,
322
- project_model:: TargetKind :: Example => Target :: Example ( tgt_name) ,
323
- project_model:: TargetKind :: Test => Target :: Test ( tgt_name) ,
324
- project_model:: TargetKind :: Bench => Target :: Benchmark ( tgt_name) ,
325
- _ => return Some ( ( None , root, package) ) ,
326
- } ;
327
-
328
- Some ( ( Some ( tgt) , root, package) )
329
- } ) ;
330
- tracing:: debug!( ?target, "flycheck target" ) ;
331
- // we have a specific non-library target, attempt to only check that target, nothing
332
- // else will be affected
333
- if let Some ( ( target, root, package) ) = target {
334
- // trigger a package check if we have a non-library target as that can't affect
335
- // anything else in the workspace OR if we're not allowed to check the workspace as
336
- // the user opted into package checks then
337
- let package_check_allowed = target. is_some ( ) || !may_flycheck_workspace;
338
- if package_check_allowed {
339
- let workspace = world. workspaces . iter ( ) . position ( |ws| match & ws. kind {
340
- project_model:: ProjectWorkspaceKind :: Cargo { cargo, .. }
341
- | project_model:: ProjectWorkspaceKind :: DetachedFile {
342
- cargo : Some ( ( cargo, _, _) ) ,
343
- ..
344
- } => * cargo. workspace_root ( ) == root,
345
- _ => false ,
346
- } ) ;
347
- if let Some ( idx) = workspace {
348
- world. flycheck [ idx] . restart_for_package ( package, target) ;
349
- }
309
+ let task: Box < dyn FnOnce ( ) -> ide:: Cancellable < ( ) > + Send + UnwindSafe > =
310
+ match invocation_strategy {
311
+ InvocationStrategy :: Once => {
312
+ Box :: new ( move || {
313
+ // FIXME: Because triomphe::Arc's auto UnwindSafe impl requires that the inner type
314
+ // be UnwindSafe, and FlycheckHandle is not UnwindSafe, `word.flycheck` cannot
315
+ // be captured directly. std::sync::Arc has an UnwindSafe impl that only requires
316
+ // that the inner type be RefUnwindSafe, so if we were using that one we wouldn't
317
+ // have this problem. Remove the line below when triomphe::Arc has an UnwindSafe impl
318
+ // like std::sync::Arc's.
319
+ let world = world;
320
+ stdx:: always!(
321
+ world. flycheck. len( ) == 1 ,
322
+ "should have exactly one flycheck handle when invocation strategy is once"
323
+ ) ;
324
+ let saved_file = vfs_path. as_path ( ) . map ( ToOwned :: to_owned) ;
325
+ world. flycheck [ 0 ] . restart_workspace ( saved_file) ;
326
+ Ok ( ( ) )
327
+ } )
350
328
}
351
- }
352
-
353
- if !may_flycheck_workspace {
354
- return Ok ( ( ) ) ;
355
- }
356
-
357
- // Trigger flychecks for all workspaces that depend on the saved file
358
- // Crates containing or depending on the saved file
359
- let crate_ids = world
360
- . analysis
361
- . crates_for ( file_id) ?
362
- . into_iter ( )
363
- . flat_map ( |id| world. analysis . transitive_rev_deps ( id) )
364
- . flatten ( )
365
- . unique ( )
366
- . collect :: < Vec < _ > > ( ) ;
367
- tracing:: debug!( ?crate_ids, "flycheck crate ids" ) ;
368
- let crate_root_paths: Vec < _ > = crate_ids
369
- . iter ( )
370
- . filter_map ( |& crate_id| {
371
- world
372
- . analysis
373
- . crate_root ( crate_id)
374
- . map ( |file_id| {
375
- world. file_id_to_file_path ( file_id) . as_path ( ) . map ( ToOwned :: to_owned)
376
- } )
377
- . transpose ( )
378
- } )
379
- . collect :: < ide:: Cancellable < _ > > ( ) ?;
380
- let crate_root_paths: Vec < _ > = crate_root_paths. iter ( ) . map ( Deref :: deref) . collect ( ) ;
381
- tracing:: debug!( ?crate_root_paths, "flycheck crate roots" ) ;
382
-
383
- // Find all workspaces that have at least one target containing the saved file
384
- let workspace_ids =
385
- world. workspaces . iter ( ) . enumerate ( ) . filter ( |( _, ws) | match & ws. kind {
386
- project_model:: ProjectWorkspaceKind :: Cargo { cargo, .. }
387
- | project_model:: ProjectWorkspaceKind :: DetachedFile {
388
- cargo : Some ( ( cargo, _, _) ) ,
389
- ..
390
- } => cargo. packages ( ) . any ( |pkg| {
391
- cargo[ pkg]
392
- . targets
329
+ InvocationStrategy :: PerWorkspace => {
330
+ Box :: new ( move || {
331
+ let target = TargetSpec :: for_file ( & world, file_id) ?. and_then ( |it| {
332
+ let tgt_kind = it. target_kind ( ) ;
333
+ let ( tgt_name, root, package) = match it {
334
+ TargetSpec :: Cargo ( c) => ( c. target , c. workspace_root , c. package ) ,
335
+ _ => return None ,
336
+ } ;
337
+
338
+ let tgt = match tgt_kind {
339
+ project_model:: TargetKind :: Bin => Target :: Bin ( tgt_name) ,
340
+ project_model:: TargetKind :: Example => Target :: Example ( tgt_name) ,
341
+ project_model:: TargetKind :: Test => Target :: Test ( tgt_name) ,
342
+ project_model:: TargetKind :: Bench => Target :: Benchmark ( tgt_name) ,
343
+ _ => return Some ( ( None , root, package) ) ,
344
+ } ;
345
+
346
+ Some ( ( Some ( tgt) , root, package) )
347
+ } ) ;
348
+ tracing:: debug!( ?target, "flycheck target" ) ;
349
+ // we have a specific non-library target, attempt to only check that target, nothing
350
+ // else will be affected
351
+ let mut package_workspace_idx = None ;
352
+ if let Some ( ( target, root, package) ) = target {
353
+ // trigger a package check if we have a non-library target as that can't affect
354
+ // anything else in the workspace OR if we're not allowed to check the workspace as
355
+ // the user opted into package checks then
356
+ let package_check_allowed = target. is_some ( ) || !may_flycheck_workspace;
357
+ if package_check_allowed {
358
+ package_workspace_idx =
359
+ world. workspaces . iter ( ) . position ( |ws| match & ws. kind {
360
+ project_model:: ProjectWorkspaceKind :: Cargo {
361
+ cargo,
362
+ ..
363
+ }
364
+ | project_model:: ProjectWorkspaceKind :: DetachedFile {
365
+ cargo : Some ( ( cargo, _, _) ) ,
366
+ ..
367
+ } => * cargo. workspace_root ( ) == root,
368
+ _ => false ,
369
+ } ) ;
370
+ if let Some ( idx) = package_workspace_idx {
371
+ world. flycheck [ idx] . restart_for_package ( package, target) ;
372
+ }
373
+ }
374
+ }
375
+
376
+ if !may_flycheck_workspace {
377
+ return Ok ( ( ) ) ;
378
+ }
379
+
380
+ // Trigger flychecks for all workspaces that depend on the saved file
381
+ // Crates containing or depending on the saved file
382
+ let crate_ids: Vec < _ > = world
383
+ . analysis
384
+ . crates_for ( file_id) ?
385
+ . into_iter ( )
386
+ . flat_map ( |id| world. analysis . transitive_rev_deps ( id) )
387
+ . flatten ( )
388
+ . unique ( )
389
+ . collect ( ) ;
390
+ tracing:: debug!( ?crate_ids, "flycheck crate ids" ) ;
391
+ let crate_root_paths: Vec < _ > = crate_ids
393
392
. iter ( )
394
- . any ( |& it| crate_root_paths. contains ( & cargo[ it] . root . as_path ( ) ) )
395
- } ) ,
396
- project_model:: ProjectWorkspaceKind :: Json ( project) => project
397
- . crates ( )
398
- . any ( |( _, krate) | crate_root_paths. contains ( & krate. root_module . as_path ( ) ) ) ,
399
- project_model:: ProjectWorkspaceKind :: DetachedFile { .. } => false ,
400
- } ) ;
401
-
402
- let saved_file = vfs_path. as_path ( ) . map ( |p| p. to_owned ( ) ) ;
403
-
404
- // Find and trigger corresponding flychecks
405
- ' flychecks: for flycheck in world. flycheck . iter ( ) {
406
- for ( id, _) in workspace_ids. clone ( ) {
407
- if id == flycheck. id ( ) {
408
- updated = true ;
409
- flycheck. restart_workspace ( saved_file. clone ( ) ) ;
410
- continue ' flychecks;
411
- }
412
- }
413
- }
414
- // No specific flycheck was triggered, so let's trigger all of them.
415
- if !updated {
416
- for flycheck in world. flycheck . iter ( ) {
417
- flycheck. restart_workspace ( saved_file. clone ( ) ) ;
393
+ . filter_map ( |& crate_id| {
394
+ world
395
+ . analysis
396
+ . crate_root ( crate_id)
397
+ . map ( |file_id| {
398
+ world
399
+ . file_id_to_file_path ( file_id)
400
+ . as_path ( )
401
+ . map ( ToOwned :: to_owned)
402
+ } )
403
+ . transpose ( )
404
+ } )
405
+ . collect :: < ide:: Cancellable < _ > > ( ) ?;
406
+ let crate_root_paths: Vec < _ > =
407
+ crate_root_paths. iter ( ) . map ( Deref :: deref) . collect ( ) ;
408
+ tracing:: debug!( ?crate_root_paths, "flycheck crate roots" ) ;
409
+
410
+ // Find all workspaces that have at least one target containing the saved file
411
+ let workspace_ids =
412
+ world. workspaces . iter ( ) . enumerate ( ) . filter ( |& ( idx, ws) | {
413
+ let ws_contains_file = match & ws. kind {
414
+ project_model:: ProjectWorkspaceKind :: Cargo {
415
+ cargo, ..
416
+ }
417
+ | project_model:: ProjectWorkspaceKind :: DetachedFile {
418
+ cargo : Some ( ( cargo, _, _) ) ,
419
+ ..
420
+ } => cargo. packages ( ) . any ( |pkg| {
421
+ cargo[ pkg] . targets . iter ( ) . any ( |& it| {
422
+ crate_root_paths. contains ( & cargo[ it] . root . as_path ( ) )
423
+ } )
424
+ } ) ,
425
+ project_model:: ProjectWorkspaceKind :: Json ( project) => {
426
+ project. crates ( ) . any ( |( _, krate) | {
427
+ crate_root_paths. contains ( & krate. root_module . as_path ( ) )
428
+ } )
429
+ }
430
+ project_model:: ProjectWorkspaceKind :: DetachedFile {
431
+ ..
432
+ } => false ,
433
+ } ;
434
+ let is_pkg_ws = match package_workspace_idx {
435
+ Some ( pkg_idx) => pkg_idx == idx,
436
+ None => false ,
437
+ } ;
438
+ ws_contains_file && !is_pkg_ws
439
+ } ) ;
440
+
441
+ let saved_file = vfs_path. as_path ( ) . map ( ToOwned :: to_owned) ;
442
+ let mut workspace_check_triggered = false ;
443
+ // Find and trigger corresponding flychecks
444
+ ' flychecks: for flycheck in world. flycheck . iter ( ) {
445
+ for ( id, _) in workspace_ids. clone ( ) {
446
+ if id == flycheck. id ( ) {
447
+ workspace_check_triggered = true ;
448
+ flycheck. restart_workspace ( saved_file. clone ( ) ) ;
449
+ continue ' flychecks;
450
+ }
451
+ }
452
+ }
453
+
454
+ // No specific flycheck was triggered, so let's trigger all of them.
455
+ if !workspace_check_triggered && package_workspace_idx. is_none ( ) {
456
+ for flycheck in world. flycheck . iter ( ) {
457
+ flycheck. restart_workspace ( saved_file. clone ( ) ) ;
458
+ }
459
+ }
460
+ Ok ( ( ) )
461
+ } )
418
462
}
419
- }
420
- Ok ( ( ) )
421
- } ;
463
+ } ;
464
+
422
465
state. task_pool . handle . spawn_with_sender ( stdx:: thread:: ThreadIntent :: Worker , move |_| {
423
466
if let Err ( e) = std:: panic:: catch_unwind ( task) {
424
467
tracing:: error!( "flycheck task panicked: {e:?}" )
0 commit comments