aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide_api/src')
-rw-r--r--crates/ra_ide_api/src/inlay_hints.rs182
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 @@
1use crate::{db::RootDatabase, FileId}; 1use crate::{db::RootDatabase, FileId};
2use hir::{HirDisplay, Ty}; 2use hir::{HirDisplay, SourceAnalyzer, Ty};
3use ra_syntax::ast::Pat; 3use ra_syntax::ast::Pat;
4use ra_syntax::{ 4use 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)]
11pub enum InlayKind { 11pub 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
71fn 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
91fn 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
92fn get_node_displayable_type( 117fn 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 );