diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-02-23 10:09:35 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2020-02-23 10:09:35 +0000 |
commit | 838ad6bcfb2a82c030e18d019b8a06752f0fc828 (patch) | |
tree | 8291c9a198751b4c31e4de0dbf6cb3d7b69c723c /crates/ra_ide/src/inlay_hints.rs | |
parent | 58d44c6ba2de32a31a09bbcaf61365a69b69374c (diff) | |
parent | b2a7b29bb9e195e75ad04dc69c572d02f98fe5ce (diff) |
Merge #3279
3279: Add basic parameter name hints heuristics r=matklad a=SomeoneToIgnore
Co-authored-by: Kirill Bulatov <[email protected]>
Diffstat (limited to 'crates/ra_ide/src/inlay_hints.rs')
-rw-r--r-- | crates/ra_ide/src/inlay_hints.rs | 213 |
1 files changed, 175 insertions, 38 deletions
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index 8d1c447ef..a484dfdeb 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,74 @@ 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 n_params_to_skip = |
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 | 1 |
119 | fn_signature.parameter_names.into_iter().skip(0) | 119 | } else { |
120 | }; | 120 | 0 |
121 | 121 | }; | |
122 | let hints = | 122 | let parameters = fn_signature.parameter_names.iter().skip(n_params_to_skip); |
123 | parameters | 123 | |
124 | .zip(args) | 124 | let hints = parameters |
125 | .filter_map(|(param, arg)| { | 125 | .zip(args) |
126 | if !param.is_empty() { | 126 | .filter(|(param, arg)| { |
127 | Some((arg.syntax().text_range(), param)) | 127 | should_show_param_hint(&fn_signature, param, &arg.syntax().to_string()) |
128 | } else { | 128 | }) |
129 | None | 129 | .map(|(param_name, arg)| InlayHint { |
130 | } | 130 | range: arg.syntax().text_range(), |
131 | }) | 131 | kind: InlayKind::ParameterHint, |
132 | .map(|(range, param_name)| InlayHint { | 132 | label: param_name.into(), |
133 | range, | 133 | }); |
134 | kind: InlayKind::ParameterHint, | ||
135 | label: param_name.into(), | ||
136 | }); | ||
137 | 134 | ||
138 | acc.extend(hints); | 135 | acc.extend(hints); |
139 | Some(()) | 136 | Some(()) |
140 | } | 137 | } |
141 | 138 | ||
139 | fn should_show_param_hint( | ||
140 | fn_signature: &FunctionSignature, | ||
141 | param_name: &str, | ||
142 | argument_string: &str, | ||
143 | ) -> bool { | ||
144 | if param_name.is_empty() || argument_string.ends_with(param_name) { | ||
145 | return false; | ||
146 | } | ||
147 | |||
148 | let parameters_len = if fn_signature.has_self_param { | ||
149 | fn_signature.parameters.len() - 1 | ||
150 | } else { | ||
151 | fn_signature.parameters.len() | ||
152 | }; | ||
153 | // avoid displaying hints for common functions like map, filter, etc. | ||
154 | if parameters_len == 1 && (param_name.len() == 1 || param_name == "predicate") { | ||
155 | return false; | ||
156 | } | ||
157 | |||
158 | true | ||
159 | } | ||
160 | |||
142 | fn get_fn_signature( | 161 | fn get_fn_signature( |
143 | db: &RootDatabase, | 162 | db: &RootDatabase, |
144 | analyzer: &SourceAnalyzer, | 163 | analyzer: &SourceAnalyzer, |
145 | expr: &ast::Expr, | 164 | expr: &ast::Expr, |
146 | ) -> Option<(bool, FunctionSignature)> { | 165 | ) -> Option<FunctionSignature> { |
147 | match expr { | 166 | match expr { |
148 | ast::Expr::CallExpr(expr) => { | 167 | ast::Expr::CallExpr(expr) => { |
149 | // FIXME: Type::as_callable is broken for closures | 168 | // FIXME: Type::as_callable is broken for closures |
150 | let callable_def = analyzer.type_of(db, &expr.expr()?)?.as_callable()?; | 169 | let callable_def = analyzer.type_of(db, &expr.expr()?)?.as_callable()?; |
151 | match callable_def { | 170 | match callable_def { |
152 | hir::CallableDef::FunctionId(it) => { | 171 | hir::CallableDef::FunctionId(it) => { |
153 | let fn_def: Function = it.into(); | 172 | Some(FunctionSignature::from_hir(db, it.into())) |
154 | Some((fn_def.has_self_param(db), FunctionSignature::from_hir(db, fn_def))) | ||
155 | } | 173 | } |
156 | hir::CallableDef::StructId(it) => FunctionSignature::from_struct(db, it.into()) | 174 | hir::CallableDef::StructId(it) => FunctionSignature::from_struct(db, it.into()), |
157 | .map(|signature| (false, signature)), | ||
158 | hir::CallableDef::EnumVariantId(it) => { | 175 | hir::CallableDef::EnumVariantId(it) => { |
159 | FunctionSignature::from_enum_variant(db, it.into()) | 176 | FunctionSignature::from_enum_variant(db, it.into()) |
160 | .map(|signature| (false, signature)) | ||
161 | } | 177 | } |
162 | } | 178 | } |
163 | } | 179 | } |
164 | ast::Expr::MethodCallExpr(expr) => { | 180 | ast::Expr::MethodCallExpr(expr) => { |
165 | let fn_def = analyzer.resolve_method_call(&expr)?; | 181 | let fn_def = analyzer.resolve_method_call(&expr)?; |
166 | Some((fn_def.has_self_param(db), FunctionSignature::from_hir(db, fn_def))) | 182 | Some(FunctionSignature::from_hir(db, fn_def)) |
167 | } | 183 | } |
168 | _ => None, | 184 | _ => None, |
169 | } | 185 | } |
@@ -723,12 +739,42 @@ fn main() { | |||
723 | fn function_call_parameter_hint() { | 739 | fn function_call_parameter_hint() { |
724 | let (analysis, file_id) = single_file( | 740 | let (analysis, file_id) = single_file( |
725 | r#" | 741 | r#" |
742 | enum CustomOption<T> { | ||
743 | None, | ||
744 | Some(T), | ||
745 | } | ||
746 | |||
747 | struct FileId {} | ||
748 | struct SmolStr {} | ||
749 | |||
750 | impl From<&str> for SmolStr { | ||
751 | fn from(_: &str) -> Self { | ||
752 | unimplemented!() | ||
753 | } | ||
754 | } | ||
755 | |||
756 | struct TextRange {} | ||
757 | struct SyntaxKind {} | ||
758 | struct NavigationTarget {} | ||
759 | |||
726 | struct Test {} | 760 | struct Test {} |
727 | 761 | ||
728 | impl Test { | 762 | impl Test { |
729 | fn method(&self, mut param: i32) -> i32 { | 763 | fn method(&self, mut param: i32) -> i32 { |
730 | param * 2 | 764 | param * 2 |
731 | } | 765 | } |
766 | |||
767 | fn from_syntax( | ||
768 | file_id: FileId, | ||
769 | name: SmolStr, | ||
770 | focus_range: CustomOption<TextRange>, | ||
771 | full_range: TextRange, | ||
772 | kind: SyntaxKind, | ||
773 | docs: CustomOption<String>, | ||
774 | description: CustomOption<String>, | ||
775 | ) -> NavigationTarget { | ||
776 | NavigationTarget {} | ||
777 | } | ||
732 | } | 778 | } |
733 | 779 | ||
734 | fn test_func(mut foo: i32, bar: i32, msg: &str, _: i32, last: i32) -> i32 { | 780 | fn test_func(mut foo: i32, bar: i32, msg: &str, _: i32, last: i32) -> i32 { |
@@ -741,53 +787,144 @@ fn main() { | |||
741 | let t: Test = Test {}; | 787 | let t: Test = Test {}; |
742 | t.method(123); | 788 | t.method(123); |
743 | Test::method(&t, 3456); | 789 | Test::method(&t, 3456); |
790 | |||
791 | Test::from_syntax( | ||
792 | FileId {}, | ||
793 | "impl".into(), | ||
794 | CustomOption::None, | ||
795 | TextRange {}, | ||
796 | SyntaxKind {}, | ||
797 | CustomOption::None, | ||
798 | CustomOption::None, | ||
799 | ); | ||
744 | }"#, | 800 | }"#, |
745 | ); | 801 | ); |
746 | 802 | ||
747 | assert_debug_snapshot!(analysis.inlay_hints(file_id, None).unwrap(), @r###" | 803 | assert_debug_snapshot!(analysis.inlay_hints(file_id, None).unwrap(), @r###" |
748 | [ | 804 | [ |
749 | InlayHint { | 805 | InlayHint { |
750 | range: [215; 226), | 806 | range: [777; 788), |
751 | kind: TypeHint, | 807 | kind: TypeHint, |
752 | label: "i32", | 808 | label: "i32", |
753 | }, | 809 | }, |
754 | InlayHint { | 810 | InlayHint { |
755 | range: [259; 260), | 811 | range: [821; 822), |
756 | kind: ParameterHint, | 812 | kind: ParameterHint, |
757 | label: "foo", | 813 | label: "foo", |
758 | }, | 814 | }, |
759 | InlayHint { | 815 | InlayHint { |
760 | range: [262; 263), | 816 | range: [824; 825), |
761 | kind: ParameterHint, | 817 | kind: ParameterHint, |
762 | label: "bar", | 818 | label: "bar", |
763 | }, | 819 | }, |
764 | InlayHint { | 820 | InlayHint { |
765 | range: [265; 272), | 821 | range: [827; 834), |
766 | kind: ParameterHint, | 822 | kind: ParameterHint, |
767 | label: "msg", | 823 | label: "msg", |
768 | }, | 824 | }, |
769 | InlayHint { | 825 | InlayHint { |
770 | range: [277; 288), | 826 | range: [839; 850), |
771 | kind: ParameterHint, | 827 | kind: ParameterHint, |
772 | label: "last", | 828 | label: "last", |
773 | }, | 829 | }, |
774 | InlayHint { | 830 | InlayHint { |
775 | range: [331; 334), | 831 | range: [893; 896), |
776 | kind: ParameterHint, | 832 | kind: ParameterHint, |
777 | label: "param", | 833 | label: "param", |
778 | }, | 834 | }, |
779 | InlayHint { | 835 | InlayHint { |
780 | range: [354; 356), | 836 | range: [916; 918), |
781 | kind: ParameterHint, | 837 | kind: ParameterHint, |
782 | label: "&self", | 838 | label: "&self", |
783 | }, | 839 | }, |
784 | InlayHint { | 840 | InlayHint { |
785 | range: [358; 362), | 841 | range: [920; 924), |
786 | kind: ParameterHint, | 842 | kind: ParameterHint, |
787 | label: "param", | 843 | label: "param", |
788 | }, | 844 | }, |
845 | InlayHint { | ||
846 | range: [959; 968), | ||
847 | kind: ParameterHint, | ||
848 | label: "file_id", | ||
849 | }, | ||
850 | InlayHint { | ||
851 | range: [978; 991), | ||
852 | kind: ParameterHint, | ||
853 | label: "name", | ||
854 | }, | ||
855 | InlayHint { | ||
856 | range: [1001; 1019), | ||
857 | kind: ParameterHint, | ||
858 | label: "focus_range", | ||
859 | }, | ||
860 | InlayHint { | ||
861 | range: [1029; 1041), | ||
862 | kind: ParameterHint, | ||
863 | label: "full_range", | ||
864 | }, | ||
865 | InlayHint { | ||
866 | range: [1051; 1064), | ||
867 | kind: ParameterHint, | ||
868 | label: "kind", | ||
869 | }, | ||
870 | InlayHint { | ||
871 | range: [1074; 1092), | ||
872 | kind: ParameterHint, | ||
873 | label: "docs", | ||
874 | }, | ||
875 | InlayHint { | ||
876 | range: [1102; 1120), | ||
877 | kind: ParameterHint, | ||
878 | label: "description", | ||
879 | }, | ||
789 | ] | 880 | ] |
790 | "### | 881 | "### |
791 | ); | 882 | ); |
792 | } | 883 | } |
884 | |||
885 | #[test] | ||
886 | fn omitted_parameters_hints_heuristics() { | ||
887 | let (analysis, file_id) = single_file( | ||
888 | r#" | ||
889 | fn map(f: i32) {} | ||
890 | fn filter(predicate: i32) {} | ||
891 | |||
892 | struct TestVarContainer { | ||
893 | test_var: i32, | ||
894 | } | ||
895 | |||
896 | struct Test {} | ||
897 | |||
898 | impl Test { | ||
899 | fn map(self, f: i32) -> Self { | ||
900 | self | ||
901 | } | ||
902 | |||
903 | fn filter(self, predicate: i32) -> Self { | ||
904 | self | ||
905 | } | ||
906 | |||
907 | fn no_hints_expected(&self, _: i32, test_var: i32) {} | ||
908 | } | ||
909 | |||
910 | fn main() { | ||
911 | let container: TestVarContainer = TestVarContainer { test_var: 42 }; | ||
912 | let test: Test = Test {}; | ||
913 | |||
914 | map(22); | ||
915 | filter(33); | ||
916 | |||
917 | let test_processed: Test = test.map(1).filter(2); | ||
918 | |||
919 | let test_var: i32 = 55; | ||
920 | test_processed.no_hints_expected(22, test_var); | ||
921 | test_processed.no_hints_expected(33, container.test_var); | ||
922 | }"#, | ||
923 | ); | ||
924 | |||
925 | assert_debug_snapshot!(analysis.inlay_hints(file_id, Some(8)).unwrap(), @r###" | ||
926 | [] | ||
927 | "### | ||
928 | ); | ||
929 | } | ||
793 | } | 930 | } |