diff options
Diffstat (limited to 'crates/ra_ide_api')
-rw-r--r-- | crates/ra_ide_api/src/inlay_hints.rs | 182 |
1 files changed, 121 insertions, 61 deletions
diff --git a/crates/ra_ide_api/src/inlay_hints.rs b/crates/ra_ide_api/src/inlay_hints.rs index 174662beb..95289f016 100644 --- a/crates/ra_ide_api/src/inlay_hints.rs +++ b/crates/ra_ide_api/src/inlay_hints.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | use crate::{db::RootDatabase, FileId}; | 1 | use crate::{db::RootDatabase, FileId}; |
2 | use hir::{HirDisplay, Ty}; | 2 | use hir::{HirDisplay, SourceAnalyzer, Ty}; |
3 | use ra_syntax::ast::Pat; | 3 | use ra_syntax::ast::Pat; |
4 | use ra_syntax::{ | 4 | use ra_syntax::{ |
5 | algo::visit::{visitor, Visitor}, | 5 | algo::visit::{visitor, Visitor}, |
@@ -7,10 +7,11 @@ use ra_syntax::{ | |||
7 | AstNode, SmolStr, SourceFile, SyntaxNode, TextRange, | 7 | AstNode, SmolStr, SourceFile, SyntaxNode, TextRange, |
8 | }; | 8 | }; |
9 | 9 | ||
10 | #[derive(Debug, PartialEq, Eq)] | 10 | #[derive(Debug, PartialEq, Eq, Clone)] |
11 | pub enum InlayKind { | 11 | pub enum InlayKind { |
12 | LetBindingType, | 12 | LetBindingType, |
13 | ClosureParameterType, | 13 | ClosureParameterType, |
14 | ForExpressionBindingType, | ||
14 | } | 15 | } |
15 | 16 | ||
16 | #[derive(Debug)] | 17 | #[derive(Debug)] |
@@ -35,67 +36,89 @@ fn get_inlay_hints( | |||
35 | ) -> Option<Vec<InlayHint>> { | 36 | ) -> Option<Vec<InlayHint>> { |
36 | visitor() | 37 | visitor() |
37 | .visit(|let_statement: ast::LetStmt| { | 38 | .visit(|let_statement: ast::LetStmt| { |
38 | let let_syntax = let_statement.syntax(); | ||
39 | |||
40 | if let_statement.ascribed_type().is_some() { | 39 | if let_statement.ascribed_type().is_some() { |
41 | return None; | 40 | return None; |
42 | } | 41 | } |
43 | 42 | let analyzer = SourceAnalyzer::new(db, file_id, let_statement.syntax(), None); | |
44 | let let_pat = let_statement.pat()?; | 43 | Some(get_pat_hints(db, &analyzer, let_statement.pat()?, InlayKind::LetBindingType)) |
45 | let inlay_type_string = get_node_displayable_type(db, file_id, let_syntax, &let_pat)? | ||
46 | .display(db) | ||
47 | .to_string() | ||
48 | .into(); | ||
49 | |||
50 | let pat_range = match let_pat.kind() { | ||
51 | PatKind::BindPat(bind_pat) => bind_pat.syntax().text_range(), | ||
52 | PatKind::TuplePat(tuple_pat) => tuple_pat.syntax().text_range(), | ||
53 | _ => return None, | ||
54 | }; | ||
55 | |||
56 | Some(vec![InlayHint { | ||
57 | range: pat_range, | ||
58 | kind: InlayKind::LetBindingType, | ||
59 | label: inlay_type_string, | ||
60 | }]) | ||
61 | }) | 44 | }) |
62 | .visit(|closure_parameter: ast::LambdaExpr| match closure_parameter.param_list() { | 45 | .visit(|closure_parameter: ast::LambdaExpr| { |
63 | Some(param_list) => Some( | 46 | let analyzer = SourceAnalyzer::new(db, file_id, closure_parameter.syntax(), None); |
47 | closure_parameter.param_list().map(|param_list| { | ||
64 | param_list | 48 | param_list |
65 | .params() | 49 | .params() |
66 | .filter(|closure_param| closure_param.ascribed_type().is_none()) | 50 | .filter(|closure_param| closure_param.ascribed_type().is_none()) |
67 | .filter_map(|closure_param| { | 51 | .filter_map(|closure_param| closure_param.pat()) |
68 | let closure_param_syntax = closure_param.syntax(); | 52 | .map(|root_pat| { |
69 | let inlay_type_string = get_node_displayable_type( | 53 | get_pat_hints(db, &analyzer, root_pat, InlayKind::ClosureParameterType) |
70 | db, | ||
71 | file_id, | ||
72 | closure_param_syntax, | ||
73 | &closure_param.pat()?, | ||
74 | )? | ||
75 | .display(db) | ||
76 | .to_string() | ||
77 | .into(); | ||
78 | |||
79 | Some(InlayHint { | ||
80 | range: closure_param_syntax.text_range(), | ||
81 | kind: InlayKind::ClosureParameterType, | ||
82 | label: inlay_type_string, | ||
83 | }) | ||
84 | }) | 54 | }) |
85 | .collect(), | 55 | .flatten() |
86 | ), | 56 | .collect() |
87 | None => None, | 57 | }) |
58 | }) | ||
59 | .visit(|for_expression: ast::ForExpr| { | ||
60 | let analyzer = SourceAnalyzer::new(db, file_id, for_expression.syntax(), None); | ||
61 | Some(get_pat_hints( | ||
62 | db, | ||
63 | &analyzer, | ||
64 | for_expression.pat()?, | ||
65 | InlayKind::ForExpressionBindingType, | ||
66 | )) | ||
88 | }) | 67 | }) |
89 | .accept(&node)? | 68 | .accept(&node)? |
90 | } | 69 | } |
91 | 70 | ||
71 | fn get_pat_hints( | ||
72 | db: &RootDatabase, | ||
73 | analyzer: &SourceAnalyzer, | ||
74 | root_pat: Pat, | ||
75 | kind: InlayKind, | ||
76 | ) -> Vec<InlayHint> { | ||
77 | get_leaf_pats(root_pat) | ||
78 | .into_iter() | ||
79 | .filter_map(|pat| { | ||
80 | get_node_displayable_type(db, &analyzer, &pat) | ||
81 | .map(|pat_type| (pat.syntax().text_range(), pat_type)) | ||
82 | }) | ||
83 | .map(|(range, pat_type)| InlayHint { | ||
84 | range, | ||
85 | kind: kind.clone(), | ||
86 | label: pat_type.display(db).to_string().into(), | ||
87 | }) | ||
88 | .collect() | ||
89 | } | ||
90 | |||
91 | fn get_leaf_pats(root_pat: Pat) -> Vec<Pat> { | ||
92 | let mut pats_to_process = std::collections::VecDeque::<Pat>::new(); | ||
93 | pats_to_process.push_back(root_pat); | ||
94 | |||
95 | let mut leaf_pats = Vec::new(); | ||
96 | |||
97 | while let Some(maybe_leaf_pat) = pats_to_process.pop_front() { | ||
98 | match maybe_leaf_pat.kind() { | ||
99 | PatKind::BindPat(bind_pat) => { | ||
100 | if let Some(pat) = bind_pat.pat() { | ||
101 | pats_to_process.push_back(pat); | ||
102 | } else { | ||
103 | leaf_pats.push(maybe_leaf_pat); | ||
104 | } | ||
105 | } | ||
106 | PatKind::TuplePat(tuple_pat) => { | ||
107 | for arg_pat in tuple_pat.args() { | ||
108 | pats_to_process.push_back(arg_pat); | ||
109 | } | ||
110 | } | ||
111 | _ => (), | ||
112 | } | ||
113 | } | ||
114 | leaf_pats | ||
115 | } | ||
116 | |||
92 | fn get_node_displayable_type( | 117 | fn get_node_displayable_type( |
93 | db: &RootDatabase, | 118 | db: &RootDatabase, |
94 | file_id: FileId, | 119 | analyzer: &SourceAnalyzer, |
95 | node_syntax: &SyntaxNode, | ||
96 | node_pat: &Pat, | 120 | node_pat: &Pat, |
97 | ) -> Option<Ty> { | 121 | ) -> Option<Ty> { |
98 | let analyzer = hir::SourceAnalyzer::new(db, file_id, node_syntax, None); | ||
99 | analyzer.type_of_pat(db, node_pat).and_then(|resolved_type| { | 122 | analyzer.type_of_pat(db, node_pat).and_then(|resolved_type| { |
100 | if let Ty::Apply(_) = resolved_type { | 123 | if let Ty::Apply(_) = resolved_type { |
101 | Some(resolved_type) | 124 | Some(resolved_type) |
@@ -120,25 +143,32 @@ fn main() { | |||
120 | struct InnerStruct {} | 143 | struct InnerStruct {} |
121 | 144 | ||
122 | let test = 54; | 145 | let test = 54; |
146 | let test: i32 = 33; | ||
147 | let mut test = 33; | ||
148 | let _ = 22; | ||
149 | let test = "test"; | ||
123 | let test = InnerStruct {}; | 150 | let test = InnerStruct {}; |
124 | let test = OuterStruct {}; | 151 | let test = OuterStruct {}; |
152 | |||
125 | let test = vec![222]; | 153 | let test = vec![222]; |
154 | let test: Vec<_> = (0..3).collect(); | ||
155 | |||
126 | let mut test = Vec::new(); | 156 | let mut test = Vec::new(); |
127 | test.push(333); | 157 | test.push(333); |
158 | |||
128 | let test = test.into_iter().map(|i| i * i).collect::<Vec<_>>(); | 159 | let test = test.into_iter().map(|i| i * i).collect::<Vec<_>>(); |
129 | let mut test = 33; | 160 | let test = test.into_iter().map(|i| i * i).collect::<Vec<u128>>(); |
130 | let _ = 22; | ||
131 | let test: Vec<_> = (0..3).collect(); | ||
132 | 161 | ||
133 | let _ = (0..23).map(|i: u32| { | 162 | let _ = (0..23).map(|i: u32| { |
134 | let i_squared = i * i; | 163 | let i_squared = i * i; |
135 | i_squared | 164 | i_squared |
136 | }); | 165 | }); |
137 | 166 | ||
138 | let test: i32 = 33; | ||
139 | |||
140 | let (x, c) = (42, 'a'); | ||
141 | let test = (42, 'a'); | 167 | let test = (42, 'a'); |
168 | let (a, (b, c, (d, e), f)) = (2, (3, 4, (6.6, 7.7), 5)); | ||
169 | |||
170 | let test = Some((2, 3)); | ||
171 | for (i, j) in test {} | ||
142 | } | 172 | } |
143 | "#, | 173 | "#, |
144 | ); | 174 | ); |
@@ -150,29 +180,59 @@ fn main() { | |||
150 | label: "i32", | 180 | label: "i32", |
151 | }, | 181 | }, |
152 | InlayHint { | 182 | InlayHint { |
153 | range: [121; 125), | 183 | range: [114; 122), |
154 | kind: LetBindingType, | 184 | kind: LetBindingType, |
155 | label: "OuterStruct", | 185 | label: "i32", |
156 | }, | 186 | }, |
157 | InlayHint { | 187 | InlayHint { |
158 | range: [297; 305), | 188 | range: [153; 157), |
159 | kind: LetBindingType, | 189 | kind: LetBindingType, |
160 | label: "i32", | 190 | label: "&str", |
161 | }, | 191 | }, |
162 | InlayHint { | 192 | InlayHint { |
163 | range: [417; 426), | 193 | range: [207; 211), |
194 | kind: LetBindingType, | ||
195 | label: "OuterStruct", | ||
196 | }, | ||
197 | InlayHint { | ||
198 | range: [538; 547), | ||
164 | kind: LetBindingType, | 199 | kind: LetBindingType, |
165 | label: "u32", | 200 | label: "u32", |
166 | }, | 201 | }, |
167 | InlayHint { | 202 | InlayHint { |
168 | range: [496; 502), | 203 | range: [592; 596), |
169 | kind: LetBindingType, | 204 | kind: LetBindingType, |
170 | label: "(i32, char)", | 205 | label: "(i32, char)", |
171 | }, | 206 | }, |
172 | InlayHint { | 207 | InlayHint { |
173 | range: [524; 528), | 208 | range: [619; 620), |
174 | kind: LetBindingType, | 209 | kind: LetBindingType, |
175 | label: "(i32, char)", | 210 | label: "i32", |
211 | }, | ||
212 | InlayHint { | ||
213 | range: [623; 624), | ||
214 | kind: LetBindingType, | ||
215 | label: "i32", | ||
216 | }, | ||
217 | InlayHint { | ||
218 | range: [626; 627), | ||
219 | kind: LetBindingType, | ||
220 | label: "i32", | ||
221 | }, | ||
222 | InlayHint { | ||
223 | range: [637; 638), | ||
224 | kind: LetBindingType, | ||
225 | label: "i32", | ||
226 | }, | ||
227 | InlayHint { | ||
228 | range: [630; 631), | ||
229 | kind: LetBindingType, | ||
230 | label: "f64", | ||
231 | }, | ||
232 | InlayHint { | ||
233 | range: [633; 634), | ||
234 | kind: LetBindingType, | ||
235 | label: "f64", | ||
176 | }, | 236 | }, |
177 | ]"# | 237 | ]"# |
178 | ); | 238 | ); |