diff options
Diffstat (limited to 'crates/ra_ide_api/src')
-rw-r--r-- | crates/ra_ide_api/src/display/structure.rs | 27 | ||||
-rw-r--r-- | crates/ra_ide_api/src/inlay_hints.rs | 110 | ||||
-rw-r--r-- | crates/ra_ide_api/src/lib.rs | 7 | ||||
-rw-r--r-- | crates/ra_ide_api/src/snapshots/tests__inlay_hints.snap | 63 |
4 files changed, 180 insertions, 27 deletions
diff --git a/crates/ra_ide_api/src/display/structure.rs b/crates/ra_ide_api/src/display/structure.rs index bd2e908da..2ba10b2ef 100644 --- a/crates/ra_ide_api/src/display/structure.rs +++ b/crates/ra_ide_api/src/display/structure.rs | |||
@@ -1,6 +1,5 @@ | |||
1 | use crate::TextRange; | 1 | use crate::TextRange; |
2 | 2 | ||
3 | use ra_syntax::ast::PatKind; | ||
4 | use ra_syntax::{ | 3 | use ra_syntax::{ |
5 | algo::visit::{visitor, Visitor}, | 4 | algo::visit::{visitor, Visitor}, |
6 | ast::{self, AttrsOwner, NameOwner, TypeAscriptionOwner, TypeParamsOwner}, | 5 | ast::{self, AttrsOwner, NameOwner, TypeAscriptionOwner, TypeParamsOwner}, |
@@ -156,32 +155,6 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> { | |||
156 | } | 155 | } |
157 | decl(mc) | 156 | decl(mc) |
158 | }) | 157 | }) |
159 | .visit(|let_statement: ast::LetStmt| { | ||
160 | let let_syntax = let_statement.syntax(); | ||
161 | |||
162 | let mut label = String::new(); | ||
163 | collapse_ws(let_syntax, &mut label); | ||
164 | |||
165 | if let_statement.ascribed_type().is_some() { | ||
166 | return None; | ||
167 | } | ||
168 | |||
169 | let pat_range = match let_statement.pat()?.kind() { | ||
170 | PatKind::BindPat(bind_pat) => bind_pat.syntax().range(), | ||
171 | PatKind::TuplePat(tuple_pat) => tuple_pat.syntax().range(), | ||
172 | _ => return None, | ||
173 | }; | ||
174 | |||
175 | Some(StructureNode { | ||
176 | parent: None, | ||
177 | label, | ||
178 | navigation_range: pat_range, | ||
179 | node_range: let_syntax.range(), | ||
180 | kind: let_syntax.kind(), | ||
181 | detail: None, | ||
182 | deprecated: false, | ||
183 | }) | ||
184 | }) | ||
185 | .accept(&node)? | 158 | .accept(&node)? |
186 | } | 159 | } |
187 | 160 | ||
diff --git a/crates/ra_ide_api/src/inlay_hints.rs b/crates/ra_ide_api/src/inlay_hints.rs new file mode 100644 index 000000000..4d1df65d0 --- /dev/null +++ b/crates/ra_ide_api/src/inlay_hints.rs | |||
@@ -0,0 +1,110 @@ | |||
1 | use ra_syntax::{ | ||
2 | algo::visit::{visitor, Visitor}, | ||
3 | ast::{self, PatKind, TypeAscriptionOwner}, | ||
4 | AstNode, SmolStr, SourceFile, SyntaxNode, TextRange, | ||
5 | }; | ||
6 | |||
7 | #[derive(Debug, PartialEq, Eq)] | ||
8 | pub enum InlayKind { | ||
9 | LetBinding, | ||
10 | ClosureParameter, | ||
11 | } | ||
12 | |||
13 | #[derive(Debug)] | ||
14 | pub struct InlayHint { | ||
15 | pub range: TextRange, | ||
16 | pub text: SmolStr, | ||
17 | pub inlay_kind: InlayKind, | ||
18 | } | ||
19 | |||
20 | pub(crate) fn inlay_hints(file: &SourceFile) -> Vec<InlayHint> { | ||
21 | file.syntax().descendants().map(|node| get_inlay_hints(&node)).flatten().collect() | ||
22 | } | ||
23 | |||
24 | fn get_inlay_hints(node: &SyntaxNode) -> Vec<InlayHint> { | ||
25 | visitor() | ||
26 | .visit(|let_statement: ast::LetStmt| { | ||
27 | let let_syntax = let_statement.syntax(); | ||
28 | |||
29 | if let_statement.ascribed_type().is_some() { | ||
30 | return Vec::new(); | ||
31 | } | ||
32 | |||
33 | let pat_range = match let_statement.pat().map(|pat| pat.kind()) { | ||
34 | Some(PatKind::BindPat(bind_pat)) => bind_pat.syntax().text_range(), | ||
35 | Some(PatKind::TuplePat(tuple_pat)) => tuple_pat.syntax().text_range(), | ||
36 | _ => return Vec::new(), | ||
37 | }; | ||
38 | |||
39 | vec![InlayHint { | ||
40 | range: pat_range, | ||
41 | text: let_syntax.text().to_smol_string(), | ||
42 | inlay_kind: InlayKind::LetBinding, | ||
43 | }] | ||
44 | }) | ||
45 | .visit(|closure_parameter: ast::LambdaExpr| { | ||
46 | if let Some(param_list) = closure_parameter.param_list() { | ||
47 | param_list | ||
48 | .params() | ||
49 | .filter(|closure_param| closure_param.ascribed_type().is_none()) | ||
50 | .map(|closure_param| { | ||
51 | let closure_param_syntax = closure_param.syntax(); | ||
52 | InlayHint { | ||
53 | range: closure_param_syntax.text_range(), | ||
54 | text: closure_param_syntax.text().to_smol_string(), | ||
55 | inlay_kind: InlayKind::ClosureParameter, | ||
56 | } | ||
57 | }) | ||
58 | .collect() | ||
59 | } else { | ||
60 | Vec::new() | ||
61 | } | ||
62 | }) | ||
63 | .accept(&node) | ||
64 | .unwrap_or_else(Vec::new) | ||
65 | } | ||
66 | |||
67 | #[cfg(test)] | ||
68 | mod tests { | ||
69 | use super::*; | ||
70 | use insta::assert_debug_snapshot_matches; | ||
71 | |||
72 | #[test] | ||
73 | fn test_inlay_hints() { | ||
74 | let file = SourceFile::parse( | ||
75 | r#" | ||
76 | struct OuterStruct {} | ||
77 | |||
78 | fn main() { | ||
79 | struct InnerStruct {} | ||
80 | |||
81 | let test = 54; | ||
82 | let test = InnerStruct {}; | ||
83 | let test = OuterStruct {}; | ||
84 | let test = vec![222]; | ||
85 | let mut test = Vec::new(); | ||
86 | test.push(333); | ||
87 | let test = test.into_iter().map(|i| i * i).collect::<Vec<_>>(); | ||
88 | let mut test = 33; | ||
89 | let _ = 22; | ||
90 | let test: Vec<_> = (0..3).collect(); | ||
91 | |||
92 | let _ = (0..23).map(|i: u32| { | ||
93 | let i_squared = i * i; | ||
94 | i_squared | ||
95 | }); | ||
96 | |||
97 | let test: i32 = 33; | ||
98 | |||
99 | let (x, c) = (42, 'a'); | ||
100 | let test = (42, 'a'); | ||
101 | } | ||
102 | |||
103 | "#, | ||
104 | ) | ||
105 | .ok() | ||
106 | .unwrap(); | ||
107 | let hints = inlay_hints(&file); | ||
108 | assert_debug_snapshot_matches!("inlay_hints", hints); | ||
109 | } | ||
110 | } | ||
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index c54d574bc..af163088a 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs | |||
@@ -38,6 +38,7 @@ mod join_lines; | |||
38 | mod typing; | 38 | mod typing; |
39 | mod matching_brace; | 39 | mod matching_brace; |
40 | mod display; | 40 | mod display; |
41 | mod inlay_hints; | ||
41 | 42 | ||
42 | #[cfg(test)] | 43 | #[cfg(test)] |
43 | mod marks; | 44 | mod marks; |
@@ -64,6 +65,7 @@ pub use crate::{ | |||
64 | display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, | 65 | display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, |
65 | folding_ranges::{Fold, FoldKind}, | 66 | folding_ranges::{Fold, FoldKind}, |
66 | hover::HoverResult, | 67 | hover::HoverResult, |
68 | inlay_hints::{InlayHint, InlayKind}, | ||
67 | line_index::{LineCol, LineIndex}, | 69 | line_index::{LineCol, LineIndex}, |
68 | line_index_utils::translate_offset_with_edit, | 70 | line_index_utils::translate_offset_with_edit, |
69 | references::ReferenceSearchResult, | 71 | references::ReferenceSearchResult, |
@@ -396,6 +398,11 @@ impl Analysis { | |||
396 | file_structure(&parse.tree()) | 398 | file_structure(&parse.tree()) |
397 | } | 399 | } |
398 | 400 | ||
401 | /// Returns a list of the places in the file where type hints can be displayed. | ||
402 | pub fn inlay_hints(&self, file_id: FileId) -> Vec<InlayHint> { | ||
403 | inlay_hints::inlay_hints(&self.db.parse(file_id).tree()) | ||
404 | } | ||
405 | |||
399 | /// Returns the set of folding ranges. | 406 | /// Returns the set of folding ranges. |
400 | pub fn folding_ranges(&self, file_id: FileId) -> Vec<Fold> { | 407 | pub fn folding_ranges(&self, file_id: FileId) -> Vec<Fold> { |
401 | let parse = self.db.parse(file_id); | 408 | let parse = self.db.parse(file_id); |
diff --git a/crates/ra_ide_api/src/snapshots/tests__inlay_hints.snap b/crates/ra_ide_api/src/snapshots/tests__inlay_hints.snap new file mode 100644 index 000000000..f4d562314 --- /dev/null +++ b/crates/ra_ide_api/src/snapshots/tests__inlay_hints.snap | |||
@@ -0,0 +1,63 @@ | |||
1 | --- | ||
2 | created: "2019-07-20T20:13:53.385368Z" | ||
3 | creator: [email protected] | ||
4 | source: crates/ra_ide_api/src/inlay_hints.rs | ||
5 | expression: hints | ||
6 | --- | ||
7 | [ | ||
8 | InlayHint { | ||
9 | range: [71; 75), | ||
10 | text: "let test = 54;", | ||
11 | inlay_kind: LetBinding, | ||
12 | }, | ||
13 | InlayHint { | ||
14 | range: [90; 94), | ||
15 | text: "let test = InnerStruct {};", | ||
16 | inlay_kind: LetBinding, | ||
17 | }, | ||
18 | InlayHint { | ||
19 | range: [121; 125), | ||
20 | text: "let test = OuterStruct {};", | ||
21 | inlay_kind: LetBinding, | ||
22 | }, | ||
23 | InlayHint { | ||
24 | range: [152; 156), | ||
25 | text: "let test = vec![222];", | ||
26 | inlay_kind: LetBinding, | ||
27 | }, | ||
28 | InlayHint { | ||
29 | range: [178; 186), | ||
30 | text: "let mut test = Vec::new();", | ||
31 | inlay_kind: LetBinding, | ||
32 | }, | ||
33 | InlayHint { | ||
34 | range: [229; 233), | ||
35 | text: "let test = test.into_iter().map(|i| i * i).collect::<Vec<_>>();", | ||
36 | inlay_kind: LetBinding, | ||
37 | }, | ||
38 | InlayHint { | ||
39 | range: [258; 259), | ||
40 | text: "i", | ||
41 | inlay_kind: ClosureParameter, | ||
42 | }, | ||
43 | InlayHint { | ||
44 | range: [297; 305), | ||
45 | text: "let mut test = 33;", | ||
46 | inlay_kind: LetBinding, | ||
47 | }, | ||
48 | InlayHint { | ||
49 | range: [417; 426), | ||
50 | text: "let i_squared = i * i;", | ||
51 | inlay_kind: LetBinding, | ||
52 | }, | ||
53 | InlayHint { | ||
54 | range: [500; 506), | ||
55 | text: "let (x, c) = (42, \'a\');", | ||
56 | inlay_kind: LetBinding, | ||
57 | }, | ||
58 | InlayHint { | ||
59 | range: [528; 532), | ||
60 | text: "let test = (42, \'a\');", | ||
61 | inlay_kind: LetBinding, | ||
62 | }, | ||
63 | ] | ||