1
1
use ide_db:: { defs:: Definition , search:: FileReference , EditionedFileId } ;
2
2
use syntax:: {
3
- algo:: find_node_at_range,
3
+ algo:: { find_node_at_range, least_common_ancestor_element } ,
4
4
ast:: { self , HasArgList } ,
5
- AstNode , SourceFile , SyntaxKind , SyntaxNode , TextRange , T ,
5
+ syntax_editor:: Element ,
6
+ AstNode , SourceFile , SyntaxElement , SyntaxKind , SyntaxNode , TextRange , T ,
6
7
} ;
7
8
8
9
use SyntaxKind :: WHITESPACE ;
@@ -74,15 +75,21 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) ->
74
75
cov_mark:: hit!( keep_used) ;
75
76
return None ;
76
77
}
78
+ let parent = param. syntax ( ) . parent ( ) ?;
77
79
acc. add (
78
80
AssistId ( "remove_unused_param" , AssistKind :: Refactor ) ,
79
81
"Remove unused parameter" ,
80
82
param. syntax ( ) . text_range ( ) ,
81
83
|builder| {
82
- builder. delete ( range_to_remove ( param. syntax ( ) ) ) ;
84
+ let mut editor = builder. make_editor ( & parent) ;
85
+ let elements = elements_to_remove ( param. syntax ( ) ) ;
86
+ for element in elements {
87
+ editor. delete ( element) ;
88
+ }
83
89
for ( file_id, references) in fn_def. usages ( & ctx. sema ) . all ( ) {
84
90
process_usages ( ctx, builder, file_id, references, param_position, is_self_present) ;
85
91
}
92
+ builder. add_file_edits ( ctx. file_id ( ) , editor) ;
86
93
} ,
87
94
)
88
95
}
@@ -96,20 +103,24 @@ fn process_usages(
96
103
is_self_present : bool ,
97
104
) {
98
105
let source_file = ctx. sema . parse ( file_id) ;
99
- builder. edit_file ( file_id) ;
100
106
let possible_ranges = references
101
107
. into_iter ( )
102
108
. filter_map ( |usage| process_usage ( & source_file, usage, arg_to_remove, is_self_present) ) ;
103
109
104
- let mut ranges_to_delete: Vec < TextRange > = vec ! [ ] ;
105
- for range in possible_ranges {
106
- if !ranges_to_delete. iter ( ) . any ( |it| it. contains_range ( range) ) {
107
- ranges_to_delete. push ( range)
110
+ for element_range in possible_ranges {
111
+ let Some ( SyntaxElement :: Node ( parent) ) = element_range
112
+ . iter ( )
113
+ . cloned ( )
114
+ . reduce ( |a, b| least_common_ancestor_element ( & a, & b) . unwrap ( ) . syntax_element ( ) )
115
+ else {
116
+ continue ;
117
+ } ;
118
+ let mut editor = builder. make_editor ( & parent) ;
119
+ for element in element_range {
120
+ editor. delete ( element) ;
108
121
}
109
- }
110
122
111
- for range in ranges_to_delete {
112
- builder. delete ( range)
123
+ builder. add_file_edits ( file_id, editor) ;
113
124
}
114
125
}
115
126
@@ -118,7 +129,7 @@ fn process_usage(
118
129
FileReference { range, .. } : FileReference ,
119
130
mut arg_to_remove : usize ,
120
131
is_self_present : bool ,
121
- ) -> Option < TextRange > {
132
+ ) -> Option < Vec < SyntaxElement > > {
122
133
let call_expr_opt: Option < ast:: CallExpr > = find_node_at_range ( source_file. syntax ( ) , range) ;
123
134
if let Some ( call_expr) = call_expr_opt {
124
135
let call_expr_range = call_expr. expr ( ) ?. syntax ( ) . text_range ( ) ;
@@ -127,7 +138,7 @@ fn process_usage(
127
138
}
128
139
129
140
let arg = call_expr. arg_list ( ) ?. args ( ) . nth ( arg_to_remove) ?;
130
- return Some ( range_to_remove ( arg. syntax ( ) ) ) ;
141
+ return Some ( elements_to_remove ( arg. syntax ( ) ) ) ;
131
142
}
132
143
133
144
let method_call_expr_opt: Option < ast:: MethodCallExpr > =
@@ -143,7 +154,7 @@ fn process_usage(
143
154
}
144
155
145
156
let arg = method_call_expr. arg_list ( ) ?. args ( ) . nth ( arg_to_remove) ?;
146
- return Some ( range_to_remove ( arg. syntax ( ) ) ) ;
157
+ return Some ( elements_to_remove ( arg. syntax ( ) ) ) ;
147
158
}
148
159
149
160
None
@@ -174,6 +185,29 @@ pub(crate) fn range_to_remove(node: &SyntaxNode) -> TextRange {
174
185
}
175
186
}
176
187
188
+ pub ( crate ) fn elements_to_remove ( node : & SyntaxNode ) -> Vec < SyntaxElement > {
189
+ let up_to_comma = next_prev ( ) . find_map ( |dir| {
190
+ node. siblings_with_tokens ( dir)
191
+ . filter_map ( |it| it. into_token ( ) )
192
+ . find ( |it| it. kind ( ) == T ! [ , ] )
193
+ . map ( |it| ( dir, it) )
194
+ } ) ;
195
+ if let Some ( ( dir, token) ) = up_to_comma {
196
+ let after = token. siblings_with_tokens ( dir) . nth ( 1 ) . unwrap ( ) ;
197
+ let mut result: Vec < _ > =
198
+ node. siblings_with_tokens ( dir) . take_while ( |it| it != & after) . collect ( ) ;
199
+ if node. next_sibling ( ) . is_some ( ) {
200
+ result. extend (
201
+ token. siblings_with_tokens ( dir) . skip ( 1 ) . take_while ( |it| it. kind ( ) == WHITESPACE ) ,
202
+ ) ;
203
+ }
204
+
205
+ result
206
+ } else {
207
+ vec ! [ node. syntax_element( ) ]
208
+ }
209
+ }
210
+
177
211
#[ cfg( test) ]
178
212
mod tests {
179
213
use crate :: tests:: { check_assist, check_assist_not_applicable} ;
0 commit comments