diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_ide/src/inlay_hints.rs | 138 |
1 files changed, 35 insertions, 103 deletions
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index 2ae97e65f..ddf67af4a 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs | |||
@@ -50,51 +50,53 @@ fn get_inlay_hints( | |||
50 | let analyzer = Lazy::new(move || sb.analyze(hir::InFile::new(file_id.into(), node), None)); | 50 | let analyzer = Lazy::new(move || sb.analyze(hir::InFile::new(file_id.into(), node), None)); |
51 | match_ast! { | 51 | match_ast! { |
52 | match node { | 52 | match node { |
53 | ast::LetStmt(it) => { | ||
54 | if it.ascribed_type().is_some() { | ||
55 | return None; | ||
56 | } | ||
57 | let pat = it.pat()?; | ||
58 | get_pat_type_hints(acc, db, &analyzer, pat, false, max_inlay_hint_length); | ||
59 | }, | ||
60 | ast::LambdaExpr(it) => { | ||
61 | it.param_list().map(|param_list| { | ||
62 | param_list | ||
63 | .params() | ||
64 | .filter(|closure_param| closure_param.ascribed_type().is_none()) | ||
65 | .filter_map(|closure_param| closure_param.pat()) | ||
66 | .for_each(|root_pat| get_pat_type_hints(acc, db, &analyzer, root_pat, false, max_inlay_hint_length)) | ||
67 | }); | ||
68 | }, | ||
69 | ast::ForExpr(it) => { | ||
70 | let pat = it.pat()?; | ||
71 | get_pat_type_hints(acc, db, &analyzer, pat, false, max_inlay_hint_length); | ||
72 | }, | ||
73 | ast::IfExpr(it) => { | ||
74 | let pat = it.condition()?.pat()?; | ||
75 | get_pat_type_hints(acc, db, &analyzer, pat, true, max_inlay_hint_length); | ||
76 | }, | ||
77 | ast::WhileExpr(it) => { | ||
78 | let pat = it.condition()?.pat()?; | ||
79 | get_pat_type_hints(acc, db, &analyzer, pat, true, max_inlay_hint_length); | ||
80 | }, | ||
81 | ast::MatchArmList(it) => { | ||
82 | it.arms() | ||
83 | .filter_map(|match_arm| match_arm.pat()) | ||
84 | .for_each(|root_pat| get_pat_type_hints(acc, db, &analyzer, root_pat, true, max_inlay_hint_length)); | ||
85 | }, | ||
86 | ast::CallExpr(it) => { | 53 | ast::CallExpr(it) => { |
87 | get_param_name_hints(acc, db, &analyzer, ast::Expr::from(it)); | 54 | get_param_name_hints(acc, db, &analyzer, ast::Expr::from(it)); |
88 | }, | 55 | }, |
89 | ast::MethodCallExpr(it) => { | 56 | ast::MethodCallExpr(it) => { |
90 | get_param_name_hints(acc, db, &analyzer, ast::Expr::from(it)); | 57 | get_param_name_hints(acc, db, &analyzer, ast::Expr::from(it)); |
91 | }, | 58 | }, |
59 | ast::BindPat(it) => { | ||
60 | if should_not_display_type_hint(&it) { | ||
61 | return None; | ||
62 | } | ||
63 | let pat = ast::Pat::from(it); | ||
64 | let ty = analyzer.type_of_pat(db, &pat)?; | ||
65 | if ty.is_unknown() { | ||
66 | return None; | ||
67 | } | ||
68 | |||
69 | acc.push( | ||
70 | InlayHint { | ||
71 | range: pat.syntax().text_range(), | ||
72 | kind: InlayKind::TypeHint, | ||
73 | label: ty.display_truncated(db, max_inlay_hint_length).to_string().into(), | ||
74 | } | ||
75 | ); | ||
76 | }, | ||
92 | _ => (), | 77 | _ => (), |
93 | } | 78 | } |
94 | }; | 79 | }; |
95 | Some(()) | 80 | Some(()) |
96 | } | 81 | } |
97 | 82 | ||
83 | fn should_not_display_type_hint(bind_pat: &ast::BindPat) -> bool { | ||
84 | for node in bind_pat.syntax().ancestors() { | ||
85 | match_ast! { | ||
86 | match node { | ||
87 | ast::LetStmt(it) => { | ||
88 | return it.ascribed_type().is_some() | ||
89 | }, | ||
90 | ast::Param(it) => { | ||
91 | return it.ascribed_type().is_some() | ||
92 | }, | ||
93 | _ => (), | ||
94 | } | ||
95 | } | ||
96 | } | ||
97 | false | ||
98 | } | ||
99 | |||
98 | fn get_param_name_hints( | 100 | fn get_param_name_hints( |
99 | acc: &mut Vec<InlayHint>, | 101 | acc: &mut Vec<InlayHint>, |
100 | db: &RootDatabase, | 102 | db: &RootDatabase, |
@@ -160,76 +162,6 @@ fn get_fn_signature( | |||
160 | } | 162 | } |
161 | } | 163 | } |
162 | 164 | ||
163 | fn get_pat_type_hints( | ||
164 | acc: &mut Vec<InlayHint>, | ||
165 | db: &RootDatabase, | ||
166 | analyzer: &SourceAnalyzer, | ||
167 | root_pat: ast::Pat, | ||
168 | skip_root_pat_hint: bool, | ||
169 | max_inlay_hint_length: Option<usize>, | ||
170 | ) { | ||
171 | let original_pat = &root_pat.clone(); | ||
172 | |||
173 | let hints = get_leaf_pats(root_pat) | ||
174 | .into_iter() | ||
175 | .filter(|pat| !skip_root_pat_hint || pat != original_pat) | ||
176 | .filter_map(|pat| { | ||
177 | let ty = analyzer.type_of_pat(db, &pat)?; | ||
178 | if ty.is_unknown() { | ||
179 | return None; | ||
180 | } | ||
181 | Some((pat.syntax().text_range(), ty)) | ||
182 | }) | ||
183 | .map(|(range, pat_type)| InlayHint { | ||
184 | range, | ||
185 | kind: InlayKind::TypeHint, | ||
186 | label: pat_type.display_truncated(db, max_inlay_hint_length).to_string().into(), | ||
187 | }); | ||
188 | |||
189 | acc.extend(hints); | ||
190 | } | ||
191 | |||
192 | fn get_leaf_pats(root_pat: ast::Pat) -> Vec<ast::Pat> { | ||
193 | let mut pats_to_process = std::collections::VecDeque::<ast::Pat>::new(); | ||
194 | pats_to_process.push_back(root_pat); | ||
195 | |||
196 | let mut leaf_pats = Vec::new(); | ||
197 | |||
198 | while let Some(maybe_leaf_pat) = pats_to_process.pop_front() { | ||
199 | match &maybe_leaf_pat { | ||
200 | ast::Pat::BindPat(bind_pat) => match bind_pat.pat() { | ||
201 | Some(pat) => pats_to_process.push_back(pat), | ||
202 | _ => leaf_pats.push(maybe_leaf_pat), | ||
203 | }, | ||
204 | ast::Pat::OrPat(ref_pat) => pats_to_process.extend(ref_pat.pats()), | ||
205 | ast::Pat::TuplePat(tuple_pat) => pats_to_process.extend(tuple_pat.args()), | ||
206 | ast::Pat::RecordPat(record_pat) => { | ||
207 | if let Some(pat_list) = record_pat.record_field_pat_list() { | ||
208 | pats_to_process.extend( | ||
209 | pat_list | ||
210 | .record_field_pats() | ||
211 | .filter_map(|record_field_pat| { | ||
212 | record_field_pat | ||
213 | .pat() | ||
214 | .filter(|pat| pat.syntax().kind() != SyntaxKind::BIND_PAT) | ||
215 | }) | ||
216 | .chain(pat_list.bind_pats().map(|bind_pat| { | ||
217 | bind_pat.pat().unwrap_or_else(|| ast::Pat::from(bind_pat)) | ||
218 | })), | ||
219 | ); | ||
220 | } | ||
221 | } | ||
222 | ast::Pat::TupleStructPat(tuple_struct_pat) => { | ||
223 | pats_to_process.extend(tuple_struct_pat.args()) | ||
224 | } | ||
225 | ast::Pat::ParenPat(inner_pat) => pats_to_process.extend(inner_pat.pat()), | ||
226 | ast::Pat::RefPat(ref_pat) => pats_to_process.extend(ref_pat.pat()), | ||
227 | _ => (), | ||
228 | } | ||
229 | } | ||
230 | leaf_pats | ||
231 | } | ||
232 | |||
233 | #[cfg(test)] | 165 | #[cfg(test)] |
234 | mod tests { | 166 | mod tests { |
235 | use insta::assert_debug_snapshot; | 167 | use insta::assert_debug_snapshot; |