aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/inlay_hints.rs
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-02-23 10:09:35 +0000
committerGitHub <[email protected]>2020-02-23 10:09:35 +0000
commit838ad6bcfb2a82c030e18d019b8a06752f0fc828 (patch)
tree8291c9a198751b4c31e4de0dbf6cb3d7b69c723c /crates/ra_ide/src/inlay_hints.rs
parent58d44c6ba2de32a31a09bbcaf61365a69b69374c (diff)
parentb2a7b29bb9e195e75ad04dc69c572d02f98fe5ce (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.rs213
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
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,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
139fn 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
142fn get_fn_signature( 161fn 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#"
742enum CustomOption<T> {
743 None,
744 Some(T),
745}
746
747struct FileId {}
748struct SmolStr {}
749
750impl From<&str> for SmolStr {
751 fn from(_: &str) -> Self {
752 unimplemented!()
753 }
754}
755
756struct TextRange {}
757struct SyntaxKind {}
758struct NavigationTarget {}
759
726struct Test {} 760struct Test {}
727 761
728impl Test { 762impl 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
734fn test_func(mut foo: i32, bar: i32, msg: &str, _: i32, last: i32) -> i32 { 780fn 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#"
889fn map(f: i32) {}
890fn filter(predicate: i32) {}
891
892struct TestVarContainer {
893 test_var: i32,
894}
895
896struct Test {}
897
898impl 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
910fn 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}