1
1
use itertools:: Itertools ;
2
2
use syntax:: {
3
- Edition , NodeOrToken , SyntaxElement , T , TextRange , TextSize ,
4
- ast:: { self , AstNode , AstToken , make} ,
5
- match_ast, ted,
3
+ Edition , NodeOrToken , SyntaxNode , SyntaxToken , T ,
4
+ ast:: { self , AstNode , make} ,
5
+ match_ast,
6
+ syntax_editor:: { Position , SyntaxEditor } ,
6
7
} ;
7
8
8
9
use crate :: { AssistContext , AssistId , Assists } ;
@@ -40,21 +41,23 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
40
41
41
42
let replacements =
42
43
macro_calls. into_iter ( ) . filter_map ( compute_dbg_replacement) . collect :: < Vec < _ > > ( ) ;
43
-
44
- acc. add (
45
- AssistId :: quick_fix ( "remove_dbg" ) ,
46
- "Remove dbg!()" ,
47
- replacements. iter ( ) . map ( |& ( range, _) | range) . reduce ( |acc, range| acc. cover ( range) ) ?,
48
- |builder| {
49
- for ( range, expr) in replacements {
50
- if let Some ( expr) = expr {
51
- builder. replace ( range, expr. to_string ( ) ) ;
52
- } else {
53
- builder. delete ( range) ;
54
- }
44
+ let target = replacements
45
+ . iter ( )
46
+ . flat_map ( |( node_or_token, _) | node_or_token. iter ( ) )
47
+ . map ( |t| t. text_range ( ) )
48
+ . reduce ( |acc, range| acc. cover ( range) ) ?;
49
+ acc. add ( AssistId :: quick_fix ( "remove_dbg" ) , "Remove dbg!()" , target, |builder| {
50
+ let mut editor = builder. make_editor ( ctx. source_file ( ) . syntax ( ) ) ;
51
+ for ( range, expr) in replacements {
52
+ if let Some ( expr) = expr {
53
+ editor. insert ( Position :: before ( range[ 0 ] . clone ( ) ) , expr. syntax ( ) . clone_for_update ( ) ) ;
54
+ }
55
+ for node_or_token in range {
56
+ editor. delete ( node_or_token) ;
55
57
}
56
- } ,
57
- )
58
+ }
59
+ builder. add_file_edits ( ctx. vfs_file_id ( ) , editor) ;
60
+ } )
58
61
}
59
62
60
63
/// Returns `None` when either
@@ -63,7 +66,9 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
63
66
/// - (`macro_expr` has no parent - is that possible?)
64
67
///
65
68
/// Returns `Some(_, None)` when the macro call should just be removed.
66
- fn compute_dbg_replacement ( macro_expr : ast:: MacroExpr ) -> Option < ( TextRange , Option < ast:: Expr > ) > {
69
+ fn compute_dbg_replacement (
70
+ macro_expr : ast:: MacroExpr ,
71
+ ) -> Option < ( Vec < NodeOrToken < SyntaxNode , SyntaxToken > > , Option < ast:: Expr > ) > {
67
72
let macro_call = macro_expr. macro_call ( ) ?;
68
73
let tt = macro_call. token_tree ( ) ?;
69
74
let r_delim = NodeOrToken :: Token ( tt. right_delimiter_token ( ) ?) ;
@@ -88,22 +93,22 @@ fn compute_dbg_replacement(macro_expr: ast::MacroExpr) -> Option<(TextRange, Opt
88
93
match_ast ! {
89
94
match parent {
90
95
ast:: StmtList ( _) => {
91
- let range = macro_expr. syntax( ) . text_range ( ) ;
92
- let range = match whitespace_start ( macro_expr. syntax( ) . prev_sibling_or_token( ) ) {
93
- Some ( start ) => range . cover_offset ( start ) ,
94
- None => range ,
95
- } ;
96
- ( range , None )
96
+ let mut replace = vec! [ macro_expr. syntax( ) . clone ( ) . into ( ) ] ;
97
+ if let Some ( prev_sibling ) = macro_expr. syntax( ) . prev_sibling_or_token( )
98
+ && prev_sibling . kind ( ) == syntax :: SyntaxKind :: WHITESPACE {
99
+ replace . push ( prev_sibling ) ;
100
+ }
101
+ ( replace , None )
97
102
} ,
98
103
ast:: ExprStmt ( it) => {
99
- let range = it. syntax( ) . text_range ( ) ;
100
- let range = match whitespace_start ( it. syntax( ) . prev_sibling_or_token( ) ) {
101
- Some ( start ) => range . cover_offset ( start ) ,
102
- None => range ,
103
- } ;
104
- ( range , None )
104
+ let mut replace = vec! [ it. syntax( ) . clone ( ) . into ( ) ] ;
105
+ if let Some ( prev_sibling ) = it. syntax( ) . prev_sibling_or_token( )
106
+ && prev_sibling . kind ( ) == syntax :: SyntaxKind :: WHITESPACE {
107
+ replace . push ( prev_sibling ) ;
108
+ }
109
+ ( replace , None )
105
110
} ,
106
- _ => ( macro_call. syntax( ) . text_range ( ) , Some ( make:: ext:: expr_unit( ) ) ) ,
111
+ _ => ( vec! [ macro_call. syntax( ) . clone ( ) . into ( ) ] , Some ( make:: ext:: expr_unit( ) ) ) ,
107
112
}
108
113
}
109
114
}
@@ -147,13 +152,13 @@ fn compute_dbg_replacement(macro_expr: ast::MacroExpr) -> Option<(TextRange, Opt
147
152
} ;
148
153
let expr = replace_nested_dbgs ( expr. clone ( ) ) ;
149
154
let expr = if wrap { make:: expr_paren ( expr) . into ( ) } else { expr. clone_subtree ( ) } ;
150
- ( macro_call. syntax ( ) . text_range ( ) , Some ( expr) )
155
+ ( vec ! [ macro_call. syntax( ) . clone ( ) . into ( ) ] , Some ( expr) )
151
156
}
152
157
// dbg!(expr0, expr1, ...)
153
158
exprs => {
154
159
let exprs = exprs. iter ( ) . cloned ( ) . map ( replace_nested_dbgs) ;
155
160
let expr = make:: expr_tuple ( exprs) ;
156
- ( macro_call. syntax ( ) . text_range ( ) , Some ( expr. into ( ) ) )
161
+ ( vec ! [ macro_call. syntax( ) . clone ( ) . into ( ) ] , Some ( expr. into ( ) ) )
157
162
}
158
163
} )
159
164
}
@@ -178,8 +183,8 @@ fn replace_nested_dbgs(expanded: ast::Expr) -> ast::Expr {
178
183
return replaced;
179
184
}
180
185
181
- let expanded = expanded. clone_for_update ( ) ;
182
-
186
+ let expanded = expanded. clone_subtree ( ) ;
187
+ let mut editor = SyntaxEditor :: new ( expanded . syntax ( ) . clone ( ) ) ;
183
188
// We need to collect to avoid mutation during traversal.
184
189
let macro_exprs: Vec < _ > =
185
190
expanded. syntax ( ) . descendants ( ) . filter_map ( ast:: MacroExpr :: cast) . collect ( ) ;
@@ -191,17 +196,13 @@ fn replace_nested_dbgs(expanded: ast::Expr) -> ast::Expr {
191
196
} ;
192
197
193
198
if let Some ( expr) = expr_opt {
194
- ted :: replace ( mac. syntax ( ) , expr. syntax ( ) . clone_for_update ( ) ) ;
199
+ editor . replace ( mac. syntax ( ) , expr. syntax ( ) . clone_for_update ( ) ) ;
195
200
} else {
196
- ted :: remove ( mac. syntax ( ) ) ;
201
+ editor . delete ( mac. syntax ( ) ) ;
197
202
}
198
203
}
199
-
200
- expanded
201
- }
202
-
203
- fn whitespace_start ( it : Option < SyntaxElement > ) -> Option < TextSize > {
204
- Some ( it?. into_token ( ) . and_then ( ast:: Whitespace :: cast) ?. syntax ( ) . text_range ( ) . start ( ) )
204
+ let expanded_syntax = editor. finish ( ) . new_root ( ) . clone ( ) ;
205
+ ast:: Expr :: cast ( expanded_syntax) . unwrap ( )
205
206
}
206
207
207
208
#[ cfg( test) ]
0 commit comments