aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide')
-rw-r--r--crates/ra_ide/src/inlay_hints.rs138
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
83fn 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
98fn get_param_name_hints( 100fn 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
163fn 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
192fn 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)]
234mod tests { 166mod tests {
235 use insta::assert_debug_snapshot; 167 use insta::assert_debug_snapshot;