@@ -19,16 +19,24 @@ use crate::linter::{
1919 isolated_lint_handler:: { IsolatedLintHandler , IsolatedLintHandlerOptions } ,
2020 tsgo_linter:: TsgoLinter ,
2121} ;
22- use crate :: options:: UnusedDisableDirectives ;
22+ use crate :: options:: { Run , UnusedDisableDirectives } ;
2323use crate :: { ConcurrentHashMap , OXC_CONFIG_FILE , Options } ;
2424
2525use super :: config_walker:: ConfigWalker ;
2626
27+ #[ derive( Debug , PartialEq , Eq ) ]
28+ pub enum ServerLinterRun {
29+ OnType ,
30+ OnSave ,
31+ Force ,
32+ }
33+
2734pub struct ServerLinter {
2835 isolated_linter : Arc < Mutex < IsolatedLintHandler > > ,
2936 tsgo_linter : Arc < Option < TsgoLinter > > ,
3037 ignore_matcher : LintIgnoreMatcher ,
3138 gitignore_glob : Vec < Gitignore > ,
39+ lint_on_run : Run ,
3240 pub extended_paths : Vec < PathBuf > ,
3341}
3442
@@ -125,6 +133,7 @@ impl ServerLinter {
125133 ) ,
126134 gitignore_glob : Self :: create_ignore_glob ( & root_path) ,
127135 extended_paths,
136+ lint_on_run : options. run ,
128137 tsgo_linter : if options. type_aware {
129138 Arc :: new ( Some ( TsgoLinter :: new ( & root_path, config_store) ) )
130139 } else {
@@ -243,14 +252,42 @@ impl ServerLinter {
243252 & self ,
244253 uri : & Uri ,
245254 content : Option < String > ,
255+ run_type : ServerLinterRun ,
246256 ) -> Option < Vec < DiagnosticReport > > {
257+ let ( oxlint, tsgolint) = match ( run_type, self . lint_on_run ) {
258+ // run everything on save, or force
259+ ( ServerLinterRun :: Force , _) | ( ServerLinterRun :: OnSave , Run :: OnSave ) => ( true , true ) ,
260+ // run only oxlint on type
261+ // tsgolint does not support memory source_text
262+ ( ServerLinterRun :: OnType , Run :: OnType ) => ( true , false ) ,
263+ // it does not match, run nothing
264+ ( ServerLinterRun :: OnType , Run :: OnSave ) => ( false , false ) ,
265+ // run only tsglint on save, even if the user wants it with type
266+ // tsgolint only supports the OS file system.
267+ ( ServerLinterRun :: OnSave , Run :: OnType ) => ( false , true ) ,
268+ } ;
269+
270+ // return `None` when both tools doe not want to be used
271+ if !oxlint && !tsgolint {
272+ return None ;
273+ }
274+
247275 if self . is_ignored ( uri) {
248276 return None ;
249277 }
250278
251- // when `IsolatedLintHandler` returns `None`, it means it does not want to lint.
252- // Do not try `tsgolint` because it could be ignored or is not supported.
253- let mut reports = self . isolated_linter . lock ( ) . await . run_single ( uri, content. clone ( ) ) ?;
279+ let mut reports = Vec :: with_capacity ( 0 ) ;
280+
281+ if oxlint
282+ && let Some ( oxlint_reports) =
283+ self . isolated_linter . lock ( ) . await . run_single ( uri, content. clone ( ) )
284+ {
285+ reports. extend ( oxlint_reports) ;
286+ }
287+
288+ if !tsgolint {
289+ return Some ( reports) ;
290+ }
254291
255292 let Some ( tsgo_linter) = & * self . tsgo_linter else {
256293 return Some ( reports) ;
@@ -296,6 +333,7 @@ mod test {
296333 use crate :: {
297334 Options ,
298335 linter:: server_linter:: { ServerLinter , normalize_path} ,
336+ options:: Run ,
299337 tester:: { Tester , get_file_path} ,
300338 } ;
301339 use rustc_hash:: FxHashMap ;
@@ -342,70 +380,110 @@ mod test {
342380 assert ! ( configs_dirs[ 0 ] . ends_with( "init_nested_configs" ) ) ;
343381 }
344382
383+ #[ test]
384+ fn test_lint_on_run_on_type_on_type ( ) {
385+ Tester :: new (
386+ "fixtures/linter/lint_on_run/on_type" ,
387+ Some ( Options { type_aware : true , run : Run :: OnType , ..Default :: default ( ) } ) ,
388+ )
389+ . test_and_snapshot_single_file ( "on-type.ts" , Run :: OnType ) ;
390+ }
391+
392+ #[ test]
393+ fn test_lint_on_run_on_type_on_save ( ) {
394+ Tester :: new (
395+ "fixtures/linter/lint_on_run/on_save" ,
396+ Some ( Options { type_aware : true , run : Run :: OnType , ..Default :: default ( ) } ) ,
397+ )
398+ . test_and_snapshot_single_file ( "on-save.ts" , Run :: OnSave ) ;
399+ }
400+
401+ #[ test]
402+ fn test_lint_on_run_on_save_on_type ( ) {
403+ Tester :: new (
404+ "fixtures/linter/lint_on_run/on_save" ,
405+ Some ( Options { type_aware : true , run : Run :: OnSave , ..Default :: default ( ) } ) ,
406+ )
407+ . test_and_snapshot_single_file ( "on-type.ts" , Run :: OnType ) ;
408+ }
409+
410+ #[ test]
411+ fn test_lint_on_run_on_save_on_save ( ) {
412+ Tester :: new (
413+ "fixtures/linter/lint_on_run/on_type" ,
414+ Some ( Options { type_aware : true , run : Run :: OnSave , ..Default :: default ( ) } ) ,
415+ )
416+ . test_and_snapshot_single_file ( "on-save.ts" , Run :: OnSave ) ;
417+ }
418+
345419 #[ test]
346420 fn test_no_errors ( ) {
347421 Tester :: new ( "fixtures/linter/no_errors" , None )
348- . test_and_snapshot_single_file ( "hello_world.js" ) ;
422+ . test_and_snapshot_single_file ( "hello_world.js" , Run :: default ( ) ) ;
349423 }
350424
351425 #[ test]
352426 fn test_no_console ( ) {
353427 Tester :: new ( "fixtures/linter/deny_no_console" , None )
354- . test_and_snapshot_single_file ( "hello_world.js" ) ;
428+ . test_and_snapshot_single_file ( "hello_world.js" , Run :: default ( ) ) ;
355429 }
356430
357431 // Test case for https://github.com/oxc-project/oxc/issues/9958
358432 #[ test]
359433 fn test_issue_9958 ( ) {
360- Tester :: new ( "fixtures/linter/issue_9958" , None ) . test_and_snapshot_single_file ( "issue.ts" ) ;
434+ Tester :: new ( "fixtures/linter/issue_9958" , None )
435+ . test_and_snapshot_single_file ( "issue.ts" , Run :: default ( ) ) ;
361436 }
362437
363438 // Test case for https://github.com/oxc-project/oxc/issues/9957
364439 #[ test]
365440 fn test_regexp ( ) {
366441 Tester :: new ( "fixtures/linter/regexp_feature" , None )
367- . test_and_snapshot_single_file ( "index.ts" ) ;
442+ . test_and_snapshot_single_file ( "index.ts" , Run :: default ( ) ) ;
368443 }
369444
370445 #[ test]
371446 fn test_frameworks ( ) {
372- Tester :: new ( "fixtures/linter/astro" , None ) . test_and_snapshot_single_file ( "debugger.astro" ) ;
373- Tester :: new ( "fixtures/linter/vue" , None ) . test_and_snapshot_single_file ( "debugger.vue" ) ;
447+ Tester :: new ( "fixtures/linter/astro" , None )
448+ . test_and_snapshot_single_file ( "debugger.astro" , Run :: default ( ) ) ;
449+ Tester :: new ( "fixtures/linter/vue" , None )
450+ . test_and_snapshot_single_file ( "debugger.vue" , Run :: default ( ) ) ;
374451 Tester :: new ( "fixtures/linter/svelte" , None )
375- . test_and_snapshot_single_file ( "debugger.svelte" ) ;
452+ . test_and_snapshot_single_file ( "debugger.svelte" , Run :: default ( ) ) ;
376453 // ToDo: fix Tester to work only with Uris and do not access the file system
377454 // Tester::new("fixtures/linter/nextjs").test_and_snapshot_single_file("%5B%5B..rest%5D%5D/debugger.ts");
378455 }
379456
380457 #[ test]
381458 fn test_invalid_syntax_file ( ) {
382459 Tester :: new ( "fixtures/linter/invalid_syntax" , None )
383- . test_and_snapshot_single_file ( "debugger.ts" ) ;
460+ . test_and_snapshot_single_file ( "debugger.ts" , Run :: default ( ) ) ;
384461 }
385462
386463 #[ test]
387464 fn test_cross_module_debugger ( ) {
388465 Tester :: new ( "fixtures/linter/cross_module" , None )
389- . test_and_snapshot_single_file ( "debugger.ts" ) ;
466+ . test_and_snapshot_single_file ( "debugger.ts" , Run :: default ( ) ) ;
390467 }
391468
392469 #[ test]
393470 fn test_cross_module_no_cycle ( ) {
394- Tester :: new ( "fixtures/linter/cross_module" , None ) . test_and_snapshot_single_file ( "dep-a.ts" ) ;
471+ Tester :: new ( "fixtures/linter/cross_module" , None )
472+ . test_and_snapshot_single_file ( "dep-a.ts" , Run :: default ( ) ) ;
395473 }
396474
397475 #[ test]
398476 fn test_cross_module_no_cycle_nested_config ( ) {
399477 Tester :: new ( "fixtures/linter/cross_module_nested_config" , None )
400- . test_and_snapshot_single_file ( "dep-a.ts" ) ;
478+ . test_and_snapshot_single_file ( "dep-a.ts" , Run :: default ( ) ) ;
401479 Tester :: new ( "fixtures/linter/cross_module_nested_config" , None )
402- . test_and_snapshot_single_file ( "folder/folder-dep-a.ts" ) ;
480+ . test_and_snapshot_single_file ( "folder/folder-dep-a.ts" , Run :: default ( ) ) ;
403481 }
404482
405483 #[ test]
406484 fn test_cross_module_no_cycle_extended_config ( ) {
407485 Tester :: new ( "fixtures/linter/cross_module_extended_config" , None )
408- . test_and_snapshot_single_file ( "dep-a.ts" ) ;
486+ . test_and_snapshot_single_file ( "dep-a.ts" , Run :: default ( ) ) ;
409487 }
410488
411489 #[ test]
@@ -420,7 +498,7 @@ mod test {
420498 ..Options :: default ( )
421499 } ) ,
422500 )
423- . test_and_snapshot_single_file ( "forward_ref.ts" ) ;
501+ . test_and_snapshot_single_file ( "forward_ref.ts" , Run :: default ( ) ) ;
424502 }
425503
426504 #[ test]
@@ -433,14 +511,14 @@ mod test {
433511 ..Default :: default ( )
434512 } ) ,
435513 )
436- . test_and_snapshot_single_file ( "test.js" ) ;
514+ . test_and_snapshot_single_file ( "test.js" , Run :: default ( ) ) ;
437515 }
438516
439517 #[ test]
440518 fn test_root_ignore_patterns ( ) {
441519 let tester = Tester :: new ( "fixtures/linter/ignore_patterns" , None ) ;
442- tester. test_and_snapshot_single_file ( "ignored-file.ts" ) ;
443- tester. test_and_snapshot_single_file ( "another_config/not-ignored-file.ts" ) ;
520+ tester. test_and_snapshot_single_file ( "ignored-file.ts" , Run :: default ( ) ) ;
521+ tester. test_and_snapshot_single_file ( "another_config/not-ignored-file.ts" , Run :: default ( ) ) ;
444522 }
445523
446524 #[ test]
@@ -452,7 +530,7 @@ mod test {
452530 ..Default :: default ( )
453531 } ) ,
454532 )
455- . test_and_snapshot_single_file ( "deep/src/dep-a.ts" ) ;
533+ . test_and_snapshot_single_file ( "deep/src/dep-a.ts" , Run :: default ( ) ) ;
456534 }
457535
458536 #[ test]
@@ -462,6 +540,6 @@ mod test {
462540 "fixtures/linter/tsgolint" ,
463541 Some ( Options { type_aware : true , ..Default :: default ( ) } ) ,
464542 ) ;
465- tester. test_and_snapshot_single_file ( "no-floating-promises/index.ts" ) ;
543+ tester. test_and_snapshot_single_file ( "no-floating-promises/index.ts" , Run :: OnSave ) ;
466544 }
467545}
0 commit comments