11use std:: borrow:: Cow ;
22
3- use lsp_types:: { self as types, Url , request as req} ;
3+ use lsp_types:: { self as types, NumberOrString , Url , request as req} ;
44use ty_project:: ProjectDatabase ;
5+ use ty_python_semantic:: { Db as _, create_suppression_fix} ;
56use types:: { CodeActionKind , CodeActionOrCommand } ;
67
78use crate :: DIAGNOSTIC_NAME ;
9+ use crate :: document:: RangeExt ;
810use crate :: server:: Result ;
911use crate :: server:: api:: RequestHandler ;
10- use crate :: server:: api:: diagnostics:: DiagnosticData ;
12+ use crate :: server:: api:: diagnostics:: { DiagnosticData , fix_to_lsp_edits } ;
1113use crate :: server:: api:: traits:: { BackgroundDocumentRequestHandler , RetriableRequestHandler } ;
1214use crate :: session:: DocumentSnapshot ;
1315use crate :: session:: client:: Client ;
@@ -24,45 +26,89 @@ impl BackgroundDocumentRequestHandler for CodeActionRequestHandler {
2426 }
2527
2628 fn run_with_snapshot (
27- _db : & ProjectDatabase ,
28- _snapshot : & DocumentSnapshot ,
29+ db : & ProjectDatabase ,
30+ snapshot : & DocumentSnapshot ,
2931 _client : & Client ,
3032 params : types:: CodeActionParams ,
3133 ) -> Result < Option < types:: CodeActionResponse > > {
3234 let diagnostics = params. context . diagnostics ;
3335
3436 let mut actions = Vec :: new ( ) ;
37+ let lint_registry = db. lint_registry ( ) ;
38+ let file = snapshot. to_notebook_or_file ( db) ;
39+
40+ tracing:: debug!( "code actions with diagnostics: {:#?}" , diagnostics) ;
3541
3642 for mut diagnostic in diagnostics. into_iter ( ) . filter ( |diagnostic| {
3743 diagnostic. source . as_deref ( ) == Some ( DIAGNOSTIC_NAME )
3844 && range_intersect ( & diagnostic. range , & params. range )
3945 } ) {
40- let Some ( data) = diagnostic. data . take ( ) else {
41- continue ;
46+ tracing:: debug!( "Lint code: {:?}" , diagnostic. code. as_ref( ) ) ;
47+
48+ let add_suppression_fix = if let Some ( NumberOrString :: String ( code) ) =
49+ diagnostic. code . as_ref ( )
50+ && let Ok ( lint) = lint_registry. get ( code)
51+ && let Some ( file) = file
52+ && let Some ( range) = diagnostic. range . to_text_range (
53+ db,
54+ file,
55+ & params. text_document . uri ,
56+ snapshot. encoding ( ) ,
57+ ) {
58+ tracing:: debug!( "Creating suppression fix" ) ;
59+ let fix = create_suppression_fix ( db, file, lint, range) ;
60+ tracing:: debug!( "Suppression fix: {fix:#?}" ) ;
61+ Some ( ( fix, lint) )
62+ } else {
63+ None
4264 } ;
4365
44- let data: DiagnosticData = match serde_json:: from_value ( data) {
45- Ok ( data) => data,
46- Err ( err) => {
47- tracing:: warn!( "Failed to deserialize diagnostic data: {err}" ) ;
48- continue ;
49- }
66+ // If the diagnostic has a fix, add it as a code action.
67+ if let Some ( data) = diagnostic. data . take ( ) {
68+ let data: DiagnosticData = match serde_json:: from_value ( data) {
69+ Ok ( data) => data,
70+ Err ( err) => {
71+ tracing:: warn!( "Failed to deserialize diagnostic data: {err}" ) ;
72+ continue ;
73+ }
74+ } ;
75+
76+ actions. push ( CodeActionOrCommand :: CodeAction ( lsp_types:: CodeAction {
77+ title : data. fix_title ,
78+ kind : Some ( CodeActionKind :: QUICKFIX ) ,
79+ diagnostics : Some ( vec ! [ diagnostic] ) ,
80+ edit : Some ( lsp_types:: WorkspaceEdit {
81+ changes : Some ( data. edits ) ,
82+ document_changes : None ,
83+ change_annotations : None ,
84+ } ) ,
85+ is_preferred : Some ( true ) ,
86+ command : None ,
87+ disabled : None ,
88+ data : None ,
89+ } ) ) ;
5090 } ;
5191
52- actions. push ( CodeActionOrCommand :: CodeAction ( lsp_types:: CodeAction {
53- title : data. fix_title ,
54- kind : Some ( CodeActionKind :: QUICKFIX ) ,
55- diagnostics : Some ( vec ! [ diagnostic] ) ,
56- edit : Some ( lsp_types:: WorkspaceEdit {
57- changes : Some ( data. edits ) ,
58- document_changes : None ,
59- change_annotations : None ,
60- } ) ,
61- is_preferred : Some ( true ) ,
62- command : None ,
63- disabled : None ,
64- data : None ,
65- } ) ) ;
92+ if let Some ( ( suppression_fix, lint) ) = add_suppression_fix
93+ && let Some ( file) = file
94+ && let Some ( suppression_edits) =
95+ fix_to_lsp_edits ( db, & suppression_fix, file, snapshot. encoding ( ) )
96+ {
97+ actions. push ( CodeActionOrCommand :: CodeAction ( lsp_types:: CodeAction {
98+ title : format ! ( "Ignore '{}' for this line" , lint. name( ) ) ,
99+ kind : Some ( CodeActionKind :: QUICKFIX ) ,
100+ diagnostics : None ,
101+ edit : Some ( lsp_types:: WorkspaceEdit {
102+ changes : Some ( suppression_edits) ,
103+ document_changes : None ,
104+ change_annotations : None ,
105+ } ) ,
106+ is_preferred : Some ( false ) ,
107+ command : None ,
108+ disabled : None ,
109+ data : None ,
110+ } ) )
111+ }
66112 }
67113
68114 if actions. is_empty ( ) {
0 commit comments