aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide
diff options
context:
space:
mode:
authorBenjamin Coenen <[email protected]>2020-04-11 21:54:22 +0100
committerBenjamin Coenen <[email protected]>2020-04-11 22:45:09 +0100
commit93bfc2d05d36a47dc05a1799210327473d702dbc (patch)
treedee25e78b24b5d1b23d73ae1009bddbd060927cf /crates/ra_ide
parentd42346fed61f706d68fe888631a41ea5f2752d7f (diff)
parentfd06fe7b13045185ab4e630b0044aa9d8bbcdf8a (diff)
Improve autocompletion by looking on the type and name
Signed-off-by: Benjamin Coenen <[email protected]>
Diffstat (limited to 'crates/ra_ide')
-rw-r--r--crates/ra_ide/src/completion/complete_dot.rs1
-rw-r--r--crates/ra_ide/src/completion/complete_fn_param.rs30
-rw-r--r--crates/ra_ide/src/completion/complete_trait_impl.rs4
-rw-r--r--crates/ra_ide/src/completion/complete_unqualified_path.rs2
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs13
-rw-r--r--crates/ra_ide/src/display/function_signature.rs21
-rw-r--r--crates/ra_ide/src/inlay_hints.rs61
-rw-r--r--crates/ra_ide/src/references.rs2
-rw-r--r--crates/ra_ide/src/syntax_tree.rs8
-rw-r--r--crates/ra_ide/src/typing.rs2
10 files changed, 103 insertions, 41 deletions
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs
index 358b041aa..b5448af5c 100644
--- a/crates/ra_ide/src/completion/complete_dot.rs
+++ b/crates/ra_ide/src/completion/complete_dot.rs
@@ -72,7 +72,6 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Ty
72 } 72 }
73 for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() { 73 for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() {
74 // FIXME: Handle visibility 74 // FIXME: Handle visibility
75 // TODO: add the same behavior with type ?
76 acc.add_tuple_field(ctx, i, &ty); 75 acc.add_tuple_field(ctx, i, &ty);
77 } 76 }
78 } 77 }
diff --git a/crates/ra_ide/src/completion/complete_fn_param.rs b/crates/ra_ide/src/completion/complete_fn_param.rs
index 62ae5ccb4..f84b559fc 100644
--- a/crates/ra_ide/src/completion/complete_fn_param.rs
+++ b/crates/ra_ide/src/completion/complete_fn_param.rs
@@ -1,6 +1,9 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use ra_syntax::{ast, match_ast, AstNode}; 3use ra_syntax::{
4 ast::{self, ModuleItemOwner},
5 match_ast, AstNode,
6};
4use rustc_hash::FxHashMap; 7use rustc_hash::FxHashMap;
5 8
6use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions}; 9use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions};
@@ -16,11 +19,19 @@ pub(super) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext)
16 19
17 let mut params = FxHashMap::default(); 20 let mut params = FxHashMap::default();
18 for node in ctx.token.parent().ancestors() { 21 for node in ctx.token.parent().ancestors() {
19 match_ast! { 22 let items = match_ast! {
20 match node { 23 match node {
21 ast::SourceFile(it) => process(it, &mut params), 24 ast::SourceFile(it) => it.items(),
22 ast::ItemList(it) => process(it, &mut params), 25 ast::ItemList(it) => it.items(),
23 _ => (), 26 _ => continue,
27 }
28 };
29 for item in items {
30 if let ast::ModuleItem::FnDef(func) = item {
31 func.param_list().into_iter().flat_map(|it| it.params()).for_each(|param| {
32 let text = param.syntax().text().to_string();
33 params.entry(text).or_insert((0, param)).0 += 1;
34 })
24 } 35 }
25 } 36 }
26 } 37 }
@@ -39,15 +50,6 @@ pub(super) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext)
39 .lookup_by(lookup) 50 .lookup_by(lookup)
40 .add_to(acc) 51 .add_to(acc)
41 }); 52 });
42
43 fn process<N: ast::FnDefOwner>(node: N, params: &mut FxHashMap<String, (u32, ast::Param)>) {
44 node.functions().filter_map(|it| it.param_list()).flat_map(|it| it.params()).for_each(
45 |param| {
46 let text = param.syntax().text().to_string();
47 params.entry(text).or_insert((0, param)).0 += 1;
48 },
49 )
50 }
51} 53}
52 54
53#[cfg(test)] 55#[cfg(test)]
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs
index ded1ff3bc..fab02945c 100644
--- a/crates/ra_ide/src/completion/complete_trait_impl.rs
+++ b/crates/ra_ide/src/completion/complete_trait_impl.rs
@@ -35,7 +35,7 @@ use hir::{self, Docs, HasSource};
35use ra_assists::utils::get_missing_impl_items; 35use ra_assists::utils::get_missing_impl_items;
36use ra_syntax::{ 36use ra_syntax::{
37 ast::{self, edit, ImplDef}, 37 ast::{self, edit, ImplDef},
38 AstNode, SyntaxKind, SyntaxNode, TextRange, 38 AstNode, SyntaxKind, SyntaxNode, TextRange, T,
39}; 39};
40use ra_text_edit::TextEdit; 40use ra_text_edit::TextEdit;
41 41
@@ -204,7 +204,7 @@ fn make_const_compl_syntax(const_: &ast::ConstDef) -> String {
204 let end = const_ 204 let end = const_
205 .syntax() 205 .syntax()
206 .children_with_tokens() 206 .children_with_tokens()
207 .find(|s| s.kind() == SyntaxKind::SEMI || s.kind() == SyntaxKind::EQ) 207 .find(|s| s.kind() == T![;] || s.kind() == T![=])
208 .map_or(const_end, |f| f.text_range().start()); 208 .map_or(const_end, |f| f.text_range().start());
209 209
210 let len = end - start; 210 let len = end - start;
diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs
index efde9bf73..0b0da6ee4 100644
--- a/crates/ra_ide/src/completion/complete_unqualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs
@@ -3,7 +3,7 @@
3use crate::completion::{CompletionContext, Completions}; 3use crate::completion::{CompletionContext, Completions};
4 4
5pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { 5pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
6 if !(ctx.is_trivial_path && !ctx.is_pat_binding_or_const) { 6 if !(ctx.is_trivial_path && !ctx.is_pat_binding_or_const && !ctx.record_lit_syntax.is_some()) {
7 return; 7 return;
8 } 8 }
9 9
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index fddaf21e4..eb8016dd1 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -1,13 +1,11 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::{db::HirDatabase, Semantics, SemanticsScope}; 3use hir::{Semantics, SemanticsScope};
4use ra_db::SourceDatabase; 4use ra_db::SourceDatabase;
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::{ 6use ra_syntax::{
7 algo::{find_covering_element, find_node_at_offset}, 7 algo::{find_covering_element, find_node_at_offset},
8 ast, 8 ast, AstNode,
9 ast::ArgListOwner,
10 AstNode,
11 SyntaxKind::*, 9 SyntaxKind::*,
12 SyntaxNode, SyntaxToken, TextRange, TextUnit, 10 SyntaxNode, SyntaxToken, TextRange, TextUnit,
13}; 11};
@@ -196,7 +194,10 @@ impl<'a> CompletionContext<'a> {
196 if let Some(name) = find_node_at_offset::<ast::Name>(&file_with_fake_ident, offset) { 194 if let Some(name) = find_node_at_offset::<ast::Name>(&file_with_fake_ident, offset) {
197 if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) { 195 if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) {
198 self.is_pat_binding_or_const = true; 196 self.is_pat_binding_or_const = true;
199 if bind_pat.has_at() || bind_pat.is_ref() || bind_pat.is_mutable() { 197 if bind_pat.at_token().is_some()
198 || bind_pat.ref_token().is_some()
199 || bind_pat.mut_token().is_some()
200 {
200 self.is_pat_binding_or_const = false; 201 self.is_pat_binding_or_const = false;
201 } 202 }
202 if bind_pat.syntax().parent().and_then(ast::RecordFieldPatList::cast).is_some() { 203 if bind_pat.syntax().parent().and_then(ast::RecordFieldPatList::cast).is_some() {
@@ -230,7 +231,7 @@ impl<'a> CompletionContext<'a> {
230 self.name_ref_syntax = 231 self.name_ref_syntax =
231 find_node_at_offset(&original_file, name_ref.syntax().text_range().start()); 232 find_node_at_offset(&original_file, name_ref.syntax().text_range().start());
232 let name_range = name_ref.syntax().text_range(); 233 let name_range = name_ref.syntax().text_range();
233 if name_ref.syntax().parent().and_then(ast::RecordField::cast).is_some() { 234 if ast::RecordField::for_field_name(&name_ref).is_some() {
234 self.record_lit_syntax = 235 self.record_lit_syntax =
235 self.sema.find_node_at_offset_with_macros(&original_file, offset); 236 self.sema.find_node_at_offset_with_macros(&original_file, offset);
236 } 237 }
diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs
index e58a78271..2d175882b 100644
--- a/crates/ra_ide/src/display/function_signature.rs
+++ b/crates/ra_ide/src/display/function_signature.rs
@@ -69,7 +69,13 @@ impl FunctionSignature {
69 for field in st.fields(db).into_iter() { 69 for field in st.fields(db).into_iter() {
70 let ty = field.signature_ty(db); 70 let ty = field.signature_ty(db);
71 let raw_param = format!("{}", ty.display(db)); 71 let raw_param = format!("{}", ty.display(db));
72 parameter_types.push(raw_param.split(':').nth(1).unwrap()[1..].to_string()); 72
73 if let Some(param_type) = raw_param.split(':').nth(1) {
74 parameter_types.push(param_type[1..].to_string());
75 } else {
76 // The unwrap_or_else is useful when you have tuple struct
77 parameter_types.push(raw_param.clone());
78 }
73 params.push(raw_param); 79 params.push(raw_param);
74 } 80 }
75 81
@@ -107,8 +113,15 @@ impl FunctionSignature {
107 for field in variant.fields(db).into_iter() { 113 for field in variant.fields(db).into_iter() {
108 let ty = field.signature_ty(db); 114 let ty = field.signature_ty(db);
109 let raw_param = format!("{}", ty.display(db)); 115 let raw_param = format!("{}", ty.display(db));
110 parameter_types.push(raw_param.split(':').nth(1).unwrap()[1..].to_string()); 116 if let Some(param_type) = raw_param.split(':').nth(1) {
111 params.push(raw_param); 117 parameter_types.push(param_type[1..].to_string());
118 } else {
119 // The unwrap_or_else is useful when you have tuple
120 parameter_types.push(raw_param);
121 }
122 let name = field.name(db);
123
124 params.push(format!("{}: {}", name, ty.display(db)));
112 } 125 }
113 126
114 Some( 127 Some(
@@ -164,7 +177,7 @@ impl From<&'_ ast::FnDef> for FunctionSignature {
164 has_self_param = true; 177 has_self_param = true;
165 let raw_param = self_param.syntax().text().to_string(); 178 let raw_param = self_param.syntax().text().to_string();
166 179
167 // TODO: better solution ? 180 // FIXME: better solution ?
168 res_types.push( 181 res_types.push(
169 raw_param.split(':').nth(1).unwrap_or_else(|| " Self")[1..].to_string(), 182 raw_param.split(':').nth(1).unwrap_or_else(|| " Self")[1..].to_string(),
170 ); 183 );
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index 4b133b19b..45b9f7802 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -1,4 +1,4 @@
1//! FIXME: write short doc here 1//! This module defines multiple types of inlay hints and their visibility
2 2
3use hir::{Adt, HirDisplay, Semantics, Type}; 3use hir::{Adt, HirDisplay, Semantics, Type};
4use ra_ide_db::RootDatabase; 4use ra_ide_db::RootDatabase;
@@ -235,8 +235,10 @@ fn should_show_param_hint(
235 param_name: &str, 235 param_name: &str,
236 argument: &ast::Expr, 236 argument: &ast::Expr,
237) -> bool { 237) -> bool {
238 let argument_string = argument.syntax().to_string(); 238 if param_name.is_empty()
239 if param_name.is_empty() || argument_string.ends_with(param_name) { 239 || is_argument_similar_to_param(argument, param_name)
240 || Some(param_name) == fn_signature.name.as_ref().map(String::as_str)
241 {
240 return false; 242 return false;
241 } 243 }
242 244
@@ -245,12 +247,32 @@ fn should_show_param_hint(
245 } else { 247 } else {
246 fn_signature.parameters.len() 248 fn_signature.parameters.len()
247 }; 249 };
250
248 // avoid displaying hints for common functions like map, filter, etc. 251 // avoid displaying hints for common functions like map, filter, etc.
249 if parameters_len == 1 && (param_name.len() == 1 || param_name == "predicate") { 252 // or other obvious words used in std
250 return false; 253 parameters_len != 1 || !is_obvious_param(param_name)
254}
255
256fn is_argument_similar_to_param(argument: &ast::Expr, param_name: &str) -> bool {
257 let argument_string = remove_ref(argument.clone()).syntax().to_string();
258 argument_string.starts_with(&param_name) || argument_string.ends_with(&param_name)
259}
260
261fn remove_ref(expr: ast::Expr) -> ast::Expr {
262 if let ast::Expr::RefExpr(ref_expr) = &expr {
263 if let Some(inner) = ref_expr.expr() {
264 return inner;
265 }
251 } 266 }
267 expr
268}
252 269
253 true 270fn is_obvious_param(param_name: &str) -> bool {
271 let is_obvious_param_name = match param_name {
272 "predicate" | "value" | "pat" | "rhs" | "other" => true,
273 _ => false,
274 };
275 param_name.len() == 1 || is_obvious_param_name
254} 276}
255 277
256fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<FunctionSignature> { 278fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<FunctionSignature> {
@@ -1059,9 +1081,22 @@ impl Test {
1059 self 1081 self
1060 } 1082 }
1061 1083
1084 fn field(self, value: i32) -> Self {
1085 self
1086 }
1087
1062 fn no_hints_expected(&self, _: i32, test_var: i32) {} 1088 fn no_hints_expected(&self, _: i32, test_var: i32) {}
1089
1090 fn frob(&self, frob: bool) {}
1063} 1091}
1064 1092
1093struct Param {}
1094
1095fn different_order(param: &Param) {}
1096fn different_order_mut(param: &mut Param) {}
1097
1098fn twiddle(twiddle: bool) {}
1099
1065fn main() { 1100fn main() {
1066 let container: TestVarContainer = TestVarContainer { test_var: 42 }; 1101 let container: TestVarContainer = TestVarContainer { test_var: 42 };
1067 let test: Test = Test {}; 1102 let test: Test = Test {};
@@ -1069,11 +1104,23 @@ fn main() {
1069 map(22); 1104 map(22);
1070 filter(33); 1105 filter(33);
1071 1106
1072 let test_processed: Test = test.map(1).filter(2); 1107 let test_processed: Test = test.map(1).filter(2).field(3);
1073 1108
1074 let test_var: i32 = 55; 1109 let test_var: i32 = 55;
1075 test_processed.no_hints_expected(22, test_var); 1110 test_processed.no_hints_expected(22, test_var);
1076 test_processed.no_hints_expected(33, container.test_var); 1111 test_processed.no_hints_expected(33, container.test_var);
1112 test_processed.frob(false);
1113
1114 twiddle(true);
1115
1116 let param_begin: Param = Param {};
1117 different_order(&param_begin);
1118 different_order(&mut param_begin);
1119
1120 let a: f64 = 7.0;
1121 let b: f64 = 4.0;
1122 let _: f64 = a.div_euclid(b);
1123 let _: f64 = a.abs_sub(b);
1077}"#, 1124}"#,
1078 ); 1125 );
1079 1126
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs
index 746cc86ba..7d0544ff4 100644
--- a/crates/ra_ide/src/references.rs
+++ b/crates/ra_ide/src/references.rs
@@ -152,7 +152,7 @@ fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Optio
152 if stmt.initializer().is_some() { 152 if stmt.initializer().is_some() {
153 let pat = stmt.pat()?; 153 let pat = stmt.pat()?;
154 if let ast::Pat::BindPat(it) = pat { 154 if let ast::Pat::BindPat(it) = pat {
155 if it.is_mutable() { 155 if it.mut_token().is_some() {
156 return Some(ReferenceAccess::Write); 156 return Some(ReferenceAccess::Write);
157 } 157 }
158 } 158 }
diff --git a/crates/ra_ide/src/syntax_tree.rs b/crates/ra_ide/src/syntax_tree.rs
index f58e436d1..5842ae2e8 100644
--- a/crates/ra_ide/src/syntax_tree.rs
+++ b/crates/ra_ide/src/syntax_tree.rs
@@ -165,7 +165,7 @@ SOURCE_FILE@[0; 60)
165 PATH_SEGMENT@[16; 22) 165 PATH_SEGMENT@[16; 22)
166 NAME_REF@[16; 22) 166 NAME_REF@[16; 22)
167 IDENT@[16; 22) "assert" 167 IDENT@[16; 22) "assert"
168 EXCL@[22; 23) "!" 168 BANG@[22; 23) "!"
169 TOKEN_TREE@[23; 57) 169 TOKEN_TREE@[23; 57)
170 L_PAREN@[23; 24) "(" 170 L_PAREN@[23; 24) "("
171 STRING@[24; 52) "\"\n fn foo() {\n ..." 171 STRING@[24; 52) "\"\n fn foo() {\n ..."
@@ -173,7 +173,7 @@ SOURCE_FILE@[0; 60)
173 WHITESPACE@[53; 54) " " 173 WHITESPACE@[53; 54) " "
174 STRING@[54; 56) "\"\"" 174 STRING@[54; 56) "\"\""
175 R_PAREN@[56; 57) ")" 175 R_PAREN@[56; 57) ")"
176 SEMI@[57; 58) ";" 176 SEMICOLON@[57; 58) ";"
177 WHITESPACE@[58; 59) "\n" 177 WHITESPACE@[58; 59) "\n"
178 R_CURLY@[59; 60) "}" 178 R_CURLY@[59; 60) "}"
179"# 179"#
@@ -226,7 +226,7 @@ EXPR_STMT@[16; 58)
226 PATH_SEGMENT@[16; 22) 226 PATH_SEGMENT@[16; 22)
227 NAME_REF@[16; 22) 227 NAME_REF@[16; 22)
228 IDENT@[16; 22) "assert" 228 IDENT@[16; 22) "assert"
229 EXCL@[22; 23) "!" 229 BANG@[22; 23) "!"
230 TOKEN_TREE@[23; 57) 230 TOKEN_TREE@[23; 57)
231 L_PAREN@[23; 24) "(" 231 L_PAREN@[23; 24) "("
232 STRING@[24; 52) "\"\n fn foo() {\n ..." 232 STRING@[24; 52) "\"\n fn foo() {\n ..."
@@ -234,7 +234,7 @@ EXPR_STMT@[16; 58)
234 WHITESPACE@[53; 54) " " 234 WHITESPACE@[53; 54) " "
235 STRING@[54; 56) "\"\"" 235 STRING@[54; 56) "\"\""
236 R_PAREN@[56; 57) ")" 236 R_PAREN@[56; 57) ")"
237 SEMI@[57; 58) ";" 237 SEMICOLON@[57; 58) ";"
238"# 238"#
239 .trim() 239 .trim()
240 ); 240 );
diff --git a/crates/ra_ide/src/typing.rs b/crates/ra_ide/src/typing.rs
index cb2cd2479..f55cd3bf5 100644
--- a/crates/ra_ide/src/typing.rs
+++ b/crates/ra_ide/src/typing.rs
@@ -63,7 +63,7 @@ fn on_char_typed_inner(
63fn on_eq_typed(file: &SourceFile, offset: TextUnit) -> Option<SingleFileChange> { 63fn on_eq_typed(file: &SourceFile, offset: TextUnit) -> Option<SingleFileChange> {
64 assert_eq!(file.syntax().text().char_at(offset), Some('=')); 64 assert_eq!(file.syntax().text().char_at(offset), Some('='));
65 let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?; 65 let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?;
66 if let_stmt.has_semi() { 66 if let_stmt.semicolon_token().is_some() {
67 return None; 67 return None;
68 } 68 }
69 if let Some(expr) = let_stmt.initializer() { 69 if let Some(expr) = let_stmt.initializer() {