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+ } ;
29use itertools:: Itertools ;
3- use syntax:: { ast, AstToken , AstNode , NodeOrToken , SyntaxKind :: COMMA , TextRange } ;
10+ use syntax:: { ast, AstNode , AstToken , NodeOrToken , SyntaxKind :: COMMA , TextRange } ;
411
512// Assist: move_format_string_arg
613//
714// Move an expression out of a format string.
815//
916// ```
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+ //
1024// fn main() {
11- // println !("{x + 1}$0");
25+ // print !("{x + 1}$0");
1226// }
1327// ```
1428// ->
1529// ```
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+ //
1637// fn main() {
17- // println !("{a}", a$0 = x + 1);
38+ // print !("{}"$0, x + 1);
1839// }
1940// ```
2041
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) ?;
2845
46+ let expanded_t = ast:: String :: cast (
47+ ctx. sema . descend_into_macros_with_kind_preference ( fmt_string. syntax ( ) . clone ( ) ) ,
48+ ) ?;
2949 if !is_format_string ( & expanded_t) {
3050 return None ;
3151 }
3252
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+ }
5385 }
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+ ) ) ;
5494 }
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+ }
90122 }
91123 }
92124 }
93125 }
94- }
95126
96- edit. insert ( str_range. end ( ) , args) ;
97- } ) ;
127+ // Insert new args
128+ edit. insert ( fmt_range. end ( ) , args) ;
129+ } ,
130+ ) ;
98131
99132 Some ( ( ) )
100133}
@@ -113,97 +146,112 @@ macro_rules! print {
113146}
114147"# ;
115148
116- fn add_macro_decl ( s : & ' static str ) -> String {
149+ fn add_macro_decl ( s : & ' static str ) -> String {
117150 MACRO_DECL . to_string ( ) + s
118151 }
119152
120153 #[ test]
121154 fn multiple_middle_arg ( ) {
122155 check_assist (
123156 move_format_string_arg,
124- & add_macro_decl ( r#"
157+ & add_macro_decl (
158+ r#"
125159fn main() {
126160 print!("{} {x + 1:b} {}$0", y + 2, 2);
127161}
128- "# ) ,
129-
130- & add_macro_decl ( r#"
162+ "# ,
163+ ) ,
164+ & add_macro_decl (
165+ r#"
131166fn main() {
132167 print!("{} {:b} {}"$0, y + 2, x + 1, 2);
133168}
134- "# ) ,
169+ "# ,
170+ ) ,
135171 ) ;
136172 }
137173
138174 #[ test]
139175 fn single_arg ( ) {
140176 check_assist (
141177 move_format_string_arg,
142- & add_macro_decl ( r#"
178+ & add_macro_decl (
179+ r#"
143180fn main() {
144181 print!("{obj.value:b}$0",);
145182}
146- "# ) ,
147- & add_macro_decl ( r#"
183+ "# ,
184+ ) ,
185+ & add_macro_decl (
186+ r#"
148187fn main() {
149188 print!("{:b}"$0, obj.value);
150189}
151- "# ) ,
190+ "# ,
191+ ) ,
152192 ) ;
153193 }
154194
155195 #[ test]
156196 fn multiple_middle_placeholders_arg ( ) {
157197 check_assist (
158198 move_format_string_arg,
159- & add_macro_decl ( r#"
199+ & add_macro_decl (
200+ r#"
160201fn main() {
161202 print!("{} {x + 1:b} {} {}$0", y + 2, 2);
162203}
163- "# ) ,
164-
165- & add_macro_decl ( r#"
204+ "# ,
205+ ) ,
206+ & add_macro_decl (
207+ r#"
166208fn main() {
167209 print!("{} {:b} {} {}"$0, y + 2, x + 1, 2, $1);
168210}
169- "# ) ,
211+ "# ,
212+ ) ,
170213 ) ;
171214 }
172215
173216 #[ test]
174217 fn multiple_trailing_args ( ) {
175218 check_assist (
176219 move_format_string_arg,
177- & add_macro_decl ( r#"
220+ & add_macro_decl (
221+ r#"
178222fn main() {
179223 print!("{} {x + 1:b} {Struct(1, 2)}$0", 1);
180224}
181- "# ) ,
182-
183- & add_macro_decl ( r#"
225+ "# ,
226+ ) ,
227+ & add_macro_decl (
228+ r#"
184229fn main() {
185230 print!("{} {:b} {}"$0, 1, x + 1, Struct(1, 2));
186231}
187- "# ) ,
232+ "# ,
233+ ) ,
188234 ) ;
189235 }
190236
191237 #[ test]
192238 fn improper_commas ( ) {
193239 check_assist (
194240 move_format_string_arg,
195- & add_macro_decl ( r#"
241+ & add_macro_decl (
242+ r#"
196243fn main() {
197244 print!("{} {x + 1:b} {Struct(1, 2)}$0", 1,);
198245}
199- "# ) ,
200-
201- & add_macro_decl ( r#"
246+ "# ,
247+ ) ,
248+ & add_macro_decl (
249+ r#"
202250fn main() {
203251 print!("{} {:b} {}"$0, 1, x + 1, Struct(1, 2));
204252}
205- "# ) ,
253+ "# ,
254+ ) ,
206255 ) ;
207256 }
208-
209257}
0 commit comments