diff options
-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 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop/handlers.rs | 70 |
5 files changed, 222 insertions, 55 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 | ] | ||
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index ea947417f..1077aafd8 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs | |||
@@ -9,7 +9,8 @@ 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, Query, RunnableKind, Severity, | 12 | AssistId, Cancelable, FileId, FilePosition, FileRange, FoldKind, InlayKind, Query, |
13 | RunnableKind, Severity, | ||
13 | }; | 14 | }; |
14 | use ra_prof::profile; | 15 | use ra_prof::profile; |
15 | use ra_syntax::{AstNode, SyntaxKind, TextRange, TextUnit}; | 16 | use ra_syntax::{AstNode, SyntaxKind, TextRange, TextUnit}; |
@@ -685,13 +686,14 @@ pub fn handle_code_lens( | |||
685 | params: req::CodeLensParams, | 686 | params: req::CodeLensParams, |
686 | ) -> Result<Option<Vec<CodeLens>>> { | 687 | ) -> Result<Option<Vec<CodeLens>>> { |
687 | let file_id = params.text_document.try_conv_with(&world)?; | 688 | let file_id = params.text_document.try_conv_with(&world)?; |
688 | let line_index = world.analysis().file_line_index(file_id); | 689 | let analysis = world.analysis(); |
690 | let line_index = analysis.file_line_index(file_id); | ||
689 | 691 | ||
690 | let mut lenses: Vec<CodeLens> = Default::default(); | 692 | let mut lenses: Vec<CodeLens> = Default::default(); |
691 | let workspace_root = world.workspace_root_for(file_id); | 693 | let workspace_root = world.workspace_root_for(file_id); |
692 | 694 | ||
693 | // Gather runnables | 695 | // Gather runnables |
694 | for runnable in world.analysis().runnables(file_id)? { | 696 | for runnable in analysis.runnables(file_id)? { |
695 | let title = match &runnable.kind { | 697 | let title = match &runnable.kind { |
696 | RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => Some("▶️Run Test"), | 698 | RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => Some("▶️Run Test"), |
697 | RunnableKind::Bench { .. } => Some("Run Bench"), | 699 | RunnableKind::Bench { .. } => Some("Run Bench"), |
@@ -726,39 +728,51 @@ pub fn handle_code_lens( | |||
726 | } | 728 | } |
727 | } | 729 | } |
728 | 730 | ||
729 | lenses.extend(world.analysis().file_structure(file_id).into_iter().filter_map(|it| { | 731 | // Handle impls |
730 | match it.kind { | 732 | lenses.extend( |
731 | // Handle impls | 733 | analysis |
732 | SyntaxKind::TRAIT_DEF | SyntaxKind::STRUCT_DEF | SyntaxKind::ENUM_DEF => { | 734 | .file_structure(file_id) |
735 | .into_iter() | ||
736 | .filter(|it| match it.kind { | ||
737 | SyntaxKind::TRAIT_DEF | SyntaxKind::STRUCT_DEF | SyntaxKind::ENUM_DEF => true, | ||
738 | _ => false, | ||
739 | }) | ||
740 | .map(|it| { | ||
733 | let range = it.node_range.conv_with(&line_index); | 741 | let range = it.node_range.conv_with(&line_index); |
734 | let pos = range.start; | 742 | let pos = range.start; |
735 | let lens_params = | 743 | let lens_params = |
736 | req::TextDocumentPositionParams::new(params.text_document.clone(), pos); | 744 | req::TextDocumentPositionParams::new(params.text_document.clone(), pos); |
737 | Some(CodeLens { | 745 | CodeLens { |
738 | range, | 746 | range, |
739 | command: None, | 747 | command: None, |
740 | data: Some(to_value(CodeLensResolveData::Impls(lens_params)).unwrap()), | 748 | data: Some(to_value(CodeLensResolveData::Impls(lens_params)).unwrap()), |
741 | }) | 749 | } |
742 | } | 750 | }), |
743 | // handle let statements | 751 | ); |
744 | SyntaxKind::LET_STMT => world | 752 | |
745 | .analysis() | 753 | lenses.extend( |
746 | .type_of(FileRange { range: it.navigation_range, file_id }) | 754 | analysis |
747 | .ok() | 755 | .inlay_hints(file_id) |
748 | .and_then(std::convert::identity) | 756 | .into_iter() |
749 | .filter(|resolved_type| "{unknown}" != resolved_type) | 757 | .filter(|hint| hint.inlay_kind == InlayKind::LetBinding) |
750 | .map(|resolved_type| CodeLens { | 758 | .filter_map(|inlay_hint| { |
751 | range: it.node_range.conv_with(&line_index), | 759 | let resolved_type = analysis |
752 | command: Some(Command { | 760 | .type_of(FileRange { range: inlay_hint.range, file_id }) |
753 | title: resolved_type, | 761 | .ok() |
754 | command: String::new(), | 762 | .and_then(std::convert::identity) |
755 | arguments: None, | 763 | .filter(|resolved_type| "{unknown}" != resolved_type); |
756 | }), | 764 | resolved_type.map(|resolved_type| (resolved_type, inlay_hint.range)) |
757 | data: None, | 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, | ||
758 | }), | 772 | }), |
759 | _ => None, | 773 | data: None, |
760 | } | 774 | }), |
761 | })); | 775 | ); |
762 | Ok(Some(lenses)) | 776 | Ok(Some(lenses)) |
763 | } | 777 | } |
764 | 778 | ||