diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_ide_api/src/inlay_hints.rs | 126 | ||||
-rw-r--r-- | crates/ra_ide_api/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop/handlers.rs | 35 |
3 files changed, 84 insertions, 79 deletions
diff --git a/crates/ra_ide_api/src/inlay_hints.rs b/crates/ra_ide_api/src/inlay_hints.rs index 739a44b19..7d49ebcf4 100644 --- a/crates/ra_ide_api/src/inlay_hints.rs +++ b/crates/ra_ide_api/src/inlay_hints.rs | |||
@@ -1,3 +1,6 @@ | |||
1 | use crate::{db::RootDatabase, FileId}; | ||
2 | use hir::{HirDisplay, Ty}; | ||
3 | use ra_syntax::ast::Pat; | ||
1 | use ra_syntax::{ | 4 | use ra_syntax::{ |
2 | algo::visit::{visitor, Visitor}, | 5 | algo::visit::{visitor, Visitor}, |
3 | ast::{self, PatKind, TypeAscriptionOwner}, | 6 | ast::{self, PatKind, TypeAscriptionOwner}, |
@@ -15,63 +18,101 @@ pub struct InlayHint { | |||
15 | pub range: TextRange, | 18 | pub range: TextRange, |
16 | pub text: SmolStr, | 19 | pub text: SmolStr, |
17 | pub inlay_kind: InlayKind, | 20 | pub inlay_kind: InlayKind, |
21 | pub inlay_type_string: String, | ||
18 | } | 22 | } |
19 | 23 | ||
20 | pub(crate) fn inlay_hints(file: &SourceFile) -> Vec<InlayHint> { | 24 | pub(crate) fn inlay_hints(db: &RootDatabase, file_id: FileId, file: &SourceFile) -> Vec<InlayHint> { |
21 | file.syntax().descendants().map(|node| get_inlay_hints(&node)).flatten().collect() | 25 | file.syntax() |
26 | .descendants() | ||
27 | .map(|node| get_inlay_hints(db, file_id, &node).unwrap_or_default()) | ||
28 | .flatten() | ||
29 | .collect() | ||
22 | } | 30 | } |
23 | 31 | ||
24 | fn get_inlay_hints(node: &SyntaxNode) -> Vec<InlayHint> { | 32 | fn get_inlay_hints( |
33 | db: &RootDatabase, | ||
34 | file_id: FileId, | ||
35 | node: &SyntaxNode, | ||
36 | ) -> Option<Vec<InlayHint>> { | ||
25 | visitor() | 37 | visitor() |
26 | .visit(|let_statement: ast::LetStmt| { | 38 | .visit(|let_statement: ast::LetStmt| { |
27 | let let_syntax = let_statement.syntax(); | 39 | let let_syntax = let_statement.syntax(); |
28 | 40 | ||
29 | if let_statement.ascribed_type().is_some() { | 41 | if let_statement.ascribed_type().is_some() { |
30 | return Vec::new(); | 42 | return None; |
31 | } | 43 | } |
32 | 44 | ||
33 | let pat_range = match let_statement.pat().map(|pat| pat.kind()) { | 45 | let let_pat = let_statement.pat()?; |
34 | Some(PatKind::BindPat(bind_pat)) => bind_pat.syntax().text_range(), | 46 | let inlay_type_string = get_node_displayable_type(db, file_id, let_syntax, &let_pat)? |
35 | Some(PatKind::TuplePat(tuple_pat)) => tuple_pat.syntax().text_range(), | 47 | .display(db) |
36 | _ => return Vec::new(), | 48 | .to_string();; |
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, | ||
37 | }; | 54 | }; |
38 | 55 | ||
39 | vec![InlayHint { | 56 | Some(vec![InlayHint { |
40 | range: pat_range, | 57 | range: pat_range, |
41 | text: let_syntax.text().to_smol_string(), | 58 | text: let_syntax.text().to_smol_string(), |
42 | inlay_kind: InlayKind::LetBinding, | 59 | inlay_kind: InlayKind::LetBinding, |
43 | }] | 60 | inlay_type_string, |
61 | }]) | ||
44 | }) | 62 | }) |
45 | .visit(|closure_parameter: ast::LambdaExpr| { | 63 | .visit(|closure_parameter: ast::LambdaExpr| match closure_parameter.param_list() { |
46 | if let Some(param_list) = closure_parameter.param_list() { | 64 | Some(param_list) => Some( |
47 | param_list | 65 | param_list |
48 | .params() | 66 | .params() |
49 | .filter(|closure_param| closure_param.ascribed_type().is_none()) | 67 | .filter(|closure_param| closure_param.ascribed_type().is_none()) |
50 | .map(|closure_param| { | 68 | .filter_map(|closure_param| { |
51 | let closure_param_syntax = closure_param.syntax(); | 69 | let closure_param_syntax = closure_param.syntax(); |
52 | InlayHint { | 70 | let inlay_type_string = get_node_displayable_type( |
71 | db, | ||
72 | file_id, | ||
73 | closure_param_syntax, | ||
74 | &closure_param.pat()?, | ||
75 | )? | ||
76 | .display(db) | ||
77 | .to_string(); | ||
78 | Some(InlayHint { | ||
53 | range: closure_param_syntax.text_range(), | 79 | range: closure_param_syntax.text_range(), |
54 | text: closure_param_syntax.text().to_smol_string(), | 80 | text: closure_param_syntax.text().to_smol_string(), |
55 | inlay_kind: InlayKind::ClosureParameter, | 81 | inlay_kind: InlayKind::ClosureParameter, |
56 | } | 82 | inlay_type_string, |
83 | }) | ||
57 | }) | 84 | }) |
58 | .collect() | 85 | .collect(), |
59 | } else { | 86 | ), |
60 | Vec::new() | 87 | None => None, |
61 | } | ||
62 | }) | 88 | }) |
63 | .accept(&node) | 89 | .accept(&node)? |
64 | .unwrap_or_default() | 90 | } |
91 | |||
92 | fn get_node_displayable_type( | ||
93 | db: &RootDatabase, | ||
94 | file_id: FileId, | ||
95 | node_syntax: &SyntaxNode, | ||
96 | node_pat: &Pat, | ||
97 | ) -> 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| { | ||
100 | if let Ty::Apply(_) = resolved_type { | ||
101 | Some(resolved_type) | ||
102 | } else { | ||
103 | None | ||
104 | } | ||
105 | }) | ||
65 | } | 106 | } |
66 | 107 | ||
67 | #[cfg(test)] | 108 | #[cfg(test)] |
68 | mod tests { | 109 | mod tests { |
69 | use super::*; | 110 | use crate::mock_analysis::single_file; |
70 | use insta::assert_debug_snapshot_matches; | 111 | use insta::assert_debug_snapshot_matches; |
71 | 112 | ||
72 | #[test] | 113 | #[test] |
73 | fn test_inlay_hints() { | 114 | fn test_inlay_hints() { |
74 | let file = SourceFile::parse( | 115 | let (analysis, file_id) = single_file( |
75 | r#" | 116 | r#" |
76 | struct OuterStruct {} | 117 | struct OuterStruct {} |
77 | 118 | ||
@@ -99,66 +140,45 @@ fn main() { | |||
99 | let (x, c) = (42, 'a'); | 140 | let (x, c) = (42, 'a'); |
100 | let test = (42, 'a'); | 141 | let test = (42, 'a'); |
101 | } | 142 | } |
102 | |||
103 | "#, | 143 | "#, |
104 | ) | 144 | ); |
105 | .ok() | 145 | |
106 | .unwrap(); | 146 | assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[ |
107 | assert_debug_snapshot_matches!(inlay_hints(&file), @r#"[ | ||
108 | InlayHint { | 147 | InlayHint { |
109 | range: [71; 75), | 148 | range: [71; 75), |
110 | text: "let test = 54;", | 149 | text: "let test = 54;", |
111 | inlay_kind: LetBinding, | 150 | inlay_kind: LetBinding, |
112 | }, | 151 | inlay_type_string: "i32", |
113 | InlayHint { | ||
114 | range: [90; 94), | ||
115 | text: "let test = InnerStruct {};", | ||
116 | inlay_kind: LetBinding, | ||
117 | }, | 152 | }, |
118 | InlayHint { | 153 | InlayHint { |
119 | range: [121; 125), | 154 | range: [121; 125), |
120 | text: "let test = OuterStruct {};", | 155 | text: "let test = OuterStruct {};", |
121 | inlay_kind: LetBinding, | 156 | inlay_kind: LetBinding, |
122 | }, | 157 | inlay_type_string: "OuterStruct", |
123 | InlayHint { | ||
124 | range: [152; 156), | ||
125 | text: "let test = vec![222];", | ||
126 | inlay_kind: LetBinding, | ||
127 | }, | ||
128 | InlayHint { | ||
129 | range: [178; 186), | ||
130 | text: "let mut test = Vec::new();", | ||
131 | inlay_kind: LetBinding, | ||
132 | }, | ||
133 | InlayHint { | ||
134 | range: [229; 233), | ||
135 | text: "let test = test.into_iter().map(|i| i * i).collect::<Vec<_>>();", | ||
136 | inlay_kind: LetBinding, | ||
137 | }, | ||
138 | InlayHint { | ||
139 | range: [258; 259), | ||
140 | text: "i", | ||
141 | inlay_kind: ClosureParameter, | ||
142 | }, | 158 | }, |
143 | InlayHint { | 159 | InlayHint { |
144 | range: [297; 305), | 160 | range: [297; 305), |
145 | text: "let mut test = 33;", | 161 | text: "let mut test = 33;", |
146 | inlay_kind: LetBinding, | 162 | inlay_kind: LetBinding, |
163 | inlay_type_string: "i32", | ||
147 | }, | 164 | }, |
148 | InlayHint { | 165 | InlayHint { |
149 | range: [417; 426), | 166 | range: [417; 426), |
150 | text: "let i_squared = i * i;", | 167 | text: "let i_squared = i * i;", |
151 | inlay_kind: LetBinding, | 168 | inlay_kind: LetBinding, |
169 | inlay_type_string: "u32", | ||
152 | }, | 170 | }, |
153 | InlayHint { | 171 | InlayHint { |
154 | range: [496; 502), | 172 | range: [496; 502), |
155 | text: "let (x, c) = (42, \'a\');", | 173 | text: "let (x, c) = (42, \'a\');", |
156 | inlay_kind: LetBinding, | 174 | inlay_kind: LetBinding, |
175 | inlay_type_string: "(i32, char)", | ||
157 | }, | 176 | }, |
158 | InlayHint { | 177 | InlayHint { |
159 | range: [524; 528), | 178 | range: [524; 528), |
160 | text: "let test = (42, \'a\');", | 179 | text: "let test = (42, \'a\');", |
161 | inlay_kind: LetBinding, | 180 | inlay_kind: LetBinding, |
181 | inlay_type_string: "(i32, char)", | ||
162 | }, | 182 | }, |
163 | ]"# | 183 | ]"# |
164 | ); | 184 | ); |
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index 32168f12d..16ffb03ce 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs | |||
@@ -400,7 +400,7 @@ impl Analysis { | |||
400 | 400 | ||
401 | /// Returns a list of the places in the file where type hints can be displayed. | 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) -> Cancelable<Vec<InlayHint>> { | 402 | pub fn inlay_hints(&self, file_id: FileId) -> Cancelable<Vec<InlayHint>> { |
403 | self.with_db(|db| inlay_hints::inlay_hints(&db.parse(file_id).tree())) | 403 | self.with_db(|db| inlay_hints::inlay_hints(db, file_id, &db.parse(file_id).tree())) |
404 | } | 404 | } |
405 | 405 | ||
406 | /// Returns the set of folding ranges. | 406 | /// Returns the set of folding ranges. |
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index 1077aafd8..e5d2ff832 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs | |||
@@ -9,8 +9,7 @@ use lsp_types::{ | |||
9 | TextDocumentIdentifier, TextEdit, WorkspaceEdit, | 9 | TextDocumentIdentifier, TextEdit, WorkspaceEdit, |
10 | }; | 10 | }; |
11 | use ra_ide_api::{ | 11 | use ra_ide_api::{ |
12 | AssistId, Cancelable, FileId, FilePosition, FileRange, FoldKind, InlayKind, Query, | 12 | AssistId, Cancelable, FileId, FilePosition, FileRange, FoldKind, Query, RunnableKind, Severity, |
13 | RunnableKind, Severity, | ||
14 | }; | 13 | }; |
15 | use ra_prof::profile; | 14 | use ra_prof::profile; |
16 | use ra_syntax::{AstNode, SyntaxKind, TextRange, TextUnit}; | 15 | use ra_syntax::{AstNode, SyntaxKind, TextRange, TextUnit}; |
@@ -750,29 +749,15 @@ pub fn handle_code_lens( | |||
750 | }), | 749 | }), |
751 | ); | 750 | ); |
752 | 751 | ||
753 | lenses.extend( | 752 | lenses.extend(analysis.inlay_hints(file_id)?.into_iter().map(|inlay_hint| CodeLens { |
754 | analysis | 753 | range: inlay_hint.range.conv_with(&line_index), |
755 | .inlay_hints(file_id) | 754 | command: Some(Command { |
756 | .into_iter() | 755 | title: inlay_hint.inlay_type_string, |
757 | .filter(|hint| hint.inlay_kind == InlayKind::LetBinding) | 756 | command: String::new(), |
758 | .filter_map(|inlay_hint| { | 757 | arguments: None, |
759 | let resolved_type = analysis | 758 | }), |
760 | .type_of(FileRange { range: inlay_hint.range, file_id }) | 759 | data: None, |
761 | .ok() | 760 | })); |
762 | .and_then(std::convert::identity) | ||
763 | .filter(|resolved_type| "{unknown}" != resolved_type); | ||
764 | resolved_type.map(|resolved_type| (resolved_type, inlay_hint.range)) | ||
765 | }) | ||
766 | .map(|(resolved_type, range)| CodeLens { | ||
767 | range: range.conv_with(&line_index), | ||
768 | command: Some(Command { | ||
769 | title: resolved_type, | ||
770 | command: String::new(), | ||
771 | arguments: None, | ||
772 | }), | ||
773 | data: None, | ||
774 | }), | ||
775 | ); | ||
776 | Ok(Some(lenses)) | 761 | Ok(Some(lenses)) |
777 | } | 762 | } |
778 | 763 | ||