]> git.lizzy.rs Git - rust.git/blob - crates/ide/src/completion.rs
51dbac078f5ed209e3da7a2b06f46bf9d2388e61
[rust.git] / crates / ide / src / completion.rs
1 mod completion_config;
2 mod completion_item;
3 mod completion_context;
4 mod presentation;
5 mod patterns;
6 #[cfg(test)]
7 mod test_utils;
8
9 mod complete_attribute;
10 mod complete_dot;
11 mod complete_record;
12 mod complete_pattern;
13 mod complete_fn_param;
14 mod complete_keyword;
15 mod complete_snippet;
16 mod complete_qualified_path;
17 mod complete_unqualified_path;
18 mod complete_postfix;
19 mod complete_macro_in_item_position;
20 mod complete_trait_impl;
21 mod unstable_feature_descriptor;
22
23 use ide_db::RootDatabase;
24
25 use crate::{
26     completion::{
27         completion_context::CompletionContext,
28         completion_item::{CompletionKind, Completions},
29     },
30     FilePosition,
31 };
32
33 //FIXME: cyclic imports caused by xtask generation, this should be better
34 use crate::completion::{
35     complete_attribute::LintCompletion, unstable_feature_descriptor::UNSTABLE_FEATURE_DESCRIPTOR,
36 };
37
38 pub use crate::completion::{
39     completion_config::CompletionConfig,
40     completion_item::{CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat},
41 };
42
43 //FIXME: split the following feature into fine-grained features.
44
45 // Feature: Magic Completions
46 //
47 // In addition to usual reference completion, rust-analyzer provides some ✨magic✨
48 // completions as well:
49 //
50 // Keywords like `if`, `else` `while`, `loop` are completed with braces, and cursor
51 // is placed at the appropriate position. Even though `if` is easy to type, you
52 // still want to complete it, to get ` { }` for free! `return` is inserted with a
53 // space or `;` depending on the return type of the function.
54 //
55 // When completing a function call, `()` are automatically inserted. If a function
56 // takes arguments, the cursor is positioned inside the parenthesis.
57 //
58 // There are postfix completions, which can be triggered by typing something like
59 // `foo().if`. The word after `.` determines postfix completion. Possible variants are:
60 //
61 // - `expr.if` -> `if expr {}` or `if let ... {}` for `Option` or `Result`
62 // - `expr.match` -> `match expr {}`
63 // - `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result`
64 // - `expr.ref` -> `&expr`
65 // - `expr.refm` -> `&mut expr`
66 // - `expr.not` -> `!expr`
67 // - `expr.dbg` -> `dbg!(expr)`
68 //
69 // There also snippet completions:
70 //
71 // .Expressions
72 // - `pd` -> `eprintln!(" = {:?}", );`
73 // - `ppd` -> `eprintln!(" = {:#?}", );`
74 //
75 // .Items
76 // - `tfn` -> `#[test] fn feature(){}`
77 // - `tmod` ->
78 // ```rust
79 // #[cfg(test)]
80 // mod tests {
81 //     use super::*;
82 //
83 //     #[test]
84 //     fn test_name() {}
85 // }
86 // ```
87
88 /// Main entry point for completion. We run completion as a two-phase process.
89 ///
90 /// First, we look at the position and collect a so-called `CompletionContext.
91 /// This is a somewhat messy process, because, during completion, syntax tree is
92 /// incomplete and can look really weird.
93 ///
94 /// Once the context is collected, we run a series of completion routines which
95 /// look at the context and produce completion items. One subtlety about this
96 /// phase is that completion engine should not filter by the substring which is
97 /// already present, it should give all possible variants for the identifier at
98 /// the caret. In other words, for
99 ///
100 /// ```no-run
101 /// fn f() {
102 ///     let foo = 92;
103 ///     let _ = bar<|>
104 /// }
105 /// ```
106 ///
107 /// `foo` *should* be present among the completion variants. Filtering by
108 /// identifier prefix/fuzzy match should be done higher in the stack, together
109 /// with ordering of completions (currently this is done by the client).
110 pub(crate) fn completions(
111     db: &RootDatabase,
112     config: &CompletionConfig,
113     position: FilePosition,
114 ) -> Option<Completions> {
115     let ctx = CompletionContext::new(db, position, config)?;
116
117     let mut acc = Completions::default();
118     complete_attribute::complete_attribute(&mut acc, &ctx);
119     complete_fn_param::complete_fn_param(&mut acc, &ctx);
120     complete_keyword::complete_expr_keyword(&mut acc, &ctx);
121     complete_keyword::complete_use_tree_keyword(&mut acc, &ctx);
122     complete_snippet::complete_expr_snippet(&mut acc, &ctx);
123     complete_snippet::complete_item_snippet(&mut acc, &ctx);
124     complete_qualified_path::complete_qualified_path(&mut acc, &ctx);
125     complete_unqualified_path::complete_unqualified_path(&mut acc, &ctx);
126     complete_dot::complete_dot(&mut acc, &ctx);
127     complete_record::complete_record(&mut acc, &ctx);
128     complete_pattern::complete_pattern(&mut acc, &ctx);
129     complete_postfix::complete_postfix(&mut acc, &ctx);
130     complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx);
131     complete_trait_impl::complete_trait_impl(&mut acc, &ctx);
132
133     Some(acc)
134 }
135
136 #[cfg(test)]
137 mod tests {
138     use crate::completion::completion_config::CompletionConfig;
139     use crate::mock_analysis::analysis_and_position;
140
141     struct DetailAndDocumentation<'a> {
142         detail: &'a str,
143         documentation: &'a str,
144     }
145
146     fn check_detail_and_documentation(ra_fixture: &str, expected: DetailAndDocumentation) {
147         let (analysis, position) = analysis_and_position(ra_fixture);
148         let config = CompletionConfig::default();
149         let completions = analysis.completions(&config, position).unwrap().unwrap();
150         for item in completions {
151             if item.detail() == Some(expected.detail) {
152                 let opt = item.documentation();
153                 let doc = opt.as_ref().map(|it| it.as_str());
154                 assert_eq!(doc, Some(expected.documentation));
155                 return;
156             }
157         }
158         panic!("completion detail not found: {}", expected.detail)
159     }
160
161     #[test]
162     fn test_completion_detail_from_macro_generated_struct_fn_doc_attr() {
163         check_detail_and_documentation(
164             r#"
165             //- /lib.rs
166             macro_rules! bar {
167                 () => {
168                     struct Bar;
169                     impl Bar {
170                         #[doc = "Do the foo"]
171                         fn foo(&self) {}
172                     }
173                 }
174             }
175
176             bar!();
177
178             fn foo() {
179                 let bar = Bar;
180                 bar.fo<|>;
181             }
182             "#,
183             DetailAndDocumentation { detail: "fn foo(&self)", documentation: "Do the foo" },
184         );
185     }
186
187     #[test]
188     fn test_completion_detail_from_macro_generated_struct_fn_doc_comment() {
189         check_detail_and_documentation(
190             r#"
191             //- /lib.rs
192             macro_rules! bar {
193                 () => {
194                     struct Bar;
195                     impl Bar {
196                         /// Do the foo
197                         fn foo(&self) {}
198                     }
199                 }
200             }
201
202             bar!();
203
204             fn foo() {
205                 let bar = Bar;
206                 bar.fo<|>;
207             }
208             "#,
209             DetailAndDocumentation { detail: "fn foo(&self)", documentation: " Do the foo" },
210         );
211     }
212 }