Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-completion/src/completions/format_string.rs')
| -rw-r--r-- | crates/ide-completion/src/completions/format_string.rs | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/crates/ide-completion/src/completions/format_string.rs b/crates/ide-completion/src/completions/format_string.rs new file mode 100644 index 0000000000..f0c994f6b6 --- /dev/null +++ b/crates/ide-completion/src/completions/format_string.rs @@ -0,0 +1,128 @@ +//! Completes identifiers in format string literals. + +use ide_db::syntax_helpers::format_string::is_format_string; +use itertools::Itertools; +use syntax::{ast, AstToken, TextRange, TextSize}; + +use crate::{context::CompletionContext, CompletionItem, CompletionItemKind, Completions}; + +/// Complete identifiers in format strings. +pub(crate) fn format_string(acc: &mut Completions, ctx: &CompletionContext) { + let string = match ast::String::cast(ctx.token.clone()) + .zip(ast::String::cast(ctx.original_token.clone())) + { + Some((expanded, original)) if is_format_string(&expanded) => original, + _ => return, + }; + let cursor = ctx.position.offset; + let lit_start = ctx.original_token.text_range().start(); + let cursor_in_lit = cursor - lit_start; + + let prefix = &string.text()[..cursor_in_lit.into()]; + let braces = prefix.char_indices().rev().skip_while(|&(_, c)| c.is_alphanumeric()).next_tuple(); + let brace_offset = match braces { + // escaped brace + Some(((_, '{'), (_, '{'))) => return, + Some(((idx, '{'), _)) => lit_start + TextSize::from(idx as u32 + 1), + _ => return, + }; + + let source_range = TextRange::new(brace_offset, cursor); + ctx.locals.iter().for_each(|(name, _)| { + CompletionItem::new(CompletionItemKind::Binding, source_range, name.to_smol_str()) + .add_to(acc); + }) +} + +#[cfg(test)] +mod tests { + use expect_test::{expect, Expect}; + + use crate::tests::{check_edit, completion_list_no_kw}; + + fn check(ra_fixture: &str, expect: Expect) { + let actual = completion_list_no_kw(ra_fixture); + expect.assert_eq(&actual); + } + + #[test] + fn works_when_wrapped() { + check( + r#" +macro_rules! format_args { + ($lit:literal $(tt:tt)*) => { 0 }, +} +macro_rules! print { + ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*))); +} +fn main() { + let foobar = 1; + print!("f$0"); +} +"#, + expect![[]], + ); + } + + #[test] + fn no_completion_without_brace() { + check( + r#" +macro_rules! format_args { + ($lit:literal $(tt:tt)*) => { 0 }, +} +fn main() { + let foobar = 1; + format_args!("f$0"); +} +"#, + expect![[]], + ); + } + + #[test] + fn completes_locals() { + check_edit( + "foobar", + r#" +macro_rules! format_args { + ($lit:literal $(tt:tt)*) => { 0 }, +} +fn main() { + let foobar = 1; + format_args!("{f$0"); +} +"#, + r#" +macro_rules! format_args { + ($lit:literal $(tt:tt)*) => { 0 }, +} +fn main() { + let foobar = 1; + format_args!("{foobar"); +} +"#, + ); + check_edit( + "foobar", + r#" +macro_rules! format_args { + ($lit:literal $(tt:tt)*) => { 0 }, +} +fn main() { + let foobar = 1; + format_args!("{$0"); +} +"#, + r#" +macro_rules! format_args { + ($lit:literal $(tt:tt)*) => { 0 }, +} +fn main() { + let foobar = 1; + format_args!("{foobar"); +} +"#, + ); + } +} |