diff options
author | Kirill Bulatov <[email protected]> | 2020-02-23 09:49:53 +0000 |
---|---|---|
committer | Kirill Bulatov <[email protected]> | 2020-02-23 09:49:53 +0000 |
commit | c200dba167c294fc7d9080945df9a01ae7461ca0 (patch) | |
tree | 6030d4f75e60600629a2bf116d56bdc7d982553f /crates/ra_ide/src | |
parent | 1651ce0ebe1cf1d51541a04457f72595d669c967 (diff) |
Add basic parameter name hints heuristics
Diffstat (limited to 'crates/ra_ide/src')
-rw-r--r-- | crates/ra_ide/src/display/function_signature.rs | 16 | ||||
-rw-r--r-- | crates/ra_ide/src/inlay_hints.rs | 212 |
2 files changed, 187 insertions, 41 deletions
diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs index b85fd8075..2c4c932de 100644 --- a/crates/ra_ide/src/display/function_signature.rs +++ b/crates/ra_ide/src/display/function_signature.rs | |||
@@ -38,6 +38,8 @@ pub struct FunctionSignature { | |||
38 | pub ret_type: Option<String>, | 38 | pub ret_type: Option<String>, |
39 | /// Where predicates | 39 | /// Where predicates |
40 | pub where_predicates: Vec<String>, | 40 | pub where_predicates: Vec<String>, |
41 | /// Self param presence | ||
42 | pub has_self_param: bool, | ||
41 | } | 43 | } |
42 | 44 | ||
43 | impl FunctionSignature { | 45 | impl FunctionSignature { |
@@ -78,6 +80,7 @@ impl FunctionSignature { | |||
78 | generic_parameters: generic_parameters(&node), | 80 | generic_parameters: generic_parameters(&node), |
79 | where_predicates: where_predicates(&node), | 81 | where_predicates: where_predicates(&node), |
80 | doc: None, | 82 | doc: None, |
83 | has_self_param: false, | ||
81 | } | 84 | } |
82 | .with_doc_opt(st.docs(db)), | 85 | .with_doc_opt(st.docs(db)), |
83 | ) | 86 | ) |
@@ -115,6 +118,7 @@ impl FunctionSignature { | |||
115 | generic_parameters: vec![], | 118 | generic_parameters: vec![], |
116 | where_predicates: vec![], | 119 | where_predicates: vec![], |
117 | doc: None, | 120 | doc: None, |
121 | has_self_param: false, | ||
118 | } | 122 | } |
119 | .with_doc_opt(variant.docs(db)), | 123 | .with_doc_opt(variant.docs(db)), |
120 | ) | 124 | ) |
@@ -136,6 +140,7 @@ impl FunctionSignature { | |||
136 | generic_parameters: vec![], | 140 | generic_parameters: vec![], |
137 | where_predicates: vec![], | 141 | where_predicates: vec![], |
138 | doc: None, | 142 | doc: None, |
143 | has_self_param: false, | ||
139 | } | 144 | } |
140 | .with_doc_opt(macro_def.docs(db)), | 145 | .with_doc_opt(macro_def.docs(db)), |
141 | ) | 146 | ) |
@@ -144,16 +149,18 @@ impl FunctionSignature { | |||
144 | 149 | ||
145 | impl From<&'_ ast::FnDef> for FunctionSignature { | 150 | impl From<&'_ ast::FnDef> for FunctionSignature { |
146 | fn from(node: &ast::FnDef) -> FunctionSignature { | 151 | fn from(node: &ast::FnDef) -> FunctionSignature { |
147 | fn param_list(node: &ast::FnDef) -> Vec<String> { | 152 | fn param_list(node: &ast::FnDef) -> (bool, Vec<String>) { |
148 | let mut res = vec![]; | 153 | let mut res = vec![]; |
154 | let mut has_self_param = false; | ||
149 | if let Some(param_list) = node.param_list() { | 155 | if let Some(param_list) = node.param_list() { |
150 | if let Some(self_param) = param_list.self_param() { | 156 | if let Some(self_param) = param_list.self_param() { |
157 | has_self_param = true; | ||
151 | res.push(self_param.syntax().text().to_string()) | 158 | res.push(self_param.syntax().text().to_string()) |
152 | } | 159 | } |
153 | 160 | ||
154 | res.extend(param_list.params().map(|param| param.syntax().text().to_string())); | 161 | res.extend(param_list.params().map(|param| param.syntax().text().to_string())); |
155 | } | 162 | } |
156 | res | 163 | (has_self_param, res) |
157 | } | 164 | } |
158 | 165 | ||
159 | fn param_name_list(node: &ast::FnDef) -> Vec<String> { | 166 | fn param_name_list(node: &ast::FnDef) -> Vec<String> { |
@@ -183,6 +190,8 @@ impl From<&'_ ast::FnDef> for FunctionSignature { | |||
183 | res | 190 | res |
184 | } | 191 | } |
185 | 192 | ||
193 | let (has_self_param, parameters) = param_list(node); | ||
194 | |||
186 | FunctionSignature { | 195 | FunctionSignature { |
187 | kind: CallableKind::Function, | 196 | kind: CallableKind::Function, |
188 | visibility: node.visibility().map(|n| n.syntax().text().to_string()), | 197 | visibility: node.visibility().map(|n| n.syntax().text().to_string()), |
@@ -191,12 +200,13 @@ impl From<&'_ ast::FnDef> for FunctionSignature { | |||
191 | .ret_type() | 200 | .ret_type() |
192 | .and_then(|r| r.type_ref()) | 201 | .and_then(|r| r.type_ref()) |
193 | .map(|n| n.syntax().text().to_string()), | 202 | .map(|n| n.syntax().text().to_string()), |
194 | parameters: param_list(node), | 203 | parameters, |
195 | parameter_names: param_name_list(node), | 204 | parameter_names: param_name_list(node), |
196 | generic_parameters: generic_parameters(node), | 205 | generic_parameters: generic_parameters(node), |
197 | where_predicates: where_predicates(node), | 206 | where_predicates: where_predicates(node), |
198 | // docs are processed separately | 207 | // docs are processed separately |
199 | doc: None, | 208 | doc: None, |
209 | has_self_param, | ||
200 | } | 210 | } |
201 | } | 211 | } |
202 | } | 212 | } |
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index 8d1c447ef..fb63fd1de 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{Function, HirDisplay, SourceAnalyzer, SourceBinder}; | 3 | use hir::{HirDisplay, SourceAnalyzer, SourceBinder}; |
4 | use once_cell::unsync::Lazy; | 4 | use once_cell::unsync::Lazy; |
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_prof::profile; | 6 | use ra_prof::profile; |
@@ -112,58 +112,73 @@ fn get_param_name_hints( | |||
112 | // we need args len to determine whether to skip or not the &self parameter | 112 | // we need args len to determine whether to skip or not the &self parameter |
113 | .collect::<Vec<_>>(); | 113 | .collect::<Vec<_>>(); |
114 | 114 | ||
115 | let (has_self_param, fn_signature) = get_fn_signature(db, analyzer, &expr)?; | 115 | let fn_signature = get_fn_signature(db, analyzer, &expr)?; |
116 | let parameters = if has_self_param && fn_signature.parameter_names.len() > args.len() { | 116 | let parameters = |
117 | fn_signature.parameter_names.into_iter().skip(1) | 117 | if fn_signature.has_self_param && fn_signature.parameter_names.len() > args.len() { |
118 | } else { | 118 | fn_signature.parameter_names.iter().skip(1) |
119 | fn_signature.parameter_names.into_iter().skip(0) | 119 | } else { |
120 | }; | 120 | fn_signature.parameter_names.iter().skip(0) |
121 | 121 | }; | |
122 | let hints = | 122 | |
123 | parameters | 123 | let hints = parameters |
124 | .zip(args) | 124 | .zip(args) |
125 | .filter_map(|(param, arg)| { | 125 | .filter(|(param, arg)| { |
126 | if !param.is_empty() { | 126 | should_show_param_hint(&fn_signature, param, &arg.syntax().to_string()) |
127 | Some((arg.syntax().text_range(), param)) | 127 | }) |
128 | } else { | 128 | .map(|(param_name, arg)| InlayHint { |
129 | None | 129 | range: arg.syntax().text_range(), |
130 | } | 130 | kind: InlayKind::ParameterHint, |
131 | }) | 131 | label: param_name.into(), |
132 | .map(|(range, param_name)| InlayHint { | 132 | }); |
133 | range, | ||
134 | kind: InlayKind::ParameterHint, | ||
135 | label: param_name.into(), | ||
136 | }); | ||
137 | 133 | ||
138 | acc.extend(hints); | 134 | acc.extend(hints); |
139 | Some(()) | 135 | Some(()) |
140 | } | 136 | } |
141 | 137 | ||
138 | fn should_show_param_hint( | ||
139 | fn_signature: &FunctionSignature, | ||
140 | param_name: &str, | ||
141 | argument_string: &str, | ||
142 | ) -> bool { | ||
143 | if param_name.is_empty() || argument_string.ends_with(param_name) { | ||
144 | return false; | ||
145 | } | ||
146 | |||
147 | let parameters_len = if fn_signature.has_self_param { | ||
148 | fn_signature.parameters.len() - 1 | ||
149 | } else { | ||
150 | fn_signature.parameters.len() | ||
151 | }; | ||
152 | // avoid displaying hints for common functions like map, filter, etc. | ||
153 | if parameters_len == 1 && (param_name.len() == 1 || param_name == "predicate") { | ||
154 | return false; | ||
155 | } | ||
156 | |||
157 | true | ||
158 | } | ||
159 | |||
142 | fn get_fn_signature( | 160 | fn get_fn_signature( |
143 | db: &RootDatabase, | 161 | db: &RootDatabase, |
144 | analyzer: &SourceAnalyzer, | 162 | analyzer: &SourceAnalyzer, |
145 | expr: &ast::Expr, | 163 | expr: &ast::Expr, |
146 | ) -> Option<(bool, FunctionSignature)> { | 164 | ) -> Option<FunctionSignature> { |
147 | match expr { | 165 | match expr { |
148 | ast::Expr::CallExpr(expr) => { | 166 | ast::Expr::CallExpr(expr) => { |
149 | // FIXME: Type::as_callable is broken for closures | 167 | // FIXME: Type::as_callable is broken for closures |
150 | let callable_def = analyzer.type_of(db, &expr.expr()?)?.as_callable()?; | 168 | let callable_def = analyzer.type_of(db, &expr.expr()?)?.as_callable()?; |
151 | match callable_def { | 169 | match callable_def { |
152 | hir::CallableDef::FunctionId(it) => { | 170 | hir::CallableDef::FunctionId(it) => { |
153 | let fn_def: Function = it.into(); | 171 | Some(FunctionSignature::from_hir(db, it.into())) |
154 | Some((fn_def.has_self_param(db), FunctionSignature::from_hir(db, fn_def))) | ||
155 | } | 172 | } |
156 | hir::CallableDef::StructId(it) => FunctionSignature::from_struct(db, it.into()) | 173 | hir::CallableDef::StructId(it) => FunctionSignature::from_struct(db, it.into()), |
157 | .map(|signature| (false, signature)), | ||
158 | hir::CallableDef::EnumVariantId(it) => { | 174 | hir::CallableDef::EnumVariantId(it) => { |
159 | FunctionSignature::from_enum_variant(db, it.into()) | 175 | FunctionSignature::from_enum_variant(db, it.into()) |
160 | .map(|signature| (false, signature)) | ||
161 | } | 176 | } |
162 | } | 177 | } |
163 | } | 178 | } |
164 | ast::Expr::MethodCallExpr(expr) => { | 179 | ast::Expr::MethodCallExpr(expr) => { |
165 | let fn_def = analyzer.resolve_method_call(&expr)?; | 180 | let fn_def = analyzer.resolve_method_call(&expr)?; |
166 | Some((fn_def.has_self_param(db), FunctionSignature::from_hir(db, fn_def))) | 181 | Some(FunctionSignature::from_hir(db, fn_def)) |
167 | } | 182 | } |
168 | _ => None, | 183 | _ => None, |
169 | } | 184 | } |
@@ -723,12 +738,42 @@ fn main() { | |||
723 | fn function_call_parameter_hint() { | 738 | fn function_call_parameter_hint() { |
724 | let (analysis, file_id) = single_file( | 739 | let (analysis, file_id) = single_file( |
725 | r#" | 740 | r#" |
741 | enum CustomOption<T> { | ||
742 | None, | ||
743 | Some(T), | ||
744 | } | ||
745 | |||
746 | struct FileId {} | ||
747 | struct SmolStr {} | ||
748 | |||
749 | impl From<&str> for SmolStr { | ||
750 | fn from(_: &str) -> Self { | ||
751 | unimplemented!() | ||
752 | } | ||
753 | } | ||
754 | |||
755 | struct TextRange {} | ||
756 | struct SyntaxKind {} | ||
757 | struct NavigationTarget {} | ||
758 | |||
726 | struct Test {} | 759 | struct Test {} |
727 | 760 | ||
728 | impl Test { | 761 | impl Test { |
729 | fn method(&self, mut param: i32) -> i32 { | 762 | fn method(&self, mut param: i32) -> i32 { |
730 | param * 2 | 763 | param * 2 |
731 | } | 764 | } |
765 | |||
766 | fn from_syntax( | ||
767 | file_id: FileId, | ||
768 | name: SmolStr, | ||
769 | focus_range: CustomOption<TextRange>, | ||
770 | full_range: TextRange, | ||
771 | kind: SyntaxKind, | ||
772 | docs: CustomOption<String>, | ||
773 | description: CustomOption<String>, | ||
774 | ) -> NavigationTarget { | ||
775 | NavigationTarget {} | ||
776 | } | ||
732 | } | 777 | } |
733 | 778 | ||
734 | fn test_func(mut foo: i32, bar: i32, msg: &str, _: i32, last: i32) -> i32 { | 779 | fn test_func(mut foo: i32, bar: i32, msg: &str, _: i32, last: i32) -> i32 { |
@@ -741,53 +786,144 @@ fn main() { | |||
741 | let t: Test = Test {}; | 786 | let t: Test = Test {}; |
742 | t.method(123); | 787 | t.method(123); |
743 | Test::method(&t, 3456); | 788 | Test::method(&t, 3456); |
789 | |||
790 | Test::from_syntax( | ||
791 | FileId {}, | ||
792 | "impl".into(), | ||
793 | CustomOption::None, | ||
794 | TextRange {}, | ||
795 | SyntaxKind {}, | ||
796 | CustomOption::None, | ||
797 | CustomOption::None, | ||
798 | ); | ||
744 | }"#, | 799 | }"#, |
745 | ); | 800 | ); |
746 | 801 | ||
747 | assert_debug_snapshot!(analysis.inlay_hints(file_id, None).unwrap(), @r###" | 802 | assert_debug_snapshot!(analysis.inlay_hints(file_id, None).unwrap(), @r###" |
748 | [ | 803 | [ |
749 | InlayHint { | 804 | InlayHint { |
750 | range: [215; 226), | 805 | range: [777; 788), |
751 | kind: TypeHint, | 806 | kind: TypeHint, |
752 | label: "i32", | 807 | label: "i32", |
753 | }, | 808 | }, |
754 | InlayHint { | 809 | InlayHint { |
755 | range: [259; 260), | 810 | range: [821; 822), |
756 | kind: ParameterHint, | 811 | kind: ParameterHint, |
757 | label: "foo", | 812 | label: "foo", |
758 | }, | 813 | }, |
759 | InlayHint { | 814 | InlayHint { |
760 | range: [262; 263), | 815 | range: [824; 825), |
761 | kind: ParameterHint, | 816 | kind: ParameterHint, |
762 | label: "bar", | 817 | label: "bar", |
763 | }, | 818 | }, |
764 | InlayHint { | 819 | InlayHint { |
765 | range: [265; 272), | 820 | range: [827; 834), |
766 | kind: ParameterHint, | 821 | kind: ParameterHint, |
767 | label: "msg", | 822 | label: "msg", |
768 | }, | 823 | }, |
769 | InlayHint { | 824 | InlayHint { |
770 | range: [277; 288), | 825 | range: [839; 850), |
771 | kind: ParameterHint, | 826 | kind: ParameterHint, |
772 | label: "last", | 827 | label: "last", |
773 | }, | 828 | }, |
774 | InlayHint { | 829 | InlayHint { |
775 | range: [331; 334), | 830 | range: [893; 896), |
776 | kind: ParameterHint, | 831 | kind: ParameterHint, |
777 | label: "param", | 832 | label: "param", |
778 | }, | 833 | }, |
779 | InlayHint { | 834 | InlayHint { |
780 | range: [354; 356), | 835 | range: [916; 918), |
781 | kind: ParameterHint, | 836 | kind: ParameterHint, |
782 | label: "&self", | 837 | label: "&self", |
783 | }, | 838 | }, |
784 | InlayHint { | 839 | InlayHint { |
785 | range: [358; 362), | 840 | range: [920; 924), |
786 | kind: ParameterHint, | 841 | kind: ParameterHint, |
787 | label: "param", | 842 | label: "param", |
788 | }, | 843 | }, |
844 | InlayHint { | ||
845 | range: [959; 968), | ||
846 | kind: ParameterHint, | ||
847 | label: "file_id", | ||
848 | }, | ||
849 | InlayHint { | ||
850 | range: [978; 991), | ||
851 | kind: ParameterHint, | ||
852 | label: "name", | ||
853 | }, | ||
854 | InlayHint { | ||
855 | range: [1001; 1019), | ||
856 | kind: ParameterHint, | ||
857 | label: "focus_range", | ||
858 | }, | ||
859 | InlayHint { | ||
860 | range: [1029; 1041), | ||
861 | kind: ParameterHint, | ||
862 | label: "full_range", | ||
863 | }, | ||
864 | InlayHint { | ||
865 | range: [1051; 1064), | ||
866 | kind: ParameterHint, | ||
867 | label: "kind", | ||
868 | }, | ||
869 | InlayHint { | ||
870 | range: [1074; 1092), | ||
871 | kind: ParameterHint, | ||
872 | label: "docs", | ||
873 | }, | ||
874 | InlayHint { | ||
875 | range: [1102; 1120), | ||
876 | kind: ParameterHint, | ||
877 | label: "description", | ||
878 | }, | ||
789 | ] | 879 | ] |
790 | "### | 880 | "### |
791 | ); | 881 | ); |
792 | } | 882 | } |
883 | |||
884 | #[test] | ||
885 | fn omitted_parameters_hints_heuristics() { | ||
886 | let (analysis, file_id) = single_file( | ||
887 | r#" | ||
888 | fn map(f: i32) {} | ||
889 | fn filter(predicate: i32) {} | ||
890 | |||
891 | struct TestVarContainer { | ||
892 | test_var: i32, | ||
893 | } | ||
894 | |||
895 | struct Test {} | ||
896 | |||
897 | impl Test { | ||
898 | fn map(self, f: i32) -> Self { | ||
899 | self | ||
900 | } | ||
901 | |||
902 | fn filter(self, predicate: i32) -> Self { | ||
903 | self | ||
904 | } | ||
905 | |||
906 | fn no_hints_expected(&self, _: i32, test_var: i32) {} | ||
907 | } | ||
908 | |||
909 | fn main() { | ||
910 | let container: TestVarContainer = TestVarContainer { test_var: 42 }; | ||
911 | let test: Test = Test {}; | ||
912 | |||
913 | map(22); | ||
914 | filter(33); | ||
915 | |||
916 | let test_processed: Test = test.map(1).filter(2); | ||
917 | |||
918 | let test_var: i32 = 55; | ||
919 | test_processed.no_hints_expected(22, test_var); | ||
920 | test_processed.no_hints_expected(33, container.test_var); | ||
921 | }"#, | ||
922 | ); | ||
923 | |||
924 | assert_debug_snapshot!(analysis.inlay_hints(file_id, Some(8)).unwrap(), @r###" | ||
925 | [] | ||
926 | "### | ||
927 | ); | ||
928 | } | ||
793 | } | 929 | } |