1
- use ide_db:: { syntax_helpers:: { format_string:: is_format_string, format_string_exprs:: { parse_format_exprs, Arg } } , assists:: { AssistId , AssistKind } } ;
1
+ use crate :: { AssistContext , Assists } ;
2
+ use ide_db:: {
3
+ assists:: { AssistId , AssistKind } ,
4
+ syntax_helpers:: {
5
+ format_string:: is_format_string,
6
+ format_string_exprs:: { parse_format_exprs, Arg } ,
7
+ } ,
8
+ } ;
2
9
use itertools:: Itertools ;
3
- use syntax:: { ast, AstToken , AstNode , NodeOrToken , SyntaxKind :: COMMA , TextRange } ;
10
+ use syntax:: { ast, AstNode , AstToken , NodeOrToken , SyntaxKind :: COMMA , TextRange } ;
4
11
5
12
// Assist: move_format_string_arg
6
13
//
7
14
// Move an expression out of a format string.
8
15
//
9
16
// ```
17
+ // macro_rules! format_args {
18
+ // ($lit:literal $(tt:tt)*) => { 0 },
19
+ // }
20
+ // macro_rules! print {
21
+ // ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
22
+ // }
23
+ //
10
24
// fn main() {
11
- // println !("{x + 1}$0");
25
+ // print !("{x + 1}$0");
12
26
// }
13
27
// ```
14
28
// ->
15
29
// ```
30
+ // macro_rules! format_args {
31
+ // ($lit:literal $(tt:tt)*) => { 0 },
32
+ // }
33
+ // macro_rules! print {
34
+ // ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
35
+ // }
36
+ //
16
37
// fn main() {
17
- // println !("{a}", a$0 = x + 1);
38
+ // print !("{}"$0, x + 1);
18
39
// }
19
40
// ```
20
41
21
- use crate :: { AssistContext , /* AssistId, AssistKind, */ Assists } ;
22
-
23
- pub ( crate ) fn move_format_string_arg ( acc : & mut Assists , ctx : & AssistContext < ' _ > ) -> Option < ( ) > {
24
- let t = ctx. find_token_at_offset :: < ast:: String > ( ) ?;
25
- let tt = t. syntax ( ) . parent_ancestors ( ) . find_map ( ast:: TokenTree :: cast) ?;
26
-
27
- let expanded_t = ast:: String :: cast ( ctx. sema . descend_into_macros_with_kind_preference ( t. syntax ( ) . clone ( ) ) ) ?;
42
+ pub ( crate ) fn move_format_string_arg ( acc : & mut Assists , ctx : & AssistContext < ' _ > ) -> Option < ( ) > {
43
+ let fmt_string = ctx. find_token_at_offset :: < ast:: String > ( ) ?;
44
+ let tt = fmt_string. syntax ( ) . parent_ancestors ( ) . find_map ( ast:: TokenTree :: cast) ?;
28
45
46
+ let expanded_t = ast:: String :: cast (
47
+ ctx. sema . descend_into_macros_with_kind_preference ( fmt_string. syntax ( ) . clone ( ) ) ,
48
+ ) ?;
29
49
if !is_format_string ( & expanded_t) {
30
50
return None ;
31
51
}
32
52
33
- let target = tt. syntax ( ) . text_range ( ) ;
34
- let extracted_args = parse_format_exprs ( & t) . ok ( ) ?;
35
- let str_range = t. syntax ( ) . text_range ( ) ;
36
-
37
- let tokens =
38
- tt. token_trees_and_tokens ( )
39
- . filter_map ( NodeOrToken :: into_token)
40
- . collect_vec ( ) ;
41
-
42
- acc. add ( AssistId ( "move_format_string_arg" , AssistKind :: QuickFix ) , "Extract format args" , target, |edit| {
43
- let mut existing_args: Vec < String > = vec ! [ ] ;
44
- let mut current_arg = String :: new ( ) ;
45
-
46
- if let [ _opening_bracket, format_string, _args_start_comma, tokens @ .., end_bracket] = tokens. as_slice ( ) {
47
- for t in tokens {
48
- if t. kind ( ) == COMMA {
49
- existing_args. push ( current_arg. trim ( ) . into ( ) ) ;
50
- current_arg. clear ( ) ;
51
- } else {
52
- current_arg. push_str ( t. text ( ) ) ;
53
+ let ( new_fmt, extracted_args) = parse_format_exprs ( fmt_string. text ( ) ) . ok ( ) ?;
54
+
55
+ acc. add (
56
+ AssistId ( "move_format_string_arg" , AssistKind :: QuickFix ) ,
57
+ "Extract format args" ,
58
+ tt. syntax ( ) . text_range ( ) ,
59
+ |edit| {
60
+ let fmt_range = fmt_string. syntax ( ) . text_range ( ) ;
61
+
62
+ // Replace old format string with new format string whose arguments have been extracted
63
+ edit. replace ( fmt_range, new_fmt) ;
64
+
65
+ // Insert cursor at end of format string
66
+ edit. insert ( fmt_range. end ( ) , "$0" ) ;
67
+
68
+ // Extract existing arguments in macro
69
+ let tokens =
70
+ tt. token_trees_and_tokens ( ) . filter_map ( NodeOrToken :: into_token) . collect_vec ( ) ;
71
+
72
+ let mut existing_args: Vec < String > = vec ! [ ] ;
73
+
74
+ let mut current_arg = String :: new ( ) ;
75
+ if let [ _opening_bracket, format_string, _args_start_comma, tokens @ .., end_bracket] =
76
+ tokens. as_slice ( )
77
+ {
78
+ for t in tokens {
79
+ if t. kind ( ) == COMMA {
80
+ existing_args. push ( current_arg. trim ( ) . into ( ) ) ;
81
+ current_arg. clear ( ) ;
82
+ } else {
83
+ current_arg. push_str ( t. text ( ) ) ;
84
+ }
53
85
}
86
+ existing_args. push ( current_arg. trim ( ) . into ( ) ) ;
87
+
88
+ // delete everything after the format string till end bracket
89
+ // we're going to insert the new arguments later
90
+ edit. delete ( TextRange :: new (
91
+ format_string. text_range ( ) . end ( ) ,
92
+ end_bracket. text_range ( ) . start ( ) ,
93
+ ) ) ;
54
94
}
55
- existing_args. push ( current_arg. trim ( ) . into ( ) ) ;
56
-
57
- // delete everything after the format string to the end bracket
58
- // we're going to insert the new arguments later
59
- edit. delete ( TextRange :: new ( format_string. text_range ( ) . end ( ) , end_bracket. text_range ( ) . start ( ) ) ) ;
60
- }
61
-
62
- let mut existing_args = existing_args. into_iter ( ) ;
63
-
64
- // insert cursor at end of format string
65
- edit. insert ( str_range. end ( ) , "$0" ) ;
66
- let mut placeholder_idx = 1 ;
67
- let mut args = String :: new ( ) ;
68
-
69
- for ( text, extracted_args) in extracted_args {
70
- // remove expr from format string
71
- edit. delete ( text) ;
72
-
73
- args. push_str ( ", " ) ;
74
-
75
- match extracted_args {
76
- Arg :: Expr ( s) => {
77
- // insert arg
78
- args. push_str ( & s) ;
79
- } ,
80
- Arg :: Placeholder => {
81
- // try matching with existing argument
82
- match existing_args. next ( ) {
83
- Some ( ea) => {
84
- args. push_str ( & ea) ;
85
- } ,
86
- None => {
87
- // insert placeholder
88
- args. push_str ( & format ! ( "${placeholder_idx}" ) ) ;
89
- placeholder_idx += 1 ;
95
+
96
+ // Start building the new args
97
+ let mut existing_args = existing_args. into_iter ( ) ;
98
+ let mut args = String :: new ( ) ;
99
+
100
+ let mut placeholder_idx = 1 ;
101
+
102
+ for extracted_args in extracted_args {
103
+ // remove expr from format string
104
+ args. push_str ( ", " ) ;
105
+
106
+ match extracted_args {
107
+ Arg :: Expr ( s) => {
108
+ // insert arg
109
+ args. push_str ( & s) ;
110
+ }
111
+ Arg :: Placeholder => {
112
+ // try matching with existing argument
113
+ match existing_args. next ( ) {
114
+ Some ( ea) => {
115
+ args. push_str ( & ea) ;
116
+ }
117
+ None => {
118
+ // insert placeholder
119
+ args. push_str ( & format ! ( "${placeholder_idx}" ) ) ;
120
+ placeholder_idx += 1 ;
121
+ }
90
122
}
91
123
}
92
124
}
93
125
}
94
- }
95
126
96
- edit. insert ( str_range. end ( ) , args) ;
97
- } ) ;
127
+ // Insert new args
128
+ edit. insert ( fmt_range. end ( ) , args) ;
129
+ } ,
130
+ ) ;
98
131
99
132
Some ( ( ) )
100
133
}
@@ -113,97 +146,112 @@ macro_rules! print {
113
146
}
114
147
"# ;
115
148
116
- fn add_macro_decl ( s : & ' static str ) -> String {
149
+ fn add_macro_decl ( s : & ' static str ) -> String {
117
150
MACRO_DECL . to_string ( ) + s
118
151
}
119
152
120
153
#[ test]
121
154
fn multiple_middle_arg ( ) {
122
155
check_assist (
123
156
move_format_string_arg,
124
- & add_macro_decl ( r#"
157
+ & add_macro_decl (
158
+ r#"
125
159
fn main() {
126
160
print!("{} {x + 1:b} {}$0", y + 2, 2);
127
161
}
128
- "# ) ,
129
-
130
- & add_macro_decl ( r#"
162
+ "# ,
163
+ ) ,
164
+ & add_macro_decl (
165
+ r#"
131
166
fn main() {
132
167
print!("{} {:b} {}"$0, y + 2, x + 1, 2);
133
168
}
134
- "# ) ,
169
+ "# ,
170
+ ) ,
135
171
) ;
136
172
}
137
173
138
174
#[ test]
139
175
fn single_arg ( ) {
140
176
check_assist (
141
177
move_format_string_arg,
142
- & add_macro_decl ( r#"
178
+ & add_macro_decl (
179
+ r#"
143
180
fn main() {
144
181
print!("{obj.value:b}$0",);
145
182
}
146
- "# ) ,
147
- & add_macro_decl ( r#"
183
+ "# ,
184
+ ) ,
185
+ & add_macro_decl (
186
+ r#"
148
187
fn main() {
149
188
print!("{:b}"$0, obj.value);
150
189
}
151
- "# ) ,
190
+ "# ,
191
+ ) ,
152
192
) ;
153
193
}
154
194
155
195
#[ test]
156
196
fn multiple_middle_placeholders_arg ( ) {
157
197
check_assist (
158
198
move_format_string_arg,
159
- & add_macro_decl ( r#"
199
+ & add_macro_decl (
200
+ r#"
160
201
fn main() {
161
202
print!("{} {x + 1:b} {} {}$0", y + 2, 2);
162
203
}
163
- "# ) ,
164
-
165
- & add_macro_decl ( r#"
204
+ "# ,
205
+ ) ,
206
+ & add_macro_decl (
207
+ r#"
166
208
fn main() {
167
209
print!("{} {:b} {} {}"$0, y + 2, x + 1, 2, $1);
168
210
}
169
- "# ) ,
211
+ "# ,
212
+ ) ,
170
213
) ;
171
214
}
172
215
173
216
#[ test]
174
217
fn multiple_trailing_args ( ) {
175
218
check_assist (
176
219
move_format_string_arg,
177
- & add_macro_decl ( r#"
220
+ & add_macro_decl (
221
+ r#"
178
222
fn main() {
179
223
print!("{} {x + 1:b} {Struct(1, 2)}$0", 1);
180
224
}
181
- "# ) ,
182
-
183
- & add_macro_decl ( r#"
225
+ "# ,
226
+ ) ,
227
+ & add_macro_decl (
228
+ r#"
184
229
fn main() {
185
230
print!("{} {:b} {}"$0, 1, x + 1, Struct(1, 2));
186
231
}
187
- "# ) ,
232
+ "# ,
233
+ ) ,
188
234
) ;
189
235
}
190
236
191
237
#[ test]
192
238
fn improper_commas ( ) {
193
239
check_assist (
194
240
move_format_string_arg,
195
- & add_macro_decl ( r#"
241
+ & add_macro_decl (
242
+ r#"
196
243
fn main() {
197
244
print!("{} {x + 1:b} {Struct(1, 2)}$0", 1,);
198
245
}
199
- "# ) ,
200
-
201
- & add_macro_decl ( r#"
246
+ "# ,
247
+ ) ,
248
+ & add_macro_decl (
249
+ r#"
202
250
fn main() {
203
251
print!("{} {:b} {}"$0, 1, x + 1, Struct(1, 2));
204
252
}
205
- "# ) ,
253
+ "# ,
254
+ ) ,
206
255
) ;
207
256
}
208
-
209
257
}
0 commit comments