diff options
Diffstat (limited to 'crates/ra_ide_api/src')
-rw-r--r-- | crates/ra_ide_api/src/inlay_hints.rs | 470 | ||||
-rw-r--r-- | crates/ra_ide_api/src/lib.rs | 86 | ||||
-rw-r--r-- | crates/ra_ide_api/src/references.rs | 3 | ||||
-rw-r--r-- | crates/ra_ide_api/src/syntax_tree.rs | 14 | ||||
-rw-r--r-- | crates/ra_ide_api/src/typing.rs | 4 |
5 files changed, 452 insertions, 125 deletions
diff --git a/crates/ra_ide_api/src/inlay_hints.rs b/crates/ra_ide_api/src/inlay_hints.rs index 174662beb..a524e014f 100644 --- a/crates/ra_ide_api/src/inlay_hints.rs +++ b/crates/ra_ide_api/src/inlay_hints.rs | |||
@@ -1,16 +1,22 @@ | |||
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; | ||
4 | use ra_syntax::{ | 3 | use ra_syntax::{ |
5 | algo::visit::{visitor, Visitor}, | 4 | algo::visit::{visitor, Visitor}, |
6 | ast::{self, PatKind, TypeAscriptionOwner}, | 5 | ast::{ |
7 | AstNode, SmolStr, SourceFile, SyntaxNode, TextRange, | 6 | AstNode, ForExpr, IfExpr, LambdaExpr, LetStmt, MatchArmList, Pat, PatKind, SourceFile, |
7 | TypeAscriptionOwner, WhileExpr, | ||
8 | }, | ||
9 | SmolStr, SyntaxKind, SyntaxNode, TextRange, | ||
8 | }; | 10 | }; |
9 | 11 | ||
10 | #[derive(Debug, PartialEq, Eq)] | 12 | #[derive(Debug, PartialEq, Eq, Clone)] |
11 | pub enum InlayKind { | 13 | pub enum InlayKind { |
12 | LetBindingType, | 14 | LetBindingType, |
13 | ClosureParameterType, | 15 | ClosureParameterType, |
16 | ForExpressionBindingType, | ||
17 | IfExpressionType, | ||
18 | WhileLetExpressionType, | ||
19 | MatchArmType, | ||
14 | } | 20 | } |
15 | 21 | ||
16 | #[derive(Debug)] | 22 | #[derive(Debug)] |
@@ -34,68 +40,142 @@ fn get_inlay_hints( | |||
34 | node: &SyntaxNode, | 40 | node: &SyntaxNode, |
35 | ) -> Option<Vec<InlayHint>> { | 41 | ) -> Option<Vec<InlayHint>> { |
36 | visitor() | 42 | visitor() |
37 | .visit(|let_statement: ast::LetStmt| { | 43 | .visit(|let_statement: LetStmt| { |
38 | let let_syntax = let_statement.syntax(); | ||
39 | |||
40 | if let_statement.ascribed_type().is_some() { | 44 | if let_statement.ascribed_type().is_some() { |
41 | return None; | 45 | return None; |
42 | } | 46 | } |
43 | 47 | let pat = let_statement.pat()?; | |
44 | let let_pat = let_statement.pat()?; | 48 | let analyzer = SourceAnalyzer::new(db, file_id, let_statement.syntax(), None); |
45 | let inlay_type_string = get_node_displayable_type(db, file_id, let_syntax, &let_pat)? | 49 | Some(get_pat_hints(db, &analyzer, pat, InlayKind::LetBindingType, false)) |
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 | }) | 50 | }) |
62 | .visit(|closure_parameter: ast::LambdaExpr| match closure_parameter.param_list() { | 51 | .visit(|closure_parameter: LambdaExpr| { |
63 | Some(param_list) => Some( | 52 | let analyzer = SourceAnalyzer::new(db, file_id, closure_parameter.syntax(), None); |
53 | closure_parameter.param_list().map(|param_list| { | ||
64 | param_list | 54 | param_list |
65 | .params() | 55 | .params() |
66 | .filter(|closure_param| closure_param.ascribed_type().is_none()) | 56 | .filter(|closure_param| closure_param.ascribed_type().is_none()) |
67 | .filter_map(|closure_param| { | 57 | .filter_map(|closure_param| closure_param.pat()) |
68 | let closure_param_syntax = closure_param.syntax(); | 58 | .map(|root_pat| { |
69 | let inlay_type_string = get_node_displayable_type( | 59 | get_pat_hints( |
70 | db, | 60 | db, |
71 | file_id, | 61 | &analyzer, |
72 | closure_param_syntax, | 62 | root_pat, |
73 | &closure_param.pat()?, | 63 | InlayKind::ClosureParameterType, |
74 | )? | 64 | false, |
75 | .display(db) | 65 | ) |
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 | }) | 66 | }) |
67 | .flatten() | ||
68 | .collect() | ||
69 | }) | ||
70 | }) | ||
71 | .visit(|for_expression: ForExpr| { | ||
72 | let pat = for_expression.pat()?; | ||
73 | let analyzer = SourceAnalyzer::new(db, file_id, for_expression.syntax(), None); | ||
74 | Some(get_pat_hints(db, &analyzer, pat, InlayKind::ForExpressionBindingType, false)) | ||
75 | }) | ||
76 | .visit(|if_expr: IfExpr| { | ||
77 | let pat = if_expr.condition()?.pat()?; | ||
78 | let analyzer = SourceAnalyzer::new(db, file_id, if_expr.syntax(), None); | ||
79 | Some(get_pat_hints(db, &analyzer, pat, InlayKind::IfExpressionType, true)) | ||
80 | }) | ||
81 | .visit(|while_expr: WhileExpr| { | ||
82 | let pat = while_expr.condition()?.pat()?; | ||
83 | let analyzer = SourceAnalyzer::new(db, file_id, while_expr.syntax(), None); | ||
84 | Some(get_pat_hints(db, &analyzer, pat, InlayKind::WhileLetExpressionType, true)) | ||
85 | }) | ||
86 | .visit(|match_arm_list: MatchArmList| { | ||
87 | let analyzer = SourceAnalyzer::new(db, file_id, match_arm_list.syntax(), None); | ||
88 | Some( | ||
89 | match_arm_list | ||
90 | .arms() | ||
91 | .map(|match_arm| match_arm.pats()) | ||
92 | .flatten() | ||
93 | .map(|root_pat| { | ||
94 | get_pat_hints(db, &analyzer, root_pat, InlayKind::MatchArmType, true) | ||
95 | }) | ||
96 | .flatten() | ||
85 | .collect(), | 97 | .collect(), |
86 | ), | 98 | ) |
87 | None => None, | ||
88 | }) | 99 | }) |
89 | .accept(&node)? | 100 | .accept(&node)? |
90 | } | 101 | } |
91 | 102 | ||
103 | fn get_pat_hints( | ||
104 | db: &RootDatabase, | ||
105 | analyzer: &SourceAnalyzer, | ||
106 | root_pat: Pat, | ||
107 | kind: InlayKind, | ||
108 | skip_root_pat_hint: bool, | ||
109 | ) -> Vec<InlayHint> { | ||
110 | let original_pat = &root_pat.clone(); | ||
111 | |||
112 | get_leaf_pats(root_pat) | ||
113 | .into_iter() | ||
114 | .filter(|pat| !skip_root_pat_hint || pat != original_pat) | ||
115 | .filter_map(|pat| { | ||
116 | get_node_displayable_type(db, &analyzer, &pat) | ||
117 | .map(|pat_type| (pat.syntax().text_range(), pat_type)) | ||
118 | }) | ||
119 | .map(|(range, pat_type)| InlayHint { | ||
120 | range, | ||
121 | kind: kind.clone(), | ||
122 | label: pat_type.display(db).to_string().into(), | ||
123 | }) | ||
124 | .collect() | ||
125 | } | ||
126 | |||
127 | fn get_leaf_pats(root_pat: Pat) -> Vec<Pat> { | ||
128 | let mut pats_to_process = std::collections::VecDeque::<Pat>::new(); | ||
129 | pats_to_process.push_back(root_pat); | ||
130 | |||
131 | let mut leaf_pats = Vec::new(); | ||
132 | |||
133 | while let Some(maybe_leaf_pat) = pats_to_process.pop_front() { | ||
134 | match maybe_leaf_pat.kind() { | ||
135 | PatKind::BindPat(bind_pat) => { | ||
136 | if let Some(pat) = bind_pat.pat() { | ||
137 | pats_to_process.push_back(pat); | ||
138 | } else { | ||
139 | leaf_pats.push(maybe_leaf_pat); | ||
140 | } | ||
141 | } | ||
142 | PatKind::TuplePat(tuple_pat) => { | ||
143 | for arg_pat in tuple_pat.args() { | ||
144 | pats_to_process.push_back(arg_pat); | ||
145 | } | ||
146 | } | ||
147 | PatKind::StructPat(struct_pat) => { | ||
148 | if let Some(pat_list) = struct_pat.field_pat_list() { | ||
149 | pats_to_process.extend( | ||
150 | pat_list | ||
151 | .field_pats() | ||
152 | .filter_map(|field_pat| { | ||
153 | field_pat | ||
154 | .pat() | ||
155 | .filter(|pat| pat.syntax().kind() != SyntaxKind::BIND_PAT) | ||
156 | }) | ||
157 | .chain(pat_list.bind_pats().map(|bind_pat| { | ||
158 | bind_pat.pat().unwrap_or_else(|| Pat::from(bind_pat)) | ||
159 | })), | ||
160 | ); | ||
161 | } | ||
162 | } | ||
163 | PatKind::TupleStructPat(tuple_struct_pat) => { | ||
164 | for arg_pat in tuple_struct_pat.args() { | ||
165 | pats_to_process.push_back(arg_pat); | ||
166 | } | ||
167 | } | ||
168 | _ => (), | ||
169 | } | ||
170 | } | ||
171 | leaf_pats | ||
172 | } | ||
173 | |||
92 | fn get_node_displayable_type( | 174 | fn get_node_displayable_type( |
93 | db: &RootDatabase, | 175 | db: &RootDatabase, |
94 | file_id: FileId, | 176 | analyzer: &SourceAnalyzer, |
95 | node_syntax: &SyntaxNode, | ||
96 | node_pat: &Pat, | 177 | node_pat: &Pat, |
97 | ) -> Option<Ty> { | 178 | ) -> 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| { | 179 | analyzer.type_of_pat(db, node_pat).and_then(|resolved_type| { |
100 | if let Ty::Apply(_) = resolved_type { | 180 | if let Ty::Apply(_) = resolved_type { |
101 | Some(resolved_type) | 181 | Some(resolved_type) |
@@ -111,68 +191,306 @@ mod tests { | |||
111 | use insta::assert_debug_snapshot_matches; | 191 | use insta::assert_debug_snapshot_matches; |
112 | 192 | ||
113 | #[test] | 193 | #[test] |
114 | fn test_inlay_hints() { | 194 | fn let_statement() { |
115 | let (analysis, file_id) = single_file( | 195 | let (analysis, file_id) = single_file( |
116 | r#" | 196 | r#" |
117 | struct OuterStruct {} | 197 | #[derive(PartialEq)] |
198 | enum CustomOption<T> { | ||
199 | None, | ||
200 | Some(T), | ||
201 | } | ||
202 | |||
203 | #[derive(PartialEq)] | ||
204 | struct Test { | ||
205 | a: CustomOption<u32>, | ||
206 | b: u8, | ||
207 | } | ||
118 | 208 | ||
119 | fn main() { | 209 | fn main() { |
120 | struct InnerStruct {} | 210 | struct InnerStruct {} |
121 | 211 | ||
122 | let test = 54; | 212 | let test = 54; |
123 | let test = InnerStruct {}; | 213 | let test: i32 = 33; |
124 | let test = OuterStruct {}; | ||
125 | let test = vec![222]; | ||
126 | let mut test = Vec::new(); | ||
127 | test.push(333); | ||
128 | let test = test.into_iter().map(|i| i * i).collect::<Vec<_>>(); | ||
129 | let mut test = 33; | 214 | let mut test = 33; |
130 | let _ = 22; | 215 | let _ = 22; |
131 | let test: Vec<_> = (0..3).collect(); | 216 | let test = "test"; |
217 | let test = InnerStruct {}; | ||
132 | 218 | ||
133 | let _ = (0..23).map(|i: u32| { | 219 | let test = vec![222]; |
134 | let i_squared = i * i; | 220 | let test: Vec<_> = (0..3).collect(); |
135 | i_squared | 221 | let test = (0..3).collect::<Vec<i128>>(); |
136 | }); | 222 | let test = (0..3).collect::<Vec<_>>(); |
137 | 223 | ||
138 | let test: i32 = 33; | 224 | let mut test = Vec::new(); |
225 | test.push(333); | ||
139 | 226 | ||
140 | let (x, c) = (42, 'a'); | ||
141 | let test = (42, 'a'); | 227 | let test = (42, 'a'); |
142 | } | 228 | let (a, (b, c, (d, e), f)) = (2, (3, 4, (6.6, 7.7), 5)); |
143 | "#, | 229 | }"#, |
144 | ); | 230 | ); |
145 | 231 | ||
146 | assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[ | 232 | assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[ |
147 | InlayHint { | 233 | InlayHint { |
148 | range: [71; 75), | 234 | range: [193; 197), |
149 | kind: LetBindingType, | 235 | kind: LetBindingType, |
150 | label: "i32", | 236 | label: "i32", |
151 | }, | 237 | }, |
152 | InlayHint { | 238 | InlayHint { |
153 | range: [121; 125), | 239 | range: [236; 244), |
154 | kind: LetBindingType, | 240 | kind: LetBindingType, |
155 | label: "OuterStruct", | 241 | label: "i32", |
156 | }, | 242 | }, |
157 | InlayHint { | 243 | InlayHint { |
158 | range: [297; 305), | 244 | range: [275; 279), |
245 | kind: LetBindingType, | ||
246 | label: "&str", | ||
247 | }, | ||
248 | InlayHint { | ||
249 | range: [539; 543), | ||
250 | kind: LetBindingType, | ||
251 | label: "(i32, char)", | ||
252 | }, | ||
253 | InlayHint { | ||
254 | range: [566; 567), | ||
159 | kind: LetBindingType, | 255 | kind: LetBindingType, |
160 | label: "i32", | 256 | label: "i32", |
161 | }, | 257 | }, |
162 | InlayHint { | 258 | InlayHint { |
163 | range: [417; 426), | 259 | range: [570; 571), |
164 | kind: LetBindingType, | 260 | kind: LetBindingType, |
165 | label: "u32", | 261 | label: "i32", |
166 | }, | 262 | }, |
167 | InlayHint { | 263 | InlayHint { |
168 | range: [496; 502), | 264 | range: [573; 574), |
169 | kind: LetBindingType, | 265 | kind: LetBindingType, |
170 | label: "(i32, char)", | 266 | label: "i32", |
171 | }, | 267 | }, |
172 | InlayHint { | 268 | InlayHint { |
173 | range: [524; 528), | 269 | range: [584; 585), |
174 | kind: LetBindingType, | 270 | kind: LetBindingType, |
175 | label: "(i32, char)", | 271 | label: "i32", |
272 | }, | ||
273 | InlayHint { | ||
274 | range: [577; 578), | ||
275 | kind: LetBindingType, | ||
276 | label: "f64", | ||
277 | }, | ||
278 | InlayHint { | ||
279 | range: [580; 581), | ||
280 | kind: LetBindingType, | ||
281 | label: "f64", | ||
282 | }, | ||
283 | ]"# | ||
284 | ); | ||
285 | } | ||
286 | |||
287 | #[test] | ||
288 | fn closure_parameter() { | ||
289 | let (analysis, file_id) = single_file( | ||
290 | r#" | ||
291 | fn main() { | ||
292 | let mut start = 0; | ||
293 | (0..2).for_each(|increment| { | ||
294 | start += increment; | ||
295 | }) | ||
296 | }"#, | ||
297 | ); | ||
298 | |||
299 | assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[ | ||
300 | InlayHint { | ||
301 | range: [21; 30), | ||
302 | kind: LetBindingType, | ||
303 | label: "i32", | ||
304 | }, | ||
305 | InlayHint { | ||
306 | range: [57; 66), | ||
307 | kind: ClosureParameterType, | ||
308 | label: "i32", | ||
309 | }, | ||
310 | ]"# | ||
311 | ); | ||
312 | } | ||
313 | |||
314 | #[test] | ||
315 | fn for_expression() { | ||
316 | let (analysis, file_id) = single_file( | ||
317 | r#" | ||
318 | fn main() { | ||
319 | let mut start = 0; | ||
320 | for increment in 0..2 { | ||
321 | start += increment; | ||
322 | } | ||
323 | }"#, | ||
324 | ); | ||
325 | |||
326 | assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[ | ||
327 | InlayHint { | ||
328 | range: [21; 30), | ||
329 | kind: LetBindingType, | ||
330 | label: "i32", | ||
331 | }, | ||
332 | InlayHint { | ||
333 | range: [44; 53), | ||
334 | kind: ForExpressionBindingType, | ||
335 | label: "i32", | ||
336 | }, | ||
337 | ]"# | ||
338 | ); | ||
339 | } | ||
340 | |||
341 | #[test] | ||
342 | fn if_expr() { | ||
343 | let (analysis, file_id) = single_file( | ||
344 | r#" | ||
345 | #[derive(PartialEq)] | ||
346 | enum CustomOption<T> { | ||
347 | None, | ||
348 | Some(T), | ||
349 | } | ||
350 | |||
351 | #[derive(PartialEq)] | ||
352 | struct Test { | ||
353 | a: CustomOption<u32>, | ||
354 | b: u8, | ||
355 | } | ||
356 | |||
357 | fn main() { | ||
358 | let test = CustomOption::Some(Test { a: CustomOption::Some(3), b: 1 }); | ||
359 | if let CustomOption::None = &test {}; | ||
360 | if let test = &test {}; | ||
361 | if let CustomOption::Some(test) = &test {}; | ||
362 | if let CustomOption::Some(Test { a, b }) = &test {}; | ||
363 | if let CustomOption::Some(Test { a: x, b: y }) = &test {}; | ||
364 | if let CustomOption::Some(Test { a: CustomOption::Some(x), b: y }) = &test {}; | ||
365 | if let CustomOption::Some(Test { a: CustomOption::None, b: y }) = &test {}; | ||
366 | if let CustomOption::Some(Test { b: y, .. }) = &test {}; | ||
367 | |||
368 | if test == CustomOption::None {} | ||
369 | }"#, | ||
370 | ); | ||
371 | |||
372 | assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[ | ||
373 | InlayHint { | ||
374 | range: [166; 170), | ||
375 | kind: LetBindingType, | ||
376 | label: "CustomOption<Test>", | ||
377 | }, | ||
378 | InlayHint { | ||
379 | range: [334; 338), | ||
380 | kind: IfExpressionType, | ||
381 | label: "&Test", | ||
382 | }, | ||
383 | InlayHint { | ||
384 | range: [389; 390), | ||
385 | kind: IfExpressionType, | ||
386 | label: "&CustomOption<u32>", | ||
387 | }, | ||
388 | InlayHint { | ||
389 | range: [392; 393), | ||
390 | kind: IfExpressionType, | ||
391 | label: "&u8", | ||
392 | }, | ||
393 | InlayHint { | ||
394 | range: [531; 532), | ||
395 | kind: IfExpressionType, | ||
396 | label: "&u32", | ||
397 | }, | ||
398 | ]"# | ||
399 | ); | ||
400 | } | ||
401 | |||
402 | #[test] | ||
403 | fn while_expr() { | ||
404 | let (analysis, file_id) = single_file( | ||
405 | r#" | ||
406 | #[derive(PartialEq)] | ||
407 | enum CustomOption<T> { | ||
408 | None, | ||
409 | Some(T), | ||
410 | } | ||
411 | |||
412 | #[derive(PartialEq)] | ||
413 | struct Test { | ||
414 | a: CustomOption<u32>, | ||
415 | b: u8, | ||
416 | } | ||
417 | |||
418 | fn main() { | ||
419 | let test = CustomOption::Some(Test { a: CustomOption::Some(3), b: 1 }); | ||
420 | while let CustomOption::None = &test {}; | ||
421 | while let test = &test {}; | ||
422 | while let CustomOption::Some(test) = &test {}; | ||
423 | while let CustomOption::Some(Test { a, b }) = &test {}; | ||
424 | while let CustomOption::Some(Test { a: x, b: y }) = &test {}; | ||
425 | while let CustomOption::Some(Test { a: CustomOption::Some(x), b: y }) = &test {}; | ||
426 | while let CustomOption::Some(Test { a: CustomOption::None, b: y }) = &test {}; | ||
427 | while let CustomOption::Some(Test { b: y, .. }) = &test {}; | ||
428 | |||
429 | while test == CustomOption::None {} | ||
430 | }"#, | ||
431 | ); | ||
432 | |||
433 | assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[ | ||
434 | InlayHint { | ||
435 | range: [166; 170), | ||
436 | kind: LetBindingType, | ||
437 | label: "CustomOption<Test>", | ||
438 | }, | ||
439 | ]"# | ||
440 | ); | ||
441 | } | ||
442 | |||
443 | #[test] | ||
444 | fn match_arm_list() { | ||
445 | let (analysis, file_id) = single_file( | ||
446 | r#" | ||
447 | #[derive(PartialEq)] | ||
448 | enum CustomOption<T> { | ||
449 | None, | ||
450 | Some(T), | ||
451 | } | ||
452 | |||
453 | #[derive(PartialEq)] | ||
454 | struct Test { | ||
455 | a: CustomOption<u32>, | ||
456 | b: u8, | ||
457 | } | ||
458 | |||
459 | fn main() { | ||
460 | match CustomOption::Some(Test { a: CustomOption::Some(3), b: 1 }) { | ||
461 | CustomOption::None => (), | ||
462 | test => (), | ||
463 | CustomOption::Some(test) => (), | ||
464 | CustomOption::Some(Test { a, b }) => (), | ||
465 | CustomOption::Some(Test { a: x, b: y }) => (), | ||
466 | CustomOption::Some(Test { a: CustomOption::Some(x), b: y }) => (), | ||
467 | CustomOption::Some(Test { a: CustomOption::None, b: y }) => (), | ||
468 | CustomOption::Some(Test { b: y, .. }) => (), | ||
469 | _ => {} | ||
470 | } | ||
471 | }"#, | ||
472 | ); | ||
473 | |||
474 | assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[ | ||
475 | InlayHint { | ||
476 | range: [312; 316), | ||
477 | kind: MatchArmType, | ||
478 | label: "Test", | ||
479 | }, | ||
480 | InlayHint { | ||
481 | range: [359; 360), | ||
482 | kind: MatchArmType, | ||
483 | label: "CustomOption<u32>", | ||
484 | }, | ||
485 | InlayHint { | ||
486 | range: [362; 363), | ||
487 | kind: MatchArmType, | ||
488 | label: "u8", | ||
489 | }, | ||
490 | InlayHint { | ||
491 | range: [485; 486), | ||
492 | kind: MatchArmType, | ||
493 | label: "u32", | ||
176 | }, | 494 | }, |
177 | ]"# | 495 | ]"# |
178 | ); | 496 | ); |
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index 16ffb03ce..edb646c11 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs | |||
@@ -317,24 +317,24 @@ impl Analysis { | |||
317 | } | 317 | } |
318 | 318 | ||
319 | /// Debug info about the current state of the analysis | 319 | /// Debug info about the current state of the analysis |
320 | pub fn status(&self) -> String { | 320 | pub fn status(&self) -> Cancelable<String> { |
321 | status::status(&*self.db) | 321 | self.with_db(|db| status::status(&*db)) |
322 | } | 322 | } |
323 | 323 | ||
324 | /// Gets the text of the source file. | 324 | /// Gets the text of the source file. |
325 | pub fn file_text(&self, file_id: FileId) -> Arc<String> { | 325 | pub fn file_text(&self, file_id: FileId) -> Cancelable<Arc<String>> { |
326 | self.db.file_text(file_id) | 326 | self.with_db(|db| db.file_text(file_id)) |
327 | } | 327 | } |
328 | 328 | ||
329 | /// Gets the syntax tree of the file. | 329 | /// Gets the syntax tree of the file. |
330 | pub fn parse(&self, file_id: FileId) -> SourceFile { | 330 | pub fn parse(&self, file_id: FileId) -> Cancelable<SourceFile> { |
331 | self.db.parse(file_id).tree() | 331 | self.with_db(|db| db.parse(file_id).tree()) |
332 | } | 332 | } |
333 | 333 | ||
334 | /// Gets the file's `LineIndex`: data structure to convert between absolute | 334 | /// Gets the file's `LineIndex`: data structure to convert between absolute |
335 | /// offsets and line/column representation. | 335 | /// offsets and line/column representation. |
336 | pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> { | 336 | pub fn file_line_index(&self, file_id: FileId) -> Cancelable<Arc<LineIndex>> { |
337 | self.db.line_index(file_id) | 337 | self.with_db(|db| db.line_index(file_id)) |
338 | } | 338 | } |
339 | 339 | ||
340 | /// Selects the next syntactic nodes encompassing the range. | 340 | /// Selects the next syntactic nodes encompassing the range. |
@@ -344,58 +344,67 @@ impl Analysis { | |||
344 | 344 | ||
345 | /// Returns position of the matching brace (all types of braces are | 345 | /// Returns position of the matching brace (all types of braces are |
346 | /// supported). | 346 | /// supported). |
347 | pub fn matching_brace(&self, position: FilePosition) -> Option<TextUnit> { | 347 | pub fn matching_brace(&self, position: FilePosition) -> Cancelable<Option<TextUnit>> { |
348 | let parse = self.db.parse(position.file_id); | 348 | self.with_db(|db| { |
349 | let file = parse.tree(); | 349 | let parse = db.parse(position.file_id); |
350 | matching_brace::matching_brace(&file, position.offset) | 350 | let file = parse.tree(); |
351 | matching_brace::matching_brace(&file, position.offset) | ||
352 | }) | ||
351 | } | 353 | } |
352 | 354 | ||
353 | /// Returns a syntax tree represented as `String`, for debug purposes. | 355 | /// Returns a syntax tree represented as `String`, for debug purposes. |
354 | // FIXME: use a better name here. | 356 | // FIXME: use a better name here. |
355 | pub fn syntax_tree(&self, file_id: FileId, text_range: Option<TextRange>) -> String { | 357 | pub fn syntax_tree( |
356 | syntax_tree::syntax_tree(&self.db, file_id, text_range) | 358 | &self, |
359 | file_id: FileId, | ||
360 | text_range: Option<TextRange>, | ||
361 | ) -> Cancelable<String> { | ||
362 | self.with_db(|db| syntax_tree::syntax_tree(&db, file_id, text_range)) | ||
357 | } | 363 | } |
358 | 364 | ||
359 | /// Returns an edit to remove all newlines in the range, cleaning up minor | 365 | /// Returns an edit to remove all newlines in the range, cleaning up minor |
360 | /// stuff like trailing commas. | 366 | /// stuff like trailing commas. |
361 | pub fn join_lines(&self, frange: FileRange) -> SourceChange { | 367 | pub fn join_lines(&self, frange: FileRange) -> Cancelable<SourceChange> { |
362 | let parse = self.db.parse(frange.file_id); | 368 | self.with_db(|db| { |
363 | let file_edit = SourceFileEdit { | 369 | let parse = db.parse(frange.file_id); |
364 | file_id: frange.file_id, | 370 | let file_edit = SourceFileEdit { |
365 | edit: join_lines::join_lines(&parse.tree(), frange.range), | 371 | file_id: frange.file_id, |
366 | }; | 372 | edit: join_lines::join_lines(&parse.tree(), frange.range), |
367 | SourceChange::source_file_edit("join lines", file_edit) | 373 | }; |
374 | SourceChange::source_file_edit("join lines", file_edit) | ||
375 | }) | ||
368 | } | 376 | } |
369 | 377 | ||
370 | /// Returns an edit which should be applied when opening a new line, fixing | 378 | /// Returns an edit which should be applied when opening a new line, fixing |
371 | /// up minor stuff like continuing the comment. | 379 | /// up minor stuff like continuing the comment. |
372 | pub fn on_enter(&self, position: FilePosition) -> Option<SourceChange> { | 380 | pub fn on_enter(&self, position: FilePosition) -> Cancelable<Option<SourceChange>> { |
373 | typing::on_enter(&self.db, position) | 381 | self.with_db(|db| typing::on_enter(&db, position)) |
374 | } | 382 | } |
375 | 383 | ||
376 | /// Returns an edit which should be applied after `=` was typed. Primarily, | 384 | /// Returns an edit which should be applied after `=` was typed. Primarily, |
377 | /// this works when adding `let =`. | 385 | /// this works when adding `let =`. |
378 | // FIXME: use a snippet completion instead of this hack here. | 386 | // FIXME: use a snippet completion instead of this hack here. |
379 | pub fn on_eq_typed(&self, position: FilePosition) -> Option<SourceChange> { | 387 | pub fn on_eq_typed(&self, position: FilePosition) -> Cancelable<Option<SourceChange>> { |
380 | let parse = self.db.parse(position.file_id); | 388 | self.with_db(|db| { |
381 | let file = parse.tree(); | 389 | let parse = db.parse(position.file_id); |
382 | let edit = typing::on_eq_typed(&file, position.offset)?; | 390 | let file = parse.tree(); |
383 | Some(SourceChange::source_file_edit( | 391 | let edit = typing::on_eq_typed(&file, position.offset)?; |
384 | "add semicolon", | 392 | Some(SourceChange::source_file_edit( |
385 | SourceFileEdit { edit, file_id: position.file_id }, | 393 | "add semicolon", |
386 | )) | 394 | SourceFileEdit { edit, file_id: position.file_id }, |
395 | )) | ||
396 | }) | ||
387 | } | 397 | } |
388 | 398 | ||
389 | /// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately. | 399 | /// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately. |
390 | pub fn on_dot_typed(&self, position: FilePosition) -> Option<SourceChange> { | 400 | pub fn on_dot_typed(&self, position: FilePosition) -> Cancelable<Option<SourceChange>> { |
391 | typing::on_dot_typed(&self.db, position) | 401 | self.with_db(|db| typing::on_dot_typed(&db, position)) |
392 | } | 402 | } |
393 | 403 | ||
394 | /// Returns a tree representation of symbols in the file. Useful to draw a | 404 | /// Returns a tree representation of symbols in the file. Useful to draw a |
395 | /// file outline. | 405 | /// file outline. |
396 | pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> { | 406 | pub fn file_structure(&self, file_id: FileId) -> Cancelable<Vec<StructureNode>> { |
397 | let parse = self.db.parse(file_id); | 407 | self.with_db(|db| file_structure(&db.parse(file_id).tree())) |
398 | file_structure(&parse.tree()) | ||
399 | } | 408 | } |
400 | 409 | ||
401 | /// Returns a list of the places in the file where type hints can be displayed. | 410 | /// Returns a list of the places in the file where type hints can be displayed. |
@@ -404,9 +413,8 @@ impl Analysis { | |||
404 | } | 413 | } |
405 | 414 | ||
406 | /// Returns the set of folding ranges. | 415 | /// Returns the set of folding ranges. |
407 | pub fn folding_ranges(&self, file_id: FileId) -> Vec<Fold> { | 416 | pub fn folding_ranges(&self, file_id: FileId) -> Cancelable<Vec<Fold>> { |
408 | let parse = self.db.parse(file_id); | 417 | self.with_db(|db| folding_ranges::folding_ranges(&db.parse(file_id).tree())) |
409 | folding_ranges::folding_ranges(&parse.tree()) | ||
410 | } | 418 | } |
411 | 419 | ||
412 | /// Fuzzy searches for a symbol. | 420 | /// Fuzzy searches for a symbol. |
diff --git a/crates/ra_ide_api/src/references.rs b/crates/ra_ide_api/src/references.rs index 379dd422e..2118e7ad3 100644 --- a/crates/ra_ide_api/src/references.rs +++ b/crates/ra_ide_api/src/references.rs | |||
@@ -437,7 +437,8 @@ mod tests { | |||
437 | } | 437 | } |
438 | } | 438 | } |
439 | } | 439 | } |
440 | let result = text_edit_builder.finish().apply(&*analysis.file_text(file_id.unwrap())); | 440 | let result = |
441 | text_edit_builder.finish().apply(&*analysis.file_text(file_id.unwrap()).unwrap()); | ||
441 | assert_eq_text!(expected, &*result); | 442 | assert_eq_text!(expected, &*result); |
442 | } | 443 | } |
443 | } | 444 | } |
diff --git a/crates/ra_ide_api/src/syntax_tree.rs b/crates/ra_ide_api/src/syntax_tree.rs index a07e670fa..dd31b9093 100644 --- a/crates/ra_ide_api/src/syntax_tree.rs +++ b/crates/ra_ide_api/src/syntax_tree.rs | |||
@@ -101,7 +101,7 @@ mod tests { | |||
101 | fn test_syntax_tree_without_range() { | 101 | fn test_syntax_tree_without_range() { |
102 | // Basic syntax | 102 | // Basic syntax |
103 | let (analysis, file_id) = single_file(r#"fn foo() {}"#); | 103 | let (analysis, file_id) = single_file(r#"fn foo() {}"#); |
104 | let syn = analysis.syntax_tree(file_id, None); | 104 | let syn = analysis.syntax_tree(file_id, None).unwrap(); |
105 | 105 | ||
106 | assert_eq_text!( | 106 | assert_eq_text!( |
107 | syn.trim(), | 107 | syn.trim(), |
@@ -133,7 +133,7 @@ fn test() { | |||
133 | }"# | 133 | }"# |
134 | .trim(), | 134 | .trim(), |
135 | ); | 135 | ); |
136 | let syn = analysis.syntax_tree(file_id, None); | 136 | let syn = analysis.syntax_tree(file_id, None).unwrap(); |
137 | 137 | ||
138 | assert_eq_text!( | 138 | assert_eq_text!( |
139 | syn.trim(), | 139 | syn.trim(), |
@@ -176,7 +176,7 @@ SOURCE_FILE@[0; 60) | |||
176 | #[test] | 176 | #[test] |
177 | fn test_syntax_tree_with_range() { | 177 | fn test_syntax_tree_with_range() { |
178 | let (analysis, range) = single_file_with_range(r#"<|>fn foo() {}<|>"#.trim()); | 178 | let (analysis, range) = single_file_with_range(r#"<|>fn foo() {}<|>"#.trim()); |
179 | let syn = analysis.syntax_tree(range.file_id, Some(range.range)); | 179 | let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); |
180 | 180 | ||
181 | assert_eq_text!( | 181 | assert_eq_text!( |
182 | syn.trim(), | 182 | syn.trim(), |
@@ -206,7 +206,7 @@ FN_DEF@[0; 11) | |||
206 | }"# | 206 | }"# |
207 | .trim(), | 207 | .trim(), |
208 | ); | 208 | ); |
209 | let syn = analysis.syntax_tree(range.file_id, Some(range.range)); | 209 | let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); |
210 | 210 | ||
211 | assert_eq_text!( | 211 | assert_eq_text!( |
212 | syn.trim(), | 212 | syn.trim(), |
@@ -244,7 +244,7 @@ fn bar() { | |||
244 | }"# | 244 | }"# |
245 | .trim(), | 245 | .trim(), |
246 | ); | 246 | ); |
247 | let syn = analysis.syntax_tree(range.file_id, Some(range.range)); | 247 | let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); |
248 | assert_eq_text!( | 248 | assert_eq_text!( |
249 | syn.trim(), | 249 | syn.trim(), |
250 | r#" | 250 | r#" |
@@ -278,7 +278,7 @@ fn bar() { | |||
278 | }"### | 278 | }"### |
279 | .trim(), | 279 | .trim(), |
280 | ); | 280 | ); |
281 | let syn = analysis.syntax_tree(range.file_id, Some(range.range)); | 281 | let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); |
282 | assert_eq_text!( | 282 | assert_eq_text!( |
283 | syn.trim(), | 283 | syn.trim(), |
284 | r#" | 284 | r#" |
@@ -311,7 +311,7 @@ fn bar() { | |||
311 | }"### | 311 | }"### |
312 | .trim(), | 312 | .trim(), |
313 | ); | 313 | ); |
314 | let syn = analysis.syntax_tree(range.file_id, Some(range.range)); | 314 | let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); |
315 | assert_eq_text!( | 315 | assert_eq_text!( |
316 | syn.trim(), | 316 | syn.trim(), |
317 | r#" | 317 | r#" |
diff --git a/crates/ra_ide_api/src/typing.rs b/crates/ra_ide_api/src/typing.rs index 6b3fd5904..2d4491442 100644 --- a/crates/ra_ide_api/src/typing.rs +++ b/crates/ra_ide_api/src/typing.rs | |||
@@ -195,7 +195,7 @@ fn foo() { | |||
195 | edit.insert(offset, ".".to_string()); | 195 | edit.insert(offset, ".".to_string()); |
196 | let before = edit.finish().apply(&before); | 196 | let before = edit.finish().apply(&before); |
197 | let (analysis, file_id) = single_file(&before); | 197 | let (analysis, file_id) = single_file(&before); |
198 | if let Some(result) = analysis.on_dot_typed(FilePosition { offset, file_id }) { | 198 | if let Some(result) = analysis.on_dot_typed(FilePosition { offset, file_id }).unwrap() { |
199 | assert_eq!(result.source_file_edits.len(), 1); | 199 | assert_eq!(result.source_file_edits.len(), 1); |
200 | let actual = result.source_file_edits[0].edit.apply(&before); | 200 | let actual = result.source_file_edits[0].edit.apply(&before); |
201 | assert_eq_text!(after, &actual); | 201 | assert_eq_text!(after, &actual); |
@@ -377,7 +377,7 @@ fn foo() { | |||
377 | fn apply_on_enter(before: &str) -> Option<String> { | 377 | fn apply_on_enter(before: &str) -> Option<String> { |
378 | let (offset, before) = extract_offset(before); | 378 | let (offset, before) = extract_offset(before); |
379 | let (analysis, file_id) = single_file(&before); | 379 | let (analysis, file_id) = single_file(&before); |
380 | let result = analysis.on_enter(FilePosition { offset, file_id })?; | 380 | let result = analysis.on_enter(FilePosition { offset, file_id }).unwrap()?; |
381 | 381 | ||
382 | assert_eq!(result.source_file_edits.len(), 1); | 382 | assert_eq!(result.source_file_edits.len(), 1); |
383 | let actual = result.source_file_edits[0].edit.apply(&before); | 383 | let actual = result.source_file_edits[0].edit.apply(&before); |