aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2019-07-23 10:51:40 +0100
committerbors[bot] <26634292+bors[bot]@users.noreply.github.com>2019-07-23 10:51:40 +0100
commit5f3ff157e3efe9f4fe2461bca1a0caaa5d2c72e5 (patch)
tree38108566787198f9a1391cbf016b05016f3fbda0
parent08efe6cf92e7058ba38833dbfab8940a57a2cbfe (diff)
parent8f3377d9f93a256f8e68ae183808fd767b529d18 (diff)
Merge #1549
1549: Show type lenses for the resolved let bindings r=matklad a=SomeoneToIgnore Types that are fully unresolved are not displayed: <img width="279" alt="image" src="https://user-images.githubusercontent.com/2690773/61518122-8e4ba980-aa11-11e9-9249-6d9f9b202e6a.png"> A few concerns that I have about the current implementation: * I've adjusted the `file_structure` API method to return the information about the `let` bindings. Although it works fine, I have a feeling that adding a new API method would be the better way. But this requires some prior discussion, so I've decided to go for an easy way with an MVP. Would be nice to hear your suggestions. * There's a hardcoded `{undersolved}` check that I was forced to use, since the method that resolves types returns a `String`. Is there a better typed API I can use? This will help, for instance, to add an action to the type lenses that will allow us to navigate to the type. Co-authored-by: Kirill Bulatov <[email protected]>
-rw-r--r--crates/ra_ide_api/src/inlay_hints.rs180
-rw-r--r--crates/ra_ide_api/src/lib.rs7
-rw-r--r--crates/ra_lsp_server/src/main_loop.rs1
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs23
-rw-r--r--crates/ra_lsp_server/src/req.rs27
5 files changed, 237 insertions, 1 deletions
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..174662beb
--- /dev/null
+++ b/crates/ra_ide_api/src/inlay_hints.rs
@@ -0,0 +1,180 @@
1use crate::{db::RootDatabase, FileId};
2use hir::{HirDisplay, Ty};
3use ra_syntax::ast::Pat;
4use ra_syntax::{
5 algo::visit::{visitor, Visitor},
6 ast::{self, PatKind, TypeAscriptionOwner},
7 AstNode, SmolStr, SourceFile, SyntaxNode, TextRange,
8};
9
10#[derive(Debug, PartialEq, Eq)]
11pub enum InlayKind {
12 LetBindingType,
13 ClosureParameterType,
14}
15
16#[derive(Debug)]
17pub struct InlayHint {
18 pub range: TextRange,
19 pub kind: InlayKind,
20 pub label: SmolStr,
21}
22
23pub(crate) fn inlay_hints(db: &RootDatabase, file_id: FileId, file: &SourceFile) -> Vec<InlayHint> {
24 file.syntax()
25 .descendants()
26 .map(|node| get_inlay_hints(db, file_id, &node).unwrap_or_default())
27 .flatten()
28 .collect()
29}
30
31fn get_inlay_hints(
32 db: &RootDatabase,
33 file_id: FileId,
34 node: &SyntaxNode,
35) -> Option<Vec<InlayHint>> {
36 visitor()
37 .visit(|let_statement: ast::LetStmt| {
38 let let_syntax = let_statement.syntax();
39
40 if let_statement.ascribed_type().is_some() {
41 return None;
42 }
43
44 let let_pat = let_statement.pat()?;
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 })
62 .visit(|closure_parameter: ast::LambdaExpr| match closure_parameter.param_list() {
63 Some(param_list) => Some(
64 param_list
65 .params()
66 .filter(|closure_param| closure_param.ascribed_type().is_none())
67 .filter_map(|closure_param| {
68 let closure_param_syntax = closure_param.syntax();
69 let inlay_type_string = get_node_displayable_type(
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 })
85 .collect(),
86 ),
87 None => None,
88 })
89 .accept(&node)?
90}
91
92fn 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 })
106}
107
108#[cfg(test)]
109mod tests {
110 use crate::mock_analysis::single_file;
111 use insta::assert_debug_snapshot_matches;
112
113 #[test]
114 fn test_inlay_hints() {
115 let (analysis, file_id) = single_file(
116 r#"
117struct OuterStruct {}
118
119fn main() {
120 struct InnerStruct {}
121
122 let test = 54;
123 let test = InnerStruct {};
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;
130 let _ = 22;
131 let test: Vec<_> = (0..3).collect();
132
133 let _ = (0..23).map(|i: u32| {
134 let i_squared = i * i;
135 i_squared
136 });
137
138 let test: i32 = 33;
139
140 let (x, c) = (42, 'a');
141 let test = (42, 'a');
142}
143"#,
144 );
145
146 assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[
147 InlayHint {
148 range: [71; 75),
149 kind: LetBindingType,
150 label: "i32",
151 },
152 InlayHint {
153 range: [121; 125),
154 kind: LetBindingType,
155 label: "OuterStruct",
156 },
157 InlayHint {
158 range: [297; 305),
159 kind: LetBindingType,
160 label: "i32",
161 },
162 InlayHint {
163 range: [417; 426),
164 kind: LetBindingType,
165 label: "u32",
166 },
167 InlayHint {
168 range: [496; 502),
169 kind: LetBindingType,
170 label: "(i32, char)",
171 },
172 InlayHint {
173 range: [524; 528),
174 kind: LetBindingType,
175 label: "(i32, char)",
176 },
177]"#
178 );
179 }
180}
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs
index c54d574bc..16ffb03ce 100644
--- a/crates/ra_ide_api/src/lib.rs
+++ b/crates/ra_ide_api/src/lib.rs
@@ -38,6 +38,7 @@ mod join_lines;
38mod typing; 38mod typing;
39mod matching_brace; 39mod matching_brace;
40mod display; 40mod display;
41mod inlay_hints;
41 42
42#[cfg(test)] 43#[cfg(test)]
43mod marks; 44mod 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) -> Cancelable<Vec<InlayHint>> {
403 self.with_db(|db| inlay_hints::inlay_hints(db, file_id, &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_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs
index 668d2fd72..8e830c8b8 100644
--- a/crates/ra_lsp_server/src/main_loop.rs
+++ b/crates/ra_lsp_server/src/main_loop.rs
@@ -362,6 +362,7 @@ fn on_request(
362 .on::<req::References>(handlers::handle_references)? 362 .on::<req::References>(handlers::handle_references)?
363 .on::<req::Formatting>(handlers::handle_formatting)? 363 .on::<req::Formatting>(handlers::handle_formatting)?
364 .on::<req::DocumentHighlightRequest>(handlers::handle_document_highlight)? 364 .on::<req::DocumentHighlightRequest>(handlers::handle_document_highlight)?
365 .on::<req::InlayHints>(handlers::handle_inlay_hints)?
365 .finish(); 366 .finish();
366 Ok(()) 367 Ok(())
367} 368}
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index 68865b755..5bf950a53 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -21,7 +21,7 @@ use url_serde::Ser;
21use crate::{ 21use crate::{
22 cargo_target_spec::{runnable_args, CargoTargetSpec}, 22 cargo_target_spec::{runnable_args, CargoTargetSpec},
23 conv::{to_location, Conv, ConvWith, MapConvWith, TryConvWith, TryConvWithToVec}, 23 conv::{to_location, Conv, ConvWith, MapConvWith, TryConvWith, TryConvWithToVec},
24 req::{self, Decoration}, 24 req::{self, Decoration, InlayHint, InlayHintsParams, InlayKind},
25 world::WorldSnapshot, 25 world::WorldSnapshot,
26 LspError, Result, 26 LspError, Result,
27}; 27};
@@ -874,3 +874,24 @@ fn to_diagnostic_severity(severity: Severity) -> DiagnosticSeverity {
874 WeakWarning => DiagnosticSeverity::Hint, 874 WeakWarning => DiagnosticSeverity::Hint,
875 } 875 }
876} 876}
877
878pub fn handle_inlay_hints(
879 world: WorldSnapshot,
880 params: InlayHintsParams,
881) -> Result<Vec<InlayHint>> {
882 let file_id = params.text_document.try_conv_with(&world)?;
883 let analysis = world.analysis();
884 let line_index = analysis.file_line_index(file_id);
885 Ok(analysis
886 .inlay_hints(file_id)?
887 .into_iter()
888 .map(|api_type| InlayHint {
889 label: api_type.label.to_string(),
890 range: api_type.range.conv_with(&line_index),
891 kind: match api_type.kind {
892 ra_ide_api::InlayKind::LetBindingType => InlayKind::LetBindingType,
893 ra_ide_api::InlayKind::ClosureParameterType => InlayKind::ClosureParameterType,
894 },
895 })
896 .collect())
897}
diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs
index 8d39b04a7..916185f99 100644
--- a/crates/ra_lsp_server/src/req.rs
+++ b/crates/ra_lsp_server/src/req.rs
@@ -196,3 +196,30 @@ pub struct SourceChange {
196 pub workspace_edit: WorkspaceEdit, 196 pub workspace_edit: WorkspaceEdit,
197 pub cursor_position: Option<TextDocumentPositionParams>, 197 pub cursor_position: Option<TextDocumentPositionParams>,
198} 198}
199
200pub enum InlayHints {}
201
202impl Request for InlayHints {
203 type Params = InlayHintsParams;
204 type Result = Vec<InlayHint>;
205 const METHOD: &'static str = "rust-analyzer/inlayHints";
206}
207
208#[derive(Serialize, Deserialize, Debug)]
209#[serde(rename_all = "camelCase")]
210pub struct InlayHintsParams {
211 pub text_document: TextDocumentIdentifier,
212}
213
214#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
215pub enum InlayKind {
216 LetBindingType,
217 ClosureParameterType,
218}
219
220#[derive(Debug, Deserialize, Serialize)]
221pub struct InlayHint {
222 pub range: Range,
223 pub kind: InlayKind,
224 pub label: String,
225}