Skip to content

Commit 1e20bf3

Browse files
committed
Auto merge of rust-lang#13684 - unvalley:extract-expressions-from-format-string, r=Veykril
feat: extract_expressions_from_format_string closes rust-lang#13640 - rename to `extract_expressions_from_format_string` - leave identifier from format string - but this is from rustc version 1.65.0 - Should I add flag or something? Note: the assist behaves below cases for now. I'll create an issue for these. ```rs let var = 1 + 1; // ok format!("{var} {1+1}"); // → format!("{var} {}", 1+1); format!("{var:?} {1+1}"); // → format!("{var:?} {}", 1 + 1); format!("{var} {var} {1+1}"); // → format!("{var} {var} {}", 1 + 1); // breaks (need to handle minimum width by postfix`$`) format!("{var:width$} {1+1}"); // → format!("{var:width\$} {}", 1+1); format!("{var:.prec$} {1+1}"); // → format!("{var:.prec\$} {}", 1+1); format!("Hello {:1$}! {1+1}", "x" 5); // → format("Hello {:1\$}! {}", "x", 1+1); format!("Hello {:width$}! {1+1}", "x", width = 5); // → println!("Hello {:width\$}! {}", "x", 1+1); ``` https://user-images.githubusercontent.com/38400669/204344911-f1f8fbd2-706d-414e-b1ab-d309376efb9b.mov
2 parents 814ff01 + 9eabc2c commit 1e20bf3

File tree

6 files changed

+106
-65
lines changed

6 files changed

+106
-65
lines changed

crates/ide-assists/src/handlers/move_format_string_arg.rs renamed to crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use itertools::Itertools;
1010
use stdx::format_to;
1111
use syntax::{ast, AstNode, AstToken, NodeOrToken, SyntaxKind::COMMA, TextRange};
1212

13-
// Assist: move_format_string_arg
13+
// Assist: extract_expressions_from_format_string
1414
//
1515
// Move an expression out of a format string.
1616
//
@@ -23,7 +23,7 @@ use syntax::{ast, AstNode, AstToken, NodeOrToken, SyntaxKind::COMMA, TextRange};
2323
// }
2424
//
2525
// fn main() {
26-
// print!("{x + 1}$0");
26+
// print!("{var} {x + 1}$0");
2727
// }
2828
// ```
2929
// ->
@@ -36,11 +36,14 @@ use syntax::{ast, AstNode, AstToken, NodeOrToken, SyntaxKind::COMMA, TextRange};
3636
// }
3737
//
3838
// fn main() {
39-
// print!("{}"$0, x + 1);
39+
// print!("{var} {}"$0, x + 1);
4040
// }
4141
// ```
4242

43-
pub(crate) fn move_format_string_arg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
43+
pub(crate) fn extract_expressions_from_format_string(
44+
acc: &mut Assists,
45+
ctx: &AssistContext<'_>,
46+
) -> Option<()> {
4447
let fmt_string = ctx.find_token_at_offset::<ast::String>()?;
4548
let tt = fmt_string.syntax().parent().and_then(ast::TokenTree::cast)?;
4649

@@ -58,15 +61,15 @@ pub(crate) fn move_format_string_arg(acc: &mut Assists, ctx: &AssistContext<'_>)
5861

5962
acc.add(
6063
AssistId(
61-
"move_format_string_arg",
64+
"extract_expressions_from_format_string",
6265
// if there aren't any expressions, then make the assist a RefactorExtract
6366
if extracted_args.iter().filter(|f| matches!(f, Arg::Expr(_))).count() == 0 {
6467
AssistKind::RefactorExtract
6568
} else {
6669
AssistKind::QuickFix
6770
},
6871
),
69-
"Extract format args",
72+
"Extract format expressions",
7073
tt.syntax().text_range(),
7174
|edit| {
7275
let fmt_range = fmt_string.syntax().text_range();
@@ -118,15 +121,14 @@ pub(crate) fn move_format_string_arg(acc: &mut Assists, ctx: &AssistContext<'_>)
118121
let mut placeholder_idx = 1;
119122

120123
for extracted_args in extracted_args {
121-
// remove expr from format string
122-
args.push_str(", ");
123-
124124
match extracted_args {
125-
Arg::Ident(s) | Arg::Expr(s) => {
125+
Arg::Expr(s)=> {
126+
args.push_str(", ");
126127
// insert arg
127128
args.push_str(&s);
128129
}
129130
Arg::Placeholder => {
131+
args.push_str(", ");
130132
// try matching with existing argument
131133
match existing_args.next() {
132134
Some(ea) => {
@@ -139,6 +141,7 @@ pub(crate) fn move_format_string_arg(acc: &mut Assists, ctx: &AssistContext<'_>)
139141
}
140142
}
141143
}
144+
Arg::Ident(_s) => (),
142145
}
143146
}
144147

@@ -171,7 +174,7 @@ macro_rules! print {
171174
#[test]
172175
fn multiple_middle_arg() {
173176
check_assist(
174-
move_format_string_arg,
177+
extract_expressions_from_format_string,
175178
&add_macro_decl(
176179
r#"
177180
fn main() {
@@ -192,7 +195,7 @@ fn main() {
192195
#[test]
193196
fn single_arg() {
194197
check_assist(
195-
move_format_string_arg,
198+
extract_expressions_from_format_string,
196199
&add_macro_decl(
197200
r#"
198201
fn main() {
@@ -213,7 +216,7 @@ fn main() {
213216
#[test]
214217
fn multiple_middle_placeholders_arg() {
215218
check_assist(
216-
move_format_string_arg,
219+
extract_expressions_from_format_string,
217220
&add_macro_decl(
218221
r#"
219222
fn main() {
@@ -234,7 +237,7 @@ fn main() {
234237
#[test]
235238
fn multiple_trailing_args() {
236239
check_assist(
237-
move_format_string_arg,
240+
extract_expressions_from_format_string,
238241
&add_macro_decl(
239242
r#"
240243
fn main() {
@@ -255,7 +258,7 @@ fn main() {
255258
#[test]
256259
fn improper_commas() {
257260
check_assist(
258-
move_format_string_arg,
261+
extract_expressions_from_format_string,
259262
&add_macro_decl(
260263
r#"
261264
fn main() {
@@ -276,7 +279,7 @@ fn main() {
276279
#[test]
277280
fn nested_tt() {
278281
check_assist(
279-
move_format_string_arg,
282+
extract_expressions_from_format_string,
280283
&add_macro_decl(
281284
r#"
282285
fn main() {
@@ -289,6 +292,29 @@ fn main() {
289292
fn main() {
290293
print!("My name is {} {}"$0, stringify!(Paperino), x + x)
291294
}
295+
"#,
296+
),
297+
);
298+
}
299+
300+
#[test]
301+
fn extract_only_expressions() {
302+
check_assist(
303+
extract_expressions_from_format_string,
304+
&add_macro_decl(
305+
r#"
306+
fn main() {
307+
let var = 1 + 1;
308+
print!("foobar {var} {var:?} {x$0 + x}")
309+
}
310+
"#,
311+
),
312+
&add_macro_decl(
313+
r#"
314+
fn main() {
315+
let var = 1 + 1;
316+
print!("foobar {var} {var:?} {}"$0, x + x)
317+
}
292318
"#,
293319
),
294320
);

crates/ide-assists/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ mod handlers {
128128
mod convert_while_to_loop;
129129
mod destructure_tuple_binding;
130130
mod expand_glob_import;
131+
mod extract_expressions_from_format_string;
131132
mod extract_function;
132133
mod extract_module;
133134
mod extract_struct_from_enum_variant;
@@ -138,7 +139,6 @@ mod handlers {
138139
mod flip_binexpr;
139140
mod flip_comma;
140141
mod flip_trait_bound;
141-
mod move_format_string_arg;
142142
mod generate_constant;
143143
mod generate_default_from_enum_variant;
144144
mod generate_default_from_new;
@@ -231,6 +231,7 @@ mod handlers {
231231
convert_while_to_loop::convert_while_to_loop,
232232
destructure_tuple_binding::destructure_tuple_binding,
233233
expand_glob_import::expand_glob_import,
234+
extract_expressions_from_format_string::extract_expressions_from_format_string,
234235
extract_struct_from_enum_variant::extract_struct_from_enum_variant,
235236
extract_type_alias::extract_type_alias,
236237
fix_visibility::fix_visibility,
@@ -265,7 +266,6 @@ mod handlers {
265266
merge_match_arms::merge_match_arms,
266267
move_bounds::move_bounds_to_where_clause,
267268
move_const_to_impl::move_const_to_impl,
268-
move_format_string_arg::move_format_string_arg,
269269
move_guard::move_arm_cond_to_match_guard,
270270
move_guard::move_guard_to_arm_body,
271271
move_module_to_file::move_module_to_file,

crates/ide-assists/src/tests/generated.rs

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,37 @@ fn qux(bar: Bar, baz: Baz) {}
624624
)
625625
}
626626

627+
#[test]
628+
fn doctest_extract_expressions_from_format_string() {
629+
check_doc_test(
630+
"extract_expressions_from_format_string",
631+
r#####"
632+
macro_rules! format_args {
633+
($lit:literal $(tt:tt)*) => { 0 },
634+
}
635+
macro_rules! print {
636+
($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
637+
}
638+
639+
fn main() {
640+
print!("{var} {x + 1}$0");
641+
}
642+
"#####,
643+
r#####"
644+
macro_rules! format_args {
645+
($lit:literal $(tt:tt)*) => { 0 },
646+
}
647+
macro_rules! print {
648+
($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
649+
}
650+
651+
fn main() {
652+
print!("{var} {}"$0, x + 1);
653+
}
654+
"#####,
655+
)
656+
}
657+
627658
#[test]
628659
fn doctest_extract_function() {
629660
check_doc_test(
@@ -1703,37 +1734,6 @@ impl S {
17031734
)
17041735
}
17051736

1706-
#[test]
1707-
fn doctest_move_format_string_arg() {
1708-
check_doc_test(
1709-
"move_format_string_arg",
1710-
r#####"
1711-
macro_rules! format_args {
1712-
($lit:literal $(tt:tt)*) => { 0 },
1713-
}
1714-
macro_rules! print {
1715-
($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
1716-
}
1717-
1718-
fn main() {
1719-
print!("{x + 1}$0");
1720-
}
1721-
"#####,
1722-
r#####"
1723-
macro_rules! format_args {
1724-
($lit:literal $(tt:tt)*) => { 0 },
1725-
}
1726-
macro_rules! print {
1727-
($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
1728-
}
1729-
1730-
fn main() {
1731-
print!("{}"$0, x + 1);
1732-
}
1733-
"#####,
1734-
)
1735-
}
1736-
17371737
#[test]
17381738
fn doctest_move_from_mod_rs() {
17391739
check_doc_test(

crates/ide-completion/src/completions/postfix.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -595,12 +595,12 @@ fn main() {
595595
check_edit(
596596
"format",
597597
r#"fn main() { "{some_var:?}".$0 }"#,
598-
r#"fn main() { format!("{:?}", some_var) }"#,
598+
r#"fn main() { format!("{some_var:?}") }"#,
599599
);
600600
check_edit(
601601
"panic",
602602
r#"fn main() { "Panic with {a}".$0 }"#,
603-
r#"fn main() { panic!("Panic with {}", a) }"#,
603+
r#"fn main() { panic!("Panic with {a}") }"#,
604604
);
605605
check_edit(
606606
"println",

crates/ide-completion/src/completions/postfix/format_like.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,11 @@ pub(crate) fn add_format_like_completions(
5454
if let Ok((out, exprs)) = parse_format_exprs(receiver_text.text()) {
5555
let exprs = with_placeholders(exprs);
5656
for (label, macro_name) in KINDS {
57-
let snippet = format!(r#"{macro_name}({out}, {})"#, exprs.join(", "));
57+
let snippet = if exprs.is_empty() {
58+
format!(r#"{}({})"#, macro_name, out)
59+
} else {
60+
format!(r#"{}({}, {})"#, macro_name, out, exprs.join(", "))
61+
};
5862

5963
postfix_snippet(label, macro_name, &snippet).add_to(acc);
6064
}
@@ -72,10 +76,9 @@ mod tests {
7276
("eprintln!", "{}", r#"eprintln!("{}", $1)"#),
7377
(
7478
"log::info!",
75-
"{} {expr} {} {2 + 2}",
76-
r#"log::info!("{} {} {} {}", $1, expr, $2, 2 + 2)"#,
79+
"{} {ident} {} {2 + 2}",
80+
r#"log::info!("{} {ident} {} {}", $1, $2, 2 + 2)"#,
7781
),
78-
("format!", "{expr:?}", r#"format!("{:?}", expr)"#),
7982
];
8083

8184
for (kind, input, output) in test_vector {
@@ -85,4 +88,18 @@ mod tests {
8588
assert_eq!(&snippet, output);
8689
}
8790
}
91+
92+
#[test]
93+
fn test_into_suggestion_no_epxrs() {
94+
let test_vector = &[
95+
("println!", "{ident}", r#"println!("{ident}")"#),
96+
("format!", "{ident:?}", r#"format!("{ident:?}")"#),
97+
];
98+
99+
for (kind, input, output) in test_vector {
100+
let (parsed_string, _exprs) = parse_format_exprs(input).unwrap();
101+
let snippet = format!(r#"{}("{}")"#, kind, parsed_string);
102+
assert_eq!(&snippet, output);
103+
}
104+
}
88105
}

crates/ide-db/src/syntax_helpers/format_string_exprs.rs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,8 @@ pub fn parse_format_exprs(input: &str) -> Result<(String, Vec<Arg>), ()> {
140140
output.push_str(trimmed);
141141
} else if matches!(state, State::Expr) {
142142
extracted_expressions.push(Arg::Expr(trimmed.into()));
143-
} else {
144-
extracted_expressions.push(Arg::Ident(trimmed.into()));
143+
} else if matches!(state, State::Ident) {
144+
output.push_str(trimmed);
145145
}
146146

147147
output.push(chr);
@@ -218,9 +218,9 @@ mod tests {
218218
let test_vector = &[
219219
("no expressions", expect![["no expressions"]]),
220220
(r"no expressions with \$0$1", expect![r"no expressions with \\\$0\$1"]),
221-
("{expr} is {2 + 2}", expect![["{} is {}; expr, 2 + 2"]]),
222-
("{expr:?}", expect![["{:?}; expr"]]),
223-
("{expr:1$}", expect![[r"{:1\$}; expr"]]),
221+
("{expr} is {2 + 2}", expect![["{expr} is {}; 2 + 2"]]),
222+
("{expr:?}", expect![["{expr:?}"]]),
223+
("{expr:1$}", expect![[r"{expr:1\$}"]]),
224224
("{:1$}", expect![[r"{:1\$}; $1"]]),
225225
("{:>padding$}", expect![[r"{:>padding\$}; $1"]]),
226226
("{}, {}, {0}", expect![[r"{}, {}, {0}; $1, $2"]]),
@@ -230,16 +230,16 @@ mod tests {
230230
("malformed}", expect![["-"]]),
231231
("{{correct", expect![["{{correct"]]),
232232
("correct}}", expect![["correct}}"]]),
233-
("{correct}}}", expect![["{}}}; correct"]]),
234-
("{correct}}}}}", expect![["{}}}}}; correct"]]),
233+
("{correct}}}", expect![["{correct}}}"]]),
234+
("{correct}}}}}", expect![["{correct}}}}}"]]),
235235
("{incorrect}}", expect![["-"]]),
236236
("placeholders {} {}", expect![["placeholders {} {}; $1, $2"]]),
237237
("mixed {} {2 + 2} {}", expect![["mixed {} {} {}; $1, 2 + 2, $2"]]),
238238
(
239239
"{SomeStruct { val_a: 0, val_b: 1 }}",
240240
expect![["{}; SomeStruct { val_a: 0, val_b: 1 }"]],
241241
),
242-
("{expr:?} is {2.32f64:.5}", expect![["{:?} is {:.5}; expr, 2.32f64"]]),
242+
("{expr:?} is {2.32f64:.5}", expect![["{expr:?} is {:.5}; 2.32f64"]]),
243243
(
244244
"{SomeStruct { val_a: 0, val_b: 1 }:?}",
245245
expect![["{:?}; SomeStruct { val_a: 0, val_b: 1 }"]],
@@ -262,8 +262,6 @@ mod tests {
262262
.unwrap()
263263
.1,
264264
vec![
265-
Arg::Ident("_ident".to_owned()),
266-
Arg::Ident("r#raw_ident".to_owned()),
267265
Arg::Expr("expr.obj".to_owned()),
268266
Arg::Expr("name {thing: 42}".to_owned()),
269267
Arg::Placeholder

0 commit comments

Comments
 (0)