Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-assists/src/handlers/move_format_string_arg.rs')
| -rw-r--r-- | crates/ide-assists/src/handlers/move_format_string_arg.rs | 238 |
1 files changed, 143 insertions, 95 deletions
diff --git a/crates/ide-assists/src/handlers/move_format_string_arg.rs b/crates/ide-assists/src/handlers/move_format_string_arg.rs index 696fd50b5c..54b5bee9b7 100644 --- a/crates/ide-assists/src/handlers/move_format_string_arg.rs +++ b/crates/ide-assists/src/handlers/move_format_string_arg.rs @@ -1,100 +1,133 @@ -use ide_db::{syntax_helpers::{format_string::is_format_string, format_string_exprs::{parse_format_exprs, Arg}}, assists::{AssistId, AssistKind}}; +use crate::{AssistContext, Assists}; +use ide_db::{ + assists::{AssistId, AssistKind}, + syntax_helpers::{ + format_string::is_format_string, + format_string_exprs::{parse_format_exprs, Arg}, + }, +}; use itertools::Itertools; -use syntax::{ast, AstToken, AstNode, NodeOrToken, SyntaxKind::COMMA, TextRange}; +use syntax::{ast, AstNode, AstToken, NodeOrToken, SyntaxKind::COMMA, TextRange}; // Assist: move_format_string_arg // // Move an expression out of a format string. // // ``` +// macro_rules! format_args { +// ($lit:literal $(tt:tt)*) => { 0 }, +// } +// macro_rules! print { +// ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*))); +// } +// // fn main() { -// println!("{x + 1}$0"); +// print!("{x + 1}$0"); // } // ``` // -> // ``` +// macro_rules! format_args { +// ($lit:literal $(tt:tt)*) => { 0 }, +// } +// macro_rules! print { +// ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*))); +// } +// // fn main() { -// println!("{a}", a$0 = x + 1); +// print!("{}"$0, x + 1); // } // ``` -use crate::{AssistContext, /* AssistId, AssistKind, */ Assists}; - -pub(crate) fn move_format_string_arg (acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let t = ctx.find_token_at_offset::<ast::String>()?; - let tt = t.syntax().parent_ancestors().find_map(ast::TokenTree::cast)?; - - let expanded_t = ast::String::cast(ctx.sema.descend_into_macros_with_kind_preference(t.syntax().clone()))?; +pub(crate) fn move_format_string_arg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let fmt_string = ctx.find_token_at_offset::<ast::String>()?; + let tt = fmt_string.syntax().parent_ancestors().find_map(ast::TokenTree::cast)?; + let expanded_t = ast::String::cast( + ctx.sema.descend_into_macros_with_kind_preference(fmt_string.syntax().clone()), + )?; if !is_format_string(&expanded_t) { return None; } - let target = tt.syntax().text_range(); - let extracted_args = parse_format_exprs(&t).ok()?; - let str_range = t.syntax().text_range(); - - let tokens = - tt.token_trees_and_tokens() - .filter_map(NodeOrToken::into_token) - .collect_vec(); - - acc.add(AssistId("move_format_string_arg", AssistKind::QuickFix), "Extract format args", target, |edit| { - let mut existing_args: Vec<String> = vec![]; - let mut current_arg = String::new(); - - if let [_opening_bracket, format_string, _args_start_comma, tokens @ .., end_bracket] = tokens.as_slice() { - for t in tokens { - if t.kind() == COMMA { - existing_args.push(current_arg.trim().into()); - current_arg.clear(); - } else { - current_arg.push_str(t.text()); + let (new_fmt, extracted_args) = parse_format_exprs(fmt_string.text()).ok()?; + + acc.add( + AssistId("move_format_string_arg", AssistKind::QuickFix), + "Extract format args", + tt.syntax().text_range(), + |edit| { + let fmt_range = fmt_string.syntax().text_range(); + + // Replace old format string with new format string whose arguments have been extracted + edit.replace(fmt_range, new_fmt); + + // Insert cursor at end of format string + edit.insert(fmt_range.end(), "$0"); + + // Extract existing arguments in macro + let tokens = + tt.token_trees_and_tokens().filter_map(NodeOrToken::into_token).collect_vec(); + + let mut existing_args: Vec<String> = vec![]; + + let mut current_arg = String::new(); + if let [_opening_bracket, format_string, _args_start_comma, tokens @ .., end_bracket] = + tokens.as_slice() + { + for t in tokens { + if t.kind() == COMMA { + existing_args.push(current_arg.trim().into()); + current_arg.clear(); + } else { + current_arg.push_str(t.text()); + } } + existing_args.push(current_arg.trim().into()); + + // delete everything after the format string till end bracket + // we're going to insert the new arguments later + edit.delete(TextRange::new( + format_string.text_range().end(), + end_bracket.text_range().start(), + )); } - existing_args.push(current_arg.trim().into()); - - // delete everything after the format string to the end bracket - // we're going to insert the new arguments later - edit.delete(TextRange::new(format_string.text_range().end(), end_bracket.text_range().start())); - } - - let mut existing_args = existing_args.into_iter(); - - // insert cursor at end of format string - edit.insert(str_range.end(), "$0"); - let mut placeholder_idx = 1; - let mut args = String::new(); - - for (text, extracted_args) in extracted_args { - // remove expr from format string - edit.delete(text); - - args.push_str(", "); - - match extracted_args { - Arg::Expr(s) => { - // insert arg - args.push_str(&s); - }, - Arg::Placeholder => { - // try matching with existing argument - match existing_args.next() { - Some(ea) => { - args.push_str(&ea); - }, - None => { - // insert placeholder - args.push_str(&format!("${placeholder_idx}")); - placeholder_idx += 1; + + // Start building the new args + let mut existing_args = existing_args.into_iter(); + let mut args = String::new(); + + let mut placeholder_idx = 1; + + for extracted_args in extracted_args { + // remove expr from format string + args.push_str(", "); + + match extracted_args { + Arg::Expr(s) => { + // insert arg + args.push_str(&s); + } + Arg::Placeholder => { + // try matching with existing argument + match existing_args.next() { + Some(ea) => { + args.push_str(&ea); + } + None => { + // insert placeholder + args.push_str(&format!("${placeholder_idx}")); + placeholder_idx += 1; + } } } } } - } - edit.insert(str_range.end(), args); - }); + // Insert new args + edit.insert(fmt_range.end(), args); + }, + ); Some(()) } @@ -113,7 +146,7 @@ macro_rules! print { } "#; - fn add_macro_decl (s: &'static str) -> String { + fn add_macro_decl(s: &'static str) -> String { MACRO_DECL.to_string() + s } @@ -121,17 +154,20 @@ macro_rules! print { fn multiple_middle_arg() { check_assist( move_format_string_arg, - &add_macro_decl(r#" + &add_macro_decl( + r#" fn main() { print!("{} {x + 1:b} {}$0", y + 2, 2); } -"#), - - &add_macro_decl(r#" +"#, + ), + &add_macro_decl( + r#" fn main() { print!("{} {:b} {}"$0, y + 2, x + 1, 2); } -"#), +"#, + ), ); } @@ -139,16 +175,20 @@ fn main() { fn single_arg() { check_assist( move_format_string_arg, - &add_macro_decl(r#" + &add_macro_decl( + r#" fn main() { print!("{obj.value:b}$0",); } -"#), - &add_macro_decl(r#" +"#, + ), + &add_macro_decl( + r#" fn main() { print!("{:b}"$0, obj.value); } -"#), +"#, + ), ); } @@ -156,17 +196,20 @@ fn main() { fn multiple_middle_placeholders_arg() { check_assist( move_format_string_arg, - &add_macro_decl(r#" + &add_macro_decl( + r#" fn main() { print!("{} {x + 1:b} {} {}$0", y + 2, 2); } -"#), - - &add_macro_decl(r#" +"#, + ), + &add_macro_decl( + r#" fn main() { print!("{} {:b} {} {}"$0, y + 2, x + 1, 2, $1); } -"#), +"#, + ), ); } @@ -174,17 +217,20 @@ fn main() { fn multiple_trailing_args() { check_assist( move_format_string_arg, - &add_macro_decl(r#" + &add_macro_decl( + r#" fn main() { print!("{} {x + 1:b} {Struct(1, 2)}$0", 1); } -"#), - - &add_macro_decl(r#" +"#, + ), + &add_macro_decl( + r#" fn main() { print!("{} {:b} {}"$0, 1, x + 1, Struct(1, 2)); } -"#), +"#, + ), ); } @@ -192,18 +238,20 @@ fn main() { fn improper_commas() { check_assist( move_format_string_arg, - &add_macro_decl(r#" + &add_macro_decl( + r#" fn main() { print!("{} {x + 1:b} {Struct(1, 2)}$0", 1,); } -"#), - - &add_macro_decl(r#" +"#, + ), + &add_macro_decl( + r#" fn main() { print!("{} {:b} {}"$0, 1, x + 1, Struct(1, 2)); } -"#), +"#, + ), ); } - } |