@@ -8,14 +8,22 @@ use tower_lsp::{
88 Diagnostic , Position , Range , TextEdit , Url , WorkspaceEdit ,
99 } ,
1010} ;
11+ use tree_sitter:: QueryCursor ;
1112
12- use crate :: Backend ;
13+ use crate :: {
14+ Backend ,
15+ util:: {
16+ CAPTURES_QUERY , NodeUtil , TextProviderRope , ToTsPoint , get_current_capture_node,
17+ get_references,
18+ } ,
19+ } ;
1320
1421#[ repr( u8 ) ]
1522#[ derive( Serialize , Deserialize , Debug , Clone ) ]
1623#[ serde( into = "u8" , try_from = "u8" ) ]
1724pub enum CodeActions {
1825 RemoveBackslash ,
26+ PrefixUnderscore ,
1927}
2028
2129impl From < CodeActions > for u8 {
@@ -30,12 +38,17 @@ impl TryFrom<u8> for CodeActions {
3038 fn try_from ( value : u8 ) -> std:: result:: Result < Self , Self :: Error > {
3139 match value {
3240 0 => Ok ( CodeActions :: RemoveBackslash ) ,
41+ 1 => Ok ( CodeActions :: PrefixUnderscore ) ,
3342 _ => Err ( "Invalid value" ) ,
3443 }
3544 }
3645}
3746
38- fn diag_to_code_action ( diagnostic : Diagnostic , uri : & Url ) -> Option < CodeActionOrCommand > {
47+ fn diag_to_code_action (
48+ backend : & Backend ,
49+ diagnostic : Diagnostic ,
50+ uri : & Url ,
51+ ) -> Option < CodeActionOrCommand > {
3952 match serde_json:: from_value :: < CodeActions > ( diagnostic. data . clone ( ) ?) {
4053 Ok ( CodeActions :: RemoveBackslash ) => Some ( CodeActionOrCommand :: CodeAction ( CodeAction {
4154 title : String :: from ( "Remove unnecessary backslash" ) ,
@@ -60,20 +73,60 @@ fn diag_to_code_action(diagnostic: Diagnostic, uri: &Url) -> Option<CodeActionOr
6073 diagnostics : Some ( vec ! [ diagnostic] ) ,
6174 ..Default :: default ( )
6275 } ) ) ,
76+ Ok ( CodeActions :: PrefixUnderscore ) => {
77+ let tree = backend. document_map . get ( uri) ?. tree . clone ( ) ;
78+ let root = tree. root_node ( ) ;
79+ let rope = backend. document_map . get ( uri) ?. rope . clone ( ) ;
80+ let current_node =
81+ get_current_capture_node ( root, diagnostic. range . start . to_ts_point ( & rope) ) ?;
82+ let mut cursor = QueryCursor :: new ( ) ;
83+ let provider = TextProviderRope ( & rope) ;
84+ let refs = get_references (
85+ & root,
86+ & current_node,
87+ & CAPTURES_QUERY ,
88+ & mut cursor,
89+ & provider,
90+ & rope,
91+ ) ;
92+ let edits = refs
93+ . into_iter ( )
94+ . map ( |node| {
95+ let mut range = node. lsp_range ( & rope) ;
96+ range. start . character += 1 ;
97+ range. end . character = range. start . character ;
98+ TextEdit {
99+ new_text : String :: from ( "_" ) ,
100+ range,
101+ }
102+ } )
103+ . collect ( ) ;
104+ Some ( CodeActionOrCommand :: CodeAction ( CodeAction {
105+ title : String :: from ( "Prefix capture name with underscore" ) ,
106+ kind : Some ( CodeActionKind :: QUICKFIX ) ,
107+ is_preferred : Some ( true ) ,
108+ edit : Some ( WorkspaceEdit {
109+ changes : Some ( HashMap :: from ( [ ( uri. clone ( ) , edits) ] ) ) ,
110+ ..Default :: default ( )
111+ } ) ,
112+ diagnostics : Some ( vec ! [ diagnostic] ) ,
113+ ..Default :: default ( )
114+ } ) )
115+ }
63116 _ => None ,
64117 }
65118}
66119
67120pub async fn code_action (
68- _backend : & Backend ,
121+ backend : & Backend ,
69122 params : CodeActionParams ,
70123) -> Result < Option < CodeActionResponse > > {
71124 let uri = & params. text_document . uri ;
72125 let diagnostics = params. context . diagnostics ;
73126
74127 let actions: Vec < CodeActionOrCommand > = diagnostics
75128 . into_iter ( )
76- . filter_map ( |diagnostic| diag_to_code_action ( diagnostic, uri) )
129+ . filter_map ( |diagnostic| diag_to_code_action ( backend , diagnostic, uri) )
77130 . collect ( ) ;
78131
79132 if actions. is_empty ( ) {
@@ -136,6 +189,40 @@ mod test {
136189 } ) ,
137190 ..Default :: default ( )
138191 } ) ] ) ]
192+ #[ case( r#"((comment) @jsdoc_comment
193+ (#lua-match? @jsdoc_comment ".*"))"# , Default :: default ( ) , Position :: new( 0 , 15 ) , CodeActionContext {
194+ diagnostics: vec![ Diagnostic {
195+ message: String :: from( "bad cap" ) ,
196+ range: Range :: new( Position :: new( 0 , 11 ) , Position :: new( 0 , 24 ) ) ,
197+ data: Some ( serde_json:: to_value( CodeActions :: PrefixUnderscore ) . unwrap( ) ) ,
198+ ..Default :: default ( )
199+ } ] ,
200+ ..Default :: default ( )
201+ } , & [ CodeActionOrCommand :: CodeAction ( CodeAction {
202+ title: String :: from( "Prefix capture name with underscore" ) ,
203+ kind: Some ( CodeActionKind :: QUICKFIX ) ,
204+ is_preferred: Some ( true ) ,
205+ diagnostics: Some ( vec![ Diagnostic {
206+ message: String :: from( "bad cap" ) ,
207+ range: Range :: new( Position :: new( 0 , 11 ) , Position :: new( 0 , 24 ) ) ,
208+ data: Some ( serde_json:: to_value( CodeActions :: PrefixUnderscore ) . unwrap( ) ) ,
209+ ..Default :: default ( )
210+ } ] ) ,
211+ edit: Some ( WorkspaceEdit {
212+ changes: Some ( HashMap :: from( [ (
213+ TEST_URI . clone( ) ,
214+ vec![ TextEdit {
215+ range: Range :: new( Position :: new( 0 , 12 ) , Position :: new( 0 , 12 ) ) ,
216+ new_text: String :: from( "_" )
217+ } , TextEdit {
218+ range: Range :: new( Position :: new( 1 , 16 ) , Position :: new( 1 , 16 ) ) ,
219+ new_text: String :: from( "_" )
220+ } ]
221+ ) , ] ) ) ,
222+ ..Default :: default ( )
223+ } ) ,
224+ ..Default :: default ( )
225+ } ) ] ) ]
139226 #[ tokio:: test( flavor = "current_thread" ) ]
140227 async fn server_code_action (
141228 #[ case] source : & str ,
0 commit comments