]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs
Merge commit 'b52fb5234cd7c11ecfae51897a6f7fa52e8777fc' into clippyup
[rust.git] / src / tools / rust-analyzer / crates / ide-completion / src / completions / format_string.rs
1 //! Completes identifiers in format string literals.
2
3 use ide_db::syntax_helpers::format_string::is_format_string;
4 use itertools::Itertools;
5 use syntax::{ast, AstToken, TextRange, TextSize};
6
7 use crate::{context::CompletionContext, CompletionItem, CompletionItemKind, Completions};
8
9 /// Complete identifiers in format strings.
10 pub(crate) fn format_string(
11     acc: &mut Completions,
12     ctx: &CompletionContext<'_>,
13     original: &ast::String,
14     expanded: &ast::String,
15 ) {
16     if !is_format_string(&expanded) {
17         return;
18     }
19     let cursor = ctx.position.offset;
20     let lit_start = ctx.original_token.text_range().start();
21     let cursor_in_lit = cursor - lit_start;
22
23     let prefix = &original.text()[..cursor_in_lit.into()];
24     let braces = prefix.char_indices().rev().skip_while(|&(_, c)| c.is_alphanumeric()).next_tuple();
25     let brace_offset = match braces {
26         // escaped brace
27         Some(((_, '{'), (_, '{'))) => return,
28         Some(((idx, '{'), _)) => lit_start + TextSize::from(idx as u32 + 1),
29         _ => return,
30     };
31
32     let source_range = TextRange::new(brace_offset, cursor);
33     ctx.locals.iter().for_each(|(name, _)| {
34         CompletionItem::new(CompletionItemKind::Binding, source_range, name.to_smol_str())
35             .add_to(acc);
36     })
37 }
38
39 #[cfg(test)]
40 mod tests {
41     use expect_test::{expect, Expect};
42
43     use crate::tests::{check_edit, completion_list_no_kw};
44
45     fn check(ra_fixture: &str, expect: Expect) {
46         let actual = completion_list_no_kw(ra_fixture);
47         expect.assert_eq(&actual);
48     }
49
50     #[test]
51     fn works_when_wrapped() {
52         check(
53             r#"
54 macro_rules! format_args {
55     ($lit:literal $(tt:tt)*) => { 0 },
56 }
57 macro_rules! print {
58     ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
59 }
60 fn main() {
61     let foobar = 1;
62     print!("f$0");
63 }
64 "#,
65             expect![[]],
66         );
67     }
68
69     #[test]
70     fn no_completion_without_brace() {
71         check(
72             r#"
73 macro_rules! format_args {
74     ($lit:literal $(tt:tt)*) => { 0 },
75 }
76 fn main() {
77     let foobar = 1;
78     format_args!("f$0");
79 }
80 "#,
81             expect![[]],
82         );
83     }
84
85     #[test]
86     fn completes_locals() {
87         check_edit(
88             "foobar",
89             r#"
90 macro_rules! format_args {
91     ($lit:literal $(tt:tt)*) => { 0 },
92 }
93 fn main() {
94     let foobar = 1;
95     format_args!("{f$0");
96 }
97 "#,
98             r#"
99 macro_rules! format_args {
100     ($lit:literal $(tt:tt)*) => { 0 },
101 }
102 fn main() {
103     let foobar = 1;
104     format_args!("{foobar");
105 }
106 "#,
107         );
108         check_edit(
109             "foobar",
110             r#"
111 macro_rules! format_args {
112     ($lit:literal $(tt:tt)*) => { 0 },
113 }
114 fn main() {
115     let foobar = 1;
116     format_args!("{$0");
117 }
118 "#,
119             r#"
120 macro_rules! format_args {
121     ($lit:literal $(tt:tt)*) => { 0 },
122 }
123 fn main() {
124     let foobar = 1;
125     format_args!("{foobar");
126 }
127 "#,
128         );
129     }
130 }