aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src
diff options
context:
space:
mode:
authorKirill Bulatov <[email protected]>2020-02-23 09:49:53 +0000
committerKirill Bulatov <[email protected]>2020-02-23 09:49:53 +0000
commitc200dba167c294fc7d9080945df9a01ae7461ca0 (patch)
tree6030d4f75e60600629a2bf116d56bdc7d982553f /crates/ra_ide/src
parent1651ce0ebe1cf1d51541a04457f72595d669c967 (diff)
Add basic parameter name hints heuristics
Diffstat (limited to 'crates/ra_ide/src')
-rw-r--r--crates/ra_ide/src/display/function_signature.rs16
-rw-r--r--crates/ra_ide/src/inlay_hints.rs212
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
43impl FunctionSignature { 45impl 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
145impl From<&'_ ast::FnDef> for FunctionSignature { 150impl 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
3use hir::{Function, HirDisplay, SourceAnalyzer, SourceBinder}; 3use hir::{HirDisplay, SourceAnalyzer, SourceBinder};
4use once_cell::unsync::Lazy; 4use once_cell::unsync::Lazy;
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_prof::profile; 6use 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
138fn 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
142fn get_fn_signature( 160fn 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#"
741enum CustomOption<T> {
742 None,
743 Some(T),
744}
745
746struct FileId {}
747struct SmolStr {}
748
749impl From<&str> for SmolStr {
750 fn from(_: &str) -> Self {
751 unimplemented!()
752 }
753}
754
755struct TextRange {}
756struct SyntaxKind {}
757struct NavigationTarget {}
758
726struct Test {} 759struct Test {}
727 760
728impl Test { 761impl 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
734fn test_func(mut foo: i32, bar: i32, msg: &str, _: i32, last: i32) -> i32 { 779fn 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#"
888fn map(f: i32) {}
889fn filter(predicate: i32) {}
890
891struct TestVarContainer {
892 test_var: i32,
893}
894
895struct Test {}
896
897impl 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
909fn 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}